This commit is contained in:
zadam 2023-06-30 11:18:34 +02:00
parent fc564f6aed
commit 192e399cb5
77 changed files with 160 additions and 159 deletions

1
package-lock.json generated
View File

@ -5,7 +5,6 @@
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "trilium",
"version": "0.60.4", "version": "0.60.4",
"hasInstallScript": true, "hasInstallScript": true,
"license": "AGPL-3.0-only", "license": "AGPL-3.0-only",

View File

@ -105,7 +105,7 @@ eventService.subscribeBeccaLoader(eventService.ENTITY_CHANGED, ({entityName, en
* *
* @param entityName * @param entityName
* @param entityRow - can be a becca entity (change comes from this trilium instance) or just a row (from sync). * @param entityRow - can be a becca entity (change comes from this trilium instance) or just a row (from sync).
* Should be therefore treated as a row. * It should be therefore treated as a row.
*/ */
function postProcessEntityUpdate(entityName, entityRow) { function postProcessEntityUpdate(entityName, entityRow) {
if (entityName === 'notes') { if (entityName === 'notes') {
@ -188,7 +188,7 @@ function branchUpdated(branchRow) {
childNote.sortParents(); childNote.sortParents();
// notes in the subtree can get new inherited attributes // notes in the subtree can get new inherited attributes
// this is in theory needed upon branch creation, but there's no create event for sync changes // this is in theory needed upon branch creation, but there's no "create" event for sync changes
childNote.invalidateSubTree(); childNote.invalidateSubTree();
} }

View File

@ -18,7 +18,7 @@ const LABEL = 'label';
const RELATION = 'relation'; const RELATION = 'relation';
/** /**
* Trilium's main entity which can represent text note, image, code note, file attachment etc. * Trilium's main entity, which can represent text note, image, code note, file attachment etc.
* *
* @extends AbstractBeccaEntity * @extends AbstractBeccaEntity
*/ */
@ -123,7 +123,7 @@ class BNote extends AbstractBeccaEntity {
* @private */ * @private */
this.__ancestorCache = null; this.__ancestorCache = null;
// following attributes are filled during searching from database // following attributes are filled during searching in the database
/** /**
* size of the content in bytes * size of the content in bytes
@ -566,7 +566,8 @@ class BNote extends AbstractBeccaEntity {
/** /**
* @param {string} type - attribute type (label, relation, etc.) * @param {string} type - attribute type (label, relation, etc.)
* @param {string} name - attribute name * @param {string} name - attribute name
* @returns {BAttribute} attribute of given type and name. If there's more such attributes, first is returned. Returns null if there's no such attribute belonging to this note. * @returns {BAttribute} attribute of the given type and name. If there are more such attributes, first is returned.
* Returns null if there's no such attribute belonging to this note.
*/ */
getAttribute(type, name) { getAttribute(type, name) {
const attributes = this.getAttributes(); const attributes = this.getAttributes();
@ -688,7 +689,7 @@ class BNote extends AbstractBeccaEntity {
areAllNotePathsArchived() { areAllNotePathsArchived() {
// there's a slight difference between note being itself archived and all its note paths being archived // there's a slight difference between note being itself archived and all its note paths being archived
// - note is archived when it itself has an archived label or inherits it // - note is archived when it itself has an archived label or inherits it
// - note does not have or inherit archived label, but each note paths contains a note with (non-inheritable) // - note does not have or inherit archived label, but each note path contains a note with (non-inheritable)
// archived label // archived label
const bestNotePathRecord = this.getSortedNotePathRecords()[0]; const bestNotePathRecord = this.getSortedNotePathRecords()[0];
@ -1093,7 +1094,7 @@ class BNote extends AbstractBeccaEntity {
/** @returns {BAttachment[]} */ /** @returns {BAttachment[]} */
getAttachments(opts = {}) { getAttachments(opts = {}) {
opts.includeContentLength = !!opts.includeContentLength; opts.includeContentLength = !!opts.includeContentLength;
// from testing it looks like calculating length does not make a difference in performance even on large-ish DB // from testing, it looks like calculating length does not make a difference in performance even on large-ish DB
// given that we're always fetching attachments only for a specific note, we might just do it always // given that we're always fetching attachments only for a specific note, we might just do it always
const query = opts.includeContentLength const query = opts.includeContentLength
@ -1148,7 +1149,7 @@ class BNote extends AbstractBeccaEntity {
const parentNotes = this.getParentNotes(); const parentNotes = this.getParentNotes();
const notePaths = parentNotes.length === 1 const notePaths = parentNotes.length === 1
? parentNotes[0].getAllNotePaths() // optimization for most common case ? parentNotes[0].getAllNotePaths() // optimization for the most common case
: parentNotes.flatMap(parentNote => parentNote.getAllNotePaths()); : parentNotes.flatMap(parentNote => parentNote.getAllNotePaths());
for (const notePath of notePaths) { for (const notePath of notePaths) {
@ -1188,7 +1189,7 @@ class BNote extends AbstractBeccaEntity {
} }
/** /**
* Returns note path considered to be the "best" * Returns a note path considered to be the "best"
* *
* @param {string} [hoistedNoteId='root'] * @param {string} [hoistedNoteId='root']
* @return {string[]} array of noteIds constituting the particular note path * @return {string[]} array of noteIds constituting the particular note path
@ -1198,7 +1199,7 @@ class BNote extends AbstractBeccaEntity {
} }
/** /**
* Returns note path considered to be the "best" * Returns a note path considered to be the "best"
* *
* @param {string} [hoistedNoteId='root'] * @param {string} [hoistedNoteId='root']
* @return {string} serialized note path (e.g. 'root/a1h315/js725h') * @return {string} serialized note path (e.g. 'root/a1h315/js725h')
@ -1338,7 +1339,7 @@ class BNote extends AbstractBeccaEntity {
} }
/** /**
* Based on enabled, attribute is either set or removed. * Based on enabled, the attribute is either set or removed.
* *
* @param {string} type - attribute type ('relation', 'label' etc.) * @param {string} type - attribute type ('relation', 'label' etc.)
* @param {boolean} enabled - toggle On or Off * @param {boolean} enabled - toggle On or Off
@ -1397,7 +1398,7 @@ class BNote extends AbstractBeccaEntity {
removeLabel(name, value) { return this.removeAttribute(LABEL, name, value); } removeLabel(name, value) { return this.removeAttribute(LABEL, name, value); }
/** /**
* Remove relation name-value pair, if it exists. * Remove the relation name-value pair, if it exists.
* *
* @param {string} name - relation name * @param {string} name - relation name
* @param {string} [value] - relation value (noteId) * @param {string} [value] - relation value (noteId)
@ -1455,16 +1456,16 @@ class BNote extends AbstractBeccaEntity {
* - it has a relation from its parent note * - it has a relation from its parent note
* - it has no children * - it has no children
* - it has no clones * - it has no clones
* - parent is of type text * - the parent is of type text
* - both notes are either unprotected or user is in protected session * - both notes are either unprotected or user is in protected session
* *
* Currently, works only for image notes. * Currently, works only for image notes.
* *
* In future this functionality might get more generic and some of the requirements relaxed. * In future, this functionality might get more generic and some of the requirements relaxed.
* *
* @params {Object} [opts] * @params {Object} [opts]
* @params {bolean} [opts.force=false} it is envisioned that user can force the conversion even if some conditions * @params {bolean} [opts.force=false} it is envisioned that user can force the conversion even if some conditions
* are not satisfied (e.g. relation to parent doesn't exist). * are not satisfied (e.g., relation to parent doesn't exist).
* *
* @returns {BAttachment|null} - null if note is not eligible for conversion * @returns {BAttachment|null} - null if note is not eligible for conversion
*/ */
@ -1549,7 +1550,7 @@ class BNote extends AbstractBeccaEntity {
} }
get isDeleted() { get isDeleted() {
// isBeingDeleted is relevant only in the transition period when the deletion process have begun, but not yet // isBeingDeleted is relevant only in the transition period when the deletion process has begun, but not yet
// finished (note is still in becca) // finished (note is still in becca)
return !(this.noteId in this.becca.notes) || this.isBeingDeleted; return !(this.noteId in this.becca.notes) || this.isBeingDeleted;
} }

View File

@ -4,7 +4,7 @@ const dateUtils = require('../../services/date_utils');
const AbstractBeccaEntity = require("./abstract_becca_entity"); const AbstractBeccaEntity = require("./abstract_becca_entity");
/** /**
* Option represents name-value pair, either directly configurable by the user or some system property. * Option represents a name-value pair, either directly configurable by the user or some system property.
* *
* @extends AbstractBeccaEntity * @extends AbstractBeccaEntity
*/ */

View File

@ -132,7 +132,7 @@ function buildRewardMap(note) {
} }
} }
// title is the top with weight 1 so smaller headings will have lower weight // the title is the top with weight 1 so smaller headings will have lower weight
// technically H1 is not supported, but for the case it's present let's weigh it just as H2 // technically H1 is not supported, but for the case it's present let's weigh it just as H2
addHeadingsToRewardMap("h1", 0.9); addHeadingsToRewardMap("h1", 0.9);
@ -260,7 +260,7 @@ async function findSimilarNotes(noteId) {
let counter = 0; let counter = 0;
// when the title is very long then weight of each individual word should be lowered // when the title is very long, then weight of each individual word should be lowered,
// also pretty important in e.g. long URLs in label values // also pretty important in e.g. long URLs in label values
const lengthPenalization = 1 / Math.pow(text.length, 0.3); const lengthPenalization = 1 / Math.pow(text.length, 0.3);
@ -364,7 +364,7 @@ async function findSimilarNotes(noteId) {
} }
/** /**
* We want to improve standing of notes which have been created in similar time to each other since * We want to improve the standing of notes which have been created in similar time to each other since
* there's a good chance they are related. * there's a good chance they are related.
* *
* But there's an exception - if they were created really close to each other (withing few seconds) then * But there's an exception - if they were created really close to each other (withing few seconds) then
@ -386,7 +386,7 @@ async function findSimilarNotes(noteId) {
console.log("Adding reward for same day of creation"); console.log("Adding reward for same day of creation");
} }
// smaller bonus when outside of the window but within same date // smaller bonus when outside of the window but within the same date
score += 0.5; score += 0.5;
} }
} }

View File

@ -107,7 +107,7 @@ class AppContext extends Component {
} }
} }
// this might hint at error but sometimes this is used by components which are at different places // this might hint at error, but sometimes this is used by components which are at different places
// in the component tree to communicate with each other // in the component tree to communicate with each other
console.debug(`Unhandled command ${name}, converting to event.`); console.debug(`Unhandled command ${name}, converting to event.`);

View File

@ -323,7 +323,7 @@ export default class TabManager extends Component {
if (notePath) { if (notePath) {
await noteContext.setNote(notePath, { await noteContext.setNote(notePath, {
// if activate is false then send normal noteSwitched event // if activate is false, then send normal noteSwitched event
triggerSwitchEvent: !activate, triggerSwitchEvent: !activate,
viewScope: viewScope viewScope: viewScope
}); });
@ -378,7 +378,7 @@ export default class TabManager extends Component {
* @returns {Promise<boolean>} true if note context has been removed, false otherwise * @returns {Promise<boolean>} true if note context has been removed, false otherwise
*/ */
async removeNoteContext(ntxId) { async removeNoteContext(ntxId) {
// removing note context is async process which can take some time, if users presses CTRL-W quickly, two // removing note context is an async process which can take some time, if users presses CTRL-W quickly, two
// close events could interleave which would then lead to attempting to activate already removed context. // close events could interleave which would then lead to attempting to activate already removed context.
return await this.mutex.runExclusively(async () => { return await this.mutex.runExclusively(async () => {
let noteContextToRemove; let noteContextToRemove;

View File

@ -6,4 +6,4 @@
<li><code>keyboardShortcut</code> - optional, pressing the keyboard shortcut will open the note</li> <li><code>keyboardShortcut</code> - optional, pressing the keyboard shortcut will open the note</li>
</ol> </ol>
<p>Launchbar displays the title / icon from the launcher which does not necessarily mirror those of the target note.</p> <p>Launchbar displays the title / icon from the launcher, which does not necessarily mirror those of the target note.</p>

View File

@ -44,7 +44,7 @@ class FBranch {
return this.froca.getNote(this.parentNoteId); return this.froca.getNote(this.parentNoteId);
} }
/** @returns {boolean} true if it's top level, meaning its parent is root note */ /** @returns {boolean} true if it's top level, meaning its parent is the root note */
isTopLevel() { isTopLevel() {
return this.parentNoteId === 'root'; return this.parentNoteId === 'root';
} }

View File

@ -23,11 +23,11 @@ class ContextMenu {
} }
positionMenu() { positionMenu() {
// code below tries to detect when dropdown would overflow from page // the code below tries to detect when dropdown would overflow from page
// in such case we'll position it above click coordinates, so it will fit into client // in such case we'll position it above click coordinates, so it will fit into the client
const CONTEXT_MENU_PADDING = 5; // How many pixels to pad context menu from edge of screen const CONTEXT_MENU_PADDING = 5; // How many pixels to pad the context menu from edge of screen
const CONTEXT_MENU_OFFSET = 0; // How many pixels to offset context menu by relative to cursor, see #3157 const CONTEXT_MENU_OFFSET = 0; // How many pixels to offset the context menu by relative to cursor, see #3157
const clientHeight = document.documentElement.clientHeight; const clientHeight = document.documentElement.clientHeight;
const clientWidth = document.documentElement.clientWidth; const clientWidth = document.documentElement.clientWidth;
@ -144,7 +144,7 @@ class ContextMenu {
hide() { hide() {
// this date checking comes from change in FF66 - https://github.com/zadam/trilium/issues/468 // this date checking comes from change in FF66 - https://github.com/zadam/trilium/issues/468
// "contextmenu" event also triggers "click" event which depending on the timing can close just opened context menu // "contextmenu" event also triggers "click" event which depending on the timing can close the just opened context menu
// we might filter out right clicks, but then it's better if even right clicks close the context menu // we might filter out right clicks, but then it's better if even right clicks close the context menu
if (Date.now() - this.dateContextMenuOpenedMs > 300) { if (Date.now() - this.dateContextMenuOpenedMs > 300) {
// seems like if we hide the menu immediately, some clicks can get propagated to the underlying component // seems like if we hide the menu immediately, some clicks can get propagated to the underlying component

View File

@ -35,7 +35,7 @@ export default class TreeContextMenu {
const isHoisted = note.noteId === appContext.tabManager.getActiveContext().hoistedNoteId; const isHoisted = note.noteId === appContext.tabManager.getActiveContext().hoistedNoteId;
const parentNote = isNotRoot ? await froca.getNote(branch.parentNoteId) : null; const parentNote = isNotRoot ? await froca.getNote(branch.parentNoteId) : null;
// some actions don't support multi-note, so they are disabled when notes are selected // some actions don't support multi-note, so they are disabled when notes are selected,
// the only exception is when the only selected note is the one that was right-clicked, then // the only exception is when the only selected note is the one that was right-clicked, then
// it's clear what the user meant to do. // it's clear what the user meant to do.
const selNodes = this.treeWidget.getSelectedNodes(); const selNodes = this.treeWidget.getSelectedNodes();

View File

@ -59,7 +59,7 @@ async function processEntityChanges(entityChanges) {
// froca is supposed to contain all notes currently being visible to the users in the tree / otherwise being processed // froca is supposed to contain all notes currently being visible to the users in the tree / otherwise being processed
// and their complete "ancestor relationship", so it's always possible to go up in the hierarchy towards the root. // and their complete "ancestor relationship", so it's always possible to go up in the hierarchy towards the root.
// To this we count: standard parent-child relationships and template/inherit relations (attribute inheritance follows them). // To this we count: standard parent-child relationships and template/inherit relations (attribute inheritance follows them).
// Here we watch for changes which might violate this principle - e.g. introduction of a new "inherit" relation might // Here we watch for changes which might violate this principle - e.g., an introduction of a new "inherit" relation might
// mean we need to load the target of the relation (and then perhaps transitively the whole note path of this target). // mean we need to load the target of the relation (and then perhaps transitively the whole note path of this target).
const missingNoteIds = []; const missingNoteIds = [];
@ -157,7 +157,7 @@ async function processBranchChange(loadResults, ec) {
if (childNote && !childNote.isRoot() && !parentNote) { if (childNote && !childNote.isRoot() && !parentNote) {
// a branch cannot exist without the parent // a branch cannot exist without the parent
// a note loaded into froca has to also contain all its ancestors, // a note loaded into froca has to also contain all its ancestors,
// this problem happened e.g., in sharing where _share was hidden and thus not loaded // this problem happened, e.g., in sharing where _share was hidden and thus not loaded
// sharing meant cloning into _share, which crashed because _share was not loaded // sharing meant cloning into _share, which crashed because _share was not loaded
parentNote = await froca.getNote(ec.entity.parentNoteId); parentNote = await froca.getNote(ec.entity.parentNoteId);
} }

View File

@ -185,7 +185,7 @@ class NoteListRenderer {
this.viewType = parentNote.getLabelValue('viewType'); this.viewType = parentNote.getLabelValue('viewType');
if (!['list', 'grid'].includes(this.viewType)) { if (!['list', 'grid'].includes(this.viewType)) {
// when not explicitly set decide based on note type // when not explicitly set, decide based on the note type
this.viewType = parentNote.type === 'search' ? 'list' : 'grid'; this.viewType = parentNote.type === 'search' ? 'list' : 'grid';
} }
@ -194,7 +194,7 @@ class NoteListRenderer {
this.showNotePath = showNotePath; this.showNotePath = showNotePath;
} }
/** @returns {Set<string>} list of noteIds included (images, included notes) into a parent note and which /** @returns {Set<string>} list of noteIds included (images, included notes) in the parent note and which
* don't have to be shown in the note list. */ * don't have to be shown in the note list. */
getIncludedNoteIds() { getIncludedNoteIds() {
const includedLinks = this.parentNote const includedLinks = this.parentNote

View File

@ -28,7 +28,7 @@ function enterProtectedSession() {
dfd.resolve(false); dfd.resolve(false);
} }
else { else {
// using deferred instead of promise because it allows resolving from outside // using deferred instead of promise because it allows resolving from the outside
protectedSessionDeferred = dfd; protectedSessionDeferred = dfd;
appContext.triggerCommand("showProtectedSessionPasswordDialog"); appContext.triggerCommand("showProtectedSessionPasswordDialog");

View File

@ -17,7 +17,7 @@ function bindElShortcut($el, keyboardShortcut, handler, namespace = null) {
if (namespace) { if (namespace) {
eventName += `.${namespace}`; eventName += `.${namespace}`;
// if there's a namespace then we replace the existing event handler with the new one // if there's a namespace, then we replace the existing event handler with the new one
$el.off(eventName); $el.off(eventName);
} }

View File

@ -47,7 +47,7 @@ export default class SpacedUpdate {
this.changed = false; this.changed = false;
} }
else { else {
// update not triggered but changes are still pending, so we need to schedule another check // update isn't triggered but changes are still pending, so we need to schedule another check
this.scheduleUpdate(); this.scheduleUpdate();
} }
} }

View File

@ -111,7 +111,7 @@ async function resolveNotePathToSegments(notePath, hoistedNoteId = 'root', logEr
throw new Error(`Did not find any path segments for '${note.toString()}', hoisted note '${hoistedNoteId}'`); throw new Error(`Did not find any path segments for '${note.toString()}', hoisted note '${hoistedNoteId}'`);
} }
// if there isn't actually any note path with hoisted note then return the original resolved note path // if there isn't actually any note path with hoisted note, then return the original resolved note path
return bestNotePath.includes(hoistedNoteId) ? bestNotePath : effectivePathSegments; return bestNotePath.includes(hoistedNoteId) ? bestNotePath : effectivePathSegments;
} }
} }

View File

@ -167,8 +167,8 @@ function isDesktop() {
|| (!window.glob?.device && !/Mobi/.test(navigator.userAgent)); || (!window.glob?.device && !/Mobi/.test(navigator.userAgent));
} }
// cookie code below works for simple use cases only - ASCII only // the cookie code below works for simple use cases only - ASCII only
// not setting path so that cookies do not leak into other websites if multiplexed with reverse proxy // not setting a path so that cookies do not leak into other websites if multiplexed with reverse proxy
function setCookie(name, value) { function setCookie(name, value) {
const date = new Date(Date.now() + 10 * 365 * 24 * 60 * 60 * 1000); const date = new Date(Date.now() + 10 * 365 * 24 * 60 * 60 * 1000);
@ -396,7 +396,7 @@ function areObjectsEqual () {
return x.toString() === y.toString(); return x.toString() === y.toString();
} }
// At last checking prototypes as good as we can // At last, checking prototypes as good as we can
if (!(x instanceof Object && y instanceof Object)) { if (!(x instanceof Object && y instanceof Object)) {
return false; return false;
} }

View File

@ -86,7 +86,7 @@ async function executeFrontendUpdate(entityChanges) {
} }
try { try {
// it's my turn so start it up // it's my turn, so start it up
consumeQueuePromise = consumeFrontendUpdateData(); consumeQueuePromise = consumeFrontendUpdateData();
await consumeQueuePromise; await consumeQueuePromise;
@ -175,7 +175,7 @@ async function consumeFrontendUpdateData() {
logError(`Encountered error ${e.message}: ${e.stack}, reloading frontend.`); logError(`Encountered error ${e.message}: ${e.stack}, reloading frontend.`);
if (!glob.isDev && !options.is('debugModeEnabled')) { if (!glob.isDev && !options.is('debugModeEnabled')) {
// if there's an error in updating the frontend then the easy option to recover is to reload the frontend completely // if there's an error in updating the frontend, then the easy option to recover is to reload the frontend completely
utils.reloadFrontendApp(); utils.reloadFrontendApp();
} }

View File

@ -310,7 +310,7 @@ export default class AttributeEditorWidget extends NoteContextAwareWidget {
this.$saveAttributesButton.fadeOut(); this.$saveAttributesButton.fadeOut();
// blink the attribute text to give visual hint that save has been executed // blink the attribute text to give a visual hint that save has been executed
this.$editor.css('opacity', 0); this.$editor.css('opacity', 0);
// revert back // revert back
@ -387,7 +387,7 @@ export default class AttributeEditorWidget extends NoteContextAwareWidget {
parsedAttrs = attributeParser.lexAndParse(this.getPreprocessedData(), true); parsedAttrs = attributeParser.lexAndParse(this.getPreprocessedData(), true);
} }
catch (e) { catch (e) {
// the input is incorrect because user messed up with it and now needs to fix it manually // the input is incorrect because the user messed up with it and now needs to fix it manually
return null; return null;
} }

View File

@ -4,7 +4,7 @@ import appContext from "../../../components/app_context.js";
import utils from "../../../services/utils.js"; import utils from "../../../services/utils.js";
import linkContextMenuService from "../../../menus/link_context_menu.js"; import linkContextMenuService from "../../../menus/link_context_menu.js";
// we're intentionally displaying the launcher title and icon instead of the target // we're intentionally displaying the launcher title and icon instead of the target,
// e.g. you want to make launchers to 2 mermaid diagrams which both have mermaid icon (ok), // e.g. you want to make launchers to 2 mermaid diagrams which both have mermaid icon (ok),
// but on the launchpad you want them distinguishable. // but on the launchpad you want them distinguishable.
// for titles, the note titles may follow a different scheme than maybe desirable on the launchpad // for titles, the note titles may follow a different scheme than maybe desirable on the launchpad

View File

@ -67,7 +67,7 @@ export default class LauncherContainer extends FlexContainer {
entitiesReloadedEvent({loadResults}) { entitiesReloadedEvent({loadResults}) {
if (loadResults.getBranchRows().find(branch => froca.getNoteFromCache(branch.parentNoteId)?.isLaunchBarConfig())) { if (loadResults.getBranchRows().find(branch => froca.getNoteFromCache(branch.parentNoteId)?.isLaunchBarConfig())) {
// changes in note placement requires reload of all launchers, all other changes are handled by individual // changes in note placement require reload of all launchers, all other changes are handled by individual
// launchers // launchers
this.load(); this.load();
} }

View File

@ -341,7 +341,7 @@ export default class RibbonContainer extends NoteContextAwareWidget {
entitiesReloadedEvent({loadResults}) { entitiesReloadedEvent({loadResults}) {
if (loadResults.isNoteReloaded(this.noteId) && this.lastNoteType !== this.note.type) { if (loadResults.isNoteReloaded(this.noteId) && this.lastNoteType !== this.note.type) {
// note type influences the list of available ribbon tabs the most // note type influences the list of available ribbon tabs the most
// check for type is so that we don't update on each title rename // check for the type is so that we don't update on each title rename
this.lastNoteType = this.note.type; this.lastNoteType = this.note.type;
this.refresh(); this.refresh();

View File

@ -20,9 +20,9 @@ export default class RightPaneContainer extends FlexContainer {
const promise = super.handleEventInChildren(name, data); const promise = super.handleEventInChildren(name, data);
if (['activeContextChanged', 'noteSwitchedAndActivated', 'noteSwitched'].includes(name)) { if (['activeContextChanged', 'noteSwitchedAndActivated', 'noteSwitched'].includes(name)) {
// right pane is displayed only if some child widget is active // the right pane is displayed only if some child widget is active,
// we'll reevaluate the visibility based on events which are probable to cause visibility change // we'll reevaluate the visibility based on events which are probable to cause visibility change
// but these events needs to be finished and only then we check // but these events need to be finished and only then we check
if (promise) { if (promise) {
promise.then(() => this.reEvaluateRightPaneVisibilityCommand()); promise.then(() => this.reEvaluateRightPaneVisibilityCommand());
} }

View File

@ -157,7 +157,7 @@ export default class SplitNoteContainer extends FlexContainer {
/** /**
* widget.hasBeenAlreadyShown is intended for lazy loading of cached tabs - initial note switches of new tabs * widget.hasBeenAlreadyShown is intended for lazy loading of cached tabs - initial note switches of new tabs
* are not executed, we're waiting for the first tab activation, and then we update the tab. After this initial * are not executed, we're waiting for the first tab activation, and then we update the tab. After this initial
* activation further note switches are always propagated to the tabs. * activation, further note switches are always propagated to the tabs.
*/ */
handleEventInChildren(name, data) { handleEventInChildren(name, data) {
if (['noteSwitched', 'noteSwitchedAndActivated'].includes(name)) { if (['noteSwitched', 'noteSwitchedAndActivated'].includes(name)) {

View File

@ -153,7 +153,7 @@ export default class BulkActionsDialog extends BasicWidget {
&& row.noteId === '_bulkAction' && row.noteId === '_bulkAction'
&& row.isDeleted)) { && row.isDeleted)) {
// this may be triggered from e.g. sync without open widget, then no need to refresh the widget // this may be triggered from e.g., sync without open widget, then no need to refresh the widget
if (this.selectedOrActiveNoteIds && this.$widget.is(":visible")) { if (this.selectedOrActiveNoteIds && this.$widget.is(":visible")) {
this.refresh(); this.refresh();
} }

View File

@ -84,7 +84,7 @@ export default class JumpToNoteDialog extends BasicWidget {
} }
showInFullText(e) { showInFullText(e) {
// stop from propagating upwards (dangerous especially with ctrl+enter executable javascript notes) // stop from propagating upwards (dangerous, especially with ctrl+enter executable javascript notes)
e.preventDefault(); e.preventDefault();
e.stopPropagation(); e.stopPropagation();

View File

@ -246,8 +246,8 @@ export default class RevisionsDialog extends BasicWidget {
this.$content.html($("<pre>").text(fullRevision.content)); this.$content.html($("<pre>").text(fullRevision.content));
} else if (revisionItem.type === 'image') { } else if (revisionItem.type === 'image') {
this.$content.html($("<img>") this.$content.html($("<img>")
// reason why we put this inline as base64 is that we do not want to let user copy this // the reason why we put this inline as base64 is that we do not want to let user copy this
// as a URL to be used in a note. Instead, if they copy and paste it into a note, it will be an uploaded as a new note // as a URL to be used in a note. Instead, if they copy and paste it into a note, it will be uploaded as a new note
.attr("src", `data:${fullRevision.mime};base64,${fullRevision.content}`) .attr("src", `data:${fullRevision.mime};base64,${fullRevision.content}`)
.css("max-width", "100%") .css("max-width", "100%")
.css("max-height", "100%")); .css("max-height", "100%"));

View File

@ -73,7 +73,7 @@ export default class NoteListWidget extends NoteContextAwareWidget {
/** /**
* We have this event so that we evaluate intersection only after note detail is loaded. * We have this event so that we evaluate intersection only after note detail is loaded.
* If it's evaluated before note detail then it's clearly intersected (visible) although after note detail load * If it's evaluated before note detail, then it's clearly intersected (visible) although after note detail load
* it is not intersected (visible) anymore. * it is not intersected (visible) anymore.
*/ */
noteDetailRefreshedEvent({ntxId}) { noteDetailRefreshedEvent({ntxId}) {

View File

@ -171,7 +171,7 @@ export default class NoteTreeWidget extends NoteContextAwareWidget {
this.triggerCommand('hoistNote', {noteId: node.data.noteId}); this.triggerCommand('hoistNote', {noteId: node.data.noteId});
}); });
// fancytree doesn't support middle click so this is a way to support it // fancytree doesn't support middle click, so this is a way to support it
this.$tree.on('mousedown', '.fancytree-title', e => { this.$tree.on('mousedown', '.fancytree-title', e => {
if (e.which === 2) { if (e.which === 2) {
const node = $.ui.fancytree.getNode(e); const node = $.ui.fancytree.getNode(e);
@ -353,7 +353,7 @@ export default class NoteTreeWidget extends NoteContextAwareWidget {
} }
}, },
beforeActivate: (event, {node}) => { beforeActivate: (event, {node}) => {
// hidden subtree is hidden hackily, prevent activating it e.g. by keyboard // hidden subtree is hidden hackily, prevent activating it, e.g. by keyboard
if (hoistedNoteService.getHoistedNoteId() === '_hidden') { if (hoistedNoteService.getHoistedNoteId() === '_hidden') {
// if we're hoisted in hidden subtree, we want to avoid crossing to "visible" tree, // if we're hoisted in hidden subtree, we want to avoid crossing to "visible" tree,
@ -545,7 +545,7 @@ export default class NoteTreeWidget extends NoteContextAwareWidget {
const $unhoistButton = $('<span class="tree-item-button unhoist-button bx bx-door-open" title="Unhoist"></span>') const $unhoistButton = $('<span class="tree-item-button unhoist-button bx bx-door-open" title="Unhoist"></span>')
.on("click", cancelClickPropagation); .on("click", cancelClickPropagation);
// unhoist button is prepended since compared to other buttons this is not just convenience // unhoist button is prepended since compared to other buttons, this is not just convenience
// on the mobile interface - it's the only way to unhoist // on the mobile interface - it's the only way to unhoist
$span.prepend($unhoistButton); $span.prepend($unhoistButton);
} }
@ -812,7 +812,7 @@ export default class NoteTreeWidget extends NoteContextAwareWidget {
await this.batchUpdate(async () => { await this.batchUpdate(async () => {
await node.load(true); await node.load(true);
if (node.data.noteId !== hoistedNoteService.getHoistedNoteId()) { // hoisted note should be always expanded if (node.data.noteId !== hoistedNoteService.getHoistedNoteId()) { // hoisted note should always be expanded
await node.setExpanded(isExpanded, {noEvents: true, noAnimation: true}); await node.setExpanded(isExpanded, {noEvents: true, noAnimation: true});
} }
}); });
@ -905,7 +905,7 @@ export default class NoteTreeWidget extends NoteContextAwareWidget {
await parentNode.setExpanded(true, {noAnimation: true}); await parentNode.setExpanded(true, {noAnimation: true});
} }
// although previous line should set the expanded status, it seems to happen asynchronously, // although the previous line should set the expanded status, it seems to happen asynchronously,
// so we need to make sure it is set properly before calling updateNode which uses this flag // so we need to make sure it is set properly before calling updateNode which uses this flag
const branch = froca.getBranch(parentNode.data.branchId); const branch = froca.getBranch(parentNode.data.branchId);
branch.isExpanded = true; branch.isExpanded = true;
@ -922,7 +922,7 @@ export default class NoteTreeWidget extends NoteContextAwareWidget {
if (!foundChildNode) { if (!foundChildNode) {
if (logErrors) { if (logErrors) {
// besides real errors this can be also caused by hiding of e.g. included images // besides real errors, this can be also caused by hiding of e.g. included images
// these are real notes with real notePath, user can display them in a detail, // these are real notes with real notePath, user can display them in a detail,
// but they don't have a node in the tree // but they don't have a node in the tree
@ -1040,7 +1040,7 @@ export default class NoteTreeWidget extends NoteContextAwareWidget {
} }
/* /*
* We're collapsing notes after period of inactivity to "cleanup" the tree - users rarely * We're collapsing notes after a period of inactivity to "cleanup" the tree - users rarely
* collapse the notes and the tree becomes unusuably large. * collapse the notes and the tree becomes unusuably large.
* Some context: https://github.com/zadam/trilium/issues/1192 * Some context: https://github.com/zadam/trilium/issues/1192
*/ */
@ -1116,8 +1116,8 @@ export default class NoteTreeWidget extends NoteContextAwareWidget {
const note = froca.getNoteFromCache(ecAttr.noteId); const note = froca.getNoteFromCache(ecAttr.noteId);
if (note && note.getChildNoteIds().includes(ecAttr.value)) { if (note && note.getChildNoteIds().includes(ecAttr.value)) {
// there's new/deleted imageLink betwen note and its image child - which can show/hide // there's a new /deleted imageLink betwen note and its image child - which can show/hide
// the image (if there is a imageLink relation between parent and child // the image (if there is an imageLink relation between parent and child,
// then it is assumed to be "contained" in the note and thus does not have to be displayed in the tree) // then it is assumed to be "contained" in the note and thus does not have to be displayed in the tree)
noteIdsToReload.add(ecAttr.noteId); noteIdsToReload.add(ecAttr.noteId);
} }
@ -1213,7 +1213,7 @@ export default class NoteTreeWidget extends NoteContextAwareWidget {
} }
}); });
// for some reason node update cannot be in the batchUpdate() block (node is not re-rendered) // for some reason, node update cannot be in the batchUpdate() block (node is not re-rendered)
for (const noteId of noteIdsToUpdate) { for (const noteId of noteIdsToUpdate) {
for (const node of this.getNodesByNoteId(noteId)) { for (const node of this.getNodesByNoteId(noteId)) {
await this.updateNode(node); await this.updateNode(node);
@ -1239,7 +1239,7 @@ export default class NoteTreeWidget extends NoteContextAwareWidget {
// so we switch to the alternative of trying to find it by noteId // so we switch to the alternative of trying to find it by noteId
const notesById = this.getNodesByNoteId(activeNoteId); const notesById = this.getNodesByNoteId(activeNoteId);
// if there are multiple clones then we'd rather not activate any one // if there are multiple clones, then we'd rather not activate anyone
node = notesById.length === 1 ? notesById[0] : null; node = notesById.length === 1 ? notesById[0] : null;
} }
@ -1252,7 +1252,7 @@ export default class NoteTreeWidget extends NoteContextAwareWidget {
await node.setActive(true, {noEvents: true, noFocus: !activeNodeFocused}); await node.setActive(true, {noEvents: true, noFocus: !activeNodeFocused});
} }
else { else {
// this is used when original note has been deleted, and we want to move the focus to the note above/below // this is used when the original note has been deleted, and we want to move the focus to the note above/below
node = await this.expandToNote(nextNotePath, false); node = await this.expandToNote(nextNotePath, false);
if (node) { if (node) {

View File

@ -64,7 +64,7 @@ export default class NoteTypeWidget extends NoteContextAwareWidget {
this.$noteTypeButton.dropdown('hide'); this.$noteTypeButton.dropdown('hide');
} }
/** actual body is rendered lazily on note-type button click */ /** the actual body is rendered lazily on note-type button click */
async renderDropdown() { async renderDropdown() {
this.$noteTypeDropdown.empty(); this.$noteTypeDropdown.empty();

View File

@ -107,7 +107,7 @@ export default class PromotedAttributesWidget extends NoteContextAwareWidget {
} }
} }
// we replace the whole content in one step so there can't be any race conditions // we replace the whole content in one step, so there can't be any race conditions
// (previously we saw promoted attributes doubling) // (previously we saw promoted attributes doubling)
this.$container.empty().append(...$cells); this.$container.empty().append(...$cells);
this.toggleInt(true); this.toggleInt(true);

View File

@ -47,8 +47,8 @@ export default class SearchString extends AbstractSearchOption {
shortcutService.bindElShortcut(this.$searchString, 'return', async () => { shortcutService.bindElShortcut(this.$searchString, 'return', async () => {
// this also in effect disallows new lines in query string. // this also in effect disallows new lines in query string.
// on one hand this makes sense since search string is a label // on one hand, this makes sense since search string is a label
// on the other hand it could be nice for structuring long search string. It's probably a niche case though. // on the other hand, it could be nice for structuring long search string. It's probably a niche case though.
await this.spacedUpdate.updateNowIfNecessary(); await this.spacedUpdate.updateNowIfNecessary();
this.triggerCommand('refreshResults'); this.triggerCommand('refreshResults');

View File

@ -336,7 +336,7 @@ export default class TabRowWidget extends BasicWidget {
position += width + MARGIN_WIDTH; position += width + MARGIN_WIDTH;
}); });
position -= MARGIN_WIDTH; // last margin should not be applied position -= MARGIN_WIDTH; // the last margin should not be applied
const newTabPosition = position; const newTabPosition = position;
const fillerPosition = position + 32; const fillerPosition = position + 32;
@ -542,7 +542,7 @@ export default class TabRowWidget extends BasicWidget {
}); });
draggabilly.on('dragMove', (event, pointer, moveVector) => { draggabilly.on('dragMove', (event, pointer, moveVector) => {
// Current index be computed within the event since it can change during the dragMove // The current index be computed within the event since it can change during the dragMove
const tabEls = this.tabEls; const tabEls = this.tabEls;
const currentIndex = tabEls.indexOf(tabEl); const currentIndex = tabEls.indexOf(tabEl);

View File

@ -2,14 +2,14 @@
* Table of contents widget * Table of contents widget
* (c) Antonio Tejada 2022 * (c) Antonio Tejada 2022
* *
* By design there's no support for nonsensical or malformed constructs: * By design, there's no support for nonsensical or malformed constructs:
* - headings inside elements (e.g. Trilium allows headings inside tables, but * - headings inside elements (e.g. Trilium allows headings inside tables, but
* not inside lists) * not inside lists)
* - nested headings when using raw HTML <H2><H3></H3></H2> * - nested headings when using raw HTML <H2><H3></H3></H2>
* - malformed headings when using raw HTML <H2></H3></H2><H3> * - malformed headings when using raw HTML <H2></H3></H2><H3>
* - etc. * - etc.
* *
* In those cases the generated TOC may be incorrect or the navigation may lead * In those cases, the generated TOC may be incorrect, or the navigation may lead
* to the wrong heading (although what "right" means in those cases is not * to the wrong heading (although what "right" means in those cases is not
* clear), but it won't crash. * clear), but it won't crash.
*/ */

View File

@ -72,7 +72,7 @@ export default class EditableCodeTypeWidget extends TypeWidget {
const blob = await this.note.getBlob(); const blob = await this.note.getBlob();
await this.spacedUpdate.allowUpdateWithoutChange(() => { await this.spacedUpdate.allowUpdateWithoutChange(() => {
// CodeMirror breaks pretty badly on null, so even though it shouldn't happen (guarded by consistency check) // CodeMirror breaks pretty badly on null, so even though it shouldn't happen (guarded by a consistency check)
// we provide fallback // we provide fallback
this.codeEditor.setValue(blob.content || ""); this.codeEditor.setValue(blob.content || "");
this.codeEditor.clearHistory(); this.codeEditor.clearHistory();

View File

@ -192,7 +192,7 @@ export default class EditableTextTypeWidget extends AbstractTextTypeWidget {
getData() { getData() {
const content = this.watchdog.editor.getData(); const content = this.watchdog.editor.getData();
// if content is only tags/whitespace (typically <p>&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 a new note to code // this is important when setting a new note to code
return { return {
content: utils.isHtmlEmpty(content) ? '' : content content: utils.isHtmlEmpty(content) ? '' : content

View File

@ -91,7 +91,7 @@ export default class RelationMapTypeWidget extends TypeWidget {
if (this.clipboard) { if (this.clipboard) {
let {x, y} = this.getMousePosition(event); let {x, y} = this.getMousePosition(event);
// modifying position so that cursor is on the top-center of the box // modifying position so that the cursor is on the top-center of the box
x -= 80; x -= 80;
y -= 15; y -= 15;
@ -186,8 +186,8 @@ export default class RelationMapTypeWidget extends TypeWidget {
async loadMapData() { async loadMapData() {
this.mapData = { this.mapData = {
notes: [], notes: [],
// it is important to have this exact value here so that initial transform is same as this // it is important to have this exact value here so that initial transform is the same as this
// which will guarantee note won't be saved on first conversion to relation map note type // which will guarantee note won't be saved on first conversion to the relation map note type
// this keeps the principle that note type change doesn't destroy note content unless user // this keeps the principle that note type change doesn't destroy note content unless user
// does some actual change // does some actual change
transform: { transform: {
@ -231,7 +231,7 @@ export default class RelationMapTypeWidget extends TypeWidget {
// this is done at this point (after async operations) to reduce flicker to the minimum // this is done at this point (after async operations) to reduce flicker to the minimum
this.jsPlumbInstance.deleteEveryEndpoint(); this.jsPlumbInstance.deleteEveryEndpoint();
// without this we still end up with note boxes remaining in the canvas // without this, we still end up with note boxes remaining in the canvas
this.$relationMapContainer.empty(); this.$relationMapContainer.empty();
} }
@ -305,7 +305,7 @@ export default class RelationMapTypeWidget extends TypeWidget {
minZoom: 0.3, minZoom: 0.3,
smoothScroll: false, smoothScroll: false,
filterKey: function(e, dx, dy, dz) { filterKey: function(e, dx, dy, dz) {
// if ALT is pressed then panzoom should bubble the event up // if ALT is pressed, then panzoom should bubble the event up
// this is to preserve ALT-LEFT, ALT-RIGHT navigation working // this is to preserve ALT-LEFT, ALT-RIGHT navigation working
return e.altKey; return e.altKey;
} }

View File

@ -43,7 +43,7 @@ function moveBranchBeforeNote(req) {
const originalBeforeNotePosition = beforeBranch.notePosition; const originalBeforeNotePosition = beforeBranch.notePosition;
// we don't change utcDateModified so other changes are prioritized in case of conflict // we don't change utcDateModified, so other changes are prioritized in case of conflict
// also we would have to sync all those modified branches otherwise hash checks would fail // also we would have to sync all those modified branches otherwise hash checks would fail
sql.execute("UPDATE branches SET notePosition = notePosition + 10 WHERE parentNoteId = ? AND notePosition >= ? AND isDeleted = 0", sql.execute("UPDATE branches SET notePosition = notePosition + 10 WHERE parentNoteId = ? AND notePosition >= ? AND isDeleted = 0",
@ -93,7 +93,7 @@ function moveBranchAfterNote(req) {
const originalAfterNotePosition = afterNote.notePosition; const originalAfterNotePosition = afterNote.notePosition;
// we don't change utcDateModified so other changes are prioritized in case of conflict // we don't change utcDateModified, so other changes are prioritized in case of conflict
// also we would have to sync all those modified branches otherwise hash checks would fail // also we would have to sync all those modified branches otherwise hash checks would fail
sql.execute("UPDATE branches SET notePosition = notePosition + 10 WHERE parentNoteId = ? AND notePosition > ? AND isDeleted = 0", sql.execute("UPDATE branches SET notePosition = notePosition + 10 WHERE parentNoteId = ? AND notePosition > ? AND isDeleted = 0",
[afterNote.parentNoteId, originalAfterNotePosition]); [afterNote.parentNoteId, originalAfterNotePosition]);

View File

@ -97,7 +97,7 @@ async function importAttachmentsToNote(req) {
const parentNote = becca.getNoteOrThrow(parentNoteId); const parentNote = becca.getNoteOrThrow(parentNoteId);
const taskContext = TaskContext.getInstance(taskId, 'importAttachment', options); const taskContext = TaskContext.getInstance(taskId, 'importAttachment', options);
// unlike in note import we let the events run, because a huge number of attachments is not likely // unlike in note import, we let the events run, because a huge number of attachments is not likely
try { try {
await singleImportService.importAttachment(taskContext, file, parentNote); await singleImportService.importAttachment(taskContext, file, parentNote);

View File

@ -92,13 +92,13 @@ function getNeighbors(note, depth) {
function getLinkMap(req) { function getLinkMap(req) {
const mapRootNote = becca.getNote(req.params.noteId); const mapRootNote = becca.getNote(req.params.noteId);
// if the map root itself has exclude attribute (journal typically) then there wouldn't be anything to display, so // if the map root itself has "excludeFromNoteMap" attribute (journal typically) then there wouldn't be anything
// we'll just ignore it // to display, so we'll just ignore it
const ignoreExcludeFromNoteMap = mapRootNote.isLabelTruthy('excludeFromNoteMap'); const ignoreExcludeFromNoteMap = mapRootNote.isLabelTruthy('excludeFromNoteMap');
let unfilteredNotes; let unfilteredNotes;
if (mapRootNote.type === 'search') { if (mapRootNote.type === 'search') {
// for search notes we want to consider the direct search results only without the descendants // for search notes, we want to consider the direct search results only without the descendants
unfilteredNotes = mapRootNote.getSearchResultNotes(); unfilteredNotes = mapRootNote.getSearchResultNotes();
} else { } else {
unfilteredNotes = mapRootNote.getSubtree({ unfilteredNotes = mapRootNote.getSubtree({
@ -167,8 +167,8 @@ function getLinkMap(req) {
function getTreeMap(req) { function getTreeMap(req) {
const mapRootNote = becca.getNote(req.params.noteId); const mapRootNote = becca.getNote(req.params.noteId);
// if the map root itself has ignore (journal typically) then there wouldn't be anything to display, so // if the map root itself has "excludeFromNoteMap" (journal typically) then there wouldn't be anything to display,
// we'll just ignore it // so we'll just ignore it
const ignoreExcludeFromNoteMap = mapRootNote.isLabelTruthy('excludeFromNoteMap'); const ignoreExcludeFromNoteMap = mapRootNote.isLabelTruthy('excludeFromNoteMap');
const subtree = mapRootNote.getSubtree({ const subtree = mapRootNote.getSubtree({
includeArchived: false, includeArchived: false,

View File

@ -5,7 +5,7 @@ const log = require('../../services/log');
const searchService = require('../../services/search/services/search'); const searchService = require('../../services/search/services/search');
const ValidationError = require("../../errors/validation_error"); const ValidationError = require("../../errors/validation_error");
// options allowed to be updated directly in options dialog // options allowed to be updated directly in the Options dialog
const ALLOWED_OPTIONS = new Set([ const ALLOWED_OPTIONS = new Set([
'eraseEntitiesAfterTimeInSeconds', 'eraseEntitiesAfterTimeInSeconds',
'protectedSessionTimeout', 'protectedSessionTimeout',

View File

@ -228,7 +228,7 @@ setInterval(() => {
}, 60 * 1000); }, 60 * 1000);
function syncFinished() { function syncFinished() {
// after first sync finishes, the application is ready to be used // after the first sync finishes, the application is ready to be used
// this is meaningless but at the same time harmless (idempotent) for further syncs // this is meaningless but at the same time harmless (idempotent) for further syncs
sqlInit.setDbAsInitialized(); sqlInit.setDbAsInitialized();
} }

View File

@ -231,7 +231,7 @@ function register(app) {
// docker health check // docker health check
route(GET, '/api/health-check', [], () => ({"status": "ok"}), apiResultHandler); route(GET, '/api/health-check', [], () => ({"status": "ok"}), apiResultHandler);
// group of services below are meant to be executed from outside // group of the services below are meant to be executed from the outside
route(GET, '/api/setup/status', [], setupApiRoute.getStatus, apiResultHandler); route(GET, '/api/setup/status', [], setupApiRoute.getStatus, apiResultHandler);
route(PST, '/api/setup/new-document', [auth.checkAppNotInitialized], setupApiRoute.setupNewDocument, apiResultHandler, false); route(PST, '/api/setup/new-document', [auth.checkAppNotInitialized], setupApiRoute.setupNewDocument, apiResultHandler, false);
route(PST, '/api/setup/sync-from-server', [auth.checkAppNotInitialized], setupApiRoute.setupSyncFromServer, apiResultHandler, false); route(PST, '/api/setup/sync-from-server', [auth.checkAppNotInitialized], setupApiRoute.setupSyncFromServer, apiResultHandler, false);
@ -380,7 +380,7 @@ function apiResultHandler(req, res, result) {
result = convertEntitiesToPojo(result); result = convertEntitiesToPojo(result);
// if it's an array and first element is integer then we consider this to be [statusCode, response] format // if it's an array and the first element is integer, then we consider this to be [statusCode, response] format
if (Array.isArray(result) && result.length > 0 && Number.isInteger(result[0])) { if (Array.isArray(result) && result.length > 0 && Number.isInteger(result[0])) {
const [statusCode, response] = result; const [statusCode, response] = result;

View File

@ -21,7 +21,7 @@ function getNotesWithLabel(name, value = undefined) {
// TODO: should be in search service // TODO: should be in search service
/** @returns {BNote|null} */ /** @returns {BNote|null} */
function getNoteWithLabel(name, value = undefined) { function getNoteWithLabel(name, value = undefined) {
// optimized version (~20 times faster) without using normal search, useful for e.g. finding date notes // optimized version (~20 times faster) without using normal search, useful for e.g., finding date notes
const attrs = becca.findAttributes('label', name); const attrs = becca.findAttributes('label', name);
if (value === undefined) { if (value === undefined) {

View File

@ -23,7 +23,7 @@ function checkAuth(req, res, next) {
} }
// for electron things which need network stuff // for electron things which need network stuff
// currently we're doing that for file upload because handling form data seems to be difficult // currently, we're doing that for file upload because handling form data seems to be difficult
function checkApiAuthOrElectron(req, res, next) { function checkApiAuthOrElectron(req, res, next) {
if (!req.session.loggedIn && !utils.isElectron() && !noAuthentication) { if (!req.session.loggedIn && !utils.isElectron() && !noAuthentication) {
reject(req, res, "Logged in session not found"); reject(req, res, "Logged in session not found");

View File

@ -142,7 +142,7 @@ function BackendScriptApi(currentNote, apiParams) {
* @method * @method
* @param {string} noteId * @param {string} noteId
* @param {string} parentNoteId * @param {string} parentNoteId
* @param {string} prefix - if branch will be created between note and parent note, set this prefix * @param {string} prefix - if branch is created between note and parent note, set this prefix
* @returns {{branch: BBranch|null}} * @returns {{branch: BBranch|null}}
*/ */
this.ensureNoteIsPresentInParent = cloningService.ensureNoteIsPresentInParent; this.ensureNoteIsPresentInParent = cloningService.ensureNoteIsPresentInParent;
@ -164,7 +164,7 @@ function BackendScriptApi(currentNote, apiParams) {
* @param {boolean} present - true if we want the branch to exist, false if we want it gone * @param {boolean} present - true if we want the branch to exist, false if we want it gone
* @param {string} noteId * @param {string} noteId
* @param {string} parentNoteId * @param {string} parentNoteId
* @param {string} prefix - if branch will be created between note and parent note, set this prefix * @param {string} prefix - if branch is created between note and parent note, set this prefix
* @returns {void} * @returns {void}
*/ */
this.toggleNoteInParent = cloningService.toggleNoteInParent; this.toggleNoteInParent = cloningService.toggleNoteInParent;
@ -244,7 +244,7 @@ function BackendScriptApi(currentNote, apiParams) {
const parentNote = becca.getNote(parentNoteId); const parentNote = becca.getNote(parentNoteId);
// code note type can be inherited, otherwise text is default // code note type can be inherited, otherwise "text" is the default
extraOptions.type = parentNote.type === 'code' ? 'code' : 'text'; extraOptions.type = parentNote.type === 'code' ? 'code' : 'text';
extraOptions.mime = parentNote.type === 'code' ? parentNote.mime : 'text/html'; extraOptions.mime = parentNote.type === 'code' ? parentNote.mime : 'text/html';

View File

@ -156,7 +156,7 @@ function cloneNoteAfter(noteId, afterBranchId) {
return validationResult; return validationResult;
} }
// we don't change utcDateModified so other changes are prioritized in case of conflict // we don't change utcDateModified, so other changes are prioritized in case of conflict
// also we would have to sync all those modified branches otherwise hash checks would fail // also we would have to sync all those modified branches otherwise hash checks would fail
sql.execute("UPDATE branches SET notePosition = notePosition + 10 WHERE parentNoteId = ? AND notePosition > ? AND isDeleted = 0", sql.execute("UPDATE branches SET notePosition = notePosition + 10 WHERE parentNoteId = ? AND notePosition > ? AND isDeleted = 0",
[afterNote.parentNoteId, afterNote.notePosition]); [afterNote.parentNoteId, afterNote.notePosition]);

View File

@ -810,7 +810,7 @@ function getBlankContent(isProtected, type, mime) {
return '{}'; return '{}';
} }
return ''; // empty string might be wrong choice for some note types, but it's the best guess return ''; // empty string might be a wrong choice for some note types, but it's the best guess
} }
function logFix(message) { function logFix(message) {

View File

@ -108,7 +108,7 @@ async function exportToZip(taskContext, branch, format, res, setHeaders = true)
} }
} }
// if the note is already named with extension (e.g. "jquery"), then it's silly to append exact same extension again // if the note is already named with extension (e.g. "jquery"), then it's silly to append the exact same extension again
if (newExtension && existingExtension !== `.${newExtension.toLowerCase()}`) { if (newExtension && existingExtension !== `.${newExtension.toLowerCase()}`) {
fileName += `.${newExtension}`; fileName += `.${newExtension}`;
} }

View File

@ -76,7 +76,7 @@ async function importOpml(taskContext, fileBuffer, parentNote) {
for (const outline of outlines) { for (const outline of outlines) {
const note = importOutline(outline, parentNote.noteId); const note = importOutline(outline, parentNote.noteId);
// first created note will be activated after import // the first created note will be activated after import
returnNote = returnNote || note; returnNote = returnNote || note;
} }

View File

@ -145,7 +145,7 @@ async function importZip(taskContext, fileBuffer, importRootNote) {
} else if (parentPath in createdPaths) { } else if (parentPath in createdPaths) {
parentNoteId = createdPaths[parentPath]; parentNoteId = createdPaths[parentPath];
} else { } else {
// ZIP allows creating out of order records - i.e., file in a directory can appear in the ZIP stream before actual directory // ZIP allows creating out of order records - i.e., file in a directory can appear in the ZIP stream before the actual directory
parentNoteId = saveDirectory(parentPath); parentNoteId = saveDirectory(parentPath);
} }
} }

View File

@ -69,7 +69,7 @@ function copyChildAttributes(parentNote, childNote) {
if (hasAlreadyTemplate && attr.type === 'relation' && name === 'template') { if (hasAlreadyTemplate && attr.type === 'relation' && name === 'template') {
// if the note already has a template, it means the template was chosen by the user explicitly // if the note already has a template, it means the template was chosen by the user explicitly
// in the menu. In that case we should override the default templates defined in the child: attrs // in the menu. In that case, we should override the default templates defined in the child: attrs
continue; continue;
} }
@ -105,8 +105,8 @@ function getNewNoteTitle(parentNote) {
} }
} }
// this isn't in theory a good place to sanitize title, but this will catch a lot of XSS attempts // this isn't in theory a good place to sanitize title, but this will catch a lot of XSS attempts.
// title is supposed to contain text only (not HTML) and be printed text only, but given the number of usages // title is supposed to contain text only (not HTML) and be printed text only, but given the number of usages,
// it's difficult to guarantee correct handling in all cases // it's difficult to guarantee correct handling in all cases
title = htmlSanitizer.sanitize(title); title = htmlSanitizer.sanitize(title);
@ -147,12 +147,12 @@ function getAndValidateParent(params) {
* - {*} content * - {*} content
* - {string} type - text, code, file, image, search, book, relationMap, canvas, render * - {string} type - text, code, file, image, search, book, relationMap, canvas, render
* *
* Following are optional (have defaults) * The following are optional (have defaults)
* - {string} mime - value is derived from default mimes for type * - {string} mime - value is derived from default mimes for type
* - {boolean} isProtected - default is false * - {boolean} isProtected - default is false
* - {boolean} isExpanded - default is false * - {boolean} isExpanded - default is false
* - {string} prefix - default is empty string * - {string} prefix - default is empty string
* - {int} notePosition - default is last existing notePosition in a parent + 10 * - {int} notePosition - default is the last existing notePosition in a parent + 10
* *
* @param params * @param params
* @returns {{note: BNote, branch: BBranch}} * @returns {{note: BNote, branch: BBranch}}
@ -226,7 +226,7 @@ function createNewNote(params) {
eventService.emit(eventService.ENTITY_CREATED, { entityName: 'notes', entity: note }); eventService.emit(eventService.ENTITY_CREATED, { entityName: 'notes', entity: note });
eventService.emit(eventService.ENTITY_CHANGED, { entityName: 'notes', entity: note }); eventService.emit(eventService.ENTITY_CHANGED, { entityName: 'notes', entity: note });
triggerNoteTitleChanged(note); triggerNoteTitleChanged(note);
// blobs doesn't use "created" event // blobs entity doesn't use "created" event
eventService.emit(eventService.ENTITY_CHANGED, { entityName: 'blobs', entity: note }); eventService.emit(eventService.ENTITY_CHANGED, { entityName: 'blobs', entity: note });
eventService.emit(eventService.ENTITY_CREATED, { entityName: 'branches', entity: branch }); eventService.emit(eventService.ENTITY_CREATED, { entityName: 'branches', entity: branch });
eventService.emit(eventService.ENTITY_CHANGED, { entityName: 'branches', entity: branch }); eventService.emit(eventService.ENTITY_CHANGED, { entityName: 'branches', entity: branch });
@ -245,7 +245,7 @@ function createNewNoteWithTarget(target, targetBranchId, params) {
if (!params.type) { if (!params.type) {
const parentNote = becca.notes[params.parentNoteId]; const parentNote = becca.notes[params.parentNoteId];
// code note type can be inherited, otherwise text is default // code note type can be inherited, otherwise "text" is the default
params.type = parentNote.type === 'code' ? 'code' : 'text'; params.type = parentNote.type === 'code' ? 'code' : 'text';
params.mime = parentNote.type === 'code' ? parentNote.mime : 'text/html'; params.mime = parentNote.type === 'code' ? parentNote.mime : 'text/html';
} }
@ -366,7 +366,7 @@ function checkImageAttachments(note, content) {
const unknownAttachments = becca.getAttachments(unknownAttachmentIds); const unknownAttachments = becca.getAttachments(unknownAttachmentIds);
for (const unknownAttachment of unknownAttachments) { for (const unknownAttachment of unknownAttachments) {
// the attachment belongs to a different note (was copy pasted). Attachments can be linked only from the note // the attachment belongs to a different note (was copy-pasted). Attachments can be linked only from the note
// which owns it, so either find an existing attachment having the same content or make a copy. // which owns it, so either find an existing attachment having the same content or make a copy.
let localAttachment = note.getAttachments().find(att => att.role === unknownAttachment.role && att.blobId === unknownAttachment.blobId); let localAttachment = note.getAttachments().find(att => att.role === unknownAttachment.role && att.blobId === unknownAttachment.blobId);
@ -412,7 +412,7 @@ function findImageLinks(content, foundLinks) {
}); });
} }
// removing absolute references to server to keep it working between instances // removing absolute references to server to keep it working between instances,
// we also omit / at the beginning to keep the paths relative // we also omit / at the beginning to keep the paths relative
return content.replace(/src="[^"]*\/api\/images\//g, 'src="api/images/'); return content.replace(/src="[^"]*\/api\/images\//g, 'src="api/images/');
} }
@ -557,10 +557,10 @@ function downloadImages(noteId, content) {
setTimeout(() => { setTimeout(() => {
// the normal expected flow of the offline image saving is that users will paste the image(s) // the normal expected flow of the offline image saving is that users will paste the image(s)
// which will get asynchronously downloaded, during that time they keep editing the note // which will get asynchronously downloaded, during that time they keep editing the note
// once the download is finished, the image note representing downloaded image will be used // once the download is finished, the image note representing the downloaded image will be used
// to replace the IMG link. // to replace the IMG link.
// However, there's another flow where user pastes the image and leaves the note before the images // However, there's another flow where the user pastes the image and leaves the note before the images
// are downloaded and the IMG references are not updated. For this occassion we have this code // are downloaded and the IMG references are not updated. For this occasion we have this code
// which upon the download of all the images will update the note if the links have not been fixed before // which upon the download of all the images will update the note if the links have not been fixed before
sql.transactional(() => { sql.transactional(() => {
@ -972,7 +972,7 @@ function eraseUnusedAttachmentsNow() {
eraseScheduledAttachments(0); eraseScheduledAttachments(0);
} }
// do a replace in str - all keys should be replaced by the corresponding values // all keys should be replaced by the corresponding values
function replaceByMap(str, mapObj) { function replaceByMap(str, mapObj) {
const re = new RegExp(Object.keys(mapObj).join("|"),"g"); const re = new RegExp(Object.keys(mapObj).join("|"),"g");
@ -1076,7 +1076,7 @@ function duplicateSubtreeInner(origNote, origBranch, newParentNoteId, noteIdMapp
const existingNote = becca.notes[newNoteId]; const existingNote = becca.notes[newNoteId];
if (existingNote && existingNote.title !== undefined) { // checking that it's not just note's skeleton created because of Branch above if (existingNote && existingNote.title !== undefined) { // checking that it's not just note's skeleton created because of Branch above
// note has multiple clones and was already created from another placement in the tree // note has multiple clones and was already created from another placement in the tree,
// so a branch is all we need for this clone // so a branch is all we need for this clone
return { return {
note: existingNote, note: existingNote,

View File

@ -123,7 +123,7 @@ function getImage(imageUrl) {
host: parsedTargetUrl.hostname, host: parsedTargetUrl.hostname,
port: parsedTargetUrl.port, port: parsedTargetUrl.port,
path: parsedTargetUrl.path, path: parsedTargetUrl.path,
timeout: opts.timeout, // works only for node client timeout: opts.timeout, // works only for the node client
headers: {}, headers: {},
agent: proxyAgent agent: proxyAgent
}); });
@ -170,7 +170,7 @@ function getProxyAgent(opts) {
} }
function getClient(opts) { function getClient(opts) {
// it's not clear how to explicitly configure proxy (as opposed to system proxy) // it's not clear how to explicitly configure proxy (as opposed to system proxy),
// so in that case, we always use node's modules // so in that case, we always use node's modules
if (utils.isElectron() && !opts.proxy) { if (utils.isElectron() && !opts.proxy) {
return require('electron').net; return require('electron').net;

View File

@ -4,7 +4,7 @@ const fs = require('fs');
const RESOURCE_DIR = path.resolve(__dirname, "../.."); const RESOURCE_DIR = path.resolve(__dirname, "../..");
// where "trilium" executable is // where the "trilium" executable is
const ELECTRON_APP_ROOT_DIR = path.resolve(RESOURCE_DIR, "../.."); const ELECTRON_APP_ROOT_DIR = path.resolve(RESOURCE_DIR, "../..");
const DB_INIT_DIR = path.resolve(RESOURCE_DIR, "db"); const DB_INIT_DIR = path.resolve(RESOURCE_DIR, "db");

View File

@ -35,7 +35,7 @@ function executeBundle(bundle, apiParams = {}) {
cls.set('componentId', 'script'); cls.set('componentId', 'script');
cls.set('bundleNoteId', bundle.note.noteId); cls.set('bundleNoteId', bundle.note.noteId);
// last \r\n is necessary if script contains line comment on its last line // last \r\n is necessary if the script contains line comment on its last line
const script = `function() {\r const script = `function() {\r
${bundle.script}\r ${bundle.script}\r
}`; }`;

View File

@ -47,7 +47,7 @@ class OrderByAndLimitExp extends Expression {
return larger; return larger;
} }
// if both are numbers then parse them for numerical comparison // if both are numbers, then parse them for numerical comparison
if (this.isNumber(valA) && this.isNumber(valB)) { if (this.isNumber(valA) && this.isNumber(valB)) {
valA = parseFloat(valA); valA = parseFloat(valA);
valB = parseFloat(valB); valB = parseFloat(valB);

View File

@ -5,8 +5,8 @@ const NoteSet = require('../note_set');
const buildComparator = require("../services/build_comparator"); const buildComparator = require("../services/build_comparator");
/** /**
* Search string is lower cased for case-insensitive comparison. But when retrieving properties * Search string is lower cased for case-insensitive comparison. But when retrieving properties,
* we need case-sensitive form, so we have this translation object. * we need the case-sensitive form, so we have this translation object.
*/ */
const PROP_MAPPING = { const PROP_MAPPING = {
"noteid": "noteId", "noteid": "noteId",

View File

@ -33,7 +33,7 @@ class SearchContext {
} }
addError(error) { addError(error) {
// we record only the first error, subsequent ones are usually consequence of the first // we record only the first error, subsequent ones are usually a consequence of the first
if (!this.error) { if (!this.error) {
this.error = error; this.error = error;
} }

View File

@ -1,7 +1,7 @@
"use strict"; "use strict";
/** /**
* Search string is lower cased for case-insensitive comparison. But when retrieving properties * Search string is lower cased for case-insensitive comparison. But when retrieving properties,
* we need a case-sensitive form, so we have this translation object. * we need a case-sensitive form, so we have this translation object.
*/ */
const PROP_MAPPING = { const PROP_MAPPING = {

View File

@ -37,7 +37,7 @@ async function sendSeedToSyncServer() {
syncVersion: appInfo.syncVersion syncVersion: appInfo.syncVersion
}); });
// this is completely new sync, need to reset counters. If this would not be new sync, // this is a completely new sync, need to reset counters. If this was not a new sync,
// the previous request would have failed. // the previous request would have failed.
optionService.setOption('lastSyncedPush', 0); optionService.setOption('lastSyncedPush', 0);
optionService.setOption('lastSyncedPull', 0); optionService.setOption('lastSyncedPull', 0);
@ -66,7 +66,7 @@ async function setupSyncFromSyncServer(syncServerHost, syncProxy, password) {
try { try {
log.info("Getting document options FROM sync server."); log.info("Getting document options FROM sync server.");
// response is expected to contain documentId and documentSecret options // the response is expected to contain documentId and documentSecret options
const resp = await request.exec({ const resp = await request.exec({
method: 'get', method: 'get',
url: `${syncServerHost}/api/setup/sync-seed`, url: `${syncServerHost}/api/setup/sync-seed`,

View File

@ -47,7 +47,7 @@ class SpacedUpdate {
this.changed = false; this.changed = false;
} }
else { else {
// update not triggered but changes are still pending, so we need to schedule another check // update isn't triggered but changes are still pending, so we need to schedule another check
this.scheduleUpdate(); this.scheduleUpdate();
} }
} }

View File

@ -171,7 +171,7 @@ dbReady.then(() => {
// kickoff first backup soon after start up // kickoff first backup soon after start up
setTimeout(() => require('./backup').regularBackup(), 5 * 60 * 1000); setTimeout(() => require('./backup').regularBackup(), 5 * 60 * 1000);
// optimize is usually inexpensive no-op so running it semi-frequently is not a big deal // optimize is usually inexpensive no-op, so running it semi-frequently is not a big deal
setTimeout(() => optimize(), 60 * 60 * 1000); setTimeout(() => optimize(), 60 * 60 * 1000);
setInterval(() => optimize(), 10 * 60 * 60 * 1000); setInterval(() => optimize(), 10 * 60 * 60 * 1000);

View File

@ -203,7 +203,7 @@ async function pushChanges(syncContext) {
}); });
if (filteredEntityChanges.length === 0) { if (filteredEntityChanges.length === 0) {
// there still might be more sync changes (because of batch limit), just all from current batch // there still might be more sync changes (because of batch limit), just all the current batch
// has been filtered out // has been filtered out
setLastSyncedPush(lastSyncedPush); setLastSyncedPush(lastSyncedPush);
@ -255,7 +255,7 @@ async function checkContentHash(syncContext) {
const failedChecks = contentHashService.checkContentHashes(resp.entityHashes); const failedChecks = contentHashService.checkContentHashes(resp.entityHashes);
if (failedChecks.length > 0) { if (failedChecks.length > 0) {
// before requeuing sectors make sure the entity changes are correct // before requeuing sectors, make sure the entity changes are correct
const consistencyChecks = require("./consistency_checks"); const consistencyChecks = require("./consistency_checks");
consistencyChecks.runEntityChangesChecks(); consistencyChecks.runEntityChangesChecks();

View File

@ -21,7 +21,7 @@ module.exports = {
isSyncSetup: () => { isSyncSetup: () => {
const syncServerHost = get('syncServerHost'); const syncServerHost = get('syncServerHost');
// special value "disabled" is here to support use case where document is configured with sync server, // special value "disabled" is here to support a use case where the document is configured with sync server,
// and we need to override it with config from config.ini // and we need to override it with config from config.ini
return !!syncServerHost && syncServerHost !== 'disabled'; return !!syncServerHost && syncServerHost !== 'disabled';
}, },

View File

@ -93,8 +93,8 @@ function updateNoteReordering(entityChange, entity, instanceId) {
} }
function handleContent(content) { function handleContent(content) {
// we always use Buffer object which is different from normal saving - there we use simple string type for // we always use a Buffer object which is different from normal saving - there we use a simple string type for
// "string notes". The problem is that in general it's not possible to detect whether a blob content // "string notes". The problem is that in general, it's not possible to detect whether a blob content
// is string note or note (syncs can arrive out of order) // is string note or note (syncs can arrive out of order)
content = content === null ? null : Buffer.from(content, 'base64'); content = content === null ? null : Buffer.from(content, 'base64');

View File

@ -14,10 +14,10 @@ class TaskContext {
// progressCount is meant to represent just some progress - to indicate the task is not stuck // progressCount is meant to represent just some progress - to indicate the task is not stuck
this.progressCount = -1; // we're incrementing immediatelly this.progressCount = -1; // we're incrementing immediatelly
this.lastSentCountTs = 0; // 0 will guarantee first message will be sent this.lastSentCountTs = 0; // 0 will guarantee the first message will be sent
// just the fact this has been initialized is a progress which should be sent to clients // just the fact this has been initialized is a progress which should be sent to clients
// this is esp. important when importing big files/images which take long time to upload/process // this is esp. important when importing big files/images which take a long time to upload/process
// which means that first "real" increaseProgressCount() will be called quite late and user is without // which means that first "real" increaseProgressCount() will be called quite late and user is without
// feedback until then // feedback until then
this.increaseProgressCount(); this.increaseProgressCount();

View File

@ -87,7 +87,7 @@ function sortNotes(parentNoteId, customSortBy = 'title', reverse = false, folder
const bHasChildren = b.hasChildren(); const bHasChildren = b.hasChildren();
if ((aHasChildren && !bHasChildren) || (!aHasChildren && bHasChildren)) { if ((aHasChildren && !bHasChildren) || (!aHasChildren && bHasChildren)) {
// exactly one note of the two is a directory so the sorting will be done based on this status // exactly one note of the two is a directory, so the sorting will be done based on this status
return aHasChildren ? -1 : 1; return aHasChildren ? -1 : 1;
} }
} }

View File

@ -9,7 +9,7 @@ const cls = require('./cls');
const keyboardActionsService = require('./keyboard_actions'); const keyboardActionsService = require('./keyboard_actions');
const {ipcMain} = require('electron'); const {ipcMain} = require('electron');
// Prevent window being garbage collected // Prevent the window being garbage collected
/** @type {Electron.BrowserWindow} */ /** @type {Electron.BrowserWindow} */
let mainWindow; let mainWindow;
/** @type {Electron.BrowserWindow} */ /** @type {Electron.BrowserWindow} */
@ -48,7 +48,7 @@ async function createMainWindow(app) {
const windowStateKeeper = require('electron-window-state'); // should not be statically imported const windowStateKeeper = require('electron-window-state'); // should not be statically imported
const mainWindowState = windowStateKeeper({ const mainWindowState = windowStateKeeper({
// default window width & height, so it's usable on 1600 * 900 display (including some extra panels etc.) // default window width & height, so it's usable on a 1600 * 900 display (including some extra panels etc.)
defaultWidth: 1200, defaultWidth: 1200,
defaultHeight: 800 defaultHeight: 800
}); });

View File

@ -185,7 +185,7 @@ function sendPing(client, entityChangeIds = []) {
// sort entity changes since froca expects "referential order", i.e. referenced entities should already exist // sort entity changes since froca expects "referential order", i.e. referenced entities should already exist
// in froca. // in froca.
// Froca needs this since it is incomplete copy, it can't create "skeletons" like becca. // Froca needs this since it is an incomplete copy, it can't create "skeletons" like becca.
entityChanges.sort((a, b) => ORDERING[a.entityName] - ORDERING[b.entityName]); entityChanges.sort((a, b) => ORDERING[a.entityName] - ORDERING[b.entityName]);
for (const entityChange of entityChanges) { for (const entityChange of entityChanges) {

View File

@ -16,7 +16,7 @@ function getSharedSubTreeRoot(note) {
} }
// every path leads to share root, but which one to choose? // every path leads to share root, but which one to choose?
// for sake of simplicity URLs are not note paths // for the sake of simplicity, URLs are not note paths
const parentNote = note.getParentNotes()[0]; const parentNote = note.getParentNotes()[0];
if (parentNote.noteId === shareRoot.SHARE_ROOT_NOTE_ID) { if (parentNote.noteId === shareRoot.SHARE_ROOT_NOTE_ID) {
@ -50,7 +50,7 @@ function checkAttachmentAccess(attachmentId, req, res) {
const note = checkNoteAccess(attachment.parentId, req, res); const note = checkNoteAccess(attachment.parentId, req, res);
// truthy note means user has access, and we can return the attachment // truthy note means the user has access, and we can return the attachment
return note ? attachment : false; return note ? attachment : false;
} }

View File

@ -330,7 +330,8 @@ class SNote extends AbstractShacaEntity {
/** /**
* @param {string} type - attribute type (label, relation, etc.) * @param {string} type - attribute type (label, relation, etc.)
* @param {string} name - attribute name * @param {string} name - attribute name
* @returns {SAttribute} attribute of given type and name. If there's more such attributes, first is returned. Returns null if there's no such attribute belonging to this note. * @returns {SAttribute} attribute of the given type and name. If there are more such attributes, first is returned.
* Returns null if there's no such attribute belonging to this note.
*/ */
getAttribute(type, name) { getAttribute(type, name) {
const attributes = this.getAttributes(); const attributes = this.getAttributes();
@ -341,7 +342,7 @@ class SNote extends AbstractShacaEntity {
/** /**
* @param {string} type - attribute type (label, relation, etc.) * @param {string} type - attribute type (label, relation, etc.)
* @param {string} name - attribute name * @param {string} name - attribute name
* @returns {string|null} attribute value of given type and name or null if no such attribute exists. * @returns {string|null} attribute value of the given type and name or null if no such attribute exists.
*/ */
getAttributeValue(type, name) { getAttributeValue(type, name) {
const attr = this.getAttribute(type, name); const attr = this.getAttribute(type, name);
@ -352,7 +353,7 @@ class SNote extends AbstractShacaEntity {
/** /**
* @param {string} type - attribute type (label, relation, etc.) * @param {string} type - attribute type (label, relation, etc.)
* @param {string} name - attribute name * @param {string} name - attribute name
* @returns {string|null} attribute value of given type and name or null if no such attribute exists. * @returns {string|null} attribute value of the given type and name or null if no such attribute exists.
*/ */
getOwnedAttributeValue(type, name) { getOwnedAttributeValue(type, name) {
const attr = this.getOwnedAttribute(type, name); const attr = this.getOwnedAttribute(type, name);

View File

@ -14,7 +14,7 @@ function load() {
const start = Date.now(); const start = Date.now();
shaca.reset(); shaca.reset();
// using raw query and passing arrays to avoid allocating new objects // using a raw query and passing arrays to avoid allocating new objects
const noteIds = sql.getColumn(` const noteIds = sql.getColumn(`
WITH RECURSIVE WITH RECURSIVE

View File

@ -1,6 +1,6 @@
/** /**
* Usage: node src/tools/generate_document.js 1000 * Usage: node src/tools/generate_document.js 1000
* will create 1000 new notes and some clones into a current document.db * will create 1000 new notes and some clones into the current document.db
*/ */
require('../becca/entity_constructor'); require('../becca/entity_constructor');

View File

@ -55,15 +55,15 @@
<div id="setup-type" data-bind="visible: step() == 'setup-type'" style="margin-top: 20px;"> <div id="setup-type" data-bind="visible: step() == 'setup-type'" style="margin-top: 20px;">
<div class="radio" style="margin-bottom: 15px;"> <div class="radio" style="margin-bottom: 15px;">
<label><input type="radio" name="setup-type" value="new-document" data-bind="checked: setupType"> <label><input type="radio" name="setup-type" value="new-document" data-bind="checked: setupType">
I'm a new user, and I want to create new Trilium document for my notes</label> I'm a new user, and I want to create a new Trilium document for my notes</label>
</div> </div>
<div class="radio" style="margin-bottom: 15px;"> <div class="radio" style="margin-bottom: 15px;">
<label><input type="radio" name="setup-type" value="sync-from-desktop" data-bind="checked: setupType"> <label><input type="radio" name="setup-type" value="sync-from-desktop" data-bind="checked: setupType">
I have desktop instance already, and I want to set up sync with it</label> I have a desktop instance already, and I want to set up sync with it</label>
</div> </div>
<div class="radio" style="margin-bottom: 15px;"> <div class="radio" style="margin-bottom: 15px;">
<label><input type="radio" name="setup-type" value="sync-from-server" data-bind="checked: setupType"> <label><input type="radio" name="setup-type" value="sync-from-server" data-bind="checked: setupType">
I have server instance already, and I want to set up sync with it</label> I have a server instance already, and I want to set up sync with it</label>
</div> </div>
<button type="button" data-bind="disable: !setupTypeSelected(), click: selectSetupType" class="btn btn-primary">Next</button> <button type="button" data-bind="disable: !setupTypeSelected(), click: selectSetupType" class="btn btn-primary">Next</button>

View File

@ -43,7 +43,7 @@ function startTrilium() {
/** /**
* The intended behavior is to detect when a second instance is running, in that case open the old instance * The intended behavior is to detect when a second instance is running, in that case open the old instance
* instead of the new one. This is complicated by the fact that it is possible to run multiple instances of Trilium * instead of the new one. This is complicated by the fact that it is possible to run multiple instances of Trilium
* if port and data dir is configured separately. This complication is the source of the following weird usage. * if port and data dir are configured separately. This complication is the source of the following weird usage.
* *
* The line below makes sure that the "second-instance" (process in window.js) is fired. Normally it returns a boolean * The line below makes sure that the "second-instance" (process in window.js) is fired. Normally it returns a boolean
* indicating whether another instance is running or not, but we ignore that and kill the app only based on the port conflict. * indicating whether another instance is running or not, but we ignore that and kill the app only based on the port conflict.