This commit is contained in:
zadam 2023-05-05 23:41:11 +02:00
parent 6dfc72c065
commit fa3cbb4645
54 changed files with 124 additions and 128 deletions

View File

@ -54,7 +54,7 @@ function getNoteTitleArrayForPath(notePathArray) {
let parentNoteId = 'root'; let parentNoteId = 'root';
let hoistedNotePassed = false; 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 hoistedNoteId = cls.getHoistedNoteId();
const outsideOfHoistedSubtree = !notePathArray.includes(hoistedNoteId); const outsideOfHoistedSubtree = !notePathArray.includes(hoistedNoteId);

View File

@ -119,7 +119,7 @@ class BAttachment extends AbstractBeccaEntity {
throw new Error(`Mapping from attachment role '${this.role}' to note's type is not defined`); 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`); throw new Error(`Cannot convert protected attachment outside of protected session`);
} }

View File

@ -224,7 +224,7 @@ class BBranch extends AbstractBeccaEntity {
for (const childBranch of this.parentNote.getChildBranches()) { for (const childBranch of this.parentNote.getChildBranches()) {
if (maxNotePos < childBranch.notePosition 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; maxNotePos = childBranch.notePosition;
} }

View File

@ -9,7 +9,7 @@ const sql = require("../../services/sql");
const BAttachment = require("./battachment"); 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. * It's used for seamless note versioning.
* *
* @extends AbstractBeccaEntity * @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 * 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 * 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. * if we don't need a content, especially for bulk operations like search.
* *

View File

@ -158,7 +158,7 @@ export default class Entrypoints extends Component {
async runActiveNoteCommand() { async runActiveNoteCommand() {
const {ntxId, note} = appContext.tabManager.getActiveContext(); 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') { if (!note || note.type !== 'code') {
return; return;
} }

View File

@ -5,7 +5,7 @@ import hoistedNoteService from "../services/hoisted_note.js";
import Component from "./component.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 * 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. * must be at the root of the component tree.
*/ */

View File

@ -106,7 +106,7 @@ class NoteContext extends Component {
} }
isMainContext() { isMainContext() {
// if null then this is a main context // if null, then this is a main context
return !this.mainNtxId; return !this.mainNtxId;
} }
@ -127,7 +127,7 @@ class NoteContext extends Component {
saveToRecentNotes(resolvedNotePath) { saveToRecentNotes(resolvedNotePath) {
setTimeout(async () => { 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) { if (resolvedNotePath && resolvedNotePath === this.notePath) {
await server.post('recent-notes', { await server.post('recent-notes', {
noteId: this.note.noteId, noteId: this.note.noteId,
@ -172,7 +172,7 @@ class NoteContext extends Component {
getPojoState() { getPojoState() {
if (!this.notePath && this.hoistedNoteId === 'root') { 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; return null;
} }
@ -288,7 +288,7 @@ class NoteContext extends Component {
resetViewScope() { resetViewScope() {
// view scope contains data specific to one note context and one "view". // 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 is reset after navigating to a different note
this.viewScope = {}; this.viewScope = {};
} }

View File

@ -6,4 +6,4 @@
<li><code>keyboardLauncher</code> - optional, pressing the keyboard launcher will open the note</li> <li><code>keyboardLauncher</code> - optional, pressing the keyboard launcher will open the note</li>
</ol> </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>

View File

@ -195,7 +195,7 @@ class FNote {
} }
// will sort the parents so that non-search & non-archived are first and archived at the end // 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() { sortParents() {
this.parents.sort((aNoteId, bNoteId) => { this.parents.sort((aNoteId, bNoteId) => {
const aBranchId = this.parentToBranch[aNoteId]; const aBranchId = this.parentToBranch[aNoteId];
@ -360,7 +360,7 @@ class FNote {
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) {
@ -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'] * @param {string} [hoistedNoteId='root']
* @return {string[]} array of noteIds constituting the particular note path * @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'] * @param {string} [hoistedNoteId='root']
* @return {string} serialized note path (e.g. 'root/a1h315/js725h') * @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 // we're not checking hideArchivedNotes since that would mean we need to lazy load the child notes
// which would seriously slow down everything. // which would seriously slow down everything.
// we check this flag only once user chooses to expand the parent. This has the negative consequence that // 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; return childBranches;
} }
@ -597,7 +597,7 @@ class FNote {
/** /**
* @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 {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) { getOwnedAttribute(type, name) {
const attributes = this.getOwnedAttributes(); const attributes = this.getOwnedAttributes();
@ -608,7 +608,7 @@ class FNote {
/** /**
* @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 {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) { getAttribute(type, name) {
const attributes = this.getAttributes(); const attributes = this.getAttributes();
@ -619,7 +619,7 @@ class FNote {
/** /**
* @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} 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) { getOwnedAttributeValue(type, name) {
const attr = this.getOwnedAttribute(type, name); const attr = this.getOwnedAttribute(type, name);
@ -630,7 +630,7 @@ class FNote {
/** /**
* @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} 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) { getAttributeValue(type, name) {
const attr = this.getAttribute(type, name); const attr = this.getAttribute(type, name);
@ -774,7 +774,7 @@ class FNote {
return def && def.isPromoted; 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) => { promotedAttrs.sort((a, b) => {
if (a.noteId === b.noteId) { if (a.noteId === b.noteId) {
return a.position < b.position ? -1 : 1; return a.position < b.position ? -1 : 1;

View File

@ -128,8 +128,8 @@ export default class DesktopLayout {
) )
.child( .child(
new RibbonContainer() new RibbonContainer()
// order of the widgets matter. Some of these want to "activate" themselves // 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". // when visible. When this happens to multiple of them, the first one "wins".
// promoted attributes should always win. // promoted attributes should always win.
.ribbon(new PromotedAttributesWidget()) .ribbon(new PromotedAttributesWidget())
.ribbon(new ScriptExecutorWidget()) .ribbon(new ScriptExecutorWidget())

View File

@ -67,7 +67,7 @@ function lex(str) {
finishWord(i - 1); finishWord(i - 1);
} }
else { else {
// it's a quote but within other kind of quotes, so it's valid as a literal character // it's a quote, but within other kind of quotes, so it's valid as a literal character
currentWord += chr; currentWord += chr;
} }
continue; continue;

View File

@ -17,7 +17,7 @@ async function renderAttribute(attribute, renderIsInheritable) {
return $attr; 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) { if (attribute.value) {
$attr.append(document.createTextNode(`~${attribute.name}${isInheritable}=`)); $attr.append(document.createTextNode(`~${attribute.name}${isInheritable}=`));
$attr.append(await createNoteLink(attribute.value)); $attr.append(await createNoteLink(attribute.value));

View File

@ -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) { async function cloneNoteAfter(noteId, afterBranchId) {
const resp = await server.put(`notes/${noteId}/clone-after/${afterBranchId}`); const resp = await server.put(`notes/${noteId}/clone-after/${afterBranchId}`);

View File

@ -24,7 +24,7 @@ class Froca {
async loadInitialTree() { async loadInitialTree() {
const resp = await server.get('tree'); 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>} */ /** @type {Object.<string, FNote>} */
this.notes = {}; this.notes = {};
@ -67,7 +67,7 @@ class Froca {
if (note) { if (note) {
note.update(noteRow); 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') { if (note.type !== 'search') {
for (const childNoteId of note.children) { for (const childNoteId of note.children) {
const childNote = this.notes[childNoteId]; const childNote = this.notes[childNoteId];

View File

@ -143,8 +143,8 @@ 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);
} }

View File

@ -25,9 +25,9 @@ function FrontendScriptApi(startNote, currentNote, originEntity = null, $contain
/** @property {jQuery} container of all the rendered script content */ /** @property {jQuery} container of all the rendered script content */
this.$container = $container; this.$container = $container;
/** @property {object} note where script started executing */ /** @property {object} note where the script started executing */
this.startNote = startNote; this.startNote = startNote;
/** @property {object} note where script is currently executing */ /** @property {object} note where the script is currently executing */
this.currentNote = currentNote; this.currentNote = currentNote;
/** @property {object|null} entity whose event triggered this execution */ /** @property {object|null} entity whose event triggered this execution */
this.originEntity = originEntity; this.originEntity = originEntity;
@ -164,7 +164,7 @@ function FrontendScriptApi(startNote, currentNote, originEntity = null, $contain
params: prepareParams(params), params: prepareParams(params),
startNoteId: startNote.noteId, startNoteId: startNote.noteId,
currentNoteId: currentNote.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 originEntityId: originEntity ? originEntity.noteId : null
}, "script"); }, "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 * @method
* @param {string} noteId * @param {string} noteId
@ -214,7 +214,7 @@ function FrontendScriptApi(startNote, currentNote, originEntity = null, $contain
this.getNote = async noteId => await froca.getNote(noteId); 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 * 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()) * otherwise (by e.g. createNoteLink())
@ -258,7 +258,7 @@ function FrontendScriptApi(startNote, currentNote, originEntity = null, $contain
this.parseDate = utils.parseDate; this.parseDate = utils.parseDate;
/** /**
* Show info message to the user. * Show an info message to the user.
* *
* @method * @method
* @param {string} message * @param {string} message
@ -266,7 +266,7 @@ function FrontendScriptApi(startNote, currentNote, originEntity = null, $contain
this.showMessage = toastService.showMessage; this.showMessage = toastService.showMessage;
/** /**
* Show error message to the user. * Show an error message to the user.
* *
* @method * @method
* @param {string} message * @param {string} message
@ -292,7 +292,7 @@ function FrontendScriptApi(startNote, currentNote, originEntity = null, $contain
this.triggerEvent = (name, data) => appContext.triggerEvent(name, data); 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 * @method
* @param {string} notePath (or noteId) * @param {string} notePath (or noteId)
@ -319,7 +319,7 @@ function FrontendScriptApi(startNote, currentNote, originEntity = null, $contain
this.getActiveContextNote = () => appContext.tabManager.getActiveContextNote(); 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 * @method
* @returns {Promise<BalloonEditor>} instance of CKEditor * @returns {Promise<BalloonEditor>} instance of CKEditor
@ -345,12 +345,12 @@ function FrontendScriptApi(startNote, currentNote, originEntity = null, $contain
/** /**
* @method * @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(); 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 * @method
* @param {Element} el - DOM element * @param {Element} el - DOM element
@ -455,11 +455,11 @@ function FrontendScriptApi(startNote, currentNote, originEntity = null, $contain
this.bindGlobalShortcut = shortcutService.bindGlobalShortcut; 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. * frontend will get asynchronously synchronized.
* *
* This method returns a promise which resolves once all the backend -> frontend synchronization is finished. * 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 * @method
* @returns {Promise<void>} * @returns {Promise<void>}

View File

@ -19,7 +19,7 @@ async function createNoteLink(notePath, options = {}) {
if (!notePath.startsWith("root")) { if (!notePath.startsWith("root")) {
// all note paths should start with "root/" (except for "root" itself) // 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}`; notePath = `root/${notePath}`;
} }
@ -222,7 +222,7 @@ async function loadReferenceLinkTitle(noteId, $el) {
} }
$(document).on('click', "a", goToLink); $(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('contextmenu', 'a', linkContextMenu);
$(document).on('dblclick', "a", e => { $(document).on('dblclick', "a", e => {
e.preventDefault(); e.preventDefault();

View File

@ -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 * 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. * as loading the tree which uses attributes heavily.
*/ */
class NoteAttributeCache { class NoteAttributeCache {
constructor() { constructor() {
/** @property {Object.<string, BAttribute[]>} */
this.attributes = {}; this.attributes = {};
} }
@ -17,4 +18,4 @@ class NoteAttributeCache {
const noteAttributeCache = new NoteAttributeCache(); const noteAttributeCache = new NoteAttributeCache();
export default noteAttributeCache; export default noteAttributeCache;

View File

@ -143,7 +143,7 @@ function initNoteAutocomplete($el, options) {
hint: false, hint: false,
autoselect: true, autoselect: true,
// openOnFocus has to be false, otherwise re-focus (after return from note type chooser dialog) forces // 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, openOnFocus: false,
minLength: 0, minLength: 0,
tabAutocomplete: false tabAutocomplete: false

View File

@ -74,7 +74,7 @@ async function getRenderedContent(note, options = {}) {
$downloadButton.on('click', () => openService.downloadFileNote(note.noteId)); $downloadButton.on('click', () => openService.downloadFileNote(note.noteId));
$openButton.on('click', () => openService.openNoteExternally(note.noteId, note.mime)); $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); $openButton.toggle(!note.isProtected);
const $content = $('<div style="display: flex; flex-direction: column; height: 100%;">'); const $content = $('<div style="display: flex; flex-direction: column; height: 100%;">');
@ -163,7 +163,7 @@ async function getRenderedContent(note, options = {}) {
} }
} }
else if (type === 'book') { 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') { else if (!options.tooltip && type === 'protectedSession') {
const $button = $(`<button class="btn btn-sm"><span class="bx bx-log-in"></span> Enter protected session</button>`) const $button = $(`<button class="btn btn-sm"><span class="bx bx-log-in"></span> Enter protected session</button>`)

View File

@ -13,8 +13,8 @@ async function createNote(parentNotePath, options = {}) {
target: 'into' target: 'into'
}, options); }, options);
// if isProtected isn't available (user didn't enter password yet), then note is created as unencrypted // if isProtected isn't available (user didn't enter password yet), then note is created as unencrypted,
// but this is quite weird since user doesn't see WHERE the note is being created, so it shouldn't occur often // but this is quite weird since the user doesn't see WHERE the note is being created, so it shouldn't occur often
if (!options.isProtected || !protectedSessionHolder.isProtectedSessionAvailable()) { if (!options.isProtected || !protectedSessionHolder.isProtectedSessionAvailable()) {
options.isProtected = false; options.isProtected = false;
} }
@ -93,7 +93,7 @@ async function createNoteWithTypePrompt(parentNotePath, options = {}) {
return await createNote(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) { function parseSelectedHtml(selectedHtml) {
const dom = $.parseHTML(selectedHtml); const dom = $.parseHTML(selectedHtml);

View File

@ -29,7 +29,7 @@ export default class HistoryNavigationButton extends ButtonFromNoteWidget {
this.webContents = utils.dynamicRequire('@electron/remote').getCurrentWebContents(); 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.webContents.clearHistory();
this.refresh(); this.refresh();
@ -87,7 +87,7 @@ export default class HistoryNavigationButton extends ButtonFromNoteWidget {
return; 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) // block UI thread for > 1 second on specific notes (book notes displaying underlying render notes with scripts)
// this.$backInHistory.toggleClass('disabled', !this.webContents.canGoBack()); // this.$backInHistory.toggleClass('disabled', !this.webContents.canGoBack());

View File

@ -141,7 +141,7 @@ export default class ExportDialog extends BasicWidget {
const exportType = this.$widget.find("input[name='export-type']:checked").val(); const exportType = this.$widget.find("input[name='export-type']:checked").val();
if (!exportType) { 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"); toastService.showError("Choose export type first please");
return; return;
} }

View File

@ -67,10 +67,10 @@ export default class JumpToNoteDialog extends BasicWidget {
appContext.tabManager.getActiveContext().setNote(suggestion.notePath); appContext.tabManager.getActiveContext().setNote(suggestion.notePath);
}); });
// if you open the Jump To dialog soon after using it previously it can often mean that you // 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) // actually want to search for the same thing (e.g., you opened the wrong note at first try)
// so we'll keep the content. // 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) { if (Date.now() - this.lastOpenedTs > KEEP_LAST_SEARCH_FOR_X_SECONDS * 1000) {
noteAutocompleteService.showRecentNotes(this.$autoComplete); noteAutocompleteService.showRecentNotes(this.$autoComplete);
} else { } else {

View File

@ -11,7 +11,7 @@ import FindInHtml from "./find_in_html.js";
const findWidgetDelayMillis = 200; const findWidgetDelayMillis = 200;
const waitForEnter = (findWidgetDelayMillis < 0); 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 // 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 // of undefined. It's -1 instead of > 0, so they don't tabstop
const TPL = ` const TPL = `
@ -185,7 +185,7 @@ export default class FindWidget extends NoteContextAwareWidget {
startSearch() { startSearch() {
// XXX This should clear the previous search immediately in all cases // XXX This should clear the previous search immediately in all cases
// (the search is stale when waitforenter but also while the // (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) { if (!waitForEnter) {
// Clear the previous timeout if any, it's ok if timeoutId is // Clear the previous timeout if any, it's ok if timeoutId is
// null or undefined // null or undefined

View File

@ -21,7 +21,7 @@ export default class FindInCode {
// highlightSelectionMatches is the overlay that highlights // highlightSelectionMatches is the overlay that highlights
// the words under the cursor. This occludes the search // 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 // the focus is back into the note
this.oldHighlightSelectionMatches = codeEditor.getOption("highlightSelectionMatches"); this.oldHighlightSelectionMatches = codeEditor.getOption("highlightSelectionMatches");
codeEditor.setOption("highlightSelectionMatches", false); codeEditor.setOption("highlightSelectionMatches", false);
@ -69,14 +69,13 @@ export default class FindInCode {
let curChar = 0; let curChar = 0;
let curMatch = null; let curMatch = null;
findResult = []; findResult = [];
// All those markText take several seconds on 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 // script, batch them inside an operation, so they become
// unnoticeable. Alternatively, an overlay could be used, see // unnoticeable. Alternatively, an overlay could be used, see
// https://codemirror.net/addon/search/match-highlighter.js ? // https://codemirror.net/addon/search/match-highlighter.js ?
codeEditor.operation(() => { codeEditor.operation(() => {
for (let i = 0; i < text.length; ++i) { for (let i = 0; i < text.length; ++i) {
// Fetch next match if it's the first time or // Fetch the next match if it's the first time or if past the current match start
// if past the current match start
if ((curMatch == null) || (curMatch.index < i)) { if ((curMatch == null) || (curMatch.index < i)) {
curMatch = re.exec(text); curMatch = re.exec(text);
if (curMatch == null) { if (curMatch == null) {
@ -88,16 +87,13 @@ export default class FindInCode {
// selected marker highlight will be done later // selected marker highlight will be done later
if (i === curMatch.index) { if (i === curMatch.index) {
let fromPos = { "line" : curLine, "ch" : curChar }; let fromPos = { "line" : curLine, "ch" : curChar };
// XXX If multiline is supported, this needs to // If multiline is supported, this needs to recalculate curLine since the match may span lines
// recalculate curLine since the match may span
// lines
let toPos = { "line" : curLine, "ch" : curChar + curMatch[0].length}; 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 }); let marker = doc.markText( fromPos, toPos, { "className" : FIND_RESULT_CSS_CLASSNAME });
findResult.push(marker); findResult.push(marker);
// Set the first match beyond the cursor as current // Set the first match beyond the cursor as the current match
// match
if (currentFound === -1) { if (currentFound === -1) {
const cursorPos = codeEditor.getCursor(); const cursorPos = codeEditor.getCursor();
if ((fromPos.line > cursorPos.line) || if ((fromPos.line > cursorPos.line) ||

View File

@ -64,8 +64,7 @@ export default class FindInText {
if (totalFound > 0) { if (totalFound > 0) {
currentFound = Math.max(0, currentFound); currentFound = Math.max(0, currentFound);
// XXX Do this accessing the private data? // XXX Do this accessing the private data?
// See // See https://github.com/ckeditor/ckeditor5/blob/b95e2faf817262ac0e1e21993d9c0bde3f1be594/packages/ckeditor5-find-and-replace/src/findnextcommand.js
// https://github.com/ckeditor/ckeditor5/blob/b95e2faf817262ac0e1e21993d9c0bde3f1be594/packages/ckeditor5-find-and-replace/src/findnextcommand.js
for (let i = 0 ; i < currentFound; ++i) { for (let i = 0 ; i < currentFound; ++i) {
textEditor.execute('findNext', searchTerm); textEditor.execute('findNext', searchTerm);
} }

View File

@ -1,4 +1,4 @@
// taken from HTML source of https://boxicons.com/ // taken from the HTML source of https://boxicons.com/
const categories = [ const categories = [
{"name": "All categories", "id": 0}, {"name": "All categories", "id": 0},

View File

@ -101,7 +101,7 @@ export default class MermaidWidget extends NoteContextAwareWidget {
const blob = await this.note.getBlob(); const blob = await this.note.getBlob();
const content = blob.content || ""; 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 // with error details
mermaid.mermaidAPI.render(`mermaid-graph-${idCounter}`, content, cb); mermaid.mermaidAPI.render(`mermaid-graph-${idCounter}`, content, cb);
} }

View File

@ -72,7 +72,7 @@ export default class NoteContextAwareWidget extends BasicWidget {
async refreshWithNote(note) {} async refreshWithNote(note) {}
async noteSwitchedEvent({noteContext, notePath}) { 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) { if (noteContext.notePath === notePath) {
await this.noteSwitched(); await this.noteSwitched();
} }
@ -92,11 +92,11 @@ export default class NoteContextAwareWidget extends BasicWidget {
await this.refresh(); 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}) { async noteSwitchedAndActivatedEvent({noteContext, notePath}) {
this.noteContext = noteContext; 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) { if (this.notePath === notePath) {
await this.refresh(); await this.refresh();
} }

View File

@ -149,7 +149,7 @@ export default class NoteDetailWidget extends NoteContextAwareWidget {
await typeWidget.handleEvent('setNoteContext', {noteContext: this.noteContext}); 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', { await typeWidget.handleEvent('noteSwitched', {
noteContext: this.noteContext, noteContext: this.noteContext,
notePath: this.noteContext.notePath notePath: this.noteContext.notePath
@ -298,13 +298,13 @@ export default class NoteDetailWidget extends NoteContextAwareWidget {
// FIXME: create a separate event to force hierarchical refresh // 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 // 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}); this.handleEvent('noteTypeMimeChanged', {noteId: this.noteId});
} }
else if (loadResults.isNoteReloaded(this.noteId, this.componentId) else if (loadResults.isNoteReloaded(this.noteId, this.componentId)
&& (this.type !== await this.getWidgetType() || this.mime !== this.note.mime)) { && (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}); this.triggerEvent('noteTypeMimeChanged', {noteId: this.noteId});
} }
else { else {

View File

@ -138,7 +138,7 @@ export default class FilePropertiesWidget extends NoteContextAwareWidget {
this.$fileSize.text(`${blob.contentLength} bytes`); 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.$openButton.toggle(!note.isProtected);
this.$downloadButton.toggle(!note.isProtected || protectedSessionHolder.isProtectedSessionAvailable()) this.$downloadButton.toggle(!note.isProtected || protectedSessionHolder.isProtectedSessionAvailable())
this.$uploadNewRevisionButton.toggle(!note.isProtected || protectedSessionHolder.isProtectedSessionAvailable()) this.$uploadNewRevisionButton.toggle(!note.isProtected || protectedSessionHolder.isProtectedSessionAvailable())

View File

@ -59,7 +59,7 @@ export default class EditableCodeTypeWidget extends TypeWidget {
lineNumbers: true, lineNumbers: true,
tabindex: 300, tabindex: 300,
// we line wrap partly also because without it horizontal scrollbar displays only when you scroll // 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'), lineWrapping: options.is('codeLineWrapEnabled'),
dragDrop: false, // with true the editor inlines dropped files which is not what we expect dragDrop: false, // with true the editor inlines dropped files which is not what we expect
placeholder: "Type the content of your code note here...", 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(); 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 consistency check)
// we provide fallback // we provide fallback
this.codeEditor.setValue(blob.content || ""); this.codeEditor.setValue(blob.content || "");
this.codeEditor.clearHistory(); this.codeEditor.clearHistory();

View File

@ -113,8 +113,8 @@ export default class EditableTextTypeWidget extends AbstractTextTypeWidget {
label: mt.title label: mt.title
})); }));
// CKEditor since version 12 needs the element to be visible before initialization. At the same time // 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 // 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. // display of $widget in both branches.
this.$widget.show(); this.$widget.show();
@ -131,7 +131,7 @@ export default class EditableTextTypeWidget extends AbstractTextTypeWidget {
// This prevents an infinite restart loop. // This prevents an infinite restart loop.
crashNumberLimit: 3, crashNumberLimit: 3,
// A minimum number of milliseconds between saving the editor data internally (defaults to 5000). // A minimum number of milliseconds between saving the editor data internally (defaults to 5000).
// Note that for large documents this might impact the editor performance. // Note that for large documents, this might impact the editor performance.
saveInterval: 5000 saveInterval: 5000
}); });
@ -194,7 +194,7 @@ export default class EditableTextTypeWidget extends AbstractTextTypeWidget {
const content = this.watchdog.editor.getData(); const content = this.watchdog.editor.getData();
// if content is only tags/whitespace (typically <p>&nbsp;</p>), then just make it empty // if content is only tags/whitespace (typically <p>&nbsp;</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 { return {
content: utils.isHtmlEmpty(content) ? '' : content content: utils.isHtmlEmpty(content) ? '' : content
}; };

View File

@ -13,7 +13,7 @@ const ValidationError = require("../../errors/validation_error");
const NotFoundError = require("../../errors/not_found_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. * for not deleted branches. There may be multiple deleted note-parent note relationships.
*/ */
@ -80,7 +80,7 @@ function moveBranchBeforeNote(req) {
treeService.sortNotesIfNeeded(parentNote.noteId); 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); entityChangesService.addNoteReorderingEntityChange(parentNote.noteId);
log.info(`Moved note ${branchToMove.noteId}, branch ${branchId} before note ${beforeBranch.noteId}, branch ${beforeBranchId}`); 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); 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); entityChangesService.addNoteReorderingEntityChange(parentNote.noteId);
log.info(`Moved note ${branchToMove.noteId}, branch ${branchId} after note ${afterNote.noteId}, branch ${afterBranchId}`); log.info(`Moved note ${branchToMove.noteId}, branch ${branchId} after note ${afterNote.noteId}, branch ${afterBranchId}`);

View File

@ -106,7 +106,7 @@ const downloadAttachment = (req, res) => downloadAttachmentInt(req.params.attach
const openAttachment = (req, res) => downloadAttachmentInt(req.params.attachmentId, res, false); const openAttachment = (req, res) => downloadAttachmentInt(req.params.attachmentId, res, false);
function fileContentProvider(req) { function fileContentProvider(req) {
// Read file name from route params. // Read the file name from route params.
const note = becca.getNote(req.params.noteId); const note = becca.getNote(req.params.noteId);
if (!note) { if (!note) {
throw new NotFoundError(`Note '${req.params.noteId}' doesn't exist.`); throw new NotFoundError(`Note '${req.params.noteId}' doesn't exist.`);
@ -116,7 +116,7 @@ function fileContentProvider(req) {
} }
function attachmentContentProvider(req) { function attachmentContentProvider(req) {
// Read file name from route params. // Read the file name from route params.
const attachment = becca.getAttachment(req.params.attachmentId); const attachment = becca.getAttachment(req.params.attachmentId);
if (!attachment) { if (!attachment) {
throw new NotFoundError(`Attachment '${req.params.attachmentId}' doesn't exist.`); throw new NotFoundError(`Attachment '${req.params.attachmentId}' doesn't exist.`);

View File

@ -72,7 +72,7 @@ async function importToBranch(req) {
} }
if (last === "true") { 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({ setTimeout(() => taskContext.taskSucceeded({
parentNoteId: parentNoteId, parentNoteId: parentNoteId,
importedNoteId: note.noteId importedNoteId: note.noteId

View File

@ -61,7 +61,7 @@ function handleRequest(req, res) {
throw new Error(`Unrecognized attribute name '${attr.name}'`); 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.`; const message = `No handler matched for custom '${path}' request.`;

View File

@ -29,7 +29,7 @@ function installLocalAppIcon() {
} }
if (!fs.existsSync(path.resolve(ELECTRON_APP_ROOT_DIR, "trilium-portable.sh"))) { 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 // only in such case it's necessary to create an icon
return; return;
} }

View File

@ -63,7 +63,7 @@ function addEntityChange(entityChange) {
const entityChangeIds = namespace.get('entityChangeIds') || []; 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); entityChangeIds.push(entityChange.id);
namespace.set('entityChangeIds', entityChangeIds); namespace.set('entityChangeIds', entityChangeIds);

View File

@ -239,11 +239,11 @@ class ConsistencyChecks {
} }
findExistencyIssues() { findExistencyIssues() {
// principle for fixing inconsistencies is that if the note itself is deleted (isDeleted=true) then all related // 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 note is not deleted, // entities should be also deleted (branches, attributes), but if the note is not deleted,
// then at least one branch should exist. // 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 // another check might create missing branch
this.findAndFixIssues(` this.findAndFixIssues(`
SELECT branchId, SELECT branchId,
@ -376,7 +376,7 @@ class ConsistencyChecks {
({noteId, type}) => { ({noteId, type}) => {
if (this.autoFix) { if (this.autoFix) {
const note = becca.getNote(noteId); 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(); note.save();
this.reloadNeeded = true; this.reloadNeeded = true;
@ -613,7 +613,7 @@ class ConsistencyChecks {
entityChangesService.addEntityChange({ entityChangesService.addEntityChange({
entityName, entityName,
entityId, 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, isErased: !!entity.isErased,
utcDateChanged: entity.utcDateModified || entity.utcDateCreated, utcDateChanged: entity.utcDateModified || entity.utcDateCreated,
isSynced: entityName !== 'options' || entity.isSynced 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 // - 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 // - 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) // - 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]); sql.execute('UPDATE attributes SET name = ? WHERE name = ?', [fixedName, origName]);
this.fixedIssues = true; this.fixedIssues = true;
@ -804,7 +804,7 @@ class ConsistencyChecks {
function getBlankContent(isProtected, type, mime) { function getBlankContent(isProtected, type, mime) {
if (isProtected) { 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') { if (mime === 'application/json') {

View File

@ -5,7 +5,7 @@
* - if TRILIUM_DATA_DIR environment variable exists, then its value is used as the path * - 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 * - 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 * - 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'); const os = require('os');

View File

@ -13,7 +13,7 @@ function arraysIdentical(a, b) {
} }
function shaArray(content) { 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(); return crypto.createHash('sha1').update(content).digest();
} }

View File

@ -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, // 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 // "trilium-local-now-datetime" header which is then stored in CLS
function localNowDateTime() { function localNowDateTime() {
return cls.getLocalNowDateTime() return cls.getLocalNowDateTime()

View File

@ -12,7 +12,7 @@ function runAttachedRelations(note, relationName, originEntity) {
return; 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( const notesToRun = new Set(
note.getRelations(relationName) note.getRelations(relationName)
.map(relation => relation.getTargetNote()) .map(relation => relation.getTargetNote())
@ -203,7 +203,7 @@ eventService.subscribe(eventService.ENTITY_CHANGED, ({ entityName, entity }) =>
eventService.subscribe(eventService.ENTITY_DELETED, ({ entityName, entity }) => { eventService.subscribe(eventService.ENTITY_DELETED, ({ entityName, entity }) => {
processInverseRelations(entityName, entity, (definition, note, targetNote) => { 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); const relations = targetNote.getOwnedRelations(definition.inverseRelation);
for (const relation of relations) { for (const relation of relations) {

View File

@ -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 * 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 * 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 same IDs. * duplicate subtrees. This way, all instances will generate the same structure with the same IDs.
*/ */
const HIDDEN_SUBTREE_DEFINITION = { const HIDDEN_SUBTREE_DEFINITION = {
@ -24,7 +24,7 @@ const HIDDEN_SUBTREE_DEFINITION = {
title: 'Hidden Notes', title: 'Hidden Notes',
type: 'doc', type: 'doc',
icon: 'bx bx-chip', 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 // over tree when it's in the middle
notePosition: 999_999_999, notePosition: 999_999_999,
attributes: [ attributes: [
@ -298,14 +298,14 @@ function checkHiddenSubtreeRecursively(parentNoteId, item) {
} }
if (note.type !== item.type) { if (note.type !== item.type) {
// enforce correct note type // enforce a correct note type
note.type = item.type; note.type = item.type;
note.save(); note.save();
} }
if (branch) { if (branch) {
// in case of launchers the branch ID is not preserved and should not be relied upon - launchers which move between // 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) { if (item.notePosition !== undefined && branch.notePosition !== item.notePosition) {
branch.notePosition = item.notePosition; branch.notePosition = item.notePosition;
branch.save(); branch.save();

View File

@ -2,8 +2,8 @@ const sanitizeHtml = require('sanitize-html');
const sanitizeUrl = require('@braintree/sanitize-url').sanitizeUrl; const sanitizeUrl = require('@braintree/sanitize-url').sanitizeUrl;
// intended mainly as protection against XSS via import // intended mainly as protection against XSS via import
// secondarily it (partly) protects against "CSS takeover" // 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 // sanitize also note titles, label values etc. - there are so many usages which make it difficult to guarantee all of them
// are properly handled // are properly handled
function sanitize(dirtyHtml) { function sanitize(dirtyHtml) {
if (!dirtyHtml) { if (!dirtyHtml) {

View File

@ -191,8 +191,8 @@ async function shrinkImage(buffer, originalName) {
finalImageBuffer = buffer; finalImageBuffer = buffer;
} }
// if resizing did not help with size then save the original // if resizing did not help with size, then save the original
// (can happen when e.g. resizing PNG into JPEG) // (can happen when e.g., resizing PNG into JPEG)
if (finalImageBuffer.byteLength >= buffer.byteLength) { if (finalImageBuffer.byteLength >= buffer.byteLength) {
finalImageBuffer = buffer; finalImageBuffer = buffer;
} }
@ -216,7 +216,7 @@ async function resize(buffer, quality) {
image.quality(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); image.background(0xFFFFFFFF);
const resultBuffer = await image.getBufferAsync(jimp.MIME_JPEG); const resultBuffer = await image.getBufferAsync(jimp.MIME_JPEG);

View File

@ -317,8 +317,8 @@ function importEnex(taskContext, file, parentNote) {
content = content.replace(mediaRegex, imageLink); content = content.replace(mediaRegex, imageLink);
if (!content.includes(imageLink)) { if (!content.includes(imageLink)) {
// if there wasn't any match for the reference, we'll add the image anyway // 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 // otherwise the image would be removed since no note would include it
content += imageLink; content += imageLink;
} }
} catch (e) { } catch (e) {

View File

@ -45,7 +45,7 @@ async function migrate() {
migrations.sort((a, b) => a.dbVersion - b.dbVersion); 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, // 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. // and too old for the new app version.
sql.transactional(() => { sql.transactional(() => {

View File

@ -4,7 +4,7 @@ const Expression = require('./expression');
const NoteSet = require('../note_set'); 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 { class IsHiddenExp extends Expression {
execute(inputNoteSet, executionContext, searchContext) { execute(inputNoteSet, executionContext, searchContext) {

View File

@ -91,7 +91,7 @@ class NoteContentFulltextExp extends Expression {
const nonMatchingToken = this.tokens.find(token => const nonMatchingToken = this.tokens.find(token =>
!content.includes(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 // so e.g. "hello world" should match when "hello" is in title and "world" in content
!this.flatText !this.flatText
|| !becca.notes[noteId].getFlatText().includes(token) || !becca.notes[noteId].getFlatText().includes(token)

View File

@ -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) { function handleParens(tokens) {
if (tokens.length === 0) { if (tokens.length === 0) {

View File

@ -80,7 +80,7 @@ function lex(str) {
quotes = false; quotes = false;
} }
else { else {
// it's a quote but within other kind of quotes, so it's valid as a literal character // it's a quote, but within other kind of quotes, so it's valid as a literal character
currentWord += chr; currentWord += chr;
} }