Compare commits

...

38 Commits

Author SHA1 Message Date
zadam
d8730f1722 release 0.36.3 2019-11-05 21:49:16 +01:00
zadam
72fda89360 fix loading of the search note results 2019-11-05 21:47:22 +01:00
zadam
7075842954 expose bindGlobalShortcut to frontend script API 2019-11-05 21:26:54 +01:00
zadam
a2c78e2c5c disabling link map animation, closes #684 2019-11-05 20:59:20 +01:00
zadam
9c9ef1c7b4 added more careful handling of search note operations, fixes #683 2019-11-04 22:41:06 +01:00
zadam
df40accdd4 fix bug when context menu sometimes does not show up, closes #682 2019-11-04 20:20:21 +01:00
zadam
b67aa99b86 small fixes in context menu item visibility 2019-11-04 19:37:03 +01:00
zadam
fcb6a06d68 fix refreshing notes after import / delete
(cherry picked from commit 5b5ab0b044)
2019-11-03 18:34:07 +01:00
zadam
f7c59700a5 release 0.36.2 2019-10-31 22:04:44 +01:00
zadam
f1a7fce277 fix creating branch without isExpanded, fixes initial document initialization 2019-10-31 22:02:55 +01:00
zadam
1a182d1b58 final fix for the ws issue 2019-10-30 19:43:17 +01:00
zadam
8bd52f349a small refactorings and defensive programming in note cache 2019-10-29 20:54:08 +01:00
zadam
68faa47c6f fix not sending latest syncs to client 2019-10-29 20:19:28 +01:00
zadam
2f2a14d4b8 shoutouts in the README 2019-10-29 19:59:19 +01:00
zadam
a2a53deb94 fancytree 2.33.0 2019-10-29 19:07:05 +01:00
zadam
d50e072ea4 fix not-updating lastSyncId in ws handler 2019-10-29 19:03:39 +01:00
zadam
904eb25f64 fix persistent toasts lingering after disposal in DOM 2019-10-29 18:59:56 +01:00
zadam
205081c804 actual fix for the not disappearing toasts 2019-10-28 20:44:11 +01:00
zadam
c4d5060a0b fix non disappearing persistent toast 2019-10-28 20:26:40 +01:00
zadam
5bba18191f small fixes 2019-10-28 19:45:36 +01:00
zadam
5e3538669d sending ws messages doesn't need await 2019-10-28 18:42:22 +01:00
zadam
23c449ca0c fix ordering of new notes 2019-10-27 22:39:38 +01:00
zadam
b7344329f4 date note fixes 2019-10-27 22:13:38 +01:00
zadam
e04845335b fixed search notes with new treecache 2019-10-27 19:17:32 +01:00
zadam
86a330c8c3 delete notes are now in cache as well which allows simplified update of the tree after deletion 2019-10-26 22:50:46 +02:00
zadam
f82e99b5ed fix "clone note after" 2019-10-26 21:14:06 +02:00
zadam
bdf42749f3 fixes 2019-10-26 20:48:56 +02:00
zadam
7ccbf45569 update to ckeditor 15 2019-10-26 10:09:48 +02:00
zadam
c0b30e603a updated API docs 2019-10-26 10:00:26 +02:00
zadam
d3c957768f branches in tree cache should always be loaded if some branchId reference exists 2019-10-26 09:58:00 +02:00
zadam
ed9ecf2a57 simplification of tree cache 2019-10-26 09:51:08 +02:00
zadam
22d48b0586 sync update persistent notification 2019-10-25 22:20:14 +02:00
zadam
edc23940d0 promise for loaded note cache 2019-10-25 21:57:08 +02:00
zadam
c72ea2ed60 changes in retrieval of initial note tree and then updates 2019-10-25 21:47:14 +02:00
zadam
4570319517 WS debugging messages and small changes 2019-10-24 23:02:29 +02:00
zadam
69d739400c refresh notes/branches sorted 2019-10-22 22:27:32 +02:00
zadam
ec7c0f0723 added sync mutex to ping messages 2019-10-22 21:59:51 +02:00
zadam
3de124748d fix a bug where render note content could be set to null during renaming 2019-10-21 22:56:43 +02:00
65 changed files with 1876 additions and 980 deletions

View File

@@ -1,4 +1,4 @@
FROM node:12.12.0-alpine
FROM node:12.13.0-alpine
# Create app directory
WORKDIR /usr/src/app

View File

