mirror of
				https://github.com/zadam/trilium.git
				synced 2025-10-31 18:36:30 +01:00 
			
		
		
		
	many small issues found by intellij analysis
This commit is contained in:
		
							
								
								
									
										1
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										1
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							| @@ -71,6 +71,7 @@ | |||||||
|         "turndown": "7.1.1", |         "turndown": "7.1.1", | ||||||
|         "unescape": "1.0.1", |         "unescape": "1.0.1", | ||||||
|         "ws": "8.12.0", |         "ws": "8.12.0", | ||||||
|  |         "xml2js": "^0.4.23", | ||||||
|         "yauzl": "2.10.0" |         "yauzl": "2.10.0" | ||||||
|       }, |       }, | ||||||
|       "bin": { |       "bin": { | ||||||
|   | |||||||
| @@ -88,6 +88,7 @@ | |||||||
|     "turndown": "7.1.1", |     "turndown": "7.1.1", | ||||||
|     "unescape": "1.0.1", |     "unescape": "1.0.1", | ||||||
|     "ws": "8.12.0", |     "ws": "8.12.0", | ||||||
|  |     "xml2js": "0.4.23", | ||||||
|     "yauzl": "2.10.0" |     "yauzl": "2.10.0" | ||||||
|   }, |   }, | ||||||
|   "devDependencies": { |   "devDependencies": { | ||||||
|   | |||||||
| @@ -1,12 +1,12 @@ | |||||||
| const backupService = require('./services/backup'); | const anonymizationService = require('./services/anonymization'); | ||||||
| const sqlInit = require('./services/sql_init'); | const sqlInit = require('./services/sql_init'); | ||||||
| require('./entities/entity_constructor'); | require('./becca/entity_constructor'); | ||||||
|  |  | ||||||
| sqlInit.dbReady.then(async () => { | sqlInit.dbReady.then(async () => { | ||||||
|     try { |     try { | ||||||
|         console.log("Starting anonymization..."); |         console.log("Starting anonymization..."); | ||||||
|  |  | ||||||
|         const resp = await backupService.anonymize(); |         const resp = await anonymizationService.createAnonymizedCopy('full'); | ||||||
|  |  | ||||||
|         if (resp.success) { |         if (resp.success) { | ||||||
|             console.log(`Anonymized file has been saved to: ${resp.anonymizedFilePath}`); |             console.log(`Anonymized file has been saved to: ${resp.anonymizedFilePath}`); | ||||||
|   | |||||||
| @@ -121,17 +121,17 @@ class Becca { | |||||||
|         return row ? new BNoteRevision(row) : null; |         return row ? new BNoteRevision(row) : null; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /** @returns {Option|null} */ |     /** @returns {BOption|null} */ | ||||||
|     getOption(name) { |     getOption(name) { | ||||||
|         return this.options[name]; |         return this.options[name]; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /** @returns {EtapiToken[]} */ |     /** @returns {BEtapiToken[]} */ | ||||||
|     getEtapiTokens() { |     getEtapiTokens() { | ||||||
|         return Object.values(this.etapiTokens); |         return Object.values(this.etapiTokens); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /** @returns {EtapiToken|null} */ |     /** @returns {BEtapiToken|null} */ | ||||||
|     getEtapiToken(etapiTokenId) { |     getEtapiToken(etapiTokenId) { | ||||||
|         return this.etapiTokens[etapiTokenId]; |         return this.etapiTokens[etapiTokenId]; | ||||||
|     } |     } | ||||||
| @@ -168,7 +168,7 @@ class Becca { | |||||||
|         const rows = sql.getRows(query, params); |         const rows = sql.getRows(query, params); | ||||||
|  |  | ||||||
|         const BNoteRevision = require("./entities/bnote_revision"); // avoiding circular dependency problems |         const BNoteRevision = require("./entities/bnote_revision"); // avoiding circular dependency problems | ||||||
|         return rows.map(row => new NoteRevision(row)); |         return rows.map(row => new BNoteRevision(row)); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /** Should be called when the set of all non-skeleton notes changes (added/removed) */ |     /** Should be called when the set of all non-skeleton notes changes (added/removed) */ | ||||||
|   | |||||||
| @@ -53,7 +53,7 @@ class BBranch extends AbstractBeccaEntity { | |||||||
|         this.noteId = noteId; |         this.noteId = noteId; | ||||||
|         /** @type {string} */ |         /** @type {string} */ | ||||||
|         this.parentNoteId = parentNoteId; |         this.parentNoteId = parentNoteId; | ||||||
|         /** @type {string} */ |         /** @type {string|null} */ | ||||||
|         this.prefix = prefix; |         this.prefix = prefix; | ||||||
|         /** @type {int} */ |         /** @type {int} */ | ||||||
|         this.notePosition = notePosition; |         this.notePosition = notePosition; | ||||||
|   | |||||||
| @@ -195,9 +195,9 @@ class BNote extends AbstractBeccaEntity { | |||||||
|  |  | ||||||
|     /* |     /* | ||||||
|      * Note content has quite special handling - it's not a separate entity, but a lazily loaded |      * Note content has quite special handling - it's not a separate entity, but a lazily loaded | ||||||
|      * part of Note entity with it's own sync. Reasons behind this hybrid design has been: |      * part of Note entity with its own sync. Reasons behind this hybrid design has been: | ||||||
|      * |      * | ||||||
|      * - content can be quite large and it's not necessary to load it / fill memory for any note access even if we don't need a content, especially for bulk operations like search |      * - content can be quite large, and it's not necessary to load it / fill memory for any note access even if we don't need a content, especially for bulk operations like search | ||||||
|      * - changes in the note metadata or title should not trigger note content sync (so we keep separate utcDateModified and entity changes records) |      * - changes in the note metadata or title should not trigger note content sync (so we keep separate utcDateModified and entity changes records) | ||||||
|      * - but to the user note content and title changes are one and the same - single dateModified (so all changes must go through Note and content is not a separate entity) |      * - but to the user note content and title changes are one and the same - single dateModified (so all changes must go through Note and content is not a separate entity) | ||||||
|      */ |      */ | ||||||
|   | |||||||
| @@ -261,7 +261,7 @@ async function findSimilarNotes(noteId) { | |||||||
|  |  | ||||||
|         let counter = 0; |         let counter = 0; | ||||||
|  |  | ||||||
|         // when the title is very long then weight of each individual word should be lower |         // when the title is very long then weight of each individual word should be lowered | ||||||
|         // also pretty important in e.g. long URLs in label values |         // also pretty important in e.g. long URLs in label values | ||||||
|         const lengthPenalization = 1 / Math.pow(text.length, 0.3); |         const lengthPenalization = 1 / Math.pow(text.length, 0.3); | ||||||
|  |  | ||||||
| @@ -448,7 +448,7 @@ async function findSimilarNotes(noteId) { | |||||||
| } | } | ||||||
|  |  | ||||||
| /** | /** | ||||||
|  * Point of this is to break up long running sync process to avoid blocking |  * Point of this is to break up long-running sync process to avoid blocking | ||||||
|  * see https://snyk.io/blog/nodejs-how-even-quick-async-functions-can-block-the-event-loop-starve-io/ |  * see https://snyk.io/blog/nodejs-how-even-quick-async-functions-can-block-the-event-loop-starve-io/ | ||||||
|  */ |  */ | ||||||
| function setImmediatePromise() { | function setImmediatePromise() { | ||||||
|   | |||||||
| @@ -89,7 +89,7 @@ class AppContext extends Component { | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     /** @returns {Promise<void>} */ |     /** @returns {Promise<void>} */ | ||||||
|     triggerEvent(name, data) { |     triggerEvent(name, data = {}) { | ||||||
|         return this.handleEvent(name, data); |         return this.handleEvent(name, data); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -4,11 +4,11 @@ import utils from '../services/utils.js'; | |||||||
|  * Abstract class for all components in the Trilium's frontend. |  * Abstract class for all components in the Trilium's frontend. | ||||||
|  * |  * | ||||||
|  * Contains also event implementation with following properties: |  * Contains also event implementation with following properties: | ||||||
|  * - event / command distribution is synchronous which among others mean that events are well ordered - event |  * - event / command distribution is synchronous which among others mean that events are well-ordered - event | ||||||
|  *   which was sent out first will also be processed first by the component |  *   which was sent out first will also be processed first by the component | ||||||
|  * - execution of the event / command is asynchronous - each component executes the event on its own without regard for |  * - execution of the event / command is asynchronous - each component executes the event on its own without regard for | ||||||
|  *   other components. |  *   other components. | ||||||
|  * - although the execution is async, we are collecting all the promises and therefore it is possible to wait until the |  * - although the execution is async, we are collecting all the promises, and therefore it is possible to wait until the | ||||||
|  *   event / command is executed in all components - by simply awaiting the `triggerEvent()`. |  *   event / command is executed in all components - by simply awaiting the `triggerEvent()`. | ||||||
|  */ |  */ | ||||||
| export default class Component { | export default class Component { | ||||||
| @@ -62,12 +62,12 @@ export default class Component { | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     /** @returns {Promise<void>} */ |     /** @returns {Promise<void>} */ | ||||||
|     triggerEvent(name, data) { |     triggerEvent(name, data = {}) { | ||||||
|         return this.parent.triggerEvent(name, data); |         return this.parent.triggerEvent(name, data); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /** @returns {Promise<void>} */ |     /** @returns {Promise<void>} */ | ||||||
|     handleEventInChildren(name, data) { |     handleEventInChildren(name, data = {}) { | ||||||
|         const promises = []; |         const promises = []; | ||||||
|  |  | ||||||
|         for (const child of this.children) { |         for (const child of this.children) { | ||||||
|   | |||||||
| @@ -9,9 +9,6 @@ import hoistedNoteService from "../services/hoisted_note.js"; | |||||||
| import options from "../services/options.js"; | import options from "../services/options.js"; | ||||||
|  |  | ||||||
| class NoteContext extends Component { | class NoteContext extends Component { | ||||||
|     /** |  | ||||||
|      * @param {string|null} ntxId |  | ||||||
|      */ |  | ||||||
|     constructor(ntxId = null, hoistedNoteId = 'root', mainNtxId = null) { |     constructor(ntxId = null, hoistedNoteId = 'root', mainNtxId = null) { | ||||||
|         super(); |         super(); | ||||||
|  |  | ||||||
|   | |||||||
| @@ -113,7 +113,7 @@ class FNote { | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     async getContent() { |     async getContent() { | ||||||
|         // we're not caching content since these objects are in froca and as such pretty long lived |         // we're not caching content since these objects are in froca and as such pretty long-lived | ||||||
|         const note = await server.get(`notes/${this.noteId}`); |         const note = await server.get(`notes/${this.noteId}`); | ||||||
|  |  | ||||||
|         return note.content; |         return note.content; | ||||||
|   | |||||||
| @@ -24,7 +24,7 @@ class ContextMenu { | |||||||
|  |  | ||||||
|     positionMenu() { |     positionMenu() { | ||||||
|         // code below tries to detect when dropdown would overflow from page |         // code below tries to detect when dropdown would overflow from page | ||||||
|         // in such case we'll position it above click coordinates so it will fit into client |         // in such case we'll position it above click coordinates, so it will fit into client | ||||||
|  |  | ||||||
|         const CONTEXT_MENU_PADDING = 5; // How many pixels to pad context menu from edge of screen |         const CONTEXT_MENU_PADDING = 5; // How many pixels to pad context menu from edge of screen | ||||||
|         const CONTEXT_MENU_OFFSET = 0; // How many pixels to offset context menu by relative to cursor, see #3157 |         const CONTEXT_MENU_OFFSET = 0; // How many pixels to offset context menu by relative to cursor, see #3157 | ||||||
|   | |||||||
| @@ -19,7 +19,7 @@ export default class LauncherContextMenu { | |||||||
|             x: e.pageX, |             x: e.pageX, | ||||||
|             y: e.pageY, |             y: e.pageY, | ||||||
|             items: await this.getMenuItems(), |             items: await this.getMenuItems(), | ||||||
|             selectMenuItemHandler: (item, e) => this.selectMenuItemHandler(item, e) |             selectMenuItemHandler: (item, e) => this.selectMenuItemHandler(item) | ||||||
|         }) |         }) | ||||||
|     } |     } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -21,7 +21,7 @@ export default class TreeContextMenu { | |||||||
|             x: e.pageX, |             x: e.pageX, | ||||||
|             y: e.pageY, |             y: e.pageY, | ||||||
|             items: await this.getMenuItems(), |             items: await this.getMenuItems(), | ||||||
|             selectMenuItemHandler: (item, e) => this.selectMenuItemHandler(item, e) |             selectMenuItemHandler: (item, e) => this.selectMenuItemHandler(item) | ||||||
|         }) |         }) | ||||||
|     } |     } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -67,7 +67,7 @@ function lex(str) { | |||||||
|                 finishWord(i - 1); |                 finishWord(i - 1); | ||||||
|             } |             } | ||||||
|             else { |             else { | ||||||
|                 // it's a quote but within other kind of quotes so it's valid as a literal character |                 // it's a quote but within other kind of quotes, so it's valid as a literal character | ||||||
|                 currentWord += chr; |                 currentWord += chr; | ||||||
|             } |             } | ||||||
|             continue; |             continue; | ||||||
|   | |||||||
| @@ -36,7 +36,7 @@ function isAffecting(attrRow, affectedNote) { | |||||||
|     const attrNote = froca.notes[attrRow.noteId]; |     const attrNote = froca.notes[attrRow.noteId]; | ||||||
|  |  | ||||||
|     if (!attrNote) { |     if (!attrNote) { | ||||||
|         // the note (owner of the attribute) is not even loaded into the cache so it should not affect anything else |         // the note (owner of the attribute) is not even loaded into the cache, so it should not affect anything else | ||||||
|         return false; |         return false; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -27,7 +27,7 @@ async function pasteAfter(afterBranchId) { | |||||||
|             await branchService.cloneNoteAfter(clipboardNote.noteId, afterBranchId); |             await branchService.cloneNoteAfter(clipboardNote.noteId, afterBranchId); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         // copy will keep clipboardBranchIds and clipboardMode so it's possible to paste into multiple places |         // copy will keep clipboardBranchIds and clipboardMode, so it's possible to paste into multiple places | ||||||
|     } |     } | ||||||
|     else { |     else { | ||||||
|         toastService.throwError(`Unrecognized clipboard mode=${clipboardMode}`); |         toastService.throwError(`Unrecognized clipboard mode=${clipboardMode}`); | ||||||
|   | |||||||
| @@ -7,20 +7,20 @@ | |||||||
|  * |  * | ||||||
|  * @source underscore.js |  * @source underscore.js | ||||||
|  * @see http://unscriptable.com/2009/03/20/debouncing-javascript-methods/ |  * @see http://unscriptable.com/2009/03/20/debouncing-javascript-methods/ | ||||||
|  * @param {Function} function to wrap |  * @param {Function} func to wrap | ||||||
|  * @param {Number} timeout in ms (`100`) |  * @param {Number} waitMs in ms (`100`) | ||||||
|  * @param {Boolean} whether to execute at the beginning (`false`) |  * @param {Boolean} immediate whether to execute at the beginning (`false`) | ||||||
|  * @api public |  * @api public | ||||||
|  */ |  */ | ||||||
| function debounce(func, wait_ms, immediate){ | function debounce(func, waitMs, immediate) { | ||||||
|     var timeout, args, context, timestamp, result; |     var timeout, args, context, timestamp, result; | ||||||
|     if (null == wait_ms) wait_ms = 100; |     if (null == waitMs) waitMs = 100; | ||||||
|  |  | ||||||
|     function later() { |     function later() { | ||||||
|         var last = Date.now() - timestamp; |         var last = Date.now() - timestamp; | ||||||
|  |  | ||||||
|         if (last < wait_ms && last >= 0) { |         if (last < waitMs && last >= 0) { | ||||||
|             timeout = setTimeout(later, wait_ms - last); |             timeout = setTimeout(later, waitMs - last); | ||||||
|         } else { |         } else { | ||||||
|             timeout = null; |             timeout = null; | ||||||
|             if (!immediate) { |             if (!immediate) { | ||||||
| @@ -28,14 +28,14 @@ function debounce(func, wait_ms, immediate){ | |||||||
|                 context = args = null; |                 context = args = null; | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|     }; |     } | ||||||
|  |  | ||||||
|     var debounced = function(){ |     var debounced = function(){ | ||||||
|         context = this; |         context = this; | ||||||
|         args = arguments; |         args = arguments; | ||||||
|         timestamp = Date.now(); |         timestamp = Date.now(); | ||||||
|         var callNow = immediate && !timeout; |         var callNow = immediate && !timeout; | ||||||
|         if (!timeout) timeout = setTimeout(later, wait_ms); |         if (!timeout) timeout = setTimeout(later, waitMs); | ||||||
|         if (callNow) { |         if (callNow) { | ||||||
|             result = func.apply(context, args); |             result = func.apply(context, args); | ||||||
|             context = args = null; |             context = args = null; | ||||||
| @@ -62,7 +62,7 @@ function debounce(func, wait_ms, immediate){ | |||||||
|     }; |     }; | ||||||
|  |  | ||||||
|     return debounced; |     return debounced; | ||||||
| }; | } | ||||||
|  |  | ||||||
| // Adds compatibility for ES modules | // Adds compatibility for ES modules | ||||||
| debounce.debounce = debounce; | debounce.debounce = debounce; | ||||||
|   | |||||||
| @@ -85,7 +85,7 @@ function processNoteChange(loadResults, ec) { | |||||||
|     const note = froca.notes[ec.entityId]; |     const note = froca.notes[ec.entityId]; | ||||||
|  |  | ||||||
|     if (!note) { |     if (!note) { | ||||||
|         // if this note has not been requested before then it's not part of froca's cached subset and |         // if this note has not been requested before then it's not part of froca's cached subset, and | ||||||
|         // we're not interested in it |         // we're not interested in it | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|   | |||||||
| @@ -175,7 +175,7 @@ function FrontendScriptApi(startNote, currentNote, originEntity = null, $contain | |||||||
|      * |      * | ||||||
|      * @method |      * @method | ||||||
|      * @param {string} script - script to be executed on the backend |      * @param {string} script - script to be executed on the backend | ||||||
|      * @param {Array.<?>} params - list of parameters to the anonymous function to be send to backend |      * @param {Array.<?>} params - list of parameters to the anonymous function to be sent to backend | ||||||
|      * @returns {Promise<*>} return value of the executed function on the backend |      * @returns {Promise<*>} return value of the executed function on the backend | ||||||
|      */ |      */ | ||||||
|     this.runOnBackend = async (script, params = []) => { |     this.runOnBackend = async (script, params = []) => { | ||||||
|   | |||||||
| @@ -1,5 +1,4 @@ | |||||||
| import appContext from "../components/app_context.js"; | import appContext from "../components/app_context.js"; | ||||||
| import utils from "./utils.js"; |  | ||||||
| import protectedSessionHolder from "./protected_session_holder.js"; | import protectedSessionHolder from "./protected_session_holder.js"; | ||||||
| import server from "./server.js"; | import server from "./server.js"; | ||||||
| import ws from "./ws.js"; | import ws from "./ws.js"; | ||||||
| @@ -15,7 +14,7 @@ async function createNote(parentNotePath, options = {}) { | |||||||
|     }, options); |     }, options); | ||||||
|  |  | ||||||
|     // if isProtected isn't available (user didn't enter password yet), then note is created as unencrypted |     // if isProtected isn't available (user didn't enter password yet), then note is created as unencrypted | ||||||
|     // but this is quite weird since user doesn't see WHERE the note is being created so it shouldn't occur often |     // but this is quite weird since user doesn't see WHERE the note is being created, so it shouldn't occur often | ||||||
|     if (!options.isProtected || !protectedSessionHolder.isProtectedSessionAvailable()) { |     if (!options.isProtected || !protectedSessionHolder.isProtectedSessionAvailable()) { | ||||||
|         options.isProtected = false; |         options.isProtected = false; | ||||||
|     } |     } | ||||||
|   | |||||||
| @@ -58,11 +58,11 @@ function downloadNoteRevision(noteId, noteRevisionId) { | |||||||
|  */ |  */ | ||||||
| function getUrlForDownload(url) { | function getUrlForDownload(url) { | ||||||
|     if (utils.isElectron()) { |     if (utils.isElectron()) { | ||||||
|         // electron needs absolute URL so we extract current host, port, protocol |         // electron needs absolute URL, so we extract current host, port, protocol | ||||||
|         return `${getHost()}/${url}`; |         return `${getHost()}/${url}`; | ||||||
|     } |     } | ||||||
|     else { |     else { | ||||||
|         // web server can be deployed on subdomain so we need to use relative path |         // web server can be deployed on subdomain, so we need to use relative path | ||||||
|         return url; |         return url; | ||||||
|     } |     } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -47,7 +47,7 @@ export default class SpacedUpdate { | |||||||
|             this.changed = false; |             this.changed = false; | ||||||
|         } |         } | ||||||
|         else { |         else { | ||||||
|             // update not triggered but changes are still pending so we need to schedule another check |             // update not triggered but changes are still pending, so we need to schedule another check | ||||||
|             this.scheduleUpdate(); |             this.scheduleUpdate(); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|   | |||||||
| @@ -1,6 +1,5 @@ | |||||||
| import ws from './ws.js'; | import ws from './ws.js'; | ||||||
| import utils from './utils.js'; | import utils from './utils.js'; | ||||||
| import server from './server.js'; |  | ||||||
| import froca from './froca.js'; | import froca from './froca.js'; | ||||||
| import hoistedNoteService from '../services/hoisted_note.js'; | import hoistedNoteService from '../services/hoisted_note.js'; | ||||||
| import appContext from "../components/app_context.js"; | import appContext from "../components/app_context.js"; | ||||||
|   | |||||||
| @@ -3,7 +3,7 @@ function reloadFrontendApp(reason) { | |||||||
|         logInfo(`Frontend app reload: ${reason}`); |         logInfo(`Frontend app reload: ${reason}`); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     window.location.reload(true); |     window.location.reload(); | ||||||
| } | } | ||||||
|  |  | ||||||
| function parseDate(str) { | function parseDate(str) { | ||||||
| @@ -314,7 +314,7 @@ function initHelpDropdown($el) { | |||||||
|     const $dropdownMenu = $el.find('.help-dropdown .dropdown-menu'); |     const $dropdownMenu = $el.find('.help-dropdown .dropdown-menu'); | ||||||
|     $dropdownMenu.on('click', e => e.stopPropagation()); |     $dropdownMenu.on('click', e => e.stopPropagation()); | ||||||
|  |  | ||||||
|     // previous propagation stop will also block help buttons from being opened so we need to re-init for this element |     // previous propagation stop will also block help buttons from being opened, so we need to re-init for this element | ||||||
|     initHelpButtons($dropdownMenu); |     initHelpButtons($dropdownMenu); | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -346,6 +346,7 @@ export default class AttributeEditorWidget extends NoteContextAwareWidget { | |||||||
|  |  | ||||||
|         this.$editor.on("click", e => this.handleEditorClick(e)); |         this.$editor.on("click", e => this.handleEditorClick(e)); | ||||||
|  |  | ||||||
|  |         /** @property {BalloonEditor} */ | ||||||
|         this.textEditor = await BalloonEditor.create(this.$editor[0], editorConfig); |         this.textEditor = await BalloonEditor.create(this.$editor[0], editorConfig); | ||||||
|         this.textEditor.model.document.on('change:data', () => this.dataChanged()); |         this.textEditor.model.document.on('change:data', () => this.dataChanged()); | ||||||
|         this.textEditor.editing.view.document.on('enter', (event, data) => { |         this.textEditor.editing.view.document.on('enter', (event, data) => { | ||||||
|   | |||||||
| @@ -112,7 +112,7 @@ export default class SplitNoteContainer extends FlexContainer { | |||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * widget.hasBeenAlreadyShown is intended for lazy loading of cached tabs - initial note switches of new tabs |      * widget.hasBeenAlreadyShown is intended for lazy loading of cached tabs - initial note switches of new tabs | ||||||
|      * are not executed, we're waiting for the first tab activation and then we update the tab. After this initial |      * are not executed, we're waiting for the first tab activation, and then we update the tab. After this initial | ||||||
|      * activation further note switches are always propagated to the tabs. |      * activation further note switches are always propagated to the tabs. | ||||||
|      */ |      */ | ||||||
|     handleEventInChildren(name, data) { |     handleEventInChildren(name, data) { | ||||||
|   | |||||||
| @@ -99,7 +99,7 @@ export default class AddLinkDialog extends BasicWidget { | |||||||
|  |  | ||||||
|         this.$addLinkTitleSettings.find('input[type=radio]').on('change', e => this.updateTitleSettingsVisibility(e)); |         this.$addLinkTitleSettings.find('input[type=radio]').on('change', e => this.updateTitleSettingsVisibility(e)); | ||||||
|  |  | ||||||
|         // with selection hyper link is implied |         // with selection hyperlink is implied | ||||||
|         if (this.textTypeWidget.hasSelection()) { |         if (this.textTypeWidget.hasSelection()) { | ||||||
|             this.$addLinkTitleSettings.find("input[value='hyper-link']").prop("checked", true); |             this.$addLinkTitleSettings.find("input[value='hyper-link']").prop("checked", true); | ||||||
|         } |         } | ||||||
|   | |||||||
| @@ -1,5 +1,4 @@ | |||||||
| import server from "../../services/server.js"; | import server from "../../services/server.js"; | ||||||
| import utils from "../../services/utils.js"; |  | ||||||
| import BasicWidget from "../basic_widget.js"; | import BasicWidget from "../basic_widget.js"; | ||||||
|  |  | ||||||
| const TPL = ` | const TPL = ` | ||||||
|   | |||||||
| @@ -1,4 +1,3 @@ | |||||||
| import utils from "../../services/utils.js"; |  | ||||||
| import BasicWidget from "../basic_widget.js"; | import BasicWidget from "../basic_widget.js"; | ||||||
|  |  | ||||||
| const TPL = ` | const TPL = ` | ||||||
|   | |||||||
| @@ -98,7 +98,7 @@ export default class IncludeNoteDialog extends BasicWidget { | |||||||
|         const boxSize = $("input[name='include-note-box-size']:checked").val(); |         const boxSize = $("input[name='include-note-box-size']:checked").val(); | ||||||
|  |  | ||||||
|         if (note.type === 'image') { |         if (note.type === 'image') { | ||||||
|             // there's no benefit to use insert note functionlity for images |             // there's no benefit to use insert note functionlity for images, | ||||||
|             // so we'll just add an IMG tag |             // so we'll just add an IMG tag | ||||||
|             this.textTypeWidget.addImage(noteId); |             this.textTypeWidget.addImage(noteId); | ||||||
|         } |         } | ||||||
|   | |||||||
| @@ -27,7 +27,7 @@ export default class InfoDialog extends BasicWidget { | |||||||
|         super(); |         super(); | ||||||
|  |  | ||||||
|         this.resolve = null; |         this.resolve = null; | ||||||
|         this.$originallyFocused = null; // element focused before the dialog was opened so we can return to it afterwards |         this.$originallyFocused = null; // element focused before the dialog was opened, so we can return to it afterwards | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     doRender() { |     doRender() { | ||||||
|   | |||||||
| @@ -239,8 +239,8 @@ export default class NoteRevisionsDialog extends BasicWidget { | |||||||
|         } |         } | ||||||
|         else if (revisionItem.type === 'image') { |         else if (revisionItem.type === 'image') { | ||||||
|             this.$content.html($("<img>") |             this.$content.html($("<img>") | ||||||
|                 // reason why we put this inline as base64 is that we do not want to let user to copy this |                 // reason why we put this inline as base64 is that we do not want to let user copy this | ||||||
|                 // as a URL to be used in a note. Instead if they copy and paste it into a note, it will be a uploaded as a new note |                 // as a URL to be used in a note. Instead, if they copy and paste it into a note, it will be an uploaded as a new note | ||||||
|                 .attr("src", `data:${note.mime};base64,${fullNoteRevision.content}`) |                 .attr("src", `data:${note.mime};base64,${fullNoteRevision.content}`) | ||||||
|                 .css("max-width", "100%") |                 .css("max-width", "100%") | ||||||
|                 .css("max-height", "100%")); |                 .css("max-height", "100%")); | ||||||
|   | |||||||
| @@ -69,8 +69,8 @@ export default class FindInCode { | |||||||
|             let curChar = 0; |             let curChar = 0; | ||||||
|             let curMatch = null; |             let curMatch = null; | ||||||
|             findResult = []; |             findResult = []; | ||||||
|             // All those markText take several seconds on eg this ~500-line |             // All those markText take several seconds on e.g. this ~500-line | ||||||
|             // script, batch them inside an operation so they become |             // script, batch them inside an operation, so they become | ||||||
|             // unnoticeable. Alternatively, an overlay could be used, see |             // unnoticeable. Alternatively, an overlay could be used, see | ||||||
|             // https://codemirror.net/addon/search/match-highlighter.js ? |             // https://codemirror.net/addon/search/match-highlighter.js ? | ||||||
|             codeEditor.operation(() => { |             codeEditor.operation(() => { | ||||||
|   | |||||||
| @@ -14,9 +14,9 @@ export default class FindInText { | |||||||
|         const selection = textEditor.model.document.selection; |         const selection = textEditor.model.document.selection; | ||||||
|         const range = selection.getFirstRange(); |         const range = selection.getFirstRange(); | ||||||
|  |  | ||||||
|  |         // FIXME | ||||||
|         for (const item of range.getItems()) { |         for (const item of range.getItems()) { | ||||||
|             // Fill in the findbox with the current selection if |             // Fill in the findbox with the current selection if any | ||||||
|             // any |  | ||||||
|             return item.data; |             return item.data; | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|   | |||||||
| @@ -19,7 +19,6 @@ import options from "../services/options.js"; | |||||||
| import protectedSessionHolder from "../services/protected_session_holder.js"; | import protectedSessionHolder from "../services/protected_session_holder.js"; | ||||||
| import dialogService from "../services/dialog.js"; | import dialogService from "../services/dialog.js"; | ||||||
| import shortcutService from "../services/shortcuts.js"; | import shortcutService from "../services/shortcuts.js"; | ||||||
| import LauncherContextMenu from "../menus/launcher_context_menu.js"; |  | ||||||
|  |  | ||||||
| const TPL = ` | const TPL = ` | ||||||
| <div class="tree-wrapper"> | <div class="tree-wrapper"> | ||||||
| @@ -915,7 +914,7 @@ export default class NoteTreeWidget extends NoteContextAwareWidget { | |||||||
|                 if (expand) { |                 if (expand) { | ||||||
|                     await parentNode.setExpanded(true, {noAnimation: true}); |                     await parentNode.setExpanded(true, {noAnimation: true}); | ||||||
|  |  | ||||||
|                     // although previous line should set the expanded status, it seems to happen asynchronously |                     // although previous line should set the expanded status, it seems to happen asynchronously, | ||||||
|                     // so we need to make sure it is set properly before calling updateNode which uses this flag |                     // so we need to make sure it is set properly before calling updateNode which uses this flag | ||||||
|                     const branch = froca.getBranch(parentNode.data.branchId); |                     const branch = froca.getBranch(parentNode.data.branchId); | ||||||
|                     branch.isExpanded = true; |                     branch.isExpanded = true; | ||||||
| @@ -925,7 +924,7 @@ export default class NoteTreeWidget extends NoteContextAwareWidget { | |||||||
|  |  | ||||||
|                 let foundChildNode = this.findChildNode(parentNode, childNoteId); |                 let foundChildNode = this.findChildNode(parentNode, childNoteId); | ||||||
|  |  | ||||||
|                 if (!foundChildNode) { // note might be recently created so we'll force reload and try again |                 if (!foundChildNode) { // note might be recently created, so we'll force reload and try again | ||||||
|                     await parentNode.load(true); |                     await parentNode.load(true); | ||||||
|  |  | ||||||
|                     foundChildNode = this.findChildNode(parentNode, childNoteId); |                     foundChildNode = this.findChildNode(parentNode, childNoteId); | ||||||
| @@ -933,7 +932,7 @@ export default class NoteTreeWidget extends NoteContextAwareWidget { | |||||||
|                     if (!foundChildNode) { |                     if (!foundChildNode) { | ||||||
|                         if (logErrors) { |                         if (logErrors) { | ||||||
|                             // besides real errors this can be also caused by hiding of e.g. included images |                             // besides real errors this can be also caused by hiding of e.g. included images | ||||||
|                             // these are real notes with real notePath, user can display them in a detail |                             // these are real notes with real notePath, user can display them in a detail, | ||||||
|                             // but they don't have a node in the tree |                             // but they don't have a node in the tree | ||||||
|  |  | ||||||
|                             const childNote = await froca.getNote(childNoteId); |                             const childNote = await froca.getNote(childNoteId); | ||||||
|   | |||||||
| @@ -54,7 +54,7 @@ export default class QuickSearchWidget extends BasicWidget { | |||||||
|  |  | ||||||
|         if(utils.isMobile()) { |         if(utils.isMobile()) { | ||||||
|             this.$searchString.keydown(e =>{ |             this.$searchString.keydown(e =>{ | ||||||
|                 if(e.which==13) { |                 if(e.which === 13) { | ||||||
|                     if (this.$dropdownMenu.is(":visible")) { |                     if (this.$dropdownMenu.is(":visible")) { | ||||||
|                         this.search(); // just update already visible dropdown |                         this.search(); // just update already visible dropdown | ||||||
|                     } else { |                     } else { | ||||||
| @@ -128,7 +128,7 @@ export default class QuickSearchWidget extends BasicWidget { | |||||||
|                 this.$dropdownToggle.dropdown("hide"); |                 this.$dropdownToggle.dropdown("hide"); | ||||||
|  |  | ||||||
|                 if (!e.target || e.target.nodeName !== 'A') { |                 if (!e.target || e.target.nodeName !== 'A') { | ||||||
|                     // click on the link is handled by link handling but we want the whole item clickable |                     // click on the link is handled by link handling, but we want the whole item clickable | ||||||
|                     appContext.tabManager.getActiveContext().setNote(note.noteId); |                     appContext.tabManager.getActiveContext().setNote(note.noteId); | ||||||
|                 } |                 } | ||||||
|             }); |             }); | ||||||
|   | |||||||
| @@ -1,9 +1,5 @@ | |||||||
| import NoteContextAwareWidget from "./note_context_aware_widget.js"; | import NoteContextAwareWidget from "./note_context_aware_widget.js"; | ||||||
| import treeService from "../services/tree.js"; |  | ||||||
| import linkService from "../services/link.js"; |  | ||||||
| import hoistedNoteService from "../services/hoisted_note.js"; |  | ||||||
| import server from "../services/server.js"; | import server from "../services/server.js"; | ||||||
| import toastService from "../services/toast.js"; |  | ||||||
|  |  | ||||||
| const TPL = ` | const TPL = ` | ||||||
| <div class="sql-table-schemas-widget"> | <div class="sql-table-schemas-widget"> | ||||||
|   | |||||||
| @@ -2,8 +2,8 @@ | |||||||
|  * Table of contents widget |  * Table of contents widget | ||||||
|  * (c) Antonio Tejada 2022 |  * (c) Antonio Tejada 2022 | ||||||
|  * |  * | ||||||
|  * By design there's no support for non-sensical or malformed constructs: |  * By design there's no support for nonsensical or malformed constructs: | ||||||
|  * - headings inside elements (eg Trilium allows headings inside tables, but |  * - headings inside elements (e.g. Trilium allows headings inside tables, but | ||||||
|  *   not inside lists) |  *   not inside lists) | ||||||
|  * - nested headings when using raw HTML <H2><H3></H3></H2> |  * - nested headings when using raw HTML <H2><H3></H3></H2> | ||||||
|  * - malformed headings when using raw HTML <H2></H3></H2><H3> |  * - malformed headings when using raw HTML <H2></H3></H2><H3> | ||||||
| @@ -52,7 +52,7 @@ const TPL = `<div class="toc-widget"> | |||||||
|  * @param {Element} parent Parent node to find a headingIndex'th in. |  * @param {Element} parent Parent node to find a headingIndex'th in. | ||||||
|  * @param {uint} headingIndex Index for the heading |  * @param {uint} headingIndex Index for the heading | ||||||
|  * @returns {Element|null} Heading node with the given index, null couldn't be |  * @returns {Element|null} Heading node with the given index, null couldn't be | ||||||
|  *          found (ie malformed like nested headings, etc) |  *          found (ie malformed like nested headings, etc.) | ||||||
|  */ |  */ | ||||||
| function findHeadingNodeByIndex(parent, headingIndex) { | function findHeadingNodeByIndex(parent, headingIndex) { | ||||||
|     let headingNode = null; |     let headingNode = null; | ||||||
| @@ -61,7 +61,7 @@ function findHeadingNodeByIndex(parent, headingIndex) { | |||||||
|  |  | ||||||
|         // Headings appear as flattened top level children in the CKEditor |         // Headings appear as flattened top level children in the CKEditor | ||||||
|         // document named as "heading" plus the level, eg "heading2", |         // document named as "heading" plus the level, eg "heading2", | ||||||
|         // "heading3", "heading2", etc and not nested wrt the heading level. If |         // "heading3", "heading2", etc. and not nested wrt the heading level. If | ||||||
|         // a heading node is found, decrement the headingIndex until zero is |         // a heading node is found, decrement the headingIndex until zero is | ||||||
|         // reached |         // reached | ||||||
|         if (child.name.startsWith("heading")) { |         if (child.name.startsWith("heading")) { | ||||||
|   | |||||||
| @@ -144,7 +144,7 @@ export default class ExcalidrawTypeWidget extends TypeWidget { | |||||||
|     /** |     /** | ||||||
|      * called to populate the widget container with the note content |      * called to populate the widget container with the note content | ||||||
|      * |      * | ||||||
|      * @param {note} note |      * @param {FNote} note | ||||||
|      */ |      */ | ||||||
|     async doRefresh(note) { |     async doRefresh(note) { | ||||||
|         // see if note changed, since we do not get a new class for a new note |         // see if note changed, since we do not get a new class for a new note | ||||||
| @@ -219,7 +219,7 @@ export default class ExcalidrawTypeWidget extends TypeWidget { | |||||||
|                 collaborators: [] |                 collaborators: [] | ||||||
|             }; |             }; | ||||||
|  |  | ||||||
|             // files are expected in an array when loading. they are stored as an key-index object |             // files are expected in an array when loading. they are stored as a key-index object | ||||||
|             // see example for loading here: |             // see example for loading here: | ||||||
|             // https://github.com/excalidraw/excalidraw/blob/c5a7723185f6ca05e0ceb0b0d45c4e3fbcb81b2a/src/packages/excalidraw/example/App.js#L68 |             // https://github.com/excalidraw/excalidraw/blob/c5a7723185f6ca05e0ceb0b0d45c4e3fbcb81b2a/src/packages/excalidraw/example/App.js#L68 | ||||||
|             const fileArray = []; |             const fileArray = []; | ||||||
|   | |||||||
| @@ -122,13 +122,13 @@ export default class EditableTextTypeWidget extends AbstractTextTypeWidget { | |||||||
|         this.watchdog = new EditorWatchdog(BalloonEditor, { |         this.watchdog = new EditorWatchdog(BalloonEditor, { | ||||||
|             // An average number of milliseconds between the last editor errors (defaults to 5000). |             // An average number of milliseconds between the last editor errors (defaults to 5000). | ||||||
|             // When the period of time between errors is lower than that and the crashNumberLimit |             // When the period of time between errors is lower than that and the crashNumberLimit | ||||||
|             // is also reached, the watchdog changes its state to crashedPermanently and it stops |             // is also reached, the watchdog changes its state to crashedPermanently, and it stops | ||||||
|             // restarting the editor. This prevents an infinite restart loop. |             // restarting the editor. This prevents an infinite restart loop. | ||||||
|             minimumNonErrorTimePeriod: 5000, |             minimumNonErrorTimePeriod: 5000, | ||||||
|             // A threshold specifying the number of errors (defaults to 3). |             // A threshold specifying the number of errors (defaults to 3). | ||||||
|             // After this limit is reached and the time between last errors |             // After this limit is reached and the time between last errors | ||||||
|             // is shorter than minimumNonErrorTimePeriod, the watchdog changes |             // is shorter than minimumNonErrorTimePeriod, the watchdog changes | ||||||
|             // its state to crashedPermanently and it stops restarting the editor. |             // its state to crashedPermanently, and it stops restarting the editor. | ||||||
|             // This prevents an infinite restart loop. |             // This prevents an infinite restart loop. | ||||||
|             crashNumberLimit: 3, |             crashNumberLimit: 3, | ||||||
|             // A minimum number of milliseconds between saving the editor data internally (defaults to 5000). |             // A minimum number of milliseconds between saving the editor data internally (defaults to 5000). | ||||||
|   | |||||||
| @@ -56,7 +56,7 @@ class ImageTypeWidget extends TypeWidget { | |||||||
|         }); |         }); | ||||||
|  |  | ||||||
|         if (utils.isElectron()) { |         if (utils.isElectron()) { | ||||||
|             // for browser we want to let the native menu |             // for browser, we want to let the native menu | ||||||
|             this.$imageView.on('contextmenu', e => { |             this.$imageView.on('contextmenu', e => { | ||||||
|                 e.preventDefault(); |                 e.preventDefault(); | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1,7 +1,6 @@ | |||||||
| import server from "../../../services/server.js"; | import server from "../../../services/server.js"; | ||||||
| import toastService from "../../../services/toast.js"; | import toastService from "../../../services/toast.js"; | ||||||
| import NoteContextAwareWidget from "../../note_context_aware_widget.js"; | import NoteContextAwareWidget from "../../note_context_aware_widget.js"; | ||||||
| import attributeService from "../../../services/attributes.js"; |  | ||||||
|  |  | ||||||
| export default class OptionsWidget extends NoteContextAwareWidget { | export default class OptionsWidget extends NoteContextAwareWidget { | ||||||
|     constructor() { |     constructor() { | ||||||
|   | |||||||
| @@ -11,10 +11,10 @@ function getFontCss(req, res) { | |||||||
|  |  | ||||||
|     const optionsMap = optionService.getOptionsMap(); |     const optionsMap = optionService.getOptionsMap(); | ||||||
|  |  | ||||||
|     const mainFontFamilyOverridden = optionsMap.mainFontFamily != 'theme'; |     const mainFontFamilyOverridden = optionsMap.mainFontFamily !== 'theme'; | ||||||
|     const treeFontFamilyOverridden = optionsMap.treeFontFamily != 'theme'; |     const treeFontFamilyOverridden = optionsMap.treeFontFamily !== 'theme'; | ||||||
|     const detailFontFamilyOverridden = optionsMap.detailFontFamily != 'theme'; |     const detailFontFamilyOverridden = optionsMap.detailFontFamily !== 'theme'; | ||||||
|     const monospaceFontFamilyOverridden = optionsMap.monospaceFontFamily != 'theme'; |     const monospaceFontFamilyOverridden = optionsMap.monospaceFontFamily !== 'theme'; | ||||||
|  |  | ||||||
|     // using body to be more specific than themes' :root |     // using body to be more specific than themes' :root | ||||||
|     let style = 'body {'; |     let style = 'body {'; | ||||||
|   | |||||||
| @@ -30,6 +30,11 @@ function buildDescendantCountMap() { | |||||||
|     return noteIdToCountMap; |     return noteIdToCountMap; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * @param {BNote} note | ||||||
|  |  * @param {int} depth | ||||||
|  |  * @returns {string[]} noteIds | ||||||
|  |  */ | ||||||
| function getNeighbors(note, depth) { | function getNeighbors(note, depth) { | ||||||
|     if (depth === 0) { |     if (depth === 0) { | ||||||
|         return []; |         return []; | ||||||
| @@ -157,7 +162,7 @@ function getLinkMap(req) { | |||||||
|  |  | ||||||
| function getTreeMap(req) { | function getTreeMap(req) { | ||||||
|     const mapRootNote = becca.getNote(req.params.noteId); |     const mapRootNote = becca.getNote(req.params.noteId); | ||||||
|     // if the map root itself has ignore (journal typically) then there wouldn't be anything to display so |     // if the map root itself has ignore (journal typically) then there wouldn't be anything to display, so | ||||||
|     // we'll just ignore it |     // we'll just ignore it | ||||||
|     const ignoreExcludeFromNoteMap = mapRootNote.hasLabel('excludeFromNoteMap'); |     const ignoreExcludeFromNoteMap = mapRootNote.hasLabel('excludeFromNoteMap'); | ||||||
|     const subtree = mapRootNote.getSubtree({ |     const subtree = mapRootNote.getSubtree({ | ||||||
| @@ -259,6 +264,7 @@ function findExcerpts(sourceNote, referencedNoteId) { | |||||||
|             centerEl = centerEl.parentElement; |             centerEl = centerEl.parentElement; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|  |         /** @var {HTMLElement[]} */ | ||||||
|         const excerptEls = [centerEl]; |         const excerptEls = [centerEl]; | ||||||
|         let excerptLength = centerEl.textContent.length; |         let excerptLength = centerEl.textContent.length; | ||||||
|         let left = centerEl; |         let left = centerEl; | ||||||
|   | |||||||
| @@ -21,7 +21,7 @@ async function testSync() { | |||||||
|  |  | ||||||
|         await syncService.login(); |         await syncService.login(); | ||||||
|  |  | ||||||
|         // login was successful so we'll kick off sync now |         // login was successful, so we'll kick off sync now | ||||||
|         // this is important in case when sync server has been just initialized |         // this is important in case when sync server has been just initialized | ||||||
|         syncService.sync(); |         syncService.sync(); | ||||||
|  |  | ||||||
|   | |||||||
| @@ -10,8 +10,8 @@ const BUILTIN_ATTRIBUTES = require("./builtin_attributes"); | |||||||
| const ATTRIBUTE_TYPES = [ 'label', 'relation' ]; | const ATTRIBUTE_TYPES = [ 'label', 'relation' ]; | ||||||
|  |  | ||||||
| /** @returns {BNote[]} */ | /** @returns {BNote[]} */ | ||||||
| function getNotesWithLabel(name, value) { | function getNotesWithLabel(name, value = undefined) { | ||||||
|     const query = formatAttrForSearch({type: 'label', name, value}, true); |     const query = formatAttrForSearch({type: 'label', name, value}, value !== undefined); | ||||||
|     return searchService.searchNotes(query, { |     return searchService.searchNotes(query, { | ||||||
|         includeArchivedNotes: true, |         includeArchivedNotes: true, | ||||||
|         ignoreHoistedNote: true |         ignoreHoistedNote: true | ||||||
|   | |||||||
| @@ -336,7 +336,7 @@ class ConsistencyChecks { | |||||||
|             ({noteId, isProtected, type, mime}) => { |             ({noteId, isProtected, type, mime}) => { | ||||||
|                 if (this.autoFix) { |                 if (this.autoFix) { | ||||||
|                     // it might be possible that the note_content is not available only because of the interrupted |                     // it might be possible that the note_content is not available only because of the interrupted | ||||||
|                     // sync and it will come later. It's therefore important to guarantee that this artifical |                     // sync, and it will come later. It's therefore important to guarantee that this artifical | ||||||
|                     // record won't overwrite the real one coming from the sync. |                     // record won't overwrite the real one coming from the sync. | ||||||
|                     const fakeDate = "2000-01-01 00:00:00Z"; |                     const fakeDate = "2000-01-01 00:00:00Z"; | ||||||
|  |  | ||||||
|   | |||||||
| @@ -5,7 +5,7 @@ function utcNowDateTime() { | |||||||
|     return utcDateTimeStr(new Date()); |     return utcDateTimeStr(new Date()); | ||||||
| } | } | ||||||
|  |  | ||||||
| // CLS date time is important in web deployments - server often runs in different time zone than user is located in | // CLS date time is important in web deployments - server often runs in different time zone than user is located in, | ||||||
| // so we'd prefer client timezone to be used to record local dates. For this reason requests from client contain | // so we'd prefer client timezone to be used to record local dates. For this reason requests from client contain | ||||||
| // "trilium-local-now-datetime" header which is then stored in CLS | // "trilium-local-now-datetime" header which is then stored in CLS | ||||||
| function localNowDateTime() { | function localNowDateTime() { | ||||||
|   | |||||||
| @@ -21,6 +21,8 @@ const ValidationError = require("../../errors/validation_error"); | |||||||
|  * @param {TaskContext} taskContext |  * @param {TaskContext} taskContext | ||||||
|  * @param {BBranch} branch |  * @param {BBranch} branch | ||||||
|  * @param {string} format - 'html' or 'markdown' |  * @param {string} format - 'html' or 'markdown' | ||||||
|  |  * @param {object} res - express response | ||||||
|  |  * @param {boolean} setHeaders | ||||||
|  */ |  */ | ||||||
| async function exportToZip(taskContext, branch, format, res, setHeaders = true) { | async function exportToZip(taskContext, branch, format, res, setHeaders = true) { | ||||||
|     if (!['html', 'markdown'].includes(format)) { |     if (!['html', 'markdown'].includes(format)) { | ||||||
|   | |||||||
| @@ -207,7 +207,7 @@ function importEnex(taskContext, file, parentNote) { | |||||||
|     }); |     }); | ||||||
|  |  | ||||||
|     function updateDates(noteId, utcDateCreated, utcDateModified) { |     function updateDates(noteId, utcDateCreated, utcDateModified) { | ||||||
|         // it's difficult to force custom dateCreated and dateModified to Note entity so we do it post-creation with SQL |         // it's difficult to force custom dateCreated and dateModified to Note entity, so we do it post-creation with SQL | ||||||
|         sql.execute(` |         sql.execute(` | ||||||
|                 UPDATE notes  |                 UPDATE notes  | ||||||
|                 SET dateCreated = ?,  |                 SET dateCreated = ?,  | ||||||
|   | |||||||
| @@ -544,7 +544,7 @@ function saveLinks(note, content) { | |||||||
|  |  | ||||||
|             existingLinks.push(newLink); |             existingLinks.push(newLink); | ||||||
|         } |         } | ||||||
|         // else the link exists so we don't need to do anything |         // else the link exists, so we don't need to do anything | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     // marking links as deleted if they are not present on the page anymore |     // marking links as deleted if they are not present on the page anymore | ||||||
|   | |||||||
| @@ -11,7 +11,7 @@ const syncOptions = require('./sync_options'); | |||||||
| function exec(opts) { | function exec(opts) { | ||||||
|     const client = getClient(opts); |     const client = getClient(opts); | ||||||
|  |  | ||||||
|     // hack for cases where electron.net does not work but we don't want to set proxy |     // hack for cases where electron.net does not work, but we don't want to set proxy | ||||||
|     if (opts.proxy === 'noproxy') { |     if (opts.proxy === 'noproxy') { | ||||||
|         opts.proxy = null; |         opts.proxy = null; | ||||||
|     } |     } | ||||||
|   | |||||||
| @@ -66,8 +66,8 @@ function executeScript(script, params, startNoteId, currentNoteId, originEntityN | |||||||
|     const currentNote = becca.getNote(currentNoteId); |     const currentNote = becca.getNote(currentNoteId); | ||||||
|     const originEntity = becca.getEntity(originEntityName, originEntityId); |     const originEntity = becca.getEntity(originEntityName, originEntityId); | ||||||
|  |  | ||||||
|     // we're just executing an excerpt of the original frontend script in the backend context so we must |     // we're just executing an excerpt of the original frontend script in the backend context, so we must | ||||||
|     // override normal note's content and it's mime type / script environment |     // override normal note's content, and it's mime type / script environment | ||||||
|     const backendOverrideContent = `return (${script}\r\n)(${getParams(params)})`; |     const backendOverrideContent = `return (${script}\r\n)(${getParams(params)})`; | ||||||
|  |  | ||||||
|     const bundle = getScriptBundle(currentNote, true, null, [], backendOverrideContent); |     const bundle = getScriptBundle(currentNote, true, null, [], backendOverrideContent); | ||||||
|   | |||||||
| @@ -17,6 +17,11 @@ class NoteFlatTextExp extends Expression { | |||||||
|         const beccaService = require('../../../becca/becca_service'); |         const beccaService = require('../../../becca/becca_service'); | ||||||
|         const resultNoteSet = new NoteSet(); |         const resultNoteSet = new NoteSet(); | ||||||
|  |  | ||||||
|  |         /** | ||||||
|  |          * @param {BNote} note | ||||||
|  |          * @param {string[]} tokens | ||||||
|  |          * @param {string[]} path | ||||||
|  |          */ | ||||||
|         function searchDownThePath(note, tokens, path) { |         function searchDownThePath(note, tokens, path) { | ||||||
|             if (tokens.length === 0) { |             if (tokens.length === 0) { | ||||||
|                 const retPath = beccaService.getSomePath(note, path); |                 const retPath = beccaService.getSomePath(note, path); | ||||||
|   | |||||||
| @@ -230,6 +230,7 @@ function parseQueryToExpression(query, searchContext) { | |||||||
|  |  | ||||||
| /** | /** | ||||||
|  * @param {string} query |  * @param {string} query | ||||||
|  |  * @param {object} params - see SearchContext | ||||||
|  * @returns {BNote[]} |  * @returns {BNote[]} | ||||||
|  */ |  */ | ||||||
| function searchNotes(query, params = {}) { | function searchNotes(query, params = {}) { | ||||||
| @@ -299,7 +300,7 @@ function highlightSearchResults(searchResults, highlightedTokens) { | |||||||
|     // < and > are used for marking <small> and </small> |     // < and > are used for marking <small> and </small> | ||||||
|     highlightedTokens = highlightedTokens.map(token => token.replace('/[<\{\}]/g', '')); |     highlightedTokens = highlightedTokens.map(token => token.replace('/[<\{\}]/g', '')); | ||||||
|  |  | ||||||
|     // sort by the longest so we first highlight longest matches |     // sort by the longest, so we first highlight the longest matches | ||||||
|     highlightedTokens.sort((a, b) => a.length > b.length ? -1 : 1); |     highlightedTokens.sort((a, b) => a.length > b.length ? -1 : 1); | ||||||
|  |  | ||||||
|     for (const result of searchResults) { |     for (const result of searchResults) { | ||||||
|   | |||||||
| @@ -47,7 +47,7 @@ class SpacedUpdate { | |||||||
|             this.changed = false; |             this.changed = false; | ||||||
|         } |         } | ||||||
|         else { |         else { | ||||||
|             // update not triggered but changes are still pending so we need to schedule another check |             // update not triggered but changes are still pending, so we need to schedule another check | ||||||
|             this.scheduleUpdate(); |             this.scheduleUpdate(); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|   | |||||||
| @@ -116,7 +116,7 @@ async function doLogin() { | |||||||
|  |  | ||||||
|     const lastSyncedPull = getLastSyncedPull(); |     const lastSyncedPull = getLastSyncedPull(); | ||||||
|  |  | ||||||
|     // this is important in a scenario where we setup the sync by manually copying the document |     // this is important in a scenario where we set up the sync by manually copying the document | ||||||
|     // lastSyncedPull then could be pretty off for the newly cloned client |     // lastSyncedPull then could be pretty off for the newly cloned client | ||||||
|     if (lastSyncedPull > resp.maxEntityChangeId) { |     if (lastSyncedPull > resp.maxEntityChangeId) { | ||||||
|         log.info(`Lowering last synced pull from ${lastSyncedPull} to ${resp.maxEntityChangeId}`); |         log.info(`Lowering last synced pull from ${lastSyncedPull} to ${resp.maxEntityChangeId}`); | ||||||
|   | |||||||
| @@ -21,7 +21,7 @@ module.exports = { | |||||||
|     isSyncSetup: () => { |     isSyncSetup: () => { | ||||||
|         const syncServerHost = get('syncServerHost'); |         const syncServerHost = get('syncServerHost'); | ||||||
|  |  | ||||||
|         // special value "disabled" is here to support use case where document is configured with sync server |         // special value "disabled" is here to support use case where document is configured with sync server, | ||||||
|         // and we need to override it with config from config.ini |         // and we need to override it with config from config.ini | ||||||
|         return !!syncServerHost && syncServerHost !== 'disabled'; |         return !!syncServerHost && syncServerHost !== 'disabled'; | ||||||
|     }, |     }, | ||||||
|   | |||||||
| @@ -85,7 +85,7 @@ function getExistingBranch(parentNoteId, childNoteId) { | |||||||
| function checkTreeCycle(parentNoteId, childNoteId) { | function checkTreeCycle(parentNoteId, childNoteId) { | ||||||
|     const subtreeNoteIds = []; |     const subtreeNoteIds = []; | ||||||
|  |  | ||||||
|     // we'll load the whole sub tree - because the cycle can start in one of the notes in the sub tree |     // we'll load the whole subtree - because the cycle can start in one of the notes in the subtree | ||||||
|     loadSubtreeNoteIds(childNoteId, subtreeNoteIds); |     loadSubtreeNoteIds(childNoteId, subtreeNoteIds); | ||||||
|  |  | ||||||
|     function checkTreeCycleInner(parentNoteId) { |     function checkTreeCycleInner(parentNoteId) { | ||||||
|   | |||||||
| @@ -29,6 +29,9 @@ function toBase64(plainText) { | |||||||
|     return Buffer.from(plainText).toString('base64'); |     return Buffer.from(plainText).toString('base64'); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * @returns {Buffer} | ||||||
|  |  */ | ||||||
| function fromBase64(encodedText) { | function fromBase64(encodedText) { | ||||||
|     return Buffer.from(encodedText, 'base64'); |     return Buffer.from(encodedText, 'base64'); | ||||||
| } | } | ||||||
|   | |||||||
| @@ -48,7 +48,7 @@ async function createMainWindow(app) { | |||||||
|     const windowStateKeeper = require('electron-window-state'); // should not be statically imported |     const windowStateKeeper = require('electron-window-state'); // should not be statically imported | ||||||
|  |  | ||||||
|     const mainWindowState = windowStateKeeper({ |     const mainWindowState = windowStateKeeper({ | ||||||
|         // default window width & height so it's usable on 1600 * 900 display (including some extra panels etc.) |         // default window width & height, so it's usable on 1600 * 900 display (including some extra panels etc.) | ||||||
|         defaultWidth: 1200, |         defaultWidth: 1200, | ||||||
|         defaultHeight: 800 |         defaultHeight: 800 | ||||||
|     }); |     }); | ||||||
|   | |||||||
| @@ -1,7 +1,7 @@ | |||||||
| /** | /** | ||||||
|  * this is used as a "standalone js" file and required by a shared note directly via script-tags |  * this is used as a "standalone js" file and required by a shared note directly via script-tags | ||||||
|  * |  * | ||||||
|  * data input comes via window variable as follow |  * data input comes via window variable as follows | ||||||
|  * const {elements, appState, files} = window.triliumExcalidraw; |  * const {elements, appState, files} = window.triliumExcalidraw; | ||||||
|  */ |  */ | ||||||
|  |  | ||||||
|   | |||||||
| @@ -3,13 +3,12 @@ | |||||||
|  * will create 1000 new notes and some clones into a current document.db |  * will create 1000 new notes and some clones into a current document.db | ||||||
|  */ |  */ | ||||||
|  |  | ||||||
| require('../entities/entity_constructor'); | require('../becca/entity_constructor'); | ||||||
| const sqlInit = require('../services/sql_init'); | const sqlInit = require('../services/sql_init'); | ||||||
| const noteService = require('../services/notes'); | const noteService = require('../services/notes'); | ||||||
| const attributeService = require('../services/attributes'); | const attributeService = require('../services/attributes'); | ||||||
| const cls = require('../services/cls'); | const cls = require('../services/cls'); | ||||||
| const cloningService = require('../services/cloning'); | const cloningService = require('../services/cloning'); | ||||||
| const noteRevisionService = require('../services/note_revisions'); |  | ||||||
| const loremIpsum = require('lorem-ipsum').loremIpsum; | const loremIpsum = require('lorem-ipsum').loremIpsum; | ||||||
|  |  | ||||||
| const noteCount = parseInt(process.argv[2]); | const noteCount = parseInt(process.argv[2]); | ||||||
| @@ -47,7 +46,7 @@ async function start() { | |||||||
|             format: 'html' |             format: 'html' | ||||||
|         }); |         }); | ||||||
|  |  | ||||||
|         const {note} = await noteService.createNewNote({ |         const {note} = noteService.createNewNote({ | ||||||
|             parentNoteId: getRandomNoteId(), |             parentNoteId: getRandomNoteId(), | ||||||
|             title, |             title, | ||||||
|             content, |             content, | ||||||
| @@ -83,7 +82,7 @@ async function start() { | |||||||
|             isInheritable: Math.random() > 0.1 // 10% are inheritable |             isInheritable: Math.random() > 0.1 // 10% are inheritable | ||||||
|         }); |         }); | ||||||
|  |  | ||||||
|         noteRevisionService.createNoteRevision(await becca.getNote(getRandomNoteId())); |         note.saveNoteRevision(); | ||||||
|  |  | ||||||
|         notes.push(note.noteId); |         notes.push(note.noteId); | ||||||
|     } |     } | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user