Compare commits

...

40 Commits

Author SHA1 Message Date
zadam
515c5411a6 release 0.61.5-beta 2023-08-16 23:02:15 +02:00
zadam
3f7a5504c7 prettier config 2023-08-16 20:31:14 +02:00
zadam
8c7c37cf98 updates 2023-08-16 20:12:22 +02:00
zadam
c0f48c0e99 mermaid 10.3.1 2023-08-15 10:30:30 +02:00
zadam
abedf2bba4 fix pasting into ckeditor, upgrade to v39.0.1 2023-08-15 10:25:23 +02:00
zadam
bb0137b2fd fix displaying options / book on firefox, closes #4165 2023-08-15 10:25:23 +02:00
zadam
6c54c7d17d save enex images as attachment, fixes #4163 2023-08-15 10:25:23 +02:00
zadam
90255ac55b release 0.61.4-beta 2023-08-10 23:49:37 +02:00
zadam
e741c2826c release 0.61.4-beta 2023-08-10 23:48:27 +02:00
zadam
026992db78 release 0.61.4-beta 2023-08-10 23:46:42 +02:00
zadam
33780c1e17 fix note tooltip code rendering 2023-08-10 13:50:25 +02:00
zadam
ede9c43f67 API docs 2023-08-10 13:40:26 +02:00
zadam
5c12ac4eee fix CKEditor crashing 2023-08-10 13:40:15 +02:00
zadam
522518cf0d disambiguous query 2023-08-09 23:12:31 +02:00
zadam
1d869d25c2 fix #clipperInbox, closes #4153 2023-08-09 23:00:42 +02:00
zadam
a9cdd93cb4 added options to disable auto-opening of promoted attributes and edited notes ribbon tabs, closes #4151 2023-08-09 22:50:41 +02:00
zadam
4240da349d add shared info to mobile layout, closes #4147 2023-08-09 00:02:45 +02:00
zadam
c257bc07a8 clipper now creates notes with image attachments instead of image notes 2023-08-09 00:01:31 +02:00
zadam
00eaa16985 clipper now creates notes with image attachments instead of image notes 2023-08-08 23:42:47 +02:00
zadam
fefb059564 clipper now creates notes with image attachments instead of image notes 2023-08-08 23:07:59 +02:00
zadam
9166765ced fix include note sizing when in readonly mode, closes #4135 2023-08-08 22:56:45 +02:00
zadam
6ae7661603 note path validation 2023-08-02 23:23:31 +02:00
zadam
30e75056bd release 0.61.3-beta 2023-07-31 23:03:45 +02:00
zadam
530e56dcb5 fixed comment 2023-07-31 22:59:47 +02:00
zadam
63675bfbae make migration 223 NOOP 2023-07-30 21:54:01 +02:00
zadam
696ce38083 ckeditor 38.1.1 2023-07-30 00:32:16 +02:00
zadam
12014b9f4d sync fixes and refactorings 2023-07-29 23:35:08 +02:00
zadam
e8b52f9e6c sync fixes and refactorings 2023-07-29 23:25:02 +02:00
zadam
04b125afc0 sync fixes and refactorings 2023-07-29 21:59:20 +02:00
zadam
2a7fe85020 VACUUM database after migration 2023-07-28 16:22:10 +02:00
zadam
119050e355 fix migration 2023-07-28 16:13:31 +02:00
zadam
72122d0f95 fix blobIds - they shouldn't contain + and / characters 2023-07-28 15:55:26 +02:00
zadam
bd22863bb7 release 0.61.2-beta 2023-07-28 00:06:23 +02:00
zadam
ce3834eb9e blob erasure is not synced, need to clean them up before each content hash check 2023-07-27 23:57:12 +02:00
zadam
8edb5428e5 small sync fixes and refactorings 2023-07-27 23:22:08 +02:00
zadam
527718eff7 add indices on blobId 2023-07-27 21:40:47 +02:00
zadam
5b0f487f3f handle ERR_* errors gracefully 2023-07-27 14:50:18 +02:00
zadam
a3fa8341ba handle ERR_CONNECTION_RESET gracefully 2023-07-27 10:55:34 +02:00
zadam
04813743e2 fix zooming speed, closes #4121 2023-07-25 22:33:35 +02:00
zadam
ddf75cd5e5 note tooltip displays the whole note with scrollbar, other behavior changes. closes #4120 2023-07-25 22:27:15 +02:00
81 changed files with 3286 additions and 2988 deletions

1
.idea/misc.xml generated
View File

@@ -1,4 +1,3 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="JavaScriptSettings">
<option name="languageLevel" value="ES6" />

View File

@@ -1,11 +1,13 @@
//https://prettier.io/docs/en/options.html
//https://prettier.io/docs/en/options.html
module.exports = {
semi: true,
trailingComma: 'es5',
trailingComma: 'none',
singleQuote: true,
printWidth: 120,
printWidth: 100,
tabWidth: 4,
// useTabs: false,
// bracketSpacing: true,
useTabs: false,
quoteProps: "as-needed",
bracketSpacing: true,
arrowParens: "avoid"
// htmlWhitespaceSensitivity: 'ignore',
};

View File