@@ -47,4 +47,11 @@ Or clone locally and run
```
npm install
npm run start-server
```
```
## Shoutouts
* [CKEditor 5](https://github.com/ckeditor/ckeditor5) - best WYSIWYG editor on the market, very interactive and listening team
* [FancyTree](https://github.com/mar10/fancytree) - very feature rich tree library without real competition. Trilium Notes would not be the same without it.
* [CodeMirror](https://github.com/codemirror/CodeMirror) - code editor with support for huge amount of languages
* [jsPlumb](https://github.com/jsplumb/jsplumb) - visual connectivity library without competition. Used in [relation maps](https://github.com/zadam/trilium/wiki/Relation-map) and [link maps](https://github.com/zadam/trilium/wiki/Link-map)

View File

@@ -1,7 +1,7 @@
#!/usr/bin/env bash
PKG_DIR=dist/trilium-linux-x64-server
NODE_VERSION=12.12.0
NODE_VERSION=12.13.0
if [ "$1" != "DONTCOPY" ]
then

View File

@@ -15,6 +15,7 @@ rm -r $SRC_DIR/node_modules/pngquant-bin/vendor/*
rm -r $SRC_DIR/node_modules/giflossy/vendor/*
rm -r $SRC_DIR/node_modules/@felixrieseberg/spellchecker/build/Release/*
rm -r $SRC_DIR/node_modules/keyboard-layout/build/Release/*
rm -r $SRC_DIR/node_modules/cld/build/Release/*
cp -r bin/deps/win-x64/sqlite/* $SRC_DIR/node_modules/sqlite3/lib/binding/
cp bin/deps/win-x64/image/cjpeg.exe $SRC_DIR/node_modules/mozjpeg/vendor/

View File

@@ -2,7 +2,7 @@
echo "Deleting existing builds"
rm -r dist/*
rm -rf dist/*
SRC_DIR=dist/trilium-src

View File

@@ -73,6 +73,10 @@ class Branch extends Entity {
this.notePosition = maxNotePos === null ? 0 : maxNotePos + 10;
}
if (!this.isExpanded) {
this.isExpanded = false;
}
if (!this.isDeleted) {
this.isDeleted = false;
}

View File

@@ -1240,6 +1240,162 @@
<h4 class="name" id="bindGlobalShortcut"><span class="type-signature"></span>bindGlobalShortcut<span class="signature">(keyboardShortcut, handler)</span><span class="type-signature"></span></h4>
<h5>Parameters:</h5>
<table class="params">
<thead>
<tr>
<th>Name</th>
<th>Type</th>
<th class="last">Description</th>
</tr>
</thead>
<tbody>
<tr>
<td class="name"><code>keyboardShortcut</code></td>
<td class="type">
<span class="param-type">string</span>
</td>
<td class="description last">e.g. "ctrl+shift+a"</td>
</tr>
<tr>
<td class="name"><code>handler</code></td>
<td class="type">
<span class="param-type">function</span>
</td>
<td class="description last"></td>
</tr>
</tbody>
</table>
<dl class="details">
<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#line368">line 368</a>
</li></ul></dd>
</dl>
@@ -1390,7 +1546,7 @@
<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#line279">line 279</a>
<a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line275">line 275</a>
</li></ul></dd>
@@ -1523,7 +1679,7 @@
<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#line239">line 239</a>
<a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line235">line 235</a>
</li></ul></dd>
@@ -1629,7 +1785,7 @@
<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#line285">line 285</a>
<a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line281">line 281</a>
</li></ul></dd>
@@ -1735,7 +1891,7 @@
<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#line291">line 291</a>
<a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line287">line 287</a>
</li></ul></dd>
@@ -1894,7 +2050,7 @@
<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#line338">line 338</a>
<a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line334">line 334</a>
</li></ul></dd>
@@ -2001,7 +2157,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#line232">line 232</a>
<a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line228">line 228</a>
</li></ul></dd>
@@ -2156,7 +2312,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#line347">line 347</a>
<a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line343">line 343</a>
</li></ul></dd>
@@ -2619,7 +2775,7 @@ otherwise (by e.g. createNoteLink())
<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#line329">line 329</a>
<a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line325">line 325</a>
</li></ul></dd>
@@ -2774,7 +2930,7 @@ otherwise (by e.g. createNoteLink())
<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#line356">line 356</a>
<a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line352">line 352</a>
</li></ul></dd>
@@ -2883,7 +3039,7 @@ note.
<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#line302">line 302</a>
<a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line298">line 298</a>
</li></ul></dd>
@@ -3038,7 +3194,7 @@ note.
<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#line310">line 310</a>
<a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line306">line 306</a>
</li></ul></dd>
@@ -3171,7 +3327,7 @@ note.
<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#line246">line 246</a>
<a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line242">line 242</a>
</li></ul></dd>
@@ -3277,7 +3433,7 @@ note.
<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#line321">line 321</a>
<a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line317">line 317</a>
</li></ul></dd>
@@ -3365,7 +3521,7 @@ note.
<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#line270">line 270</a>
<a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line266">line 266</a>
</li></ul></dd>
@@ -3419,13 +3575,17 @@ note.
<h4 class="name" id="reloadNotesAndTheirChildren"><span class="type-signature"></span>reloadNotesAndTheirChildren<span class="signature">(noteId)</span><span class="type-signature"></span></h4>
<h4 class="name" id="reloadNotes"><span class="type-signature"></span>reloadNotes<span class="signature">(noteIds)</span><span class="type-signature"></span></h4>
<div class="description">
Update frontend tree (note) cache from the backend.
</div>
@@ -3459,13 +3619,13 @@ note.
<tr>
<td class="name"><code>noteId</code></td>
<td class="name"><code>noteIds</code></td>
<td class="type">
<span class="param-type">string</span>
<span class="param-type">Array.&lt;string></span>
@@ -3516,140 +3676,7 @@ note.
<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#line218">line 218</a>
</li></ul></dd>
</dl>
<h4 class="name" id="reloadParents"><span class="type-signature"></span>reloadParents<span class="signature">(noteId)</span><span class="type-signature"></span></h4>
<h5>Parameters:</h5>
<table class="params">
<thead>
<tr>
<th>Name</th>
<th>Type</th>
<th class="last">Description</th>
</tr>
</thead>
<tbody>
<tr>
<td class="name"><code>noteId</code></td>
<td class="type">
<span class="param-type">string</span>
</td>
<td class="description last"></td>
</tr>
</tbody>
</table>
<dl class="details">
<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#line224">line 224</a>
<a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line220">line 220</a>
</li></ul></dd>
@@ -4281,7 +4308,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#line365">line 365</a>
<a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line361">line 361</a>
</li></ul></dd>
@@ -4432,7 +4459,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#line316">line 316</a>
<a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line312">line 312</a>
</li></ul></dd>
@@ -4569,7 +4596,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#line262">line 262</a>
<a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line258">line 258</a>
</li></ul></dd>
@@ -4706,7 +4733,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#line254">line 254</a>
<a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line250">line 250</a>
</li></ul></dd>

View File

@@ -28,7 +28,7 @@
<header>
<h2><span class="attribs"><span class="type-signature"></span></span>NoteShort<span class="signature">()</span><span class="type-signature"></span></h2>
<h2><span class="attribs"><span class="type-signature"></span></span>NoteShort<span class="signature">(treeCache, row, branches)</span><span class="type-signature"></span></h2>
<div class="class-description">FIXME: rethink how attributes are cached in Note entities since they are long lived inside the cache.
Attribute cache should be limited to "transaction".
@@ -48,7 +48,7 @@ This note's representation is used in note tree and is kept in TreeCache.</div>
<h4 class="name" id="NoteShort"><span class="type-signature"></span>new NoteShort<span class="signature">()</span><span class="type-signature"></span></h4>
<h4 class="name" id="NoteShort"><span class="type-signature"></span>new NoteShort<span class="signature">(treeCache, row, branches)</span><span class="type-signature"></span></h4>
@@ -63,6 +63,101 @@ This note's representation is used in note tree and is kept in TreeCache.</div>
<h5>Parameters:</h5>
<table class="params">
<thead>
<tr>
<th>Name</th>
<th>Type</th>
<th class="last">Description</th>
</tr>
</thead>
<tbody>
<tr>
<td class="name"><code>treeCache</code></td>
<td class="type">
<span class="param-type">TreeCache</span>
</td>
<td class="description last"></td>
</tr>
<tr>
<td class="name"><code>row</code></td>
<td class="type">
<span class="param-type">Object.&lt;string, Object></span>
</td>
<td class="description last"></td>
</tr>
<tr>
<td class="name"><code>branches</code></td>
<td class="type">
<span class="param-type">Array.&lt;<a href="Branch.html">Branch</a>></span>
</td>
<td class="description last">all relevant branches, i.e. where this note is either child or parent</td>
</tr>
</tbody>
</table>
@@ -96,7 +191,7 @@ This note's representation is used in note tree and is kept in TreeCache.</div>
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_note_short.js.html">entities/note_short.js</a>, <a href="entities_note_short.js.html#line16">line 16</a>
<a href="entities_note_short.js.html">entities/note_short.js</a>, <a href="entities_note_short.js.html#line22">line 22</a>
</li></ul></dd>
@@ -186,7 +281,143 @@ This note's representation is used in note tree and is kept in TreeCache.</div>
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_note_short.js.html">entities/note_short.js</a>, <a href="entities_note_short.js.html#line29">line 29</a>
<a href="entities_note_short.js.html">entities/note_short.js</a>, <a href="entities_note_short.js.html#line37">line 37</a>
</li></ul></dd>
</dl>
<h4 class="name" id="children"><span class="type-signature"></span>children<span class="type-signature"> :Array.&lt;string></span></h4>
<h5>Type:</h5>
<ul>
<li>
<span class="param-type">Array.&lt;string></span>
</li>
</ul>
<dl class="details">
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_note_short.js.html">entities/note_short.js</a>, <a href="entities_note_short.js.html#line44">line 44</a>
</li></ul></dd>
</dl>
<h4 class="name" id="childToBranch"><span class="type-signature"></span>childToBranch<span class="type-signature"> :Object.&lt;string, string></span></h4>
<h5>Type:</h5>
<ul>
<li>
<span class="param-type">Object.&lt;string, string></span>
</li>
</ul>
<dl class="details">
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_note_short.js.html">entities/note_short.js</a>, <a href="entities_note_short.js.html#line50">line 50</a>
</li></ul></dd>
@@ -244,7 +475,65 @@ This note's representation is used in note tree and is kept in TreeCache.</div>
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_note_short.js.html">entities/note_short.js</a>, <a href="entities_note_short.js.html#line31">line 31</a>
<a href="entities_note_short.js.html">entities/note_short.js</a>, <a href="entities_note_short.js.html#line39">line 39</a>
</li></ul></dd>
</dl>
<h4 class="name" id="isDeleted"><span class="type-signature"></span>isDeleted<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="entities_note_short.js.html">entities/note_short.js</a>, <a href="entities_note_short.js.html#line35">line 35</a>
</li></ul></dd>
@@ -302,7 +591,7 @@ This note's representation is used in note tree and is kept in TreeCache.</div>
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_note_short.js.html">entities/note_short.js</a>, <a href="entities_note_short.js.html#line23">line 23</a>
<a href="entities_note_short.js.html">entities/note_short.js</a>, <a href="entities_note_short.js.html#line29">line 29</a>
</li></ul></dd>
@@ -360,7 +649,7 @@ This note's representation is used in note tree and is kept in TreeCache.</div>
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_note_short.js.html">entities/note_short.js</a>, <a href="entities_note_short.js.html#line27">line 27</a>
<a href="entities_note_short.js.html">entities/note_short.js</a>, <a href="entities_note_short.js.html#line33">line 33</a>
</li></ul></dd>
@@ -418,7 +707,143 @@ This note's representation is used in note tree and is kept in TreeCache.</div>
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_note_short.js.html">entities/note_short.js</a>, <a href="entities_note_short.js.html#line19">line 19</a>
<a href="entities_note_short.js.html">entities/note_short.js</a>, <a href="entities_note_short.js.html#line25">line 25</a>
</li></ul></dd>
</dl>
<h4 class="name" id="parents"><span class="type-signature"></span>parents<span class="type-signature"> :Array.&lt;string></span></h4>
<h5>Type:</h5>
<ul>
<li>
<span class="param-type">Array.&lt;string></span>
</li>
</ul>
<dl class="details">
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_note_short.js.html">entities/note_short.js</a>, <a href="entities_note_short.js.html#line42">line 42</a>
</li></ul></dd>
</dl>
<h4 class="name" id="parentToBranch"><span class="type-signature"></span>parentToBranch<span class="type-signature"> :Object.&lt;string, string></span></h4>
<h5>Type:</h5>
<ul>
<li>
<span class="param-type">Object.&lt;string, string></span>
</li>
</ul>
<dl class="details">
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_note_short.js.html">entities/note_short.js</a>, <a href="entities_note_short.js.html#line47">line 47</a>
</li></ul></dd>
@@ -476,7 +901,7 @@ This note's representation is used in note tree and is kept in TreeCache.</div>
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_note_short.js.html">entities/note_short.js</a>, <a href="entities_note_short.js.html#line21">line 21</a>
<a href="entities_note_short.js.html">entities/note_short.js</a>, <a href="entities_note_short.js.html#line27">line 27</a>
</li></ul></dd>
@@ -534,7 +959,7 @@ This note's representation is used in note tree and is kept in TreeCache.</div>
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_note_short.js.html">entities/note_short.js</a>, <a href="entities_note_short.js.html#line25">line 25</a>
<a href="entities_note_short.js.html">entities/note_short.js</a>, <a href="entities_note_short.js.html#line31">line 31</a>
</li></ul></dd>
@@ -682,7 +1107,7 @@ This note's representation is used in note tree and is kept in TreeCache.</div>
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_note_short.js.html">entities/note_short.js</a>, <a href="entities_note_short.js.html#line169">line 169</a>
<a href="entities_note_short.js.html">entities/note_short.js</a>, <a href="entities_note_short.js.html#line220">line 220</a>
</li></ul></dd>
@@ -849,7 +1274,7 @@ This note's representation is used in note tree and is kept in TreeCache.</div>
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_note_short.js.html">entities/note_short.js</a>, <a href="entities_note_short.js.html#line109">line 109</a>
<a href="entities_note_short.js.html">entities/note_short.js</a>, <a href="entities_note_short.js.html#line160">line 160</a>
</li></ul></dd>
@@ -1023,7 +1448,7 @@ This note's representation is used in note tree and is kept in TreeCache.</div>
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_note_short.js.html">entities/note_short.js</a>, <a href="entities_note_short.js.html#line180">line 180</a>
<a href="entities_note_short.js.html">entities/note_short.js</a>, <a href="entities_note_short.js.html#line231">line 231</a>
</li></ul></dd>
@@ -1129,7 +1554,7 @@ This note's representation is used in note tree and is kept in TreeCache.</div>
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_note_short.js.html">entities/note_short.js</a>, <a href="entities_note_short.js.html#line60">line 60</a>
<a href="entities_note_short.js.html">entities/note_short.js</a>, <a href="entities_note_short.js.html#line117">line 117</a>
</li></ul></dd>
@@ -1231,7 +1656,7 @@ This note's representation is used in note tree and is kept in TreeCache.</div>
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_note_short.js.html">entities/note_short.js</a>, <a href="entities_note_short.js.html#line74">line 74</a>
<a href="entities_note_short.js.html">entities/note_short.js</a>, <a href="entities_note_short.js.html#line129">line 129</a>
</li></ul></dd>
@@ -1333,7 +1758,7 @@ This note's representation is used in note tree and is kept in TreeCache.</div>
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_note_short.js.html">entities/note_short.js</a>, <a href="entities_note_short.js.html#line96">line 96</a>
<a href="entities_note_short.js.html">entities/note_short.js</a>, <a href="entities_note_short.js.html#line147">line 147</a>
</li></ul></dd>
@@ -1435,7 +1860,7 @@ This note's representation is used in note tree and is kept in TreeCache.</div>
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_note_short.js.html">entities/note_short.js</a>, <a href="entities_note_short.js.html#line101">line 101</a>
<a href="entities_note_short.js.html">entities/note_short.js</a>, <a href="entities_note_short.js.html#line152">line 152</a>
</li></ul></dd>
@@ -1586,7 +2011,7 @@ This note's representation is used in note tree and is kept in TreeCache.</div>
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_note_short.js.html">entities/note_short.js</a>, <a href="entities_note_short.js.html#line202">line 202</a>
<a href="entities_note_short.js.html">entities/note_short.js</a>, <a href="entities_note_short.js.html#line253">line 253</a>
</li></ul></dd>
@@ -1753,7 +2178,7 @@ This note's representation is used in note tree and is kept in TreeCache.</div>
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_note_short.js.html">entities/note_short.js</a>, <a href="entities_note_short.js.html#line135">line 135</a>
<a href="entities_note_short.js.html">entities/note_short.js</a>, <a href="entities_note_short.js.html#line186">line 186</a>
</li></ul></dd>
@@ -1920,7 +2345,7 @@ This note's representation is used in note tree and is kept in TreeCache.</div>
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_note_short.js.html">entities/note_short.js</a>, <a href="entities_note_short.js.html#line127">line 127</a>
<a href="entities_note_short.js.html">entities/note_short.js</a>, <a href="entities_note_short.js.html#line178">line 178</a>
</li></ul></dd>
@@ -2075,7 +2500,7 @@ This note's representation is used in note tree and is kept in TreeCache.</div>
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_note_short.js.html">entities/note_short.js</a>, <a href="entities_note_short.js.html#line214">line 214</a>
<a href="entities_note_short.js.html">entities/note_short.js</a>, <a href="entities_note_short.js.html#line265">line 265</a>
</li></ul></dd>
@@ -2181,7 +2606,7 @@ This note's representation is used in note tree and is kept in TreeCache.</div>
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_note_short.js.html">entities/note_short.js</a>, <a href="entities_note_short.js.html#line86">line 86</a>
<a href="entities_note_short.js.html">entities/note_short.js</a>, <a href="entities_note_short.js.html#line137">line 137</a>
</li></ul></dd>
@@ -2283,7 +2708,7 @@ This note's representation is used in note tree and is kept in TreeCache.</div>
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_note_short.js.html">entities/note_short.js</a>, <a href="entities_note_short.js.html#line91">line 91</a>
<a href="entities_note_short.js.html">entities/note_short.js</a>, <a href="entities_note_short.js.html#line142">line 142</a>
</li></ul></dd>
@@ -2434,7 +2859,7 @@ This note's representation is used in note tree and is kept in TreeCache.</div>
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_note_short.js.html">entities/note_short.js</a>, <a href="entities_note_short.js.html#line208">line 208</a>
<a href="entities_note_short.js.html">entities/note_short.js</a>, <a href="entities_note_short.js.html#line259">line 259</a>
</li></ul></dd>
@@ -2601,7 +3026,7 @@ This note's representation is used in note tree and is kept in TreeCache.</div>
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_note_short.js.html">entities/note_short.js</a>, <a href="entities_note_short.js.html#line151">line 151</a>
<a href="entities_note_short.js.html">entities/note_short.js</a>, <a href="entities_note_short.js.html#line202">line 202</a>
</li></ul></dd>
@@ -2768,7 +3193,7 @@ This note's representation is used in note tree and is kept in TreeCache.</div>
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_note_short.js.html">entities/note_short.js</a>, <a href="entities_note_short.js.html#line143">line 143</a>
<a href="entities_note_short.js.html">entities/note_short.js</a>, <a href="entities_note_short.js.html#line194">line 194</a>
</li></ul></dd>
@@ -2923,7 +3348,7 @@ This note's representation is used in note tree and is kept in TreeCache.</div>
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_note_short.js.html">entities/note_short.js</a>, <a href="entities_note_short.js.html#line226">line 226</a>
<a href="entities_note_short.js.html">entities/note_short.js</a>, <a href="entities_note_short.js.html#line277">line 277</a>
</li></ul></dd>
@@ -3093,7 +3518,7 @@ This note's representation is used in note tree and is kept in TreeCache.</div>
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_note_short.js.html">entities/note_short.js</a>, <a href="entities_note_short.js.html#line236">line 236</a>
<a href="entities_note_short.js.html">entities/note_short.js</a>, <a href="entities_note_short.js.html#line287">line 287</a>
</li></ul></dd>
@@ -3244,7 +3669,7 @@ This note's representation is used in note tree and is kept in TreeCache.</div>
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_note_short.js.html">entities/note_short.js</a>, <a href="entities_note_short.js.html#line220">line 220</a>
<a href="entities_note_short.js.html">entities/note_short.js</a>, <a href="entities_note_short.js.html#line271">line 271</a>
</li></ul></dd>
@@ -3354,7 +3779,7 @@ This note's representation is used in note tree and is kept in TreeCache.</div>
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_note_short.js.html">entities/note_short.js</a>, <a href="entities_note_short.js.html#line260">line 260</a>
<a href="entities_note_short.js.html">entities/note_short.js</a>, <a href="entities_note_short.js.html#line311">line 311</a>
</li></ul></dd>
@@ -3528,7 +3953,7 @@ This note's representation is used in note tree and is kept in TreeCache.</div>
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_note_short.js.html">entities/note_short.js</a>, <a href="entities_note_short.js.html#line160">line 160</a>
<a href="entities_note_short.js.html">entities/note_short.js</a>, <a href="entities_note_short.js.html#line211">line 211</a>
</li></ul></dd>
@@ -3634,7 +4059,7 @@ This note's representation is used in note tree and is kept in TreeCache.</div>
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_note_short.js.html">entities/note_short.js</a>, <a href="entities_note_short.js.html#line68">line 68</a>
<a href="entities_note_short.js.html">entities/note_short.js</a>, <a href="entities_note_short.js.html#line124">line 124</a>
</li></ul></dd>
@@ -3785,7 +4210,7 @@ This note's representation is used in note tree and is kept in TreeCache.</div>
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_note_short.js.html">entities/note_short.js</a>, <a href="entities_note_short.js.html#line190">line 190</a>
<a href="entities_note_short.js.html">entities/note_short.js</a>, <a href="entities_note_short.js.html#line241">line 241</a>
</li></ul></dd>
@@ -3940,7 +4365,7 @@ This note's representation is used in note tree and is kept in TreeCache.</div>
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_note_short.js.html">entities/note_short.js</a>, <a href="entities_note_short.js.html#line196">line 196</a>
<a href="entities_note_short.js.html">entities/note_short.js</a>, <a href="entities_note_short.js.html#line247">line 247</a>
</li></ul></dd>
@@ -4051,7 +4476,7 @@ Cache is note instance scoped.
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_note_short.js.html">entities/note_short.js</a>, <a href="entities_note_short.js.html#line251">line 251</a>
<a href="entities_note_short.js.html">entities/note_short.js</a>, <a href="entities_note_short.js.html#line302">line 302</a>
</li></ul></dd>
@@ -4135,7 +4560,7 @@ Cache is note instance scoped.
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_note_short.js.html">entities/note_short.js</a>, <a href="entities_note_short.js.html#line35">line 35</a>
<a href="entities_note_short.js.html">entities/note_short.js</a>, <a href="entities_note_short.js.html#line92">line 92</a>
</li></ul></dd>

View File

@@ -32,8 +32,8 @@
* Represents full note, specifically including note's content.
*/
class NoteFull extends NoteShort {
constructor(treeCache, row) {
super(treeCache, row);
constructor(treeCache, row, noteShort) {
super(treeCache, row, []);
/** @param {string} */
this.content = row.content;
@@ -49,6 +49,12 @@ class NoteFull extends NoteShort {
/** @param {string} */
this.utcDateModified = row.utcDateModified;
/* ugly */
this.parents = noteShort.parents;
this.parentToBranch = noteShort.parentToBranch;
this.children = noteShort.children;
this.childToBranch = noteShort.childToBranch;
}
}

View File

@@ -28,6 +28,7 @@
<article>
<pre class="prettyprint source linenums"><code>import server from '../services/server.js';
import Attribute from './attribute.js';
import branches from "../services/branches.js";
const LABEL = 'label';
const LABEL_DEFINITION = 'label-definition';
@@ -41,7 +42,12 @@ const RELATION_DEFINITION = 'relation-definition';
* This note's representation is used in note tree and is kept in TreeCache.
*/
class NoteShort {
constructor(treeCache, row) {
/**
* @param {TreeCache} treeCache
* @param {Object.&lt;string, Object>} row
* @param {Branch[]} branches - all relevant branches, i.e. where this note is either child or parent
*/
constructor(treeCache, row, branches) {
this.treeCache = treeCache;
/** @param {string} */
this.noteId = row.noteId;
@@ -54,9 +60,60 @@ class NoteShort {
/** @param {string} content-type, e.g. "application/json" */
this.mime = row.mime;
/** @param {boolean} */
this.isDeleted = row.isDeleted;
/** @param {boolean} */
this.archived = row.archived;
/** @param {string} */
this.cssClass = row.cssClass;
/** @type {string[]} */
this.parents = [];
/** @type {string[]} */
this.children = [];
/** @type {Object.&lt;string, string>} */
this.parentToBranch = {};
/** @type {Object.&lt;string, string>} */
this.childToBranch = {};
for (const branch of branches) {
if (this.noteId === branch.noteId) {
this.parents.push(branch.parentNoteId);
this.parentToBranch[branch.parentNoteId] = branch.branchId;
}
else if (this.noteId === branch.parentNoteId) {
this.children.push(branch.noteId);
this.childToBranch[branch.noteId] = branch.branchId;
}
else {
throw new Error(`Unknown branch ${branch.branchId} for note ${this.noteId}`);
}
}
}
addParent(parentNoteId, branchId) {
if (!this.parents.includes(parentNoteId)) {
this.parents.push(parentNoteId);
}
this.parentToBranch[parentNoteId] = branchId;
}
addChild(childNoteId, branchId) {
if (!this.children.includes(childNoteId)) {
this.children.push(childNoteId);
}
this.childToBranch[childNoteId] = branchId;
const branchIdPos = {};
for (const branchId of Object.values(this.childToBranch)) {
branchIdPos[branchId] = this.treeCache.getBranch(branchId).notePosition;
}
this.children.sort((a, b) => branchIdPos[this.childToBranch[a]] &lt; branchIdPos[this.childToBranch[b]] ? -1 : 1);
}
/** @returns {boolean} */
@@ -86,48 +143,42 @@ class NoteShort {
/** @returns {Promise&lt;Branch[]>} */
async getBranches() {
const branchIds = this.treeCache.parents[this.noteId].map(
parentNoteId => this.treeCache.getBranchIdByChildParent(this.noteId, parentNoteId));
const branchIds = Object.values(this.parentToBranch);
return this.treeCache.getBranches(branchIds);
}
/** @returns {boolean} */
hasChildren() {
return this.treeCache.children[this.noteId]
&amp;&amp; this.treeCache.children[this.noteId].length > 0;
return this.children.length > 0;
}
/** @returns {Promise&lt;Branch[]>} */
async getChildBranches() {
if (!this.treeCache.children[this.noteId]) {
return [];
}
// don't use Object.values() to guarantee order
const branchIds = this.children.map(childNoteId => this.childToBranch[childNoteId]);
const branchIds = this.treeCache.children[this.noteId].map(
childNoteId => this.treeCache.getBranchIdByChildParent(childNoteId, this.noteId));
return await this.treeCache.getBranches(branchIds);
return this.treeCache.getBranches(branchIds);
}
/** @returns {string[]} */
getParentNoteIds() {
return this.treeCache.parents[this.noteId] || [];
return this.parents;
}
/** @returns {Promise&lt;NoteShort[]>} */
async getParentNotes() {
return await this.treeCache.getNotes(this.getParentNoteIds());
return await this.treeCache.getNotes(this.parents);
}
/** @returns {string[]} */
getChildNoteIds() {
return this.treeCache.children[this.noteId] || [];
return this.children;
}
/** @returns {Promise&lt;NoteShort[]>} */
async getChildNotes() {
return await this.treeCache.getNotes(this.getChildNoteIds());
return await this.treeCache.getNotes(this.children);
}
/**

View File

@@ -240,16 +240,12 @@ function FrontendScriptApi(startNote, currentNote, originEntity = null, tabConte
this.getNotes = async (noteIds, silentNotFoundError = false) => await treeCache.getNotes(noteIds, silentNotFoundError);
/**
* @param {string} noteId
* Update frontend tree (note) cache from the backend.
*
* @param {string[]} noteIds
* @method
*/
this.reloadNotesAndTheirChildren = async noteId => await treeCache.reloadNotesAndTheirChildren(noteId);
/**
* @param {string} noteId
* @method
*/
this.reloadParents = async noteId => await treeCache.reloadParents(noteId);
this.reloadNotes = async noteIds => await treeCache.reloadNotes(noteIds);
/**
* Instance name identifies particular Trilium instance. It can be useful for scripts
@@ -391,6 +387,13 @@ function FrontendScriptApi(startNote, currentNote, originEntity = null, tabConte
* @return {Promise}
*/
this.setHoistedNoteId = hoistedNoteService.setHoistedNoteId;
/**
* @method
* @param {string} keyboardShortcut - e.g. "ctrl+shift+a"
* @param {function} handler
*/
this.bindGlobalShortcut = utils.bindGlobalShortcut;
}
export default FrontendScriptApi;</code></pre>

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

View File

@@ -1,4 +1,4 @@
/*! jQuery Fancytree Plugin - 2.32.0 - 2019-09-10T07:42:12Z
/*! jQuery Fancytree Plugin - 2.33.0 - 2019-10-29T08:00:07Z
* https://github.com/mar10/fancytree
* Copyright (c) 2019 Martin Wendt; Licensed MIT
*/
@@ -1365,8 +1365,8 @@ var uniqueId = $.fn.extend( {
* Released under the MIT license
* https://github.com/mar10/fancytree/wiki/LicenseInfo
*
* @version 2.32.0
* @date 2019-09-10T07:42:12Z
* @version 2.33.0
* @date 2019-10-29T08:00:07Z
*/
/** Core Fancytree module.
@@ -2947,15 +2947,7 @@ var uniqueId = $.fn.extend( {
// i.e. return false for nodes (but not parents) that are hidden
// by a filter
if (hasFilter && !this.match && !this.subMatchCount) {
this.debug(
"isVisible: HIDDEN (" +
hasFilter +
", " +
this.match +
", " +
this.match +
")"
);
// this.debug( "isVisible: HIDDEN (" + hasFilter + ", " + this.match + ", " + this.match + ")" );
return false;
}
@@ -2963,7 +2955,7 @@ var uniqueId = $.fn.extend( {
n = parents[i];
if (!n.expanded) {
this.debug("isVisible: HIDDEN (parent collapsed)");
// this.debug("isVisible: HIDDEN (parent collapsed)");
return false;
}
// if (hasFilter && !n.match && !n.subMatchCount) {
@@ -2971,7 +2963,7 @@ var uniqueId = $.fn.extend( {
// return false;
// }
}
this.debug("isVisible: VISIBLE");
// this.debug("isVisible: VISIBLE");
return true;
},
/** Deprecated.
@@ -4788,6 +4780,10 @@ var uniqueId = $.fn.extend( {
},
/**
* Return an array of selected nodes.
*
* Note: you cannot send this result via Ajax directly. Instead the
* node object need to be converted to plain objects, for example
* by using `$.map()` and `node.toDict()`.
* @param {boolean} [stopOnParents=false] only return the topmost selected
* node (useful with selectMode 3)
* @returns {FancytreeNode[]}
@@ -6714,7 +6710,13 @@ var uniqueId = $.fn.extend( {
dfd.done(function() {
var lastChild = node.getLastChild();
if (flag && opts.autoScroll && !noAnimation && lastChild) {
if (
flag &&
opts.autoScroll &&
!noAnimation &&
lastChild &&
tree._enableUpdate
) {
// Scroll down to last child, but keep current node visible
lastChild
.scrollIntoView(true, { topNode: node })
@@ -6773,9 +6775,7 @@ var uniqueId = $.fn.extend( {
$(node.li).addClass(cn.animating); // #717
if ($.isFunction($(node.ul)[effect.effect])) {
tree.debug(
"use jquery." + effect.effect + " method"
);
// tree.debug( "use jquery." + effect.effect + " method" );
$(node.ul)[effect.effect]({
duration: effect.duration,
always: function() {
@@ -7263,16 +7263,30 @@ var uniqueId = $.fn.extend( {
type = $container.data("type") || "html";
switch (type) {
case "html":
$ul = $container.find(">ul").first();
$ul.addClass(
"ui-fancytree-source fancytree-helper-hidden"
);
source = $.ui.fancytree.parseHtml($ul);
// allow to init tree.data.foo from <ul data-foo=''>
this.data = $.extend(
this.data,
_getElementDataAsDict($ul)
);
// There should be an embedded `<ul>` with initial nodes,
// but another `<ul class='fancytree-container'>` is appended
// to the tree's <div> on startup anyway.
$ul = $container
.find(">ul")
.not(".fancytree-container")
.first();
if ($ul.length) {
$ul.addClass(
"ui-fancytree-source fancytree-helper-hidden"
);
source = $.ui.fancytree.parseHtml($ul);
// allow to init tree.data.foo from <ul data-foo=''>
this.data = $.extend(
this.data,
_getElementDataAsDict($ul)
);
} else {
FT.warn(
"No `source` option was passed and container does not contain `<ul>`: assuming `source: []`."
);
source = [];
}
break;
case "json":
source = $.parseJSON($container.text());
@@ -7312,8 +7326,9 @@ var uniqueId = $.fn.extend( {
$.error("Not implemented");
}
// TODO: might be useful? Let's wait for a use case...
// tree._triggerTreeEvent("beforeInitLoad", null);
// preInit is fired when the widget markup is created, but nodes
// not yet loaded
tree._triggerTreeEvent("preInit", null);
// Trigger fancytreeinit after nodes have been loaded
dfd = this.nodeLoadChildren(rootCtx, source)
@@ -7927,7 +7942,7 @@ var uniqueId = $.fn.extend( {
/** @lends Fancytree_Static# */
{
/** @type {string} */
version: "2.32.0", // Set to semver by 'grunt release'
version: "2.33.0", // Set to semver by 'grunt release'
/** @type {string} */
buildType: "production", // Set to 'production' by 'grunt build'
/** @type {int} */
@@ -8550,8 +8565,8 @@ var uniqueId = $.fn.extend( {
* Released under the MIT license
* https://github.com/mar10/fancytree/wiki/LicenseInfo
*
* @version 2.32.0
* @date 2019-09-10T07:42:12Z
* @version 2.33.0
* @date 2019-10-29T08:00:07Z
*/
// To keep the global namespace clean, we wrap everything in a closure.
@@ -8670,7 +8685,7 @@ var uniqueId = $.fn.extend( {
// Every extension must be registered by a unique name.
name: "childcounter",
// Version information should be compliant with [semver](http://semver.org)
version: "2.32.0",
version: "2.33.0",
// Extension specific options and their defaults.
// This options will be available as `tree.options.childcounter.hideExpanded`
@@ -8781,8 +8796,8 @@ var uniqueId = $.fn.extend( {
* Released under the MIT license
* https://github.com/mar10/fancytree/wiki/LicenseInfo
*
* @version 2.32.0
* @date 2019-09-10T07:42:12Z
* @version 2.33.0
* @date 2019-10-29T08:00:07Z
*/
(function(factory) {
@@ -9140,7 +9155,7 @@ var uniqueId = $.fn.extend( {
*/
$.ui.fancytree.registerExtension({
name: "clones",
version: "2.32.0",
version: "2.33.0",
// Default options for this extension.
options: {
highlightActiveClones: true, // set 'fancytree-active-clone' on active clones and all peers
@@ -9302,8 +9317,8 @@ var uniqueId = $.fn.extend( {
* Released under the MIT license
* https://github.com/mar10/fancytree/wiki/LicenseInfo
*
* @version 2.32.0
* @date 2019-09-10T07:42:12Z
* @version 2.33.0
* @date 2019-10-29T08:00:07Z
*/
/*
@@ -10123,6 +10138,7 @@ var uniqueId = $.fn.extend( {
// ": dropEffect: " +
// dataTransfer.dropEffect
// );
prepareDropEffectCallback(event, data);
LAST_HIT_MODE = handleDragOver(event, data);
// The flag controls the preventDefault() below:
@@ -10306,7 +10322,7 @@ var uniqueId = $.fn.extend( {
$.ui.fancytree.registerExtension({
name: "dnd5",
version: "2.32.0",
version: "2.33.0",
// Default options for this extension.
options: {
autoExpandMS: 1500, // Expand nodes after n milliseconds of hovering
@@ -10439,8 +10455,8 @@ var uniqueId = $.fn.extend( {
* Released under the MIT license
* https://github.com/mar10/fancytree/wiki/LicenseInfo
*
* @version 2.32.0
* @date 2019-09-10T07:42:12Z
* @version 2.33.0
* @date 2019-10-29T08:00:07Z
*/
(function(factory) {
@@ -10732,7 +10748,7 @@ var uniqueId = $.fn.extend( {
*/
$.ui.fancytree.registerExtension({
name: "edit",
version: "2.32.0",
version: "2.33.0",
// Default options for this extension.
options: {
adjustWidthOfs: 4, // null: don't adjust input size to content
@@ -10843,8 +10859,8 @@ var uniqueId = $.fn.extend( {
* Released under the MIT license
* https://github.com/mar10/fancytree/wiki/LicenseInfo
*
* @version 2.32.0
* @date 2019-09-10T07:42:12Z
* @version 2.33.0
* @date 2019-10-29T08:00:07Z
*/
(function(factory) {
@@ -11008,7 +11024,9 @@ var uniqueId = $.fn.extend( {
count++;
node.match = true;
node.visitParents(function(p) {
p.subMatchCount += 1;
if (p !== node) {
p.subMatchCount += 1;
}
// Expand match (unless this is no real match, but only a node in a matched branch)
if (opts.autoExpand && !matchedByBranch && !p.expanded) {
p.setExpanded(true, {
@@ -11180,7 +11198,7 @@ var uniqueId = $.fn.extend( {
*/
$.ui.fancytree.registerExtension({
name: "filter",
version: "2.32.0",
version: "2.33.0",
// Default options for this extension.
options: {
autoApply: true, // Re-apply last filter if lazy data is loaded
@@ -11300,8 +11318,8 @@ var uniqueId = $.fn.extend( {
* Released under the MIT license
* https://github.com/mar10/fancytree/wiki/LicenseInfo
*
* @version 2.32.0
* @date 2019-09-10T07:42:12Z
* @version 2.33.0
* @date 2019-10-29T08:00:07Z
*/
(function(factory) {
@@ -11484,7 +11502,7 @@ var uniqueId = $.fn.extend( {
$.ui.fancytree.registerExtension({
name: "glyph",
version: "2.32.0",
version: "2.33.0",
// Default options for this extension.
options: {
preset: null, // 'awesome3', 'awesome4', 'bootstrap3', 'material'
@@ -11638,8 +11656,8 @@ var uniqueId = $.fn.extend( {
* Released under the MIT license
* https://github.com/mar10/fancytree/wiki/LicenseInfo
*
* @version 2.32.0
* @date 2019-09-10T07:42:12Z
* @version 2.33.0
* @date 2019-10-29T08:00:07Z
*/
(function(factory) {
@@ -11756,7 +11774,7 @@ var uniqueId = $.fn.extend( {
*/
$.ui.fancytree.registerExtension({
name: "gridnav",
version: "2.32.0",
version: "2.33.0",
// Default options for this extension.
options: {
autofocusInput: false, // Focus first embedded input if node gets activated
@@ -11863,8 +11881,8 @@ var uniqueId = $.fn.extend( {
* Released under the MIT license
* https://github.com/mar10/fancytree/wiki/LicenseInfo
*
* @version 2.32.0
* @date 2019-09-10T07:42:12Z
* @version 2.33.0
* @date 2019-10-29T08:00:07Z
*/
(function(factory) {
@@ -11893,7 +11911,7 @@ var uniqueId = $.fn.extend( {
*/
$.ui.fancytree.registerExtension({
name: "multi",
version: "2.32.0",
version: "2.33.0",
// Default options for this extension.
options: {
allowNoSelect: false, //
@@ -11995,8 +12013,8 @@ var uniqueId = $.fn.extend( {
* Released under the MIT license
* https://github.com/mar10/fancytree/wiki/LicenseInfo
*
* @version 2.32.0
* @date 2019-09-10T07:42:12Z
* @version 2.33.0
* @date 2019-10-29T08:00:07Z
*/
(function(factory) {
@@ -12196,7 +12214,7 @@ var uniqueId = $.fn.extend( {
*/
$.ui.fancytree.registerExtension({
name: "persist",
version: "2.32.0",
version: "2.33.0",
// Default options for this extension.
options: {
cookieDelimiter: "~",
@@ -12489,8 +12507,8 @@ var uniqueId = $.fn.extend( {
* Released under the MIT license
* https://github.com/mar10/fancytree/wiki/LicenseInfo
*
* @version 2.32.0
* @date 2019-09-10T07:42:12Z
* @version 2.33.0
* @date 2019-10-29T08:00:07Z
*/
(function(factory) {
@@ -12573,7 +12591,7 @@ var uniqueId = $.fn.extend( {
$.ui.fancytree.registerExtension({
name: "table",
version: "2.32.0",
version: "2.33.0",
// Default options for this extension.
options: {
checkboxColumnIdx: null, // render the checkboxes into the this column index (default: nodeColumnIdx)
@@ -13039,8 +13057,8 @@ var uniqueId = $.fn.extend( {
* Released under the MIT license
* https://github.com/mar10/fancytree/wiki/LicenseInfo
*
* @version 2.32.0
* @date 2019-09-10T07:42:12Z
* @version 2.33.0
* @date 2019-10-29T08:00:07Z
*/
(function(factory) {
@@ -13063,7 +13081,7 @@ var uniqueId = $.fn.extend( {
*/
$.ui.fancytree.registerExtension({
name: "themeroller",
version: "2.32.0",
version: "2.33.0",
// Default options for this extension.
options: {
activeClass: "ui-state-active", // Class added to active node
@@ -13159,8 +13177,8 @@ var uniqueId = $.fn.extend( {
* Released under the MIT license
* https://github.com/mar10/fancytree/wiki/LicenseInfo
*
* @version 2.32.0
* @date 2019-09-10T07:42:12Z
* @version 2.33.0
* @date 2019-10-29T08:00:07Z
*/
(function(factory) {
@@ -13290,7 +13308,7 @@ var uniqueId = $.fn.extend( {
*/
$.ui.fancytree.registerExtension({
name: "wide",
version: "2.32.0",
version: "2.33.0",
// Default options for this extension.
options: {
iconWidth: null, // Adjust this if @fancy-icon-width != "16px"

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -13,8 +13,8 @@
* Released under the MIT license
* https://github.com/mar10/fancytree/wiki/LicenseInfo
*
* @version 2.32.0
* @date 2019-09-10T07:42:12Z
* @version 2.33.0
* @date 2019-10-29T08:00:07Z
******************************************************************************/
/*------------------------------------------------------------------------------
* Helpers
@@ -121,7 +121,6 @@ span.fancytree-drag-helper-img,
display: inline-block;
vertical-align: top;
background-repeat: no-repeat;
background-position: left;
background-image: url("../skin-win8/icons.gif");
background-position: 0px 0px;
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -191,7 +191,7 @@
this.addNodes.apply(this, json['nodes']);
this.addEdges.apply(this, json['edges']);
}
}
};
// find the edges from node1 to node2
@@ -479,51 +479,35 @@
return energy;
};
var __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }; // stolen from coffeescript, thanks jashkenas! ;-)
Springy.requestAnimationFrame = __bind(this.requestAnimationFrame ||
this.webkitRequestAnimationFrame ||
this.mozRequestAnimationFrame ||
this.oRequestAnimationFrame ||
this.msRequestAnimationFrame ||
(function(callback, element) {
this.setTimeout(callback, 10);
}), this);
/**
* Start simulation if it's not running already.
* In case it's running then the call is ignored, and none of the callbacks passed is ever executed.
*/
Layout.ForceDirected.prototype.start = function(render, onRenderStop, onRenderStart) {
Layout.ForceDirected.prototype.start = function(onRenderStop) {
var t = this;
if (this._started) return;
this._started = true;
this._stop = false;
if (onRenderStart !== undefined) { onRenderStart(); }
Springy.requestAnimationFrame(function step() {
function step() {
t.tick(0.03);
if (!t._stop && render !== undefined) {
render();
}
// stop simulation when energy of the system goes below a threshold
if (t._stop || t.totalEnergy() < t.minEnergyThreshold) {
t._started = false;
if (onRenderStop !== undefined) { onRenderStop(); }
onRenderStop();
} else {
Springy.requestAnimationFrame(step);
setImmediate(step);
}
});
}
step();
};
Layout.ForceDirected.prototype.stop = function() {
this._stop = true;
}
};
Layout.ForceDirected.prototype.tick = function(timestep) {
this.applyCoulombsLaw();
@@ -644,85 +628,35 @@
/**
* Renderer handles the layout rendering loop
* @param onRenderStop optional callback function that gets executed whenever rendering stops.
* @param onRenderStart optional callback function that gets executed whenever rendering starts.
* @param onRenderFrame optional callback function that gets executed after each frame is rendered.
*/
var Renderer = Springy.Renderer = function(layout, clear, drawEdge, drawNode, onRenderStop, onRenderStart, onRenderFrame) {
var Renderer = Springy.Renderer = function(layout, onRenderStop) {
this.layout = layout;
this.clear = clear;
this.drawEdge = drawEdge;
this.drawNode = drawNode;
this.onRenderStop = onRenderStop;
this.onRenderStart = onRenderStart;
this.onRenderFrame = onRenderFrame;
this.layout.graph.addGraphListener(this);
}
};
Renderer.prototype.graphChanged = function(e) {
Renderer.prototype.graphChanged = function() {
this.start();
};
/**
* Starts the simulation of the layout in use.
*
* Note that in case the algorithm is still or already running then the layout that's in use
* might silently ignore the call, and your optional <code>done</code> callback is never executed.
* At least the built-in ForceDirected layout behaves in this way.
*
* @param done An optional callback function that gets executed when the springy algorithm stops,
* either because it ended or because stop() was called.
*/
Renderer.prototype.start = function(done) {
var t = this;
this.layout.start(function render() {
t.clear();
Renderer.prototype.start = function(maxTime) {
if (maxTime) {
setTimeout(() => this.stop(), maxTime);
}
t.layout.eachEdge(function(edge, spring) {
t.drawEdge(edge, spring.point1.p, spring.point2.p);
});
t.layout.eachNode(function(node, point) {
t.drawNode(node, point.p);
});
if (t.onRenderFrame !== undefined) { t.onRenderFrame(); }
}, this.onRenderStop, this.onRenderStart);
return new Promise((res, rej) => {
this.layout.start(res);
});
};
Renderer.prototype.stop = function() {
this.layout.stop();
};
// Array.forEach implementation for IE support..
//https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/forEach
if ( !Array.prototype.forEach ) {
Array.prototype.forEach = function( callback, thisArg ) {
var T, k;
if ( this == null ) {
throw new TypeError( " this is null or not defined" );
}
var O = Object(this);
var len = O.length >>> 0; // Hack to convert O.length to a UInt32
if ( {}.toString.call(callback) != "[object Function]" ) {
throw new TypeError( callback + " is not a function" );
}
if ( thisArg ) {
T = thisArg;
}
k = 0;
while( k < len ) {
var kValue;
if ( k in O ) {
kValue = O[ k ];
callback.call( T, kValue, k, O );
}
k++;
}
};
}
var isEmpty = function(obj) {
for (var k in obj) {
if (obj.hasOwnProperty(k)) {

515
package-lock.json generated
View File

@@ -1,6 +1,6 @@
{
"name": "trilium",
"version": "0.36.0-beta",
"version": "0.36.2",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
@@ -106,14 +106,16 @@
}
},
"@electron/get": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/@electron/get/-/get-1.5.0.tgz",
"integrity": "sha512-tafxBz6n08G6SX961F/h8XFtpB/DdwRvJJoDeOH9x78jDSCMQ2G/rRWqSwLFp9oeMFBJf0Pf5Kkw6TKt5w9TWg==",
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/@electron/get/-/get-1.6.0.tgz",
"integrity": "sha512-xuvAzbN9iBApfAMvW0hKUpxHR5wPVbG9RaoSTbpu/WaHISDu0MVfMWYhfeU0X730CpBV0G2RkLgwAs9WDan3GA==",
"dev": true,
"requires": {
"debug": "^4.1.1",
"env-paths": "^2.2.0",
"fs-extra": "^8.1.0",
"global-agent": "^2.0.2",
"global-tunnel-ng": "^2.7.1",
"got": "^9.6.0",
"sanitize-filename": "^1.6.2",
"sumchecker": "^3.0.0"
@@ -782,29 +784,29 @@
"integrity": "sha512-uMgjozySS8adZZYePpaWs8cxB9/kdzmpX6SgJZ+wbz1K5eYk5QMYDVJaZKhxyIHUdnnJkfR7SVgStgH7LkGUyg=="
},
"app-builder-bin": {
"version": "3.4.3",
"resolved": "https://registry.npmjs.org/app-builder-bin/-/app-builder-bin-3.4.3.tgz",
"integrity": "sha512-qMhayIwi3juerQEVJMQ76trObEbfQT0nhUdxZz9a26/3NLT3pE6awmQ8S1cEnrGugaaM5gYqR8OElcDezfmEsg==",
"version": "3.4.4",
"resolved": "https://registry.npmjs.org/app-builder-bin/-/app-builder-bin-3.4.4.tgz",
"integrity": "sha512-Xib+wgdK+8zZhbZr5pma3pNB23Y4JRY5Yt6h8peou6MTFSQzXdIkqalh/ezy9SMLuS43S4b0s7jTVAmUs8WVmA==",
"dev": true
},
"app-builder-lib": {
"version": "21.2.0",
"resolved": "https://registry.npmjs.org/app-builder-lib/-/app-builder-lib-21.2.0.tgz",
"integrity": "sha512-aOX/nv77/Bti6NymJDg7p9T067xD8m1ipIEJR7B4Mm1GsJWpMm9PZdXtCRiMNRjHtQS5KIljT0g17781y6qn5A==",
"version": "22.1.0",
"resolved": "https://registry.npmjs.org/app-builder-lib/-/app-builder-lib-22.1.0.tgz",
"integrity": "sha512-jDTfWsVS/MePO4FexqiSQcsWM9Yfr81ETIYbmVbKmW05o0dn9k1DvMOMoLb0kTLQpW+pWBVvGMAOPfk68HnBrg==",
"dev": true,
"requires": {
"7zip-bin": "~5.0.3",
"@develar/schema-utils": "~2.1.0",
"async-exit-hook": "^2.0.1",
"bluebird-lst": "^1.0.9",
"builder-util": "21.2.0",
"builder-util-runtime": "8.3.0",
"builder-util": "22.1.0",
"builder-util-runtime": "8.4.0",
"chromium-pickle-js": "^0.2.0",
"debug": "^4.1.1",
"ejs": "^2.6.2",
"electron-publish": "21.2.0",
"ejs": "^2.7.1",
"electron-publish": "22.1.0",
"fs-extra": "^8.1.0",
"hosted-git-info": "^2.7.1",
"hosted-git-info": "^3.0.0",
"is-ci": "^2.0.0",
"isbinaryfile": "^4.0.2",
"js-yaml": "^3.13.1",
@@ -812,9 +814,20 @@
"minimatch": "^3.0.4",
"normalize-package-data": "^2.5.0",
"read-config-file": "5.0.0",
"sanitize-filename": "^1.6.2",
"sanitize-filename": "^1.6.3",
"semver": "^6.3.0",
"temp-file": "^3.3.4"
},
"dependencies": {
"hosted-git-info": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-3.0.2.tgz",
"integrity": "sha512-ezZMWtHXm7Eb7Rq4Mwnx2vs79WUx2QmRg3+ZqeGroKzfDO+EprOcgRPYghsOP9JuYBfK18VojmRTGCg8Ma+ktw==",
"dev": true,
"requires": {
"lru-cache": "^5.1.1"
}
}
}
},
"append-field": {
@@ -1339,6 +1352,13 @@
"resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz",
"integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24="
},
"boolean": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/boolean/-/boolean-2.0.2.tgz",
"integrity": "sha512-ymsbJQlux/uogyEWfsXJUYzuyoOzPyp6NvEV71s6/ptQR7ptKO9uHF+WZL2GRATDeN52EFhNyrIu+exNZKh3Cw==",
"dev": true,
"optional": true
},
"bowser": {
"version": "2.7.0",
"resolved": "https://registry.npmjs.org/bowser/-/bowser-2.7.0.tgz",
@@ -1536,16 +1556,16 @@
}
},
"builder-util": {
"version": "21.2.0",
"resolved": "https://registry.npmjs.org/builder-util/-/builder-util-21.2.0.tgz",
"integrity": "sha512-Nd6CUb6YgDY8EXAXEIegx+1kzKqyFQ5ZM5BoYkeunAlwz/zDJoH1UCyULjoS5wQe5czNClFQy07zz2bzYD0Z4A==",
"version": "22.1.0",
"resolved": "https://registry.npmjs.org/builder-util/-/builder-util-22.1.0.tgz",
"integrity": "sha512-BPvpWvxQ5XOzm2WepIgmOAyo2IyaM/Bd1LJmeTYy5CtknNAtxgmAPQJfCHCikMKKQA4Obz/KYecXQiGpGJ2ThA==",
"dev": true,
"requires": {
"7zip-bin": "~5.0.3",
"@types/debug": "^4.1.4",
"app-builder-bin": "3.4.3",
"@types/debug": "^4.1.5",
"app-builder-bin": "3.4.4",
"bluebird-lst": "^1.0.9",
"builder-util-runtime": "8.3.0",
"builder-util-runtime": "8.4.0",
"chalk": "^2.4.2",
"debug": "^4.1.1",
"fs-extra": "^8.1.0",
@@ -1594,9 +1614,9 @@
}
},
"builder-util-runtime": {
"version": "8.3.0",
"resolved": "https://registry.npmjs.org/builder-util-runtime/-/builder-util-runtime-8.3.0.tgz",
"integrity": "sha512-CSOdsYqf4RXIHh1HANPbrZHlZ9JQJXSuDDloblZPcWQVN62inyYoTQuSmY3KrgefME2Sv3Kn2MxHvbGQHRf8Iw==",
"version": "8.4.0",
"resolved": "https://registry.npmjs.org/builder-util-runtime/-/builder-util-runtime-8.4.0.tgz",
"integrity": "sha512-CJB/eKfPf2vHrkmirF5eicVnbDCkMBbwd5tRYlTlgud16zFeqD7QmrVUAOEXdnsrcNkiLg9dbuUsQKtl/AwsYQ==",
"dev": true,
"requires": {
"debug": "^4.1.1",
@@ -2735,6 +2755,13 @@
"resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz",
"integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups="
},
"detect-node": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.0.4.tgz",
"integrity": "sha512-ZIzRpLJrOj7jjP2miAtgqIfmzbxa4ZOr5jJc601zklsfEx9oTzmmj2nVpIPRpNlRTIh8lc1kyViIY7BWSGNmKw==",
"dev": true,
"optional": true
},
"dicer": {
"version": "0.2.5",
"resolved": "https://registry.npmjs.org/dicer/-/dicer-0.2.5.tgz",
@@ -2790,6 +2817,108 @@
"sanitize-filename": "^1.6.2"
},
"dependencies": {
"ansi-styles": {
"version": "3.2.1",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
"integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
"dev": true,
"requires": {
"color-convert": "^1.9.0"
}
},
"app-builder-bin": {
"version": "3.4.3",
"resolved": "https://registry.npmjs.org/app-builder-bin/-/app-builder-bin-3.4.3.tgz",
"integrity": "sha512-qMhayIwi3juerQEVJMQ76trObEbfQT0nhUdxZz9a26/3NLT3pE6awmQ8S1cEnrGugaaM5gYqR8OElcDezfmEsg==",
"dev": true
},
"app-builder-lib": {
"version": "21.2.0",
"resolved": "https://registry.npmjs.org/app-builder-lib/-/app-builder-lib-21.2.0.tgz",
"integrity": "sha512-aOX/nv77/Bti6NymJDg7p9T067xD8m1ipIEJR7B4Mm1GsJWpMm9PZdXtCRiMNRjHtQS5KIljT0g17781y6qn5A==",
"dev": true,
"requires": {
"7zip-bin": "~5.0.3",
"@develar/schema-utils": "~2.1.0",
"async-exit-hook": "^2.0.1",
"bluebird-lst": "^1.0.9",
"builder-util": "21.2.0",
"builder-util-runtime": "8.3.0",
"chromium-pickle-js": "^0.2.0",
"debug": "^4.1.1",
"ejs": "^2.6.2",
"electron-publish": "21.2.0",
"fs-extra": "^8.1.0",
"hosted-git-info": "^2.7.1",
"is-ci": "^2.0.0",
"isbinaryfile": "^4.0.2",
"js-yaml": "^3.13.1",
"lazy-val": "^1.0.4",
"minimatch": "^3.0.4",
"normalize-package-data": "^2.5.0",
"read-config-file": "5.0.0",
"sanitize-filename": "^1.6.2",
"semver": "^6.3.0",
"temp-file": "^3.3.4"
}
},
"builder-util": {
"version": "21.2.0",
"resolved": "https://registry.npmjs.org/builder-util/-/builder-util-21.2.0.tgz",
"integrity": "sha512-Nd6CUb6YgDY8EXAXEIegx+1kzKqyFQ5ZM5BoYkeunAlwz/zDJoH1UCyULjoS5wQe5czNClFQy07zz2bzYD0Z4A==",
"dev": true,
"requires": {
"7zip-bin": "~5.0.3",
"@types/debug": "^4.1.4",
"app-builder-bin": "3.4.3",
"bluebird-lst": "^1.0.9",
"builder-util-runtime": "8.3.0",
"chalk": "^2.4.2",
"debug": "^4.1.1",
"fs-extra": "^8.1.0",
"is-ci": "^2.0.0",
"js-yaml": "^3.13.1",
"source-map-support": "^0.5.13",
"stat-mode": "^0.3.0",
"temp-file": "^3.3.4"
}
},
"builder-util-runtime": {
"version": "8.3.0",
"resolved": "https://registry.npmjs.org/builder-util-runtime/-/builder-util-runtime-8.3.0.tgz",
"integrity": "sha512-CSOdsYqf4RXIHh1HANPbrZHlZ9JQJXSuDDloblZPcWQVN62inyYoTQuSmY3KrgefME2Sv3Kn2MxHvbGQHRf8Iw==",
"dev": true,
"requires": {
"debug": "^4.1.1",
"sax": "^1.2.4"
}
},
"chalk": {
"version": "2.4.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
"integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
"dev": true,
"requires": {
"ansi-styles": "^3.2.1",
"escape-string-regexp": "^1.0.5",
"supports-color": "^5.3.0"
}
},
"electron-publish": {
"version": "21.2.0",
"resolved": "https://registry.npmjs.org/electron-publish/-/electron-publish-21.2.0.tgz",
"integrity": "sha512-mWavuoWJe87iaeKd0I24dNWIaR+0yRzshjNVqGyK019H766fsPWl3caQJnVKFaEyrZRP397v4JZVG0e7s16AxA==",
"dev": true,
"requires": {
"bluebird-lst": "^1.0.9",
"builder-util": "~21.2.0",
"builder-util-runtime": "8.3.0",
"chalk": "^2.4.2",
"fs-extra": "^8.1.0",
"lazy-val": "^1.0.4",
"mime": "^2.4.4"
}
},
"iconv-lite": {
"version": "0.5.0",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.5.0.tgz",
@@ -2798,6 +2927,27 @@
"requires": {
"safer-buffer": ">= 2.1.2 < 3"
}
},
"mime": {
"version": "2.4.4",
"resolved": "https://registry.npmjs.org/mime/-/mime-2.4.4.tgz",
"integrity": "sha512-LRxmNwziLPT828z+4YkNzloCFC2YM4wrB99k+AV5ZbEyfGNWfG8SO1FUXLmLDBSo89NrJZ4DIWeLjy1CHGhMGA==",
"dev": true
},
"stat-mode": {
"version": "0.3.0",
"resolved": "https://registry.npmjs.org/stat-mode/-/stat-mode-0.3.0.tgz",
"integrity": "sha512-QjMLR0A3WwFY2aZdV0okfFEJB5TRjkggXZjxP3A1RsWsNHNu3YPv8btmtc6iCFZ0Rul3FE93OYogvhOUClU+ng==",
"dev": true
},
"supports-color": {
"version": "5.5.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
"integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
"dev": true,
"requires": {
"has-flag": "^3.0.0"
}
}
}
},
@@ -2874,9 +3024,9 @@
}
},
"dotenv": {
"version": "8.1.0",
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-8.1.0.tgz",
"integrity": "sha512-GUE3gqcDCaMltj2++g6bRQ5rBJWtkWTmqmD0fo1RnnMuUqHNCt2oTPeDnS9n6fKYvlhn7AeBkb38lymBtWBQdA==",
"version": "8.2.0",
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-8.2.0.tgz",
"integrity": "sha512-8sJ78ElpbDJBHNeBzUbUVLsqKdccaa/BXF1uPTw3GrvQTBgrQrtObr2mUrE38vzYd8cEv+m/JBfDLioYcfXoaw==",
"dev": true
},
"dotenv-expand": {
@@ -2978,34 +3128,40 @@
},
"dependencies": {
"@types/node": {
"version": "10.14.21",
"resolved": "https://registry.npmjs.org/@types/node/-/node-10.14.21.tgz",
"integrity": "sha512-nuFlRdBiqbF+PJIEVxm2jLFcQWN7q7iWEJGsBV4n7v1dbI9qXB8im2pMMKMCUZe092sQb5SQft2DHfuQGK5hqQ==",
"version": "10.17.0",
"resolved": "https://registry.npmjs.org/@types/node/-/node-10.17.0.tgz",
"integrity": "sha512-wuJwN2KV4tIRz1bu9vq5kSPasJ8IsEjZaP1ZR7KlmdUZvGF/rXy8DmXOVwUD0kAtvtJ7aqMKPqUXC0NUTDbrDg==",
"dev": true
}
}
},
"electron-builder": {
"version": "21.2.0",
"resolved": "https://registry.npmjs.org/electron-builder/-/electron-builder-21.2.0.tgz",
"integrity": "sha512-x8EXrqFbAb2L3N22YlGar3dGh8vwptbB3ovo3OF6K7NTpcsmM2zEoJv7GhFyX73rNzSG2HaWpXwGAtOp2JWiEw==",
"version": "22.1.0",
"resolved": "https://registry.npmjs.org/electron-builder/-/electron-builder-22.1.0.tgz",
"integrity": "sha512-uu2W9BLG38D0i2PG6dHupmOYc+q/TRL+Ztf8xitqK+2Quq33PFbeN0ipfySuVEDg4I6whDRBOgxBEWwnUYqZZQ==",
"dev": true,
"requires": {
"app-builder-lib": "21.2.0",
"app-builder-lib": "22.1.0",
"bluebird-lst": "^1.0.9",
"builder-util": "21.2.0",
"builder-util-runtime": "8.3.0",
"builder-util": "22.1.0",
"builder-util-runtime": "8.4.0",
"chalk": "^2.4.2",
"dmg-builder": "21.2.0",
"fs-extra": "^8.1.0",
"is-ci": "^2.0.0",
"lazy-val": "^1.0.4",
"read-config-file": "5.0.0",
"sanitize-filename": "^1.6.2",
"sanitize-filename": "^1.6.3",
"update-notifier": "^3.0.1",
"yargs": "^13.3.0"
"yargs": "^14.0.0"
},
"dependencies": {
"ansi-regex": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz",
"integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==",
"dev": true
},
"ansi-styles": {
"version": "3.2.1",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
@@ -3015,6 +3171,12 @@
"color-convert": "^1.9.0"
}
},
"camelcase": {
"version": "5.3.1",
"resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz",
"integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==",
"dev": true
},
"chalk": {
"version": "2.4.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
@@ -3026,6 +3188,41 @@
"supports-color": "^5.3.0"
}
},
"find-up": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz",
"integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==",
"dev": true,
"requires": {
"locate-path": "^3.0.0"
}
},
"is-fullwidth-code-point": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
"integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=",
"dev": true
},
"string-width": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz",
"integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==",
"dev": true,
"requires": {
"emoji-regex": "^7.0.1",
"is-fullwidth-code-point": "^2.0.0",
"strip-ansi": "^5.1.0"
}
},
"strip-ansi": {
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz",
"integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==",
"dev": true,
"requires": {
"ansi-regex": "^4.1.0"
}
},
"supports-color": {
"version": "5.5.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
@@ -3034,6 +3231,35 @@
"requires": {
"has-flag": "^3.0.0"
}
},
"yargs": {
"version": "14.2.0",
"resolved": "https://registry.npmjs.org/yargs/-/yargs-14.2.0.tgz",
"integrity": "sha512-/is78VKbKs70bVZH7w4YaZea6xcJWOAwkhbR0CFuZBmYtfTYF0xjGJF43AYd8g2Uii1yJwmS5GR2vBmrc32sbg==",
"dev": true,
"requires": {
"cliui": "^5.0.0",
"decamelize": "^1.2.0",
"find-up": "^3.0.0",
"get-caller-file": "^2.0.1",
"require-directory": "^2.1.1",
"require-main-filename": "^2.0.0",
"set-blocking": "^2.0.0",
"string-width": "^3.0.0",
"which-module": "^2.0.0",
"y18n": "^4.0.0",
"yargs-parser": "^15.0.0"
}
},
"yargs-parser": {
"version": "15.0.0",
"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-15.0.0.tgz",
"integrity": "sha512-xLTUnCMc4JhxrPEPUYD5IBR1mWCK/aT6+RJ/K29JY2y1vD+FhtgKK0AXRWvI262q3QSffAQuTouFIKUuHX89wQ==",
"dev": true,
"requires": {
"camelcase": "^5.0.0",
"decamelize": "^1.2.0"
}
}
}
},
@@ -3478,12 +3704,12 @@
}
},
"electron-packager": {
"version": "14.0.6",
"resolved": "https://registry.npmjs.org/electron-packager/-/electron-packager-14.0.6.tgz",
"integrity": "sha512-X+ikV+TnnNkIrK93vOjsjPeykCQBFxBS7LXKMTE1s62rXWirGMdjWL+edVkBOMRkH0ROJyFmWM28Dpj6sfEg+A==",
"version": "14.1.0",
"resolved": "https://registry.npmjs.org/electron-packager/-/electron-packager-14.1.0.tgz",
"integrity": "sha512-4oGtQYYjSA/M4BGOwG0NBqEtThBf7aCV76C2AZsD71eGocYqZNvtvfzeeMnKkc3EW1nPq7iJ4Wge+GXma0kwIA==",
"dev": true,
"requires": {
"@electron/get": "^1.3.0",
"@electron/get": "^1.6.0",
"asar": "^2.0.1",
"cross-zip": "^2.1.5",
"debug": "^4.0.1",
@@ -3499,18 +3725,36 @@
"resolve": "^1.1.6",
"sanitize-filename": "^1.6.0",
"semver": "^6.0.0",
"yargs-parser": "^13.0.0"
"yargs-parser": "^16.0.0"
},
"dependencies": {
"camelcase": {
"version": "5.3.1",
"resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz",
"integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==",
"dev": true
},
"yargs-parser": {
"version": "16.0.0",
"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-16.0.0.tgz",
"integrity": "sha512-WyNdFYED0penIaLqLSDSeFksC5lVjrjp9vNkG7IKfgQNiAYa6FfHuJXjuaQgt0eOVN2z+gJhHJSX7uRhJKPc7Q==",
"dev": true,
"requires": {
"camelcase": "^5.0.0",
"decamelize": "^1.2.0"
}
}
}
},
"electron-publish": {
"version": "21.2.0",
"resolved": "https://registry.npmjs.org/electron-publish/-/electron-publish-21.2.0.tgz",
"integrity": "sha512-mWavuoWJe87iaeKd0I24dNWIaR+0yRzshjNVqGyK019H766fsPWl3caQJnVKFaEyrZRP397v4JZVG0e7s16AxA==",
"version": "22.1.0",
"resolved": "https://registry.npmjs.org/electron-publish/-/electron-publish-22.1.0.tgz",
"integrity": "sha512-jHjMCaL2dFU+iOq8wW568F59+DW1jFJGT3vc2xqm9iXyZ8gWlQ+NVve4bq9HZG7m4iNqWbGw9StmZcOzmIBxMQ==",
"dev": true,
"requires": {
"bluebird-lst": "^1.0.9",
"builder-util": "~21.2.0",
"builder-util-runtime": "8.3.0",
"builder-util": "~22.1.0",
"builder-util-runtime": "8.4.0",
"chalk": "^2.4.2",
"fs-extra": "^8.1.0",
"lazy-val": "^1.0.4",
@@ -3731,6 +3975,13 @@
"is-symbol": "^1.0.2"
}
},
"es6-error": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz",
"integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==",
"dev": true,
"optional": true
},
"es6-promise": {
"version": "4.2.8",
"resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.8.tgz",
@@ -4968,9 +5219,9 @@
}
},
"file-type": {
"version": "12.3.0",
"resolved": "https://registry.npmjs.org/file-type/-/file-type-12.3.0.tgz",
"integrity": "sha512-4E4Esq9KLwjYCY32E7qSmd0h7LefcniZHX+XcdJ4Wfx1uGJX7QCigiqw/U0yT7WOslm28yhxl87DJ0wHYv0RAA=="
"version": "12.3.1",
"resolved": "https://registry.npmjs.org/file-type/-/file-type-12.3.1.tgz",
"integrity": "sha512-FXxY5h6vSYMjrRal4YqbtfuoKD/oE0AMjJ7E5Hm+BdaQECcFVD03B41RAWYJ7wyuLr/wRnCtFo7y37l+nh+TAA=="
},
"filename-regex": {
"version": "2.0.1",
@@ -5658,6 +5909,31 @@
"process": "~0.5.1"
}
},
"global-agent": {
"version": "2.1.5",
"resolved": "https://registry.npmjs.org/global-agent/-/global-agent-2.1.5.tgz",
"integrity": "sha512-pYJjCxxNBzYxo6iNO62JZn8iCFVbvpiM0zE4w/G5hBNIvLjnvzIeCVQPMKc3aK8ju5L7Q8NNI/oBSosU0eeSYw==",
"dev": true,
"optional": true,
"requires": {
"boolean": "^2.0.2",
"core-js": "^3.3.3",
"es6-error": "^4.1.1",
"matcher": "^2.0.0",
"roarr": "^2.14.2",
"semver": "^6.3.0",
"serialize-error": "^5.0.0"
},
"dependencies": {
"core-js": {
"version": "3.3.5",
"resolved": "https://registry.npmjs.org/core-js/-/core-js-3.3.5.tgz",
"integrity": "sha512-0J3K+Par/ZydhKg8pEiTcK/9d65/nqJOzY62uMkjeBmt05fDOt/khUVjDdh8TpeIuGQDy1yLDDCjiWN/8pFIuw==",
"dev": true,
"optional": true
}
}
},
"global-dirs": {
"version": "0.1.1",
"resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-0.1.1.tgz",
@@ -5667,12 +5943,37 @@
"ini": "^1.3.4"
}
},
"global-tunnel-ng": {
"version": "2.7.1",
"resolved": "https://registry.npmjs.org/global-tunnel-ng/-/global-tunnel-ng-2.7.1.tgz",
"integrity": "sha512-4s+DyciWBV0eK148wqXxcmVAbFVPqtc3sEtUE/GTQfuU80rySLcMhUmHKSHI7/LDj8q0gDYI1lIhRRB7ieRAqg==",
"dev": true,
"optional": true,
"requires": {
"encodeurl": "^1.0.2",
"lodash": "^4.17.10",
"npm-conf": "^1.1.3",
"tunnel": "^0.0.6"
}
},
"globals": {
"version": "11.12.0",
"resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz",
"integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==",
"dev": true
},
"globalthis": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.0.tgz",
"integrity": "sha512-vcCAZTJ3r5Qcu5l8/2oyVdoFwxKgfYnMTR2vwWeux/NAVZK3PwcMaWkdUIn4GJbmKuRK7xcvDsLuK+CKcXyodg==",
"dev": true,
"optional": true,
"requires": {
"define-properties": "^1.1.2",
"function-bind": "^1.1.1",
"object-keys": "^1.0.12"
}
},
"globby": {
"version": "10.0.1",
"resolved": "https://registry.npmjs.org/globby/-/globby-10.0.1.tgz",
@@ -6003,9 +6304,9 @@
"integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw=="
},
"helmet": {
"version": "3.21.1",
"resolved": "https://registry.npmjs.org/helmet/-/helmet-3.21.1.tgz",
"integrity": "sha512-IC/54Lxvvad2YiUdgLmPlNFKLhNuG++waTF5KPYq/Feo3NNhqMFbcLAlbVkai+9q0+4uxjxGPJ9bNykG+3zZNg==",
"version": "3.21.2",
"resolved": "https://registry.npmjs.org/helmet/-/helmet-3.21.2.tgz",
"integrity": "sha512-okUo+MeWgg00cKB8Csblu8EXgcIoDyb5ZS/3u0W4spCimeVuCUvVZ6Vj3O2VJ1Sxpyb8jCDvzu0L1KKT11pkIg==",
"requires": {
"depd": "2.0.0",
"dns-prefetch-control": "0.2.0",
@@ -6014,7 +6315,7 @@
"feature-policy": "0.3.0",
"frameguard": "3.1.0",
"helmet-crossdomain": "0.4.0",
"helmet-csp": "2.9.2",
"helmet-csp": "2.9.4",
"hide-powered-by": "1.1.0",
"hpkp": "2.0.0",
"hsts": "2.2.0",
@@ -6037,11 +6338,11 @@
"integrity": "sha512-AB4DTykRw3HCOxovD1nPR16hllrVImeFp5VBV9/twj66lJ2nU75DP8FPL0/Jp4jj79JhTfG+pFI2MD02kWJ+fA=="
},
"helmet-csp": {
"version": "2.9.2",
"resolved": "https://registry.npmjs.org/helmet-csp/-/helmet-csp-2.9.2.tgz",
"integrity": "sha512-Lt5WqNfbNjEJ6ysD4UNpVktSyjEKfU9LVJ1LaFmPfYseg/xPealPfgHhtqdAdjPDopp5zbg/VWCyp4cluMIckw==",
"version": "2.9.4",
"resolved": "https://registry.npmjs.org/helmet-csp/-/helmet-csp-2.9.4.tgz",
"integrity": "sha512-qUgGx8+yk7Xl8XFEGI4MFu1oNmulxhQVTlV8HP8tV3tpfslCs30OZz/9uQqsWPvDISiu/NwrrCowsZBhFADYqg==",
"requires": {
"bowser": "^2.6.1",
"bowser": "^2.7.0",
"camelize": "1.0.0",
"content-security-policy-builder": "2.1.0",
"dasherize": "2.0.0"
@@ -6175,9 +6476,9 @@
}
},
"https-proxy-agent": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-3.0.0.tgz",
"integrity": "sha512-y4jAxNEihqvBI5F3SaO2rtsjIOnnNA8sEbuiP+UhJZJHeM2NRm6c09ax2tgqme+SgUUvjao2fJXF4h3D6Cb2HQ==",
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-3.0.1.tgz",
"integrity": "sha512-+ML2Rbh6DAuee7d07tYGEKOEi2voWPUGan+ExdPbPW6Z3svq+JCqr0v8WmKPOkz1vOVykPCBSuobe7G8GJUtVg==",
"requires": {
"agent-base": "^4.3.0",
"debug": "^3.1.0"
@@ -7728,6 +8029,25 @@
"integrity": "sha512-c+yYdCZJQrsRjTPhUx7VKkApw9bwDkNbHUKo1ovgcfDjb2kc8rLuRbIFyXL5WOEUwzSSKo3IXpph2K6DqB/KZg==",
"dev": true
},
"matcher": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/matcher/-/matcher-2.0.0.tgz",
"integrity": "sha512-nlmfSlgHBFx36j/Pl/KQPbIaqE8Zf0TqmSMjsuddHDg6PMSVgmyW9HpkLs0o0M1n2GIZ/S2BZBLIww/xjhiGng==",
"dev": true,
"optional": true,
"requires": {
"escape-string-regexp": "^2.0.0"
},
"dependencies": {
"escape-string-regexp": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz",
"integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==",
"dev": true,
"optional": true
}
}
},
"math-random": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/math-random/-/math-random-1.0.4.tgz",
@@ -8646,9 +8966,9 @@
"integrity": "sha512-0L9FvHG3nfnnmaEQPjT9xhfN4ISk0A8/2j4M37Np4mcDesJjHgEUfgPhdCyZuFI954tjokaIj/A3NdpFNdEh4Q=="
},
"node-abi": {
"version": "2.11.0",
"resolved": "https://registry.npmjs.org/node-abi/-/node-abi-2.11.0.tgz",
"integrity": "sha512-kuy/aEg75u40v378WRllQ4ZexaXJiCvB68D2scDXclp/I4cRq6togpbOoKhmN07tns9Zldu51NNERo0wehfX9g==",
"version": "2.12.0",
"resolved": "https://registry.npmjs.org/node-abi/-/node-abi-2.12.0.tgz",
"integrity": "sha512-VhPBXCIcvmo/5K8HPmnWJyyhvgKxnHTUMXR/XwGHV68+wrgkzST4UmQrY/XszSWA5dtnXpNp528zkcyJ/pzVcw==",
"requires": {
"semver": "^5.4.1"
},
@@ -10994,6 +11314,30 @@
"resolved": "https://registry.npmjs.org/rndm/-/rndm-1.2.0.tgz",
"integrity": "sha1-8z/pz7Urv9UgqhgyO8ZdsRCht2w="
},
"roarr": {
"version": "2.14.2",
"resolved": "https://registry.npmjs.org/roarr/-/roarr-2.14.2.tgz",
"integrity": "sha512-ibqv70DCUhGVMfPe0JSUHBZ9PKLhxdk8VJ/Y2M7vVr+L4VakW1CdVTU9cJQBbM2STQa84CgBAzd7hJGcnALGeg==",
"dev": true,
"optional": true,
"requires": {
"boolean": "^2.0.2",
"detect-node": "^2.0.4",
"globalthis": "^1.0.0",
"json-stringify-safe": "^5.0.1",
"semver-compare": "^1.0.0",
"sprintf-js": "^1.1.2"
},
"dependencies": {
"sprintf-js": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.2.tgz",
"integrity": "sha512-VE0SOVEHCk7Qc8ulkWw3ntAzXuqf7S2lvwQaDLRnUeIEaKNQJzV6BwmLKhOqT61aGhfUMrXeaBk+oDGCzvhcug==",
"dev": true,
"optional": true
}
}
},
"run-async": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/run-async/-/run-async-2.3.0.tgz",
@@ -11069,6 +11413,13 @@
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
"integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw=="
},
"semver-compare": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/semver-compare/-/semver-compare-1.0.0.tgz",
"integrity": "sha1-De4hahyUGrN+nvsXiPavxf9VN/w=",
"dev": true,
"optional": true
},
"semver-diff": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/semver-diff/-/semver-diff-2.1.0.tgz",
@@ -11148,6 +11499,25 @@
}
}
},
"serialize-error": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/serialize-error/-/serialize-error-5.0.0.tgz",
"integrity": "sha512-/VtpuyzYf82mHYTtI4QKtwHa79vAdU5OQpNPAmE/0UDdlGT0ZxHwC+J6gXkw29wwoVI8fMPsfcVHOwXtUQYYQA==",
"dev": true,
"optional": true,
"requires": {
"type-fest": "^0.8.0"
},
"dependencies": {
"type-fest": {
"version": "0.8.1",
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz",
"integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==",
"dev": true,
"optional": true
}
}
},
"serve-favicon": {
"version": "2.5.0",
"resolved": "https://registry.npmjs.org/serve-favicon/-/serve-favicon-2.5.0.tgz",
@@ -11457,9 +11827,9 @@
}
},
"source-map-support": {
"version": "0.5.13",
"resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz",
"integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==",
"version": "0.5.16",
"resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.16.tgz",
"integrity": "sha512-efyLRJDr68D9hBBNIPWFjhpFzURh+KJykQwvMyW5UiZzYwoF6l4YMMDIJJEyFWxWCqfyxLzz6tSfUFR+kXXsVQ==",
"dev": true,
"requires": {
"buffer-from": "^1.0.0",
@@ -12295,6 +12665,13 @@
"resolved": "https://registry.npmjs.org/tsscmp/-/tsscmp-1.0.6.tgz",
"integrity": "sha512-LxhtAkPDTkVCMQjt2h6eBVY28KCjikZqZfMcC15YBeNjkgUpdCfBu5HoiOTDu86v6smE8yOjyEktJ8hlbANHQA=="
},
"tunnel": {
"version": "0.0.6",
"resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.6.tgz",
"integrity": "sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==",
"dev": true,
"optional": true
},
"tunnel-agent": {
"version": "0.4.3",
"resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.4.3.tgz",

View File

@@ -2,7 +2,7 @@
"name": "trilium",
"productName": "Trilium Notes",
"description": "Trilium Notes",
"version": "0.36.1-beta",
"version": "0.36.3",
"license": "AGPL-3.0-only",
"main": "electron.js",
"bin": {
@@ -37,13 +37,13 @@
"electron-window-state": "5.0.3",
"express": "4.17.1",
"express-session": "1.17.0",
"file-type": "12.3.0",
"file-type": "12.3.1",
"fs-extra": "8.1.0",
"helmet": "3.21.1",
"helmet": "3.21.2",
"html": "1.0.0",
"html2plaintext": "2.1.2",
"http-proxy-agent": "2.1.0",
"https-proxy-agent": "3.0.0",
"https-proxy-agent": "3.0.1",
"image-type": "4.1.0",
"imagemin": "7.0.0",
"imagemin-giflossy": "5.1.10",
@@ -54,7 +54,7 @@
"mime-types": "2.1.24",
"moment": "2.24.0",
"multer": "1.4.2",
"node-abi": "2.11.0",
"node-abi": "2.12.0",
"open": "7.0.0",
"pngjs": "3.4.0",
"portscanner": "2.2.0",
@@ -79,10 +79,10 @@
},
"devDependencies": {
"electron": "6.0.12",
"electron-builder": "21.2.0",
"electron-builder": "22.1.0",
"electron-compile": "6.4.4",
"electron-installer-debian": "2.0.1",
"electron-packager": "14.0.6",
"electron-packager": "14.1.0",
"electron-rebuild": "1.8.6",
"jsdoc": "3.6.3",
"lorem-ipsum": "2.0.3",

View File

@@ -91,6 +91,8 @@ app.use((err, req, res, next) => {
if (err && err.message && (
err.message.includes("Invalid package")
|| (err.message.includes("Router not found for request") && err.message.includes("node_modules"))
|| (err.message.includes("Router not found for request") && err.message.includes(".js.map"))
|| (err.message.includes("Router not found for request") && err.message.includes(".css.map"))
)) {
// electron 6 outputs a lot of such errors which do not seem important
}

View File

@@ -45,6 +45,10 @@ class Branch extends Entity {
this.notePosition = maxNotePos === null ? 0 : maxNotePos + 10;
}
if (!this.isExpanded) {
this.isExpanded = false;
}
if (!this.isDeleted) {
this.isDeleted = false;
}

View File

@@ -20,7 +20,7 @@ export async function showDialog(node) {
$dialog.modal();
branchId = node.data.branchId;
const branch = await treeCache.getBranch(branchId);
const branch = treeCache.getBranch(branchId);
$treePrefixInput.val(branch.prefix);

View File

@@ -23,19 +23,18 @@ export async function showDialog() {
// set default settings
$maxNotesInput.val(20);
const note = noteDetailService.getActiveTabNote();
if (!note) {
return;
}
$linkMapContainer.css("height", $("body").height() - 150);
linkMapService = new LinkMapService(note, $linkMapContainer, getOptions());
linkMapService.render();
$linkMapContainer.empty();
$dialog.modal();
}
$dialog.on('shown.bs.modal', () => {
const note = noteDetailService.getActiveTabNote();
linkMapService = new LinkMapService(note, $linkMapContainer, getOptions());
linkMapService.render();
});
$maxNotesInput.on("input", () => linkMapService.loadNotesAndRelations(getOptions()));

View File

@@ -4,8 +4,8 @@ import NoteShort from './note_short.js';
* Represents full note, specifically including note's content.
*/
class NoteFull extends NoteShort {
constructor(treeCache, row) {
super(treeCache, row);
constructor(treeCache, row, noteShort) {
super(treeCache, row, []);
/** @param {string} */
this.content = row.content;
@@ -21,6 +21,12 @@ class NoteFull extends NoteShort {
/** @param {string} */
this.utcDateModified = row.utcDateModified;
/* ugly */
this.parents = noteShort.parents;
this.parentToBranch = noteShort.parentToBranch;
this.children = noteShort.children;
this.childToBranch = noteShort.childToBranch;
}
}

View File

@@ -1,5 +1,6 @@
import server from '../services/server.js';
import Attribute from './attribute.js';
import branches from "../services/branches.js";
const LABEL = 'label';
const LABEL_DEFINITION = 'label-definition';
@@ -13,7 +14,12 @@ const RELATION_DEFINITION = 'relation-definition';
* This note's representation is used in note tree and is kept in TreeCache.
*/
class NoteShort {
constructor(treeCache, row) {
/**
* @param {TreeCache} treeCache
* @param {Object.<string, Object>} row
* @param {Branch[]} branches - all relevant branches, i.e. where this note is either child or parent
*/
constructor(treeCache, row, branches) {
this.treeCache = treeCache;
/** @param {string} */
this.noteId = row.noteId;
@@ -26,9 +32,60 @@ class NoteShort {
/** @param {string} content-type, e.g. "application/json" */
this.mime = row.mime;
/** @param {boolean} */
this.isDeleted = row.isDeleted;
/** @param {boolean} */
this.archived = row.archived;
/** @param {string} */
this.cssClass = row.cssClass;
/** @type {string[]} */
this.parents = [];
/** @type {string[]} */
this.children = [];
/** @type {Object.<string, string>} */
this.parentToBranch = {};
/** @type {Object.<string, string>} */
this.childToBranch = {};
for (const branch of branches) {
if (this.noteId === branch.noteId) {
this.parents.push(branch.parentNoteId);
this.parentToBranch[branch.parentNoteId] = branch.branchId;
}
else if (this.noteId === branch.parentNoteId) {
this.children.push(branch.noteId);
this.childToBranch[branch.noteId] = branch.branchId;
}
else {
throw new Error(`Unknown branch ${branch.branchId} for note ${this.noteId}`);
}
}
}
addParent(parentNoteId, branchId) {
if (!this.parents.includes(parentNoteId)) {
this.parents.push(parentNoteId);
}
this.parentToBranch[parentNoteId] = branchId;
}
addChild(childNoteId, branchId) {
if (!this.children.includes(childNoteId)) {
this.children.push(childNoteId);
}
this.childToBranch[childNoteId] = branchId;
const branchIdPos = {};
for (const branchId of Object.values(this.childToBranch)) {
branchIdPos[branchId] = this.treeCache.getBranch(branchId).notePosition;
}
this.children.sort((a, b) => branchIdPos[this.childToBranch[a]] < branchIdPos[this.childToBranch[b]] ? -1 : 1);
}
/** @returns {boolean} */
@@ -58,48 +115,42 @@ class NoteShort {
/** @returns {Promise<Branch[]>} */
async getBranches() {
const branchIds = this.treeCache.parents[this.noteId].map(
parentNoteId => this.treeCache.getBranchIdByChildParent(this.noteId, parentNoteId));
const branchIds = Object.values(this.parentToBranch);
return this.treeCache.getBranches(branchIds);
}
/** @returns {boolean} */
hasChildren() {
return this.treeCache.children[this.noteId]
&& this.treeCache.children[this.noteId].length > 0;
return this.children.length > 0;
}
/** @returns {Promise<Branch[]>} */
async getChildBranches() {
if (!this.treeCache.children[this.noteId]) {
return [];
}
// don't use Object.values() to guarantee order
const branchIds = this.children.map(childNoteId => this.childToBranch[childNoteId]);
const branchIds = this.treeCache.children[this.noteId].map(
childNoteId => this.treeCache.getBranchIdByChildParent(childNoteId, this.noteId));
return await this.treeCache.getBranches(branchIds);
return this.treeCache.getBranches(branchIds);
}
/** @returns {string[]} */
getParentNoteIds() {
return this.treeCache.parents[this.noteId] || [];
return this.parents;
}
/** @returns {Promise<NoteShort[]>} */
async getParentNotes() {
return await this.treeCache.getNotes(this.getParentNoteIds());
return await this.treeCache.getNotes(this.parents);
}
/** @returns {string[]} */
getChildNoteIds() {
return this.treeCache.children[this.noteId] || [];
return this.children;
}
/** @returns {Promise<NoteShort[]>} */
async getChildNotes() {
return await this.treeCache.getNotes(this.getChildNoteIds());
return await this.treeCache.getNotes(this.children);
}
/**

View File

@@ -91,7 +91,7 @@ async function showTree() {
$detail.on("click", ".note-menu-button", async e => {
const node = treeService.getActiveNode();
const branch = await treeCache.getBranch(node.data.branchId);
const branch = treeCache.getBranch(node.data.branchId);
const note = await treeCache.getNote(node.data.noteId);
const parentNote = await treeCache.getNote(branch.parentNoteId);
const isNotRoot = note.noteId !== 'root';

View File

@@ -221,7 +221,7 @@ class Attributes {
.prop("title", "Remove this attribute")
.click(async () => {
if (valueAttr.attributeId) {
await server.remove("notes/" + noteId + "/attributes/" + valueAttr.attributeId);
await server.remove("notes/" + this.ctx.note.noteId + "/attributes/" + valueAttr.attributeId);
}
$tr.remove();

View File

@@ -26,9 +26,7 @@ async function moveBeforeNode(nodesToMove, beforeNode) {
await changeNode(
node => node.moveTo(beforeNode, 'before'),
nodeToMove,
beforeNode.data.noteId,
null);
nodeToMove);
}
}
@@ -52,9 +50,7 @@ async function moveAfterNode(nodesToMove, afterNode) {
await changeNode(
node => node.moveTo(afterNode, 'after'),
nodeToMove,
null,
afterNode.data.noteId);
nodeToMove);
}
}
@@ -62,6 +58,11 @@ async function moveToNode(nodesToMove, toNode) {
nodesToMove = await filterRootNote(nodesToMove);
for (const nodeToMove of nodesToMove) {
if (nodeToMove.data.noteId === await hoistedNoteService.getHoistedNoteId()
|| nodeToMove.getParent().data.noteType === 'search') {
continue;
}
const resp = await server.put('branches/' + nodeToMove.data.branchId + '/move-to/' + toNode.data.noteId);
if (!resp.success) {
@@ -80,6 +81,22 @@ async function moveToNode(nodesToMove, toNode) {
}
}
async function getNextNode(nodes) {
// following code assumes that nodes contain only top-most selected nodes - getSelectedNodes has been
// called with stopOnParent=true
let next = nodes[nodes.length - 1].getNextSibling();
if (!next) {
next = nodes[0].getPrevSibling();
}
if (!next && !await hoistedNoteService.isRootNode(nodes[0])) {
next = nodes[0].getParent();
}
return treeUtils.getNotePath(next);
}
async function deleteNodes(nodes) {
nodes = await filterRootNote(nodes);
@@ -130,53 +147,19 @@ async function deleteNodes(nodes) {
}
}
if (deleteClones) {
// if clones are also deleted we give up with targeted cleanup of the tree
treeService.reload();
return true;
}
const nextNotePath = await getNextNode(nodes);
// following code assumes that nodes contain only top-most selected nodes - getSelectedNodes has been
// called with stopOnParent=true
let next = nodes[nodes.length - 1].getNextSibling();
const noteIds = Array.from(new Set(nodes.map(node => node.getParent().data.noteId)));
if (!next) {
next = nodes[0].getPrevSibling();
}
if (!next && !await hoistedNoteService.isRootNode(nodes[0])) {
next = nodes[0].getParent();
}
let activeNotePath = null;
if (next) {
activeNotePath = await treeUtils.getNotePath(next);
}
await treeService.loadTreeCache();
const parentNoteIds = Array.from(new Set(nodes.map(node => node.getParent().data.noteId)));
for (const node of nodes) {
node.remove();
}
await treeService.reloadNotes(parentNoteIds);
// activate after all the reloading
if (activeNotePath) {
treeService.focusTree();
const node = await treeService.activateNote(activeNotePath);
node.setFocus(true);
}
await treeService.reloadNotes(noteIds, nextNotePath);
return true;
}
async function moveNodeUpInHierarchy(node) {
if (await hoistedNoteService.isRootNode(node) || await hoistedNoteService.isTopLevelNode(node)) {
if (await hoistedNoteService.isRootNode(node)
|| await hoistedNoteService.isTopLevelNode(node)
|| node.getParent().data.noteType === 'search') {
return;
}
@@ -197,7 +180,7 @@ async function moveNodeUpInHierarchy(node) {
node);
}
async function changeNode(func, node, beforeNoteId = null, afterNoteId = null) {
async function changeNode(func, node) {
utils.assertArguments(func, node);
const childNoteId = node.data.noteId;
@@ -214,7 +197,7 @@ async function changeNode(func, node, beforeNoteId = null, afterNoteId = null) {
node.data.parentNoteId = thisNewParentNode.data.noteId;
await treeCache.moveNote(childNoteId, thisOldParentNode.data.noteId, thisNewParentNode.data.noteId, beforeNoteId, afterNoteId);
await treeCache.reloadNotes([childNoteId]);
await treeService.checkFolderStatus(thisOldParentNode);
await treeService.checkFolderStatus(thisNewParentNode);

View File

@@ -2,6 +2,7 @@ import treeUtils from "./tree_utils.js";
import treeChangesService from "./branches.js";
import cloningService from "./cloning.js";
import toastService from "./toast.js";
import hoistedNoteService from "./hoisted_note.js";
let clipboardIds = [];
let clipboardMode = null;
@@ -66,10 +67,16 @@ function copy(nodes) {
}
function cut(nodes) {
clipboardIds = nodes.map(node => node.key);
clipboardMode = 'cut';
clipboardIds = nodes
.filter(node => node.data.noteId !== hoistedNoteService.getHoistedNoteNoPromise())
.filter(node => node.getParent().data.noteType !== 'search')
.map(node => node.data.noteId);
toastService.showMessage("Note(s) have been cut into clipboard.");
if (clipboardIds.length > 0) {
clipboardMode = 'cut';
toastService.showMessage("Note(s) have been cut into clipboard.");
}
}
function isEmpty() {

View File

@@ -12,9 +12,7 @@ async function cloneNoteTo(childNoteId, parentNoteId, prefix) {
return;
}
treeCache.addBranchRelationship(resp.branchId, childNoteId, parentNoteId);
await treeService.reloadNotes([parentNoteId]);
await treeService.reloadNotes([childNoteId, parentNoteId]);
}
// beware that first arg is noteId and second is branchId!
@@ -26,11 +24,9 @@ async function cloneNoteAfter(noteId, afterBranchId) {
return;
}
const afterBranch = await treeCache.getBranch(afterBranchId);
const afterBranch = treeCache.getBranch(afterBranchId);
treeCache.addBranchRelationship(resp.branchId, noteId, afterBranch.parentNoteId);
await treeService.reloadNotes([afterBranch.parentNoteId]);
await treeService.reloadNotes([noteId, afterBranch.parentNoteId]);
}
export default {

View File

@@ -8,27 +8,21 @@ async function getTodayNote() {
/** @return {NoteShort} */
async function getDateNote(date) {
const note = await server.get('date-notes/date/' + date);
await treeCache.reloadParents(note.noteId);
const note = await server.get('date-notes/date/' + date, {'trilium-source-id': "date-note"});
return await treeCache.getNote(note.noteId);
}
/** @return {NoteShort} */
async function getMonthNote(month) {
const note = await server.get('date-notes/month/' + month);
await treeCache.reloadParents(note.noteId);
const note = await server.get('date-notes/month/' + month, {'trilium-source-id': "date-note"});
return await treeCache.getNote(note.noteId);
}
/** @return {NoteShort} */
async function getYearNote(year) {
const note = await server.get('date-notes/year/' + year);
await treeCache.reloadParents(note.noteId);
const note = await server.get('date-notes/year/' + year, {'trilium-source-id': "date-note"});
return await treeCache.getNote(note.noteId);
}

View File

@@ -1,11 +1,13 @@
import treeService from './tree.js';
import treeChangesService from './branches.js';
import hoistedNoteService from './hoisted_note.js';
const dragAndDropSetup = {
autoExpandMS: 600,
dragStart: (node, data) => {
// don't allow dragging root node
if (node.data.noteId === 'root') {
if (node.data.noteId === hoistedNoteService.getHoistedNoteNoPromise()
|| node.getParent().data.noteType === 'search') {
return false;
}
@@ -25,6 +27,17 @@ const dragAndDropSetup = {
dragEnter: (node, data) => true, // allow drop on any node
dragOver: (node, data) => true,
dragDrop: async (node, data) => {
if ((data.hitMode === 'over' && node.data.noteType === 'search') ||
(['after', 'before'].includes(data.hitMode)
&& (node.data.noteId === hoistedNoteService.getHoistedNoteNoPromise() || node.getParent().data.noteType === 'search'))) {
const infoDialog = await import('../dialogs/info.js');
await infoDialog.info("Dropping notes into this location is not allowed.");
return;
}
const dataTransfer = data.dataTransfer;
if (dataTransfer && dataTransfer.files && dataTransfer.files.length > 0) {

View File

@@ -212,16 +212,12 @@ function FrontendScriptApi(startNote, currentNote, originEntity = null, tabConte
this.getNotes = async (noteIds, silentNotFoundError = false) => await treeCache.getNotes(noteIds, silentNotFoundError);
/**
* @param {string} noteId
* Update frontend tree (note) cache from the backend.
*
* @param {string[]} noteIds
* @method
*/
this.reloadNotesAndTheirChildren = async noteId => await treeCache.reloadNotesAndTheirChildren(noteId);
/**
* @param {string} noteId
* @method
*/
this.reloadParents = async noteId => await treeCache.reloadParents(noteId);
this.reloadNotes = async noteIds => await treeCache.reloadNotes(noteIds);
/**
* Instance name identifies particular Trilium instance. It can be useful for scripts
@@ -363,6 +359,13 @@ function FrontendScriptApi(startNote, currentNote, originEntity = null, tabConte
* @return {Promise}
*/
this.setHoistedNoteId = hoistedNoteService.setHoistedNoteId;
/**
* @method
* @param {string} keyboardShortcut - e.g. "ctrl+shift+a"
* @param {function} handler
*/
this.bindGlobalShortcut = utils.bindGlobalShortcut;
}
export default FrontendScriptApi;

View File

@@ -3,12 +3,16 @@ import server from "./server.js";
import tree from "./tree.js";
import noteDetailService from "./note_detail.js";
let hoistedNoteId;
let hoistedNoteId = 'root';
optionsService.waitForOptions().then(options => {
hoistedNoteId = options.get('hoistedNoteId');
});
function getHoistedNoteNoPromise() {
return hoistedNoteId;
}
async function getHoistedNoteId() {
await optionsService.waitForOptions();
@@ -49,6 +53,7 @@ async function isRootNode(node) {
export default {
getHoistedNoteId,
getHoistedNoteNoPromise,
setHoistedNoteId,
unhoist,
isTopLevelNode,

View File

@@ -63,7 +63,7 @@ ws.subscribeToMessages(async message => {
toastService.showPersistent(toast);
await treeService.reloadNotes([message.parentNoteId]);
await treeService.reloadNotes([message.result.parentNoteId]);
if (message.result.importedNoteId) {
const node = await treeService.activateNote(message.result.importedNoteId);

View File

@@ -119,60 +119,54 @@ export default class LinkMap {
stop: params => {}
});
return $noteBox;
};
this.renderer = new Springy.Renderer(
layout,
() => {},
(edge, p1, p2) => {
const connectionId = this.linkMapContainerId + '-' + edge.source.id + '-' + edge.target.id;
this.renderer = new Springy.Renderer(layout);
await this.renderer.start(500);
if ($("#" + connectionId).length > 0) {
return;
}
layout.eachNode((node, point) => {
const $noteBox = getNoteBox(node.id);
const middleW = this.$linkMapContainer.width() / 2;
const middleH = this.$linkMapContainer.height() / 2;
getNoteBox(edge.source.id);
getNoteBox(edge.target.id);
$noteBox
.css("left", (middleW + point.p.x * 100) + "px")
.css("top", (middleH + point.p.y * 100) + "px");
const connection = this.jsPlumbInstance.connect({
source: this.noteIdToId(edge.source.id),
target: this.noteIdToId(edge.target.id),
type: 'link'
});
if (connection) {
$(connection.canvas)
.prop("id", connectionId)
.addClass('link-' + edge.source.id)
.addClass('link-' + edge.target.id);
}
else {
console.log(`connection not created for`, edge);
}
},
(node, p) => {
const $noteBox = getNoteBox(node.id);
const middleW = this.$linkMapContainer.width() / 2;
const middleH = this.$linkMapContainer.height() / 2;
$noteBox
.css("left", (middleW + p.x * 100) + "px")
.css("top", (middleH + p.y * 100) + "px");
if ($noteBox.hasClass("link-map-active-note")) {
this.moveToCenterOfElement($noteBox[0]);
}
},
() => {},
() => {},
() => {
this.jsPlumbInstance.repaintEverything();
if ($noteBox.hasClass("link-map-active-note")) {
this.moveToCenterOfElement($noteBox[0]);
}
);
});
this.renderer.start();
layout.eachEdge(edge => {
const connectionId = this.linkMapContainerId + '-' + edge.source.id + '-' + edge.target.id;
if ($("#" + connectionId).length > 0) {
return;
}
getNoteBox(edge.source.id);
getNoteBox(edge.target.id);
const connection = this.jsPlumbInstance.connect({
source: this.noteIdToId(edge.source.id),
target: this.noteIdToId(edge.target.id),
type: 'link'
});
if (connection) {
$(connection.canvas)
.prop("id", connectionId)
.addClass('link-' + edge.source.id)
.addClass('link-' + edge.target.id);
}
else {
console.log(`connection not created for`, edge);
}
});
this.jsPlumbInstance.repaintEverything();
}
moveToCenterOfElement(element) {

View File

@@ -250,7 +250,9 @@ async function loadNoteDetail(origNotePath, options = {}) {
async function loadNote(noteId) {
const row = await server.get('notes/' + noteId);
return new NoteFull(treeCache, row);
const noteShort = await treeCache.getNote(noteId);
return new NoteFull(treeCache, row, noteShort);
}
async function filterTabs(noteId) {

View File

@@ -64,15 +64,16 @@ async function call(method, url, data, headers = {}) {
});
}
else {
return await ajax(url, method, data);
return await ajax(url, method, data, headers);
}
}
async function ajax(url, method, data) {
async function ajax(url, method, data, headers) {
const options = {
url: baseApiUrl + url,
type: method,
headers: getHeaders()
headers: getHeaders(headers),
timeout: 60000
};
if (data) {

View File

@@ -45,12 +45,12 @@ function showPersistent(options) {
}
if (options.closeAfter) {
setTimeout(() => $toast.toast('dispose'), options.closeAfter);
setTimeout(() => $toast.remove(), options.closeAfter);
}
}
function closePersistent(id) {
$("#toast-persistent-" + id).toast("dispose");
$("#toast-" + id).remove();
}
function showMessage(message, delay = 2000) {

View File

@@ -48,7 +48,7 @@ function getActiveNode() {
async function getNodesByBranchId(branchId) {
utils.assertArguments(branchId);
const branch = await treeCache.getBranch(branchId);
const branch = treeCache.getBranch(branchId);
return getNodesByNoteId(branch.noteId).filter(node => node.data.branchId === branchId);
}
@@ -64,7 +64,7 @@ function getNodesByNoteId(noteId) {
async function setPrefix(branchId, prefix) {
utils.assertArguments(branchId);
const branch = await treeCache.getBranch(branchId);
const branch = treeCache.getBranch(branchId);
branch.prefix = prefix;
@@ -75,7 +75,7 @@ async function setPrefix(branchId, prefix) {
async function setNodeTitleWithPrefix(node) {
const noteTitle = await treeUtils.getNoteTitle(node.data.noteId);
const branch = await treeCache.getBranch(node.data.branchId);
const branch = treeCache.getBranch(node.data.branchId);
const prefix = branch.prefix;
@@ -559,14 +559,10 @@ function getHashValueFromAddress() {
return str.split("-");
}
async function loadTreeCache() {
async function loadTree() {
const resp = await server.get('tree');
treeCache.load(resp.notes, resp.branches, resp.relations);
}
async function loadTree() {
await loadTreeCache();
treeCache.load(resp.notes, resp.branches);
return await treeBuilder.prepareTree();
}
@@ -670,10 +666,8 @@ async function createNote(node, parentNoteId, target, extraOptions = {}) {
noteDetailService.addDetailLoadedListener(note.noteId, noteDetailService.focusAndSelectTitle);
const noteEntity = new NoteShort(treeCache, note);
const branchEntity = new Branch(treeCache, branch);
treeCache.add(noteEntity, branchEntity);
const noteEntity = await treeCache.getNote(note.noteId);
const branchEntity = treeCache.getBranch(branch.branchId);
let newNode = {
title: newNoteName,
@@ -682,6 +676,7 @@ async function createNote(node, parentNoteId, target, extraOptions = {}) {
refKey: branchEntity.noteId,
branchId: branchEntity.branchId,
isProtected: extraOptions.isProtected,
type: noteEntity.type,
extraClasses: await treeBuilder.getExtraClasses(noteEntity),
icon: await treeBuilder.getIcon(noteEntity),
folder: extraOptions.type === 'search',
@@ -826,33 +821,41 @@ async function checkFolderStatus(node) {
node.folder = note.type === 'search' || note.getChildNoteIds().length > 0;
node.icon = await treeBuilder.getIcon(note);
node.extraClasses = await treeBuilder.getExtraClasses(note);
node.renderTitle();
}
async function reloadNotes(noteIds) {
async function reloadNotes(noteIds, activateNotePath = null) {
if (noteIds.length === 0) {
return;
}
console.debug("Reloading notes", noteIds);
await treeCache.reloadNotes(noteIds);
await treeCache.reloadNotesAndTheirChildren(noteIds);
const activeNotePath = noteDetailService.getActiveTabNotePath();
if (!activateNotePath) {
activateNotePath = noteDetailService.getActiveTabNotePath();
}
for (const noteId of noteIds) {
for (const node of getNodesByNoteId(noteId)) {
await node.load(true);
const branch = treeCache.getBranch(node.data.branchId, true);
await checkFolderStatus(node);
if (!branch) {
node.remove();
}
else {
await node.load(true);
await checkFolderStatus(node);
}
}
}
if (activeNotePath) {
const node = await getNodeFromPath(activeNotePath);
if (activateNotePath) {
const node = await getNodeFromPath(activateNotePath);
if (node) {
await node.setActive(true, {noEvents: true}); // this node has been already active so no need to fire events again
await node.setActive(true);
}
}
}
@@ -929,7 +932,6 @@ export default {
getNodesByNoteId,
checkFolderStatus,
reloadNotes,
loadTreeCache,
expandToNote,
getNodeFromPath,
resolveNotePath,

View File

@@ -1,6 +1,4 @@
import utils from "./utils.js";
import Branch from "../entities/branch.js";
import server from "./server.js";
import treeCache from "./tree_cache.js";
import ws from "./ws.js";
import hoistedNoteService from "./hoisted_note.js";
@@ -11,7 +9,7 @@ async function prepareTree() {
let hoistedBranch;
if (hoistedNoteId === 'root') {
hoistedBranch = await treeCache.getBranch('root');
hoistedBranch = treeCache.getBranch('root');
}
else {
const hoistedNote = await treeCache.getNote(hoistedNoteId);
@@ -72,6 +70,7 @@ async function prepareNode(branch) {
parentNoteId: branch.parentNoteId,
branchId: branch.branchId,
isProtected: note.isProtected,
noteType: note.type,
title: utils.escapeHtml(title),
extraClasses: await getExtraClasses(note),
icon: await getIcon(note),
@@ -110,26 +109,11 @@ async function prepareRealBranch(parentNote) {
}
async function prepareSearchBranch(note) {
const results = await server.get('search-note/' + note.noteId);
await treeCache.reloadNotes([note.noteId]);
// force to load all the notes at once instead of one by one
await treeCache.getNotes(results.map(res => res.noteId));
const newNote = await treeCache.getNote(note.noteId);
for (const result of results) {
const origBranch = await treeCache.getBranch(result.branchId);
const branch = new Branch(treeCache, {
branchId: "virt" + utils.randomString(10),
noteId: result.noteId,
parentNoteId: note.noteId,
prefix: origBranch.prefix,
virtual: true
});
treeCache.addBranch(branch);
}
return await prepareRealBranch(note);
return await prepareRealBranch(newNote);
}
async function getExtraClasses(note) {

View File

@@ -1,33 +1,22 @@
import utils from "./utils.js";
import Branch from "../entities/branch.js";
import NoteShort from "../entities/note_short.js";
import toastService from "./toast.js";
import ws from "./ws.js";
import server from "./server.js";
/**
* TreeCache keeps a read only cache of note tree structure in frontend's memory.
* - notes are loaded lazily when unknown noteId is requested
* - when note is loaded, all its parent and child branches are loaded as well. For a branch to be used, it's not must be loaded before
* - deleted notes are present in the cache as well, but they don't have any branches. As a result check for deleted branch is done by presence check - if the branch is not there even though the corresponding note has been loaded, we can infer it is deleted.
*
* Note and branch deletions are corner cases and usually not needed.
*/
class TreeCache {
constructor() {
this.init();
}
load(noteRows, branchRows, relations) {
this.init();
this.addResp(noteRows, branchRows, relations);
}
init() {
/** @type {Object.<string, string>} */
this.parents = {};
/** @type {Object.<string, string>} */
this.children = {};
/** @type {Object.<string, string>} */
this.childParentToBranch = {};
/** @type {Object.<string, NoteShort>} */
this.notes = {};
@@ -35,61 +24,107 @@ class TreeCache {
this.branches = {};
}
addResp(noteRows, branchRows, relations) {
for (const noteRow of noteRows) {
const note = new NoteShort(this, noteRow);
load(noteRows, branchRows) {
this.init();
this.notes[note.noteId] = note;
}
this.addResp(noteRows, branchRows);
}
addResp(noteRows, branchRows) {
const branchesByNotes = {};
for (const branchRow of branchRows) {
const branch = new Branch(this, branchRow);
this.addBranch(branch);
branchesByNotes[branch.noteId] = branchesByNotes[branch.noteId] || [];
branchesByNotes[branch.noteId].push(branch);
branchesByNotes[branch.parentNoteId] = branchesByNotes[branch.parentNoteId] || [];
branchesByNotes[branch.parentNoteId].push(branch);
}
for (const relation of relations) {
this.addBranchRelationship(relation.branchId, relation.childNoteId, relation.parentNoteId);
}
}
for (const noteRow of noteRows) {
const {noteId} = noteRow;
/**
* Reload notes and their children.
*/
async reloadNotesAndTheirChildren(noteIds) {
// first load the data before clearing the cache
const resp = await server.post('tree/load', { noteIds });
const oldNote = this.notes[noteId];
for (const noteId of noteIds) {
for (const childNoteId of this.children[noteId] || []) {
this.parents[childNoteId] = this.parents[childNoteId].filter(p => p !== noteId);
if (oldNote) {
for (const childNoteId of oldNote.children) {
const childNote = this.notes[childNoteId];
const branchId = this.getBranchIdByChildParent(childNoteId, noteId);
if (childNote) {
childNote.parents = childNote.parents.filter(p => p !== noteId);
delete this.branches[branchId];
delete this.childParentToBranch[childNoteId + '-' + noteId];
delete this.branches[childNote.parentToBranch[noteId]];
delete childNote.parentToBranch[noteId];
}
}
for (const parentNoteId of oldNote.parents) {
const parentNote = this.notes[parentNoteId];
if (parentNote) {
parentNote.children = parentNote.children.filter(p => p !== noteId);
delete this.branches[parentNote.childToBranch[noteId]];
delete parentNote.childToBranch[noteId];
}
}
}
this.children[noteId] = [];
for (const branch of branchesByNotes[noteId] || []) { // can be empty for deleted notes
this.branches[branch.branchId] = branch;
}
delete this.notes[noteId];
const note = new NoteShort(this, noteRow, branchesByNotes[noteId] || []);
this.notes[note.noteId] = note;
for (const childNoteId of note.children) {
const childNote = this.notes[childNoteId];
if (childNote) {
childNote.addParent(noteId, note.childToBranch[childNoteId]);
}
}
for (const parentNoteId of note.parents) {
const parentNote = this.notes[parentNoteId];
if (parentNote) {
parentNote.addChild(noteId, note.parentToBranch[parentNoteId]);
}
}
}
this.addResp(resp.notes, resp.branches, resp.relations);
}
/**
* Reloads parents of given noteId - useful when new note is created to make sure note is loaded
* in a correct order.
*/
async reloadParents(noteId) {
// to be able to find parents we need first to make sure it is actually loaded
await this.getNote(noteId);
async reloadNotes(noteIds) {
const resp = await server.post('tree/load', { noteIds });
await this.reloadNotesAndTheirChildren(this.parents[noteId] || []);
this.addResp(resp.notes, resp.branches);
// this is done to load the new parents for the noteId
await this.reloadNotesAndTheirChildren([noteId]);
for (const note of resp.notes) {
if (note.type === 'search') {
const searchResults = await server.get('search-note/' + note.noteId);
// force to load all the notes at once instead of one by one
await treeCache.getNotes(searchResults.map(res => res.noteId));
const branches = resp.branches.filter(b => b.noteId === note.noteId || b.parentNoteId === note.noteId);
searchResults.forEach((result, index) => branches.push({
// branchId should be repeatable since sometimes we reload some notes without rerendering the tree
branchId: "virt" + result.noteId + '-' + note.noteId,
noteId: result.noteId,
parentNoteId: note.noteId,
prefix: treeCache.getBranch(result.branchId).prefix,
notePosition: (index + 1) * 10
}));
// update this note with standard (parent) branches + virtual (children) branches
treeCache.addResp([note], branches);
}
}
}
/** @return {Promise<NoteShort[]>} */
@@ -97,9 +132,7 @@ class TreeCache {
const missingNoteIds = noteIds.filter(noteId => this.notes[noteId] === undefined);
if (missingNoteIds.length > 0) {
const resp = await server.post('tree/load', { noteIds: missingNoteIds });
this.addResp(resp.notes, resp.branches, resp.relations);
await this.reloadNotes(missingNoteIds);
}
return noteIds.map(noteId => {
@@ -130,113 +163,21 @@ class TreeCache {
return (await this.getNotes([noteId], silentNotFoundError))[0];
}
addBranch(branch) {
this.branches[branch.branchId] = branch;
this.addBranchRelationship(branch.branchId, branch.noteId, branch.parentNoteId);
getBranches(branchIds) {
return branchIds
.map(branchId => this.getBranch(branchId))
.filter(b => b !== null);
}
addBranchRelationship(branchId, childNoteId, parentNoteId) {
if (parentNoteId === 'none') { // applies only to root element
return;
}
this.childParentToBranch[childNoteId + '-' + parentNoteId] = branchId;
this.parents[childNoteId] = this.parents[childNoteId] || [];
if (!this.parents[childNoteId].includes(parentNoteId)) {
this.parents[childNoteId].push(parentNoteId);
}
this.children[parentNoteId] = this.children[parentNoteId] || [];
if (!this.children[parentNoteId].includes(childNoteId)) {
this.children[parentNoteId].push(childNoteId);
}
}
add(note, branch) {
this.notes[note.noteId] = note;
this.addBranch(branch);
}
async getBranches(branchIds) {
const missingBranchIds = branchIds.filter(branchId => this.branches[branchId] === undefined);
if (missingBranchIds.length > 0) {
const resp = await server.post('tree/load', { branchIds: branchIds });
this.addResp(resp.notes, resp.branches, resp.relations);
}
return branchIds.map(branchId => {
if (!this.branches[branchId]) {
throw new Error(`Can't find branch ${branchId}`);
/** @return {Branch} */
getBranch(branchId, silentNotFoundError = false) {
if (!(branchId in this.branches)) {
if (!silentNotFoundError) {
console.error(`Not existing branch ${branchId}`);
}
else {
return this.branches[branchId];
}
});
}
/** @return Branch */
async getBranch(branchId) {
return (await this.getBranches([branchId]))[0];
}
/** @return Branch */
async getBranchByChildParent(childNoteId, parentNoteId) {
const branchId = this.getBranchIdByChildParent(childNoteId, parentNoteId);
return await this.getBranch(branchId);
}
getBranchIdByChildParent(childNoteId, parentNoteId) {
const key = childNoteId + '-' + parentNoteId;
const branchId = this.childParentToBranch[key];
if (!branchId) {
toastService.throwError("Cannot find branch for child-parent=" + key);
}
return branchId;
}
/* Move note from one parent to another. */
async moveNote(childNoteId, oldParentNoteId, newParentNoteId, beforeNoteId, afterNoteId) {
utils.assertArguments(childNoteId, oldParentNoteId, newParentNoteId);
if (oldParentNoteId === newParentNoteId) {
return;
}
const branchId = this.childParentToBranch[childNoteId + '-' + oldParentNoteId];
const branch = await this.getBranch(branchId);
branch.parentNoteId = newParentNoteId;
this.childParentToBranch[childNoteId + '-' + newParentNoteId] = branchId;
delete this.childParentToBranch[childNoteId + '-' + oldParentNoteId]; // this is correct because we know that oldParentId isn't same as newParentId
// remove old associations
this.parents[childNoteId] = this.parents[childNoteId].filter(p => p !== oldParentNoteId);
this.children[oldParentNoteId] = this.children[oldParentNoteId].filter(ch => ch !== childNoteId);
// add new associations
this.parents[childNoteId].push(newParentNoteId);
const children = this.children[newParentNoteId] = this.children[newParentNoteId] || []; // this might be first child
// we try to put the note into precise order which might be used again by lazy-loaded nodes
if (beforeNoteId && children.includes(beforeNoteId)) {
children.splice(children.indexOf(beforeNoteId), 0, childNoteId);
}
else if (afterNoteId && children.includes(afterNoteId)) {
children.splice(children.indexOf(afterNoteId) + 1, 0, childNoteId);
}
else {
children.push(childNoteId);
return this.branches[branchId];
}
}
}

