mirror of
				https://github.com/zadam/trilium.git
				synced 2025-10-31 02:16:05 +01:00 
			
		
		
		
	typos
This commit is contained in:
		
							
								
								
									
										1
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										1
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							| @@ -5,7 +5,6 @@ | |||||||
|   "requires": true, |   "requires": true, | ||||||
|   "packages": { |   "packages": { | ||||||
|     "": { |     "": { | ||||||
|       "name": "trilium", |  | ||||||
|       "version": "0.60.4", |       "version": "0.60.4", | ||||||
|       "hasInstallScript": true, |       "hasInstallScript": true, | ||||||
|       "license": "AGPL-3.0-only", |       "license": "AGPL-3.0-only", | ||||||
|   | |||||||
| @@ -105,7 +105,7 @@ eventService.subscribeBeccaLoader(eventService.ENTITY_CHANGED,  ({entityName, en | |||||||
|  * |  * | ||||||
|  * @param entityName |  * @param entityName | ||||||
|  * @param entityRow - can be a becca entity (change comes from this trilium instance) or just a row (from sync). |  * @param entityRow - can be a becca entity (change comes from this trilium instance) or just a row (from sync). | ||||||
|  *                    Should be therefore treated as a row. |  *                    It should be therefore treated as a row. | ||||||
|  */ |  */ | ||||||
| function postProcessEntityUpdate(entityName, entityRow) { | function postProcessEntityUpdate(entityName, entityRow) { | ||||||
|     if (entityName === 'notes') { |     if (entityName === 'notes') { | ||||||
| @@ -188,7 +188,7 @@ function branchUpdated(branchRow) { | |||||||
|         childNote.sortParents(); |         childNote.sortParents(); | ||||||
|  |  | ||||||
|         // notes in the subtree can get new inherited attributes |         // notes in the subtree can get new inherited attributes | ||||||
|         // this is in theory needed upon branch creation, but there's no create event for sync changes |         // this is in theory needed upon branch creation, but there's no "create" event for sync changes | ||||||
|         childNote.invalidateSubTree(); |         childNote.invalidateSubTree(); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -18,7 +18,7 @@ const LABEL = 'label'; | |||||||
| const RELATION = 'relation'; | const RELATION = 'relation'; | ||||||
|  |  | ||||||
| /** | /** | ||||||
|  * Trilium's main entity which can represent text note, image, code note, file attachment etc. |  * Trilium's main entity, which can represent text note, image, code note, file attachment etc. | ||||||
|  * |  * | ||||||
|  * @extends AbstractBeccaEntity |  * @extends AbstractBeccaEntity | ||||||
|  */ |  */ | ||||||
| @@ -123,7 +123,7 @@ class BNote extends AbstractBeccaEntity { | |||||||
|          * @private */ |          * @private */ | ||||||
|         this.__ancestorCache = null; |         this.__ancestorCache = null; | ||||||
|  |  | ||||||
|         // following attributes are filled during searching from database |         // following attributes are filled during searching in the database | ||||||
|  |  | ||||||
|         /** |         /** | ||||||
|          * size of the content in bytes |          * size of the content in bytes | ||||||
| @@ -566,7 +566,8 @@ class BNote extends AbstractBeccaEntity { | |||||||
|     /** |     /** | ||||||
|      * @param {string} type - attribute type (label, relation, etc.) |      * @param {string} type - attribute type (label, relation, etc.) | ||||||
|      * @param {string} name - attribute name |      * @param {string} name - attribute name | ||||||
|      * @returns {BAttribute} attribute of given type and name. If there's more such attributes, first is  returned. Returns null if there's no such attribute belonging to this note. |      * @returns {BAttribute} attribute of the given type and name. If there are more such attributes, first is returned. | ||||||
|  |      *                       Returns null if there's no such attribute belonging to this note. | ||||||
|      */ |      */ | ||||||
|     getAttribute(type, name) { |     getAttribute(type, name) { | ||||||
|         const attributes = this.getAttributes(); |         const attributes = this.getAttributes(); | ||||||
| @@ -688,7 +689,7 @@ class BNote extends AbstractBeccaEntity { | |||||||
|     areAllNotePathsArchived() { |     areAllNotePathsArchived() { | ||||||
|         // there's a slight difference between note being itself archived and all its note paths being archived |         // there's a slight difference between note being itself archived and all its note paths being archived | ||||||
|         // - note is archived when it itself has an archived label or inherits it |         // - note is archived when it itself has an archived label or inherits it | ||||||
|         // - note does not have or inherit archived label, but each note paths contains a note with (non-inheritable) |         // - note does not have or inherit archived label, but each note path contains a note with (non-inheritable) | ||||||
|         //   archived label |         //   archived label | ||||||
|  |  | ||||||
|         const bestNotePathRecord = this.getSortedNotePathRecords()[0]; |         const bestNotePathRecord = this.getSortedNotePathRecords()[0]; | ||||||
| @@ -1093,7 +1094,7 @@ class BNote extends AbstractBeccaEntity { | |||||||
|     /** @returns {BAttachment[]} */ |     /** @returns {BAttachment[]} */ | ||||||
|     getAttachments(opts = {}) { |     getAttachments(opts = {}) { | ||||||
|         opts.includeContentLength = !!opts.includeContentLength; |         opts.includeContentLength = !!opts.includeContentLength; | ||||||
|         // from testing it looks like calculating length does not make a difference in performance even on large-ish DB |         // from testing, it looks like calculating length does not make a difference in performance even on large-ish DB | ||||||
|         // given that we're always fetching attachments only for a specific note, we might just do it always |         // given that we're always fetching attachments only for a specific note, we might just do it always | ||||||
|  |  | ||||||
|         const query = opts.includeContentLength |         const query = opts.includeContentLength | ||||||
| @@ -1148,7 +1149,7 @@ class BNote extends AbstractBeccaEntity { | |||||||
|         const parentNotes = this.getParentNotes(); |         const parentNotes = this.getParentNotes(); | ||||||
|  |  | ||||||
|         const notePaths = parentNotes.length === 1 |         const notePaths = parentNotes.length === 1 | ||||||
|             ? parentNotes[0].getAllNotePaths() // optimization for most common case |             ? parentNotes[0].getAllNotePaths() // optimization for the most common case | ||||||
|             : parentNotes.flatMap(parentNote => parentNote.getAllNotePaths()); |             : parentNotes.flatMap(parentNote => parentNote.getAllNotePaths()); | ||||||
|  |  | ||||||
|         for (const notePath of notePaths) { |         for (const notePath of notePaths) { | ||||||
| @@ -1188,7 +1189,7 @@ class BNote extends AbstractBeccaEntity { | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * Returns note path considered to be the "best" |      * Returns a note path considered to be the "best" | ||||||
|      * |      * | ||||||
|      * @param {string} [hoistedNoteId='root'] |      * @param {string} [hoistedNoteId='root'] | ||||||
|      * @return {string[]} array of noteIds constituting the particular note path |      * @return {string[]} array of noteIds constituting the particular note path | ||||||
| @@ -1198,7 +1199,7 @@ class BNote extends AbstractBeccaEntity { | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * Returns note path considered to be the "best" |      * Returns a note path considered to be the "best" | ||||||
|      * |      * | ||||||
|      * @param {string} [hoistedNoteId='root'] |      * @param {string} [hoistedNoteId='root'] | ||||||
|      * @return {string} serialized note path (e.g. 'root/a1h315/js725h') |      * @return {string} serialized note path (e.g. 'root/a1h315/js725h') | ||||||
| @@ -1338,7 +1339,7 @@ class BNote extends AbstractBeccaEntity { | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * Based on enabled, attribute is either set or removed. |      * Based on enabled, the attribute is either set or removed. | ||||||
|      * |      * | ||||||
|      * @param {string} type - attribute type ('relation', 'label' etc.) |      * @param {string} type - attribute type ('relation', 'label' etc.) | ||||||
|      * @param {boolean} enabled - toggle On or Off |      * @param {boolean} enabled - toggle On or Off | ||||||
| @@ -1397,7 +1398,7 @@ class BNote extends AbstractBeccaEntity { | |||||||
|     removeLabel(name, value) { return this.removeAttribute(LABEL, name, value); } |     removeLabel(name, value) { return this.removeAttribute(LABEL, name, value); } | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * Remove relation name-value pair, if it exists. |      * Remove the relation name-value pair, if it exists. | ||||||
|      * |      * | ||||||
|      * @param {string} name - relation name |      * @param {string} name - relation name | ||||||
|      * @param {string} [value] - relation value (noteId) |      * @param {string} [value] - relation value (noteId) | ||||||
| @@ -1455,16 +1456,16 @@ class BNote extends AbstractBeccaEntity { | |||||||
|      * - it has a relation from its parent note |      * - it has a relation from its parent note | ||||||
|      * - it has no children |      * - it has no children | ||||||
|      * - it has no clones |      * - it has no clones | ||||||
|      * - parent is of type text |      * - the parent is of type text | ||||||
|      * - both notes are either unprotected or user is in protected session |      * - both notes are either unprotected or user is in protected session | ||||||
|      * |      * | ||||||
|      * Currently, works only for image notes. |      * Currently, works only for image notes. | ||||||
|      * |      * | ||||||
|      * In future this functionality might get more generic and some of the requirements relaxed. |      * In future, this functionality might get more generic and some of the requirements relaxed. | ||||||
|      * |      * | ||||||
|      * @params {Object} [opts] |      * @params {Object} [opts] | ||||||
|      * @params {bolean} [opts.force=false} it is envisioned that user can force the conversion even if some conditions |      * @params {bolean} [opts.force=false} it is envisioned that user can force the conversion even if some conditions | ||||||
|      *                                     are not satisfied (e.g. relation to parent doesn't exist). |      *                                     are not satisfied (e.g., relation to parent doesn't exist). | ||||||
|      * |      * | ||||||
|      * @returns {BAttachment|null} - null if note is not eligible for conversion |      * @returns {BAttachment|null} - null if note is not eligible for conversion | ||||||
|      */ |      */ | ||||||
| @@ -1549,7 +1550,7 @@ class BNote extends AbstractBeccaEntity { | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     get isDeleted() { |     get isDeleted() { | ||||||
|         // isBeingDeleted is relevant only in the transition period when the deletion process have begun, but not yet |         // isBeingDeleted is relevant only in the transition period when the deletion process has begun, but not yet | ||||||
|         // finished (note is still in becca) |         // finished (note is still in becca) | ||||||
|         return !(this.noteId in this.becca.notes) || this.isBeingDeleted; |         return !(this.noteId in this.becca.notes) || this.isBeingDeleted; | ||||||
|     } |     } | ||||||
|   | |||||||
| @@ -4,7 +4,7 @@ const dateUtils = require('../../services/date_utils'); | |||||||
| const AbstractBeccaEntity = require("./abstract_becca_entity"); | const AbstractBeccaEntity = require("./abstract_becca_entity"); | ||||||
|  |  | ||||||
| /** | /** | ||||||
|  * Option represents name-value pair, either directly configurable by the user or some system property. |  * Option represents a name-value pair, either directly configurable by the user or some system property. | ||||||
|  * |  * | ||||||
|  * @extends AbstractBeccaEntity |  * @extends AbstractBeccaEntity | ||||||
|  */ |  */ | ||||||
|   | |||||||
| @@ -132,7 +132,7 @@ function buildRewardMap(note) { | |||||||
|             } |             } | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         // title is the top with weight 1 so smaller headings will have lower weight |         // the title is the top with weight 1 so smaller headings will have lower weight | ||||||
|  |  | ||||||
|         // technically H1 is not supported, but for the case it's present let's weigh it just as H2 |         // technically H1 is not supported, but for the case it's present let's weigh it just as H2 | ||||||
|         addHeadingsToRewardMap("h1", 0.9); |         addHeadingsToRewardMap("h1", 0.9); | ||||||
| @@ -260,7 +260,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 lowered |         // 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); | ||||||
|  |  | ||||||
| @@ -364,7 +364,7 @@ async function findSimilarNotes(noteId) { | |||||||
|         } |         } | ||||||
|  |  | ||||||
|         /** |         /** | ||||||
|          * We want to improve standing of notes which have been created in similar time to each other since |          * We want to improve the standing of notes which have been created in similar time to each other since | ||||||
|          * there's a good chance they are related. |          * there's a good chance they are related. | ||||||
|          * |          * | ||||||
|          * But there's an exception - if they were created really close to each other (withing few seconds) then |          * But there's an exception - if they were created really close to each other (withing few seconds) then | ||||||
| @@ -386,7 +386,7 @@ async function findSimilarNotes(noteId) { | |||||||
|                     console.log("Adding reward for same day of creation"); |                     console.log("Adding reward for same day of creation"); | ||||||
|                 } |                 } | ||||||
|  |  | ||||||
|                 // smaller bonus when outside of the window but within same date |                 // smaller bonus when outside of the window but within the same date | ||||||
|                 score += 0.5; |                 score += 0.5; | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|   | |||||||
| @@ -107,7 +107,7 @@ class AppContext extends Component { | |||||||
|             } |             } | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         // this might hint at error but sometimes this is used by components which are at different places |         // this might hint at error, but sometimes this is used by components which are at different places | ||||||
|         // in the component tree to communicate with each other |         // in the component tree to communicate with each other | ||||||
|         console.debug(`Unhandled command ${name}, converting to event.`); |         console.debug(`Unhandled command ${name}, converting to event.`); | ||||||
|  |  | ||||||
|   | |||||||
| @@ -323,7 +323,7 @@ export default class TabManager extends Component { | |||||||
|  |  | ||||||
|         if (notePath) { |         if (notePath) { | ||||||
|             await noteContext.setNote(notePath, { |             await noteContext.setNote(notePath, { | ||||||
|                 // if activate is false then send normal noteSwitched event |                 // if activate is false, then send normal noteSwitched event | ||||||
|                 triggerSwitchEvent: !activate, |                 triggerSwitchEvent: !activate, | ||||||
|                 viewScope: viewScope |                 viewScope: viewScope | ||||||
|             }); |             }); | ||||||
| @@ -378,7 +378,7 @@ export default class TabManager extends Component { | |||||||
|      * @returns {Promise<boolean>} true if note context has been removed, false otherwise |      * @returns {Promise<boolean>} true if note context has been removed, false otherwise | ||||||
|      */ |      */ | ||||||
|     async removeNoteContext(ntxId) { |     async removeNoteContext(ntxId) { | ||||||
|         // removing note context is async process which can take some time, if users presses CTRL-W quickly, two |         // removing note context is an async process which can take some time, if users presses CTRL-W quickly, two | ||||||
|         // close events could interleave which would then lead to attempting to activate already removed context. |         // close events could interleave which would then lead to attempting to activate already removed context. | ||||||
|         return await this.mutex.runExclusively(async () => { |         return await this.mutex.runExclusively(async () => { | ||||||
|             let noteContextToRemove; |             let noteContextToRemove; | ||||||
|   | |||||||
| @@ -6,4 +6,4 @@ | |||||||
|     <li><code>keyboardShortcut</code> - optional, pressing the keyboard shortcut will open the note</li> |     <li><code>keyboardShortcut</code> - optional, pressing the keyboard shortcut will open the note</li> | ||||||
| </ol> | </ol> | ||||||
|  |  | ||||||
| <p>Launchbar displays the title / icon from the launcher which does not necessarily mirror those of the target note.</p> | <p>Launchbar displays the title / icon from the launcher, which does not necessarily mirror those of the target note.</p> | ||||||
|   | |||||||
| @@ -44,7 +44,7 @@ class FBranch { | |||||||
|         return this.froca.getNote(this.parentNoteId); |         return this.froca.getNote(this.parentNoteId); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /** @returns {boolean} true if it's top level, meaning its parent is root note */ |     /** @returns {boolean} true if it's top level, meaning its parent is the root note */ | ||||||
|     isTopLevel() { |     isTopLevel() { | ||||||
|         return this.parentNoteId === 'root'; |         return this.parentNoteId === 'root'; | ||||||
|     } |     } | ||||||
|   | |||||||
| @@ -23,11 +23,11 @@ class ContextMenu { | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     positionMenu() { |     positionMenu() { | ||||||
|         // code below tries to detect when dropdown would overflow from page |         // the 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 the 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 the 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 the context menu by relative to cursor, see #3157 | ||||||
|  |  | ||||||
|         const clientHeight = document.documentElement.clientHeight; |         const clientHeight = document.documentElement.clientHeight; | ||||||
|         const clientWidth = document.documentElement.clientWidth; |         const clientWidth = document.documentElement.clientWidth; | ||||||
| @@ -144,7 +144,7 @@ class ContextMenu { | |||||||
|  |  | ||||||
|     hide() { |     hide() { | ||||||
|         // this date checking comes from change in FF66 - https://github.com/zadam/trilium/issues/468 |         // this date checking comes from change in FF66 - https://github.com/zadam/trilium/issues/468 | ||||||
|         // "contextmenu" event also triggers "click" event which depending on the timing can close just opened context menu |         // "contextmenu" event also triggers "click" event which depending on the timing can close the just opened context menu | ||||||
|         // we might filter out right clicks, but then it's better if even right clicks close the context menu |         // we might filter out right clicks, but then it's better if even right clicks close the context menu | ||||||
|         if (Date.now() - this.dateContextMenuOpenedMs > 300) { |         if (Date.now() - this.dateContextMenuOpenedMs > 300) { | ||||||
|             // seems like if we hide the menu immediately, some clicks can get propagated to the underlying component |             // seems like if we hide the menu immediately, some clicks can get propagated to the underlying component | ||||||
|   | |||||||
| @@ -35,7 +35,7 @@ export default class TreeContextMenu { | |||||||
|         const isHoisted = note.noteId === appContext.tabManager.getActiveContext().hoistedNoteId; |         const isHoisted = note.noteId === appContext.tabManager.getActiveContext().hoistedNoteId; | ||||||
|         const parentNote = isNotRoot ? await froca.getNote(branch.parentNoteId) : null; |         const parentNote = isNotRoot ? await froca.getNote(branch.parentNoteId) : null; | ||||||
|  |  | ||||||
|         // some actions don't support multi-note, so they are disabled when notes are selected |         // some actions don't support multi-note, so they are disabled when notes are selected, | ||||||
|         // the only exception is when the only selected note is the one that was right-clicked, then |         // the only exception is when the only selected note is the one that was right-clicked, then | ||||||
|         // it's clear what the user meant to do. |         // it's clear what the user meant to do. | ||||||
|         const selNodes = this.treeWidget.getSelectedNodes(); |         const selNodes = this.treeWidget.getSelectedNodes(); | ||||||
|   | |||||||
| @@ -59,7 +59,7 @@ async function processEntityChanges(entityChanges) { | |||||||
|     // froca is supposed to contain all notes currently being visible to the users in the tree / otherwise being processed |     // froca is supposed to contain all notes currently being visible to the users in the tree / otherwise being processed | ||||||
|     // and their complete "ancestor relationship", so it's always possible to go up in the hierarchy towards the root. |     // and their complete "ancestor relationship", so it's always possible to go up in the hierarchy towards the root. | ||||||
|     // To this we count: standard parent-child relationships and template/inherit relations (attribute inheritance follows them). |     // To this we count: standard parent-child relationships and template/inherit relations (attribute inheritance follows them). | ||||||
|     // Here we watch for changes which might violate this principle - e.g. introduction of a new "inherit" relation might |     // Here we watch for changes which might violate this principle - e.g., an introduction of a new "inherit" relation might | ||||||
|     // mean we need to load the target of the relation (and then perhaps transitively the whole note path of this target). |     // mean we need to load the target of the relation (and then perhaps transitively the whole note path of this target). | ||||||
|     const missingNoteIds = []; |     const missingNoteIds = []; | ||||||
|  |  | ||||||
| @@ -157,7 +157,7 @@ async function processBranchChange(loadResults, ec) { | |||||||
|     if (childNote && !childNote.isRoot() && !parentNote) { |     if (childNote && !childNote.isRoot() && !parentNote) { | ||||||
|         // a branch cannot exist without the parent |         // a branch cannot exist without the parent | ||||||
|         // a note loaded into froca has to also contain all its ancestors, |         // a note loaded into froca has to also contain all its ancestors, | ||||||
|         // this problem happened e.g., in sharing where _share was hidden and thus not loaded |         // this problem happened, e.g., in sharing where _share was hidden and thus not loaded | ||||||
|         // sharing meant cloning into _share, which crashed because _share was not loaded |         // sharing meant cloning into _share, which crashed because _share was not loaded | ||||||
|         parentNote = await froca.getNote(ec.entity.parentNoteId); |         parentNote = await froca.getNote(ec.entity.parentNoteId); | ||||||
|     } |     } | ||||||
|   | |||||||
| @@ -185,7 +185,7 @@ class NoteListRenderer { | |||||||
|         this.viewType = parentNote.getLabelValue('viewType'); |         this.viewType = parentNote.getLabelValue('viewType'); | ||||||
|  |  | ||||||
|         if (!['list', 'grid'].includes(this.viewType)) { |         if (!['list', 'grid'].includes(this.viewType)) { | ||||||
|             // when not explicitly set decide based on note type |             // when not explicitly set, decide based on the note type | ||||||
|             this.viewType = parentNote.type === 'search' ? 'list' : 'grid'; |             this.viewType = parentNote.type === 'search' ? 'list' : 'grid'; | ||||||
|         } |         } | ||||||
|  |  | ||||||
| @@ -194,7 +194,7 @@ class NoteListRenderer { | |||||||
|         this.showNotePath = showNotePath; |         this.showNotePath = showNotePath; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /** @returns {Set<string>} list of noteIds included (images, included notes) into a parent note and which |     /** @returns {Set<string>} list of noteIds included (images, included notes) in the parent note and which | ||||||
|      *                        don't have to be shown in the note list. */ |      *                        don't have to be shown in the note list. */ | ||||||
|     getIncludedNoteIds() { |     getIncludedNoteIds() { | ||||||
|         const includedLinks = this.parentNote |         const includedLinks = this.parentNote | ||||||
|   | |||||||
| @@ -28,7 +28,7 @@ function enterProtectedSession() { | |||||||
|         dfd.resolve(false); |         dfd.resolve(false); | ||||||
|     } |     } | ||||||
|     else { |     else { | ||||||
|         // using deferred instead of promise because it allows resolving from outside |         // using deferred instead of promise because it allows resolving from the outside | ||||||
|         protectedSessionDeferred = dfd; |         protectedSessionDeferred = dfd; | ||||||
|  |  | ||||||
|         appContext.triggerCommand("showProtectedSessionPasswordDialog"); |         appContext.triggerCommand("showProtectedSessionPasswordDialog"); | ||||||
|   | |||||||
| @@ -17,7 +17,7 @@ function bindElShortcut($el, keyboardShortcut, handler, namespace = null) { | |||||||
|         if (namespace) { |         if (namespace) { | ||||||
|             eventName += `.${namespace}`; |             eventName += `.${namespace}`; | ||||||
|  |  | ||||||
|             // if there's a namespace then we replace the existing event handler with the new one |             // if there's a namespace, then we replace the existing event handler with the new one | ||||||
|             $el.off(eventName); |             $el.off(eventName); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -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 isn't triggered but changes are still pending, so we need to schedule another check | ||||||
|             this.scheduleUpdate(); |             this.scheduleUpdate(); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|   | |||||||
| @@ -111,7 +111,7 @@ async function resolveNotePathToSegments(notePath, hoistedNoteId = 'root', logEr | |||||||
|             throw new Error(`Did not find any path segments for '${note.toString()}', hoisted note '${hoistedNoteId}'`); |             throw new Error(`Did not find any path segments for '${note.toString()}', hoisted note '${hoistedNoteId}'`); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         // if there isn't actually any note path with hoisted note then return the original resolved note path |         // if there isn't actually any note path with hoisted note, then return the original resolved note path | ||||||
|         return bestNotePath.includes(hoistedNoteId) ? bestNotePath : effectivePathSegments; |         return bestNotePath.includes(hoistedNoteId) ? bestNotePath : effectivePathSegments; | ||||||
|     } |     } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -167,8 +167,8 @@ function isDesktop() { | |||||||
|         || (!window.glob?.device && !/Mobi/.test(navigator.userAgent)); |         || (!window.glob?.device && !/Mobi/.test(navigator.userAgent)); | ||||||
| } | } | ||||||
|  |  | ||||||
| // cookie code below works for simple use cases only - ASCII only | // the cookie code below works for simple use cases only - ASCII only | ||||||
| // not setting path so that cookies do not leak into other websites if multiplexed with reverse proxy | // not setting a path so that cookies do not leak into other websites if multiplexed with reverse proxy | ||||||
|  |  | ||||||
| function setCookie(name, value) { | function setCookie(name, value) { | ||||||
|     const date = new Date(Date.now() + 10 * 365 * 24 * 60 * 60 * 1000); |     const date = new Date(Date.now() + 10 * 365 * 24 * 60 * 60 * 1000); | ||||||
| @@ -396,7 +396,7 @@ function areObjectsEqual () { | |||||||
|             return x.toString() === y.toString(); |             return x.toString() === y.toString(); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         // At last checking prototypes as good as we can |         // At last, checking prototypes as good as we can | ||||||
|         if (!(x instanceof Object && y instanceof Object)) { |         if (!(x instanceof Object && y instanceof Object)) { | ||||||
|             return false; |             return false; | ||||||
|         } |         } | ||||||
|   | |||||||
| @@ -86,7 +86,7 @@ async function executeFrontendUpdate(entityChanges) { | |||||||
|         } |         } | ||||||
|  |  | ||||||
|         try { |         try { | ||||||
|             // it's my turn so start it up |             // it's my turn, so start it up | ||||||
|             consumeQueuePromise = consumeFrontendUpdateData(); |             consumeQueuePromise = consumeFrontendUpdateData(); | ||||||
|  |  | ||||||
|             await consumeQueuePromise; |             await consumeQueuePromise; | ||||||
| @@ -175,7 +175,7 @@ async function consumeFrontendUpdateData() { | |||||||
|             logError(`Encountered error ${e.message}: ${e.stack}, reloading frontend.`); |             logError(`Encountered error ${e.message}: ${e.stack}, reloading frontend.`); | ||||||
|  |  | ||||||
|             if (!glob.isDev && !options.is('debugModeEnabled')) { |             if (!glob.isDev && !options.is('debugModeEnabled')) { | ||||||
|                 // if there's an error in updating the frontend then the easy option to recover is to reload the frontend completely |                 // if there's an error in updating the frontend, then the easy option to recover is to reload the frontend completely | ||||||
|  |  | ||||||
|                 utils.reloadFrontendApp(); |                 utils.reloadFrontendApp(); | ||||||
|             } |             } | ||||||
|   | |||||||
| @@ -310,7 +310,7 @@ export default class AttributeEditorWidget extends NoteContextAwareWidget { | |||||||
|  |  | ||||||
|             this.$saveAttributesButton.fadeOut(); |             this.$saveAttributesButton.fadeOut(); | ||||||
|  |  | ||||||
|             // blink the attribute text to give visual hint that save has been executed |             // blink the attribute text to give a visual hint that save has been executed | ||||||
|             this.$editor.css('opacity', 0); |             this.$editor.css('opacity', 0); | ||||||
|  |  | ||||||
|             // revert back |             // revert back | ||||||
| @@ -387,7 +387,7 @@ export default class AttributeEditorWidget extends NoteContextAwareWidget { | |||||||
|                 parsedAttrs = attributeParser.lexAndParse(this.getPreprocessedData(), true); |                 parsedAttrs = attributeParser.lexAndParse(this.getPreprocessedData(), true); | ||||||
|             } |             } | ||||||
|             catch (e) { |             catch (e) { | ||||||
|                 // the input is incorrect because user messed up with it and now needs to fix it manually |                 // the input is incorrect because the user messed up with it and now needs to fix it manually | ||||||
|                 return null; |                 return null; | ||||||
|             } |             } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -4,7 +4,7 @@ import appContext from "../../../components/app_context.js"; | |||||||
| import utils from "../../../services/utils.js"; | import utils from "../../../services/utils.js"; | ||||||
| import linkContextMenuService from "../../../menus/link_context_menu.js"; | import linkContextMenuService from "../../../menus/link_context_menu.js"; | ||||||
|  |  | ||||||
| // we're intentionally displaying the launcher title and icon instead of the target | // we're intentionally displaying the launcher title and icon instead of the target, | ||||||
| // e.g. you want to make launchers to 2 mermaid diagrams which both have mermaid icon (ok), | // e.g. you want to make launchers to 2 mermaid diagrams which both have mermaid icon (ok), | ||||||
| // but on the launchpad you want them distinguishable. | // but on the launchpad you want them distinguishable. | ||||||
| // for titles, the note titles may follow a different scheme than maybe desirable on the launchpad | // for titles, the note titles may follow a different scheme than maybe desirable on the launchpad | ||||||
|   | |||||||
| @@ -67,7 +67,7 @@ export default class LauncherContainer extends FlexContainer { | |||||||
|  |  | ||||||
|     entitiesReloadedEvent({loadResults}) { |     entitiesReloadedEvent({loadResults}) { | ||||||
|         if (loadResults.getBranchRows().find(branch => froca.getNoteFromCache(branch.parentNoteId)?.isLaunchBarConfig())) { |         if (loadResults.getBranchRows().find(branch => froca.getNoteFromCache(branch.parentNoteId)?.isLaunchBarConfig())) { | ||||||
|             // changes in note placement requires reload of all launchers, all other changes are handled by individual |             // changes in note placement require reload of all launchers, all other changes are handled by individual | ||||||
|             // launchers |             // launchers | ||||||
|             this.load(); |             this.load(); | ||||||
|         } |         } | ||||||
|   | |||||||
| @@ -341,7 +341,7 @@ export default class RibbonContainer extends NoteContextAwareWidget { | |||||||
|     entitiesReloadedEvent({loadResults}) { |     entitiesReloadedEvent({loadResults}) { | ||||||
|         if (loadResults.isNoteReloaded(this.noteId) && this.lastNoteType !== this.note.type) { |         if (loadResults.isNoteReloaded(this.noteId) && this.lastNoteType !== this.note.type) { | ||||||
|             // note type influences the list of available ribbon tabs the most |             // note type influences the list of available ribbon tabs the most | ||||||
|             // check for type is so that we don't update on each title rename |             // check for the type is so that we don't update on each title rename | ||||||
|             this.lastNoteType = this.note.type; |             this.lastNoteType = this.note.type; | ||||||
|  |  | ||||||
|             this.refresh(); |             this.refresh(); | ||||||
|   | |||||||
| @@ -20,9 +20,9 @@ export default class RightPaneContainer extends FlexContainer { | |||||||
|         const promise = super.handleEventInChildren(name, data); |         const promise = super.handleEventInChildren(name, data); | ||||||
|  |  | ||||||
|         if (['activeContextChanged', 'noteSwitchedAndActivated', 'noteSwitched'].includes(name)) { |         if (['activeContextChanged', 'noteSwitchedAndActivated', 'noteSwitched'].includes(name)) { | ||||||
|             // right pane is displayed only if some child widget is active |             // the right pane is displayed only if some child widget is active, | ||||||
|             // we'll reevaluate the visibility based on events which are probable to cause visibility change |             // we'll reevaluate the visibility based on events which are probable to cause visibility change | ||||||
|             // but these events needs to be finished and only then we check |             // but these events need to be finished and only then we check | ||||||
|             if (promise) { |             if (promise) { | ||||||
|                 promise.then(() => this.reEvaluateRightPaneVisibilityCommand()); |                 promise.then(() => this.reEvaluateRightPaneVisibilityCommand()); | ||||||
|             } |             } | ||||||
|   | |||||||
| @@ -157,7 +157,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) { | ||||||
|         if (['noteSwitched', 'noteSwitchedAndActivated'].includes(name)) { |         if (['noteSwitched', 'noteSwitchedAndActivated'].includes(name)) { | ||||||
|   | |||||||
| @@ -153,7 +153,7 @@ export default class BulkActionsDialog extends BasicWidget { | |||||||
|             && row.noteId === '_bulkAction' |             && row.noteId === '_bulkAction' | ||||||
|             && row.isDeleted)) { |             && row.isDeleted)) { | ||||||
|  |  | ||||||
|             // this may be triggered from e.g. sync without open widget, then no need to refresh the widget |             // this may be triggered from e.g., sync without open widget, then no need to refresh the widget | ||||||
|             if (this.selectedOrActiveNoteIds && this.$widget.is(":visible")) { |             if (this.selectedOrActiveNoteIds && this.$widget.is(":visible")) { | ||||||
|                 this.refresh(); |                 this.refresh(); | ||||||
|             } |             } | ||||||
|   | |||||||
| @@ -84,7 +84,7 @@ export default class JumpToNoteDialog extends BasicWidget { | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     showInFullText(e) { |     showInFullText(e) { | ||||||
|         // stop from propagating upwards (dangerous especially with ctrl+enter executable javascript notes) |         // stop from propagating upwards (dangerous, especially with ctrl+enter executable javascript notes) | ||||||
|         e.preventDefault(); |         e.preventDefault(); | ||||||
|         e.stopPropagation(); |         e.stopPropagation(); | ||||||
|  |  | ||||||
|   | |||||||
| @@ -246,8 +246,8 @@ export default class RevisionsDialog extends BasicWidget { | |||||||
|             this.$content.html($("<pre>").text(fullRevision.content)); |             this.$content.html($("<pre>").text(fullRevision.content)); | ||||||
|         } 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 copy this |                 // the 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 an 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 uploaded as a new note | ||||||
|                 .attr("src", `data:${fullRevision.mime};base64,${fullRevision.content}`) |                 .attr("src", `data:${fullRevision.mime};base64,${fullRevision.content}`) | ||||||
|                 .css("max-width", "100%") |                 .css("max-width", "100%") | ||||||
|                 .css("max-height", "100%")); |                 .css("max-height", "100%")); | ||||||
|   | |||||||
| @@ -73,7 +73,7 @@ export default class NoteListWidget extends NoteContextAwareWidget { | |||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * We have this event so that we evaluate intersection only after note detail is loaded. |      * We have this event so that we evaluate intersection only after note detail is loaded. | ||||||
|      * If it's evaluated before note detail then it's clearly intersected (visible) although after note detail load |      * If it's evaluated before note detail, then it's clearly intersected (visible) although after note detail load | ||||||
|      * it is not intersected (visible) anymore. |      * it is not intersected (visible) anymore. | ||||||
|      */ |      */ | ||||||
|     noteDetailRefreshedEvent({ntxId}) { |     noteDetailRefreshedEvent({ntxId}) { | ||||||
|   | |||||||
| @@ -171,7 +171,7 @@ export default class NoteTreeWidget extends NoteContextAwareWidget { | |||||||
|             this.triggerCommand('hoistNote', {noteId: node.data.noteId}); |             this.triggerCommand('hoistNote', {noteId: node.data.noteId}); | ||||||
|         }); |         }); | ||||||
|  |  | ||||||
|         // fancytree doesn't support middle click so this is a way to support it |         // fancytree doesn't support middle click, so this is a way to support it | ||||||
|         this.$tree.on('mousedown', '.fancytree-title', e => { |         this.$tree.on('mousedown', '.fancytree-title', e => { | ||||||
|             if (e.which === 2) { |             if (e.which === 2) { | ||||||
|                 const node = $.ui.fancytree.getNode(e); |                 const node = $.ui.fancytree.getNode(e); | ||||||
| @@ -353,7 +353,7 @@ export default class NoteTreeWidget extends NoteContextAwareWidget { | |||||||
|                 } |                 } | ||||||
|             }, |             }, | ||||||
|             beforeActivate: (event, {node}) => { |             beforeActivate: (event, {node}) => { | ||||||
|                 // hidden subtree is hidden hackily, prevent activating it e.g. by keyboard |                 // hidden subtree is hidden hackily, prevent activating it, e.g. by keyboard | ||||||
|  |  | ||||||
|                 if (hoistedNoteService.getHoistedNoteId() === '_hidden') { |                 if (hoistedNoteService.getHoistedNoteId() === '_hidden') { | ||||||
|                     // if we're hoisted in hidden subtree, we want to avoid crossing to "visible" tree, |                     // if we're hoisted in hidden subtree, we want to avoid crossing to "visible" tree, | ||||||
| @@ -545,7 +545,7 @@ export default class NoteTreeWidget extends NoteContextAwareWidget { | |||||||
|                     const $unhoistButton = $('<span class="tree-item-button unhoist-button bx bx-door-open" title="Unhoist"></span>') |                     const $unhoistButton = $('<span class="tree-item-button unhoist-button bx bx-door-open" title="Unhoist"></span>') | ||||||
|                         .on("click", cancelClickPropagation); |                         .on("click", cancelClickPropagation); | ||||||
|  |  | ||||||
|                     // unhoist button is prepended since compared to other buttons this is not just convenience |                     // unhoist button is prepended since compared to other buttons, this is not just convenience | ||||||
|                     // on the mobile interface - it's the only way to unhoist |                     // on the mobile interface - it's the only way to unhoist | ||||||
|                     $span.prepend($unhoistButton); |                     $span.prepend($unhoistButton); | ||||||
|                 } |                 } | ||||||
| @@ -812,7 +812,7 @@ export default class NoteTreeWidget extends NoteContextAwareWidget { | |||||||
|         await this.batchUpdate(async () => { |         await this.batchUpdate(async () => { | ||||||
|             await node.load(true); |             await node.load(true); | ||||||
|  |  | ||||||
|             if (node.data.noteId !== hoistedNoteService.getHoistedNoteId()) { // hoisted note should be always expanded |             if (node.data.noteId !== hoistedNoteService.getHoistedNoteId()) { // hoisted note should always be expanded | ||||||
|                 await node.setExpanded(isExpanded, {noEvents: true, noAnimation: true}); |                 await node.setExpanded(isExpanded, {noEvents: true, noAnimation: true}); | ||||||
|             } |             } | ||||||
|         }); |         }); | ||||||
| @@ -905,7 +905,7 @@ export default class NoteTreeWidget extends NoteContextAwareWidget { | |||||||
|                         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 the 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; | ||||||
| @@ -922,7 +922,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 | ||||||
|  |  | ||||||
| @@ -1040,7 +1040,7 @@ export default class NoteTreeWidget extends NoteContextAwareWidget { | |||||||
|             } |             } | ||||||
|  |  | ||||||
|             /* |             /* | ||||||
|              * We're collapsing notes after period of inactivity to "cleanup" the tree - users rarely |              * We're collapsing notes after a period of inactivity to "cleanup" the tree - users rarely | ||||||
|              * collapse the notes and the tree becomes unusuably large. |              * collapse the notes and the tree becomes unusuably large. | ||||||
|              * Some context: https://github.com/zadam/trilium/issues/1192 |              * Some context: https://github.com/zadam/trilium/issues/1192 | ||||||
|              */ |              */ | ||||||
| @@ -1116,8 +1116,8 @@ export default class NoteTreeWidget extends NoteContextAwareWidget { | |||||||
|                 const note = froca.getNoteFromCache(ecAttr.noteId); |                 const note = froca.getNoteFromCache(ecAttr.noteId); | ||||||
|  |  | ||||||
|                 if (note && note.getChildNoteIds().includes(ecAttr.value)) { |                 if (note && note.getChildNoteIds().includes(ecAttr.value)) { | ||||||
|                     // there's new/deleted imageLink betwen note and its image child - which can show/hide |                     // there's a new /deleted imageLink betwen note and its image child - which can show/hide | ||||||
|                     // the image (if there is a imageLink relation between parent and child |                     // the image (if there is an imageLink relation between parent and child, | ||||||
|                     // then it is assumed to be "contained" in the note and thus does not have to be displayed in the tree) |                     // then it is assumed to be "contained" in the note and thus does not have to be displayed in the tree) | ||||||
|                     noteIdsToReload.add(ecAttr.noteId); |                     noteIdsToReload.add(ecAttr.noteId); | ||||||
|                 } |                 } | ||||||
| @@ -1213,7 +1213,7 @@ export default class NoteTreeWidget extends NoteContextAwareWidget { | |||||||
|             } |             } | ||||||
|         }); |         }); | ||||||
|  |  | ||||||
|         // for some reason node update cannot be in the batchUpdate() block (node is not re-rendered) |         // for some reason, node update cannot be in the batchUpdate() block (node is not re-rendered) | ||||||
|         for (const noteId of noteIdsToUpdate) { |         for (const noteId of noteIdsToUpdate) { | ||||||
|             for (const node of this.getNodesByNoteId(noteId)) { |             for (const node of this.getNodesByNoteId(noteId)) { | ||||||
|                 await this.updateNode(node); |                 await this.updateNode(node); | ||||||
| @@ -1239,7 +1239,7 @@ export default class NoteTreeWidget extends NoteContextAwareWidget { | |||||||
|                 // so we switch to the alternative of trying to find it by noteId |                 // so we switch to the alternative of trying to find it by noteId | ||||||
|                 const notesById = this.getNodesByNoteId(activeNoteId); |                 const notesById = this.getNodesByNoteId(activeNoteId); | ||||||
|  |  | ||||||
|                 // if there are multiple clones then we'd rather not activate any one |                 // if there are multiple clones, then we'd rather not activate anyone | ||||||
|                 node = notesById.length === 1 ? notesById[0] : null; |                 node = notesById.length === 1 ? notesById[0] : null; | ||||||
|             } |             } | ||||||
|  |  | ||||||
| @@ -1252,7 +1252,7 @@ export default class NoteTreeWidget extends NoteContextAwareWidget { | |||||||
|                 await node.setActive(true, {noEvents: true, noFocus: !activeNodeFocused}); |                 await node.setActive(true, {noEvents: true, noFocus: !activeNodeFocused}); | ||||||
|             } |             } | ||||||
|             else { |             else { | ||||||
|                 // this is used when original note has been deleted, and we want to move the focus to the note above/below |                 // this is used when the original note has been deleted, and we want to move the focus to the note above/below | ||||||
|                 node = await this.expandToNote(nextNotePath, false); |                 node = await this.expandToNote(nextNotePath, false); | ||||||
|  |  | ||||||
|                 if (node) { |                 if (node) { | ||||||
|   | |||||||
| @@ -64,7 +64,7 @@ export default class NoteTypeWidget extends NoteContextAwareWidget { | |||||||
|         this.$noteTypeButton.dropdown('hide'); |         this.$noteTypeButton.dropdown('hide'); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /** actual body is rendered lazily on note-type button click */ |     /** the actual body is rendered lazily on note-type button click */ | ||||||
|     async renderDropdown() { |     async renderDropdown() { | ||||||
|         this.$noteTypeDropdown.empty(); |         this.$noteTypeDropdown.empty(); | ||||||
|  |  | ||||||
|   | |||||||
| @@ -107,7 +107,7 @@ export default class PromotedAttributesWidget extends NoteContextAwareWidget { | |||||||
|             } |             } | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         // we replace the whole content in one step so there can't be any race conditions |         // we replace the whole content in one step, so there can't be any race conditions | ||||||
|         // (previously we saw promoted attributes doubling) |         // (previously we saw promoted attributes doubling) | ||||||
|         this.$container.empty().append(...$cells); |         this.$container.empty().append(...$cells); | ||||||
|         this.toggleInt(true); |         this.toggleInt(true); | ||||||
|   | |||||||
| @@ -47,8 +47,8 @@ export default class SearchString extends AbstractSearchOption { | |||||||
|  |  | ||||||
|         shortcutService.bindElShortcut(this.$searchString, 'return', async () => { |         shortcutService.bindElShortcut(this.$searchString, 'return', async () => { | ||||||
|             // this also in effect disallows new lines in query string. |             // this also in effect disallows new lines in query string. | ||||||
|             // on one hand this makes sense since search string is a label |             // on one hand, this makes sense since search string is a label | ||||||
|             // on the other hand it could be nice for structuring long search string. It's probably a niche case though. |             // on the other hand, it could be nice for structuring long search string. It's probably a niche case though. | ||||||
|             await this.spacedUpdate.updateNowIfNecessary(); |             await this.spacedUpdate.updateNowIfNecessary(); | ||||||
|  |  | ||||||
|             this.triggerCommand('refreshResults'); |             this.triggerCommand('refreshResults'); | ||||||
|   | |||||||
| @@ -336,7 +336,7 @@ export default class TabRowWidget extends BasicWidget { | |||||||
|             position += width + MARGIN_WIDTH; |             position += width + MARGIN_WIDTH; | ||||||
|         }); |         }); | ||||||
|  |  | ||||||
|         position -= MARGIN_WIDTH; // last margin should not be applied |         position -= MARGIN_WIDTH; // the last margin should not be applied | ||||||
|  |  | ||||||
|         const newTabPosition = position; |         const newTabPosition = position; | ||||||
|         const fillerPosition = position + 32; |         const fillerPosition = position + 32; | ||||||
| @@ -542,7 +542,7 @@ export default class TabRowWidget extends BasicWidget { | |||||||
|             }); |             }); | ||||||
|  |  | ||||||
|             draggabilly.on('dragMove', (event, pointer, moveVector) => { |             draggabilly.on('dragMove', (event, pointer, moveVector) => { | ||||||
|                 // Current index be computed within the event since it can change during the dragMove |                 // The current index be computed within the event since it can change during the dragMove | ||||||
|                 const tabEls = this.tabEls; |                 const tabEls = this.tabEls; | ||||||
|                 const currentIndex = tabEls.indexOf(tabEl); |                 const currentIndex = tabEls.indexOf(tabEl); | ||||||
|  |  | ||||||
|   | |||||||
| @@ -2,14 +2,14 @@ | |||||||
|  * Table of contents widget |  * Table of contents widget | ||||||
|  * (c) Antonio Tejada 2022 |  * (c) Antonio Tejada 2022 | ||||||
|  * |  * | ||||||
|  * By design there's no support for nonsensical or malformed constructs: |  * By design, there's no support for nonsensical or malformed constructs: | ||||||
|  * - headings inside elements (e.g. 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> | ||||||
|  * - etc. |  * - etc. | ||||||
|  * |  * | ||||||
|  * In those cases the generated TOC may be incorrect or the navigation may lead |  * In those cases, the generated TOC may be incorrect, or the navigation may lead | ||||||
|  * to the wrong heading (although what "right" means in those cases is not |  * to the wrong heading (although what "right" means in those cases is not | ||||||
|  * clear), but it won't crash. |  * clear), but it won't crash. | ||||||
|  */ |  */ | ||||||
|   | |||||||
| @@ -72,7 +72,7 @@ export default class EditableCodeTypeWidget extends TypeWidget { | |||||||
|         const blob = await this.note.getBlob(); |         const blob = await this.note.getBlob(); | ||||||
|  |  | ||||||
|         await this.spacedUpdate.allowUpdateWithoutChange(() => { |         await this.spacedUpdate.allowUpdateWithoutChange(() => { | ||||||
|             // CodeMirror breaks pretty badly on null, so even though it shouldn't happen (guarded by consistency check) |             // CodeMirror breaks pretty badly on null, so even though it shouldn't happen (guarded by a consistency check) | ||||||
|             // we provide fallback |             // we provide fallback | ||||||
|             this.codeEditor.setValue(blob.content || ""); |             this.codeEditor.setValue(blob.content || ""); | ||||||
|             this.codeEditor.clearHistory(); |             this.codeEditor.clearHistory(); | ||||||
|   | |||||||
| @@ -192,7 +192,7 @@ export default class EditableTextTypeWidget extends AbstractTextTypeWidget { | |||||||
|     getData() { |     getData() { | ||||||
|         const content = this.watchdog.editor.getData(); |         const content = this.watchdog.editor.getData(); | ||||||
|  |  | ||||||
|         // if content is only tags/whitespace (typically <p> </p>), then just make it empty |         // if content is only tags/whitespace (typically <p> </p>), then just make it empty, | ||||||
|         // this is important when setting a new note to code |         // this is important when setting a new note to code | ||||||
|         return { |         return { | ||||||
|             content: utils.isHtmlEmpty(content) ? '' : content |             content: utils.isHtmlEmpty(content) ? '' : content | ||||||
|   | |||||||
| @@ -91,7 +91,7 @@ export default class RelationMapTypeWidget extends TypeWidget { | |||||||
|             if (this.clipboard) { |             if (this.clipboard) { | ||||||
|                 let {x, y} = this.getMousePosition(event); |                 let {x, y} = this.getMousePosition(event); | ||||||
|  |  | ||||||
|                 // modifying position so that cursor is on the top-center of the box |                 // modifying position so that the cursor is on the top-center of the box | ||||||
|                 x -= 80; |                 x -= 80; | ||||||
|                 y -= 15; |                 y -= 15; | ||||||
|  |  | ||||||
| @@ -186,8 +186,8 @@ export default class RelationMapTypeWidget extends TypeWidget { | |||||||
|     async loadMapData() { |     async loadMapData() { | ||||||
|         this.mapData = { |         this.mapData = { | ||||||
|             notes: [], |             notes: [], | ||||||
|             // it is important to have this exact value here so that initial transform is same as this |             // it is important to have this exact value here so that initial transform is the same as this | ||||||
|             // which will guarantee note won't be saved on first conversion to relation map note type |             // which will guarantee note won't be saved on first conversion to the relation map note type | ||||||
|             // this keeps the principle that note type change doesn't destroy note content unless user |             // this keeps the principle that note type change doesn't destroy note content unless user | ||||||
|             // does some actual change |             // does some actual change | ||||||
|             transform: { |             transform: { | ||||||
| @@ -231,7 +231,7 @@ export default class RelationMapTypeWidget extends TypeWidget { | |||||||
|         // this is done at this point (after async operations) to reduce flicker to the minimum |         // this is done at this point (after async operations) to reduce flicker to the minimum | ||||||
|         this.jsPlumbInstance.deleteEveryEndpoint(); |         this.jsPlumbInstance.deleteEveryEndpoint(); | ||||||
|  |  | ||||||
|         // without this we still end up with note boxes remaining in the canvas |         // without this, we still end up with note boxes remaining in the canvas | ||||||
|         this.$relationMapContainer.empty(); |         this.$relationMapContainer.empty(); | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -305,7 +305,7 @@ export default class RelationMapTypeWidget extends TypeWidget { | |||||||
|             minZoom: 0.3, |             minZoom: 0.3, | ||||||
|             smoothScroll: false, |             smoothScroll: false, | ||||||
|             filterKey: function(e, dx, dy, dz) { |             filterKey: function(e, dx, dy, dz) { | ||||||
|                 // if ALT is pressed then panzoom should bubble the event up |                 // if ALT is pressed, then panzoom should bubble the event up | ||||||
|                 // this is to preserve ALT-LEFT, ALT-RIGHT navigation working |                 // this is to preserve ALT-LEFT, ALT-RIGHT navigation working | ||||||
|                 return e.altKey; |                 return e.altKey; | ||||||
|             } |             } | ||||||
|   | |||||||
| @@ -43,7 +43,7 @@ function moveBranchBeforeNote(req) { | |||||||
|  |  | ||||||
|     const originalBeforeNotePosition = beforeBranch.notePosition; |     const originalBeforeNotePosition = beforeBranch.notePosition; | ||||||
|  |  | ||||||
|     // we don't change utcDateModified so other changes are prioritized in case of conflict |     // we don't change utcDateModified, so other changes are prioritized in case of conflict | ||||||
|     // also we would have to sync all those modified branches otherwise hash checks would fail |     // also we would have to sync all those modified branches otherwise hash checks would fail | ||||||
|  |  | ||||||
|     sql.execute("UPDATE branches SET notePosition = notePosition + 10 WHERE parentNoteId = ? AND notePosition >= ? AND isDeleted = 0", |     sql.execute("UPDATE branches SET notePosition = notePosition + 10 WHERE parentNoteId = ? AND notePosition >= ? AND isDeleted = 0", | ||||||
| @@ -93,7 +93,7 @@ function moveBranchAfterNote(req) { | |||||||
|  |  | ||||||
|     const originalAfterNotePosition = afterNote.notePosition; |     const originalAfterNotePosition = afterNote.notePosition; | ||||||
|  |  | ||||||
|     // we don't change utcDateModified so other changes are prioritized in case of conflict |     // we don't change utcDateModified, so other changes are prioritized in case of conflict | ||||||
|     // also we would have to sync all those modified branches otherwise hash checks would fail |     // also we would have to sync all those modified branches otherwise hash checks would fail | ||||||
|     sql.execute("UPDATE branches SET notePosition = notePosition + 10 WHERE parentNoteId = ? AND notePosition > ? AND isDeleted = 0", |     sql.execute("UPDATE branches SET notePosition = notePosition + 10 WHERE parentNoteId = ? AND notePosition > ? AND isDeleted = 0", | ||||||
|         [afterNote.parentNoteId, originalAfterNotePosition]); |         [afterNote.parentNoteId, originalAfterNotePosition]); | ||||||
|   | |||||||
| @@ -97,7 +97,7 @@ async function importAttachmentsToNote(req) { | |||||||
|     const parentNote = becca.getNoteOrThrow(parentNoteId); |     const parentNote = becca.getNoteOrThrow(parentNoteId); | ||||||
|     const taskContext = TaskContext.getInstance(taskId, 'importAttachment', options); |     const taskContext = TaskContext.getInstance(taskId, 'importAttachment', options); | ||||||
|  |  | ||||||
|     // unlike in note import we let the events run, because a huge number of attachments is not likely |     // unlike in note import, we let the events run, because a huge number of attachments is not likely | ||||||
|  |  | ||||||
|     try { |     try { | ||||||
|         await singleImportService.importAttachment(taskContext, file, parentNote); |         await singleImportService.importAttachment(taskContext, file, parentNote); | ||||||
|   | |||||||
| @@ -92,13 +92,13 @@ function getNeighbors(note, depth) { | |||||||
|  |  | ||||||
| function getLinkMap(req) { | function getLinkMap(req) { | ||||||
|     const mapRootNote = becca.getNote(req.params.noteId); |     const mapRootNote = becca.getNote(req.params.noteId); | ||||||
|     // if the map root itself has exclude attribute (journal typically) then there wouldn't be anything to display, so |     // if the map root itself has "excludeFromNoteMap" attribute (journal typically) then there wouldn't be anything | ||||||
|     // we'll just ignore it |     // to display, so we'll just ignore it | ||||||
|     const ignoreExcludeFromNoteMap = mapRootNote.isLabelTruthy('excludeFromNoteMap'); |     const ignoreExcludeFromNoteMap = mapRootNote.isLabelTruthy('excludeFromNoteMap'); | ||||||
|     let unfilteredNotes; |     let unfilteredNotes; | ||||||
|  |  | ||||||
|     if (mapRootNote.type === 'search') { |     if (mapRootNote.type === 'search') { | ||||||
|         // for search notes we want to consider the direct search results only without the descendants |         // for search notes, we want to consider the direct search results only without the descendants | ||||||
|         unfilteredNotes = mapRootNote.getSearchResultNotes(); |         unfilteredNotes = mapRootNote.getSearchResultNotes(); | ||||||
|     } else { |     } else { | ||||||
|         unfilteredNotes = mapRootNote.getSubtree({ |         unfilteredNotes = mapRootNote.getSubtree({ | ||||||
| @@ -167,8 +167,8 @@ 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 "excludeFromNoteMap" (journal typically) then there wouldn't be anything to display, | ||||||
|     // we'll just ignore it |     // so we'll just ignore it | ||||||
|     const ignoreExcludeFromNoteMap = mapRootNote.isLabelTruthy('excludeFromNoteMap'); |     const ignoreExcludeFromNoteMap = mapRootNote.isLabelTruthy('excludeFromNoteMap'); | ||||||
|     const subtree = mapRootNote.getSubtree({ |     const subtree = mapRootNote.getSubtree({ | ||||||
|         includeArchived: false, |         includeArchived: false, | ||||||
|   | |||||||
| @@ -5,7 +5,7 @@ const log = require('../../services/log'); | |||||||
| const searchService = require('../../services/search/services/search'); | const searchService = require('../../services/search/services/search'); | ||||||
| const ValidationError = require("../../errors/validation_error"); | const ValidationError = require("../../errors/validation_error"); | ||||||
|  |  | ||||||
| // options allowed to be updated directly in options dialog | // options allowed to be updated directly in the Options dialog | ||||||
| const ALLOWED_OPTIONS = new Set([ | const ALLOWED_OPTIONS = new Set([ | ||||||
|     'eraseEntitiesAfterTimeInSeconds', |     'eraseEntitiesAfterTimeInSeconds', | ||||||
|     'protectedSessionTimeout', |     'protectedSessionTimeout', | ||||||
|   | |||||||
| @@ -228,7 +228,7 @@ setInterval(() => { | |||||||
| }, 60 * 1000); | }, 60 * 1000); | ||||||
|  |  | ||||||
| function syncFinished() { | function syncFinished() { | ||||||
|     // after first sync finishes, the application is ready to be used |     // after the first sync finishes, the application is ready to be used | ||||||
|     // this is meaningless but at the same time harmless (idempotent) for further syncs |     // this is meaningless but at the same time harmless (idempotent) for further syncs | ||||||
|     sqlInit.setDbAsInitialized(); |     sqlInit.setDbAsInitialized(); | ||||||
| } | } | ||||||
|   | |||||||
| @@ -231,7 +231,7 @@ function register(app) { | |||||||
|     // docker health check |     // docker health check | ||||||
|     route(GET, '/api/health-check', [], () => ({"status": "ok"}), apiResultHandler); |     route(GET, '/api/health-check', [], () => ({"status": "ok"}), apiResultHandler); | ||||||
|  |  | ||||||
|     // group of services below are meant to be executed from outside |     // group of the services below are meant to be executed from the outside | ||||||
|     route(GET, '/api/setup/status', [], setupApiRoute.getStatus, apiResultHandler); |     route(GET, '/api/setup/status', [], setupApiRoute.getStatus, apiResultHandler); | ||||||
|     route(PST, '/api/setup/new-document', [auth.checkAppNotInitialized], setupApiRoute.setupNewDocument, apiResultHandler, false); |     route(PST, '/api/setup/new-document', [auth.checkAppNotInitialized], setupApiRoute.setupNewDocument, apiResultHandler, false); | ||||||
|     route(PST, '/api/setup/sync-from-server', [auth.checkAppNotInitialized], setupApiRoute.setupSyncFromServer, apiResultHandler, false); |     route(PST, '/api/setup/sync-from-server', [auth.checkAppNotInitialized], setupApiRoute.setupSyncFromServer, apiResultHandler, false); | ||||||
| @@ -380,7 +380,7 @@ function apiResultHandler(req, res, result) { | |||||||
|  |  | ||||||
|     result = convertEntitiesToPojo(result); |     result = convertEntitiesToPojo(result); | ||||||
|  |  | ||||||
|     // if it's an array and first element is integer then we consider this to be [statusCode, response] format |     // if it's an array and the first element is integer, then we consider this to be [statusCode, response] format | ||||||
|     if (Array.isArray(result) && result.length > 0 && Number.isInteger(result[0])) { |     if (Array.isArray(result) && result.length > 0 && Number.isInteger(result[0])) { | ||||||
|         const [statusCode, response] = result; |         const [statusCode, response] = result; | ||||||
|  |  | ||||||
|   | |||||||
| @@ -21,7 +21,7 @@ function getNotesWithLabel(name, value = undefined) { | |||||||
| // TODO: should be in search service | // TODO: should be in search service | ||||||
| /** @returns {BNote|null} */ | /** @returns {BNote|null} */ | ||||||
| function getNoteWithLabel(name, value = undefined) { | function getNoteWithLabel(name, value = undefined) { | ||||||
|     // optimized version (~20 times faster) without using normal search, useful for e.g. finding date notes |     // optimized version (~20 times faster) without using normal search, useful for e.g., finding date notes | ||||||
|     const attrs = becca.findAttributes('label', name); |     const attrs = becca.findAttributes('label', name); | ||||||
|  |  | ||||||
|     if (value === undefined) { |     if (value === undefined) { | ||||||
|   | |||||||
| @@ -23,7 +23,7 @@ function checkAuth(req, res, next) { | |||||||
| } | } | ||||||
|  |  | ||||||
| // for electron things which need network stuff | // for electron things which need network stuff | ||||||
| // currently we're doing that for file upload because handling form data seems to be difficult | //  currently, we're doing that for file upload because handling form data seems to be difficult | ||||||
| function checkApiAuthOrElectron(req, res, next) { | function checkApiAuthOrElectron(req, res, next) { | ||||||
|     if (!req.session.loggedIn && !utils.isElectron() && !noAuthentication) { |     if (!req.session.loggedIn && !utils.isElectron() && !noAuthentication) { | ||||||
|         reject(req, res, "Logged in session not found"); |         reject(req, res, "Logged in session not found"); | ||||||
|   | |||||||
| @@ -142,7 +142,7 @@ function BackendScriptApi(currentNote, apiParams) { | |||||||
|      * @method |      * @method | ||||||
|      * @param {string} noteId |      * @param {string} noteId | ||||||
|      * @param {string} parentNoteId |      * @param {string} parentNoteId | ||||||
|      * @param {string} prefix - if branch will be created between note and parent note, set this prefix |      * @param {string} prefix - if branch is created between note and parent note, set this prefix | ||||||
|      * @returns {{branch: BBranch|null}} |      * @returns {{branch: BBranch|null}} | ||||||
|      */ |      */ | ||||||
|     this.ensureNoteIsPresentInParent = cloningService.ensureNoteIsPresentInParent; |     this.ensureNoteIsPresentInParent = cloningService.ensureNoteIsPresentInParent; | ||||||
| @@ -164,7 +164,7 @@ function BackendScriptApi(currentNote, apiParams) { | |||||||
|      * @param {boolean} present - true if we want the branch to exist, false if we want it gone |      * @param {boolean} present - true if we want the branch to exist, false if we want it gone | ||||||
|      * @param {string} noteId |      * @param {string} noteId | ||||||
|      * @param {string} parentNoteId |      * @param {string} parentNoteId | ||||||
|      * @param {string} prefix - if branch will be created between note and parent note, set this prefix |      * @param {string} prefix - if branch is created between note and parent note, set this prefix | ||||||
|      * @returns {void} |      * @returns {void} | ||||||
|      */ |      */ | ||||||
|     this.toggleNoteInParent = cloningService.toggleNoteInParent; |     this.toggleNoteInParent = cloningService.toggleNoteInParent; | ||||||
| @@ -244,7 +244,7 @@ function BackendScriptApi(currentNote, apiParams) { | |||||||
|  |  | ||||||
|         const parentNote = becca.getNote(parentNoteId); |         const parentNote = becca.getNote(parentNoteId); | ||||||
|  |  | ||||||
|         // code note type can be inherited, otherwise text is default |         // code note type can be inherited, otherwise "text" is the default | ||||||
|         extraOptions.type = parentNote.type === 'code' ? 'code' : 'text'; |         extraOptions.type = parentNote.type === 'code' ? 'code' : 'text'; | ||||||
|         extraOptions.mime = parentNote.type === 'code' ? parentNote.mime : 'text/html'; |         extraOptions.mime = parentNote.type === 'code' ? parentNote.mime : 'text/html'; | ||||||
|  |  | ||||||
|   | |||||||
| @@ -156,7 +156,7 @@ function cloneNoteAfter(noteId, afterBranchId) { | |||||||
|         return validationResult; |         return validationResult; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     // we don't change utcDateModified so other changes are prioritized in case of conflict |     // we don't change utcDateModified, so other changes are prioritized in case of conflict | ||||||
|     // also we would have to sync all those modified branches otherwise hash checks would fail |     // also we would have to sync all those modified branches otherwise hash checks would fail | ||||||
|     sql.execute("UPDATE branches SET notePosition = notePosition + 10 WHERE parentNoteId = ? AND notePosition > ? AND isDeleted = 0", |     sql.execute("UPDATE branches SET notePosition = notePosition + 10 WHERE parentNoteId = ? AND notePosition > ? AND isDeleted = 0", | ||||||
|         [afterNote.parentNoteId, afterNote.notePosition]); |         [afterNote.parentNoteId, afterNote.notePosition]); | ||||||
|   | |||||||
| @@ -810,7 +810,7 @@ function getBlankContent(isProtected, type, mime) { | |||||||
|         return '{}'; |         return '{}'; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     return ''; // empty string might be wrong choice for some note types, but it's the best guess |     return ''; // empty string might be a wrong choice for some note types, but it's the best guess | ||||||
| } | } | ||||||
|  |  | ||||||
| function logFix(message) { | function logFix(message) { | ||||||
|   | |||||||
| @@ -108,7 +108,7 @@ async function exportToZip(taskContext, branch, format, res, setHeaders = true) | |||||||
|             } |             } | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         // if the note is already named with extension (e.g. "jquery"), then it's silly to append exact same extension again |         // if the note is already named with extension (e.g. "jquery"), then it's silly to append the exact same extension again | ||||||
|         if (newExtension && existingExtension !== `.${newExtension.toLowerCase()}`) { |         if (newExtension && existingExtension !== `.${newExtension.toLowerCase()}`) { | ||||||
|             fileName += `.${newExtension}`; |             fileName += `.${newExtension}`; | ||||||
|         } |         } | ||||||
|   | |||||||
| @@ -76,7 +76,7 @@ async function importOpml(taskContext, fileBuffer, parentNote) { | |||||||
|     for (const outline of outlines) { |     for (const outline of outlines) { | ||||||
|         const note = importOutline(outline, parentNote.noteId); |         const note = importOutline(outline, parentNote.noteId); | ||||||
|  |  | ||||||
|         // first created note will be activated after import |         // the first created note will be activated after import | ||||||
|         returnNote = returnNote || note; |         returnNote = returnNote || note; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -145,7 +145,7 @@ async function importZip(taskContext, fileBuffer, importRootNote) { | |||||||
|             } else if (parentPath in createdPaths) { |             } else if (parentPath in createdPaths) { | ||||||
|                 parentNoteId = createdPaths[parentPath]; |                 parentNoteId = createdPaths[parentPath]; | ||||||
|             } else { |             } else { | ||||||
|                 // ZIP allows creating out of order records - i.e., file in a directory can appear in the ZIP stream before actual directory |                 // ZIP allows creating out of order records - i.e., file in a directory can appear in the ZIP stream before the actual directory | ||||||
|                 parentNoteId = saveDirectory(parentPath); |                 parentNoteId = saveDirectory(parentPath); | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|   | |||||||
| @@ -69,7 +69,7 @@ function copyChildAttributes(parentNote, childNote) { | |||||||
|  |  | ||||||
|             if (hasAlreadyTemplate && attr.type === 'relation' && name === 'template') { |             if (hasAlreadyTemplate && attr.type === 'relation' && name === 'template') { | ||||||
|                 // if the note already has a template, it means the template was chosen by the user explicitly |                 // if the note already has a template, it means the template was chosen by the user explicitly | ||||||
|                 // in the menu. In that case we should override the default templates defined in the child: attrs |                 // in the menu. In that case, we should override the default templates defined in the child: attrs | ||||||
|                 continue; |                 continue; | ||||||
|             } |             } | ||||||
|  |  | ||||||
| @@ -105,8 +105,8 @@ function getNewNoteTitle(parentNote) { | |||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     // this isn't in theory a good place to sanitize title, but this will catch a lot of XSS attempts |     // this isn't in theory a good place to sanitize title, but this will catch a lot of XSS attempts. | ||||||
|     // title is supposed to contain text only (not HTML) and be printed text only, but given the number of usages |     // title is supposed to contain text only (not HTML) and be printed text only, but given the number of usages, | ||||||
|     // it's difficult to guarantee correct handling in all cases |     // it's difficult to guarantee correct handling in all cases | ||||||
|     title = htmlSanitizer.sanitize(title); |     title = htmlSanitizer.sanitize(title); | ||||||
|  |  | ||||||
| @@ -147,12 +147,12 @@ function getAndValidateParent(params) { | |||||||
|  * - {*} content |  * - {*} content | ||||||
|  * - {string} type - text, code, file, image, search, book, relationMap, canvas, render |  * - {string} type - text, code, file, image, search, book, relationMap, canvas, render | ||||||
|  * |  * | ||||||
|  * Following are optional (have defaults) |  * The following are optional (have defaults) | ||||||
|  * - {string} mime - value is derived from default mimes for type |  * - {string} mime - value is derived from default mimes for type | ||||||
|  * - {boolean} isProtected - default is false |  * - {boolean} isProtected - default is false | ||||||
|  * - {boolean} isExpanded - default is false |  * - {boolean} isExpanded - default is false | ||||||
|  * - {string} prefix - default is empty string |  * - {string} prefix - default is empty string | ||||||
|  * - {int} notePosition - default is last existing notePosition in a parent + 10 |  * - {int} notePosition - default is the last existing notePosition in a parent + 10 | ||||||
|  * |  * | ||||||
|  * @param params |  * @param params | ||||||
|  * @returns {{note: BNote, branch: BBranch}} |  * @returns {{note: BNote, branch: BBranch}} | ||||||
| @@ -226,7 +226,7 @@ function createNewNote(params) { | |||||||
|         eventService.emit(eventService.ENTITY_CREATED, { entityName: 'notes', entity: note }); |         eventService.emit(eventService.ENTITY_CREATED, { entityName: 'notes', entity: note }); | ||||||
|         eventService.emit(eventService.ENTITY_CHANGED, { entityName: 'notes', entity: note }); |         eventService.emit(eventService.ENTITY_CHANGED, { entityName: 'notes', entity: note }); | ||||||
|         triggerNoteTitleChanged(note); |         triggerNoteTitleChanged(note); | ||||||
|         // blobs doesn't use "created" event |         // blobs entity doesn't use "created" event | ||||||
|         eventService.emit(eventService.ENTITY_CHANGED, { entityName: 'blobs', entity: note }); |         eventService.emit(eventService.ENTITY_CHANGED, { entityName: 'blobs', entity: note }); | ||||||
|         eventService.emit(eventService.ENTITY_CREATED, { entityName: 'branches', entity: branch }); |         eventService.emit(eventService.ENTITY_CREATED, { entityName: 'branches', entity: branch }); | ||||||
|         eventService.emit(eventService.ENTITY_CHANGED, { entityName: 'branches', entity: branch }); |         eventService.emit(eventService.ENTITY_CHANGED, { entityName: 'branches', entity: branch }); | ||||||
| @@ -245,7 +245,7 @@ function createNewNoteWithTarget(target, targetBranchId, params) { | |||||||
|     if (!params.type) { |     if (!params.type) { | ||||||
|         const parentNote = becca.notes[params.parentNoteId]; |         const parentNote = becca.notes[params.parentNoteId]; | ||||||
|  |  | ||||||
|         // code note type can be inherited, otherwise text is default |         // code note type can be inherited, otherwise "text" is the default | ||||||
|         params.type = parentNote.type === 'code' ? 'code' : 'text'; |         params.type = parentNote.type === 'code' ? 'code' : 'text'; | ||||||
|         params.mime = parentNote.type === 'code' ? parentNote.mime : 'text/html'; |         params.mime = parentNote.type === 'code' ? parentNote.mime : 'text/html'; | ||||||
|     } |     } | ||||||
| @@ -366,7 +366,7 @@ function checkImageAttachments(note, content) { | |||||||
|     const unknownAttachments = becca.getAttachments(unknownAttachmentIds); |     const unknownAttachments = becca.getAttachments(unknownAttachmentIds); | ||||||
|  |  | ||||||
|     for (const unknownAttachment of unknownAttachments) { |     for (const unknownAttachment of unknownAttachments) { | ||||||
|         // the attachment belongs to a different note (was copy pasted). Attachments can be linked only from the note |         // the attachment belongs to a different note (was copy-pasted). Attachments can be linked only from the note | ||||||
|         // which owns it, so either find an existing attachment having the same content or make a copy. |         // which owns it, so either find an existing attachment having the same content or make a copy. | ||||||
|         let localAttachment = note.getAttachments().find(att => att.role === unknownAttachment.role && att.blobId === unknownAttachment.blobId); |         let localAttachment = note.getAttachments().find(att => att.role === unknownAttachment.role && att.blobId === unknownAttachment.blobId); | ||||||
|  |  | ||||||
| @@ -412,7 +412,7 @@ function findImageLinks(content, foundLinks) { | |||||||
|         }); |         }); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     // removing absolute references to server to keep it working between instances |     // removing absolute references to server to keep it working between instances, | ||||||
|     // we also omit / at the beginning to keep the paths relative |     // we also omit / at the beginning to keep the paths relative | ||||||
|     return content.replace(/src="[^"]*\/api\/images\//g, 'src="api/images/'); |     return content.replace(/src="[^"]*\/api\/images\//g, 'src="api/images/'); | ||||||
| } | } | ||||||
| @@ -557,10 +557,10 @@ function downloadImages(noteId, content) { | |||||||
|         setTimeout(() => { |         setTimeout(() => { | ||||||
|             // the normal expected flow of the offline image saving is that users will paste the image(s) |             // the normal expected flow of the offline image saving is that users will paste the image(s) | ||||||
|             // which will get asynchronously downloaded, during that time they keep editing the note |             // which will get asynchronously downloaded, during that time they keep editing the note | ||||||
|             // once the download is finished, the image note representing downloaded image will be used |             // once the download is finished, the image note representing the downloaded image will be used | ||||||
|             // to replace the IMG link. |             // to replace the IMG link. | ||||||
|             // However, there's another flow where user pastes the image and leaves the note before the images |             // However, there's another flow where the user pastes the image and leaves the note before the images | ||||||
|             // are downloaded and the IMG references are not updated. For this occassion we have this code |             // are downloaded and the IMG references are not updated. For this occasion we have this code | ||||||
|             // which upon the download of all the images will update the note if the links have not been fixed before |             // which upon the download of all the images will update the note if the links have not been fixed before | ||||||
|  |  | ||||||
|             sql.transactional(() => { |             sql.transactional(() => { | ||||||
| @@ -972,7 +972,7 @@ function eraseUnusedAttachmentsNow() { | |||||||
|     eraseScheduledAttachments(0); |     eraseScheduledAttachments(0); | ||||||
| } | } | ||||||
|  |  | ||||||
| // do a replace in str - all keys should be replaced by the corresponding values | // all keys should be replaced by the corresponding values | ||||||
| function replaceByMap(str, mapObj) { | function replaceByMap(str, mapObj) { | ||||||
|     const re = new RegExp(Object.keys(mapObj).join("|"),"g"); |     const re = new RegExp(Object.keys(mapObj).join("|"),"g"); | ||||||
|  |  | ||||||
| @@ -1076,7 +1076,7 @@ function duplicateSubtreeInner(origNote, origBranch, newParentNoteId, noteIdMapp | |||||||
|     const existingNote = becca.notes[newNoteId]; |     const existingNote = becca.notes[newNoteId]; | ||||||
|  |  | ||||||
|     if (existingNote && existingNote.title !== undefined) { // checking that it's not just note's skeleton created because of Branch above |     if (existingNote && existingNote.title !== undefined) { // checking that it's not just note's skeleton created because of Branch above | ||||||
|         // note has multiple clones and was already created from another placement in the tree |         // note has multiple clones and was already created from another placement in the tree, | ||||||
|         // so a branch is all we need for this clone |         // so a branch is all we need for this clone | ||||||
|         return { |         return { | ||||||
|             note: existingNote, |             note: existingNote, | ||||||
|   | |||||||
| @@ -123,7 +123,7 @@ function getImage(imageUrl) { | |||||||
|                 host: parsedTargetUrl.hostname, |                 host: parsedTargetUrl.hostname, | ||||||
|                 port: parsedTargetUrl.port, |                 port: parsedTargetUrl.port, | ||||||
|                 path: parsedTargetUrl.path, |                 path: parsedTargetUrl.path, | ||||||
|                 timeout: opts.timeout, // works only for node client |                 timeout: opts.timeout, // works only for the node client | ||||||
|                 headers: {}, |                 headers: {}, | ||||||
|                 agent: proxyAgent |                 agent: proxyAgent | ||||||
|             }); |             }); | ||||||
| @@ -170,7 +170,7 @@ function getProxyAgent(opts) { | |||||||
| } | } | ||||||
|  |  | ||||||
| function getClient(opts) { | function getClient(opts) { | ||||||
|     // it's not clear how to explicitly configure proxy (as opposed to system proxy) |     // it's not clear how to explicitly configure proxy (as opposed to system proxy), | ||||||
|     // so in that case, we always use node's modules |     // so in that case, we always use node's modules | ||||||
|     if (utils.isElectron() && !opts.proxy) { |     if (utils.isElectron() && !opts.proxy) { | ||||||
|         return require('electron').net; |         return require('electron').net; | ||||||
|   | |||||||
| @@ -4,7 +4,7 @@ const fs = require('fs'); | |||||||
|  |  | ||||||
| const RESOURCE_DIR = path.resolve(__dirname, "../.."); | const RESOURCE_DIR = path.resolve(__dirname, "../.."); | ||||||
|  |  | ||||||
| // where "trilium" executable is | // where the "trilium" executable is | ||||||
| const ELECTRON_APP_ROOT_DIR = path.resolve(RESOURCE_DIR, "../.."); | const ELECTRON_APP_ROOT_DIR = path.resolve(RESOURCE_DIR, "../.."); | ||||||
| const DB_INIT_DIR = path.resolve(RESOURCE_DIR, "db"); | const DB_INIT_DIR = path.resolve(RESOURCE_DIR, "db"); | ||||||
|  |  | ||||||
|   | |||||||
| @@ -35,7 +35,7 @@ function executeBundle(bundle, apiParams = {}) { | |||||||
|     cls.set('componentId', 'script'); |     cls.set('componentId', 'script'); | ||||||
|     cls.set('bundleNoteId', bundle.note.noteId); |     cls.set('bundleNoteId', bundle.note.noteId); | ||||||
|  |  | ||||||
|     // last \r\n is necessary if script contains line comment on its last line |     // last \r\n is necessary if the script contains line comment on its last line | ||||||
|     const script = `function() {\r |     const script = `function() {\r | ||||||
| ${bundle.script}\r | ${bundle.script}\r | ||||||
| }`; | }`; | ||||||
|   | |||||||
| @@ -47,7 +47,7 @@ class OrderByAndLimitExp extends Expression { | |||||||
|                     return larger; |                     return larger; | ||||||
|                 } |                 } | ||||||
|  |  | ||||||
|                 // if both are numbers then parse them for numerical comparison |                 // if both are numbers, then parse them for numerical comparison | ||||||
|                 if (this.isNumber(valA) && this.isNumber(valB)) { |                 if (this.isNumber(valA) && this.isNumber(valB)) { | ||||||
|                     valA = parseFloat(valA); |                     valA = parseFloat(valA); | ||||||
|                     valB = parseFloat(valB); |                     valB = parseFloat(valB); | ||||||
|   | |||||||
| @@ -5,8 +5,8 @@ const NoteSet = require('../note_set'); | |||||||
| const buildComparator = require("../services/build_comparator"); | const buildComparator = require("../services/build_comparator"); | ||||||
|  |  | ||||||
| /** | /** | ||||||
|  * Search string is lower cased for case-insensitive comparison. But when retrieving properties |  * Search string is lower cased for case-insensitive comparison. But when retrieving properties, | ||||||
|  * we need case-sensitive form, so we have this translation object. |  * we need the case-sensitive form, so we have this translation object. | ||||||
|  */ |  */ | ||||||
| const PROP_MAPPING = { | const PROP_MAPPING = { | ||||||
|     "noteid": "noteId", |     "noteid": "noteId", | ||||||
|   | |||||||
| @@ -33,7 +33,7 @@ class SearchContext { | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     addError(error) { |     addError(error) { | ||||||
|         // we record only the first error, subsequent ones are usually consequence of the first |         // we record only the first error, subsequent ones are usually a consequence of the first | ||||||
|         if (!this.error) { |         if (!this.error) { | ||||||
|             this.error = error; |             this.error = error; | ||||||
|         } |         } | ||||||
|   | |||||||
| @@ -1,7 +1,7 @@ | |||||||
| "use strict"; | "use strict"; | ||||||
|  |  | ||||||
| /** | /** | ||||||
|  * Search string is lower cased for case-insensitive comparison. But when retrieving properties |  * Search string is lower cased for case-insensitive comparison. But when retrieving properties, | ||||||
|  * we need a case-sensitive form, so we have this translation object. |  * we need a case-sensitive form, so we have this translation object. | ||||||
|  */ |  */ | ||||||
| const PROP_MAPPING = { | const PROP_MAPPING = { | ||||||
|   | |||||||
| @@ -37,7 +37,7 @@ async function sendSeedToSyncServer() { | |||||||
|         syncVersion: appInfo.syncVersion |         syncVersion: appInfo.syncVersion | ||||||
|     }); |     }); | ||||||
|  |  | ||||||
|     // this is completely new sync, need to reset counters. If this would not be new sync, |     // this is a completely new sync, need to reset counters. If this was not a new sync, | ||||||
|     // the previous request would have failed. |     // the previous request would have failed. | ||||||
|     optionService.setOption('lastSyncedPush', 0); |     optionService.setOption('lastSyncedPush', 0); | ||||||
|     optionService.setOption('lastSyncedPull', 0); |     optionService.setOption('lastSyncedPull', 0); | ||||||
| @@ -66,7 +66,7 @@ async function setupSyncFromSyncServer(syncServerHost, syncProxy, password) { | |||||||
|     try { |     try { | ||||||
|         log.info("Getting document options FROM sync server."); |         log.info("Getting document options FROM sync server."); | ||||||
|  |  | ||||||
|         // response is expected to contain documentId and documentSecret options |         // the response is expected to contain documentId and documentSecret options | ||||||
|         const resp = await request.exec({ |         const resp = await request.exec({ | ||||||
|             method: 'get', |             method: 'get', | ||||||
|             url: `${syncServerHost}/api/setup/sync-seed`, |             url: `${syncServerHost}/api/setup/sync-seed`, | ||||||
|   | |||||||
| @@ -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 isn't triggered but changes are still pending, so we need to schedule another check | ||||||
|             this.scheduleUpdate(); |             this.scheduleUpdate(); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|   | |||||||
| @@ -171,7 +171,7 @@ dbReady.then(() => { | |||||||
|     // kickoff first backup soon after start up |     // kickoff first backup soon after start up | ||||||
|     setTimeout(() => require('./backup').regularBackup(), 5 * 60 * 1000); |     setTimeout(() => require('./backup').regularBackup(), 5 * 60 * 1000); | ||||||
|  |  | ||||||
|     // optimize is usually inexpensive no-op so running it semi-frequently is not a big deal |     // optimize is usually inexpensive no-op, so running it semi-frequently is not a big deal | ||||||
|     setTimeout(() => optimize(), 60 * 60 * 1000); |     setTimeout(() => optimize(), 60 * 60 * 1000); | ||||||
|  |  | ||||||
|     setInterval(() => optimize(), 10 * 60 * 60 * 1000); |     setInterval(() => optimize(), 10 * 60 * 60 * 1000); | ||||||
|   | |||||||
| @@ -203,7 +203,7 @@ async function pushChanges(syncContext) { | |||||||
|         }); |         }); | ||||||
|  |  | ||||||
|         if (filteredEntityChanges.length === 0) { |         if (filteredEntityChanges.length === 0) { | ||||||
|             // there still might be more sync changes (because of batch limit), just all from current batch |             // there still might be more sync changes (because of batch limit), just all the current batch | ||||||
|             // has been filtered out |             // has been filtered out | ||||||
|             setLastSyncedPush(lastSyncedPush); |             setLastSyncedPush(lastSyncedPush); | ||||||
|  |  | ||||||
| @@ -255,7 +255,7 @@ async function checkContentHash(syncContext) { | |||||||
|     const failedChecks = contentHashService.checkContentHashes(resp.entityHashes); |     const failedChecks = contentHashService.checkContentHashes(resp.entityHashes); | ||||||
|  |  | ||||||
|     if (failedChecks.length > 0) { |     if (failedChecks.length > 0) { | ||||||
|         // before requeuing sectors make sure the entity changes are correct |         // before requeuing sectors, make sure the entity changes are correct | ||||||
|         const consistencyChecks = require("./consistency_checks"); |         const consistencyChecks = require("./consistency_checks"); | ||||||
|         consistencyChecks.runEntityChangesChecks(); |         consistencyChecks.runEntityChangesChecks(); | ||||||
|  |  | ||||||
|   | |||||||
| @@ -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 a use case where the 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'; | ||||||
|     }, |     }, | ||||||
|   | |||||||
| @@ -93,8 +93,8 @@ function updateNoteReordering(entityChange, entity, instanceId) { | |||||||
| } | } | ||||||
|  |  | ||||||
| function handleContent(content) { | function handleContent(content) { | ||||||
|     // we always use Buffer object which is different from normal saving - there we use simple string type for |     // we always use a Buffer object which is different from normal saving - there we use a simple string type for | ||||||
|     // "string notes". The problem is that in general it's not possible to detect whether a blob content |     // "string notes". The problem is that in general, it's not possible to detect whether a blob content | ||||||
|     // is string note or note (syncs can arrive out of order) |     // is string note or note (syncs can arrive out of order) | ||||||
|     content = content === null ? null : Buffer.from(content, 'base64'); |     content = content === null ? null : Buffer.from(content, 'base64'); | ||||||
|  |  | ||||||
|   | |||||||
| @@ -14,10 +14,10 @@ class TaskContext { | |||||||
|  |  | ||||||
|         // progressCount is meant to represent just some progress - to indicate the task is not stuck |         // progressCount is meant to represent just some progress - to indicate the task is not stuck | ||||||
|         this.progressCount = -1; // we're incrementing immediatelly |         this.progressCount = -1; // we're incrementing immediatelly | ||||||
|         this.lastSentCountTs = 0; // 0 will guarantee first message will be sent |         this.lastSentCountTs = 0; // 0 will guarantee the first message will be sent | ||||||
|  |  | ||||||
|         // just the fact this has been initialized is a progress which should be sent to clients |         // just the fact this has been initialized is a progress which should be sent to clients | ||||||
|         // this is esp. important when importing big files/images which take long time to upload/process |         // this is esp. important when importing big files/images which take a long time to upload/process | ||||||
|         // which means that first "real" increaseProgressCount() will be called quite late and user is without |         // which means that first "real" increaseProgressCount() will be called quite late and user is without | ||||||
|         // feedback until then |         // feedback until then | ||||||
|         this.increaseProgressCount(); |         this.increaseProgressCount(); | ||||||
|   | |||||||
| @@ -87,7 +87,7 @@ function sortNotes(parentNoteId, customSortBy = 'title', reverse = false, folder | |||||||
|                 const bHasChildren = b.hasChildren(); |                 const bHasChildren = b.hasChildren(); | ||||||
|  |  | ||||||
|                 if ((aHasChildren && !bHasChildren) || (!aHasChildren && bHasChildren)) { |                 if ((aHasChildren && !bHasChildren) || (!aHasChildren && bHasChildren)) { | ||||||
|                     // exactly one note of the two is a directory so the sorting will be done based on this status |                     // exactly one note of the two is a directory, so the sorting will be done based on this status | ||||||
|                     return aHasChildren ? -1 : 1; |                     return aHasChildren ? -1 : 1; | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|   | |||||||
| @@ -9,7 +9,7 @@ const cls = require('./cls'); | |||||||
| const keyboardActionsService = require('./keyboard_actions'); | const keyboardActionsService = require('./keyboard_actions'); | ||||||
| const {ipcMain} = require('electron'); | const {ipcMain} = require('electron'); | ||||||
|  |  | ||||||
| // Prevent window being garbage collected | // Prevent the window being garbage collected | ||||||
| /** @type {Electron.BrowserWindow} */ | /** @type {Electron.BrowserWindow} */ | ||||||
| let mainWindow; | let mainWindow; | ||||||
| /** @type {Electron.BrowserWindow} */ | /** @type {Electron.BrowserWindow} */ | ||||||
| @@ -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 a 1600 * 900 display (including some extra panels etc.) | ||||||
|         defaultWidth: 1200, |         defaultWidth: 1200, | ||||||
|         defaultHeight: 800 |         defaultHeight: 800 | ||||||
|     }); |     }); | ||||||
|   | |||||||
| @@ -185,7 +185,7 @@ function sendPing(client, entityChangeIds = []) { | |||||||
|  |  | ||||||
|     // sort entity changes since froca expects "referential order", i.e. referenced entities should already exist |     // sort entity changes since froca expects "referential order", i.e. referenced entities should already exist | ||||||
|     // in froca. |     // in froca. | ||||||
|     // Froca needs this since it is incomplete copy, it can't create "skeletons" like becca. |     // Froca needs this since it is an incomplete copy, it can't create "skeletons" like becca. | ||||||
|     entityChanges.sort((a, b) => ORDERING[a.entityName] - ORDERING[b.entityName]); |     entityChanges.sort((a, b) => ORDERING[a.entityName] - ORDERING[b.entityName]); | ||||||
|  |  | ||||||
|     for (const entityChange of entityChanges) { |     for (const entityChange of entityChanges) { | ||||||
|   | |||||||
| @@ -16,7 +16,7 @@ function getSharedSubTreeRoot(note) { | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     // every path leads to share root, but which one to choose? |     // every path leads to share root, but which one to choose? | ||||||
|     // for sake of simplicity URLs are not note paths |     // for the sake of simplicity, URLs are not note paths | ||||||
|     const parentNote = note.getParentNotes()[0]; |     const parentNote = note.getParentNotes()[0]; | ||||||
|  |  | ||||||
|     if (parentNote.noteId === shareRoot.SHARE_ROOT_NOTE_ID) { |     if (parentNote.noteId === shareRoot.SHARE_ROOT_NOTE_ID) { | ||||||
| @@ -50,7 +50,7 @@ function checkAttachmentAccess(attachmentId, req, res) { | |||||||
|  |  | ||||||
|     const note = checkNoteAccess(attachment.parentId, req, res); |     const note = checkNoteAccess(attachment.parentId, req, res); | ||||||
|  |  | ||||||
|     // truthy note means user has access, and we can return the attachment |     // truthy note means the user has access, and we can return the attachment | ||||||
|     return note ? attachment : false; |     return note ? attachment : false; | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -330,7 +330,8 @@ class SNote extends AbstractShacaEntity { | |||||||
|     /** |     /** | ||||||
|      * @param {string} type - attribute type (label, relation, etc.) |      * @param {string} type - attribute type (label, relation, etc.) | ||||||
|      * @param {string} name - attribute name |      * @param {string} name - attribute name | ||||||
|      * @returns {SAttribute} attribute of given type and name. If there's more such attributes, first is  returned. Returns null if there's no such attribute belonging to this note. |      * @returns {SAttribute} attribute of the given type and name. If there are more such attributes, first is  returned. | ||||||
|  |      *                       Returns null if there's no such attribute belonging to this note. | ||||||
|      */ |      */ | ||||||
|     getAttribute(type, name) { |     getAttribute(type, name) { | ||||||
|         const attributes = this.getAttributes(); |         const attributes = this.getAttributes(); | ||||||
| @@ -341,7 +342,7 @@ class SNote extends AbstractShacaEntity { | |||||||
|     /** |     /** | ||||||
|      * @param {string} type - attribute type (label, relation, etc.) |      * @param {string} type - attribute type (label, relation, etc.) | ||||||
|      * @param {string} name - attribute name |      * @param {string} name - attribute name | ||||||
|      * @returns {string|null} attribute value of given type and name or null if no such attribute exists. |      * @returns {string|null} attribute value of the given type and name or null if no such attribute exists. | ||||||
|      */ |      */ | ||||||
|     getAttributeValue(type, name) { |     getAttributeValue(type, name) { | ||||||
|         const attr = this.getAttribute(type, name); |         const attr = this.getAttribute(type, name); | ||||||
| @@ -352,7 +353,7 @@ class SNote extends AbstractShacaEntity { | |||||||
|     /** |     /** | ||||||
|      * @param {string} type - attribute type (label, relation, etc.) |      * @param {string} type - attribute type (label, relation, etc.) | ||||||
|      * @param {string} name - attribute name |      * @param {string} name - attribute name | ||||||
|      * @returns {string|null} attribute value of given type and name or null if no such attribute exists. |      * @returns {string|null} attribute value of the given type and name or null if no such attribute exists. | ||||||
|      */ |      */ | ||||||
|     getOwnedAttributeValue(type, name) { |     getOwnedAttributeValue(type, name) { | ||||||
|         const attr = this.getOwnedAttribute(type, name); |         const attr = this.getOwnedAttribute(type, name); | ||||||
|   | |||||||
| @@ -14,7 +14,7 @@ function load() { | |||||||
|     const start = Date.now(); |     const start = Date.now(); | ||||||
|     shaca.reset(); |     shaca.reset(); | ||||||
|  |  | ||||||
|     // using raw query and passing arrays to avoid allocating new objects |     // using a raw query and passing arrays to avoid allocating new objects | ||||||
|  |  | ||||||
|     const noteIds = sql.getColumn(` |     const noteIds = sql.getColumn(` | ||||||
|         WITH RECURSIVE |         WITH RECURSIVE | ||||||
|   | |||||||
| @@ -1,6 +1,6 @@ | |||||||
| /** | /** | ||||||
|  * Usage: node src/tools/generate_document.js 1000 |  * Usage: node src/tools/generate_document.js 1000 | ||||||
|  * will create 1000 new notes and some clones into a current document.db |  * will create 1000 new notes and some clones into the current document.db | ||||||
|  */ |  */ | ||||||
|  |  | ||||||
| require('../becca/entity_constructor'); | require('../becca/entity_constructor'); | ||||||
|   | |||||||
| @@ -55,15 +55,15 @@ | |||||||
|         <div id="setup-type" data-bind="visible: step() == 'setup-type'" style="margin-top: 20px;"> |         <div id="setup-type" data-bind="visible: step() == 'setup-type'" style="margin-top: 20px;"> | ||||||
|             <div class="radio" style="margin-bottom: 15px;"> |             <div class="radio" style="margin-bottom: 15px;"> | ||||||
|                 <label><input type="radio" name="setup-type" value="new-document" data-bind="checked: setupType"> |                 <label><input type="radio" name="setup-type" value="new-document" data-bind="checked: setupType"> | ||||||
|                     I'm a new user, and I want to create new Trilium document for my notes</label> |                     I'm a new user, and I want to create a new Trilium document for my notes</label> | ||||||
|             </div> |             </div> | ||||||
|             <div class="radio" style="margin-bottom: 15px;"> |             <div class="radio" style="margin-bottom: 15px;"> | ||||||
|                 <label><input type="radio" name="setup-type" value="sync-from-desktop" data-bind="checked: setupType"> |                 <label><input type="radio" name="setup-type" value="sync-from-desktop" data-bind="checked: setupType"> | ||||||
|                     I have desktop instance already, and I want to set up sync with it</label> |                     I have a desktop instance already, and I want to set up sync with it</label> | ||||||
|             </div> |             </div> | ||||||
|             <div class="radio" style="margin-bottom: 15px;"> |             <div class="radio" style="margin-bottom: 15px;"> | ||||||
|                 <label><input type="radio" name="setup-type" value="sync-from-server" data-bind="checked: setupType"> |                 <label><input type="radio" name="setup-type" value="sync-from-server" data-bind="checked: setupType"> | ||||||
|                     I have server instance already, and I want to set up sync with it</label> |                     I have a server instance already, and I want to set up sync with it</label> | ||||||
|             </div> |             </div> | ||||||
|  |  | ||||||
|             <button type="button" data-bind="disable: !setupTypeSelected(), click: selectSetupType" class="btn btn-primary">Next</button> |             <button type="button" data-bind="disable: !setupTypeSelected(), click: selectSetupType" class="btn btn-primary">Next</button> | ||||||
|   | |||||||
							
								
								
									
										2
									
								
								src/www
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								src/www
									
									
									
									
									
								
							| @@ -43,7 +43,7 @@ function startTrilium() { | |||||||
|     /** |     /** | ||||||
|      * The intended behavior is to detect when a second instance is running, in that case open the old instance |      * The intended behavior is to detect when a second instance is running, in that case open the old instance | ||||||
|      * instead of the new one. This is complicated by the fact that it is possible to run multiple instances of Trilium |      * instead of the new one. This is complicated by the fact that it is possible to run multiple instances of Trilium | ||||||
|      * if port and data dir is configured separately. This complication is the source of the following weird usage. |      * if port and data dir are configured separately. This complication is the source of the following weird usage. | ||||||
|      * |      * | ||||||
|      * The line below makes sure that the "second-instance" (process in window.js) is fired. Normally it returns a boolean |      * The line below makes sure that the "second-instance" (process in window.js) is fired. Normally it returns a boolean | ||||||
|      * indicating whether another instance is running or not, but we ignore that and kill the app only based on the port conflict. |      * indicating whether another instance is running or not, but we ignore that and kill the app only based on the port conflict. | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user