@@ -3,11 +3,11 @@ module.exports = async () => {
const beccaLoader = require("../../src/becca/becca_loader");
const log = require("../../src/services/log");
const consistencyChecks = require("../../src/services/consistency_checks");
const noteService = require("../../src/services/notes");
const eraseService = require("../../src/services/erase");
await cls.init(async () => {
// precaution for the 0211 migration
noteService.eraseDeletedNotesNow();
eraseService.eraseDeletedNotesNow();
beccaLoader.load();

View File

@@ -0,0 +1 @@
SELECT 1;

View File

@@ -0,0 +1,14 @@
UPDATE blobs SET blobId = REPLACE(blobId, '+', 'X');
UPDATE blobs SET blobId = REPLACE(blobId, '/', 'Y');
UPDATE notes SET blobId = REPLACE(blobId, '+', 'X');
UPDATE notes SET blobId = REPLACE(blobId, '/', 'Y');
UPDATE attachments SET blobId = REPLACE(blobId, '+', 'X');
UPDATE attachments SET blobId = REPLACE(blobId, '/', 'Y');
UPDATE revisions SET blobId = REPLACE(blobId, '+', 'X');
UPDATE revisions SET blobId = REPLACE(blobId, '/', 'Y');
UPDATE entity_changes SET entityId = REPLACE(entityId, '+', 'X') WHERE entityName = 'blobs';
UPDATE entity_changes SET entityId = REPLACE(entityId, '/', 'Y') WHERE entityName = 'blobs';

View File

@@ -0,0 +1,3 @@
CREATE INDEX IF NOT EXISTS IDX_notes_blobId on notes (blobId);
CREATE INDEX IF NOT EXISTS IDX_revisions_blobId on revisions (blobId);
CREATE INDEX IF NOT EXISTS IDX_attachments_blobId on attachments (blobId);

View File

@@ -128,3 +128,7 @@ CREATE TABLE IF NOT EXISTS "attachments"
deleteId TEXT DEFAULT NULL);
CREATE INDEX IDX_attachments_ownerId_role
on attachments (ownerId, role);
CREATE INDEX IDX_notes_blobId on notes (blobId);
CREATE INDEX IDX_revisions_blobId on revisions (blobId);
CREATE INDEX IDX_attachments_blobId on attachments (blobId);

View File

@@ -259,7 +259,7 @@
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="becca_entities_abstract_becca_entity.js.html">becca/entities/abstract_becca_entity.js</a>, <a href="becca_entities_abstract_becca_entity.js.html#line243">line 243</a>
<a href="becca_entities_abstract_becca_entity.js.html">becca/entities/abstract_becca_entity.js</a>, <a href="becca_entities_abstract_becca_entity.js.html#line246">line 246</a>
</li></ul></dd>
@@ -394,90 +394,6 @@
<h4 class="name" id="addEntityChange"><span class="type-signature">(protected) </span>addEntityChange<span class="signature">()</span><span class="type-signature"></span></h4>
<dl class="details">
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="becca_entities_abstract_becca_entity.js.html">becca/entities/abstract_becca_entity.js</a>, <a href="becca_entities_abstract_becca_entity.js.html#line64">line 64</a>
</li></ul></dd>
</dl>
@@ -1022,7 +938,91 @@ This is a low-level method, for notes and branches use `note.deleteNote()` and '
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="becca_entities_abstract_becca_entity.js.html">becca/entities/abstract_becca_entity.js</a>, <a href="becca_entities_abstract_becca_entity.js.html#line260">line 260</a>
<a href="becca_entities_abstract_becca_entity.js.html">becca/entities/abstract_becca_entity.js</a>, <a href="becca_entities_abstract_becca_entity.js.html#line263">line 263</a>
</li></ul></dd>
</dl>
<h4 class="name" id="putEntityChange"><span class="type-signature">(protected) </span>putEntityChange<span class="signature">()</span><span class="type-signature"></span></h4>
<dl class="details">
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="becca_entities_abstract_becca_entity.js.html">becca/entities/abstract_becca_entity.js</a>, <a href="becca_entities_abstract_becca_entity.js.html#line64">line 64</a>
</li></ul></dd>

View File

@@ -825,7 +825,7 @@ and relation (representing named relationship between source and target note)</d
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="becca_entities_abstract_becca_entity.js.html">becca/entities/abstract_becca_entity.js</a>, <a href="becca_entities_abstract_becca_entity.js.html#line243">line 243</a>
<a href="becca_entities_abstract_becca_entity.js.html">becca/entities/abstract_becca_entity.js</a>, <a href="becca_entities_abstract_becca_entity.js.html#line246">line 246</a>
</li></ul></dd>
@@ -965,95 +965,6 @@ and relation (representing named relationship between source and target note)</d
<h4 class="name" id="addEntityChange"><span class="type-signature">(protected) </span>addEntityChange<span class="signature">()</span><span class="type-signature"></span></h4>
<dl class="details">
<dt class="tag-overrides">Overrides:</dt>
<dd class="tag-overrides"><ul class="dummy"><li>
<a href="AbstractBeccaEntity.html#addEntityChange">AbstractBeccaEntity#addEntityChange</a>
</li></ul></dd>
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="becca_entities_abstract_becca_entity.js.html">becca/entities/abstract_becca_entity.js</a>, <a href="becca_entities_abstract_becca_entity.js.html#line64">line 64</a>
</li></ul></dd>
</dl>
@@ -1940,7 +1851,96 @@ This is a low-level method, for notes and branches use `note.deleteNote()` and '
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="becca_entities_abstract_becca_entity.js.html">becca/entities/abstract_becca_entity.js</a>, <a href="becca_entities_abstract_becca_entity.js.html#line260">line 260</a>
<a href="becca_entities_abstract_becca_entity.js.html">becca/entities/abstract_becca_entity.js</a>, <a href="becca_entities_abstract_becca_entity.js.html#line263">line 263</a>
</li></ul></dd>
</dl>
<h4 class="name" id="putEntityChange"><span class="type-signature">(protected) </span>putEntityChange<span class="signature">()</span><span class="type-signature"></span></h4>
<dl class="details">
<dt class="tag-overrides">Overrides:</dt>
<dd class="tag-overrides"><ul class="dummy"><li>
<a href="AbstractBeccaEntity.html#putEntityChange">AbstractBeccaEntity#putEntityChange</a>
</li></ul></dd>
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="becca_entities_abstract_becca_entity.js.html">becca/entities/abstract_becca_entity.js</a>, <a href="becca_entities_abstract_becca_entity.js.html#line64">line 64</a>
</li></ul></dd>

View File

@@ -945,7 +945,7 @@ of deletion should not act as a clone.
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="becca_entities_abstract_becca_entity.js.html">becca/entities/abstract_becca_entity.js</a>, <a href="becca_entities_abstract_becca_entity.js.html#line243">line 243</a>
<a href="becca_entities_abstract_becca_entity.js.html">becca/entities/abstract_becca_entity.js</a>, <a href="becca_entities_abstract_becca_entity.js.html#line246">line 246</a>
</li></ul></dd>
@@ -1085,95 +1085,6 @@ of deletion should not act as a clone.
<h4 class="name" id="addEntityChange"><span class="type-signature">(protected) </span>addEntityChange<span class="signature">()</span><span class="type-signature"></span></h4>
<dl class="details">
<dt class="tag-overrides">Overrides:</dt>
<dd class="tag-overrides"><ul class="dummy"><li>
<a href="AbstractBeccaEntity.html#addEntityChange">AbstractBeccaEntity#addEntityChange</a>
</li></ul></dd>
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="becca_entities_abstract_becca_entity.js.html">becca/entities/abstract_becca_entity.js</a>, <a href="becca_entities_abstract_becca_entity.js.html#line64">line 64</a>
</li></ul></dd>
</dl>
@@ -2054,7 +1965,96 @@ This is a low-level method, for notes and branches use `note.deleteNote()` and '
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="becca_entities_abstract_becca_entity.js.html">becca/entities/abstract_becca_entity.js</a>, <a href="becca_entities_abstract_becca_entity.js.html#line260">line 260</a>
<a href="becca_entities_abstract_becca_entity.js.html">becca/entities/abstract_becca_entity.js</a>, <a href="becca_entities_abstract_becca_entity.js.html#line263">line 263</a>
</li></ul></dd>
</dl>
<h4 class="name" id="putEntityChange"><span class="type-signature">(protected) </span>putEntityChange<span class="signature">()</span><span class="type-signature"></span></h4>
<dl class="details">
<dt class="tag-overrides">Overrides:</dt>
<dd class="tag-overrides"><ul class="dummy"><li>
<a href="AbstractBeccaEntity.html#putEntityChange">AbstractBeccaEntity#putEntityChange</a>
</li></ul></dd>
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="becca_entities_abstract_becca_entity.js.html">becca/entities/abstract_becca_entity.js</a>, <a href="becca_entities_abstract_becca_entity.js.html#line64">line 64</a>
</li></ul></dd>

View File

@@ -694,7 +694,7 @@ from tokenHash and token.</div>
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="becca_entities_abstract_becca_entity.js.html">becca/entities/abstract_becca_entity.js</a>, <a href="becca_entities_abstract_becca_entity.js.html#line243">line 243</a>
<a href="becca_entities_abstract_becca_entity.js.html">becca/entities/abstract_becca_entity.js</a>, <a href="becca_entities_abstract_becca_entity.js.html#line246">line 246</a>
</li></ul></dd>
@@ -834,95 +834,6 @@ from tokenHash and token.</div>
<h4 class="name" id="addEntityChange"><span class="type-signature">(protected) </span>addEntityChange<span class="signature">()</span><span class="type-signature"></span></h4>
<dl class="details">
<dt class="tag-overrides">Overrides:</dt>
<dd class="tag-overrides"><ul class="dummy"><li>
<a href="AbstractBeccaEntity.html#addEntityChange">AbstractBeccaEntity#addEntityChange</a>
</li></ul></dd>
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="becca_entities_abstract_becca_entity.js.html">becca/entities/abstract_becca_entity.js</a>, <a href="becca_entities_abstract_becca_entity.js.html#line64">line 64</a>
</li></ul></dd>
</dl>
@@ -1497,7 +1408,96 @@ This is a low-level method, for notes and branches use `note.deleteNote()` and '
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="becca_entities_abstract_becca_entity.js.html">becca/entities/abstract_becca_entity.js</a>, <a href="becca_entities_abstract_becca_entity.js.html#line260">line 260</a>
<a href="becca_entities_abstract_becca_entity.js.html">becca/entities/abstract_becca_entity.js</a>, <a href="becca_entities_abstract_becca_entity.js.html#line263">line 263</a>
</li></ul></dd>
</dl>
<h4 class="name" id="putEntityChange"><span class="type-signature">(protected) </span>putEntityChange<span class="signature">()</span><span class="type-signature"></span></h4>
<dl class="details">
<dt class="tag-overrides">Overrides:</dt>
<dd class="tag-overrides"><ul class="dummy"><li>
<a href="AbstractBeccaEntity.html#putEntityChange">AbstractBeccaEntity#putEntityChange</a>
</li></ul></dd>
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="becca_entities_abstract_becca_entity.js.html">becca/entities/abstract_becca_entity.js</a>, <a href="becca_entities_abstract_becca_entity.js.html#line64">line 64</a>
</li></ul></dd>

View File

@@ -1171,7 +1171,7 @@
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="becca_entities_abstract_becca_entity.js.html">becca/entities/abstract_becca_entity.js</a>, <a href="becca_entities_abstract_becca_entity.js.html#line243">line 243</a>
<a href="becca_entities_abstract_becca_entity.js.html">becca/entities/abstract_becca_entity.js</a>, <a href="becca_entities_abstract_becca_entity.js.html#line246">line 246</a>
</li></ul></dd>
@@ -1588,7 +1588,7 @@ See addLabel, addRelation for more specific methods.
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line1303">line 1303</a>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line1308">line 1308</a>
</li></ul></dd>
@@ -1636,95 +1636,6 @@ See addLabel, addRelation for more specific methods.
<h4 class="name" id="addEntityChange"><span class="type-signature">(protected) </span>addEntityChange<span class="signature">()</span><span class="type-signature"></span></h4>
<dl class="details">
<dt class="tag-overrides">Overrides:</dt>
<dd class="tag-overrides"><ul class="dummy"><li>
<a href="AbstractBeccaEntity.html#addEntityChange">AbstractBeccaEntity#addEntityChange</a>
</li></ul></dd>
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="becca_entities_abstract_becca_entity.js.html">becca/entities/abstract_becca_entity.js</a>, <a href="becca_entities_abstract_becca_entity.js.html#line64">line 64</a>
</li></ul></dd>
</dl>
@@ -1924,7 +1835,7 @@ See addLabel, addRelation for more specific methods.
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line1324">line 1324</a>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line1329">line 1329</a>
</li></ul></dd>
@@ -2170,7 +2081,7 @@ returned.
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line1337">line 1337</a>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line1342">line 1342</a>
</li></ul></dd>
@@ -2405,7 +2316,7 @@ returned.
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line1422">line 1422</a>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line1427">line 1427</a>
</li></ul></dd>
@@ -2521,7 +2432,7 @@ In the future, this functionality might get more generic and some of the require
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line1474">line 1474</a>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line1479">line 1479</a>
</li></ul></dd>
@@ -2742,7 +2653,7 @@ In the future, this functionality might get more generic and some of the require
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line1512">line 1512</a>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line1517">line 1517</a>
</li></ul></dd>
@@ -3008,7 +2919,7 @@ In the future, this functionality might get more generic and some of the require
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line1144">line 1144</a>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line1149">line 1149</a>
</li></ul></dd>
@@ -3114,7 +3025,7 @@ In the future, this functionality might get more generic and some of the require
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line1032">line 1032</a>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line1037">line 1037</a>
</li></ul></dd>
@@ -3216,7 +3127,7 @@ In the future, this functionality might get more generic and some of the require
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line1006">line 1006</a>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line1011">line 1011</a>
</li></ul></dd>
@@ -3318,7 +3229,7 @@ In the future, this functionality might get more generic and some of the require
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line1113">line 1113</a>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line1118">line 1118</a>
</li></ul></dd>
@@ -3423,7 +3334,7 @@ In the future, this functionality might get more generic and some of the require
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line1128">line 1128</a>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line1133">line 1133</a>
</li></ul></dd>
@@ -3525,7 +3436,7 @@ In the future, this functionality might get more generic and some of the require
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line1095">line 1095</a>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line1100">line 1100</a>
</li></ul></dd>
@@ -3699,7 +3610,7 @@ In the future, this functionality might get more generic and some of the require
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line572">line 572</a>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line574">line 574</a>
</li></ul></dd>
@@ -3878,7 +3789,7 @@ In the future, this functionality might get more generic and some of the require
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line583">line 583</a>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line585">line 585</a>
</li></ul></dd>
@@ -3946,6 +3857,11 @@ In the future, this functionality might get more generic and some of the require
<div class="description">
Beware that the method must not create a copy of the array, but actually returns its internal array
(for performance reasons)
</div>
@@ -4081,7 +3997,7 @@ In the future, this functionality might get more generic and some of the require
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line324">line 324</a>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line327">line 327</a>
</li></ul></dd>
@@ -4260,7 +4176,7 @@ In the future, this functionality might get more generic and some of the require
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line1197">line 1197</a>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line1202">line 1202</a>
</li></ul></dd>
@@ -4439,7 +4355,7 @@ In the future, this functionality might get more generic and some of the require
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line1207">line 1207</a>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line1212">line 1212</a>
</li></ul></dd>
@@ -5062,7 +4978,7 @@ In the future, this functionality might get more generic and some of the require
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line953">line 953</a>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line958">line 958</a>
</li></ul></dd>
@@ -5152,7 +5068,7 @@ In the future, this functionality might get more generic and some of the require
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line754">line 754</a>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line759">line 759</a>
</li></ul></dd>
@@ -5258,7 +5174,7 @@ In the future, this functionality might get more generic and some of the require
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line1058">line 1058</a>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line1063">line 1063</a>
</li></ul></dd>
@@ -5516,7 +5432,7 @@ In the future, this functionality might get more generic and some of the require
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line512">line 512</a>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line514">line 514</a>
</li></ul></dd>
@@ -5674,7 +5590,7 @@ In the future, this functionality might get more generic and some of the require
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line536">line 536</a>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line538">line 538</a>
</li></ul></dd>
@@ -5844,7 +5760,7 @@ In the future, this functionality might get more generic and some of the require
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line612">line 612</a>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line614">line 614</a>
</li></ul></dd>
@@ -6011,7 +5927,7 @@ In the future, this functionality might get more generic and some of the require
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line604">line 604</a>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line606">line 606</a>
</li></ul></dd>
@@ -6117,7 +6033,7 @@ In the future, this functionality might get more generic and some of the require
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line679">line 679</a>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line684">line 684</a>
</li></ul></dd>
@@ -6297,7 +6213,7 @@ This method can be significantly faster than the getAttribute()
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line594">line 594</a>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line596">line 596</a>
</li></ul></dd>
@@ -6365,6 +6281,11 @@ This method can be significantly faster than the getAttribute()
<div class="description">
Beware that the method must not create a copy of the array, but actually returns its internal array
(for performance reasons)
</div>
@@ -6562,7 +6483,7 @@ This method can be significantly faster than the getAttribute()
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line654">line 654</a>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line659">line 659</a>
</li></ul></dd>
@@ -6717,7 +6638,7 @@ This method can be significantly faster than the getAttribute()
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line518">line 518</a>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line520">line 520</a>
</li></ul></dd>
@@ -6875,7 +6796,7 @@ This method can be significantly faster than the getAttribute()
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line542">line 542</a>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line544">line 544</a>
</li></ul></dd>
@@ -7045,7 +6966,7 @@ This method can be significantly faster than the getAttribute()
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line628">line 628</a>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line630">line 630</a>
</li></ul></dd>
@@ -7212,7 +7133,7 @@ This method can be significantly faster than the getAttribute()
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line620">line 620</a>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line622">line 622</a>
</li></ul></dd>
@@ -7367,7 +7288,7 @@ This method can be significantly faster than the getAttribute()
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line530">line 530</a>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line532">line 532</a>
</li></ul></dd>
@@ -7525,7 +7446,7 @@ This method can be significantly faster than the getAttribute()
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line554">line 554</a>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line556">line 556</a>
</li></ul></dd>
@@ -7695,7 +7616,7 @@ This method can be significantly faster than the getAttribute()
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line644">line 644</a>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line646">line 646</a>
</li></ul></dd>
@@ -8143,7 +8064,7 @@ This method can be significantly faster than the getAttribute()
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line524">line 524</a>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line526">line 526</a>
</li></ul></dd>
@@ -8301,7 +8222,7 @@ This method can be significantly faster than the getAttribute()
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line548">line 548</a>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line550">line 550</a>
</li></ul></dd>
@@ -8471,7 +8392,7 @@ This method can be significantly faster than the getAttribute()
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line636">line 636</a>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line638">line 638</a>
</li></ul></dd>
@@ -8577,7 +8498,7 @@ This method can be significantly faster than the getAttribute()
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line1089">line 1089</a>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line1094">line 1094</a>
</li></ul></dd>
@@ -8788,7 +8709,7 @@ This method can be significantly faster than the getAttribute()
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line864">line 864</a>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line869">line 869</a>
</li></ul></dd>
@@ -8959,7 +8880,7 @@ This method can be significantly faster than the getAttribute()
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line1166">line 1166</a>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line1171">line 1171</a>
</li></ul></dd>
@@ -9167,7 +9088,7 @@ This method can be significantly faster than the getAttribute()
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line887">line 887</a>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line892">line 892</a>
</li></ul></dd>
@@ -9269,7 +9190,7 @@ This method can be significantly faster than the getAttribute()
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line946">line 946</a>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line951">line 951</a>
</li></ul></dd>
@@ -9375,7 +9296,7 @@ This method can be significantly faster than the getAttribute()
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line832">line 832</a>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line837">line 837</a>
</li></ul></dd>
@@ -9477,7 +9398,7 @@ This method can be significantly faster than the getAttribute()
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line1052">line 1052</a>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line1057">line 1057</a>
</li></ul></dd>
@@ -9668,7 +9589,7 @@ This method can be significantly faster than the getAttribute()
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line1037">line 1037</a>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line1042">line 1042</a>
</li></ul></dd>
@@ -9894,7 +9815,7 @@ This method can be significantly faster than the getAttribute()
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line442">line 442</a>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line444">line 444</a>
</li></ul></dd>
@@ -10190,7 +10111,7 @@ This method can be significantly faster than the getAttribute()
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line471">line 471</a>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line473">line 473</a>
</li></ul></dd>
@@ -10419,7 +10340,7 @@ This method can be significantly faster than the getAttribute()
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line562">line 562</a>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line564">line 564</a>
</li></ul></dd>
@@ -10617,7 +10538,7 @@ This method can be significantly faster than the getAttribute()
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line492">line 492</a>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line494">line 494</a>
</li></ul></dd>
@@ -10815,7 +10736,7 @@ This method can be significantly faster than the getAttribute()
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line506">line 506</a>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line508">line 508</a>
</li></ul></dd>
@@ -11013,7 +10934,7 @@ This method can be significantly faster than the getAttribute()
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line499">line 499</a>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line501">line 501</a>
</li></ul></dd>
@@ -11269,7 +11190,7 @@ This method can be significantly faster than the getAttribute()
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line1238">line 1238</a>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line1243">line 1243</a>
</li></ul></dd>
@@ -11375,7 +11296,7 @@ This method can be significantly faster than the getAttribute()
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line1216">line 1216</a>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line1221">line 1221</a>
</li></ul></dd>
@@ -11942,7 +11863,7 @@ This method can be significantly faster than the getAttribute()
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line477">line 477</a>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line479">line 479</a>
</li></ul></dd>
@@ -12315,7 +12236,96 @@ This is a low-level method, for notes and branches use `note.deleteNote()` and '
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="becca_entities_abstract_becca_entity.js.html">becca/entities/abstract_becca_entity.js</a>, <a href="becca_entities_abstract_becca_entity.js.html#line260">line 260</a>
<a href="becca_entities_abstract_becca_entity.js.html">becca/entities/abstract_becca_entity.js</a>, <a href="becca_entities_abstract_becca_entity.js.html#line263">line 263</a>
</li></ul></dd>
</dl>
<h4 class="name" id="putEntityChange"><span class="type-signature">(protected) </span>putEntityChange<span class="signature">()</span><span class="type-signature"></span></h4>
<dl class="details">
<dt class="tag-overrides">Overrides:</dt>
<dd class="tag-overrides"><ul class="dummy"><li>
<a href="AbstractBeccaEntity.html#putEntityChange">AbstractBeccaEntity#putEntityChange</a>
</li></ul></dd>
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="becca_entities_abstract_becca_entity.js.html">becca/entities/abstract_becca_entity.js</a>, <a href="becca_entities_abstract_becca_entity.js.html#line64">line 64</a>
</li></ul></dd>
@@ -12526,7 +12536,7 @@ This is a low-level method, for notes and branches use `note.deleteNote()` and '
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line1282">line 1282</a>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line1287">line 1287</a>
</li></ul></dd>
@@ -12706,7 +12716,7 @@ This is a low-level method, for notes and branches use `note.deleteNote()` and '
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line1398">line 1398</a>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line1403">line 1403</a>
</li></ul></dd>
@@ -12886,7 +12896,7 @@ This is a low-level method, for notes and branches use `note.deleteNote()` and '
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line1406">line 1406</a>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line1411">line 1411</a>
</li></ul></dd>
@@ -13081,7 +13091,7 @@ This is a low-level method, for notes and branches use `note.deleteNote()` and '
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line1619">line 1619</a>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line1624">line 1624</a>
</li></ul></dd>
@@ -13183,7 +13193,7 @@ This is a low-level method, for notes and branches use `note.deleteNote()` and '
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line1566">line 1566</a>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line1571">line 1571</a>
</li></ul></dd>
@@ -13415,7 +13425,7 @@ This is a low-level method, for notes and branches use `note.deleteNote()` and '
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line1251">line 1251</a>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line1256">line 1256</a>
</li></ul></dd>
@@ -13874,7 +13884,7 @@ This is a low-level method, for notes and branches use `note.deleteNote()` and '
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line1382">line 1382</a>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line1387">line 1387</a>
</li></ul></dd>
@@ -14034,7 +14044,7 @@ This is a low-level method, for notes and branches use `note.deleteNote()` and '
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line1390">line 1390</a>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line1395">line 1395</a>
</li></ul></dd>
@@ -14276,7 +14286,7 @@ This is a low-level method, for notes and branches use `note.deleteNote()` and '
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line1349">line 1349</a>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line1354">line 1354</a>
</li></ul></dd>
@@ -14487,7 +14497,7 @@ This is a low-level method, for notes and branches use `note.deleteNote()` and '
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line1365">line 1365</a>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line1370">line 1370</a>
</li></ul></dd>
@@ -14698,7 +14708,7 @@ This is a low-level method, for notes and branches use `note.deleteNote()` and '
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line1374">line 1374</a>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line1379">line 1379</a>
</li></ul></dd>

View File

@@ -552,7 +552,7 @@
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="becca_entities_abstract_becca_entity.js.html">becca/entities/abstract_becca_entity.js</a>, <a href="becca_entities_abstract_becca_entity.js.html#line243">line 243</a>
<a href="becca_entities_abstract_becca_entity.js.html">becca/entities/abstract_becca_entity.js</a>, <a href="becca_entities_abstract_becca_entity.js.html#line246">line 246</a>
</li></ul></dd>
@@ -692,95 +692,6 @@
<h4 class="name" id="addEntityChange"><span class="type-signature">(protected) </span>addEntityChange<span class="signature">()</span><span class="type-signature"></span></h4>
<dl class="details">
<dt class="tag-overrides">Overrides:</dt>
<dd class="tag-overrides"><ul class="dummy"><li>
<a href="AbstractBeccaEntity.html#addEntityChange">AbstractBeccaEntity#addEntityChange</a>
</li></ul></dd>
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="becca_entities_abstract_becca_entity.js.html">becca/entities/abstract_becca_entity.js</a>, <a href="becca_entities_abstract_becca_entity.js.html#line64">line 64</a>
</li></ul></dd>
</dl>
@@ -1355,7 +1266,96 @@ This is a low-level method, for notes and branches use `note.deleteNote()` and '
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="becca_entities_abstract_becca_entity.js.html">becca/entities/abstract_becca_entity.js</a>, <a href="becca_entities_abstract_becca_entity.js.html#line260">line 260</a>
<a href="becca_entities_abstract_becca_entity.js.html">becca/entities/abstract_becca_entity.js</a>, <a href="becca_entities_abstract_becca_entity.js.html#line263">line 263</a>
</li></ul></dd>
</dl>
<h4 class="name" id="putEntityChange"><span class="type-signature">(protected) </span>putEntityChange<span class="signature">()</span><span class="type-signature"></span></h4>
<dl class="details">
<dt class="tag-overrides">Overrides:</dt>
<dd class="tag-overrides"><ul class="dummy"><li>
<a href="AbstractBeccaEntity.html#putEntityChange">AbstractBeccaEntity#putEntityChange</a>
</li></ul></dd>
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="becca_entities_abstract_becca_entity.js.html">becca/entities/abstract_becca_entity.js</a>, <a href="becca_entities_abstract_becca_entity.js.html#line64">line 64</a>
</li></ul></dd>

View File

@@ -484,7 +484,7 @@
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="becca_entities_abstract_becca_entity.js.html">becca/entities/abstract_becca_entity.js</a>, <a href="becca_entities_abstract_becca_entity.js.html#line243">line 243</a>
<a href="becca_entities_abstract_becca_entity.js.html">becca/entities/abstract_becca_entity.js</a>, <a href="becca_entities_abstract_becca_entity.js.html#line246">line 246</a>
</li></ul></dd>
@@ -624,95 +624,6 @@
<h4 class="name" id="addEntityChange"><span class="type-signature">(protected) </span>addEntityChange<span class="signature">()</span><span class="type-signature"></span></h4>
<dl class="details">
<dt class="tag-overrides">Overrides:</dt>
<dd class="tag-overrides"><ul class="dummy"><li>
<a href="AbstractBeccaEntity.html#addEntityChange">AbstractBeccaEntity#addEntityChange</a>
</li></ul></dd>
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="becca_entities_abstract_becca_entity.js.html">becca/entities/abstract_becca_entity.js</a>, <a href="becca_entities_abstract_becca_entity.js.html#line64">line 64</a>
</li></ul></dd>
</dl>
@@ -1287,7 +1198,96 @@ This is a low-level method, for notes and branches use `note.deleteNote()` and '
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="becca_entities_abstract_becca_entity.js.html">becca/entities/abstract_becca_entity.js</a>, <a href="becca_entities_abstract_becca_entity.js.html#line260">line 260</a>
<a href="becca_entities_abstract_becca_entity.js.html">becca/entities/abstract_becca_entity.js</a>, <a href="becca_entities_abstract_becca_entity.js.html#line263">line 263</a>
</li></ul></dd>
</dl>
<h4 class="name" id="putEntityChange"><span class="type-signature">(protected) </span>putEntityChange<span class="signature">()</span><span class="type-signature"></span></h4>
<dl class="details">
<dt class="tag-overrides">Overrides:</dt>
<dd class="tag-overrides"><ul class="dummy"><li>
<a href="AbstractBeccaEntity.html#putEntityChange">AbstractBeccaEntity#putEntityChange</a>
</li></ul></dd>
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="becca_entities_abstract_becca_entity.js.html">becca/entities/abstract_becca_entity.js</a>, <a href="becca_entities_abstract_becca_entity.js.html#line64">line 64</a>
</li></ul></dd>

View File

@@ -89,8 +89,8 @@ class AbstractBeccaEntity {
}
/** @protected */
addEntityChange(isDeleted = false) {
entityChangesService.addEntityChange({
putEntityChange(isDeleted = false) {
entityChangesService.putEntityChange({
entityName: this.constructor.entityName,
entityId: this[this.constructor.primaryKeyName],
hash: this.generateHash(isDeleted),
@@ -129,7 +129,7 @@ class AbstractBeccaEntity {
return;
}
this.addEntityChange(false);
this.putEntityChange(false);
if (!cls.isEntityEventsDisabled()) {
const eventPayload = {
@@ -160,8 +160,7 @@ class AbstractBeccaEntity {
if (this.hasStringContent()) {
content = content.toString();
}
else {
} else {
content = Buffer.isBuffer(content) ? content : Buffer.from(content);
}
@@ -184,32 +183,34 @@ class AbstractBeccaEntity {
this.save();
if (newBlobId !== oldBlobId) {
this.#deleteBlobIfNoteUsed(oldBlobId);
this.#deleteBlobIfNotUsed(oldBlobId);
}
}
});
}
#deleteBlobIfNoteUsed(blobId) {
if (sql.getValue("SELECT 1 FROM notes WHERE blobId = ? LIMIT 1", [blobId])) {
#deleteBlobIfNotUsed(oldBlobId) {
if (sql.getValue("SELECT 1 FROM notes WHERE blobId = ? LIMIT 1", [oldBlobId])) {
return;
}
if (sql.getValue("SELECT 1 FROM attachments WHERE blobId = ? LIMIT 1", [blobId])) {
if (sql.getValue("SELECT 1 FROM attachments WHERE blobId = ? LIMIT 1", [oldBlobId])) {
return;
}
if (sql.getValue("SELECT 1 FROM revisions WHERE blobId = ? LIMIT 1", [blobId])) {
if (sql.getValue("SELECT 1 FROM revisions WHERE blobId = ? LIMIT 1", [oldBlobId])) {
return;
}
sql.execute("DELETE FROM blobs WHERE blobId = ?", [blobId]);
sql.execute("DELETE FROM entity_changes WHERE entityName = 'blobs' AND entityId = ?", [blobId]);
sql.execute("DELETE FROM blobs WHERE blobId = ?", [oldBlobId]);
// blobs are not marked as erased in entity_changes, they are just purged completely
// this is because technically every keystroke can create a new blob and there would be just too many
sql.execute("DELETE FROM entity_changes WHERE entityName = 'blobs' AND entityId = ?", [oldBlobId]);
}
#getUnencryptedContentForHashCalculation(unencryptedContent) {
if (this.isProtected) {
// a "random" prefix make sure that the calculated hash/blobId is different for an encrypted note and decrypted
// a "random" prefix makes sure that the calculated hash/blobId is different for a decrypted/encrypted content
const encryptedPrefixSuffix = "t$[nvQg7q)&amp;_ENCRYPTED_?M:Bf&amp;j3jr_";
return Buffer.isBuffer(unencryptedContent)
? Buffer.concat([Buffer.from(encryptedPrefixSuffix), unencryptedContent])
@@ -224,7 +225,7 @@ class AbstractBeccaEntity {
* We're using the unencrypted blob for the hash calculation, because otherwise the random IV would
* cause every content blob to be unique which would balloon the database size (esp. with revisioning).
* This has minor security implications (it's easy to infer that given content is shared between different
* notes/attachments, but the trade-off comes out clearly positive).
* notes/attachments), but the trade-off comes out clearly positive.
*/
const newBlobId = utils.hashedBlobId(unencryptedContentForHashCalculation);
const blobNeedsInsert = !sql.getValue('SELECT 1 FROM blobs WHERE blobId = ?', [newBlobId]);
@@ -242,9 +243,11 @@ class AbstractBeccaEntity {
sql.upsert("blobs", "blobId", pojo);
const hash = utils.hash(`${newBlobId}|${pojo.content.toString()}`);
// we can't reuse blobId as an entity_changes hash, because this one has to be calculatable without having
// access to the decrypted content
const hash = blobService.calculateContentHash(pojo);
entityChangesService.addEntityChange({
entityChangesService.putEntityChange({
entityName: 'blobs',
entityId: newBlobId,
hash: hash,
@@ -304,7 +307,7 @@ class AbstractBeccaEntity {
log.info(`Marking ${entityName} ${entityId} as deleted`);
this.addEntityChange(true);
this.putEntityChange(true);
eventService.emit(eventService.ENTITY_DELETED, { entityName, entityId, entity: this });
}
@@ -321,7 +324,7 @@ class AbstractBeccaEntity {
log.info(`Marking ${entityName} ${entityId} as deleted`);
this.addEntityChange(true);
this.putEntityChange(true);
eventService.emit(eventService.ENTITY_DELETED, { entityName, entityId, entity: this });
}

View File

@@ -219,7 +219,7 @@ class BBranch extends AbstractBeccaEntity {
this.becca.notes[note.noteId].isBeingDeleted = true;
for (const attribute of note.getOwnedAttributes()) {
for (const attribute of note.getOwnedAttributes().slice()) {
attribute.markAsDeleted(deleteId);
}

View File

@@ -345,6 +345,9 @@ class BNote extends AbstractBeccaEntity {
}
/**
* Beware that the method must not create a copy of the array, but actually returns its internal array
* (for performance reasons)
*
* @param {string} [type] - (optional) attribute type to filter
* @param {string} [name] - (optional) attribute name to filter
* @returns {BAttribute[]} all note's attributes, including inherited ones
@@ -363,7 +366,6 @@ class BNote extends AbstractBeccaEntity {
return this.__attributeCache.filter(attr => attr.name === name);
}
else {
// a bit unsafe to return the original array, but defensive copy would be costly
return this.__attributeCache;
}
}
@@ -674,6 +676,9 @@ class BNote extends AbstractBeccaEntity {
}
/**
* Beware that the method must not create a copy of the array, but actually returns its internal array
* (for performance reasons)
*
* @param {string|null} [type] - (optional) attribute type to filter
* @param {string|null} [name] - (optional) attribute name to filter
* @param {string|null} [value] - (optional) attribute value to filter

View File

@@ -194,7 +194,75 @@ and relation (representing named relationship between source and target note)</d
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_fattribute.js.html">entities/fattribute.js</a>, <a href="entities_fattribute.js.html#line16">line 16</a>
<a href="entities_fattribute.js.html">entities/fattribute.js</a>, <a href="entities_fattribute.js.html#line17">line 17</a>
</li></ul></dd>
</dl>
<h4 class="name" id="froca"><span class="type-signature"></span>froca<span class="type-signature"> :Froca</span></h4>
<h5>Type:</h5>
<ul>
<li>
<span class="param-type">Froca</span>
</li>
</ul>
<dl class="details">
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_fattribute.js.html">entities/fattribute.js</a>, <a href="entities_fattribute.js.html#line10">line 10</a>
</li></ul></dd>
@@ -262,7 +330,7 @@ and relation (representing named relationship between source and target note)</d
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_fattribute.js.html">entities/fattribute.js</a>, <a href="entities_fattribute.js.html#line28">line 28</a>
<a href="entities_fattribute.js.html">entities/fattribute.js</a>, <a href="entities_fattribute.js.html#line29">line 29</a>
</li></ul></dd>
@@ -330,7 +398,7 @@ and relation (representing named relationship between source and target note)</d
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_fattribute.js.html">entities/fattribute.js</a>, <a href="entities_fattribute.js.html#line22">line 22</a>
<a href="entities_fattribute.js.html">entities/fattribute.js</a>, <a href="entities_fattribute.js.html#line23">line 23</a>
</li></ul></dd>
@@ -398,7 +466,7 @@ and relation (representing named relationship between source and target note)</d
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_fattribute.js.html">entities/fattribute.js</a>, <a href="entities_fattribute.js.html#line18">line 18</a>
<a href="entities_fattribute.js.html">entities/fattribute.js</a>, <a href="entities_fattribute.js.html#line19">line 19</a>
</li></ul></dd>
@@ -466,7 +534,7 @@ and relation (representing named relationship between source and target note)</d
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_fattribute.js.html">entities/fattribute.js</a>, <a href="entities_fattribute.js.html#line26">line 26</a>
<a href="entities_fattribute.js.html">entities/fattribute.js</a>, <a href="entities_fattribute.js.html#line27">line 27</a>
</li></ul></dd>
@@ -534,7 +602,7 @@ and relation (representing named relationship between source and target note)</d
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_fattribute.js.html">entities/fattribute.js</a>, <a href="entities_fattribute.js.html#line20">line 20</a>
<a href="entities_fattribute.js.html">entities/fattribute.js</a>, <a href="entities_fattribute.js.html#line21">line 21</a>
</li></ul></dd>
@@ -602,7 +670,7 @@ and relation (representing named relationship between source and target note)</d
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_fattribute.js.html">entities/fattribute.js</a>, <a href="entities_fattribute.js.html#line24">line 24</a>
<a href="entities_fattribute.js.html">entities/fattribute.js</a>, <a href="entities_fattribute.js.html#line25">line 25</a>
</li></ul></dd>
@@ -678,7 +746,7 @@ and relation (representing named relationship between source and target note)</d
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_fattribute.js.html">entities/fattribute.js</a>, <a href="entities_fattribute.js.html#line32">line 32</a>
<a href="entities_fattribute.js.html">entities/fattribute.js</a>, <a href="entities_fattribute.js.html#line33">line 33</a>
</li></ul></dd>
@@ -780,7 +848,7 @@ and relation (representing named relationship between source and target note)</d
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_fattribute.js.html">entities/fattribute.js</a>, <a href="entities_fattribute.js.html#line37">line 37</a>
<a href="entities_fattribute.js.html">entities/fattribute.js</a>, <a href="entities_fattribute.js.html#line38">line 38</a>
</li></ul></dd>

View File

@@ -198,7 +198,75 @@ parents.</div>
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_fbranch.js.html">entities/fbranch.js</a>, <a href="entities_fbranch.js.html#line17">line 17</a>
<a href="entities_fbranch.js.html">entities/fbranch.js</a>, <a href="entities_fbranch.js.html#line18">line 18</a>
</li></ul></dd>
</dl>
<h4 class="name" id="froca"><span class="type-signature"></span>froca<span class="type-signature"> :Froca</span></h4>
<h5>Type:</h5>
<ul>
<li>
<span class="param-type">Froca</span>
</li>
</ul>
<dl class="details">
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_fbranch.js.html">entities/fbranch.js</a>, <a href="entities_fbranch.js.html#line8">line 8</a>
</li></ul></dd>
@@ -266,7 +334,7 @@ parents.</div>
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_fbranch.js.html">entities/fbranch.js</a>, <a href="entities_fbranch.js.html#line29">line 29</a>
<a href="entities_fbranch.js.html">entities/fbranch.js</a>, <a href="entities_fbranch.js.html#line30">line 30</a>
</li></ul></dd>
@@ -334,7 +402,7 @@ parents.</div>
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_fbranch.js.html">entities/fbranch.js</a>, <a href="entities_fbranch.js.html#line27">line 27</a>
<a href="entities_fbranch.js.html">entities/fbranch.js</a>, <a href="entities_fbranch.js.html#line28">line 28</a>
</li></ul></dd>
@@ -402,7 +470,7 @@ parents.</div>
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_fbranch.js.html">entities/fbranch.js</a>, <a href="entities_fbranch.js.html#line19">line 19</a>
<a href="entities_fbranch.js.html">entities/fbranch.js</a>, <a href="entities_fbranch.js.html#line20">line 20</a>
</li></ul></dd>
@@ -470,7 +538,7 @@ parents.</div>
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_fbranch.js.html">entities/fbranch.js</a>, <a href="entities_fbranch.js.html#line23">line 23</a>
<a href="entities_fbranch.js.html">entities/fbranch.js</a>, <a href="entities_fbranch.js.html#line24">line 24</a>
</li></ul></dd>
@@ -538,7 +606,7 @@ parents.</div>
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_fbranch.js.html">entities/fbranch.js</a>, <a href="entities_fbranch.js.html#line21">line 21</a>
<a href="entities_fbranch.js.html">entities/fbranch.js</a>, <a href="entities_fbranch.js.html#line22">line 22</a>
</li></ul></dd>
@@ -606,7 +674,7 @@ parents.</div>
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_fbranch.js.html">entities/fbranch.js</a>, <a href="entities_fbranch.js.html#line25">line 25</a>
<a href="entities_fbranch.js.html">entities/fbranch.js</a>, <a href="entities_fbranch.js.html#line26">line 26</a>
</li></ul></dd>
@@ -682,7 +750,7 @@ parents.</div>
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_fbranch.js.html">entities/fbranch.js</a>, <a href="entities_fbranch.js.html#line33">line 33</a>
<a href="entities_fbranch.js.html">entities/fbranch.js</a>, <a href="entities_fbranch.js.html#line34">line 34</a>
</li></ul></dd>
@@ -784,7 +852,7 @@ parents.</div>
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_fbranch.js.html">entities/fbranch.js</a>, <a href="entities_fbranch.js.html#line38">line 38</a>
<a href="entities_fbranch.js.html">entities/fbranch.js</a>, <a href="entities_fbranch.js.html#line39">line 39</a>
</li></ul></dd>
@@ -886,7 +954,7 @@ parents.</div>
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_fbranch.js.html">entities/fbranch.js</a>, <a href="entities_fbranch.js.html#line43">line 43</a>
<a href="entities_fbranch.js.html">entities/fbranch.js</a>, <a href="entities_fbranch.js.html#line44">line 44</a>
</li></ul></dd>
@@ -988,7 +1056,7 @@ parents.</div>
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_fbranch.js.html">entities/fbranch.js</a>, <a href="entities_fbranch.js.html#line48">line 48</a>
<a href="entities_fbranch.js.html">entities/fbranch.js</a>, <a href="entities_fbranch.js.html#line49">line 49</a>
</li></ul></dd>

View File

@@ -264,7 +264,7 @@
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line54">line 54</a>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line55">line 55</a>
</li></ul></dd>
@@ -332,7 +332,7 @@
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line37">line 37</a>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line38">line 38</a>
</li></ul></dd>
@@ -400,7 +400,7 @@
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line51">line 51</a>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line52">line 52</a>
</li></ul></dd>
@@ -468,7 +468,75 @@
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line45">line 45</a>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line46">line 46</a>
</li></ul></dd>
</dl>
<h4 class="name" id="froca"><span class="type-signature"></span>froca<span class="type-signature"> :Froca</span></h4>
<h5>Type:</h5>
<ul>
<li>
<span class="param-type">Froca</span>
</li>
</ul>
<dl class="details">
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line35">line 35</a>
</li></ul></dd>
@@ -536,7 +604,7 @@
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line65">line 65</a>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line66">line 66</a>
</li></ul></dd>
@@ -608,7 +676,7 @@
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line75">line 75</a>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line76">line 76</a>
</li></ul></dd>
@@ -676,7 +744,7 @@
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line61">line 61</a>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line62">line 62</a>
</li></ul></dd>
@@ -744,7 +812,7 @@
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line48">line 48</a>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line49">line 49</a>
</li></ul></dd>
@@ -812,7 +880,7 @@
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line43">line 43</a>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line44">line 44</a>
</li></ul></dd>
@@ -880,7 +948,7 @@
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line40">line 40</a>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line41">line 41</a>
</li></ul></dd>
@@ -948,7 +1016,7 @@
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line63">line 63</a>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line64">line 64</a>
</li></ul></dd>
@@ -1020,7 +1088,7 @@
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line70">line 70</a>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line71">line 71</a>
</li></ul></dd>
@@ -1100,7 +1168,7 @@
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line353">line 353</a>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line354">line 354</a>
</li></ul></dd>
@@ -1206,7 +1274,7 @@
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line240">line 240</a>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line241">line 241</a>
</li></ul></dd>
@@ -1308,7 +1376,7 @@
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line231">line 231</a>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line232">line 232</a>
</li></ul></dd>
@@ -1482,7 +1550,7 @@
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line616">line 616</a>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line617">line 617</a>
</li></ul></dd>
@@ -1660,7 +1728,7 @@
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line638">line 638</a>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line639">line 639</a>
</li></ul></dd>
@@ -1860,7 +1928,7 @@
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line287">line 287</a>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line288">line 288</a>
</li></ul></dd>
@@ -2039,7 +2107,7 @@
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line409">line 409</a>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line410">line 410</a>
</li></ul></dd>
@@ -2218,7 +2286,7 @@
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line419">line 419</a>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line420">line 420</a>
</li></ul></dd>
@@ -2291,70 +2359,6 @@
<h5>Parameters:</h5>
<table class="params">
<thead>
<tr>
<th>Name</th>
<th>Type</th>
<th>Attributes</th>
<th>Default</th>
<th class="last">Description</th>
</tr>
</thead>
<tbody>
<tr>
<td class="name"><code>opts.preview</code></td>
<td class="type">
</td>
<td class="attributes">
&lt;optional><br>
</td>
<td class="default">
false
</td>
<td class="description last">retrieve only first 10 000 characters for a preview</td>
</tr>
</tbody>
</table>
@@ -2388,7 +2392,7 @@
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line866">line 866</a>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line864">line 864</a>
</li></ul></dd>
@@ -2492,7 +2496,7 @@
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line152">line 152</a>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line153">line 153</a>
</li></ul></dd>
@@ -2596,7 +2600,7 @@
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line169">line 169</a>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line170">line 170</a>
</li></ul></dd>
@@ -2698,7 +2702,7 @@
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line179">line 179</a>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line180">line 180</a>
</li></ul></dd>
@@ -2800,7 +2804,7 @@
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line221">line 221</a>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line222">line 222</a>
</li></ul></dd>
@@ -2902,7 +2906,7 @@
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line226">line 226</a>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line227">line 227</a>
</li></ul></dd>
@@ -3053,7 +3057,7 @@
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line692">line 692</a>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line693">line 693</a>
</li></ul></dd>
@@ -3208,7 +3212,7 @@
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line716">line 716</a>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line717">line 717</a>
</li></ul></dd>
@@ -3375,7 +3379,7 @@
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line502">line 502</a>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line503">line 503</a>
</li></ul></dd>
@@ -3483,7 +3487,7 @@
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line858">line 858</a>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line859">line 859</a>
</li></ul></dd>
@@ -3585,7 +3589,7 @@
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line758">line 758</a>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line759">line 759</a>
</li></ul></dd>
@@ -3759,7 +3763,7 @@
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line605">line 605</a>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line606">line 606</a>
</li></ul></dd>
@@ -3937,7 +3941,7 @@
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line627">line 627</a>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line628">line 628</a>
</li></ul></dd>
@@ -4137,7 +4141,7 @@
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line274">line 274</a>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line275">line 275</a>
</li></ul></dd>
@@ -4292,7 +4296,7 @@
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line686">line 686</a>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line687">line 687</a>
</li></ul></dd>
@@ -4447,7 +4451,7 @@
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line710">line 710</a>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line711">line 711</a>
</li></ul></dd>
@@ -4614,7 +4618,7 @@
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line494">line 494</a>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line495">line 495</a>
</li></ul></dd>
@@ -4769,7 +4773,7 @@
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line698">line 698</a>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line699">line 699</a>
</li></ul></dd>
@@ -4924,7 +4928,7 @@
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line722">line 722</a>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line723">line 723</a>
</li></ul></dd>
@@ -5091,7 +5095,7 @@
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line568">line 568</a>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line569">line 569</a>
</li></ul></dd>
@@ -5197,7 +5201,7 @@
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line144">line 144</a>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line145">line 145</a>
</li></ul></dd>
@@ -5299,7 +5303,7 @@
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line159">line 159</a>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line160">line 160</a>
</li></ul></dd>
@@ -5401,7 +5405,7 @@
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line187">line 187</a>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line188">line 188</a>
</li></ul></dd>
@@ -5503,7 +5507,7 @@
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line192">line 192</a>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line193">line 193</a>
</li></ul></dd>
@@ -5654,7 +5658,7 @@
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line704">line 704</a>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line705">line 705</a>
</li></ul></dd>
@@ -5809,7 +5813,7 @@
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line734">line 734</a>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line735">line 735</a>
</li></ul></dd>
@@ -5979,7 +5983,7 @@
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line744">line 744</a>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line745">line 745</a>
</li></ul></dd>
@@ -6130,7 +6134,7 @@
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line728">line 728</a>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line729">line 729</a>
</li></ul></dd>
@@ -6297,7 +6301,7 @@
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line576">line 576</a>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line577">line 577</a>
</li></ul></dd>
@@ -6403,7 +6407,7 @@
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line910">line 910</a>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line908">line 908</a>
</li></ul></dd>
@@ -6581,7 +6585,7 @@
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line375">line 375</a>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line376">line 376</a>
</li></ul></dd>
@@ -6687,7 +6691,7 @@
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line848">line 848</a>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line849">line 849</a>
</li></ul></dd>
@@ -6793,7 +6797,7 @@
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line838">line 838</a>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line839">line 839</a>
</li></ul></dd>
@@ -6967,7 +6971,7 @@
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line585">line 585</a>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line586">line 586</a>
</li></ul></dd>
@@ -7073,7 +7077,7 @@
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line174">line 174</a>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line175">line 175</a>
</li></ul></dd>
@@ -7224,7 +7228,7 @@
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line654">line 654</a>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line655">line 655</a>
</li></ul></dd>
@@ -7402,7 +7406,7 @@
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line596">line 596</a>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line597">line 597</a>
</li></ul></dd>
@@ -7557,7 +7561,7 @@
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line648">line 648</a>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line649">line 649</a>
</li></ul></dd>
@@ -7712,7 +7716,7 @@
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line674">line 674</a>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line675">line 675</a>
</li></ul></dd>
@@ -7867,7 +7871,7 @@
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line680">line 680</a>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line681">line 681</a>
</li></ul></dd>
@@ -7975,7 +7979,7 @@
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line831">line 831</a>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line832">line 832</a>
</li></ul></dd>
@@ -8059,7 +8063,7 @@
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line428">line 428</a>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line429">line 429</a>
</li></ul></dd>
@@ -8153,7 +8157,7 @@
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line905">line 905</a>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line903">line 903</a>
</li></ul></dd>
@@ -8259,7 +8263,7 @@
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line897">line 897</a>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line895">line 895</a>
</li></ul></dd>
@@ -8365,7 +8369,7 @@
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line117">line 117</a>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line118">line 118</a>
</li></ul></dd>
@@ -8516,7 +8520,7 @@
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line660">line 660</a>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line661">line 661</a>
</li></ul></dd>

View File

@@ -572,6 +572,66 @@ available in the JS frontend notes. You can use e.g. <code>api.showMessage(api.s
<h4 class="name" id="createNoteLink"><span class="type-signature"></span>createNoteLink<span class="type-signature"></span></h4>
<dl class="details">
<dt class="important tag-deprecated">Deprecated:</dt><dd><ul class="dummy"><li>- use api.createLink() instead</li></ul></dd>
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line308">line 308</a>
</li></ul></dd>
</dl>
<h4 class="name" id="currentNote"><span class="type-signature"></span>currentNote<span class="type-signature"></span></h4>
@@ -1759,7 +1819,7 @@ available in the JS frontend notes. You can use e.g. <code>api.showMessage(api.s
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line313">line 313</a>
<a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line316">line 316</a>
</li></ul></dd>
@@ -1967,7 +2027,7 @@ available in the JS frontend notes. You can use e.g. <code>api.showMessage(api.s
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line455">line 455</a>
<a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line458">line 458</a>
</li></ul></dd>
@@ -2639,7 +2699,7 @@ available in the JS frontend notes. You can use e.g. <code>api.showMessage(api.s
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line499">line 499</a>
<a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line502">line 502</a>
</li></ul></dd>
@@ -2794,7 +2854,7 @@ available in the JS frontend notes. You can use e.g. <code>api.showMessage(api.s
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line491">line 491</a>
<a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line494">line 494</a>
</li></ul></dd>
@@ -2904,7 +2964,7 @@ available in the JS frontend notes. You can use e.g. <code>api.showMessage(api.s
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line335">line 335</a>
<a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line338">line 338</a>
</li></ul></dd>
@@ -3010,7 +3070,7 @@ available in the JS frontend notes. You can use e.g. <code>api.showMessage(api.s
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line319">line 319</a>
<a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line322">line 322</a>
</li></ul></dd>
@@ -3116,7 +3176,7 @@ available in the JS frontend notes. You can use e.g. <code>api.showMessage(api.s
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line350">line 350</a>
<a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line353">line 353</a>
</li></ul></dd>
@@ -3226,7 +3286,7 @@ available in the JS frontend notes. You can use e.g. <code>api.showMessage(api.s
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line327">line 327</a>
<a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line330">line 330</a>
</li></ul></dd>
@@ -3337,7 +3397,7 @@ implementation of actual widget type.
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line344">line 344</a>
<a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line347">line 347</a>
</li></ul></dd>
@@ -3492,7 +3552,7 @@ implementation of actual widget type.
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line359">line 359</a>
<a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line362">line 362</a>
</li></ul></dd>
@@ -3647,7 +3707,7 @@ implementation of actual widget type.
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line403">line 403</a>
<a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line406">line 406</a>
</li></ul></dd>
@@ -3909,7 +3969,7 @@ if some action needs to happen on only one specific instance.
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line421">line 421</a>
<a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line424">line 424</a>
</li></ul></dd>
@@ -4372,7 +4432,7 @@ otherwise (by e.g. createLink())
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line394">line 394</a>
<a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line397">line 397</a>
</li></ul></dd>
@@ -4527,7 +4587,7 @@ otherwise (by e.g. createLink())
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line412">line 412</a>
<a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line415">line 415</a>
</li></ul></dd>
@@ -4682,7 +4742,7 @@ otherwise (by e.g. createLink())
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line430">line 430</a>
<a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line433">line 433</a>
</li></ul></dd>
@@ -4832,7 +4892,7 @@ otherwise (by e.g. createLink())
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line510">line 510</a>
<a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line513">line 513</a>
</li></ul></dd>
@@ -5517,7 +5577,7 @@ otherwise (by e.g. createLink())
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line374">line 374</a>
<a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line377">line 377</a>
</li></ul></dd>
@@ -5691,7 +5751,7 @@ otherwise (by e.g. createLink())
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line384">line 384</a>
<a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line387">line 387</a>
</li></ul></dd>
@@ -5846,7 +5906,7 @@ otherwise (by e.g. createLink())
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line484">line 484</a>
<a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line487">line 487</a>
</li></ul></dd>
@@ -6000,7 +6060,7 @@ otherwise (by e.g. createLink())
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line475">line 475</a>
<a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line478">line 478</a>
</li></ul></dd>
@@ -6787,7 +6847,7 @@ Internally this serializes the anonymous function into string and sends it to ba
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line439">line 439</a>
<a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line442">line 442</a>
</li></ul></dd>
@@ -6938,7 +6998,7 @@ Internally this serializes the anonymous function into string and sends it to ba
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line366">line 366</a>
<a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line369">line 369</a>
</li></ul></dd>
@@ -7642,7 +7702,7 @@ Typical use case is when a new note has been created, we should wait until it is
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line467">line 467</a>
<a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line470">line 470</a>
</li></ul></dd>

View File

@@ -34,6 +34,7 @@
*/
class FAttribute {
constructor(froca, row) {
/** @type {Froca} */
this.froca = froca;
this.update(row);

View File

@@ -32,6 +32,7 @@
*/
class FBranch {
constructor(froca, row) {
/** @type {Froca} */
this.froca = froca;
this.update(row);

View File

@@ -59,6 +59,7 @@ class FNote {
* @param {Object.&lt;string, Object>} row
*/
constructor(froca, row) {
/** @type {Froca} */
this.froca = froca;
/** @type {string[]} */
@@ -887,12 +888,9 @@ class FNote {
return this.getBlob();
}
/**
* @param [opts.preview=false] - retrieve only first 10 000 characters for a preview
* @return {Promise&lt;FBlob>}
*/
async getBlob(opts = {}) {
return await this.froca.getBlob('notes', this.noteId, opts);
/** @return {Promise&lt;FBlob>} */
async getBlob() {
return await this.froca.getBlob('notes', this.noteId);
}
toString() {

View File

@@ -332,6 +332,9 @@ function FrontendScriptApi(startNote, currentNote, originEntity = null, $contain
*/
this.createLink = linkService.createLink;
/** @deprecated - use api.createLink() instead */
this.createNoteLink = linkService.createLink;
/**
* Adds given text to the editor cursor
*

View File

@@ -5,8 +5,8 @@
}
/*
* CKEditor 5 (v38.0.1) content styles.
* Generated on Thu, 25 May 2023 13:25:51 GMT.
* CKEditor 5 (v39.0.1) content styles.
* Generated on Thu, 10 Aug 2023 09:21:07 GMT.
* For more information, check out https://ckeditor.com/docs/ckeditor5/latest/installation/advanced/content-styles.html
*/
@@ -15,8 +15,8 @@
--ck-color-image-caption-text: hsl(0, 0%, 20%);
--ck-color-mention-background: hsla(341, 100%, 30%, 0.1);
--ck-color-mention-text: hsl(341, 100%, 30%);
--ck-color-table-caption-background: hsl(0, 0%, 97%);
--ck-color-table-caption-text: hsl(0, 0%, 20%);
--ck-color-selector-caption-background: hsl(0, 0%, 97%);
--ck-color-selector-caption-text: hsl(0, 0%, 20%);
--ck-highlight-marker-blue: hsl(201, 97%, 72%);
--ck-highlight-marker-green: hsl(120, 93%, 68%);
--ck-highlight-marker-pink: hsl(345, 96%, 73%);
@@ -28,227 +28,107 @@
--ck-todo-list-checkmark-size: 16px;
}
/* @ckeditor/ckeditor5-block-quote/theme/blockquote.css */
.ck-content blockquote {
/* @ckeditor/ckeditor5-table/theme/tablecolumnresize.css */
.ck-content .table .ck-table-resized {
table-layout: fixed;
}
/* @ckeditor/ckeditor5-table/theme/tablecolumnresize.css */
.ck-content .table table {
overflow: hidden;
padding-right: 1.5em;
padding-left: 1.5em;
margin-left: 0;
margin-right: 0;
font-style: italic;
border-left: solid 5px hsl(0, 0%, 80%);
}
/* @ckeditor/ckeditor5-block-quote/theme/blockquote.css */
.ck-content[dir="rtl"] blockquote {
border-left: 0;
border-right: solid 5px hsl(0, 0%, 80%);
/* @ckeditor/ckeditor5-table/theme/tablecolumnresize.css */
.ck-content .table td,
.ck-content .table th {
overflow-wrap: break-word;
position: relative;
}
/* @ckeditor/ckeditor5-basic-styles/theme/code.css */
.ck-content code {
background-color: hsla(0, 0%, 78%, 0.3);
padding: .15em;
border-radius: 2px;
/* @ckeditor/ckeditor5-table/theme/table.css */
.ck-content .table {
margin: 0.9em auto;
display: table;
}
/* @ckeditor/ckeditor5-font/theme/fontsize.css */
.ck-content .text-tiny {
font-size: .7em;
/* @ckeditor/ckeditor5-table/theme/table.css */
.ck-content .table table {
border-collapse: collapse;
border-spacing: 0;
width: 100%;
height: 100%;
border: 1px double hsl(0, 0%, 70%);
}
/* @ckeditor/ckeditor5-font/theme/fontsize.css */
.ck-content .text-small {
font-size: .85em;
/* @ckeditor/ckeditor5-table/theme/table.css */
.ck-content .table table td,
.ck-content .table table th {
min-width: 2em;
padding: .4em;
border: 1px solid hsl(0, 0%, 75%);
}
/* @ckeditor/ckeditor5-font/theme/fontsize.css */
.ck-content .text-big {
font-size: 1.4em;
/* @ckeditor/ckeditor5-table/theme/table.css */
.ck-content .table table th {
font-weight: bold;
background: hsla(0, 0%, 0%, 5%);
}
/* @ckeditor/ckeditor5-font/theme/fontsize.css */
.ck-content .text-huge {
font-size: 1.8em;
/* @ckeditor/ckeditor5-table/theme/table.css */
.ck-content[dir="rtl"] .table th {
text-align: right;
}
/* @ckeditor/ckeditor5-highlight/theme/highlight.css */
.ck-content .marker-yellow {
background-color: var(--ck-highlight-marker-yellow);
/* @ckeditor/ckeditor5-table/theme/table.css */
.ck-content[dir="ltr"] .table th {
text-align: left;
}
/* @ckeditor/ckeditor5-highlight/theme/highlight.css */
.ck-content .marker-green {
background-color: var(--ck-highlight-marker-green);
}
/* @ckeditor/ckeditor5-highlight/theme/highlight.css */
.ck-content .marker-pink {
background-color: var(--ck-highlight-marker-pink);
}
/* @ckeditor/ckeditor5-highlight/theme/highlight.css */
.ck-content .marker-blue {
background-color: var(--ck-highlight-marker-blue);
}
/* @ckeditor/ckeditor5-highlight/theme/highlight.css */
.ck-content .pen-red {
color: var(--ck-highlight-pen-red);
background-color: transparent;
}
/* @ckeditor/ckeditor5-highlight/theme/highlight.css */
.ck-content .pen-green {
color: var(--ck-highlight-pen-green);
background-color: transparent;
}
/* @ckeditor/ckeditor5-image/theme/imagecaption.css */
.ck-content .image > figcaption {
/* @ckeditor/ckeditor5-table/theme/tablecaption.css */
.ck-content .table > figcaption {
display: table-caption;
caption-side: bottom;
caption-side: top;
word-break: break-word;
color: var(--ck-color-image-caption-text);
background-color: var(--ck-color-image-caption-background);
text-align: center;
color: var(--ck-color-selector-caption-text);
background-color: var(--ck-color-selector-caption-background);
padding: .6em;
font-size: .75em;
outline-offset: -1px;
}
/* @ckeditor/ckeditor5-image/theme/image.css */
.ck-content .image {
display: table;
/* @ckeditor/ckeditor5-page-break/theme/pagebreak.css */
.ck-content .page-break {
position: relative;
clear: both;
text-align: center;
margin: 0.9em auto;
min-width: 50px;
}
/* @ckeditor/ckeditor5-image/theme/image.css */
.ck-content .image img {
display: block;
margin: 0 auto;
max-width: 100%;
min-width: 100%;
}
/* @ckeditor/ckeditor5-image/theme/image.css */
.ck-content .image-inline {
/*
* Normally, the .image-inline would have "display: inline-block" and "img { width: 100% }" (to follow the wrapper while resizing).;
* Unfortunately, together with "srcset", it gets automatically stretched up to the width of the editing root.
* This strange behavior does not happen with inline-flex.
*/
display: inline-flex;
max-width: 100%;
align-items: flex-start;
}
/* @ckeditor/ckeditor5-image/theme/image.css */
.ck-content .image-inline picture {
padding: 5px 0;
display: flex;
align-items: center;
justify-content: center;
}
/* @ckeditor/ckeditor5-image/theme/image.css */
.ck-content .image-inline picture,
.ck-content .image-inline img {
flex-grow: 1;
flex-shrink: 1;
max-width: 100%;
}
/* @ckeditor/ckeditor5-image/theme/imageresize.css */
.ck-content .image.image_resized {
max-width: 100%;
display: block;
box-sizing: border-box;
}
/* @ckeditor/ckeditor5-image/theme/imageresize.css */
.ck-content .image.image_resized img {
/* @ckeditor/ckeditor5-page-break/theme/pagebreak.css */
.ck-content .page-break::after {
content: '';
position: absolute;
border-bottom: 2px dashed hsl(0, 0%, 77%);
width: 100%;
}
/* @ckeditor/ckeditor5-image/theme/imageresize.css */
.ck-content .image.image_resized > figcaption {
/* @ckeditor/ckeditor5-page-break/theme/pagebreak.css */
.ck-content .page-break__label {
position: relative;
z-index: 1;
padding: .3em .6em;
display: block;
text-transform: uppercase;
border: 1px solid hsl(0, 0%, 77%);
border-radius: 2px;
font-family: Helvetica, Arial, Tahoma, Verdana, Sans-Serif;
font-size: 0.75em;
font-weight: bold;
color: hsl(0, 0%, 20%);
background: hsl(0, 0%, 100%);
box-shadow: 2px 2px 1px hsla(0, 0%, 0%, 0.15);
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
/* @ckeditor/ckeditor5-image/theme/imagestyle.css */
.ck-content .image-style-block-align-left,
.ck-content .image-style-block-align-right {
max-width: calc(100% - var(--ck-image-style-spacing));
}
/* @ckeditor/ckeditor5-image/theme/imagestyle.css */
.ck-content .image-style-align-left,
.ck-content .image-style-align-right {
clear: none;
}
/* @ckeditor/ckeditor5-image/theme/imagestyle.css */
.ck-content .image-style-side {
float: right;
margin-left: var(--ck-image-style-spacing);
max-width: 50%;
}
/* @ckeditor/ckeditor5-image/theme/imagestyle.css */
.ck-content .image-style-align-left {
float: left;
margin-right: var(--ck-image-style-spacing);
}
/* @ckeditor/ckeditor5-image/theme/imagestyle.css */
.ck-content .image-style-align-center {
margin-left: auto;
margin-right: auto;
}
/* @ckeditor/ckeditor5-image/theme/imagestyle.css */
.ck-content .image-style-align-right {
float: right;
margin-left: var(--ck-image-style-spacing);
}
/* @ckeditor/ckeditor5-image/theme/imagestyle.css */
.ck-content .image-style-block-align-right {
margin-right: 0;
margin-left: auto;
}
/* @ckeditor/ckeditor5-image/theme/imagestyle.css */
.ck-content .image-style-block-align-left {
margin-left: 0;
margin-right: auto;
}
/* @ckeditor/ckeditor5-image/theme/imagestyle.css */
.ck-content p + .image-style-align-left,
.ck-content p + .image-style-align-right,
.ck-content p + .image-style-side {
margin-top: 0;
}
/* @ckeditor/ckeditor5-image/theme/imagestyle.css */
.ck-content .image-inline.image-style-align-left,
.ck-content .image-inline.image-style-align-right {
margin-top: var(--ck-inline-image-style-spacing);
margin-bottom: var(--ck-inline-image-style-spacing);
}
/* @ckeditor/ckeditor5-image/theme/imagestyle.css */
.ck-content .image-inline.image-style-align-left {
margin-right: var(--ck-inline-image-style-spacing);
}
/* @ckeditor/ckeditor5-image/theme/imagestyle.css */
.ck-content .image-inline.image-style-align-right {
margin-left: var(--ck-inline-image-style-spacing);
}
/* @ckeditor/ckeditor5-list/theme/list.css */
.ck-content ol {
list-style-type: decimal;
}
/* @ckeditor/ckeditor5-list/theme/list.css */
.ck-content ol ol {
list-style-type: lower-latin;
}
/* @ckeditor/ckeditor5-list/theme/list.css */
.ck-content ol ol ol {
list-style-type: lower-roman;
}
/* @ckeditor/ckeditor5-list/theme/list.css */
.ck-content ol ol ol ol {
list-style-type: upper-latin;
}
/* @ckeditor/ckeditor5-list/theme/list.css */
.ck-content ol ol ol ol ol {
list-style-type: upper-roman;
}
/* @ckeditor/ckeditor5-list/theme/list.css */
.ck-content ul {
list-style-type: disc;
}
/* @ckeditor/ckeditor5-list/theme/list.css */
.ck-content ul ul {
list-style-type: circle;
}
/* @ckeditor/ckeditor5-list/theme/list.css */
.ck-content ul ul ul {
list-style-type: square;
}
/* @ckeditor/ckeditor5-list/theme/list.css */
.ck-content ul ul ul ul {
list-style-type: square;
/* @ckeditor/ckeditor5-media-embed/theme/mediaembed.css */
.ck-content .media {
clear: both;
margin: 0.9em 0;
display: block;
min-width: 15em;
}
/* @ckeditor/ckeditor5-list/theme/todolist.css */
.ck-content .todo-list {
@@ -317,107 +197,240 @@
.ck-content .todo-list .todo-list__label .todo-list__label__description {
vertical-align: middle;
}
/* @ckeditor/ckeditor5-media-embed/theme/mediaembed.css */
.ck-content .media {
clear: both;
margin: 0.9em 0;
display: block;
min-width: 15em;
}
/* @ckeditor/ckeditor5-page-break/theme/pagebreak.css */
.ck-content .page-break {
position: relative;
clear: both;
padding: 5px 0;
display: flex;
align-items: center;
justify-content: center;
}
/* @ckeditor/ckeditor5-page-break/theme/pagebreak.css */
.ck-content .page-break::after {
content: '';
position: absolute;
border-bottom: 2px dashed hsl(0, 0%, 77%);
width: 100%;
}
/* @ckeditor/ckeditor5-page-break/theme/pagebreak.css */
.ck-content .page-break__label {
position: relative;
z-index: 1;
padding: .3em .6em;
display: block;
text-transform: uppercase;
border: 1px solid hsl(0, 0%, 77%);
border-radius: 2px;
font-family: Helvetica, Arial, Tahoma, Verdana, Sans-Serif;
font-size: 0.75em;
font-weight: bold;
color: hsl(0, 0%, 20%);
background: hsl(0, 0%, 100%);
box-shadow: 2px 2px 1px hsla(0, 0%, 0%, 0.15);
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
/* @ckeditor/ckeditor5-table/theme/tablecolumnresize.css */
.ck-content .table .ck-table-resized {
table-layout: fixed;
}
/* @ckeditor/ckeditor5-table/theme/tablecolumnresize.css */
.ck-content .table table {
overflow: hidden;
}
/* @ckeditor/ckeditor5-table/theme/tablecolumnresize.css */
.ck-content .table td,
.ck-content .table th {
position: relative;
}
/* @ckeditor/ckeditor5-table/theme/table.css */
.ck-content .table {
margin: 0.9em auto;
/* @ckeditor/ckeditor5-image/theme/image.css */
.ck-content .image {
display: table;
}
/* @ckeditor/ckeditor5-table/theme/table.css */
.ck-content .table table {
border-collapse: collapse;
border-spacing: 0;
width: 100%;
height: 100%;
border: 1px double hsl(0, 0%, 70%);
}
/* @ckeditor/ckeditor5-table/theme/table.css */
.ck-content .table table td,
.ck-content .table table th {
min-width: 2em;
padding: .4em;
border: 1px solid hsl(0, 0%, 75%);
}
/* @ckeditor/ckeditor5-table/theme/table.css */
.ck-content .table table th {
font-weight: bold;
background: hsla(0, 0%, 0%, 5%);
}
/* @ckeditor/ckeditor5-table/theme/table.css */
.ck-content[dir="rtl"] .table th {
text-align: right;
}
/* @ckeditor/ckeditor5-table/theme/table.css */
.ck-content[dir="ltr"] .table th {
text-align: left;
}
/* @ckeditor/ckeditor5-table/theme/tablecaption.css */
.ck-content .table > figcaption {
display: table-caption;
caption-side: top;
word-break: break-word;
clear: both;
text-align: center;
color: var(--ck-color-table-caption-text);
background-color: var(--ck-color-table-caption-background);
margin: 0.9em auto;
min-width: 50px;
}
/* @ckeditor/ckeditor5-image/theme/image.css */
.ck-content .image img {
display: block;
margin: 0 auto;
max-width: 100%;
min-width: 100%;
}
/* @ckeditor/ckeditor5-image/theme/image.css */
.ck-content .image-inline {
/*
* Normally, the .image-inline would have "display: inline-block" and "img { width: 100% }" (to follow the wrapper while resizing).;
* Unfortunately, together with "srcset", it gets automatically stretched up to the width of the editing root.
* This strange behavior does not happen with inline-flex.
*/
display: inline-flex;
max-width: 100%;
align-items: flex-start;
}
/* @ckeditor/ckeditor5-image/theme/image.css */
.ck-content .image-inline picture {
display: flex;
}
/* @ckeditor/ckeditor5-image/theme/image.css */
.ck-content .image-inline picture,
.ck-content .image-inline img {
flex-grow: 1;
flex-shrink: 1;
max-width: 100%;
}
/* @ckeditor/ckeditor5-image/theme/imageresize.css */
.ck-content .image.image_resized {
max-width: 100%;
display: block;
box-sizing: border-box;
}
/* @ckeditor/ckeditor5-image/theme/imageresize.css */
.ck-content .image.image_resized img {
width: 100%;
}
/* @ckeditor/ckeditor5-image/theme/imageresize.css */
.ck-content .image.image_resized > figcaption {
display: block;
}
/* @ckeditor/ckeditor5-image/theme/imagecaption.css */
.ck-content .image > figcaption {
display: table-caption;
caption-side: bottom;
word-break: break-word;
color: var(--ck-color-image-caption-text);
background-color: var(--ck-color-image-caption-background);
padding: .6em;
font-size: .75em;
outline-offset: -1px;
}
/* @ckeditor/ckeditor5-highlight/theme/highlight.css */
.ck-content .marker-yellow {
background-color: var(--ck-highlight-marker-yellow);
}
/* @ckeditor/ckeditor5-highlight/theme/highlight.css */
.ck-content .marker-green {
background-color: var(--ck-highlight-marker-green);
}
/* @ckeditor/ckeditor5-highlight/theme/highlight.css */
.ck-content .marker-pink {
background-color: var(--ck-highlight-marker-pink);
}
/* @ckeditor/ckeditor5-highlight/theme/highlight.css */
.ck-content .marker-blue {
background-color: var(--ck-highlight-marker-blue);
}
/* @ckeditor/ckeditor5-highlight/theme/highlight.css */
.ck-content .pen-red {
color: var(--ck-highlight-pen-red);
background-color: transparent;
}
/* @ckeditor/ckeditor5-highlight/theme/highlight.css */
.ck-content .pen-green {
color: var(--ck-highlight-pen-green);
background-color: transparent;
}
/* @ckeditor/ckeditor5-list/theme/list.css */
.ck-content ol {
list-style-type: decimal;
}
/* @ckeditor/ckeditor5-list/theme/list.css */
.ck-content ol ol {
list-style-type: lower-latin;
}
/* @ckeditor/ckeditor5-list/theme/list.css */
.ck-content ol ol ol {
list-style-type: lower-roman;
}
/* @ckeditor/ckeditor5-list/theme/list.css */
.ck-content ol ol ol ol {
list-style-type: upper-latin;
}
/* @ckeditor/ckeditor5-list/theme/list.css */
.ck-content ol ol ol ol ol {
list-style-type: upper-roman;
}
/* @ckeditor/ckeditor5-list/theme/list.css */
.ck-content ul {
list-style-type: disc;
}
/* @ckeditor/ckeditor5-list/theme/list.css */
.ck-content ul ul {
list-style-type: circle;
}
/* @ckeditor/ckeditor5-list/theme/list.css */
.ck-content ul ul ul {
list-style-type: square;
}
/* @ckeditor/ckeditor5-list/theme/list.css */
.ck-content ul ul ul ul {
list-style-type: square;
}
/* @ckeditor/ckeditor5-image/theme/imagestyle.css */
.ck-content .image-style-block-align-left,
.ck-content .image-style-block-align-right {
max-width: calc(100% - var(--ck-image-style-spacing));
}
/* @ckeditor/ckeditor5-image/theme/imagestyle.css */
.ck-content .image-style-align-left,
.ck-content .image-style-align-right {
clear: none;
}
/* @ckeditor/ckeditor5-image/theme/imagestyle.css */
.ck-content .image-style-side {
float: right;
margin-left: var(--ck-image-style-spacing);
max-width: 50%;
}
/* @ckeditor/ckeditor5-image/theme/imagestyle.css */
.ck-content .image-style-align-left {
float: left;
margin-right: var(--ck-image-style-spacing);
}
/* @ckeditor/ckeditor5-image/theme/imagestyle.css */
.ck-content .image-style-align-center {
margin-left: auto;
margin-right: auto;
}
/* @ckeditor/ckeditor5-image/theme/imagestyle.css */
.ck-content .image-style-align-right {
float: right;
margin-left: var(--ck-image-style-spacing);
}
/* @ckeditor/ckeditor5-image/theme/imagestyle.css */
.ck-content .image-style-block-align-right {
margin-right: 0;
margin-left: auto;
}
/* @ckeditor/ckeditor5-image/theme/imagestyle.css */
.ck-content .image-style-block-align-left {
margin-left: 0;
margin-right: auto;
}
/* @ckeditor/ckeditor5-image/theme/imagestyle.css */
.ck-content p + .image-style-align-left,
.ck-content p + .image-style-align-right,
.ck-content p + .image-style-side {
margin-top: 0;
}
/* @ckeditor/ckeditor5-image/theme/imagestyle.css */
.ck-content .image-inline.image-style-align-left,
.ck-content .image-inline.image-style-align-right {
margin-top: var(--ck-inline-image-style-spacing);
margin-bottom: var(--ck-inline-image-style-spacing);
}
/* @ckeditor/ckeditor5-image/theme/imagestyle.css */
.ck-content .image-inline.image-style-align-left {
margin-right: var(--ck-inline-image-style-spacing);
}
/* @ckeditor/ckeditor5-image/theme/imagestyle.css */
.ck-content .image-inline.image-style-align-right {
margin-left: var(--ck-inline-image-style-spacing);
}
/* @ckeditor/ckeditor5-block-quote/theme/blockquote.css */
.ck-content blockquote {
overflow: hidden;
padding-right: 1.5em;
padding-left: 1.5em;
margin-left: 0;
margin-right: 0;
font-style: italic;
border-left: solid 5px hsl(0, 0%, 80%);
}
/* @ckeditor/ckeditor5-block-quote/theme/blockquote.css */
.ck-content[dir="rtl"] blockquote {
border-left: 0;
border-right: solid 5px hsl(0, 0%, 80%);
}
/* @ckeditor/ckeditor5-basic-styles/theme/code.css */
.ck-content code {
background-color: hsla(0, 0%, 78%, 0.3);
padding: .15em;
border-radius: 2px;
}
/* @ckeditor/ckeditor5-font/theme/fontsize.css */
.ck-content .text-tiny {
font-size: .7em;
}
/* @ckeditor/ckeditor5-font/theme/fontsize.css */
.ck-content .text-small {
font-size: .85em;
}
/* @ckeditor/ckeditor5-font/theme/fontsize.css */
.ck-content .text-big {
font-size: 1.4em;
}
/* @ckeditor/ckeditor5-font/theme/fontsize.css */
.ck-content .text-huge {
font-size: 1.8em;
}
/* @ckeditor/ckeditor5-mention/theme/mention.css */
.ck-content .mention {
background: var(--ck-color-mention-background);
color: var(--ck-color-mention-text);
}
/* @ckeditor/ckeditor5-horizontal-line/theme/horizontalline.css */
.ck-content hr {
margin: 15px 0;
height: 4px;
background: hsl(0, 0%, 87%);
border: 0;
}
/* @ckeditor/ckeditor5-code-block/theme/codeblock.css */
.ck-content pre {
padding: 1em;
@@ -438,18 +451,6 @@
padding: 0;
border-radius: 0;
}
/* @ckeditor/ckeditor5-horizontal-line/theme/horizontalline.css */
.ck-content hr {
margin: 15px 0;
height: 4px;
background: hsl(0, 0%, 87%);
border: 0;
}
/* @ckeditor/ckeditor5-mention/theme/mention.css */
.ck-content .mention {
background: var(--ck-color-mention-background);
color: var(--ck-color-mention-text);
}
@media print {
/* @ckeditor/ckeditor5-page-break/theme/pagebreak.css */
.ck-content .page-break {

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

2041
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -2,7 +2,7 @@
"name": "trilium",
"productName": "Trilium Notes",
"description": "Trilium Notes",
"version": "0.61.1-beta",
"version": "0.61.5-beta",
"license": "AGPL-3.0-only",
"main": "electron.js",
"bin": {
@@ -31,9 +31,9 @@
"prepare": "husky install || echo 'Husky install failed, expected on flatpak build'"
},
"dependencies": {
"@braintree/sanitize-url": "6.0.2",
"@braintree/sanitize-url": "6.0.4",
"@electron/remote": "2.0.10",
"@excalidraw/excalidraw": "0.15.2",
"@excalidraw/excalidraw": "0.15.3",
"archiver": "5.3.1",
"async-mutex": "0.4.0",
"axios": "1.4.0",
@@ -53,7 +53,7 @@
"escape-html": "1.0.3",
"express": "4.18.2",
"express-partial-content": "1.0.2",
"express-rate-limit": "6.8.0",
"express-rate-limit": "6.9.0",
"express-session": "1.17.3",
"fs-extra": "11.1.1",
"helmet": "7.0.0",
@@ -65,13 +65,13 @@
"ini": "3.0.1",
"is-animated": "2.0.2",
"is-svg": "4.3.2",
"jimp": "0.22.8",
"jimp": "0.22.10",
"joplin-turndown-plugin-gfm": "1.0.12",
"jsdom": "22.1.0",
"marked": "5.1.1",
"marked": "7.0.3",
"mime-types": "2.1.35",
"multer": "1.4.5-lts.1",
"node-abi": "3.45.0",
"node-abi": "3.46.0",
"normalize-strings": "1.1.1",
"open": "8.4.1",
"rand-token": "1.0.1",
@@ -92,31 +92,31 @@
"turndown": "7.1.2",
"unescape": "1.0.1",
"ws": "8.13.0",
"xml2js": "0.6.0",
"xml2js": "0.6.2",
"yauzl": "2.10.0"
},
"devDependencies": {
"cross-env": "7.0.3",
"electron": "25.3.1",
"electron-builder": "24.4.0",
"electron": "25.5.0",
"electron-builder": "24.6.3",
"electron-packager": "17.1.1",
"electron-rebuild": "3.2.9",
"eslint": "8.45.0",
"eslint": "8.47.0",
"eslint-config-airbnb-base": "15.0.0",
"eslint-config-prettier": "8.8.0",
"eslint-plugin-import": "2.27.5",
"eslint-config-prettier": "9.0.0",
"eslint-plugin-import": "2.28.0",
"eslint-plugin-jsonc": "2.9.0",
"eslint-plugin-prettier": "5.0.0",
"esm": "3.2.25",
"husky": "8.0.3",
"jasmine": "5.0.2",
"jasmine": "5.1.0",
"jsdoc": "4.0.2",
"jsonc-eslint-parser": "2.3.0",
"lint-staged": "13.2.3",
"lint-staged": "14.0.0",
"lorem-ipsum": "2.0.8",
"nodemon": "3.0.1",
"prettier": "3.0.0",
"rcedit": "3.0.1",
"prettier": "3.0.2",
"rcedit": "3.1.0",
"webpack": "5.88.2",
"webpack-cli": "5.1.4"
},

View File

@@ -61,8 +61,8 @@ class AbstractBeccaEntity {
}
/** @protected */
addEntityChange(isDeleted = false) {
entityChangesService.addEntityChange({
putEntityChange(isDeleted = false) {
entityChangesService.putEntityChange({
entityName: this.constructor.entityName,
entityId: this[this.constructor.primaryKeyName],
hash: this.generateHash(isDeleted),
@@ -101,7 +101,7 @@ class AbstractBeccaEntity {
return;
}
this.addEntityChange(false);
this.putEntityChange(false);
if (!cls.isEntityEventsDisabled()) {
const eventPayload = {
@@ -132,8 +132,7 @@ class AbstractBeccaEntity {
if (this.hasStringContent()) {
content = content.toString();
}
else {
} else {
content = Buffer.isBuffer(content) ? content : Buffer.from(content);
}
@@ -156,32 +155,34 @@ class AbstractBeccaEntity {
this.save();
if (newBlobId !== oldBlobId) {
this.#deleteBlobIfNoteUsed(oldBlobId);
this.#deleteBlobIfNotUsed(oldBlobId);
}
}
});
}
#deleteBlobIfNoteUsed(blobId) {
if (sql.getValue("SELECT 1 FROM notes WHERE blobId = ? LIMIT 1", [blobId])) {
#deleteBlobIfNotUsed(oldBlobId) {
if (sql.getValue("SELECT 1 FROM notes WHERE blobId = ? LIMIT 1", [oldBlobId])) {
return;
}
if (sql.getValue("SELECT 1 FROM attachments WHERE blobId = ? LIMIT 1", [blobId])) {
if (sql.getValue("SELECT 1 FROM attachments WHERE blobId = ? LIMIT 1", [oldBlobId])) {
return;
}
if (sql.getValue("SELECT 1 FROM revisions WHERE blobId = ? LIMIT 1", [blobId])) {
if (sql.getValue("SELECT 1 FROM revisions WHERE blobId = ? LIMIT 1", [oldBlobId])) {
return;
}
sql.execute("DELETE FROM blobs WHERE blobId = ?", [blobId]);
sql.execute("DELETE FROM entity_changes WHERE entityName = 'blobs' AND entityId = ?", [blobId]);
sql.execute("DELETE FROM blobs WHERE blobId = ?", [oldBlobId]);
// blobs are not marked as erased in entity_changes, they are just purged completely
// this is because technically every keystroke can create a new blob and there would be just too many
sql.execute("DELETE FROM entity_changes WHERE entityName = 'blobs' AND entityId = ?", [oldBlobId]);
}
#getUnencryptedContentForHashCalculation(unencryptedContent) {
if (this.isProtected) {
// a "random" prefix make sure that the calculated hash/blobId is different for an encrypted note and decrypted
// a "random" prefix makes sure that the calculated hash/blobId is different for a decrypted/encrypted content
const encryptedPrefixSuffix = "t$[nvQg7q)&_ENCRYPTED_?M:Bf&j3jr_";
return Buffer.isBuffer(unencryptedContent)
? Buffer.concat([Buffer.from(encryptedPrefixSuffix), unencryptedContent])
@@ -196,7 +197,7 @@ class AbstractBeccaEntity {
* We're using the unencrypted blob for the hash calculation, because otherwise the random IV would
* cause every content blob to be unique which would balloon the database size (esp. with revisioning).
* This has minor security implications (it's easy to infer that given content is shared between different
* notes/attachments, but the trade-off comes out clearly positive).
* notes/attachments), but the trade-off comes out clearly positive.
*/
const newBlobId = utils.hashedBlobId(unencryptedContentForHashCalculation);
const blobNeedsInsert = !sql.getValue('SELECT 1 FROM blobs WHERE blobId = ?', [newBlobId]);
@@ -214,9 +215,11 @@ class AbstractBeccaEntity {
sql.upsert("blobs", "blobId", pojo);
const hash = utils.hash(`${newBlobId}|${pojo.content.toString()}`);
// we can't reuse blobId as an entity_changes hash, because this one has to be calculatable without having
// access to the decrypted content
const hash = blobService.calculateContentHash(pojo);
entityChangesService.addEntityChange({
entityChangesService.putEntityChange({
entityName: 'blobs',
entityId: newBlobId,
hash: hash,
@@ -276,7 +279,7 @@ class AbstractBeccaEntity {
log.info(`Marking ${entityName} ${entityId} as deleted`);
this.addEntityChange(true);
this.putEntityChange(true);
eventService.emit(eventService.ENTITY_DELETED, { entityName, entityId, entity: this });
}
@@ -293,7 +296,7 @@ class AbstractBeccaEntity {
log.info(`Marking ${entityName} ${entityId} as deleted`);
this.addEntityChange(true);
this.putEntityChange(true);
eventService.emit(eventService.ENTITY_DELETED, { entityName, entityId, entity: this });
}

View File

@@ -75,7 +75,7 @@ function register(router) {
eu.route(router, 'post' ,'/etapi/refresh-note-ordering/:parentNoteId', (req, res, next) => {
eu.getAndCheckNote(req.params.parentNoteId);
entityChangesService.addNoteReorderingEntityChange(req.params.parentNoteId, "etapi");
entityChangesService.putNoteReorderingEntityChange(req.params.parentNoteId, "etapi");
res.sendStatus(204);
});

View File

@@ -182,8 +182,6 @@ export default class Entrypoints extends Component {
}
hideAllPopups() {
$(".tooltip").removeClass("show");
if (utils.isDesktop()) {
$(".aa-input").autocomplete("close");
}

View File

@@ -1,5 +1,6 @@
class FAttachment {
constructor(froca, row) {
/** @type {Froca} */
this.froca = froca;
this.update(row);
@@ -34,12 +35,9 @@ class FAttachment {
return this.froca.notes[this.ownerId];
}
/**
* @param [opts.preview=false] - retrieve only first 10 000 characters for a preview
* @return {FBlob}
*/
async getBlob(opts = {}) {
return await this.froca.getBlob('attachments', this.attachmentId, opts);
/** @return {FBlob} */
async getBlob() {
return await this.froca.getBlob('attachments', this.attachmentId);
}
}

View File

@@ -6,6 +6,7 @@ import promotedAttributeDefinitionParser from '../services/promoted_attribute_de
*/
class FAttribute {
constructor(froca, row) {
/** @type {Froca} */
this.froca = froca;
this.update(row);

View File

@@ -4,6 +4,7 @@
*/
class FBranch {
constructor(froca, row) {
/** @type {Froca} */
this.froca = froca;
this.update(row);

View File

@@ -31,6 +31,7 @@ class FNote {
* @param {Object.<string, Object>} row
*/
constructor(froca, row) {
/** @type {Froca} */
this.froca = froca;
/** @type {string[]} */
@@ -859,12 +860,9 @@ class FNote {
return this.getBlob();
}
/**
* @param [opts.preview=false] - retrieve only first 10 000 characters for a preview
* @return {Promise<FBlob>}
*/
async getBlob(opts = {}) {
return await this.froca.getBlob('notes', this.noteId, opts);
/** @return {Promise<FBlob>} */
async getBlob() {
return await this.froca.getBlob('notes', this.noteId);
}
toString() {

View File

@@ -21,6 +21,7 @@ import NoteListWidget from "../widgets/note_list.js";
import GlobalMenuWidget from "../widgets/buttons/global_menu.js";
import LauncherContainer from "../widgets/containers/launcher_container.js";
import RootContainer from "../widgets/containers/root_container.js";
import SharedInfoWidget from "../widgets/shared_info.js";
const MOBILE_CSS = `
<style>
@@ -144,6 +145,7 @@ export default class MobileLayout {
.css("top: 5px;")
)
.child(new CloseDetailButtonWidget().contentSized()))
.child(new SharedInfoWidget())
.child(new FloatingButtons()
.child(new EditButton())
.child(new RelationMapButtons())

View File

@@ -67,7 +67,6 @@ export default class TreeContextMenu {
{ title: "Advanced", uiIcon: "bx bx-empty", enabled: true, items: [
{ title: 'Expand subtree <kbd data-command="expandSubtree"></kbd>', command: "expandSubtree", uiIcon: "bx bx-expand", enabled: noSelectedNotes },
{ title: 'Collapse subtree <kbd data-command="collapseSubtree"></kbd>', command: "collapseSubtree", uiIcon: "bx bx-collapse", enabled: noSelectedNotes },
{ title: "Force note sync", command: "forceNoteSync", uiIcon: "bx bx-refresh", enabled: noSelectedNotes },
{ title: 'Sort by ... <kbd data-command="sortChildNotes"></kbd>', command: "sortChildNotes", uiIcon: "bx bx-empty", enabled: noSelectedNotes && notSearch },
{ title: 'Recent changes in subtree', command: "recentChangesInSubtree", uiIcon: "bx bx-history", enabled: noSelectedNotes },
{ title: 'Convert to attachment', command: "convertNoteToAttachment", uiIcon: "bx bx-empty", enabled: isNotRoot && !isHoisted }

View File

@@ -19,7 +19,6 @@ let idCounter = 1;
*/
async function getRenderedContent(entity, options = {}) {
options = Object.assign({
trim: false,
tooltip: false
}, options);
@@ -29,10 +28,10 @@ async function getRenderedContent(entity, options = {}) {
const $renderedContent = $('<div class="rendered-content">');
if (type === 'text') {
await renderText(entity, options, $renderedContent);
await renderText(entity, $renderedContent);
}
else if (type === 'code') {
await renderCode(entity, options, $renderedContent);
await renderCode(entity, $renderedContent);
}
else if (type === 'image') {
renderImage(entity, $renderedContent, options);
@@ -86,12 +85,13 @@ async function getRenderedContent(entity, options = {}) {
};
}
async function renderText(note, options, $renderedContent) {
/** @param {FNote} note */
async function renderText(note, $renderedContent) {
// entity must be FNote
const blob = await note.getBlob({preview: options.trim});
const blob = await note.getBlob();
if (!utils.isHtmlEmpty(blob.content)) {
$renderedContent.append($('<div class="ck-content">').html(trim(blob.content, options.trim)));
$renderedContent.append($('<div class="ck-content">').html(blob.content));
if ($renderedContent.find('span.math-tex').length > 0) {
await libraryLoader.requireLibrary(libraryLoader.KATEX);
@@ -112,10 +112,11 @@ async function renderText(note, options, $renderedContent) {
}
}
async function renderCode(note, options, $renderedContent) {
const blob = await note.getBlob({preview: options.trim});
/** @param {FNote} note */
async function renderCode(note, $renderedContent) {
const blob = await note.getBlob();
$renderedContent.append($("<pre>").text(trim(blob.content, options.trim)));
$renderedContent.append($("<pre>").text(blob.content));
}
function renderImage(entity, $renderedContent, options = {}) {
@@ -144,8 +145,8 @@ function renderImage(entity, $renderedContent, options = {}) {
if (options.imageHasZoom) {
libraryLoader.requireLibrary(libraryLoader.WHEEL_ZOOM).then(() => {
WZoom.create(`#${$img.attr("id")}`, {
maxScale: 10,
speed: 20,
maxScale: 50,
speed: 1.3,
zoomOnClick: false
});
});
@@ -285,15 +286,6 @@ async function renderChildrenList($renderedContent, note) {
}
}
function trim(text, doTrim) {
if (!doTrim) {
return text;
}
else {
return text.substr(0, Math.min(text.length, 2000));
}
}
function getRenderingType(entity) {
let type = entity.type || entity.role;
const mime = entity.mime;

View File

@@ -368,12 +368,11 @@ class Froca {
}
/** @returns {Promise<FBlob>} */
async getBlob(entityType, entityId, opts = {}) {
opts.preview = !!opts.preview;
const key = `${entityType}-${entityId}-${opts.preview}`;
async getBlob(entityType, entityId) {
const key = `${entityType}-${entityId}`;
if (!this.blobPromises[key]) {
this.blobPromises[key] = server.get(`${entityType}/${entityId}/blob?preview=${opts.preview}`)
this.blobPromises[key] = server.get(`${entityType}/${entityId}/blob`)
.then(row => new FBlob(row))
.catch(e => console.error(`Cannot get blob for ${entityType} '${entityId}'`));

View File

@@ -148,6 +148,11 @@ function parseNavigationStateFromUrl(url) {
const hash = url.substr(hashIdx + 1); // strip also the initial '#'
const [notePath, paramString] = hash.split("?");
if (!notePath.match(/^[_a-z0-9]{4,}(\/[_a-z0-9]{4,})*$/i)) {
return {};
}
const viewScope = {
viewMode: 'default'
};

View File

@@ -259,8 +259,6 @@ function init() {
};
$.fn.setSelectedExternalLink = function (externalLink) {
console.trace("setSelectedExternalLink");
if (externalLink) {
$(this)
.closest(".input-group")

View File

@@ -8,27 +8,32 @@ import appContext from "../components/app_context.js";
function setupGlobalTooltip() {
$(document).on("mouseenter", "a", mouseEnterHandler);
$(document).on("mouseleave", "a", mouseLeaveHandler);
// close any note tooltip after click, this fixes the problem that sometimes tooltips remained on the screen
$(document).on("click", () => $('.note-tooltip').remove());
$(document).on("click", e => {
if ($(e.target).closest(".note-tooltip").length) {
// click within the tooltip shouldn't close it
return;
}
$('.note-tooltip').remove();
});
}
function setupElementTooltip($el) {
$el.on('mouseenter', mouseEnterHandler);
$el.on('mouseleave', mouseLeaveHandler);
}
async function mouseEnterHandler() {
const $link = $(this);
if ($link.hasClass("no-tooltip-preview")
|| $link.hasClass("disabled")) {
if ($link.hasClass("no-tooltip-preview") || $link.hasClass("disabled")) {
return;
}
// this is to avoid showing tooltip from inside the CKEditor link editor dialog
if ($link.closest(".ck-link-actions").length) {
} else if ($link.closest(".ck-link-actions").length) {
// this is to avoid showing tooltip from inside the CKEditor link editor dialog
return;
} else if ($link.closest(".note-tooltip").length) {
// don't show tooltip for links within tooltip
return;
}
@@ -39,8 +44,21 @@ async function mouseEnterHandler() {
return;
}
const linkId = $link.attr("data-link-id") || `link-${Math.floor(Math.random() * 1000000)}`;
$link.attr("data-link-id", linkId);
if ($(`.${linkId}`).is(":visible")) {
// tooltip is already open for this link
return;
}
const note = await froca.getNote(noteId);
const content = await renderTooltip(note);
const [content] = await Promise.all([
renderTooltip(note),
// to reduce flicker due to accidental mouseover, cursor must stay for a bit over the link for tooltip to appear
new Promise(res => setTimeout(res, 500))
]);
if (utils.isHtmlEmpty(content)) {
return;
@@ -53,7 +71,6 @@ async function mouseEnterHandler() {
// we now create tooltip which won't close because it won't receive mouseleave event
if ($(this).is(":hover")) {
$(this).tooltip({
delay: {"show": 300, "hide": 100},
container: 'body',
// https://github.com/zadam/trilium/issues/2794 https://github.com/zadam/trilium/issues/2988
// with bottom this flickering happens a bit less
@@ -63,15 +80,19 @@ async function mouseEnterHandler() {
title: html,
html: true,
template: '<div class="tooltip note-tooltip" role="tooltip"><div class="arrow"></div><div class="tooltip-inner"></div></div>',
sanitize: false
sanitize: false,
customClass: linkId
});
$(this).tooltip('show');
}
}
function mouseLeaveHandler() {
$(this).tooltip('dispose');
setTimeout(() => {
if (!$(this).is(":hover") && !$(`.${linkId}`).is(":hover")) {
// cursor is neither over the link nor over the tooltip, user likely is not interested
$(this).tooltip('dispose');
}
}, 1000);
}
}
async function renderTooltip(note) {

View File

@@ -18,13 +18,6 @@ async function syncNow(ignoreNotConfigured = false) {
}
}
async function forceNoteSync(noteId) {
await server.post(`sync/force-note-sync/${noteId}`);
toastService.showMessage("Note added to sync queue.");
}
export default {
syncNow,
forceNoteSync
syncNow
};

View File

@@ -37,7 +37,9 @@ export default class NoteListWidget extends NoteContextAwareWidget {
threshold: 0.1
});
observer.observe(this.$widget[0]);
// there seems to be a race condition on Firefox which triggers the observer only before the widget is visible
// (intersection is false). https://github.com/zadam/trilium/issues/4165
setTimeout(() => observer.observe(this.$widget[0]), 10);
}
checkRenderStatus() {

View File

@@ -1564,10 +1564,6 @@ export default class NoteTreeWidget extends NoteContextAwareWidget {
this.triggerCommand("showImportDialog", {noteId: node.data.noteId});
}
forceNoteSyncCommand({node}) {
syncService.forceNoteSync(node.data.noteId);
}
editNoteTitleCommand({node}) {
appContext.triggerCommand('focusOnTitle');
}

View File

@@ -2,6 +2,7 @@ import linkService from "../../services/link.js";
import server from "../../services/server.js";
import froca from "../../services/froca.js";
import NoteContextAwareWidget from "../note_context_aware_widget.js";
import options from "../../services/options.js";
const TPL = `
<div class="edited-notes-widget">
@@ -34,7 +35,9 @@ export default class EditedNotesWidget extends NoteContextAwareWidget {
return {
show: this.isEnabled(),
// promoted attributes have priority over edited notes
activate: this.note.getPromotedDefinitionAttributes().length === 0,
activate:
(this.note.getPromotedDefinitionAttributes().length === 0 || !options.is('promotedAttributesOpenInRibbon'))
&& options.is('editedNotesOpenInRibbon'),
title: 'Edited Notes',
icon: 'bx bx-calendar-edit'
};

View File

@@ -4,6 +4,7 @@ import treeService from "../../services/tree.js";
import noteAutocompleteService from "../../services/note_autocomplete.js";
import NoteContextAwareWidget from "../note_context_aware_widget.js";
import attributeService from "../../services/attributes.js";
import options from "../../services/options.js";
const TPL = `
<div>
@@ -62,7 +63,7 @@ export default class PromotedAttributesWidget extends NoteContextAwareWidget {
return {
show: true,
activate: true,
activate: options.is('promotedAttributesOpenInRibbon'),
title: "Promoted Attributes",
icon: "bx bx-table"
};

View File

@@ -31,6 +31,7 @@ import VacuumDatabaseOptions from "./options/advanced/vacuum_database.js";
import DatabaseAnonymizationOptions from "./options/advanced/database_anonymization.js";
import BackendLogWidget from "./content/backend_log.js";
import AttachmentErasureTimeoutOptions from "./options/other/attachment_erasure_timeout.js";
import RibbonOptions from "./options/appearance/ribbon.js";
const TPL = `<div class="note-detail-content-widget note-detail-printable">
<style>
@@ -57,7 +58,8 @@ const CONTENT_WIDGETS = {
FontsOptions,
ZoomFactorOptions,
NativeTitleBarOptions,
MaxContentWidthOptions
MaxContentWidthOptions,
RibbonOptions
],
_optionsShortcuts: [ KeyboardShortcutsOptions ],
_optionsTextNotes: [

View File

@@ -0,0 +1,34 @@
import OptionsWidget from "../options_widget.js";
const TPL = `
<div class="options-section">
<h4>Ribbon widgets</h4>
<label>
<input type="checkbox" class="promoted-attributes-open-in-ribbon">
Promoted Attributes ribbon tab will automatically open if promoted attributes are present on the note
</label>
<label>
<input type="checkbox" class="edited-notes-open-in-ribbon">
Edited Notes ribbon tab will automatically open on day notes
</label>
</div>`;
export default class RibbonOptions extends OptionsWidget {
doRender() {
this.$widget = $(TPL);
this.$promotedAttributesOpenInRibbon = this.$widget.find(".promoted-attributes-open-in-ribbon");
this.$promotedAttributesOpenInRibbon.on('change', () =>
this.updateCheckboxOption('promotedAttributesOpenInRibbon', this.$promotedAttributesOpenInRibbon));
this.$editedNotesOpenInRibbon = this.$widget.find(".edited-notes-open-in-ribbon");
this.$editedNotesOpenInRibbon.on('change', () =>
this.updateCheckboxOption('editedNotesOpenInRibbon', this.$editedNotesOpenInRibbon));
}
async optionsLoaded(options) {
this.setCheckboxState(this.$promotedAttributesOpenInRibbon, options.promotedAttributesOpenInRibbon);
this.setCheckboxState(this.$editedNotesOpenInRibbon, options.editedNotesOpenInRibbon);
}
}

View File

@@ -471,7 +471,7 @@ table.promoted-attributes-in-tooltip td, table.promoted-attributes-in-tooltip th
.note-tooltip-content {
/* height needs to stay small because tooltip has problem when it can't fit to either top or bottom of the cursor */
max-height: 300px;
overflow: hidden;
overflow: auto;
}
.note-tooltip-content .note-title-with-path .note-path {
@@ -722,25 +722,26 @@ a.external:not(.no-arrow):after, a[href^="http://"]:not(.no-arrow):after, a[href
content: '' !important;
}
.include-note.box-size-small .include-note-content {
/* Using data- attribute to support both CKEditor and readonly view */
.include-note[data-box-size=small] .include-note-content {
max-height: 10em;
overflow: auto;
}
.include-note.box-size-small .include-note-content.type-pdf {
.include-note[data-box-size=small] .include-note-content.type-pdf {
height: 10em; /* PDF is rendered in iframe and must be sized absolutely */
}
.include-note.box-size-medium .include-note-content {
.include-note[data-box-size=medium] .include-note-content {
max-height: 20em;
overflow: auto;
}
.include-note.box-size-medium .include-note-content.type-pdf .rendered-content {
.include-note[data-box-size=medium] .include-note-content.type-pdf .rendered-content {
height: 20em; /* PDF is rendered in iframe and must be sized absolutely */
}
.include-note.box-size-full .include-note-content.type-pdf .rendered-content {
.include-note[data-box-size=full] .include-note-content.type-pdf .rendered-content {
height: 50em; /* PDF is rendered in iframe and it's not possible to put full height so at least a large height */
}

View File

@@ -4,7 +4,7 @@ const sql = require('../../services/sql');
const utils = require('../../services/utils');
const entityChangesService = require('../../services/entity_changes');
const treeService = require('../../services/tree');
const noteService = require('../../services/notes');
const eraseService = require('../../services/erase');
const becca = require('../../becca/becca');
const TaskContext = require('../../services/task_context');
const branchService = require("../../services/branches");
@@ -72,7 +72,7 @@ function moveBranchBeforeNote(req) {
treeService.sortNotesIfNeeded(parentNote.noteId);
// if sorting is not needed, then still the ordering might have changed above manually
entityChangesService.addNoteReorderingEntityChange(parentNote.noteId);
entityChangesService.putNoteReorderingEntityChange(parentNote.noteId);
log.info(`Moved note ${branchToMove.noteId}, branch ${branchId} before note ${beforeBranch.noteId}, branch ${beforeBranchId}`);
@@ -123,7 +123,7 @@ function moveBranchAfterNote(req) {
treeService.sortNotesIfNeeded(parentNote.noteId);
// if sorting is not needed, then still the ordering might have changed above manually
entityChangesService.addNoteReorderingEntityChange(parentNote.noteId);
entityChangesService.putNoteReorderingEntityChange(parentNote.noteId);
log.info(`Moved note ${branchToMove.noteId}, branch ${branchId} after note ${afterNote.noteId}, branch ${afterBranchId}`);
@@ -193,7 +193,7 @@ function deleteBranch(req) {
if (eraseNotes) {
// erase automatically means deleting all clones + note itself
branch.getNote().deleteNote(deleteId, taskContext);
noteService.eraseNotesWithDeleteId(deleteId);
eraseService.eraseNotesWithDeleteId(deleteId);
noteDeleted = true;
} else {
noteDeleted = branch.deleteBranch(deleteId, taskContext);

View File

@@ -11,28 +11,26 @@ const ws = require('../../services/ws');
const log = require('../../services/log');
const utils = require('../../services/utils');
const path = require('path');
const BAttribute = require('../../becca/entities/battribute');
const htmlSanitizer = require('../../services/html_sanitizer');
const {formatAttrForSearch} = require("../../services/attribute_formatter");
const jsdom = require("jsdom");
const { JSDOM } = jsdom;
function addClipping(req) {
// if a note under the clipperInbox as the same 'pageUrl' attribute,
// if a note under the clipperInbox has the same 'pageUrl' attribute,
// add the content to that note and clone it under today's inbox
// otherwise just create a new note under today's inbox
let {title, content, pageUrl, images} = req.body;
const clipType = 'clippings';
const clipperInbox = getClipperInboxNote();
const dailyNote = dateNoteService.getDayNote(dateUtils.localNowDate());
pageUrl = htmlSanitizer.sanitizeUrl(pageUrl);
let clippingNote = findClippingNote(clipperInbox, pageUrl, clipType);
if (!clippingNote) {
clippingNote = noteService.createNewNote({
parentNoteId: dailyNote.noteId,
parentNoteId: clipperInbox.noteId,
title: title,
content: '',
type: 'text'
@@ -49,8 +47,8 @@ function addClipping(req) {
clippingNote.setContent(`${existingContent}${existingContent.trim() ? "<br>" : ""}${rewrittenContent}`);
if (clippingNote.parentNoteId !== dailyNote.noteId) {
cloneService.cloneNoteToParentNote(clippingNote.noteId, dailyNote.noteId);
if (clippingNote.parentNoteId !== clipperInbox.noteId) {
cloneService.cloneNoteToParentNote(clippingNote.noteId, clipperInbox.noteId);
}
return {
@@ -80,7 +78,7 @@ function getClipperInboxNote() {
let clipperInbox = attributeService.getNoteWithLabel('clipperInbox');
if (!clipperInbox) {
clipperInbox = dateNoteService.getRootCalendarNote();
clipperInbox = dateNoteService.getDayNote(dateUtils.localNowDate());
}
return clipperInbox;
@@ -127,7 +125,10 @@ function createNote(req) {
const existingContent = note.getContent();
const rewrittenContent = processContent(images, note, content);
note.setContent(`${existingContent}${existingContent.trim() ? "<br/>" : ""}${rewrittenContent}`);
const newContent = `${existingContent}${existingContent.trim() ? "<br/>" : ""}${rewrittenContent}`;
note.setContent(newContent);
noteService.asyncPostProcessContent(note, newContent); // to mark attachments as used
return {
noteId: note.noteId
@@ -152,20 +153,9 @@ function processContent(images, note, content) {
const buffer = Buffer.from(dataUrl.split(",")[1], 'base64');
const {note: imageNote, url} = imageService.saveImage(note.noteId, buffer, filename, true);
new BAttribute({
noteId: imageNote.noteId,
type: 'label',
name: 'archived'
}).save(); // so that these image notes don't show up in search / autocomplete
new BAttribute({
noteId: note.noteId,
type: 'relation',
name: 'imageLink',
value: imageNote.noteId
}).save();
const attachment = imageService.saveImageToAttachment(note.noteId, buffer, filename, true);
const sanitizedTitle = attachment.title.replace(/[^a-z0-9-.]/gi, "");
const url = `api/attachments/${attachment.attachmentId}/image/${sanitizedTitle}`;
log.info(`Replacing '${imageId}' with '${url}' in note '${note.noteId}'`);

View File

@@ -3,7 +3,7 @@
const options = require('../../services/options');
const utils = require('../../services/utils');
const dateUtils = require('../../services/date_utils');
const instanceId = require('../../services/member_id');
const instanceId = require('../../services/instance_id');
const passwordEncryptionService = require('../../services/encryption/password_encryption');
const protectedSessionService = require('../../services/protected_session');
const appInfo = require('../../services/app_info');

View File

@@ -1,6 +1,7 @@
"use strict";
const noteService = require('../../services/notes');
const eraseService = require('../../services/erase');
const treeService = require('../../services/tree');
const sql = require('../../services/sql');
const utils = require('../../services/utils');
@@ -15,9 +16,7 @@ function getNote(req) {
}
function getNoteBlob(req) {
const preview = req.query.preview === 'true';
return blobService.getBlobPojo('notes', req.params.noteId, { preview });
return blobService.getBlobPojo('notes', req.params.noteId);
}
function getNoteMetadata(req) {
@@ -67,7 +66,7 @@ function deleteNote(req) {
note.deleteNote(deleteId, taskContext);
if (eraseNotes) {
noteService.eraseNotesWithDeleteId(deleteId);
eraseService.eraseNotesWithDeleteId(deleteId);
}
if (last) {
@@ -152,11 +151,11 @@ function duplicateSubtree(req) {
}
function eraseDeletedNotesNow() {
noteService.eraseDeletedNotesNow();
eraseService.eraseDeletedNotesNow();
}
function eraseUnusedAttachmentsNow() {
noteService.eraseUnusedAttachmentsNow();
eraseService.eraseUnusedAttachmentsNow();
}
function getDeleteNotesPreview(req) {

View File

@@ -55,7 +55,9 @@ const ALLOWED_OPTIONS = new Set([
'eraseUnusedAttachmentsAfterSeconds',
'disableTray',
'customSearchEngineName',
'customSearchEngineUrl'
'customSearchEngineUrl',
'promotedAttributesOpenInRibbon',
'editedNotesOpenInRibbon'
]);
function getOptions() {

View File

@@ -21,8 +21,8 @@ function getRevisions(req) {
LENGTH(blobs.content) AS contentLength
FROM revisions
JOIN blobs ON revisions.blobId = blobs.blobId
WHERE noteId = ?
ORDER BY utcDateCreated DESC`, [req.params.noteId]);
WHERE revisions.noteId = ?
ORDER BY revisions.utcDateCreated DESC`, [req.params.noteId]);
}
function getRevision(req) {

View File

@@ -9,10 +9,8 @@ const optionService = require('../../services/options');
const contentHashService = require('../../services/content_hash');
const log = require('../../services/log');
const syncOptions = require('../../services/sync_options');
const dateUtils = require('../../services/date_utils');
const utils = require('../../services/utils');
const ws = require('../../services/ws');
const becca = require("../../becca/becca");
async function testSync() {
try {
@@ -84,54 +82,14 @@ function forceFullSync() {
syncService.sync();
}
function forceNoteSync(req) {
const noteId = req.params.noteId;
const note = becca.getNote(noteId);
const now = dateUtils.utcNowDateTime();
sql.execute(`UPDATE notes SET utcDateModified = ? WHERE noteId = ?`, [now, noteId]);
entityChangesService.moveEntityChangeToTop('notes', noteId);
sql.execute(`UPDATE blobs SET utcDateModified = ? WHERE blobId = ?`, [now, note.blobId]);
entityChangesService.moveEntityChangeToTop('blobs', note.blobId);
for (const branchId of sql.getColumn("SELECT branchId FROM branches WHERE noteId = ?", [noteId])) {
sql.execute(`UPDATE branches SET utcDateModified = ? WHERE branchId = ?`, [now, branchId]);
entityChangesService.moveEntityChangeToTop('branches', branchId);
}
for (const attributeId of sql.getColumn("SELECT attributeId FROM attributes WHERE noteId = ?", [noteId])) {
sql.execute(`UPDATE attributes SET utcDateModified = ? WHERE attributeId = ?`, [now, attributeId]);
entityChangesService.moveEntityChangeToTop('attributes', attributeId);
}
for (const revisionId of sql.getColumn("SELECT revisionId FROM revisions WHERE noteId = ?", [noteId])) {
sql.execute(`UPDATE revisions SET utcDateModified = ? WHERE revisionId = ?`, [now, revisionId]);
entityChangesService.moveEntityChangeToTop('revisions', revisionId);
}
for (const attachmentId of sql.getColumn("SELECT attachmentId FROM attachments WHERE noteId = ?", [noteId])) {
sql.execute(`UPDATE attachments SET utcDateModified = ? WHERE attachmentId = ?`, [now, attachmentId]);
entityChangesService.moveEntityChangeToTop('attachments', attachmentId);
}
log.info(`Forcing note sync for ${noteId}`);
// not awaiting for the job to finish (will probably take a long time)
syncService.sync();
}
function getChanged(req) {
const startTime = Date.now();
let lastEntityChangeId = parseInt(req.query.lastEntityChangeId);
const clientinstanceId = req.query.instanceId;
const clientInstanceId = req.query.instanceId;
let filteredEntityChanges = [];
while (filteredEntityChanges.length === 0) {
do {
const entityChanges = sql.getRows(`
SELECT *
FROM entity_changes
@@ -144,20 +102,22 @@ function getChanged(req) {
break;
}
filteredEntityChanges = entityChanges.filter(ec => ec.instanceId !== clientinstanceId);
filteredEntityChanges = entityChanges.filter(ec => ec.instanceId !== clientInstanceId);
if (filteredEntityChanges.length === 0) {
lastEntityChangeId = entityChanges[entityChanges.length - 1].id;
}
}
} while (filteredEntityChanges.length === 0);
const entityChangeRecords = syncService.getEntityChangeRecords(filteredEntityChanges);
if (entityChangeRecords.length > 0) {
lastEntityChangeId = entityChangeRecords[entityChangeRecords.length - 1].entityChange.id;
log.info(`Returning ${entityChangeRecords.length} entity changes in ${Date.now() - startTime}ms`);
}
const ret = {
return {
entityChanges: entityChangeRecords,
lastEntityChangeId,
outstandingPullCount: sql.getValue(`
@@ -165,14 +125,8 @@ function getChanged(req) {
FROM entity_changes
WHERE isSynced = 1
AND instanceId != ?
AND id > ?`, [clientinstanceId, lastEntityChangeId])
AND id > ?`, [clientInstanceId, lastEntityChangeId])
};
if (ret.entityChanges.length > 0) {
log.info(`Returning ${ret.entityChanges.length} entity changes in ${Date.now() - startTime}ms`);
}
return ret;
}
const partialRequests = {};
@@ -194,12 +148,12 @@ function update(req) {
}
if (!partialRequests[requestId]) {
throw new Error(`Partial request ${requestId}, index ${pageIndex} of ${pageCount} of pages does not have expected record.`);
throw new Error(`Partial request ${requestId}, page ${pageIndex + 1} of ${pageCount} of pages does not have expected record.`);
}
partialRequests[requestId].payload += req.body;
log.info(`Receiving partial request ${requestId}, page index ${pageIndex} out of ${pageCount} pages.`);
log.info(`Receiving a partial request ${requestId}, page ${pageIndex + 1} out of ${pageCount} pages.`);
if (pageIndex !== pageCount - 1) {
return;
@@ -212,9 +166,11 @@ function update(req) {
const {entities, instanceId} = body;
for (const {entityChange, entity} of entities) {
syncUpdateService.updateEntity(entityChange, entity, instanceId);
}
sql.transactional(() => {
for (const {entityChange, entity} of entities) {
syncUpdateService.updateEntity(entityChange, entity, instanceId);
}
});
}
setInterval(() => {
@@ -241,8 +197,7 @@ function queueSector(req) {
}
function checkEntityChanges() {
const consistencyChecks = require("../../services/consistency_checks");
consistencyChecks.runEntityChangesChecks();
require("../../services/consistency_checks").runEntityChangesChecks();
}
module.exports = {
@@ -251,7 +206,6 @@ module.exports = {
syncNow,
fillEntityChanges,
forceFullSync,
forceNoteSync,
getChanged,
update,
getStats,

View File

@@ -216,7 +216,6 @@ function register(app) {
apiRoute(PST, '/api/sync/now', syncApiRoute.syncNow);
apiRoute(PST, '/api/sync/fill-entity-changes', syncApiRoute.fillEntityChanges);
apiRoute(PST, '/api/sync/force-full-sync', syncApiRoute.forceFullSync);
apiRoute(PST, '/api/sync/force-note-sync/:noteId', syncApiRoute.forceNoteSync);
route(GET, '/api/sync/check', [auth.checkApiAuth], syncApiRoute.checkSync, apiResultHandler);
route(GET, '/api/sync/changed', [auth.checkApiAuth], syncApiRoute.getChanged, apiResultHandler);
route(PUT, '/api/sync/update', [auth.checkApiAuth], syncApiRoute.update, apiResultHandler);

View File

@@ -4,8 +4,8 @@ const build = require('./build');
const packageJson = require('../../package');
const {TRILIUM_DATA_DIR} = require('./data_dir');
const APP_DB_VERSION = 222;
const SYNC_VERSION = 30;
const APP_DB_VERSION = 225;
const SYNC_VERSION = 31;
const CLIPPER_PROTOCOL_VERSION = "1.0";
module.exports = {

View File

@@ -1,10 +1,9 @@
const becca = require('../becca/becca');
const NotFoundError = require("../errors/not_found_error");
const protectedSessionService = require("./protected_session");
const utils = require("./utils");
function getBlobPojo(entityName, entityId, opts = {}) {
opts.preview = !!opts.preview;
function getBlobPojo(entityName, entityId) {
const entity = becca.getEntity(entityName, entityId);
if (!entity) {
@@ -19,10 +18,6 @@ function getBlobPojo(entityName, entityId, opts = {}) {
pojo.content = null;
} else {
pojo.content = processContent(pojo.content, entity.isProtected, true);
if (opts.preview && pojo.content.length > 10000) {
pojo.content = `${pojo.content.substr(0, 10000)}\r\n\r\n... and ${pojo.content.length - 10000} more characters.`;
}
}
return pojo;
@@ -51,7 +46,12 @@ function processContent(content, isProtected, isStringContent) {
}
}
function calculateContentHash({blobId, content}) {
return utils.hash(`${blobId}|${content.toString()}`);
}
module.exports = {
getBlobPojo,
processContent
processContent,
calculateContentHash
};

View File

@@ -1 +1 @@
module.exports = { buildDate:"2023-07-24T23:47:57+02:00", buildRevision: "b4631e927c5848fdb48a8182b112b0f815192dcf" };
module.exports = { buildDate:"2023-08-16T23:02:15+02:00", buildRevision: "3f7a5504c77263a7118cede5c0d9b450ba37f424" };

View File

@@ -65,6 +65,7 @@ module.exports = [
{ type: 'label', name: 'executeButton'},
{ type: 'label', name: 'executeDescription'},
{ type: 'label', name: 'newNotesOnTop'},
{ type: 'label', name: 'clipperInbox'},
// relation names
{ type: 'relation', name: 'internalLink' },

View File

@@ -161,7 +161,7 @@ function cloneNoteAfter(noteId, afterBranchId) {
sql.execute("UPDATE branches SET notePosition = notePosition + 10 WHERE parentNoteId = ? AND notePosition > ? AND isDeleted = 0",
[afterNote.parentNoteId, afterNote.notePosition]);
eventChangesService.addNoteReorderingEntityChange(afterNote.parentNoteId);
eventChangesService.putNoteReorderingEntityChange(afterNote.parentNoteId);
const branch = new BBranch({
noteId: noteId,

View File

@@ -56,7 +56,7 @@ function getAndClearEntityChangeIds() {
return entityChangeIds;
}
function addEntityChange(entityChange) {
function putEntityChange(entityChange) {
if (namespace.get('ignoreEntityChangeIds')) {
return;
}
@@ -91,6 +91,6 @@ module.exports = {
isEntityEventsDisabled,
reset,
getAndClearEntityChangeIds,
addEntityChange,
putEntityChange,
ignoreEntityChangeIds,
};

View File

@@ -414,7 +414,7 @@ class ConsistencyChecks {
const hash = utils.hash(utils.randomString(10));
entityChangesService.addEntityChange({
entityChangesService.putEntityChange({
entityName: 'blobs',
entityId: blobId,
hash: hash,
@@ -597,23 +597,19 @@ class ConsistencyChecks {
runEntityChangeChecks(entityName, key) {
this.findAndFixIssues(`
SELECT
${key} as entityId
FROM
${entityName}
LEFT JOIN entity_changes ON entity_changes.entityName = '${entityName}'
AND entity_changes.entityId = ${key}
WHERE
entity_changes.id IS NULL`,
SELECT ${key} as entityId
FROM ${entityName}
LEFT JOIN entity_changes ec ON ec.entityName = '${entityName}' AND ec.entityId = ${entityName}.${key}
WHERE ec.id IS NULL`,
({entityId}) => {
const entityRow = sql.getRow(`SELECT * FROM ${entityName} WHERE ${key} = ?`, [entityId]);
if (this.autoFix) {
entityChangesService.addEntityChange({
entityChangesService.putEntityChange({
entityName,
entityId,
hash: utils.randomString(10), // doesn't matter, will force sync, but that's OK
isErased: !!entityRow.isErased,
isErased: false,
utcDateChanged: entityRow.utcDateModified || entityRow.utcDateCreated,
isSynced: entityName !== 'options' || entityRow.isSynced
});
@@ -625,15 +621,13 @@ class ConsistencyChecks {
});
this.findAndFixIssues(`
SELECT
id, entityId
FROM
entity_changes
LEFT JOIN ${entityName} ON entityId = ${key}
SELECT id, entityId
FROM entity_changes
LEFT JOIN ${entityName} ON entityId = ${entityName}.${key}
WHERE
entity_changes.isErased = 0
AND entity_changes.entityName = '${entityName}'
AND ${key} IS NULL`,
AND ${entityName}.${key} IS NULL`,
({id, entityId}) => {
if (this.autoFix) {
sql.execute("DELETE FROM entity_changes WHERE entityName = ? AND entityId = ?", [entityName, entityId]);
@@ -645,11 +639,9 @@ class ConsistencyChecks {
});
this.findAndFixIssues(`
SELECT
id, entityId
FROM
entity_changes
JOIN ${entityName} ON entityId = ${key}
SELECT id, entityId
FROM entity_changes
JOIN ${entityName} ON entityId = ${entityName}.${key}
WHERE
entity_changes.isErased = 1
AND entity_changes.entityName = '${entityName}'`,

View File

@@ -3,14 +3,19 @@
const sql = require('./sql');
const utils = require('./utils');
const log = require('./log');
const eraseService = require("./erase");
function getEntityHashes() {
// blob erasure is not synced, we should check before each sync if there's some blob to erase
eraseService.eraseUnusedBlobs();
const startTime = new Date();
const hashRows = sql.getRawRows(`
SELECT entityName,
entityId,
hash
hash,
isErased
FROM entity_changes
WHERE isSynced = 1
AND entityName != 'note_reordering'`);
@@ -21,12 +26,13 @@ function getEntityHashes() {
const hashMap = {};
for (const [entityName, entityId, hash] of hashRows) {
for (const [entityName, entityId, hash, isErased] of hashRows) {
const entityHashMap = hashMap[entityName] = hashMap[entityName] || {};
const sector = entityId[0];
entityHashMap[sector] = (entityHashMap[sector] || "") + hash
// if the entity is erased, its hash is not updated, so it has to be added extra
entityHashMap[sector] = (entityHashMap[sector] || "") + hash + isErased;
}
for (const entityHashMap of Object.values(hashMap)) {

View File

@@ -3,18 +3,19 @@ const dateUtils = require('./date_utils');
const log = require('./log');
const cls = require('./cls');
const utils = require('./utils');
const instanceId = require('./member_id');
const instanceId = require('./instance_id');
const becca = require("../becca/becca");
const blobService = require("../services/blob");
let maxEntityChangeId = 0;
function addEntityChangeWithInstanceId(origEntityChange, instanceId) {
function putEntityChangeWithInstanceId(origEntityChange, instanceId) {
const ec = {...origEntityChange, instanceId};
return addEntityChange(ec);
putEntityChange(ec);
}
function addEntityChange(origEntityChange) {
function putEntityChange(origEntityChange) {
const ec = {...origEntityChange};
delete ec.id;
@@ -31,11 +32,11 @@ function addEntityChange(origEntityChange) {
maxEntityChangeId = Math.max(maxEntityChangeId, ec.id);
cls.addEntityChange(ec);
cls.putEntityChange(ec);
}
function addNoteReorderingEntityChange(parentNoteId, componentId) {
addEntityChange({
function putNoteReorderingEntityChange(parentNoteId, componentId) {
putEntityChange({
entityName: "note_reordering",
entityId: parentNoteId,
hash: 'N/A',
@@ -54,24 +55,24 @@ function addNoteReorderingEntityChange(parentNoteId, componentId) {
});
}
function moveEntityChangeToTop(entityName, entityId) {
const ec = sql.getRow(`SELECT * FROM entity_changes WHERE entityName = ? AND entityId = ?`, [entityName, entityId]);
addEntityChange(ec);
function putEntityChangeForOtherInstances(ec) {
putEntityChange({
...ec,
changeId: null,
instanceId: null
});
}
function addEntityChangesForSector(entityName, sector) {
const startTime = Date.now();
const entityChanges = sql.getRows(`SELECT * FROM entity_changes WHERE entityName = ? AND SUBSTR(entityId, 1, 1) = ?`, [entityName, sector]);
sql.transactional(() => {
for (const ec of entityChanges) {
addEntityChange(ec);
putEntityChange(ec);
}
});
log.info(`Added sector ${sector} of '${entityName}' to sync queue in ${Date.now() - startTime}ms.`);
log.info(`Added sector ${sector} of '${entityName}' (${entityChanges.length} entities) to the sync queue.`);
}
function cleanupEntityChangesForMissingEntities(entityName, entityPrimaryKey) {
@@ -88,52 +89,48 @@ function fillEntityChanges(entityName, entityPrimaryKey, condition = '') {
cleanupEntityChangesForMissingEntities(entityName, entityPrimaryKey);
sql.transactional(() => {
const entityIds = sql.getColumn(`SELECT ${entityPrimaryKey} FROM ${entityName}`
+ (condition ? ` WHERE ${condition}` : ''));
const entityIds = sql.getColumn(`SELECT ${entityPrimaryKey} FROM ${entityName} ${condition}`);
let createdCount = 0;
for (const entityId of entityIds) {
const existingRows = sql.getValue("SELECT COUNT(1) FROM entity_changes WHERE entityName = ? AND entityId = ?", [entityName, entityId]);
// we don't want to replace existing entities (which would effectively cause full resync)
if (existingRows === 0) {
createdCount++;
let hash;
let utcDateChanged;
let isSynced;
if (entityName === 'blobs') {
// FIXME: hacky, not sure if it might cause some problems
hash = "fake value";
utcDateChanged = dateUtils.utcNowDateTime();
isSynced = true; // contents are always synced
} else {
const entity = becca.getEntity(entityName, entityId);
if (entity) {
hash = entity?.generateHash() || "|deleted";
utcDateChanged = entity?.getUtcDateChanged() || dateUtils.utcNowDateTime();
isSynced = entityName !== 'options' || !!entity?.isSynced;
} else {
// entity might be null (not present in becca) when it's deleted
// FIXME: hacky, not sure if it might cause some problems
hash = "deleted";
utcDateChanged = dateUtils.utcNowDateTime();
isSynced = true; // deletable (the ones with isDeleted) entities are synced
}
}
addEntityChange({
entityName,
entityId,
hash: hash,
isErased: false,
utcDateChanged: utcDateChanged,
isSynced: isSynced
});
if (existingRows !== 0) {
// we don't want to replace existing entities (which would effectively cause full resync)
continue;
}
createdCount++;
const ec = {
entityName,
entityId,
isErased: false
};
if (entityName === 'blobs') {
const blob = sql.getRow("SELECT blobId, content, utcDateModified FROM blobs WHERE blobId = ?", [entityId]);
ec.hash = blobService.calculateContentHash(blob);
ec.utcDateChanged = blob.utcDateModified;
ec.isSynced = true; // blobs are always synced
} else {
const entity = becca.getEntity(entityName, entityId);
if (entity) {
ec.hash = entity.generateHash() || "|deleted";
ec.utcDateChanged = entity.getUtcDateChanged() || dateUtils.utcNowDateTime();
ec.isSynced = entityName !== 'options' || !!entity.isSynced;
} else {
// entity might be null (not present in becca) when it's deleted
// FIXME: hacky, not sure if it might cause some problems
ec.hash = "deleted";
ec.utcDateChanged = dateUtils.utcNowDateTime();
ec.isSynced = true; // deletable (the ones with isDeleted) entities are synced
}
}
putEntityChange(ec);
}
if (createdCount > 0) {
@@ -153,7 +150,7 @@ function fillAllEntityChanges() {
fillEntityChanges("blobs", "blobId");
fillEntityChanges("attributes", "attributeId");
fillEntityChanges("etapi_tokens", "etapiTokenId");
fillEntityChanges("options", "name", 'isSynced = 1');
fillEntityChanges("options", "name", 'WHERE isSynced = 1');
});
}
@@ -162,10 +159,10 @@ function recalculateMaxEntityChangeId() {
}
module.exports = {
addNoteReorderingEntityChange,
moveEntityChangeToTop,
addEntityChange,
addEntityChangeWithInstanceId,
putNoteReorderingEntityChange,
putEntityChangeForOtherInstances,
putEntityChange,
putEntityChangeWithInstanceId,
fillAllEntityChanges,
addEntityChangesForSector,
getMaxEntityChangeId: () => maxEntityChangeId,

187
src/services/erase.js Normal file
View File

@@ -0,0 +1,187 @@
const sql = require("./sql.js");
const revisionService = require("./revisions.js");
const log = require("./log.js");
const entityChangesService = require("./entity_changes.js");
const optionService = require("./options.js");
const dateUtils = require("./date_utils.js");
const sqlInit = require("./sql_init.js");
const cls = require("./cls.js");
function eraseNotes(noteIdsToErase) {
if (noteIdsToErase.length === 0) {
return;
}
sql.executeMany(`DELETE FROM notes WHERE noteId IN (???)`, noteIdsToErase);
setEntityChangesAsErased(sql.getManyRows(`SELECT * FROM entity_changes WHERE entityName = 'notes' AND entityId IN (???)`, noteIdsToErase));
// we also need to erase all "dependent" entities of the erased notes
const branchIdsToErase = sql.getManyRows(`SELECT branchId FROM branches WHERE noteId IN (???)`, noteIdsToErase)
.map(row => row.branchId);
eraseBranches(branchIdsToErase);
const attributeIdsToErase = sql.getManyRows(`SELECT attributeId FROM attributes WHERE noteId IN (???)`, noteIdsToErase)
.map(row => row.attributeId);
eraseAttributes(attributeIdsToErase);
const revisionIdsToErase = sql.getManyRows(`SELECT revisionId FROM revisions WHERE noteId IN (???)`, noteIdsToErase)
.map(row => row.revisionId);
revisionService.eraseRevisions(revisionIdsToErase);
log.info(`Erased notes: ${JSON.stringify(noteIdsToErase)}`);
}
function setEntityChangesAsErased(entityChanges) {
for (const ec of entityChanges) {
ec.isErased = true;
ec.utcDateChanged = dateUtils.utcNowDateTime();
entityChangesService.putEntityChange(ec);
}
}
function eraseBranches(branchIdsToErase) {
if (branchIdsToErase.length === 0) {
return;
}
sql.executeMany(`DELETE FROM branches WHERE branchId IN (???)`, branchIdsToErase);
setEntityChangesAsErased(sql.getManyRows(`SELECT * FROM entity_changes WHERE entityName = 'branches' AND entityId IN (???)`, branchIdsToErase));
log.info(`Erased branches: ${JSON.stringify(branchIdsToErase)}`);
}
function eraseAttributes(attributeIdsToErase) {
if (attributeIdsToErase.length === 0) {
return;
}
sql.executeMany(`DELETE FROM attributes WHERE attributeId IN (???)`, attributeIdsToErase);
setEntityChangesAsErased(sql.getManyRows(`SELECT * FROM entity_changes WHERE entityName = 'attributes' AND entityId IN (???)`, attributeIdsToErase));
log.info(`Erased attributes: ${JSON.stringify(attributeIdsToErase)}`);
}
function eraseAttachments(attachmentIdsToErase) {
if (attachmentIdsToErase.length === 0) {
return;
}
sql.executeMany(`DELETE FROM attachments WHERE attachmentId IN (???)`, attachmentIdsToErase);
setEntityChangesAsErased(sql.getManyRows(`SELECT * FROM entity_changes WHERE entityName = 'attachments' AND entityId IN (???)`, attachmentIdsToErase));
log.info(`Erased attachments: ${JSON.stringify(attachmentIdsToErase)}`);
}
function eraseUnusedBlobs() {
const unusedBlobIds = sql.getColumn(`
SELECT blobs.blobId
FROM blobs
LEFT JOIN notes ON notes.blobId = blobs.blobId
LEFT JOIN attachments ON attachments.blobId = blobs.blobId
LEFT JOIN revisions ON revisions.blobId = blobs.blobId
WHERE notes.noteId IS NULL
AND attachments.attachmentId IS NULL
AND revisions.revisionId IS NULL`);
if (unusedBlobIds.length === 0) {
return;
}
sql.executeMany(`DELETE FROM blobs WHERE blobId IN (???)`, unusedBlobIds);
// blobs are not marked as erased in entity_changes, they are just purged completely
// this is because technically every keystroke can create a new blob and there would be just too many
sql.executeMany(`DELETE FROM entity_changes WHERE entityName = 'blobs' AND entityId IN (???)`, unusedBlobIds);
log.info(`Erased unused blobs: ${JSON.stringify(unusedBlobIds)}`);
}
function eraseDeletedEntities(eraseEntitiesAfterTimeInSeconds = null) {
// this is important also so that the erased entity changes are sent to the connected clients
sql.transactional(() => {
if (eraseEntitiesAfterTimeInSeconds === null) {
eraseEntitiesAfterTimeInSeconds = optionService.getOptionInt('eraseEntitiesAfterTimeInSeconds');
}
const cutoffDate = new Date(Date.now() - eraseEntitiesAfterTimeInSeconds * 1000);
const noteIdsToErase = sql.getColumn("SELECT noteId FROM notes WHERE isDeleted = 1 AND utcDateModified <= ?", [dateUtils.utcDateTimeStr(cutoffDate)]);
eraseNotes(noteIdsToErase);
const branchIdsToErase = sql.getColumn("SELECT branchId FROM branches WHERE isDeleted = 1 AND utcDateModified <= ?", [dateUtils.utcDateTimeStr(cutoffDate)]);
eraseBranches(branchIdsToErase);
const attributeIdsToErase = sql.getColumn("SELECT attributeId FROM attributes WHERE isDeleted = 1 AND utcDateModified <= ?", [dateUtils.utcDateTimeStr(cutoffDate)]);
eraseAttributes(attributeIdsToErase);
const attachmentIdsToErase = sql.getColumn("SELECT attachmentId FROM attachments WHERE isDeleted = 1 AND utcDateModified <= ?", [dateUtils.utcDateTimeStr(cutoffDate)]);
eraseAttachments(attachmentIdsToErase);
eraseUnusedBlobs();
});
}
function eraseNotesWithDeleteId(deleteId) {
const noteIdsToErase = sql.getColumn("SELECT noteId FROM notes WHERE isDeleted = 1 AND deleteId = ?", [deleteId]);
eraseNotes(noteIdsToErase);
const branchIdsToErase = sql.getColumn("SELECT branchId FROM branches WHERE isDeleted = 1 AND deleteId = ?", [deleteId]);
eraseBranches(branchIdsToErase);
const attributeIdsToErase = sql.getColumn("SELECT attributeId FROM attributes WHERE isDeleted = 1 AND deleteId = ?", [deleteId]);
eraseAttributes(attributeIdsToErase);
const attachmentIdsToErase = sql.getColumn("SELECT attachmentId FROM attachments WHERE isDeleted = 1 AND deleteId = ?", [deleteId]);
eraseAttachments(attachmentIdsToErase);
eraseUnusedBlobs();
}
function eraseDeletedNotesNow() {
eraseDeletedEntities(0);
}
function eraseUnusedAttachmentsNow() {
eraseScheduledAttachments(0);
}
function eraseScheduledAttachments(eraseUnusedAttachmentsAfterSeconds = null) {
if (eraseUnusedAttachmentsAfterSeconds === null) {
eraseUnusedAttachmentsAfterSeconds = optionService.getOptionInt('eraseUnusedAttachmentsAfterSeconds');
}
const cutOffDate = dateUtils.utcDateTimeStr(new Date(Date.now() - (eraseUnusedAttachmentsAfterSeconds * 1000)));
const attachmentIdsToErase = sql.getColumn('SELECT attachmentId FROM attachments WHERE utcDateScheduledForErasureSince < ?', [cutOffDate]);
eraseAttachments(attachmentIdsToErase);
}
sqlInit.dbReady.then(() => {
// first cleanup kickoff 5 minutes after startup
setTimeout(cls.wrap(() => eraseDeletedEntities()), 5 * 60 * 1000);
setTimeout(cls.wrap(() => eraseScheduledAttachments()), 6 * 60 * 1000);
setInterval(cls.wrap(() => eraseDeletedEntities()), 4 * 3600 * 1000);
setInterval(cls.wrap(() => eraseScheduledAttachments()), 3600 * 1000);
});
module.exports = {
eraseDeletedNotesNow,
eraseUnusedAttachmentsNow,
eraseNotesWithDeleteId,
eraseUnusedBlobs
};

View File

@@ -301,16 +301,10 @@ function importEnex(taskContext, file, parentNote) {
? resource.title
: `image.${resource.mime.substr(6)}`; // default if real name is not present
const {url, note: imageNote} = imageService.saveImage(noteEntity.noteId, resource.content, originalName, taskContext.data.shrinkImages);
for (const attr of resource.attributes) {
if (attr.name !== 'originalFileName') { // this one is already saved in imageService
imageNote.addAttribute(attr.type, attr.name, attr.value);
}
}
updateDates(imageNote, utcDateCreated, utcDateModified);
const attachment = imageService.saveImageToAttachment(noteEntity.noteId, resource.content, originalName, taskContext.data.shrinkImages);
const sanitizedTitle = attachment.title.replace(/[^a-z0-9-.]/gi, "");
const url = `api/attachments/${attachment.attachmentId}/image/${sanitizedTitle}`;
const imageLink = `<img src="${url}">`;
content = content.replace(mediaRegex, imageLink);

View File

@@ -18,7 +18,7 @@ async function migrate() {
// backup before attempting migration
await backupService.backupNow(
// special name for the pre-0.60 migration to prevent later overwrite
// creating a special backup for versions 0.60.X and older, the changes in 0.61 are major.
currentDbVersion < 214
? `before-migration-v${currentDbVersion}`
: 'before-migration'
@@ -72,6 +72,9 @@ async function migrate() {
}
}
});
log.info("VACUUMing database, this might take a while ...");
sql.execute("VACUUM");
}
function executeMigration(mig) {

View File

@@ -265,7 +265,7 @@ function createNewNoteWithTarget(target, targetBranchId, params) {
const retObject = createNewNote(params);
entityChangesService.addNoteReorderingEntityChange(params.parentNoteId);
entityChangesService.putNoteReorderingEntityChange(params.parentNoteId);
return retObject;
}
@@ -855,158 +855,6 @@ async function asyncPostProcessContent(note, content) {
scanForLinks(note, content);
}
function eraseNotes(noteIdsToErase) {
if (noteIdsToErase.length === 0) {
return;
}
sql.executeMany(`DELETE FROM notes WHERE noteId IN (???)`, noteIdsToErase);
setEntityChangesAsErased(sql.getManyRows(`SELECT * FROM entity_changes WHERE entityName = 'notes' AND entityId IN (???)`, noteIdsToErase));
// we also need to erase all "dependent" entities of the erased notes
const branchIdsToErase = sql.getManyRows(`SELECT branchId FROM branches WHERE noteId IN (???)`, noteIdsToErase)
.map(row => row.branchId);
eraseBranches(branchIdsToErase);
const attributeIdsToErase = sql.getManyRows(`SELECT attributeId FROM attributes WHERE noteId IN (???)`, noteIdsToErase)
.map(row => row.attributeId);
eraseAttributes(attributeIdsToErase);
const revisionIdsToErase = sql.getManyRows(`SELECT revisionId FROM revisions WHERE noteId IN (???)`, noteIdsToErase)
.map(row => row.revisionId);
revisionService.eraseRevisions(revisionIdsToErase);
log.info(`Erased notes: ${JSON.stringify(noteIdsToErase)}`);
}
function setEntityChangesAsErased(entityChanges) {
for (const ec of entityChanges) {
ec.isErased = true;
entityChangesService.addEntityChange(ec);
}
}
function eraseBranches(branchIdsToErase) {
if (branchIdsToErase.length === 0) {
return;
}
sql.executeMany(`DELETE FROM branches WHERE branchId IN (???)`, branchIdsToErase);
setEntityChangesAsErased(sql.getManyRows(`SELECT * FROM entity_changes WHERE entityName = 'branches' AND entityId IN (???)`, branchIdsToErase));
log.info(`Erased branches: ${JSON.stringify(branchIdsToErase)}`);
}
function eraseAttributes(attributeIdsToErase) {
if (attributeIdsToErase.length === 0) {
return;
}
sql.executeMany(`DELETE FROM attributes WHERE attributeId IN (???)`, attributeIdsToErase);
setEntityChangesAsErased(sql.getManyRows(`SELECT * FROM entity_changes WHERE entityName = 'attributes' AND entityId IN (???)`, attributeIdsToErase));
log.info(`Erased attributes: ${JSON.stringify(attributeIdsToErase)}`);
}
function eraseAttachments(attachmentIdsToErase) {
if (attachmentIdsToErase.length === 0) {
return;
}
sql.executeMany(`DELETE FROM attachments WHERE attachmentId IN (???)`, attachmentIdsToErase);
setEntityChangesAsErased(sql.getManyRows(`SELECT * FROM entity_changes WHERE entityName = 'attachments' AND entityId IN (???)`, attachmentIdsToErase));
log.info(`Erased attachments: ${JSON.stringify(attachmentIdsToErase)}`);
}
function eraseUnusedBlobs() {
// this method is rather defense in depth - in normal operation, the unused blobs should be erased immediately
// after getting unused (handled in entity._setContent())
const unusedBlobIds = sql.getColumn(`
SELECT blobs.blobId
FROM blobs
LEFT JOIN notes ON notes.blobId = blobs.blobId
LEFT JOIN attachments ON attachments.blobId = blobs.blobId
LEFT JOIN revisions ON revisions.blobId = blobs.blobId
WHERE notes.noteId IS NULL
AND attachments.attachmentId IS NULL
AND revisions.revisionId IS NULL`);
if (unusedBlobIds.length === 0) {
return;
}
sql.executeMany(`DELETE FROM blobs WHERE blobId IN (???)`, unusedBlobIds);
setEntityChangesAsErased(sql.getManyRows(`SELECT * FROM entity_changes WHERE entityName = 'blobs' AND entityId IN (???)`, unusedBlobIds));
log.info(`Erased unused blobs: ${JSON.stringify(unusedBlobIds)}`);
}
function eraseDeletedEntities(eraseEntitiesAfterTimeInSeconds = null) {
// this is important also so that the erased entity changes are sent to the connected clients
sql.transactional(() => {
if (eraseEntitiesAfterTimeInSeconds === null) {
eraseEntitiesAfterTimeInSeconds = optionService.getOptionInt('eraseEntitiesAfterTimeInSeconds');
}
const cutoffDate = new Date(Date.now() - eraseEntitiesAfterTimeInSeconds * 1000);
const noteIdsToErase = sql.getColumn("SELECT noteId FROM notes WHERE isDeleted = 1 AND utcDateModified <= ?", [dateUtils.utcDateTimeStr(cutoffDate)]);
eraseNotes(noteIdsToErase);
const branchIdsToErase = sql.getColumn("SELECT branchId FROM branches WHERE isDeleted = 1 AND utcDateModified <= ?", [dateUtils.utcDateTimeStr(cutoffDate)]);
eraseBranches(branchIdsToErase);
const attributeIdsToErase = sql.getColumn("SELECT attributeId FROM attributes WHERE isDeleted = 1 AND utcDateModified <= ?", [dateUtils.utcDateTimeStr(cutoffDate)]);
eraseAttributes(attributeIdsToErase);
const attachmentIdsToErase = sql.getColumn("SELECT attachmentId FROM attachments WHERE isDeleted = 1 AND utcDateModified <= ?", [dateUtils.utcDateTimeStr(cutoffDate)]);
eraseAttachments(attachmentIdsToErase);
eraseUnusedBlobs();
});
}
function eraseNotesWithDeleteId(deleteId) {
const noteIdsToErase = sql.getColumn("SELECT noteId FROM notes WHERE isDeleted = 1 AND deleteId = ?", [deleteId]);
eraseNotes(noteIdsToErase);
const branchIdsToErase = sql.getColumn("SELECT branchId FROM branches WHERE isDeleted = 1 AND deleteId = ?", [deleteId]);
eraseBranches(branchIdsToErase);
const attributeIdsToErase = sql.getColumn("SELECT attributeId FROM attributes WHERE isDeleted = 1 AND deleteId = ?", [deleteId]);
eraseAttributes(attributeIdsToErase);
const attachmentIdsToErase = sql.getColumn("SELECT attachmentId FROM attachments WHERE isDeleted = 1 AND deleteId = ?", [deleteId]);
eraseAttachments(attachmentIdsToErase);
eraseUnusedBlobs();
}
function eraseDeletedNotesNow() {
eraseDeletedEntities(0);
}
function eraseUnusedAttachmentsNow() {
eraseScheduledAttachments(0);
}
// all keys should be replaced by the corresponding values
function replaceByMap(str, mapObj) {
const re = new RegExp(Object.keys(mapObj).join("|"),"g");
@@ -1138,26 +986,6 @@ function getNoteIdMapping(origNote) {
return noteIdMapping;
}
function eraseScheduledAttachments(eraseUnusedAttachmentsAfterSeconds = null) {
if (eraseUnusedAttachmentsAfterSeconds === null) {
eraseUnusedAttachmentsAfterSeconds = optionService.getOptionInt('eraseUnusedAttachmentsAfterSeconds');
}
const cutOffDate = dateUtils.utcDateTimeStr(new Date(Date.now() - (eraseUnusedAttachmentsAfterSeconds * 1000)));
const attachmentIdsToErase = sql.getColumn('SELECT attachmentId FROM attachments WHERE utcDateScheduledForErasureSince < ?', [cutOffDate]);
eraseAttachments(attachmentIdsToErase);
}
sqlInit.dbReady.then(() => {
// first cleanup kickoff 5 minutes after startup
setTimeout(cls.wrap(() => eraseDeletedEntities()), 5 * 60 * 1000);
setTimeout(cls.wrap(() => eraseScheduledAttachments()), 6 * 60 * 1000);
setInterval(cls.wrap(() => eraseDeletedEntities()), 4 * 3600 * 1000);
setInterval(cls.wrap(() => eraseScheduledAttachments()), 3600 * 1000);
});
module.exports = {
createNewNote,
createNewNoteWithTarget,
@@ -1168,9 +996,6 @@ module.exports = {
duplicateSubtreeWithoutRoot,
getUndeletedParentBranchIds,
triggerNoteTitleChanged,
eraseDeletedNotesNow,
eraseUnusedAttachmentsNow,
eraseNotesWithDeleteId,
saveRevisionIfNeeded,
downloadImages,
asyncPostProcessContent

View File

@@ -88,7 +88,9 @@ const defaultOptions = [
{ name: 'disableTray', value: 'false', isSynced: false },
{ name: 'eraseUnusedAttachmentsAfterSeconds', value: '2592000', isSynced: true },
{ name: 'customSearchEngineName', value: 'DuckDuckGo', isSynced: true },
{ name: 'customSearchEngineUrl', value: 'https://duckduckgo.com/?q={keyword}', isSynced: true }
{ name: 'customSearchEngineUrl', value: 'https://duckduckgo.com/?q={keyword}', isSynced: true },
{ name: 'promotedAttributesOpenInRibbon', value: 'true', isSynced: true },
{ name: 'editedNotesOpenInRibbon', value: 'true', isSynced: true }
];
function initStartupOptions() {

View File

@@ -3,6 +3,7 @@
const log = require('./log');
const sql = require('./sql');
const protectedSessionService = require("./protected_session");
const dateUtils = require("./date_utils");
/**
* @param {BNote} note
@@ -40,7 +41,7 @@ function eraseRevisions(revisionIdsToErase) {
log.info(`Removing note revisions: ${JSON.stringify(revisionIdsToErase)}`);
sql.executeMany(`DELETE FROM revisions WHERE revisionId IN (???)`, revisionIdsToErase);
sql.executeMany(`UPDATE entity_changes SET isErased = 1 WHERE entityName = 'revisions' AND entityId IN (???)`, revisionIdsToErase);
sql.executeMany(`UPDATE entity_changes SET isErased = 1, utcDateChanged = '${dateUtils.utcNowDateTime()}' WHERE entityName = 'revisions' AND entityId IN (???)`, revisionIdsToErase);
}
module.exports = {

View File

@@ -4,7 +4,7 @@ const log = require('./log');
const sql = require('./sql');
const optionService = require('./options');
const utils = require('./utils');
const instanceId = require('./member_id');
const instanceId = require('./instance_id');
const dateUtils = require('./date_utils');
const syncUpdateService = require('./sync_update');
const contentHashService = require('./content_hash');
@@ -54,13 +54,12 @@ async function sync() {
});
}
catch (e) {
// we're dynamically switching whether we're using proxy or not based on whether we encountered error with the current method
proxyToggle = !proxyToggle;
if (e.message &&
(e.message.includes('ECONNREFUSED') ||
e.message.includes('ERR_CONNECTION_REFUSED') ||
e.message.includes('ERR_ADDRESS_UNREACHABLE') ||
e.message.includes('Bad Gateway'))) {
if (e.message?.includes('ECONNREFUSED') ||
e.message?.includes('ERR_') || // node network errors
e.message?.includes('Bad Gateway')) {
ws.syncFailed();
@@ -109,7 +108,7 @@ async function doLogin() {
});
if (resp.instanceId === instanceId) {
throw new Error(`Sync server has member ID '${resp.instanceId}' which is also local. This usually happens when the sync client is (mis)configured to sync with itself (URL points back to client) instead of the correct sync server.`);
throw new Error(`Sync server has instance ID '${resp.instanceId}' which is also local. This usually happens when the sync client is (mis)configured to sync with itself (URL points back to client) instead of the correct sync server.`);
}
syncContext.instanceId = resp.instanceId;
@@ -147,17 +146,19 @@ async function pullChanges(syncContext) {
sql.transactional(() => {
for (const {entityChange, entity} of entityChanges) {
const changeAppliedAlready = entityChange.changeId
&& !!sql.getValue("SELECT id FROM entity_changes WHERE changeId = ?", [entityChange.changeId]);
&& !!sql.getValue("SELECT 1 FROM entity_changes WHERE changeId = ?", [entityChange.changeId]);
if (!changeAppliedAlready) {
if (!atLeastOnePullApplied) { // send only for first
ws.syncPullInProgress();
atLeastOnePullApplied = true;
}
syncUpdateService.updateEntity(entityChange, entity, syncContext.instanceId);
if (changeAppliedAlready) {
continue;
}
if (!atLeastOnePullApplied) { // send only for first
ws.syncPullInProgress();
atLeastOnePullApplied = true;
}
syncUpdateService.updateEntity(entityChange, entity, syncContext.instanceId);
}
if (lastSyncedPull !== lastEntityChangeId) {
@@ -255,7 +256,7 @@ async function checkContentHash(syncContext) {
const failedChecks = contentHashService.checkContentHashes(resp.entityHashes);
if (failedChecks.length > 0) {
// before requeuing sectors, make sure the entity changes are correct
// before re-queuing sectors, make sure the entity changes are correct
const consistencyChecks = require("./consistency_checks");
consistencyChecks.runEntityChangesChecks();
@@ -352,7 +353,8 @@ function getEntityChangeRecords(entityChanges) {
length += JSON.stringify(record).length;
if (length > 1000000) {
if (length > 1_000_000) {
// each sync request/response should have at most ~1 MB.
break;
}
}

View File

@@ -4,106 +4,85 @@ const entityChangesService = require('./entity_changes');
const eventService = require('./events');
const entityConstructor = require("../becca/entity_constructor");
function updateEntity(entityChange, entityRow, instanceId) {
// can be undefined for options with isSynced=false
if (!entityRow) {
if (entityChange.isSynced) {
if (entityChange.isErased) {
eraseEntity(entityChange, instanceId);
}
else {
log.info(`Encountered synced non-erased entity change without entity: ${JSON.stringify(entityChange)}`);
}
}
else if (entityChange.entityName !== 'options') {
log.info(`Encountered unsynced non-option entity change without entity: ${JSON.stringify(entityChange)}`);
}
return;
function updateEntity(remoteEC, remoteEntityRow, instanceId) {
if (!remoteEntityRow && remoteEC.entityName === 'options') {
return; // can be undefined for options with isSynced=false
}
const updated = entityChange.entityName === 'note_reordering'
? updateNoteReordering(entityChange, entityRow, instanceId)
: updateNormalEntity(entityChange, entityRow, instanceId);
const updated = remoteEC.entityName === 'note_reordering'
? updateNoteReordering(remoteEC, remoteEntityRow, instanceId)
: updateNormalEntity(remoteEC, remoteEntityRow, instanceId);
if (updated) {
if (entityRow.isDeleted) {
if (remoteEntityRow?.isDeleted) {
eventService.emit(eventService.ENTITY_DELETE_SYNCED, {
entityName: entityChange.entityName,
entityId: entityChange.entityId
entityName: remoteEC.entityName,
entityId: remoteEC.entityId
});
}
else if (!entityChange.isErased) {
else if (!remoteEC.isErased) {
eventService.emit(eventService.ENTITY_CHANGE_SYNCED, {
entityName: entityChange.entityName,
entityRow
entityName: remoteEC.entityName,
entityRow: remoteEntityRow
});
}
}
}
function updateNormalEntity(remoteEntityChange, remoteEntityRow, instanceId) {
const localEntityChange = sql.getRow(`
SELECT utcDateChanged, hash, isErased
FROM entity_changes
WHERE entityName = ? AND entityId = ?`, [remoteEntityChange.entityName, remoteEntityChange.entityId]);
function updateNormalEntity(remoteEC, remoteEntityRow, instanceId) {
const localEC = sql.getRow(`SELECT * FROM entity_changes WHERE entityName = ? AND entityId = ?`, [remoteEC.entityName, remoteEC.entityId]);
if (localEntityChange && !localEntityChange.isErased && remoteEntityChange.isErased) {
sql.transactional(() => {
const primaryKey = entityConstructor.getEntityFromEntityName(remoteEntityChange.entityName).primaryKeyName;
sql.execute(`DELETE FROM ${remoteEntityChange.entityName} WHERE ${primaryKey} = ?`, remoteEntityChange.entityId);
entityChangesService.addEntityChangeWithInstanceId(remoteEntityChange, instanceId);
});
if (!localEC?.isErased && remoteEC.isErased) {
eraseEntity(remoteEC, instanceId);
return true;
} else if (localEC?.isErased && !remoteEC.isErased) {
// on this side, we can't unerase the entity, so force the entity to be erased on the other side.
entityChangesService.putEntityChangeForOtherInstances(localEC);
return false;
}
if (!localEntityChange
|| localEntityChange.utcDateChanged < remoteEntityChange.utcDateChanged
|| localEntityChange.hash !== remoteEntityChange.hash // sync error, we should still update
if (!localEC
|| localEC.utcDateChanged < remoteEC.utcDateChanged
|| (localEC.utcDateChanged === remoteEC.utcDateChanged && localEC.hash !== remoteEC.hash) // sync error, we should still update
) {
if (remoteEntityChange.entityName === 'blobs') {
remoteEntityRow.content = handleContent(remoteEntityRow.content);
if (remoteEC.entityName === 'blobs' && remoteEntityRow.content !== null) {
// 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
// is string note or note (syncs can arrive out of order)
remoteEntityRow.content = Buffer.from(remoteEntityRow.content, 'base64');
if (remoteEntityRow.content.byteLength === 0) {
// there seems to be a bug which causes empty buffer to be stored as NULL which is then picked up as inconsistency
// (possibly not a problem anymore with the newer better-sqlite3)
remoteEntityRow.content = "";
}
}
sql.transactional(() => {
sql.replace(remoteEntityChange.entityName, remoteEntityRow);
sql.replace(remoteEC.entityName, remoteEntityRow);
entityChangesService.addEntityChangeWithInstanceId(remoteEntityChange, instanceId);
});
entityChangesService.putEntityChangeWithInstanceId(remoteEC, instanceId);
return true;
} else if (localEC.hash !== remoteEC.hash && localEC.utcDateChanged > remoteEC.utcDateChanged) {
// the change on our side is newer than on the other side, so the other side should update
entityChangesService.putEntityChangeForOtherInstances(localEC);
return false;
}
return false;
}
function updateNoteReordering(entityChange, entity, instanceId) {
sql.transactional(() => {
for (const key in entity) {
sql.execute("UPDATE branches SET notePosition = ? WHERE branchId = ?", [entity[key], key]);
}
entityChangesService.addEntityChangeWithInstanceId(entityChange, instanceId);
});
return true;
}
function handleContent(content) {
// 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
// is string note or note (syncs can arrive out of order)
content = content === null ? null : Buffer.from(content, 'base64');
if (content && content.byteLength === 0) {
// there seems to be a bug which causes empty buffer to be stored as NULL which is then picked up as inconsistency
content = "";
function updateNoteReordering(remoteEC, remoteEntityRow, instanceId) {
for (const key in remoteEntityRow) {
sql.execute("UPDATE branches SET notePosition = ? WHERE branchId = ?", [remoteEntityRow[key], key]);
}
return content;
entityChangesService.putEntityChangeWithInstanceId(remoteEC, instanceId);
return true;
}
function eraseEntity(entityChange, instanceId) {
@@ -115,21 +94,19 @@ function eraseEntity(entityChange, instanceId) {
"attributes",
"revisions",
"attachments",
"blobs",
"blobs"
];
if (!entityNames.includes(entityName)) {
log.error(`Cannot erase entity '${entityName}', id '${entityId}'`);
log.error(`Cannot erase entity '${entityName}', id '${entityId}'.`);
return;
}
const keyName = entityConstructor.getEntityFromEntityName(entityName).primaryKeyName;
const primaryKeyName = entityConstructor.getEntityFromEntityName(entityName).primaryKeyName;
sql.execute(`DELETE FROM ${entityName} WHERE ${keyName} = ?`, [entityId]);
sql.execute(`DELETE FROM ${entityName} WHERE ${primaryKeyName} = ?`, [entityId]);
eventService.emit(eventService.ENTITY_DELETE_SYNCED, { entityName, entityId });
entityChangesService.addEntityChangeWithInstanceId(entityChange, instanceId);
entityChangesService.putEntityChangeWithInstanceId(entityChange, instanceId);
}
module.exports = {

View File

@@ -165,7 +165,7 @@ function sortNotes(parentNoteId, customSortBy = 'title', reverse = false, folder
}
if (someBranchUpdated) {
entityChangesService.addNoteReorderingEntityChange(parentNoteId);
entityChangesService.putNoteReorderingEntityChange(parentNoteId);
}
});
}

View File

@@ -28,8 +28,13 @@ function hashedBlobId(content) {
// sha512 is faster than sha256
const base64Hash = crypto.createHash('sha512').update(content).digest('base64');
// 20 characters of base64 gives us 120 bit of entropy which is plenty enough
return base64Hash.substr(0, 20);
// we don't want such + and / in the IDs
const kindaBase62Hash = base64Hash
.replace('+', 'X')
.replace('/', 'Y');
// 20 characters of base62 gives us ~120 bit of entropy which is plenty enough
return kindaBase62Hash.substr(0, 20);
}
function toBase64(plainText) {