mirror of
				https://github.com/zadam/trilium.git
				synced 2025-10-31 11:39:01 +01:00 
			
		
		
		
	grammar
This commit is contained in:
		
							parent
							
								
									6dfc72c065
								
							
						
					
					
						commit
						fa3cbb4645
					
				| @ -54,7 +54,7 @@ function getNoteTitleArrayForPath(notePathArray) { | ||||
|     let parentNoteId = 'root'; | ||||
|     let hoistedNotePassed = false; | ||||
| 
 | ||||
|     // this is a notePath from outside of hoisted subtree so full title path needs to be returned
 | ||||
|     // this is a notePath from outside of hoisted subtree, so the full title path needs to be returned
 | ||||
|     const hoistedNoteId = cls.getHoistedNoteId(); | ||||
|     const outsideOfHoistedSubtree = !notePathArray.includes(hoistedNoteId); | ||||
| 
 | ||||
|  | ||||
| @ -119,7 +119,7 @@ class BAttachment extends AbstractBeccaEntity { | ||||
|             throw new Error(`Mapping from attachment role '${this.role}' to note's type is not defined`); | ||||
|         } | ||||
| 
 | ||||
|         if (!this.isContentAvailable()) { // isProtected is same for attachment
 | ||||
|         if (!this.isContentAvailable()) { // isProtected is the same for attachment
 | ||||
|             throw new Error(`Cannot convert protected attachment outside of protected session`); | ||||
|         } | ||||
| 
 | ||||
|  | ||||
| @ -224,7 +224,7 @@ class BBranch extends AbstractBeccaEntity { | ||||
| 
 | ||||
|             for (const childBranch of this.parentNote.getChildBranches()) { | ||||
|                 if (maxNotePos < childBranch.notePosition | ||||
|                     && childBranch.noteId !== '_hidden' // hidden has very large notePosition to always stay last
 | ||||
|                     && childBranch.noteId !== '_hidden' // hidden has a very large notePosition to always stay last
 | ||||
|                 ) { | ||||
|                     maxNotePos = childBranch.notePosition; | ||||
|                 } | ||||
|  | ||||
| @ -9,7 +9,7 @@ const sql = require("../../services/sql"); | ||||
| const BAttachment = require("./battachment"); | ||||
| 
 | ||||
| /** | ||||
|  * NoteRevision represents snapshot of note's title and content at some point in the past. | ||||
|  * NoteRevision represents a snapshot of note's title and content at some point in the past. | ||||
|  * It's used for seamless note versioning. | ||||
|  * | ||||
|  * @extends AbstractBeccaEntity | ||||
| @ -73,7 +73,7 @@ class BNoteRevision extends AbstractBeccaEntity { | ||||
| 
 | ||||
|     /* | ||||
|      * Note revision content has quite special handling - it's not a separate entity, but a lazily loaded | ||||
|      * part of NoteRevision entity with its own sync. Reason behind this hybrid design is that | ||||
|      * part of NoteRevision entity with its own sync. The reason behind this hybrid design is that | ||||
|      * content can be quite large, and it's not necessary to load it / fill memory for any note access even | ||||
|      * if we don't need a content, especially for bulk operations like search. | ||||
|      * | ||||
|  | ||||
| @ -158,7 +158,7 @@ export default class Entrypoints extends Component { | ||||
|     async runActiveNoteCommand() { | ||||
|         const {ntxId, note} = appContext.tabManager.getActiveContext(); | ||||
| 
 | ||||
|         // ctrl+enter is also used elsewhere so make sure we're running only when appropriate
 | ||||
|         // ctrl+enter is also used elsewhere, so make sure we're running only when appropriate
 | ||||
|         if (!note || note.type !== 'code') { | ||||
|             return; | ||||
|         } | ||||
|  | ||||
| @ -5,7 +5,7 @@ import hoistedNoteService from "../services/hoisted_note.js"; | ||||
| import Component from "./component.js"; | ||||
| 
 | ||||
| /** | ||||
|  * This class contains command executors which logically belong to the NoteTree widget, but for better user experience | ||||
|  * This class contains command executors which logically belong to the NoteTree widget, but for better user experience, | ||||
|  * the keyboard shortcuts must be active on the whole screen and not just on the widget itself, so the executors | ||||
|  * must be at the root of the component tree. | ||||
|  */ | ||||
|  | ||||
| @ -106,7 +106,7 @@ class NoteContext extends Component { | ||||
|     } | ||||
| 
 | ||||
|     isMainContext() { | ||||
|         // if null then this is a main context
 | ||||
|         // if null, then this is a main context
 | ||||
|         return !this.mainNtxId; | ||||
|     } | ||||
| 
 | ||||
| @ -127,7 +127,7 @@ class NoteContext extends Component { | ||||
| 
 | ||||
|     saveToRecentNotes(resolvedNotePath) { | ||||
|         setTimeout(async () => { | ||||
|             // we include the note into recent list only if the user stayed on the note at least 5 seconds
 | ||||
|             // we include the note in the recent list only if the user stayed on the note at least 5 seconds
 | ||||
|             if (resolvedNotePath && resolvedNotePath === this.notePath) { | ||||
|                 await server.post('recent-notes', { | ||||
|                     noteId: this.note.noteId, | ||||
| @ -172,7 +172,7 @@ class NoteContext extends Component { | ||||
| 
 | ||||
|     getPojoState() { | ||||
|         if (!this.notePath && this.hoistedNoteId === 'root') { | ||||
|             // keeping empty hoisted tab is esp. important for mobile (e.g. opened launcher config)
 | ||||
|             // keeping empty hoisted tab is esp. important for mobile (e.g., opened launcher config)
 | ||||
|             return null; | ||||
|         } | ||||
| 
 | ||||
| @ -288,7 +288,7 @@ class NoteContext extends Component { | ||||
| 
 | ||||
|     resetViewScope() { | ||||
|         // view scope contains data specific to one note context and one "view".
 | ||||
|         // it is used to e.g. make read-only note temporarily editable or to hide TOC
 | ||||
|         // it is used to e.g., make read-only note temporarily editable or to hide TOC
 | ||||
|         // this is reset after navigating to a different note
 | ||||
|         this.viewScope = {}; | ||||
|     } | ||||
|  | ||||
| @ -6,4 +6,4 @@ | ||||
|     <li><code>keyboardLauncher</code> - optional, pressing the keyboard launcher will open the note</li> | ||||
| </ol> | ||||
| 
 | ||||
| <p>Launchbar displays the title / icon from the launcher which does not necessarily mirrors 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> | ||||
|  | ||||
| @ -195,7 +195,7 @@ class FNote { | ||||
|     } | ||||
| 
 | ||||
|     // will sort the parents so that non-search & non-archived are first and archived at the end
 | ||||
|     // this is done so that non-search & non-archived paths are always explored as first when looking for note path
 | ||||
|     // this is done so that non-search & non-archived paths are always explored as first when looking for a note path
 | ||||
|     sortParents() { | ||||
|         this.parents.sort((aNoteId, bNoteId) => { | ||||
|             const aBranchId = this.parentToBranch[aNoteId]; | ||||
| @ -360,7 +360,7 @@ class FNote { | ||||
|         const parentNotes = this.getParentNotes(); | ||||
| 
 | ||||
|         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()); | ||||
| 
 | ||||
|         for (const notePath of notePaths) { | ||||
| @ -400,7 +400,7 @@ class FNote { | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Returns note path considered to be the "best" | ||||
|      * Returns the note path considered to be the "best" | ||||
|      * | ||||
|      * @param {string} [hoistedNoteId='root'] | ||||
|      * @return {string[]} array of noteIds constituting the particular note path | ||||
| @ -410,7 +410,7 @@ class FNote { | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Returns note path considered to be the "best" | ||||
|      * Returns the note path considered to be the "best" | ||||
|      * | ||||
|      * @param {string} [hoistedNoteId='root'] | ||||
|      * @return {string} serialized note path (e.g. 'root/a1h315/js725h') | ||||
| @ -553,7 +553,7 @@ class FNote { | ||||
|         // we're not checking hideArchivedNotes since that would mean we need to lazy load the child notes
 | ||||
|         // which would seriously slow down everything.
 | ||||
|         // we check this flag only once user chooses to expand the parent. This has the negative consequence that
 | ||||
|         // note may appear as folder but not contain any children when all of them are archived
 | ||||
|         // note may appear as a folder but not contain any children when all of them are archived
 | ||||
| 
 | ||||
|         return childBranches; | ||||
|     } | ||||
| @ -597,7 +597,7 @@ class FNote { | ||||
|     /** | ||||
|      * @param {string} type - attribute type (label, relation, etc.) | ||||
|      * @param {string} name - attribute name | ||||
|      * @returns {FAttribute} 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 {FAttribute} 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. | ||||
|      */ | ||||
|     getOwnedAttribute(type, name) { | ||||
|         const attributes = this.getOwnedAttributes(); | ||||
| @ -608,7 +608,7 @@ class FNote { | ||||
|     /** | ||||
|      * @param {string} type - attribute type (label, relation, etc.) | ||||
|      * @param {string} name - attribute name | ||||
|      * @returns {FAttribute} 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 {FAttribute} 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) { | ||||
|         const attributes = this.getAttributes(); | ||||
| @ -619,7 +619,7 @@ class FNote { | ||||
|     /** | ||||
|      * @param {string} type - attribute type (label, relation, etc.) | ||||
|      * @param {string} name - attribute name | ||||
|      * @returns {string} attribute value of given type and name or null if no such attribute exists. | ||||
|      * @returns {string} attribute value of the given type and name or null if no such attribute exists. | ||||
|      */ | ||||
|     getOwnedAttributeValue(type, name) { | ||||
|         const attr = this.getOwnedAttribute(type, name); | ||||
| @ -630,7 +630,7 @@ class FNote { | ||||
|     /** | ||||
|      * @param {string} type - attribute type (label, relation, etc.) | ||||
|      * @param {string} name - attribute name | ||||
|      * @returns {string} attribute value of given type and name or null if no such attribute exists. | ||||
|      * @returns {string} attribute value of the given type and name or null if no such attribute exists. | ||||
|      */ | ||||
|     getAttributeValue(type, name) { | ||||
|         const attr = this.getAttribute(type, name); | ||||
| @ -774,7 +774,7 @@ class FNote { | ||||
|                 return def && def.isPromoted; | ||||
|             }); | ||||
| 
 | ||||
|         // attrs are not resorted if position changes after initial load
 | ||||
|         // attrs are not resorted if position changes after the initial load
 | ||||
|         promotedAttrs.sort((a, b) => { | ||||
|             if (a.noteId === b.noteId) { | ||||
|                 return a.position < b.position ? -1 : 1; | ||||
|  | ||||
| @ -128,8 +128,8 @@ export default class DesktopLayout { | ||||
|                                     ) | ||||
|                                     .child( | ||||
|                                         new RibbonContainer() | ||||
|                                             // order of the widgets matter. Some of these want to "activate" themselves
 | ||||
|                                             // when visible, when this happens to multiple of them, the first one "wins".
 | ||||
|                                             // the order of the widgets matter. Some of these want to "activate" themselves
 | ||||
|                                             // when visible. When this happens to multiple of them, the first one "wins".
 | ||||
|                                             // promoted attributes should always win.
 | ||||
|                                             .ribbon(new PromotedAttributesWidget()) | ||||
|                                             .ribbon(new ScriptExecutorWidget()) | ||||
|  | ||||
| @ -67,7 +67,7 @@ function lex(str) { | ||||
|                 finishWord(i - 1); | ||||
|             } | ||||
|             else { | ||||
|                 // it's a quote but within other kind of quotes, so it's valid as a literal character
 | ||||
|                 // it's a quote, but within other kind of quotes, so it's valid as a literal character
 | ||||
|                 currentWord += chr; | ||||
|             } | ||||
|             continue; | ||||
|  | ||||
| @ -17,7 +17,7 @@ async function renderAttribute(attribute, renderIsInheritable) { | ||||
|             return $attr; | ||||
|         } | ||||
| 
 | ||||
|         // when the relation has just been created then it might not have a value
 | ||||
|         // when the relation has just been created, then it might not have a value
 | ||||
|         if (attribute.value) { | ||||
|             $attr.append(document.createTextNode(`~${attribute.name}${isInheritable}=`)); | ||||
|             $attr.append(await createNoteLink(attribute.value)); | ||||
|  | ||||
| @ -237,7 +237,7 @@ async function cloneNoteToParentNote(childNoteId, parentNoteId, prefix) { | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| // beware that first arg is noteId and second is branchId!
 | ||||
| // beware that the first arg is noteId and the second is branchId!
 | ||||
| async function cloneNoteAfter(noteId, afterBranchId) { | ||||
|     const resp = await server.put(`notes/${noteId}/clone-after/${afterBranchId}`); | ||||
| 
 | ||||
|  | ||||
| @ -24,7 +24,7 @@ class Froca { | ||||
|     async loadInitialTree() { | ||||
|         const resp = await server.get('tree'); | ||||
| 
 | ||||
|         // clear the cache only directly before adding new content which is important for e.g. switching to protected session
 | ||||
|         // clear the cache only directly before adding new content which is important for e.g., switching to protected session
 | ||||
| 
 | ||||
|         /** @type {Object.<string, FNote>} */ | ||||
|         this.notes = {}; | ||||
| @ -67,7 +67,7 @@ class Froca { | ||||
|             if (note) { | ||||
|                 note.update(noteRow); | ||||
| 
 | ||||
|                 // search note doesn't have child branches in database and all the children are virtual branches
 | ||||
|                 // search note doesn't have child branches in the database and all the children are virtual branches
 | ||||
|                 if (note.type !== 'search') { | ||||
|                     for (const childNoteId of note.children) { | ||||
|                         const childNote = this.notes[childNoteId]; | ||||
|  | ||||
| @ -143,8 +143,8 @@ async function processBranchChange(loadResults, ec) { | ||||
| 
 | ||||
|     if (childNote && !childNote.isRoot() && !parentNote) { | ||||
|         // a branch cannot exist without the parent
 | ||||
|         // 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
 | ||||
|         // 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
 | ||||
|         // sharing meant cloning into _share, which crashed because _share was not loaded
 | ||||
|         parentNote = await froca.getNote(ec.entity.parentNoteId); | ||||
|     } | ||||
|  | ||||
| @ -25,9 +25,9 @@ function FrontendScriptApi(startNote, currentNote, originEntity = null, $contain | ||||
|     /** @property {jQuery} container of all the rendered script content */ | ||||
|     this.$container = $container; | ||||
| 
 | ||||
|     /** @property {object} note where script started executing */ | ||||
|     /** @property {object} note where the script started executing */ | ||||
|     this.startNote = startNote; | ||||
|     /** @property {object} note where script is currently executing */ | ||||
|     /** @property {object} note where the script is currently executing */ | ||||
|     this.currentNote = currentNote; | ||||
|     /** @property {object|null} entity whose event triggered this execution */ | ||||
|     this.originEntity = originEntity; | ||||
| @ -164,7 +164,7 @@ function FrontendScriptApi(startNote, currentNote, originEntity = null, $contain | ||||
|             params: prepareParams(params), | ||||
|             startNoteId: startNote.noteId, | ||||
|             currentNoteId: currentNote.noteId, | ||||
|             originEntityName: "notes", // currently there's no other entity on frontend which can trigger event
 | ||||
|             originEntityName: "notes", // currently there's no other entity on the frontend which can trigger event
 | ||||
|             originEntityId: originEntity ? originEntity.noteId : null | ||||
|         }, "script"); | ||||
| 
 | ||||
| @ -205,7 +205,7 @@ function FrontendScriptApi(startNote, currentNote, originEntity = null, $contain | ||||
|     }; | ||||
| 
 | ||||
|     /** | ||||
|      * Returns note by given noteId. If note is missing from cache, it's loaded. | ||||
|      * Returns note by given noteId. If note is missing from the cache, it's loaded. | ||||
|      ** | ||||
|      * @method | ||||
|      * @param {string} noteId | ||||
| @ -214,7 +214,7 @@ function FrontendScriptApi(startNote, currentNote, originEntity = null, $contain | ||||
|     this.getNote = async noteId => await froca.getNote(noteId); | ||||
| 
 | ||||
|     /** | ||||
|      * Returns list of notes. If note is missing from cache, it's loaded. | ||||
|      * Returns list of notes. If note is missing from the cache, it's loaded. | ||||
|      * | ||||
|      * This is often used to bulk-fill the cache with notes which would have to be picked one by one | ||||
|      * otherwise (by e.g. createNoteLink()) | ||||
| @ -258,7 +258,7 @@ function FrontendScriptApi(startNote, currentNote, originEntity = null, $contain | ||||
|     this.parseDate = utils.parseDate; | ||||
| 
 | ||||
|     /** | ||||
|      * Show info message to the user. | ||||
|      * Show an info message to the user. | ||||
|      * | ||||
|      * @method | ||||
|      * @param {string} message | ||||
| @ -266,7 +266,7 @@ function FrontendScriptApi(startNote, currentNote, originEntity = null, $contain | ||||
|     this.showMessage = toastService.showMessage; | ||||
| 
 | ||||
|     /** | ||||
|      * Show error message to the user. | ||||
|      * Show an error message to the user. | ||||
|      * | ||||
|      * @method | ||||
|      * @param {string} message | ||||
| @ -292,7 +292,7 @@ function FrontendScriptApi(startNote, currentNote, originEntity = null, $contain | ||||
|     this.triggerEvent = (name, data) => appContext.triggerEvent(name, data); | ||||
| 
 | ||||
|     /** | ||||
|      * Create note link (jQuery object) for given note. | ||||
|      * Create a note link (jQuery object) for given note. | ||||
|      * | ||||
|      * @method | ||||
|      * @param {string} notePath (or noteId) | ||||
| @ -319,7 +319,7 @@ function FrontendScriptApi(startNote, currentNote, originEntity = null, $contain | ||||
|     this.getActiveContextNote = () => appContext.tabManager.getActiveContextNote(); | ||||
| 
 | ||||
|     /** | ||||
|      * See https://ckeditor.com/docs/ckeditor5/latest/api/module_core_editor_editor-Editor.html for a documentation on the returned instance.
 | ||||
|      * See https://ckeditor.com/docs/ckeditor5/latest/api/module_core_editor_editor-Editor.html for documentation on the returned instance.
 | ||||
|      * | ||||
|      * @method | ||||
|      * @returns {Promise<BalloonEditor>} instance of CKEditor | ||||
| @ -345,12 +345,12 @@ function FrontendScriptApi(startNote, currentNote, originEntity = null, $contain | ||||
| 
 | ||||
|     /** | ||||
|      * @method | ||||
|      * @returns {Promise<string|null>} returns note path of active note or null if there isn't active note | ||||
|      * @returns {Promise<string|null>} returns a note path of active note or null if there isn't active note | ||||
|      */ | ||||
|     this.getActiveContextNotePath = () => appContext.tabManager.getActiveContextNotePath(); | ||||
| 
 | ||||
|     /** | ||||
|      * Returns component which owns given DOM element (the nearest parent component in DOM tree) | ||||
|      * Returns component which owns the given DOM element (the nearest parent component in DOM tree) | ||||
|      * | ||||
|      * @method | ||||
|      * @param {Element} el - DOM element | ||||
| @ -455,11 +455,11 @@ function FrontendScriptApi(startNote, currentNote, originEntity = null, $contain | ||||
|     this.bindGlobalShortcut = shortcutService.bindGlobalShortcut; | ||||
| 
 | ||||
|     /** | ||||
|      * Trilium runs in backend and frontend process, when something is changed on the backend from script, | ||||
|      * Trilium runs in a backend and frontend process, when something is changed on the backend from a script, | ||||
|      * frontend will get asynchronously synchronized. | ||||
|      * | ||||
|      * This method returns a promise which resolves once all the backend -> frontend synchronization is finished. | ||||
|      * Typical use case is when new note has been created, we should wait until it is synced into frontend and only then activate it. | ||||
|      * Typical use case is when a new note has been created, we should wait until it is synced into frontend and only then activate it. | ||||
|      * | ||||
|      * @method | ||||
|      * @returns {Promise<void>} | ||||
|  | ||||
| @ -19,7 +19,7 @@ async function createNoteLink(notePath, options = {}) { | ||||
| 
 | ||||
|     if (!notePath.startsWith("root")) { | ||||
|         // all note paths should start with "root/" (except for "root" itself)
 | ||||
|         // used e.g. to find internal links
 | ||||
|         // used e.g., to find internal links
 | ||||
|         notePath = `root/${notePath}`; | ||||
|     } | ||||
| 
 | ||||
| @ -222,7 +222,7 @@ async function loadReferenceLinkTitle(noteId, $el) { | ||||
| } | ||||
| 
 | ||||
| $(document).on('click', "a", goToLink); | ||||
| $(document).on('auxclick', "a", goToLink); // to handle middle button
 | ||||
| $(document).on('auxclick', "a", goToLink); // to handle the middle button
 | ||||
| $(document).on('contextmenu', 'a', linkContextMenu); | ||||
| $(document).on('dblclick', "a", e => { | ||||
|     e.preventDefault(); | ||||
|  | ||||
| @ -1,12 +1,13 @@ | ||||
| /** | ||||
|  * Purpose of this class is to cache list of attributes for notes. | ||||
|  * The purpose of this class is to cache the list of attributes for notes. | ||||
|  * | ||||
|  * Cache invalidation granularity is global - whenever a write operation is detected to notes, branches or attributes | ||||
|  * Cache invalidation granularity is global - whenever a write operation is detected to notes, branches or attributes, | ||||
|  * we invalidate the whole cache. That's OK, since the purpose for this is to speed up batch read-only operations, such | ||||
|  * as loading the tree which uses attributes heavily. | ||||
|  */ | ||||
| class NoteAttributeCache { | ||||
|     constructor() { | ||||
|         /** @property {Object.<string, BAttribute[]>} */ | ||||
|         this.attributes = {}; | ||||
|     } | ||||
| 
 | ||||
|  | ||||
| @ -143,7 +143,7 @@ function initNoteAutocomplete($el, options) { | ||||
|         hint: false, | ||||
|         autoselect: true, | ||||
|         // openOnFocus has to be false, otherwise re-focus (after return from note type chooser dialog) forces
 | ||||
|         // re-querying of the autocomplete source which then changes currently selected suggestion
 | ||||
|         // re-querying of the autocomplete source which then changes the currently selected suggestion
 | ||||
|         openOnFocus: false, | ||||
|         minLength: 0, | ||||
|         tabAutocomplete: false | ||||
|  | ||||
| @ -74,7 +74,7 @@ async function getRenderedContent(note, options = {}) { | ||||
|         $downloadButton.on('click', () => openService.downloadFileNote(note.noteId)); | ||||
|         $openButton.on('click', () => openService.openNoteExternally(note.noteId, note.mime)); | ||||
| 
 | ||||
|         // open doesn't work for protected notes since it works through browser which isn't in protected session
 | ||||
|         // open doesn't work for protected notes since it works through a browser which isn't in protected session
 | ||||
|         $openButton.toggle(!note.isProtected); | ||||
| 
 | ||||
|         const $content = $('<div style="display: flex; flex-direction: column; height: 100%;">'); | ||||
| @ -163,7 +163,7 @@ async function getRenderedContent(note, options = {}) { | ||||
|         } | ||||
|     } | ||||
|     else if (type === 'book') { | ||||
|         // nothing, book doesn't have its own content
 | ||||
|         // nothing, a book doesn't have its own content
 | ||||
|     } | ||||
|     else if (!options.tooltip && type === 'protectedSession') { | ||||
|         const $button = $(`<button class="btn btn-sm"><span class="bx bx-log-in"></span> Enter protected session</button>`) | ||||
|  | ||||
| @ -13,8 +13,8 @@ async function createNote(parentNotePath, options = {}) { | ||||
|         target: 'into' | ||||
|     }, options); | ||||
| 
 | ||||
|     // if isProtected isn't available (user didn't enter password yet), then note is created as unencrypted
 | ||||
|     // but this is quite weird since user doesn't see WHERE the note is being created, so it shouldn't occur often
 | ||||
|     // if isProtected isn't available (user didn't enter password yet), then note is created as unencrypted,
 | ||||
|     // but this is quite weird since the user doesn't see WHERE the note is being created, so it shouldn't occur often
 | ||||
|     if (!options.isProtected || !protectedSessionHolder.isProtectedSessionAvailable()) { | ||||
|         options.isProtected = false; | ||||
|     } | ||||
| @ -93,7 +93,7 @@ async function createNoteWithTypePrompt(parentNotePath, options = {}) { | ||||
|     return await createNote(parentNotePath, options); | ||||
| } | ||||
| 
 | ||||
| /* If first element is heading, parse it out and use it as a new heading. */ | ||||
| /* If the first element is heading, parse it out and use it as a new heading. */ | ||||
| function parseSelectedHtml(selectedHtml) { | ||||
|     const dom = $.parseHTML(selectedHtml); | ||||
| 
 | ||||
|  | ||||
| @ -29,7 +29,7 @@ export default class HistoryNavigationButton extends ButtonFromNoteWidget { | ||||
| 
 | ||||
|         this.webContents = utils.dynamicRequire('@electron/remote').getCurrentWebContents(); | ||||
| 
 | ||||
|         // without this the history is preserved across frontend reloads
 | ||||
|         // without this, the history is preserved across frontend reloads
 | ||||
|         this.webContents.clearHistory(); | ||||
| 
 | ||||
|         this.refresh(); | ||||
| @ -87,7 +87,7 @@ export default class HistoryNavigationButton extends ButtonFromNoteWidget { | ||||
|             return; | ||||
|         } | ||||
| 
 | ||||
|         // disabling this because in electron 9 there's weird performance problem which makes these webContents calls
 | ||||
|         // disabling this because in electron 9 there's a weird performance problem which makes these webContents calls
 | ||||
|         // block UI thread for > 1 second on specific notes (book notes displaying underlying render notes with scripts)
 | ||||
| 
 | ||||
|         // this.$backInHistory.toggleClass('disabled', !this.webContents.canGoBack());
 | ||||
|  | ||||
| @ -141,7 +141,7 @@ export default class ExportDialog extends BasicWidget { | ||||
|             const exportType = this.$widget.find("input[name='export-type']:checked").val(); | ||||
| 
 | ||||
|             if (!exportType) { | ||||
|                 // this shouldn't happen as we always choose default export type
 | ||||
|                 // this shouldn't happen as we always choose a default export type
 | ||||
|                 toastService.showError("Choose export type first please"); | ||||
|                 return; | ||||
|             } | ||||
|  | ||||
| @ -67,10 +67,10 @@ export default class JumpToNoteDialog extends BasicWidget { | ||||
|                 appContext.tabManager.getActiveContext().setNote(suggestion.notePath); | ||||
|             }); | ||||
| 
 | ||||
|         // if you open the Jump To dialog soon after using it previously it can often mean that you
 | ||||
|         // actually want to search for the same thing (e.g. you opened the wrong note at first try)
 | ||||
|         // if you open the Jump To dialog soon after using it previously, it can often mean that you
 | ||||
|         // actually want to search for the same thing (e.g., you opened the wrong note at first try)
 | ||||
|         // so we'll keep the content.
 | ||||
|         // if it's outside of this time limit then we assume it's a completely new search and show recent notes instead.
 | ||||
|         // if it's outside of this time limit, then we assume it's a completely new search and show recent notes instead.
 | ||||
|         if (Date.now() - this.lastOpenedTs > KEEP_LAST_SEARCH_FOR_X_SECONDS * 1000) { | ||||
|             noteAutocompleteService.showRecentNotes(this.$autoComplete); | ||||
|         } else { | ||||
|  | ||||
| @ -11,7 +11,7 @@ import FindInHtml from "./find_in_html.js"; | ||||
| const findWidgetDelayMillis = 200; | ||||
| const waitForEnter = (findWidgetDelayMillis < 0); | ||||
| 
 | ||||
| // tabIndex=-1 on the checkbox labels is necessary so when clicking on the label
 | ||||
| // tabIndex=-1 on the checkbox labels is necessary, so when clicking on the label,
 | ||||
| // the focusout handler is called with relatedTarget equal to the label instead
 | ||||
| // of undefined. It's -1 instead of > 0, so they don't tabstop
 | ||||
| const TPL = ` | ||||
| @ -185,7 +185,7 @@ export default class FindWidget extends NoteContextAwareWidget { | ||||
|     startSearch() { | ||||
|         // XXX This should clear the previous search immediately in all cases
 | ||||
|         //     (the search is stale when waitforenter but also while the
 | ||||
|         //     delay is running for non waitforenter case)
 | ||||
|         //     delay is running for the non waitforenter case)
 | ||||
|         if (!waitForEnter) { | ||||
|             // Clear the previous timeout if any, it's ok if timeoutId is
 | ||||
|             // null or undefined
 | ||||
|  | ||||
| @ -21,7 +21,7 @@ export default class FindInCode { | ||||
| 
 | ||||
|         // highlightSelectionMatches is the overlay that highlights
 | ||||
|         // the words under the cursor. This occludes the search
 | ||||
|         // markers style, save it, disable it. Will be restored when
 | ||||
|         // markers style, save it, disable it. It will be restored when
 | ||||
|         // the focus is back into the note
 | ||||
|         this.oldHighlightSelectionMatches = codeEditor.getOption("highlightSelectionMatches"); | ||||
|         codeEditor.setOption("highlightSelectionMatches", false); | ||||
| @ -69,14 +69,13 @@ export default class FindInCode { | ||||
|             let curChar = 0; | ||||
|             let curMatch = null; | ||||
|             findResult = []; | ||||
|             // All those markText take several seconds on e.g. this ~500-line
 | ||||
|             // All those markText take several seconds on e.g., this ~500-line
 | ||||
|             // script, batch them inside an operation, so they become
 | ||||
|             // unnoticeable. Alternatively, an overlay could be used, see
 | ||||
|             // https://codemirror.net/addon/search/match-highlighter.js ?
 | ||||
|             codeEditor.operation(() => { | ||||
|                 for (let i = 0; i < text.length; ++i) { | ||||
|                     // Fetch next match if it's the first time or
 | ||||
|                     // if past the current match start
 | ||||
|                     // Fetch the next match if it's the first time or if past the current match start
 | ||||
|                     if ((curMatch == null) || (curMatch.index < i)) { | ||||
|                         curMatch = re.exec(text); | ||||
|                         if (curMatch == null) { | ||||
| @ -88,16 +87,13 @@ export default class FindInCode { | ||||
|                     // selected marker highlight will be done later
 | ||||
|                     if (i === curMatch.index) { | ||||
|                         let fromPos = { "line" : curLine, "ch" : curChar }; | ||||
|                         // XXX If multiline is supported, this needs to
 | ||||
|                         //     recalculate curLine since the match may span
 | ||||
|                         //     lines
 | ||||
|                         // If multiline is supported, this needs to recalculate curLine since the match may span lines
 | ||||
|                         let toPos = { "line" : curLine, "ch" : curChar + curMatch[0].length}; | ||||
|                         // XXX or css = "color: #f3"
 | ||||
|                         // or css = "color: #f3"
 | ||||
|                         let marker = doc.markText( fromPos, toPos, { "className" : FIND_RESULT_CSS_CLASSNAME }); | ||||
|                         findResult.push(marker); | ||||
| 
 | ||||
|                         // Set the first match beyond the cursor as current
 | ||||
|                         // match
 | ||||
|                         // Set the first match beyond the cursor as the current match
 | ||||
|                         if (currentFound === -1) { | ||||
|                             const cursorPos = codeEditor.getCursor(); | ||||
|                             if ((fromPos.line > cursorPos.line) || | ||||
|  | ||||
| @ -64,8 +64,7 @@ export default class FindInText { | ||||
|         if (totalFound > 0) { | ||||
|             currentFound = Math.max(0, currentFound); | ||||
|             // XXX Do this accessing the private data?
 | ||||
|             // See
 | ||||
|             // https://github.com/ckeditor/ckeditor5/blob/b95e2faf817262ac0e1e21993d9c0bde3f1be594/packages/ckeditor5-find-and-replace/src/findnextcommand.js
 | ||||
|             // See https://github.com/ckeditor/ckeditor5/blob/b95e2faf817262ac0e1e21993d9c0bde3f1be594/packages/ckeditor5-find-and-replace/src/findnextcommand.js
 | ||||
|             for (let i = 0 ; i < currentFound; ++i) { | ||||
|                 textEditor.execute('findNext', searchTerm); | ||||
|             } | ||||
|  | ||||
| @ -1,4 +1,4 @@ | ||||
| // taken from HTML source of https://boxicons.com/
 | ||||
| // taken from the HTML source of https://boxicons.com/
 | ||||
| 
 | ||||
| const categories = [ | ||||
|     {"name": "All categories", "id": 0}, | ||||
|  | ||||
| @ -101,7 +101,7 @@ export default class MermaidWidget extends NoteContextAwareWidget { | ||||
|         const blob = await this.note.getBlob(); | ||||
|         const content = blob.content || ""; | ||||
| 
 | ||||
|         // this can't be promisified since in case of error this both calls callback with error SVG and throws exception
 | ||||
|         // this can't be promisified since in case of error, this both calls callback with error SVG and throws exception
 | ||||
|         // with error details
 | ||||
|         mermaid.mermaidAPI.render(`mermaid-graph-${idCounter}`, content, cb); | ||||
|     } | ||||
|  | ||||
| @ -72,7 +72,7 @@ export default class NoteContextAwareWidget extends BasicWidget { | ||||
|     async refreshWithNote(note) {} | ||||
| 
 | ||||
|     async noteSwitchedEvent({noteContext, notePath}) { | ||||
|         // if notePath does not match then the noteContext has been switched to another note in the meantime
 | ||||
|         // if notePath does not match, then the noteContext has been switched to another note in the meantime
 | ||||
|         if (noteContext.notePath === notePath) { | ||||
|             await this.noteSwitched(); | ||||
|         } | ||||
| @ -92,11 +92,11 @@ export default class NoteContextAwareWidget extends BasicWidget { | ||||
|         await this.refresh(); | ||||
|     } | ||||
| 
 | ||||
|     // when note is both switched and activated, this should not produce double refresh
 | ||||
|     // when note is both switched and activated, this should not produce a double refresh
 | ||||
|     async noteSwitchedAndActivatedEvent({noteContext, notePath}) { | ||||
|         this.noteContext = noteContext; | ||||
| 
 | ||||
|         // if notePath does not match then the noteContext has been switched to another note in the meantime
 | ||||
|         // if notePath does not match, then the noteContext has been switched to another note in the meantime
 | ||||
|         if (this.notePath === notePath) { | ||||
|             await this.refresh(); | ||||
|         } | ||||
|  | ||||
| @ -149,7 +149,7 @@ export default class NoteDetailWidget extends NoteContextAwareWidget { | ||||
| 
 | ||||
|             await typeWidget.handleEvent('setNoteContext', {noteContext: this.noteContext}); | ||||
| 
 | ||||
|             // this is happening in update() so note has been already set, and we need to reflect this
 | ||||
|             // this is happening in update(), so note has been already set, and we need to reflect this
 | ||||
|             await typeWidget.handleEvent('noteSwitched', { | ||||
|                 noteContext: this.noteContext, | ||||
|                 notePath: this.noteContext.notePath | ||||
| @ -298,13 +298,13 @@ export default class NoteDetailWidget extends NoteContextAwareWidget { | ||||
|             // FIXME: create a separate event to force hierarchical refresh
 | ||||
| 
 | ||||
|             // this uses handleEvent to make sure that the ordinary content updates are propagated only in the subtree
 | ||||
|             // to avoid problem in #3365
 | ||||
|             // to avoid the problem in #3365
 | ||||
|             this.handleEvent('noteTypeMimeChanged', {noteId: this.noteId}); | ||||
|         } | ||||
|         else if (loadResults.isNoteReloaded(this.noteId, this.componentId) | ||||
|             && (this.type !== await this.getWidgetType() || this.mime !== this.note.mime)) { | ||||
| 
 | ||||
|             // this needs to have a triggerEvent so that e.g. note type (not in the component subtree) is updated
 | ||||
|             // this needs to have a triggerEvent so that e.g., note type (not in the component subtree) is updated
 | ||||
|             this.triggerEvent('noteTypeMimeChanged', {noteId: this.noteId}); | ||||
|         } | ||||
|         else { | ||||
|  | ||||
| @ -138,7 +138,7 @@ export default class FilePropertiesWidget extends NoteContextAwareWidget { | ||||
| 
 | ||||
|         this.$fileSize.text(`${blob.contentLength} bytes`); | ||||
| 
 | ||||
|         // open doesn't work for protected notes since it works through browser which isn't in protected session
 | ||||
|         // open doesn't work for protected notes since it works through a browser which isn't in protected session
 | ||||
|         this.$openButton.toggle(!note.isProtected); | ||||
|         this.$downloadButton.toggle(!note.isProtected || protectedSessionHolder.isProtectedSessionAvailable()) | ||||
|         this.$uploadNewRevisionButton.toggle(!note.isProtected || protectedSessionHolder.isProtectedSessionAvailable()) | ||||
|  | ||||
| @ -59,7 +59,7 @@ export default class EditableCodeTypeWidget extends TypeWidget { | ||||
|             lineNumbers: true, | ||||
|             tabindex: 300, | ||||
|             // we line wrap partly also because without it horizontal scrollbar displays only when you scroll
 | ||||
|             // all the way to the bottom of the note. With line wrap there's no horizontal scrollbar so no problem
 | ||||
|             // all the way to the bottom of the note. With line wrap, there's no horizontal scrollbar so no problem
 | ||||
|             lineWrapping: options.is('codeLineWrapEnabled'), | ||||
|             dragDrop: false, // with true the editor inlines dropped files which is not what we expect
 | ||||
|             placeholder: "Type the content of your code note here...", | ||||
| @ -72,7 +72,7 @@ export default class EditableCodeTypeWidget extends TypeWidget { | ||||
|         const blob = await this.note.getBlob(); | ||||
| 
 | ||||
|         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 consistency check)
 | ||||
|             // we provide fallback
 | ||||
|             this.codeEditor.setValue(blob.content || ""); | ||||
|             this.codeEditor.clearHistory(); | ||||
|  | ||||
| @ -113,8 +113,8 @@ export default class EditableTextTypeWidget extends AbstractTextTypeWidget { | ||||
|                         label: mt.title | ||||
|                     })); | ||||
| 
 | ||||
|         // CKEditor since version 12 needs the element to be visible before initialization. At the same time
 | ||||
|         // we want to avoid flicker - i.e. show editor only once everything is ready. That's why we have separate
 | ||||
|         // CKEditor since version 12 needs the element to be visible before initialization. At the same time,
 | ||||
|         // we want to avoid flicker - i.e., show editor only once everything is ready. That's why we have separate
 | ||||
|         // display of $widget in both branches.
 | ||||
|         this.$widget.show(); | ||||
| 
 | ||||
| @ -131,7 +131,7 @@ export default class EditableTextTypeWidget extends AbstractTextTypeWidget { | ||||
|             // This prevents an infinite restart loop.
 | ||||
|             crashNumberLimit: 3, | ||||
|             // A minimum number of milliseconds between saving the editor data internally (defaults to 5000).
 | ||||
|             // Note that for large documents this might impact the editor performance.
 | ||||
|             // Note that for large documents, this might impact the editor performance.
 | ||||
|             saveInterval: 5000 | ||||
|         }); | ||||
| 
 | ||||
| @ -194,7 +194,7 @@ export default class EditableTextTypeWidget extends AbstractTextTypeWidget { | ||||
|         const content = this.watchdog.editor.getData(); | ||||
| 
 | ||||
|         // if content is only tags/whitespace (typically <p> </p>), then just make it empty
 | ||||
|         // this is important when setting new note to code
 | ||||
|         // this is important when setting a new note to code
 | ||||
|         return { | ||||
|             content: utils.isHtmlEmpty(content) ? '' : content | ||||
|         }; | ||||
|  | ||||
| @ -13,7 +13,7 @@ const ValidationError = require("../../errors/validation_error"); | ||||
| const NotFoundError = require("../../errors/not_found_error"); | ||||
| 
 | ||||
| /** | ||||
|  * Code in this file deals with moving and cloning branches. Relationship between note and parent note is unique | ||||
|  * Code in this file deals with moving and cloning branches. The relationship between note and parent note is unique | ||||
|  * for not deleted branches. There may be multiple deleted note-parent note relationships. | ||||
|  */ | ||||
| 
 | ||||
| @ -80,7 +80,7 @@ function moveBranchBeforeNote(req) { | ||||
| 
 | ||||
|     treeService.sortNotesIfNeeded(parentNote.noteId); | ||||
| 
 | ||||
|     // if sorting is not needed then still the ordering might have changed above manually
 | ||||
|     // if sorting is not needed, then still the ordering might have changed above manually
 | ||||
|     entityChangesService.addNoteReorderingEntityChange(parentNote.noteId); | ||||
| 
 | ||||
|     log.info(`Moved note ${branchToMove.noteId}, branch ${branchId} before note ${beforeBranch.noteId}, branch ${beforeBranchId}`); | ||||
| @ -131,7 +131,7 @@ function moveBranchAfterNote(req) { | ||||
| 
 | ||||
|     treeService.sortNotesIfNeeded(parentNote.noteId); | ||||
| 
 | ||||
|     // if sorting is not needed then still the ordering might have changed above manually
 | ||||
|     // if sorting is not needed, then still the ordering might have changed above manually
 | ||||
|     entityChangesService.addNoteReorderingEntityChange(parentNote.noteId); | ||||
| 
 | ||||
|     log.info(`Moved note ${branchToMove.noteId}, branch ${branchId} after note ${afterNote.noteId}, branch ${afterBranchId}`); | ||||
|  | ||||
| @ -106,7 +106,7 @@ const downloadAttachment = (req, res) => downloadAttachmentInt(req.params.attach | ||||
| const openAttachment = (req, res) => downloadAttachmentInt(req.params.attachmentId, res, false); | ||||
| 
 | ||||
| function fileContentProvider(req) { | ||||
|     // Read file name from route params.
 | ||||
|     // Read the file name from route params.
 | ||||
|     const note = becca.getNote(req.params.noteId); | ||||
|     if (!note) { | ||||
|         throw new NotFoundError(`Note '${req.params.noteId}' doesn't exist.`); | ||||
| @ -116,7 +116,7 @@ function fileContentProvider(req) { | ||||
| } | ||||
| 
 | ||||
| function attachmentContentProvider(req) { | ||||
|     // Read file name from route params.
 | ||||
|     // Read the file name from route params.
 | ||||
|     const attachment = becca.getAttachment(req.params.attachmentId); | ||||
|     if (!attachment) { | ||||
|         throw new NotFoundError(`Attachment '${req.params.attachmentId}' doesn't exist.`); | ||||
|  | ||||
| @ -72,7 +72,7 @@ async function importToBranch(req) { | ||||
|     } | ||||
| 
 | ||||
|     if (last === "true") { | ||||
|         // small timeout to avoid race condition (message is received before the transaction is committed)
 | ||||
|         // small timeout to avoid race condition (the message is received before the transaction is committed)
 | ||||
|         setTimeout(() => taskContext.taskSucceeded({ | ||||
|             parentNoteId: parentNoteId, | ||||
|             importedNoteId: note.noteId | ||||
|  | ||||
| @ -61,7 +61,7 @@ function handleRequest(req, res) { | ||||
|             throw new Error(`Unrecognized attribute name '${attr.name}'`); | ||||
|         } | ||||
| 
 | ||||
|         return; // only first handler is executed
 | ||||
|         return; // only the first handler is executed
 | ||||
|     } | ||||
| 
 | ||||
|     const message = `No handler matched for custom '${path}' request.`; | ||||
|  | ||||
| @ -29,7 +29,7 @@ function installLocalAppIcon() { | ||||
|     } | ||||
| 
 | ||||
|     if (!fs.existsSync(path.resolve(ELECTRON_APP_ROOT_DIR, "trilium-portable.sh"))) { | ||||
|         // simple heuristic to detect ".tar.xz" linux build (i.e. not flatpak, not debian)
 | ||||
|         // simple heuristic to detect ".tar.xz" linux build (i.e., not flatpak, not debian)
 | ||||
|         // only in such case it's necessary to create an icon
 | ||||
|         return; | ||||
|     } | ||||
|  | ||||
| @ -63,7 +63,7 @@ function addEntityChange(entityChange) { | ||||
| 
 | ||||
|     const entityChangeIds = namespace.get('entityChangeIds') || []; | ||||
| 
 | ||||
|     // store only ID since the record can be modified (e.g. in erase)
 | ||||
|     // store only ID since the record can be modified (e.g., in erase)
 | ||||
|     entityChangeIds.push(entityChange.id); | ||||
| 
 | ||||
|     namespace.set('entityChangeIds', entityChangeIds); | ||||
|  | ||||
| @ -239,11 +239,11 @@ class ConsistencyChecks { | ||||
|     } | ||||
| 
 | ||||
|     findExistencyIssues() { | ||||
|         // principle for fixing inconsistencies is that if the note itself is deleted (isDeleted=true) then all related
 | ||||
|         // entities should be also deleted (branches, attributes), but if note is not deleted,
 | ||||
|         // the principle for fixing inconsistencies is that if the note itself is deleted (isDeleted=true) then all related
 | ||||
|         // entities should be also deleted (branches, attributes), but if the note is not deleted,
 | ||||
|         // then at least one branch should exist.
 | ||||
| 
 | ||||
|         // the order here is important - first we might need to delete inconsistent branches and after that
 | ||||
|         // the order here is important - first we might need to delete inconsistent branches, and after that
 | ||||
|         // another check might create missing branch
 | ||||
|         this.findAndFixIssues(` | ||||
|                     SELECT branchId, | ||||
| @ -376,7 +376,7 @@ class ConsistencyChecks { | ||||
|             ({noteId, type}) => { | ||||
|                 if (this.autoFix) { | ||||
|                     const note = becca.getNote(noteId); | ||||
|                     note.type = 'file'; // file is a safe option to recover notes if type is not known
 | ||||
|                     note.type = 'file'; // file is a safe option to recover notes if the type is not known
 | ||||
|                     note.save(); | ||||
| 
 | ||||
|                     this.reloadNeeded = true; | ||||
| @ -613,7 +613,7 @@ class ConsistencyChecks { | ||||
|                     entityChangesService.addEntityChange({ | ||||
|                         entityName, | ||||
|                         entityId, | ||||
|                         hash: utils.randomString(10), // doesn't matter, will force sync but that's OK
 | ||||
|                         hash: utils.randomString(10), // doesn't matter, will force sync, but that's OK
 | ||||
|                         isErased: !!entity.isErased, | ||||
|                         utcDateChanged: entity.utcDateModified || entity.utcDateCreated, | ||||
|                         isSynced: entityName !== 'options' || entity.isSynced | ||||
| @ -690,7 +690,7 @@ class ConsistencyChecks { | ||||
|                     // - just SQL query will fix it in DB but not notify frontend (or other caches) that it has been fixed
 | ||||
|                     // - renaming the attribute would break the invariant that single attribute never changes the name
 | ||||
|                     // - deleting the old attribute and creating new will create duplicates across synchronized cluster (specifically in the initial migration)
 | ||||
|                     // But in general we assume there won't be many such problems
 | ||||
|                     // But in general, we assume there won't be many such problems
 | ||||
|                     sql.execute('UPDATE attributes SET name = ? WHERE name = ?', [fixedName, origName]); | ||||
| 
 | ||||
|                     this.fixedIssues = true; | ||||
| @ -804,7 +804,7 @@ class ConsistencyChecks { | ||||
| 
 | ||||
| function getBlankContent(isProtected, type, mime) { | ||||
|     if (isProtected) { | ||||
|         return null; // this is wrong for protected non-erased notes, but we cannot create a valid value without password
 | ||||
|         return null; // this is wrong for protected non-erased notes, but we cannot create a valid value without a password
 | ||||
|     } | ||||
| 
 | ||||
|     if (mime === 'application/json') { | ||||
|  | ||||
| @ -5,7 +5,7 @@ | ||||
|  * - if TRILIUM_DATA_DIR environment variable exists, then its value is used as the path | ||||
|  * - if "trilium-data" dir exists directly in the home dir, then it is used | ||||
|  * - based on OS convention, if the "app data directory" exists, we'll use or create "trilium-data" directory there | ||||
|  * - as a fallback if previous step fails, we'll use home dir | ||||
|  * - as a fallback if the previous step fails, we'll use home dir | ||||
|  */ | ||||
| 
 | ||||
| const os = require('os'); | ||||
|  | ||||
| @ -13,7 +13,7 @@ function arraysIdentical(a, b) { | ||||
| } | ||||
| 
 | ||||
| function shaArray(content) { | ||||
|     // we use this as simple checksum and don't rely on its security so SHA-1 is good enough
 | ||||
|     // we use this as a simple checksum and don't rely on its security, so SHA-1 is good enough
 | ||||
|     return crypto.createHash('sha1').update(content).digest(); | ||||
| } | ||||
| 
 | ||||
|  | ||||
| @ -6,7 +6,7 @@ function utcNowDateTime() { | ||||
| } | ||||
| 
 | ||||
| // CLS date time is important in web deployments - server often runs in different time zone than user is located in,
 | ||||
| // so we'd prefer client timezone to be used to record local dates. For this reason requests from client contain
 | ||||
| // so we'd prefer client timezone to be used to record local dates. For this reason, requests from clients contain
 | ||||
| // "trilium-local-now-datetime" header which is then stored in CLS
 | ||||
| function localNowDateTime() { | ||||
|     return cls.getLocalNowDateTime() | ||||
|  | ||||
| @ -12,7 +12,7 @@ function runAttachedRelations(note, relationName, originEntity) { | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     // same script note can get here with multiple ways, but execute only once
 | ||||
|     // the same script note can get here with multiple ways, but execute only once
 | ||||
|     const notesToRun = new Set( | ||||
|         note.getRelations(relationName) | ||||
|             .map(relation => relation.getTargetNote()) | ||||
| @ -203,7 +203,7 @@ eventService.subscribe(eventService.ENTITY_CHANGED, ({ entityName, entity }) => | ||||
| 
 | ||||
| eventService.subscribe(eventService.ENTITY_DELETED, ({ entityName, entity }) => { | ||||
|     processInverseRelations(entityName, entity, (definition, note, targetNote) => { | ||||
|         // if one inverse attribute is deleted then the other should be deleted as well
 | ||||
|         // if one inverse attribute is deleted, then the other should be deleted as well
 | ||||
|         const relations = targetNote.getOwnedRelations(definition.inverseRelation); | ||||
| 
 | ||||
|         for (const relation of relations) { | ||||
|  | ||||
| @ -15,8 +15,8 @@ const LBTPL_CUSTOM_WIDGET = "_lbTplCustomWidget"; | ||||
| 
 | ||||
| /* | ||||
|  * Hidden subtree is generated as a "predictable structure" which means that it avoids generating random IDs to always | ||||
|  * produce same structure. This is needed because it is run on multiple instances in the sync cluster which might produce | ||||
|  * duplicate subtrees. This way, all instances will generate the same structure with same IDs. | ||||
|  * produce the same structure. This is needed because it is run on multiple instances in the sync cluster which might produce | ||||
|  * duplicate subtrees. This way, all instances will generate the same structure with the same IDs. | ||||
|  */ | ||||
| 
 | ||||
| const HIDDEN_SUBTREE_DEFINITION = { | ||||
| @ -24,7 +24,7 @@ const HIDDEN_SUBTREE_DEFINITION = { | ||||
|     title: 'Hidden Notes', | ||||
|     type: 'doc', | ||||
|     icon: 'bx bx-chip', | ||||
|     // we want to keep the hidden subtree always last, otherwise there will be problems with e.g. keyboard navigation
 | ||||
|     // we want to keep the hidden subtree always last, otherwise there will be problems with e.g., keyboard navigation
 | ||||
|     // over tree when it's in the middle
 | ||||
|     notePosition: 999_999_999, | ||||
|     attributes: [ | ||||
| @ -298,14 +298,14 @@ function checkHiddenSubtreeRecursively(parentNoteId, item) { | ||||
|     } | ||||
| 
 | ||||
|     if (note.type !== item.type) { | ||||
|         // enforce correct note type
 | ||||
|         // enforce a correct note type
 | ||||
|         note.type = item.type; | ||||
|         note.save(); | ||||
|     } | ||||
| 
 | ||||
|     if (branch) { | ||||
|         // in case of launchers the branch ID is not preserved and should not be relied upon - launchers which move between
 | ||||
|         // visible and available will change branch since branch's parent-child relationship is immutable
 | ||||
|         // visible and available will change branch since the branch's parent-child relationship is immutable
 | ||||
|         if (item.notePosition !== undefined && branch.notePosition !== item.notePosition) { | ||||
|             branch.notePosition = item.notePosition; | ||||
|             branch.save(); | ||||
|  | ||||
| @ -2,8 +2,8 @@ const sanitizeHtml = require('sanitize-html'); | ||||
| const sanitizeUrl = require('@braintree/sanitize-url').sanitizeUrl; | ||||
| 
 | ||||
| // intended mainly as protection against XSS via import
 | ||||
| // secondarily it (partly) protects against "CSS takeover"
 | ||||
| // sanitize also note titles, label values etc. - there's so many usage which make it difficult to guarantee all of them
 | ||||
| //  secondarily, it (partly) protects against "CSS takeover"
 | ||||
| // sanitize also note titles, label values etc. - there are so many usages which make it difficult to guarantee all of them
 | ||||
| // are properly handled
 | ||||
| function sanitize(dirtyHtml) { | ||||
|     if (!dirtyHtml) { | ||||
|  | ||||
| @ -191,8 +191,8 @@ async function shrinkImage(buffer, originalName) { | ||||
|         finalImageBuffer = buffer; | ||||
|     } | ||||
| 
 | ||||
|     // if resizing did not help with size then save the original
 | ||||
|     // (can happen when e.g. resizing PNG into JPEG)
 | ||||
|     // if resizing did not help with size, then save the original
 | ||||
|     // (can happen when e.g., resizing PNG into JPEG)
 | ||||
|     if (finalImageBuffer.byteLength >= buffer.byteLength) { | ||||
|         finalImageBuffer = buffer; | ||||
|     } | ||||
| @ -216,7 +216,7 @@ async function resize(buffer, quality) { | ||||
| 
 | ||||
|     image.quality(quality); | ||||
| 
 | ||||
|     // when converting PNG to JPG we lose alpha channel, this is replaced by white to match Trilium white background
 | ||||
|     // when converting PNG to JPG, we lose the alpha channel, this is replaced by white to match Trilium white background
 | ||||
|     image.background(0xFFFFFFFF); | ||||
| 
 | ||||
|     const resultBuffer = await image.getBufferAsync(jimp.MIME_JPEG); | ||||
|  | ||||
| @ -317,8 +317,8 @@ function importEnex(taskContext, file, parentNote) { | ||||
|                     content = content.replace(mediaRegex, imageLink); | ||||
| 
 | ||||
|                     if (!content.includes(imageLink)) { | ||||
|                         // if there wasn't any match for the reference, we'll add the image anyway
 | ||||
|                         // otherwise image would be removed since no note would include it
 | ||||
|                         // if there wasn't any match for the reference, we'll add the image anyway,
 | ||||
|                         // otherwise the image would be removed since no note would include it
 | ||||
|                         content += imageLink; | ||||
|                     } | ||||
|                 } catch (e) { | ||||
|  | ||||
| @ -45,7 +45,7 @@ async function migrate() { | ||||
| 
 | ||||
|     migrations.sort((a, b) => a.dbVersion - b.dbVersion); | ||||
| 
 | ||||
|     // all migrations are executed in one transaction - upgrade either succeeds or the user can stay at the old version
 | ||||
|     // all migrations are executed in one transaction - upgrade either succeeds, or the user can stay at the old version
 | ||||
|     // otherwise if half of the migrations succeed, user can't use any version - DB is too "new" for the old app,
 | ||||
|     // and too old for the new app version.
 | ||||
|     sql.transactional(() => { | ||||
|  | ||||
| @ -4,7 +4,7 @@ const Expression = require('./expression'); | ||||
| const NoteSet = require('../note_set'); | ||||
| 
 | ||||
| /** | ||||
|  * Note is hidden when all its note paths start in hidden subtree (i.e. the note is not cloned into visible tree) | ||||
|  * Note is hidden when all its note paths start in hidden subtree (i.e., the note is not cloned into visible tree) | ||||
|  */ | ||||
| class IsHiddenExp extends Expression { | ||||
|     execute(inputNoteSet, executionContext, searchContext) { | ||||
|  | ||||
| @ -91,7 +91,7 @@ class NoteContentFulltextExp extends Expression { | ||||
|             const nonMatchingToken = this.tokens.find(token => | ||||
|                 !content.includes(token) && | ||||
|                 ( | ||||
|                     // in case of default fulltext search we should consider both title, attrs and content
 | ||||
|                     // in case of default fulltext search, we should consider both title, attrs and content
 | ||||
|                     // so e.g. "hello world" should match when "hello" is in title and "world" in content
 | ||||
|                     !this.flatText | ||||
|                     || !becca.notes[noteId].getFlatText().includes(token) | ||||
|  | ||||
| @ -1,5 +1,5 @@ | ||||
| /** | ||||
|  * This will create a recursive object from list of tokens - tokens between parenthesis are grouped in a single array | ||||
|  * This will create a recursive object from a list of tokens - tokens between parenthesis are grouped in a single array | ||||
|  */ | ||||
| function handleParens(tokens) { | ||||
|     if (tokens.length === 0) { | ||||
|  | ||||
| @ -80,7 +80,7 @@ function lex(str) { | ||||
|                 quotes = false; | ||||
|             } | ||||
|             else { | ||||
|                 // it's a quote but within other kind of quotes, so it's valid as a literal character
 | ||||
|                 // it's a quote, but within other kind of quotes, so it's valid as a literal character
 | ||||
|                 currentWord += chr; | ||||
|             } | ||||
| 
 | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 zadam
						zadam