View File

@@ -26,8 +26,8 @@ class TreeContextMenu {
}
async getContextMenuItems() {
const branch = await treeCache.getBranch(this.node.data.branchId);
const note = await treeCache.getNote(this.node.data.noteId);
const branch = treeCache.getBranch(this.node.data.branchId);
const parentNote = await treeCache.getNote(branch.parentNoteId);
const isNotRoot = note.noteId !== 'root';
const isHoisted = note.noteId === await hoistedNoteService.getHoistedNoteId();
@@ -39,8 +39,9 @@ class TreeContextMenu {
const noSelectedNotes = selNodes.length === 0
|| (selNodes.length === 1 && selNodes[0] === this.node);
const insertNoteAfterEnabled = isNotRoot && !isHoisted && parentNote.type !== 'search';
const insertChildNoteEnabled = note.type !== 'search';
const notSearch = note.type !== 'search';
const parentNotSearch = parentNote.type !== 'search';
const insertNoteAfterEnabled = isNotRoot && !isHoisted && parentNotSearch;
return [
{ title: "Open in new tab", cmd: "openInTab", uiIcon: "empty", enabled: noSelectedNotes },
@@ -48,15 +49,15 @@ class TreeContextMenu {
items: insertNoteAfterEnabled ? this.getNoteTypeItems("insertNoteAfter") : null,
enabled: insertNoteAfterEnabled && noSelectedNotes },
{ title: "Insert child note <kbd>Ctrl+P</kbd>", cmd: "insertChildNote", uiIcon: "plus",
items: insertChildNoteEnabled ? this.getNoteTypeItems("insertChildNote") : null,
enabled: insertChildNoteEnabled && noSelectedNotes },
items: notSearch ? this.getNoteTypeItems("insertChildNote") : null,
enabled: notSearch && noSelectedNotes },
{ title: "Delete <kbd>Delete</kbd>", cmd: "delete", uiIcon: "trash",
enabled: isNotRoot && !isHoisted && parentNote.type !== 'search' },
enabled: isNotRoot && !isHoisted && parentNotSearch },
{ title: "----" },
isHoisted ? null : { title: "Hoist note <kbd>Ctrl-H</kbd>", cmd: "hoist", uiIcon: "empty", enabled: noSelectedNotes },
isHoisted ? null : { title: "Hoist note <kbd>Ctrl-H</kbd>", cmd: "hoist", uiIcon: "empty", enabled: noSelectedNotes && notSearch },
!isHoisted || !isNotRoot ? null : { title: "Unhoist note <kbd>Ctrl-H</kbd>", cmd: "unhoist", uiIcon: "arrow-up" },
{ title: "Edit branch prefix <kbd>F2</kbd>", cmd: "editBranchPrefix", uiIcon: "empty",
enabled: isNotRoot && parentNote.type !== 'search' && noSelectedNotes},
enabled: isNotRoot && parentNotSearch && noSelectedNotes},
{ title: "----" },
{ title: "Protect subtree", cmd: "protectSubtree", uiIcon: "shield-check", enabled: noSelectedNotes },
{ title: "Unprotect subtree", cmd: "unprotectSubtree", uiIcon: "shield-close", enabled: noSelectedNotes },
@@ -64,22 +65,22 @@ class TreeContextMenu {
{ title: "Copy / clone <kbd>Ctrl+C</kbd>", cmd: "copy", uiIcon: "files",
enabled: isNotRoot },
{ title: "Cut <kbd>Ctrl+X</kbd>", cmd: "cut", uiIcon: "scissors",
enabled: isNotRoot },
enabled: isNotRoot && !isHoisted && parentNotSearch },
{ title: "Paste into <kbd>Ctrl+V</kbd>", cmd: "pasteInto", uiIcon: "clipboard",
enabled: !clipboard.isEmpty() && note.type !== 'search' && noSelectedNotes },
enabled: !clipboard.isEmpty() && notSearch && noSelectedNotes },
{ title: "Paste after", cmd: "pasteAfter", uiIcon: "clipboard",
enabled: !clipboard.isEmpty() && isNotRoot && parentNote.type !== 'search' && noSelectedNotes },
enabled: !clipboard.isEmpty() && isNotRoot && parentNotSearch && noSelectedNotes },
{ title: "Duplicate note here", cmd: "duplicateNote", uiIcon: "empty",
enabled: noSelectedNotes && (!note.isProtected || protectedSessionHolder.isProtectedSessionAvailable()) },
enabled: noSelectedNotes && parentNotSearch && (!note.isProtected || protectedSessionHolder.isProtectedSessionAvailable()) },
{ title: "----" },
{ title: "Export", cmd: "export", uiIcon: "empty",
enabled: note.type !== 'search' && noSelectedNotes },
enabled: notSearch && noSelectedNotes },
{ title: "Import into note", cmd: "importIntoNote", uiIcon: "empty",
enabled: note.type !== 'search' && noSelectedNotes },
enabled: notSearch && noSelectedNotes },
{ title: "----" },
{ title: "Collapse subtree <kbd>Alt+-</kbd>", cmd: "collapseSubtree", uiIcon: "align-justify", enabled: noSelectedNotes },
{ title: "Force note sync", cmd: "forceNoteSync", uiIcon: "refresh", enabled: noSelectedNotes },
{ title: "Sort alphabetically <kbd>Alt+S</kbd>", cmd: "sortAlphabetically", uiIcon: "empty", enabled: noSelectedNotes }
{ title: "Sort alphabetically <kbd>Alt+S</kbd>", cmd: "sortAlphabetically", uiIcon: "empty", enabled: noSelectedNotes && notSearch }
].filter(row => row !== null);
}
@@ -156,7 +157,7 @@ class TreeContextMenu {
hoistedNoteService.unhoist();
}
else if (cmd === "duplicateNote") {
const branch = await treeCache.getBranch(this.node.data.branchId);
const branch = treeCache.getBranch(this.node.data.branchId);
treeService.duplicateNote(this.node.data.noteId, branch.parentNoteId);
}

View File

@@ -3,6 +3,7 @@ import treeChangesService from "./branches.js";
import treeService from "./tree.js";
import hoistedNoteService from "./hoisted_note.js";
import clipboard from "./clipboard.js";
import treeCache from "./tree_cache.js";
const keyBindings = {
"del": node => {
@@ -130,12 +131,16 @@ const keyBindings = {
}
},
"ctrl+h": node => {
hoistedNoteService.getHoistedNoteId().then(hoistedNoteId => {
hoistedNoteService.getHoistedNoteId().then(async hoistedNoteId => {
if (node.data.noteId === hoistedNoteId) {
hoistedNoteService.unhoist();
}
else {
hoistedNoteService.setHoistedNoteId(node.data.noteId);
const note = await treeCache.getNote(node.data.noteId);
if (note.type !== 'search') {
hoistedNoteService.setHoistedNoteId(node.data.noteId);
}
}
});

View File

@@ -59,10 +59,14 @@ async function getNoteTitle(noteId, parentNoteId = null) {
let {title} = note;
if (parentNoteId !== null) {
const branch = await treeCache.getBranchByChildParent(noteId, parentNoteId);
const branchId = note.parentToBranch[parentNoteId];
if (branch && branch.prefix) {
title = branch.prefix + ' - ' + title;
if (branchId) {
const branch = treeCache.getBranch(branchId);
if (branch && branch.prefix) {
title = branch.prefix + ' - ' + title;
}
}
}

View File

@@ -1,6 +1,5 @@
import utils from './utils.js';
import toastService from "./toast.js";
import treeService from "./tree.js";
const $outstandingSyncsCount = $("#outstanding-syncs-count");
@@ -48,14 +47,15 @@ async function handleMessage(event) {
}
if (message.type === 'sync') {
const syncRows = message.data;
lastPingTs = Date.now();
$outstandingSyncsCount.html(message.outstandingSyncs);
if (message.data.length > 0) {
console.debug(utils.now(), "Sync data: ", message.data);
if (syncRows.length > 0) {
console.debug(utils.now(), "Sync data: ", syncRows);
syncDataQueue.push(...message.data);
syncDataQueue.push(...syncRows);
// first wait for all the preceding consumers to finish
while (consumeQueuePromise) {
@@ -70,6 +70,8 @@ async function handleMessage(event) {
// finish and set to null to signal somebody else can pick it up
consumeQueuePromise = null;
}
checkSyncIdListeners();
}
else if (message.type === 'sync-hash-check-failed') {
toastService.showError("Sync check failed!", 60000);
@@ -82,8 +84,6 @@ async function handleMessage(event) {
let syncIdReachedListeners = [];
function waitForSyncId(desiredSyncId) {
console.log("Waiting for ", desiredSyncId);
if (desiredSyncId <= lastSyncId) {
return Promise.resolve();
}
@@ -91,13 +91,26 @@ function waitForSyncId(desiredSyncId) {
return new Promise((res, rej) => {
syncIdReachedListeners.push({
desiredSyncId,
resolvePromise: res
resolvePromise: res,
start: Date.now()
})
});
}
function checkSyncIdListeners() {
syncIdReachedListeners
.filter(l => l.desiredSyncId <= lastSyncId)
.forEach(l => l.resolvePromise());
syncIdReachedListeners = syncIdReachedListeners
.filter(l => l.desiredSyncId > lastSyncId);
syncIdReachedListeners.filter(l => Date.now() > l.start - 60000)
.forEach(l => console.log(`Waiting for syncId ${l.desiredSyncId} while current is ${lastSyncId} for ${Math.floor((Date.now() - l.start) / 1000)}s`));
}
async function consumeSyncData() {
if (syncDataQueue.length >= 0) {
if (syncDataQueue.length > 0) {
const allSyncData = syncDataQueue;
syncDataQueue = [];
@@ -111,13 +124,6 @@ async function consumeSyncData() {
lastSyncId = allSyncData[allSyncData.length - 1].id;
}
syncIdReachedListeners
.filter(l => l.desiredSyncId <= lastSyncId)
.forEach(l => l.resolvePromise());
syncIdReachedListeners = syncIdReachedListeners
.filter(l => l.desiredSyncId > lastSyncId);
}
function connectWebSocket() {
@@ -140,7 +146,7 @@ setTimeout(() => {
setInterval(async () => {
if (Date.now() - lastPingTs > 30000) {
console.log("Lost connection to server");
console.log(utils.now(), "Lost connection to server");
}
if (ws.readyState === ws.OPEN) {
@@ -150,13 +156,28 @@ setTimeout(() => {
}));
}
else if (ws.readyState === ws.CLOSED || ws.readyState === ws.CLOSING) {
console.log("WS closed or closing, trying to reconnect");
console.log(utils.now(), "WS closed or closing, trying to reconnect");
ws = connectWebSocket();
}
}, 1000);
}, 0);
subscribeToMessages(message => {
if (message.type === 'sync-pull-in-progress') {
toastService.showPersistent({
id: 'sync',
title: "Sync status",
message: "Sync update in progress",
icon: "refresh"
});
}
else if (message.type === 'sync-pull-finished') {
// this gives user a chance to see the toast in case of fast sync finish
setTimeout(() => toastService.closePersistent('sync'), 1000);
}
});
export default {
logError,
subscribeToMessages,

View File

@@ -32,17 +32,16 @@ class EditedNotesWidget extends StandardWidget {
return;
}
const noteIds = editedNotes.flatMap(note => note.notePath);
const noteIds = editedNotes.flatMap(note => note.noteId);
await treeCache.getNotes(noteIds, true); // preload all at once
const $list = $('<ul>');
for (const editedNote of editedNotes) {
const note = await treeCache.getNote(editedNote.noteId, true);
const $item = $("<li>");
if (!note) {
if (editedNote.isDeleted) {
$item.append($("<i>").text(editedNote.title + " (deleted)"));
}
else {

View File

@@ -29,6 +29,7 @@ class LinkMapWidget extends StandardWidget {
}
async doRenderBody() {
this.$body.css('opacity', 0);
this.$body.html(TPL);
const $linkMapContainer = this.$body.find('.link-map-container');
@@ -43,6 +44,8 @@ class LinkMapWidget extends StandardWidget {
});
await this.linkMapService.render();
this.$body.animate({opacity: 1}, 300);
}
cleanup() {

View File

@@ -3,16 +3,14 @@
const cloningService = require('../../services/cloning');
async function cloneNoteToParent(req) {
const noteId = req.params.noteId;
const parentNoteId = req.params.parentNoteId;
const prefix = req.body.prefix;
const {noteId, parentNoteId} = req.params;
const {prefix} = req.body;
return await cloningService.cloneNoteToParent(noteId, parentNoteId, prefix);
}
async function cloneNoteAfter(req) {
const noteId = req.params.noteId;
const afterBranchId = req.params.afterBranchId;
const {noteId, afterBranchId} = req.params;
return await cloningService.cloneNoteAfter(noteId, afterBranchId);
}

View File

@@ -64,7 +64,9 @@ async function update(name, value) {
return false;
}
log.info(`Updating option ${name} to ${value}`);
if (name !== 'openTabs') {
log.info(`Updating option ${name} to ${value}`);
}
await optionService.setOption(name, value);

View File

@@ -6,9 +6,17 @@ const protectedSessionService = require('../../services/protected_session');
const noteCacheService = require('../../services/note_cache');
async function getNotes(noteIds) {
// we return also deleted notes which have been specifically asked for
const notes = await sql.getManyRows(`
SELECT noteId, title, isProtected, type, mime
FROM notes WHERE isDeleted = 0 AND noteId IN (???)`, noteIds);
SELECT
noteId,
title,
isProtected,
type,
mime,
isDeleted
FROM notes
WHERE noteId IN (???)`, noteIds);
const cssClassLabels = await sql.getManyRows(`
SELECT noteId, value FROM attributes WHERE isDeleted = 0 AND type = 'label'
@@ -32,6 +40,8 @@ async function getNotes(noteIds) {
protectedSessionService.decryptNotes(notes);
await noteCacheService.loadedPromise;
notes.forEach(note => {
note.isProtected = !!note.isProtected;
note.archived = noteCacheService.isArchived(note.noteId)
@@ -40,19 +50,31 @@ async function getNotes(noteIds) {
return notes;
}
async function getRelations(noteIds) {
// we need to fetch both parentNoteId and noteId matches because we can have loaded child
// of which only some of the parents has been loaded.
// also now with note hoisting, it is possible to have the note displayed without its parent chain being loaded
async function getNotesAndBranches(noteIds) {
noteIds = Array.from(new Set(noteIds));
const notes = await getNotes(noteIds);
const relations = await sql.getManyRows(`SELECT branchId, noteId AS 'childNoteId', parentNoteId, notePosition FROM branches WHERE isDeleted = 0
AND (parentNoteId IN (???) OR noteId IN (???))`, noteIds);
noteIds = notes.map(n => n.noteId);
// although we're fetching relations for multiple notes, ordering will stay correct for single note as well - relations are being added into tree cache in the order they were returned
// cannot use ORDER BY because of usage of getManyRows which is not a single SQL query
relations.sort((a, b) => a.notePosition > b.notePosition ? 1 : -1);
const branches = await sql.getManyRows(`
SELECT
branches.branchId,
branches.noteId,
branches.parentNoteId,
branches.notePosition,
branches.prefix,
branches.isExpanded
FROM branches
WHERE branches.isDeleted = 0
AND (branches.noteId IN (???) OR parentNoteId IN (???))`, noteIds);
return relations;
// sorting in memory is faster
branches.sort((a, b) => a.notePosition - b.notePosition < 0 ? -1 : 1);
return {
branches,
notes
};
}
async function getTree() {
@@ -60,7 +82,7 @@ async function getTree() {
// we fetch all branches of notes, even if that particular branch isn't visible
// this allows us to e.g. detect and properly display clones
const branches = await sql.getRows(`
let noteIds = await sql.getColumn(`
WITH RECURSIVE
tree(branchId, noteId, isExpanded) AS (
SELECT branchId, noteId, isExpanded FROM branches WHERE noteId = ?
@@ -69,44 +91,15 @@ async function getTree() {
JOIN tree ON branches.parentNoteId = tree.noteId
WHERE tree.isExpanded = 1 AND branches.isDeleted = 0
)
SELECT branches.* FROM tree JOIN branches USING(noteId) WHERE branches.isDeleted = 0 ORDER BY branches.notePosition`, [hoistedNoteId]);
SELECT noteId FROM tree`, [hoistedNoteId]);
// we also want root branch in there because all the paths start with root
branches.push(await sql.getRow(`SELECT * FROM branches WHERE branchId = 'root'`));
noteIds.push('root');
const noteIds = Array.from(new Set(branches.map(b => b.noteId)));
const notes = await getNotes(noteIds);
const relations = await getRelations(noteIds);
return {
branches,
notes,
relations
};
return await getNotesAndBranches(noteIds);
}
async function load(req) {
let noteIds = req.body.noteIds;
const branchIds = req.body.branchIds;
if (branchIds && branchIds.length > 0) {
noteIds = (await sql.getManyRows(`SELECT noteId FROM branches WHERE isDeleted = 0 AND branchId IN(???)`, branchIds))
.map(note => note.noteId);
}
const branches = await sql.getManyRows(`SELECT * FROM branches WHERE isDeleted = 0 AND noteId IN (???)`, noteIds);
const notes = await getNotes(noteIds);
const relations = await getRelations(noteIds);
return {
branches,
notes,
relations
};
return await getNotesAndBranches(req.body.noteIds);
}
module.exports = {

View File

@@ -1 +1 @@
module.exports = { buildDate:"2019-10-21T21:59:17+02:00", buildRevision: "132360b46bfd326d8f50d71fed04ab4af969f3cb" };
module.exports = { buildDate:"2019-11-05T21:49:16+01:00", buildRevision: "72fda89360d924aedf7a26216fd387346254dfd3" };

View File

@@ -75,7 +75,7 @@ async function cloneNoteAfter(noteId, afterBranchId) {
const validationResult = await treeService.validateParentChild(afterNote.parentNoteId, noteId);
if (!validationResult.result) {
if (!validationResult.success) {
return validationResult;
}

View File

@@ -57,7 +57,7 @@ async function checkContentHashes(otherHashes) {
if (key !== 'recent_notes') {
// let's not get alarmed about recent notes which get updated often and can cause failures in race conditions
await ws.sendMessageToAllClients({type: 'sync-hash-check-failed'});
ws.sendMessageToAllClients({type: 'sync-hash-check-failed'});
}
}
}

View File

@@ -42,6 +42,10 @@ function request(req) {
}
}
if (req.url.includes(".js.map") || req.url.includes(".css.map")) {
return;
}
logger.info(req.method + " " + req.url);
}

View File

@@ -8,6 +8,10 @@ const hoistedNoteService = require('./hoisted_note');
const stringSimilarity = require('string-similarity');
let loaded = false;
let loadedPromiseResolve;
/** Is resolved after the initial load */
let loadedPromise = new Promise(res => loadedPromiseResolve = res);
let noteTitles = {};
let protectedNoteTitles = {};
let noteIds;
@@ -24,9 +28,9 @@ async function load() {
prefixes = await sql.getMap(`SELECT noteId || '-' || parentNoteId, prefix FROM branches WHERE prefix IS NOT NULL AND prefix != ''`);
const relations = await sql.getRows(`SELECT branchId, noteId, parentNoteId FROM branches WHERE isDeleted = 0`);
const branches = await sql.getRows(`SELECT branchId, noteId, parentNoteId FROM branches WHERE isDeleted = 0`);
for (const rel of relations) {
for (const rel of branches) {
childToParent[rel.noteId] = childToParent[rel.noteId] || [];
childToParent[rel.noteId].push(rel.parentNoteId);
childParentToBranchId[`${rel.noteId}-${rel.parentNoteId}`] = rel.branchId;
@@ -43,6 +47,7 @@ async function load() {
}
loaded = true;
loadedPromiseResolve();
}
async function loadProtectedNotes() {
@@ -98,7 +103,7 @@ async function findNotes(query) {
let noteIds = Object.keys(noteTitles);
if (protectedSessionService.isProtectedSessionAvailable()) {
noteIds = noteIds.concat(Object.keys(protectedNoteTitles));
noteIds = [...new Set(noteIds.concat(Object.keys(protectedNoteTitles)))];
}
for (const noteId of noteIds) {
@@ -169,34 +174,6 @@ async function findNotes(query) {
return apiResults;
}
function isNotePathArchived(notePath) {
// if the note is archived directly
if (archived[notePath[notePath.length - 1]] !== undefined) {
return true;
}
for (let i = 0; i < notePath.length - 1; i++) {
// this is going through parents so archived must be inheritable
if (archived[notePath[i]] === 1) {
return true;
}
}
return false;
}
/**
* This assumes that note is available. "archived" note means that there isn't a single non-archived note-path
* leading to this note.
*
* @param noteId
*/
function isArchived(noteId) {
const notePath = getSomePath(noteId);
return isNotePathArchived(notePath);
}
function search(noteId, tokens, path, results) {
if (tokens.length === 0) {
const retPath = getSomePath(noteId, path);
@@ -247,6 +224,34 @@ function search(noteId, tokens, path, results) {
}
}
function isNotePathArchived(notePath) {
// if the note is archived directly
if (archived[notePath[notePath.length - 1]] !== undefined) {
return true;
}
for (let i = 0; i < notePath.length - 1; i++) {
// this is going through parents so archived must be inheritable
if (archived[notePath[i]] === 1) {
return true;
}
}
return false;
}
/**
* This assumes that note is available. "archived" note means that there isn't a single non-archived note-path
* leading to this note.
*
* @param noteId
*/
function isArchived(noteId) {
const notePath = getSomePath(noteId);
return isNotePathArchived(notePath);
}
function getNoteTitle(noteId, parentNoteId) {
const prefix = prefixes[noteId + '-' + parentNoteId];
@@ -449,7 +454,10 @@ eventService.subscribe([eventService.ENTITY_CHANGED, eventService.ENTITY_DELETED
prefixes[branch.noteId + '-' + branch.parentNoteId] = branch.prefix;
}
childToParent[branch.noteId].push(branch.parentNoteId);
if (!childToParent[branch.noteId].includes(branch.parentNoteId)) {
childToParent[branch.noteId].push(branch.parentNoteId);
}
resortChildToParent(branch.noteId);
childParentToBranchId[branch.noteId + '-' + branch.parentNoteId] = branch.branchId;
@@ -476,7 +484,7 @@ eventService.subscribe([eventService.ENTITY_CHANGED, eventService.ENTITY_DELETED
// will sort the childs so that non-archived are first and archived at the end
// this is done so that non-archived paths are always explored as first when searching for note path
function resortChildToParent(noteId) {
if (!childToParent[noteId]) {
if (!(noteId in childToParent)) {
return;
}
@@ -494,14 +502,13 @@ function isAvailable(noteId) {
}
eventService.subscribe(eventService.ENTER_PROTECTED_SESSION, () => {
if (loaded) {
loadProtectedNotes();
}
loadedPromise.then(() => loadProtectedNotes());
});
sqlInit.dbReady.then(() => utils.stopWatch("Autocomplete load", load));
sqlInit.dbReady.then(() => utils.stopWatch("Note cache load", load));
module.exports = {
loadedPromise,
findNotes,
getNotePath,
getNoteTitleForPath,

View File

@@ -370,7 +370,7 @@ async function updateNote(noteId, noteUpdates) {
note.isProtected = noteUpdates.isProtected;
await note.save();
if (!['file', 'image'].includes(note.type)) {
if (!['file', 'image', 'render'].includes(note.type)) {
noteUpdates.content = await saveLinks(note, noteUpdates.content);
await note.setContent(noteUpdates.content);

View File

@@ -14,6 +14,7 @@ const syncOptions = require('./sync_options');
const syncMutexService = require('./sync_mutex');
const cls = require('./cls');
const request = require('./request');
const ws = require('./ws');
let proxyToggle = true;
@@ -116,6 +117,8 @@ async function doLogin() {
}
async function pullSync(syncContext) {
let appliedPulls = 0;
while (true) {
const lastSyncedPull = await getLastSyncedPull();
const changesUri = '/api/sync/changed?lastSyncId=' + lastSyncedPull;
@@ -140,16 +143,25 @@ async function pullSync(syncContext) {
for (const {sync, entity} of rows) {
if (!sourceIdService.isLocalSourceId(sync.sourceId)) {
if (appliedPulls === 0 && sync.entity !== 'recent_notes') { // send only for first
ws.syncPullInProgress();
appliedPulls++;
}
await syncUpdateService.updateEntity(sync, entity, syncContext.sourceId);
}
stats.outstandingPulls = resp.maxSyncId - sync.id;
}
await setLastSyncedPull(rows[rows.length - 1].sync.id);
}
if (appliedPulls > 0) {
ws.syncPullFinished();
}
log.info("Finished pull");
}
@@ -167,9 +179,6 @@ async function pushSync(syncContext) {
const filteredSyncs = syncs.filter(sync => {
if (sync.sourceId === syncContext.sourceId) {
// too noisy
//log.info(`Skipping push #${sync.id} ${sync.entityName} ${sync.entityId} because it originates from sync target`);
// this may set lastSyncedPush beyond what's actually sent (because of size limit)
// so this is applied to the database only if there's no actual update
// TODO: it would be better to simplify this somehow

View File

@@ -25,13 +25,13 @@ class TaskContext {
return taskContexts[taskId];
}
async increaseProgressCount() {
increaseProgressCount() {
this.progressCount++;
if (Date.now() - this.lastSentCountTs >= 300) {
this.lastSentCountTs = Date.now();
await ws.sendMessageToAllClients({
ws.sendMessageToAllClients({
type: 'task-progress-count',
taskId: this.taskId,
taskType: this.taskType,
@@ -41,8 +41,8 @@ class TaskContext {
}
}
async reportError(message) {
await ws.sendMessageToAllClients({
reportError(message) {
ws.sendMessageToAllClients({
type: 'task-error',
taskId: this.taskId,
taskType: this.taskType,
@@ -51,8 +51,8 @@ class TaskContext {
});
}
async taskSucceeded(result) {
await ws.sendMessageToAllClients({
taskSucceeded(result) {
ws.sendMessageToAllClients({
type: 'task-succeeded',
taskId: this.taskId,
taskType: this.taskType,

View File

@@ -2,6 +2,7 @@ const WebSocket = require('ws');
const utils = require('./utils');
const log = require('./log');
const sql = require('./sql');
const syncMutexService = require('./sync_mutex');
let webSocketServer;
@@ -31,7 +32,7 @@ function init(httpServer, sessionParser) {
log.error('JS Error: ' + message.error);
}
else if (message.type === 'ping') {
sendPing(ws, message.lastSyncId);
syncMutexService.doExclusively(async () => await sendPing(ws, message.lastSyncId));
}
else {
log.error('Unrecognized message: ');
@@ -41,7 +42,7 @@ function init(httpServer, sessionParser) {
});
}
async function sendMessage(client, message) {
function sendMessage(client, message) {
const jsonStr = JSON.stringify(message);
if (client.readyState === WebSocket.OPEN) {
@@ -49,7 +50,7 @@ async function sendMessage(client, message) {
}
}
async function sendMessageToAllClients(message) {
function sendMessageToAllClients(message) {
const jsonStr = JSON.stringify(message);
if (webSocketServer) {
@@ -84,19 +85,29 @@ async function sendPing(client, lastSentSyncId) {
const stats = require('./sync').stats;
await sendMessage(client, {
sendMessage(client, {
type: 'sync',
data: syncData,
outstandingSyncs: stats.outstandingPushes + stats.outstandingPulls
});
}
async function refreshTree() {
await sendMessageToAllClients({ type: 'refresh-tree' });
function refreshTree() {
sendMessageToAllClients({ type: 'refresh-tree' });
}
function syncPullInProgress() {
sendMessageToAllClients({ type: 'sync-pull-in-progress' });
}
function syncPullFinished() {
sendMessageToAllClients({ type: 'sync-pull-finished' });
}
module.exports = {
init,
sendMessageToAllClients,
refreshTree
refreshTree,
syncPullInProgress,
syncPullFinished
};

View File

@@ -1,4 +1,4 @@
<div class="note-detail-sidebar" style="width: <%= sidebarWidthPercent %>%; min-width: <%= sidebarMinWidth %>px">
<div class="note-detail-sidebar hide-toggle" style="width: <%= sidebarWidthPercent %>%; min-width: <%= sidebarMinWidth %>px">
<div style="text-align: center; margin-bottom: 10px;">
<button class="hide-sidebar-button" style="background: none; border: none;">hide sidebar <span class="jam jam-chevron-right"></span></button>
</div>