mirror of
https://github.com/zadam/trilium.git
synced 2025-10-30 09:56:36 +01:00
Compare commits
171 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fac9fef652 | ||
|
|
f0ab1fb5a1 | ||
|
|
df3fdb59c5 | ||
|
|
23e9bcfdc5 | ||
|
|
96c4934c00 | ||
|
|
31fb02f810 | ||
|
|
e206d9cc68 | ||
|
|
2d33f570f4 | ||
|
|
15f8173add | ||
|
|
7609bc78ec | ||
|
|
b4ac41eff8 | ||
|
|
de1c76ee3c | ||
|
|
ec4fac421b | ||
|
|
f587e0dfd9 | ||
|
|
b5214e6cea | ||
|
|
5fbaed61c1 | ||
|
|
8fcc76ad6d | ||
|
|
15a9ff4450 | ||
|
|
0a4f419e5e | ||
|
|
7fa531b3d6 | ||
|
|
3cfca27b54 | ||
|
|
103aa95ccf | ||
|
|
339a6d7817 | ||
|
|
93dd9274e7 | ||
|
|
ca35527aeb | ||
|
|
388dcadef3 | ||
|
|
8905148dbc | ||
|
|
daa36192cc | ||
|
|
ade77e5fb8 | ||
|
|
f250b72563 | ||
|
|
37cb5f5e9a | ||
|
|
82fcc97ed2 | ||
|
|
53e9c8cdac | ||
|
|
541d451168 | ||
|
|
7c64dc9440 | ||
|
|
27570a7756 | ||
|
|
d6931f7441 | ||
|
|
7d39d080f5 | ||
|
|
13ccd2ba67 | ||
|
|
81fd7397e4 | ||
|
|
6f75f944a3 | ||
|
|
308b0f7464 | ||
|
|
678e883044 | ||
|
|
819cf0907d | ||
|
|
942f17b2f4 | ||
|
|
2085dc5ed4 | ||
|
|
a1d1b4580a | ||
|
|
9e089cc7cd | ||
|
|
cd622cbdd7 | ||
|
|
4978a3ff1a | ||
|
|
fca0b82610 | ||
|
|
6cef1082b2 | ||
|
|
37eb16b2f3 | ||
|
|
c24c807921 | ||
|
|
5bc629d1c7 | ||
|
|
04379b4e1f | ||
|
|
c51e6107a1 | ||
|
|
bb7ad496bf | ||
|
|
c50d8e85dc | ||
|
|
1d8664927d | ||
|
|
dbb5d02ecf | ||
|
|
593a275795 | ||
|
|
6778e1e60e | ||
|
|
36308c307b | ||
|
|
078fc420b0 | ||
|
|
8ec814c29f | ||
|
|
e87e065100 | ||
|
|
8318ab7ac0 | ||
|
|
87b75a9a22 | ||
|
|
8df3b0a5bd | ||
|
|
6906c82408 | ||
|
|
45edef2d71 | ||
|
|
bf49648896 | ||
|
|
91d23c540a | ||
|
|
1cbf918024 | ||
|
|
26f3c1d453 | ||
|
|
c421ee79b0 | ||
|
|
77f8474d83 | ||
|
|
9a04a76672 | ||
|
|
963c18b8e4 | ||
|
|
bbbad67764 | ||
|
|
3491e71084 | ||
|
|
c85f70e197 | ||
|
|
3df712b64f | ||
|
|
160bd0a790 | ||
|
|
81e0c6dcc2 | ||
|
|
11bd48a1b5 | ||
|
|
61657087f5 | ||
|
|
87f436c6ea | ||
|
|
065e4f55c3 | ||
|
|
92adcf82e4 | ||
|
|
06e0f2418c | ||
|
|
35c4c61d15 | ||
|
|
a168edb168 | ||
|
|
d2975bbd21 | ||
|
|
358e8c548c | ||
|
|
f85ed672cc | ||
|
|
83f1a68bfd | ||
|
|
552e5d7d06 | ||
|
|
9c7f8cf5d8 | ||
|
|
f0f9274a3c | ||
|
|
65c725c21e | ||
|
|
dfa30358c5 | ||
|
|
2394fe6ed9 | ||
|
|
6cae68288d | ||
|
|
83afb89a16 | ||
|
|
93cc6b12ec | ||
|
|
dc35df9f63 | ||
|
|
1a4bc0b989 | ||
|
|
a37d75a08f | ||
|
|
8d510a3fdd | ||
|
|
7bcd1c3009 | ||
|
|
5dab189815 | ||
|
|
a9dc62505d | ||
|
|
4e4010e15e | ||
|
|
6bdaf050c5 | ||
|
|
05c8c6cfaa | ||
|
|
2441515666 | ||
|
|
c42bcd6c59 | ||
|
|
fc95bb8f18 | ||
|
|
cb88f316db | ||
|
|
e19ddc10d3 | ||
|
|
d3e86acfaa | ||
|
|
536643ed3b | ||
|
|
9771b441ad | ||
|
|
c295fdb142 | ||
|
|
ee7aa3d3da | ||
|
|
e437a9d70f | ||
|
|
a8655fcd27 | ||
|
|
aefc9f1593 | ||
|
|
73671671d7 | ||
|
|
f53a93e828 | ||
|
|
e156c6292b | ||
|
|
e365521d5e | ||
|
|
f354821f25 | ||
|
|
82e278a2a2 | ||
|
|
b4d4606c73 | ||
|
|
b14b7b6ad1 | ||
|
|
32aa7bb540 | ||
|
|
300f4ad357 | ||
|
|
cf6b5c3b6e | ||
|
|
f1c9dda366 | ||
|
|
7f01032b6d | ||
|
|
f08afd4723 | ||
|
|
afe2a03aef | ||
|
|
272bb136d8 | ||
|
|
27d0388d79 | ||
|
|
025032de42 | ||
|
|
15a3b42124 | ||
|
|
4266156cee | ||
|
|
45a66ab694 | ||
|
|
49f4ce7149 | ||
|
|
220df662ad | ||
|
|
6e535bac05 | ||
|
|
3b1dcc7199 | ||
|
|
ae75ac424f | ||
|
|
5c46fe792d | ||
|
|
a33b0f1e1c | ||
|
|
bd28ed07d7 | ||
|
|
12185fbd32 | ||
|
|
24911da8db | ||
|
|
da84d16421 | ||
|
|
53666cbfe8 | ||
|
|
8897d98bd9 | ||
|
|
93e485cea9 | ||
|
|
22363f5b74 | ||
|
|
2a2c82cd29 | ||
|
|
5ebe717da8 | ||
|
|
2f2d8327e4 | ||
|
|
a894c19c2b | ||
|
|
0469962c5e |
@@ -2,7 +2,7 @@ image:
|
|||||||
file: .gitpod.dockerfile
|
file: .gitpod.dockerfile
|
||||||
|
|
||||||
tasks:
|
tasks:
|
||||||
- before: nvm install 16.14.2 && nvm use 16.14.2
|
- before: nvm install 16.15.0 && nvm use 16.15.0
|
||||||
init: npm install
|
init: npm install
|
||||||
command: npm run start-server
|
command: npm run start-server
|
||||||
|
|
||||||
|
|||||||
5
.idea/misc.xml
generated
5
.idea/misc.xml
generated
@@ -6,9 +6,4 @@
|
|||||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_11" default="true" project-jdk-name="11" project-jdk-type="JavaSDK">
|
<component name="ProjectRootManager" version="2" languageLevel="JDK_11" default="true" project-jdk-name="11" project-jdk-type="JavaSDK">
|
||||||
<output url="file://$PROJECT_DIR$/out" />
|
<output url="file://$PROJECT_DIR$/out" />
|
||||||
</component>
|
</component>
|
||||||
<component name="SwUserDefinedSpecifications">
|
|
||||||
<option name="specTypeByUrl">
|
|
||||||
<map />
|
|
||||||
</option>
|
|
||||||
</component>
|
|
||||||
</project>
|
</project>
|
||||||
11
Dockerfile
11
Dockerfile
@@ -1,5 +1,5 @@
|
|||||||
# !!! Don't try to build this Dockerfile directly, run it through bin/build-docker.sh script !!!
|
# !!! Don't try to build this Dockerfile directly, run it through bin/build-docker.sh script !!!
|
||||||
FROM node:16.14.2-alpine
|
FROM node:16.15.0-alpine
|
||||||
|
|
||||||
# Create app directory
|
# Create app directory
|
||||||
WORKDIR /usr/src/app
|
WORKDIR /usr/src/app
|
||||||
@@ -21,10 +21,15 @@ RUN set -x \
|
|||||||
&& npm install --production \
|
&& npm install --production \
|
||||||
&& apk del .build-dependencies
|
&& apk del .build-dependencies
|
||||||
|
|
||||||
|
# Some setup tools need to be kept
|
||||||
|
RUN apk add --no-cache su-exec shadow
|
||||||
|
|
||||||
# Bundle app source
|
# Bundle app source
|
||||||
COPY . .
|
COPY . .
|
||||||
|
|
||||||
USER node
|
# Add application user and setup proper volume permissions
|
||||||
|
RUN adduser -s /bin/false node; exit 0
|
||||||
|
|
||||||
|
# Start the application
|
||||||
EXPOSE 8080
|
EXPOSE 8080
|
||||||
CMD [ "node", "./src/www" ]
|
CMD [ "./start-docker.sh" ]
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
PKG_DIR=dist/trilium-linux-x64-server
|
PKG_DIR=dist/trilium-linux-x64-server
|
||||||
NODE_VERSION=16.14.2
|
NODE_VERSION=16.15.0
|
||||||
|
|
||||||
if [ "$1" != "DONTCOPY" ]
|
if [ "$1" != "DONTCOPY" ]
|
||||||
then
|
then
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ if [[ $# -eq 0 ]] ; then
|
|||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
n exec 16.14.2 npm run webpack
|
n exec 16.15.0 npm run webpack
|
||||||
|
|
||||||
DIR=$1
|
DIR=$1
|
||||||
|
|
||||||
@@ -30,7 +30,7 @@ cp -r electron.js $DIR/
|
|||||||
cp webpack-* $DIR/
|
cp webpack-* $DIR/
|
||||||
|
|
||||||
# run in subshell (so we return to original dir)
|
# run in subshell (so we return to original dir)
|
||||||
(cd $DIR && n exec 16.14.2 npm install --only=prod)
|
(cd $DIR && n exec 16.15.0 npm install --only=prod)
|
||||||
|
|
||||||
# cleanup of useless files in dependencies
|
# cleanup of useless files in dependencies
|
||||||
rm -r $DIR/node_modules/image-q/demo
|
rm -r $DIR/node_modules/image-q/demo
|
||||||
|
|||||||
@@ -0,0 +1,2 @@
|
|||||||
|
-- removing potential remnants of recent notes in entity changes, see https://github.com/zadam/trilium/issues/2842
|
||||||
|
DELETE FROM entity_changes WHERE entityName = 'recent_notes';
|
||||||
@@ -131,6 +131,78 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<h4 class="name" id="__private"><span class="type-signature"></span>__private<span class="type-signature"> :Object</span></h4>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<div class="description">
|
||||||
|
This object contains "at your risk" and "no BC guarantees" objects for advanced use cases.
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<h5>Type:</h5>
|
||||||
|
<ul>
|
||||||
|
<li>
|
||||||
|
|
||||||
|
<span class="param-type">Object</span>
|
||||||
|
|
||||||
|
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<dl class="details">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<dt class="tag-source">Source:</dt>
|
||||||
|
<dd class="tag-source"><ul class="dummy"><li>
|
||||||
|
<a href="services_backend_script_api.js.html">services/backend_script_api.js</a>, <a href="services_backend_script_api.js.html#line435">line 435</a>
|
||||||
|
</li></ul></dd>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
</dl>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<h4 class="name" id="axios"><span class="type-signature"></span>axios<span class="type-signature"></span></h4>
|
<h4 class="name" id="axios"><span class="type-signature"></span>axios<span class="type-signature"></span></h4>
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -93,7 +93,7 @@
|
|||||||
|
|
||||||
<dt class="tag-source">Source:</dt>
|
<dt class="tag-source">Source:</dt>
|
||||||
<dd class="tag-source"><ul class="dummy"><li>
|
<dd class="tag-source"><ul class="dummy"><li>
|
||||||
<a href="becca_entities_note.js.html">becca/entities/note.js</a>, <a href="becca_entities_note.js.html#line21">line 21</a>
|
<a href="becca_entities_note.js.html">becca/entities/note.js</a>, <a href="becca_entities_note.js.html#line23">line 23</a>
|
||||||
</li></ul></dd>
|
</li></ul></dd>
|
||||||
|
|
||||||
|
|
||||||
@@ -204,7 +204,7 @@
|
|||||||
|
|
||||||
<dt class="tag-source">Source:</dt>
|
<dt class="tag-source">Source:</dt>
|
||||||
<dd class="tag-source"><ul class="dummy"><li>
|
<dd class="tag-source"><ul class="dummy"><li>
|
||||||
<a href="becca_entities_note.js.html">becca/entities/note.js</a>, <a href="becca_entities_note.js.html#line92">line 92</a>
|
<a href="becca_entities_note.js.html">becca/entities/note.js</a>, <a href="becca_entities_note.js.html#line94">line 94</a>
|
||||||
</li></ul></dd>
|
</li></ul></dd>
|
||||||
|
|
||||||
|
|
||||||
@@ -279,7 +279,7 @@
|
|||||||
|
|
||||||
<dt class="tag-source">Source:</dt>
|
<dt class="tag-source">Source:</dt>
|
||||||
<dd class="tag-source"><ul class="dummy"><li>
|
<dd class="tag-source"><ul class="dummy"><li>
|
||||||
<a href="becca_entities_note.js.html">becca/entities/note.js</a>, <a href="becca_entities_note.js.html#line118">line 118</a>
|
<a href="becca_entities_note.js.html">becca/entities/note.js</a>, <a href="becca_entities_note.js.html#line120">line 120</a>
|
||||||
</li></ul></dd>
|
</li></ul></dd>
|
||||||
|
|
||||||
|
|
||||||
@@ -347,7 +347,7 @@
|
|||||||
|
|
||||||
<dt class="tag-source">Source:</dt>
|
<dt class="tag-source">Source:</dt>
|
||||||
<dd class="tag-source"><ul class="dummy"><li>
|
<dd class="tag-source"><ul class="dummy"><li>
|
||||||
<a href="becca_entities_note.js.html">becca/entities/note.js</a>, <a href="becca_entities_note.js.html#line65">line 65</a>
|
<a href="becca_entities_note.js.html">becca/entities/note.js</a>, <a href="becca_entities_note.js.html#line67">line 67</a>
|
||||||
</li></ul></dd>
|
</li></ul></dd>
|
||||||
|
|
||||||
|
|
||||||
@@ -415,7 +415,7 @@
|
|||||||
|
|
||||||
<dt class="tag-source">Source:</dt>
|
<dt class="tag-source">Source:</dt>
|
||||||
<dd class="tag-source"><ul class="dummy"><li>
|
<dd class="tag-source"><ul class="dummy"><li>
|
||||||
<a href="becca_entities_note.js.html">becca/entities/note.js</a>, <a href="becca_entities_note.js.html#line67">line 67</a>
|
<a href="becca_entities_note.js.html">becca/entities/note.js</a>, <a href="becca_entities_note.js.html#line69">line 69</a>
|
||||||
</li></ul></dd>
|
</li></ul></dd>
|
||||||
|
|
||||||
|
|
||||||
@@ -486,7 +486,7 @@
|
|||||||
|
|
||||||
<dt class="tag-source">Source:</dt>
|
<dt class="tag-source">Source:</dt>
|
||||||
<dd class="tag-source"><ul class="dummy"><li>
|
<dd class="tag-source"><ul class="dummy"><li>
|
||||||
<a href="becca_entities_note.js.html">becca/entities/note.js</a>, <a href="becca_entities_note.js.html#line81">line 81</a>
|
<a href="becca_entities_note.js.html">becca/entities/note.js</a>, <a href="becca_entities_note.js.html#line83">line 83</a>
|
||||||
</li></ul></dd>
|
</li></ul></dd>
|
||||||
|
|
||||||
|
|
||||||
@@ -554,7 +554,7 @@
|
|||||||
|
|
||||||
<dt class="tag-source">Source:</dt>
|
<dt class="tag-source">Source:</dt>
|
||||||
<dd class="tag-source"><ul class="dummy"><li>
|
<dd class="tag-source"><ul class="dummy"><li>
|
||||||
<a href="becca_entities_note.js.html">becca/entities/note.js</a>, <a href="becca_entities_note.js.html#line76">line 76</a>
|
<a href="becca_entities_note.js.html">becca/entities/note.js</a>, <a href="becca_entities_note.js.html#line78">line 78</a>
|
||||||
</li></ul></dd>
|
</li></ul></dd>
|
||||||
|
|
||||||
|
|
||||||
@@ -622,7 +622,7 @@
|
|||||||
|
|
||||||
<dt class="tag-source">Source:</dt>
|
<dt class="tag-source">Source:</dt>
|
||||||
<dd class="tag-source"><ul class="dummy"><li>
|
<dd class="tag-source"><ul class="dummy"><li>
|
||||||
<a href="becca_entities_note.js.html">becca/entities/note.js</a>, <a href="becca_entities_note.js.html#line59">line 59</a>
|
<a href="becca_entities_note.js.html">becca/entities/note.js</a>, <a href="becca_entities_note.js.html#line61">line 61</a>
|
||||||
</li></ul></dd>
|
</li></ul></dd>
|
||||||
|
|
||||||
|
|
||||||
@@ -690,7 +690,7 @@
|
|||||||
|
|
||||||
<dt class="tag-source">Source:</dt>
|
<dt class="tag-source">Source:</dt>
|
||||||
<dd class="tag-source"><ul class="dummy"><li>
|
<dd class="tag-source"><ul class="dummy"><li>
|
||||||
<a href="becca_entities_note.js.html">becca/entities/note.js</a>, <a href="becca_entities_note.js.html#line63">line 63</a>
|
<a href="becca_entities_note.js.html">becca/entities/note.js</a>, <a href="becca_entities_note.js.html#line65">line 65</a>
|
||||||
</li></ul></dd>
|
</li></ul></dd>
|
||||||
|
|
||||||
|
|
||||||
@@ -758,7 +758,7 @@
|
|||||||
|
|
||||||
<dt class="tag-source">Source:</dt>
|
<dt class="tag-source">Source:</dt>
|
||||||
<dd class="tag-source"><ul class="dummy"><li>
|
<dd class="tag-source"><ul class="dummy"><li>
|
||||||
<a href="becca_entities_note.js.html">becca/entities/note.js</a>, <a href="becca_entities_note.js.html#line55">line 55</a>
|
<a href="becca_entities_note.js.html">becca/entities/note.js</a>, <a href="becca_entities_note.js.html#line57">line 57</a>
|
||||||
</li></ul></dd>
|
</li></ul></dd>
|
||||||
|
|
||||||
|
|
||||||
@@ -833,7 +833,7 @@
|
|||||||
|
|
||||||
<dt class="tag-source">Source:</dt>
|
<dt class="tag-source">Source:</dt>
|
||||||
<dd class="tag-source"><ul class="dummy"><li>
|
<dd class="tag-source"><ul class="dummy"><li>
|
||||||
<a href="becca_entities_note.js.html">becca/entities/note.js</a>, <a href="becca_entities_note.js.html#line123">line 123</a>
|
<a href="becca_entities_note.js.html">becca/entities/note.js</a>, <a href="becca_entities_note.js.html#line125">line 125</a>
|
||||||
</li></ul></dd>
|
</li></ul></dd>
|
||||||
|
|
||||||
|
|
||||||
@@ -901,7 +901,7 @@
|
|||||||
|
|
||||||
<dt class="tag-source">Source:</dt>
|
<dt class="tag-source">Source:</dt>
|
||||||
<dd class="tag-source"><ul class="dummy"><li>
|
<dd class="tag-source"><ul class="dummy"><li>
|
||||||
<a href="becca_entities_note.js.html">becca/entities/note.js</a>, <a href="becca_entities_note.js.html#line94">line 94</a>
|
<a href="becca_entities_note.js.html">becca/entities/note.js</a>, <a href="becca_entities_note.js.html#line96">line 96</a>
|
||||||
</li></ul></dd>
|
</li></ul></dd>
|
||||||
|
|
||||||
|
|
||||||
@@ -969,7 +969,7 @@
|
|||||||
|
|
||||||
<dt class="tag-source">Source:</dt>
|
<dt class="tag-source">Source:</dt>
|
||||||
<dd class="tag-source"><ul class="dummy"><li>
|
<dd class="tag-source"><ul class="dummy"><li>
|
||||||
<a href="becca_entities_note.js.html">becca/entities/note.js</a>, <a href="becca_entities_note.js.html#line88">line 88</a>
|
<a href="becca_entities_note.js.html">becca/entities/note.js</a>, <a href="becca_entities_note.js.html#line90">line 90</a>
|
||||||
</li></ul></dd>
|
</li></ul></dd>
|
||||||
|
|
||||||
|
|
||||||
@@ -1037,7 +1037,7 @@
|
|||||||
|
|
||||||
<dt class="tag-source">Source:</dt>
|
<dt class="tag-source">Source:</dt>
|
||||||
<dd class="tag-source"><ul class="dummy"><li>
|
<dd class="tag-source"><ul class="dummy"><li>
|
||||||
<a href="becca_entities_note.js.html">becca/entities/note.js</a>, <a href="becca_entities_note.js.html#line90">line 90</a>
|
<a href="becca_entities_note.js.html">becca/entities/note.js</a>, <a href="becca_entities_note.js.html#line92">line 92</a>
|
||||||
</li></ul></dd>
|
</li></ul></dd>
|
||||||
|
|
||||||
|
|
||||||
@@ -1112,7 +1112,7 @@
|
|||||||
|
|
||||||
<dt class="tag-source">Source:</dt>
|
<dt class="tag-source">Source:</dt>
|
||||||
<dd class="tag-source"><ul class="dummy"><li>
|
<dd class="tag-source"><ul class="dummy"><li>
|
||||||
<a href="becca_entities_note.js.html">becca/entities/note.js</a>, <a href="becca_entities_note.js.html#line128">line 128</a>
|
<a href="becca_entities_note.js.html">becca/entities/note.js</a>, <a href="becca_entities_note.js.html#line130">line 130</a>
|
||||||
</li></ul></dd>
|
</li></ul></dd>
|
||||||
|
|
||||||
|
|
||||||
@@ -1180,7 +1180,7 @@
|
|||||||
|
|
||||||
<dt class="tag-source">Source:</dt>
|
<dt class="tag-source">Source:</dt>
|
||||||
<dd class="tag-source"><ul class="dummy"><li>
|
<dd class="tag-source"><ul class="dummy"><li>
|
||||||
<a href="becca_entities_note.js.html">becca/entities/note.js</a>, <a href="becca_entities_note.js.html#line104">line 104</a>
|
<a href="becca_entities_note.js.html">becca/entities/note.js</a>, <a href="becca_entities_note.js.html#line106">line 106</a>
|
||||||
</li></ul></dd>
|
</li></ul></dd>
|
||||||
|
|
||||||
|
|
||||||
@@ -1248,7 +1248,7 @@
|
|||||||
|
|
||||||
<dt class="tag-source">Source:</dt>
|
<dt class="tag-source">Source:</dt>
|
||||||
<dd class="tag-source"><ul class="dummy"><li>
|
<dd class="tag-source"><ul class="dummy"><li>
|
||||||
<a href="becca_entities_note.js.html">becca/entities/note.js</a>, <a href="becca_entities_note.js.html#line57">line 57</a>
|
<a href="becca_entities_note.js.html">becca/entities/note.js</a>, <a href="becca_entities_note.js.html#line59">line 59</a>
|
||||||
</li></ul></dd>
|
</li></ul></dd>
|
||||||
|
|
||||||
|
|
||||||
@@ -1316,7 +1316,7 @@
|
|||||||
|
|
||||||
<dt class="tag-source">Source:</dt>
|
<dt class="tag-source">Source:</dt>
|
||||||
<dd class="tag-source"><ul class="dummy"><li>
|
<dd class="tag-source"><ul class="dummy"><li>
|
||||||
<a href="becca_entities_note.js.html">becca/entities/note.js</a>, <a href="becca_entities_note.js.html#line61">line 61</a>
|
<a href="becca_entities_note.js.html">becca/entities/note.js</a>, <a href="becca_entities_note.js.html#line63">line 63</a>
|
||||||
</li></ul></dd>
|
</li></ul></dd>
|
||||||
|
|
||||||
|
|
||||||
@@ -1384,7 +1384,7 @@
|
|||||||
|
|
||||||
<dt class="tag-source">Source:</dt>
|
<dt class="tag-source">Source:</dt>
|
||||||
<dd class="tag-source"><ul class="dummy"><li>
|
<dd class="tag-source"><ul class="dummy"><li>
|
||||||
<a href="becca_entities_note.js.html">becca/entities/note.js</a>, <a href="becca_entities_note.js.html#line69">line 69</a>
|
<a href="becca_entities_note.js.html">becca/entities/note.js</a>, <a href="becca_entities_note.js.html#line71">line 71</a>
|
||||||
</li></ul></dd>
|
</li></ul></dd>
|
||||||
|
|
||||||
|
|
||||||
@@ -1452,7 +1452,7 @@
|
|||||||
|
|
||||||
<dt class="tag-source">Source:</dt>
|
<dt class="tag-source">Source:</dt>
|
||||||
<dd class="tag-source"><ul class="dummy"><li>
|
<dd class="tag-source"><ul class="dummy"><li>
|
||||||
<a href="becca_entities_note.js.html">becca/entities/note.js</a>, <a href="becca_entities_note.js.html#line71">line 71</a>
|
<a href="becca_entities_note.js.html">becca/entities/note.js</a>, <a href="becca_entities_note.js.html#line73">line 73</a>
|
||||||
</li></ul></dd>
|
</li></ul></dd>
|
||||||
|
|
||||||
|
|
||||||
@@ -1528,7 +1528,7 @@
|
|||||||
|
|
||||||
<dt class="tag-source">Source:</dt>
|
<dt class="tag-source">Source:</dt>
|
||||||
<dd class="tag-source"><ul class="dummy"><li>
|
<dd class="tag-source"><ul class="dummy"><li>
|
||||||
<a href="becca_entities_note.js.html">becca/entities/note.js</a>, <a href="becca_entities_note.js.html#line405">line 405</a>
|
<a href="becca_entities_note.js.html">becca/entities/note.js</a>, <a href="becca_entities_note.js.html#line407">line 407</a>
|
||||||
</li></ul></dd>
|
</li></ul></dd>
|
||||||
|
|
||||||
|
|
||||||
@@ -1630,7 +1630,7 @@
|
|||||||
|
|
||||||
<dt class="tag-source">Source:</dt>
|
<dt class="tag-source">Source:</dt>
|
||||||
<dd class="tag-source"><ul class="dummy"><li>
|
<dd class="tag-source"><ul class="dummy"><li>
|
||||||
<a href="becca_entities_note.js.html">becca/entities/note.js</a>, <a href="becca_entities_note.js.html#line1023">line 1023</a>
|
<a href="becca_entities_note.js.html">becca/entities/note.js</a>, <a href="becca_entities_note.js.html#line1025">line 1025</a>
|
||||||
</li></ul></dd>
|
</li></ul></dd>
|
||||||
|
|
||||||
|
|
||||||
@@ -1830,7 +1830,7 @@
|
|||||||
|
|
||||||
<dt class="tag-source">Source:</dt>
|
<dt class="tag-source">Source:</dt>
|
||||||
<dd class="tag-source"><ul class="dummy"><li>
|
<dd class="tag-source"><ul class="dummy"><li>
|
||||||
<a href="becca_entities_note.js.html">becca/entities/note.js</a>, <a href="becca_entities_note.js.html#line1135">line 1135</a>
|
<a href="becca_entities_note.js.html">becca/entities/note.js</a>, <a href="becca_entities_note.js.html#line1137">line 1137</a>
|
||||||
</li></ul></dd>
|
</li></ul></dd>
|
||||||
|
|
||||||
|
|
||||||
@@ -1914,7 +1914,7 @@
|
|||||||
|
|
||||||
<dt class="tag-source">Source:</dt>
|
<dt class="tag-source">Source:</dt>
|
||||||
<dd class="tag-source"><ul class="dummy"><li>
|
<dd class="tag-source"><ul class="dummy"><li>
|
||||||
<a href="becca_entities_note.js.html">becca/entities/note.js</a>, <a href="becca_entities_note.js.html#line945">line 945</a>
|
<a href="becca_entities_note.js.html">becca/entities/note.js</a>, <a href="becca_entities_note.js.html#line947">line 947</a>
|
||||||
</li></ul></dd>
|
</li></ul></dd>
|
||||||
|
|
||||||
|
|
||||||
@@ -2020,7 +2020,7 @@
|
|||||||
|
|
||||||
<dt class="tag-source">Source:</dt>
|
<dt class="tag-source">Source:</dt>
|
||||||
<dd class="tag-source"><ul class="dummy"><li>
|
<dd class="tag-source"><ul class="dummy"><li>
|
||||||
<a href="becca_entities_note.js.html">becca/entities/note.js</a>, <a href="becca_entities_note.js.html#line865">line 865</a>
|
<a href="becca_entities_note.js.html">becca/entities/note.js</a>, <a href="becca_entities_note.js.html#line867">line 867</a>
|
||||||
</li></ul></dd>
|
</li></ul></dd>
|
||||||
|
|
||||||
|
|
||||||
@@ -2194,7 +2194,7 @@
|
|||||||
|
|
||||||
<dt class="tag-source">Source:</dt>
|
<dt class="tag-source">Source:</dt>
|
||||||
<dd class="tag-source"><ul class="dummy"><li>
|
<dd class="tag-source"><ul class="dummy"><li>
|
||||||
<a href="becca_entities_note.js.html">becca/entities/note.js</a>, <a href="becca_entities_note.js.html#line523">line 523</a>
|
<a href="becca_entities_note.js.html">becca/entities/note.js</a>, <a href="becca_entities_note.js.html#line525">line 525</a>
|
||||||
</li></ul></dd>
|
</li></ul></dd>
|
||||||
|
|
||||||
|
|
||||||
@@ -2394,7 +2394,7 @@
|
|||||||
|
|
||||||
<dt class="tag-source">Source:</dt>
|
<dt class="tag-source">Source:</dt>
|
||||||
<dd class="tag-source"><ul class="dummy"><li>
|
<dd class="tag-source"><ul class="dummy"><li>
|
||||||
<a href="becca_entities_note.js.html">becca/entities/note.js</a>, <a href="becca_entities_note.js.html#line336">line 336</a>
|
<a href="becca_entities_note.js.html">becca/entities/note.js</a>, <a href="becca_entities_note.js.html#line338">line 338</a>
|
||||||
</li></ul></dd>
|
</li></ul></dd>
|
||||||
|
|
||||||
|
|
||||||
@@ -2572,7 +2572,7 @@
|
|||||||
|
|
||||||
<dt class="tag-source">Source:</dt>
|
<dt class="tag-source">Source:</dt>
|
||||||
<dd class="tag-source"><ul class="dummy"><li>
|
<dd class="tag-source"><ul class="dummy"><li>
|
||||||
<a href="becca_entities_note.js.html">becca/entities/note.js</a>, <a href="becca_entities_note.js.html#line534">line 534</a>
|
<a href="becca_entities_note.js.html">becca/entities/note.js</a>, <a href="becca_entities_note.js.html#line536">line 536</a>
|
||||||
</li></ul></dd>
|
</li></ul></dd>
|
||||||
|
|
||||||
|
|
||||||
@@ -2683,7 +2683,7 @@
|
|||||||
|
|
||||||
<dt class="tag-source">Source:</dt>
|
<dt class="tag-source">Source:</dt>
|
||||||
<dd class="tag-source"><ul class="dummy"><li>
|
<dd class="tag-source"><ul class="dummy"><li>
|
||||||
<a href="becca_entities_note.js.html">becca/entities/note.js</a>, <a href="becca_entities_note.js.html#line150">line 150</a>
|
<a href="becca_entities_note.js.html">becca/entities/note.js</a>, <a href="becca_entities_note.js.html#line152">line 152</a>
|
||||||
</li></ul></dd>
|
</li></ul></dd>
|
||||||
|
|
||||||
|
|
||||||
@@ -2785,7 +2785,7 @@
|
|||||||
|
|
||||||
<dt class="tag-source">Source:</dt>
|
<dt class="tag-source">Source:</dt>
|
||||||
<dd class="tag-source"><ul class="dummy"><li>
|
<dd class="tag-source"><ul class="dummy"><li>
|
||||||
<a href="becca_entities_note.js.html">becca/entities/note.js</a>, <a href="becca_entities_note.js.html#line170">line 170</a>
|
<a href="becca_entities_note.js.html">becca/entities/note.js</a>, <a href="becca_entities_note.js.html#line172">line 172</a>
|
||||||
</li></ul></dd>
|
</li></ul></dd>
|
||||||
|
|
||||||
|
|
||||||
@@ -2887,7 +2887,7 @@
|
|||||||
|
|
||||||
<dt class="tag-source">Source:</dt>
|
<dt class="tag-source">Source:</dt>
|
||||||
<dd class="tag-source"><ul class="dummy"><li>
|
<dd class="tag-source"><ul class="dummy"><li>
|
||||||
<a href="becca_entities_note.js.html">becca/entities/note.js</a>, <a href="becca_entities_note.js.html#line160">line 160</a>
|
<a href="becca_entities_note.js.html">becca/entities/note.js</a>, <a href="becca_entities_note.js.html#line162">line 162</a>
|
||||||
</li></ul></dd>
|
</li></ul></dd>
|
||||||
|
|
||||||
|
|
||||||
@@ -2989,7 +2989,7 @@
|
|||||||
|
|
||||||
<dt class="tag-source">Source:</dt>
|
<dt class="tag-source">Source:</dt>
|
||||||
<dd class="tag-source"><ul class="dummy"><li>
|
<dd class="tag-source"><ul class="dummy"><li>
|
||||||
<a href="becca_entities_note.js.html">becca/entities/note.js</a>, <a href="becca_entities_note.js.html#line184">line 184</a>
|
<a href="becca_entities_note.js.html">becca/entities/note.js</a>, <a href="becca_entities_note.js.html#line186">line 186</a>
|
||||||
</li></ul></dd>
|
</li></ul></dd>
|
||||||
|
|
||||||
|
|
||||||
@@ -3091,7 +3091,7 @@
|
|||||||
|
|
||||||
<dt class="tag-source">Source:</dt>
|
<dt class="tag-source">Source:</dt>
|
||||||
<dd class="tag-source"><ul class="dummy"><li>
|
<dd class="tag-source"><ul class="dummy"><li>
|
||||||
<a href="becca_entities_note.js.html">becca/entities/note.js</a>, <a href="becca_entities_note.js.html#line218">line 218</a>
|
<a href="becca_entities_note.js.html">becca/entities/note.js</a>, <a href="becca_entities_note.js.html#line220">line 220</a>
|
||||||
</li></ul></dd>
|
</li></ul></dd>
|
||||||
|
|
||||||
|
|
||||||
@@ -3199,7 +3199,7 @@
|
|||||||
|
|
||||||
<dt class="tag-source">Source:</dt>
|
<dt class="tag-source">Source:</dt>
|
||||||
<dd class="tag-source"><ul class="dummy"><li>
|
<dd class="tag-source"><ul class="dummy"><li>
|
||||||
<a href="becca_entities_note.js.html">becca/entities/note.js</a>, <a href="becca_entities_note.js.html#line660">line 660</a>
|
<a href="becca_entities_note.js.html">becca/entities/note.js</a>, <a href="becca_entities_note.js.html#line662">line 662</a>
|
||||||
</li></ul></dd>
|
</li></ul></dd>
|
||||||
|
|
||||||
|
|
||||||
@@ -3305,7 +3305,7 @@
|
|||||||
|
|
||||||
<dt class="tag-source">Source:</dt>
|
<dt class="tag-source">Source:</dt>
|
||||||
<dd class="tag-source"><ul class="dummy"><li>
|
<dd class="tag-source"><ul class="dummy"><li>
|
||||||
<a href="becca_entities_note.js.html">becca/entities/note.js</a>, <a href="becca_entities_note.js.html#line229">line 229</a>
|
<a href="becca_entities_note.js.html">becca/entities/note.js</a>, <a href="becca_entities_note.js.html#line231">line 231</a>
|
||||||
</li></ul></dd>
|
</li></ul></dd>
|
||||||
|
|
||||||
|
|
||||||
@@ -3456,7 +3456,7 @@
|
|||||||
|
|
||||||
<dt class="tag-source">Source:</dt>
|
<dt class="tag-source">Source:</dt>
|
||||||
<dd class="tag-source"><ul class="dummy"><li>
|
<dd class="tag-source"><ul class="dummy"><li>
|
||||||
<a href="becca_entities_note.js.html">becca/entities/note.js</a>, <a href="becca_entities_note.js.html#line465">line 465</a>
|
<a href="becca_entities_note.js.html">becca/entities/note.js</a>, <a href="becca_entities_note.js.html#line467">line 467</a>
|
||||||
</li></ul></dd>
|
</li></ul></dd>
|
||||||
|
|
||||||
|
|
||||||
@@ -3626,7 +3626,7 @@
|
|||||||
|
|
||||||
<dt class="tag-source">Source:</dt>
|
<dt class="tag-source">Source:</dt>
|
||||||
<dd class="tag-source"><ul class="dummy"><li>
|
<dd class="tag-source"><ul class="dummy"><li>
|
||||||
<a href="becca_entities_note.js.html">becca/entities/note.js</a>, <a href="becca_entities_note.js.html#line555">line 555</a>
|
<a href="becca_entities_note.js.html">becca/entities/note.js</a>, <a href="becca_entities_note.js.html#line557">line 557</a>
|
||||||
</li></ul></dd>
|
</li></ul></dd>
|
||||||
|
|
||||||
|
|
||||||
@@ -3781,7 +3781,7 @@
|
|||||||
|
|
||||||
<dt class="tag-source">Source:</dt>
|
<dt class="tag-source">Source:</dt>
|
||||||
<dd class="tag-source"><ul class="dummy"><li>
|
<dd class="tag-source"><ul class="dummy"><li>
|
||||||
<a href="becca_entities_note.js.html">becca/entities/note.js</a>, <a href="becca_entities_note.js.html#line489">line 489</a>
|
<a href="becca_entities_note.js.html">becca/entities/note.js</a>, <a href="becca_entities_note.js.html#line491">line 491</a>
|
||||||
</li></ul></dd>
|
</li></ul></dd>
|
||||||
|
|
||||||
|
|
||||||
@@ -3951,7 +3951,7 @@
|
|||||||
|
|
||||||
<dt class="tag-source">Source:</dt>
|
<dt class="tag-source">Source:</dt>
|
||||||
<dd class="tag-source"><ul class="dummy"><li>
|
<dd class="tag-source"><ul class="dummy"><li>
|
||||||
<a href="becca_entities_note.js.html">becca/entities/note.js</a>, <a href="becca_entities_note.js.html#line563">line 563</a>
|
<a href="becca_entities_note.js.html">becca/entities/note.js</a>, <a href="becca_entities_note.js.html#line565">line 565</a>
|
||||||
</li></ul></dd>
|
</li></ul></dd>
|
||||||
|
|
||||||
|
|
||||||
@@ -4057,7 +4057,7 @@
|
|||||||
|
|
||||||
<dt class="tag-source">Source:</dt>
|
<dt class="tag-source">Source:</dt>
|
||||||
<dd class="tag-source"><ul class="dummy"><li>
|
<dd class="tag-source"><ul class="dummy"><li>
|
||||||
<a href="becca_entities_note.js.html">becca/entities/note.js</a>, <a href="becca_entities_note.js.html#line629">line 629</a>
|
<a href="becca_entities_note.js.html">becca/entities/note.js</a>, <a href="becca_entities_note.js.html#line631">line 631</a>
|
||||||
</li></ul></dd>
|
</li></ul></dd>
|
||||||
|
|
||||||
|
|
||||||
@@ -4259,7 +4259,7 @@ This method can be significantly faster than the getAttribute()
|
|||||||
|
|
||||||
<dt class="tag-source">Source:</dt>
|
<dt class="tag-source">Source:</dt>
|
||||||
<dd class="tag-source"><ul class="dummy"><li>
|
<dd class="tag-source"><ul class="dummy"><li>
|
||||||
<a href="becca_entities_note.js.html">becca/entities/note.js</a>, <a href="becca_entities_note.js.html#line604">line 604</a>
|
<a href="becca_entities_note.js.html">becca/entities/note.js</a>, <a href="becca_entities_note.js.html#line606">line 606</a>
|
||||||
</li></ul></dd>
|
</li></ul></dd>
|
||||||
|
|
||||||
|
|
||||||
@@ -4437,7 +4437,7 @@ This method can be significantly faster than the getAttribute()
|
|||||||
|
|
||||||
<dt class="tag-source">Source:</dt>
|
<dt class="tag-source">Source:</dt>
|
||||||
<dd class="tag-source"><ul class="dummy"><li>
|
<dd class="tag-source"><ul class="dummy"><li>
|
||||||
<a href="becca_entities_note.js.html">becca/entities/note.js</a>, <a href="becca_entities_note.js.html#line545">line 545</a>
|
<a href="becca_entities_note.js.html">becca/entities/note.js</a>, <a href="becca_entities_note.js.html#line547">line 547</a>
|
||||||
</li></ul></dd>
|
</li></ul></dd>
|
||||||
|
|
||||||
|
|
||||||
@@ -4595,7 +4595,7 @@ This method can be significantly faster than the getAttribute()
|
|||||||
|
|
||||||
<dt class="tag-source">Source:</dt>
|
<dt class="tag-source">Source:</dt>
|
||||||
<dd class="tag-source"><ul class="dummy"><li>
|
<dd class="tag-source"><ul class="dummy"><li>
|
||||||
<a href="becca_entities_note.js.html">becca/entities/note.js</a>, <a href="becca_entities_note.js.html#line471">line 471</a>
|
<a href="becca_entities_note.js.html">becca/entities/note.js</a>, <a href="becca_entities_note.js.html#line473">line 473</a>
|
||||||
</li></ul></dd>
|
</li></ul></dd>
|
||||||
|
|
||||||
|
|
||||||
@@ -4765,7 +4765,7 @@ This method can be significantly faster than the getAttribute()
|
|||||||
|
|
||||||
<dt class="tag-source">Source:</dt>
|
<dt class="tag-source">Source:</dt>
|
||||||
<dd class="tag-source"><ul class="dummy"><li>
|
<dd class="tag-source"><ul class="dummy"><li>
|
||||||
<a href="becca_entities_note.js.html">becca/entities/note.js</a>, <a href="becca_entities_note.js.html#line571">line 571</a>
|
<a href="becca_entities_note.js.html">becca/entities/note.js</a>, <a href="becca_entities_note.js.html#line573">line 573</a>
|
||||||
</li></ul></dd>
|
</li></ul></dd>
|
||||||
|
|
||||||
|
|
||||||
@@ -4920,7 +4920,7 @@ This method can be significantly faster than the getAttribute()
|
|||||||
|
|
||||||
<dt class="tag-source">Source:</dt>
|
<dt class="tag-source">Source:</dt>
|
||||||
<dd class="tag-source"><ul class="dummy"><li>
|
<dd class="tag-source"><ul class="dummy"><li>
|
||||||
<a href="becca_entities_note.js.html">becca/entities/note.js</a>, <a href="becca_entities_note.js.html#line495">line 495</a>
|
<a href="becca_entities_note.js.html">becca/entities/note.js</a>, <a href="becca_entities_note.js.html#line497">line 497</a>
|
||||||
</li></ul></dd>
|
</li></ul></dd>
|
||||||
|
|
||||||
|
|
||||||
@@ -5090,7 +5090,7 @@ This method can be significantly faster than the getAttribute()
|
|||||||
|
|
||||||
<dt class="tag-source">Source:</dt>
|
<dt class="tag-source">Source:</dt>
|
||||||
<dd class="tag-source"><ul class="dummy"><li>
|
<dd class="tag-source"><ul class="dummy"><li>
|
||||||
<a href="becca_entities_note.js.html">becca/entities/note.js</a>, <a href="becca_entities_note.js.html#line579">line 579</a>
|
<a href="becca_entities_note.js.html">becca/entities/note.js</a>, <a href="becca_entities_note.js.html#line581">line 581</a>
|
||||||
</li></ul></dd>
|
</li></ul></dd>
|
||||||
|
|
||||||
|
|
||||||
@@ -5245,7 +5245,7 @@ This method can be significantly faster than the getAttribute()
|
|||||||
|
|
||||||
<dt class="tag-source">Source:</dt>
|
<dt class="tag-source">Source:</dt>
|
||||||
<dd class="tag-source"><ul class="dummy"><li>
|
<dd class="tag-source"><ul class="dummy"><li>
|
||||||
<a href="becca_entities_note.js.html">becca/entities/note.js</a>, <a href="becca_entities_note.js.html#line483">line 483</a>
|
<a href="becca_entities_note.js.html">becca/entities/note.js</a>, <a href="becca_entities_note.js.html#line485">line 485</a>
|
||||||
</li></ul></dd>
|
</li></ul></dd>
|
||||||
|
|
||||||
|
|
||||||
@@ -5415,7 +5415,7 @@ This method can be significantly faster than the getAttribute()
|
|||||||
|
|
||||||
<dt class="tag-source">Source:</dt>
|
<dt class="tag-source">Source:</dt>
|
||||||
<dd class="tag-source"><ul class="dummy"><li>
|
<dd class="tag-source"><ul class="dummy"><li>
|
||||||
<a href="becca_entities_note.js.html">becca/entities/note.js</a>, <a href="becca_entities_note.js.html#line595">line 595</a>
|
<a href="becca_entities_note.js.html">becca/entities/note.js</a>, <a href="becca_entities_note.js.html#line597">line 597</a>
|
||||||
</li></ul></dd>
|
</li></ul></dd>
|
||||||
|
|
||||||
|
|
||||||
@@ -5570,7 +5570,7 @@ This method can be significantly faster than the getAttribute()
|
|||||||
|
|
||||||
<dt class="tag-source">Source:</dt>
|
<dt class="tag-source">Source:</dt>
|
||||||
<dd class="tag-source"><ul class="dummy"><li>
|
<dd class="tag-source"><ul class="dummy"><li>
|
||||||
<a href="becca_entities_note.js.html">becca/entities/note.js</a>, <a href="becca_entities_note.js.html#line507">line 507</a>
|
<a href="becca_entities_note.js.html">becca/entities/note.js</a>, <a href="becca_entities_note.js.html#line509">line 509</a>
|
||||||
</li></ul></dd>
|
</li></ul></dd>
|
||||||
|
|
||||||
|
|
||||||
@@ -5679,7 +5679,7 @@ This method can be significantly faster than the getAttribute()
|
|||||||
|
|
||||||
<dt class="tag-source">Source:</dt>
|
<dt class="tag-source">Source:</dt>
|
||||||
<dd class="tag-source"><ul class="dummy"><li>
|
<dd class="tag-source"><ul class="dummy"><li>
|
||||||
<a href="becca_entities_note.js.html">becca/entities/note.js</a>, <a href="becca_entities_note.js.html#line142">line 142</a>
|
<a href="becca_entities_note.js.html">becca/entities/note.js</a>, <a href="becca_entities_note.js.html#line144">line 144</a>
|
||||||
</li></ul></dd>
|
</li></ul></dd>
|
||||||
|
|
||||||
|
|
||||||
@@ -5781,7 +5781,7 @@ This method can be significantly faster than the getAttribute()
|
|||||||
|
|
||||||
<dt class="tag-source">Source:</dt>
|
<dt class="tag-source">Source:</dt>
|
||||||
<dd class="tag-source"><ul class="dummy"><li>
|
<dd class="tag-source"><ul class="dummy"><li>
|
||||||
<a href="becca_entities_note.js.html">becca/entities/note.js</a>, <a href="becca_entities_note.js.html#line155">line 155</a>
|
<a href="becca_entities_note.js.html">becca/entities/note.js</a>, <a href="becca_entities_note.js.html#line157">line 157</a>
|
||||||
</li></ul></dd>
|
</li></ul></dd>
|
||||||
|
|
||||||
|
|
||||||
@@ -5932,7 +5932,7 @@ This method can be significantly faster than the getAttribute()
|
|||||||
|
|
||||||
<dt class="tag-source">Source:</dt>
|
<dt class="tag-source">Source:</dt>
|
||||||
<dd class="tag-source"><ul class="dummy"><li>
|
<dd class="tag-source"><ul class="dummy"><li>
|
||||||
<a href="becca_entities_note.js.html">becca/entities/note.js</a>, <a href="becca_entities_note.js.html#line477">line 477</a>
|
<a href="becca_entities_note.js.html">becca/entities/note.js</a>, <a href="becca_entities_note.js.html#line479">line 479</a>
|
||||||
</li></ul></dd>
|
</li></ul></dd>
|
||||||
|
|
||||||
|
|
||||||
@@ -6102,7 +6102,7 @@ This method can be significantly faster than the getAttribute()
|
|||||||
|
|
||||||
<dt class="tag-source">Source:</dt>
|
<dt class="tag-source">Source:</dt>
|
||||||
<dd class="tag-source"><ul class="dummy"><li>
|
<dd class="tag-source"><ul class="dummy"><li>
|
||||||
<a href="becca_entities_note.js.html">becca/entities/note.js</a>, <a href="becca_entities_note.js.html#line587">line 587</a>
|
<a href="becca_entities_note.js.html">becca/entities/note.js</a>, <a href="becca_entities_note.js.html#line589">line 589</a>
|
||||||
</li></ul></dd>
|
</li></ul></dd>
|
||||||
|
|
||||||
|
|
||||||
@@ -6257,7 +6257,7 @@ This method can be significantly faster than the getAttribute()
|
|||||||
|
|
||||||
<dt class="tag-source">Source:</dt>
|
<dt class="tag-source">Source:</dt>
|
||||||
<dd class="tag-source"><ul class="dummy"><li>
|
<dd class="tag-source"><ul class="dummy"><li>
|
||||||
<a href="becca_entities_note.js.html">becca/entities/note.js</a>, <a href="becca_entities_note.js.html#line501">line 501</a>
|
<a href="becca_entities_note.js.html">becca/entities/note.js</a>, <a href="becca_entities_note.js.html#line503">line 503</a>
|
||||||
</li></ul></dd>
|
</li></ul></dd>
|
||||||
|
|
||||||
|
|
||||||
@@ -6366,7 +6366,7 @@ This method can be significantly faster than the getAttribute()
|
|||||||
|
|
||||||
<dt class="tag-source">Source:</dt>
|
<dt class="tag-source">Source:</dt>
|
||||||
<dd class="tag-source"><ul class="dummy"><li>
|
<dd class="tag-source"><ul class="dummy"><li>
|
||||||
<a href="becca_entities_note.js.html">becca/entities/note.js</a>, <a href="becca_entities_note.js.html#line315">line 315</a>
|
<a href="becca_entities_note.js.html">becca/entities/note.js</a>, <a href="becca_entities_note.js.html#line317">line 317</a>
|
||||||
</li></ul></dd>
|
</li></ul></dd>
|
||||||
|
|
||||||
|
|
||||||
@@ -6475,7 +6475,7 @@ This method can be significantly faster than the getAttribute()
|
|||||||
|
|
||||||
<dt class="tag-source">Source:</dt>
|
<dt class="tag-source">Source:</dt>
|
||||||
<dd class="tag-source"><ul class="dummy"><li>
|
<dd class="tag-source"><ul class="dummy"><li>
|
||||||
<a href="becca_entities_note.js.html">becca/entities/note.js</a>, <a href="becca_entities_note.js.html#line808">line 808</a>
|
<a href="becca_entities_note.js.html">becca/entities/note.js</a>, <a href="becca_entities_note.js.html#line810">line 810</a>
|
||||||
</li></ul></dd>
|
</li></ul></dd>
|
||||||
|
|
||||||
|
|
||||||
@@ -6577,7 +6577,7 @@ This method can be significantly faster than the getAttribute()
|
|||||||
|
|
||||||
<dt class="tag-source">Source:</dt>
|
<dt class="tag-source">Source:</dt>
|
||||||
<dd class="tag-source"><ul class="dummy"><li>
|
<dd class="tag-source"><ul class="dummy"><li>
|
||||||
<a href="becca_entities_note.js.html">becca/entities/note.js</a>, <a href="becca_entities_note.js.html#line787">line 787</a>
|
<a href="becca_entities_note.js.html">becca/entities/note.js</a>, <a href="becca_entities_note.js.html#line789">line 789</a>
|
||||||
</li></ul></dd>
|
</li></ul></dd>
|
||||||
|
|
||||||
|
|
||||||
@@ -6679,7 +6679,7 @@ This method can be significantly faster than the getAttribute()
|
|||||||
|
|
||||||
<dt class="tag-source">Source:</dt>
|
<dt class="tag-source">Source:</dt>
|
||||||
<dd class="tag-source"><ul class="dummy"><li>
|
<dd class="tag-source"><ul class="dummy"><li>
|
||||||
<a href="becca_entities_note.js.html">becca/entities/note.js</a>, <a href="becca_entities_note.js.html#line756">line 756</a>
|
<a href="becca_entities_note.js.html">becca/entities/note.js</a>, <a href="becca_entities_note.js.html#line758">line 758</a>
|
||||||
</li></ul></dd>
|
</li></ul></dd>
|
||||||
|
|
||||||
|
|
||||||
@@ -6781,7 +6781,7 @@ This method can be significantly faster than the getAttribute()
|
|||||||
|
|
||||||
<dt class="tag-source">Source:</dt>
|
<dt class="tag-source">Source:</dt>
|
||||||
<dd class="tag-source"><ul class="dummy"><li>
|
<dd class="tag-source"><ul class="dummy"><li>
|
||||||
<a href="becca_entities_note.js.html">becca/entities/note.js</a>, <a href="becca_entities_note.js.html#line907">line 907</a>
|
<a href="becca_entities_note.js.html">becca/entities/note.js</a>, <a href="becca_entities_note.js.html#line909">line 909</a>
|
||||||
</li></ul></dd>
|
</li></ul></dd>
|
||||||
|
|
||||||
|
|
||||||
@@ -6888,7 +6888,7 @@ This method can be significantly faster than the getAttribute()
|
|||||||
|
|
||||||
<dt class="tag-source">Source:</dt>
|
<dt class="tag-source">Source:</dt>
|
||||||
<dd class="tag-source"><ul class="dummy"><li>
|
<dd class="tag-source"><ul class="dummy"><li>
|
||||||
<a href="becca_entities_note.js.html">becca/entities/note.js</a>, <a href="becca_entities_note.js.html#line891">line 891</a>
|
<a href="becca_entities_note.js.html">becca/entities/note.js</a>, <a href="becca_entities_note.js.html#line893">line 893</a>
|
||||||
</li></ul></dd>
|
</li></ul></dd>
|
||||||
|
|
||||||
|
|
||||||
@@ -6990,7 +6990,7 @@ This method can be significantly faster than the getAttribute()
|
|||||||
|
|
||||||
<dt class="tag-source">Source:</dt>
|
<dt class="tag-source">Source:</dt>
|
||||||
<dd class="tag-source"><ul class="dummy"><li>
|
<dd class="tag-source"><ul class="dummy"><li>
|
||||||
<a href="becca_entities_note.js.html">becca/entities/note.js</a>, <a href="becca_entities_note.js.html#line165">line 165</a>
|
<a href="becca_entities_note.js.html">becca/entities/note.js</a>, <a href="becca_entities_note.js.html#line167">line 167</a>
|
||||||
</li></ul></dd>
|
</li></ul></dd>
|
||||||
|
|
||||||
|
|
||||||
@@ -7141,7 +7141,7 @@ This method can be significantly faster than the getAttribute()
|
|||||||
|
|
||||||
<dt class="tag-source">Source:</dt>
|
<dt class="tag-source">Source:</dt>
|
||||||
<dd class="tag-source"><ul class="dummy"><li>
|
<dd class="tag-source"><ul class="dummy"><li>
|
||||||
<a href="becca_entities_note.js.html">becca/entities/note.js</a>, <a href="becca_entities_note.js.html#line441">line 441</a>
|
<a href="becca_entities_note.js.html">becca/entities/note.js</a>, <a href="becca_entities_note.js.html#line443">line 443</a>
|
||||||
</li></ul></dd>
|
</li></ul></dd>
|
||||||
|
|
||||||
|
|
||||||
@@ -7319,7 +7319,7 @@ This method can be significantly faster than the getAttribute()
|
|||||||
|
|
||||||
<dt class="tag-source">Source:</dt>
|
<dt class="tag-source">Source:</dt>
|
||||||
<dd class="tag-source"><ul class="dummy"><li>
|
<dd class="tag-source"><ul class="dummy"><li>
|
||||||
<a href="becca_entities_note.js.html">becca/entities/note.js</a>, <a href="becca_entities_note.js.html#line514">line 514</a>
|
<a href="becca_entities_note.js.html">becca/entities/note.js</a>, <a href="becca_entities_note.js.html#line516">line 516</a>
|
||||||
</li></ul></dd>
|
</li></ul></dd>
|
||||||
|
|
||||||
|
|
||||||
@@ -7474,7 +7474,7 @@ This method can be significantly faster than the getAttribute()
|
|||||||
|
|
||||||
<dt class="tag-source">Source:</dt>
|
<dt class="tag-source">Source:</dt>
|
||||||
<dd class="tag-source"><ul class="dummy"><li>
|
<dd class="tag-source"><ul class="dummy"><li>
|
||||||
<a href="becca_entities_note.js.html">becca/entities/note.js</a>, <a href="becca_entities_note.js.html#line447">line 447</a>
|
<a href="becca_entities_note.js.html">becca/entities/note.js</a>, <a href="becca_entities_note.js.html#line449">line 449</a>
|
||||||
</li></ul></dd>
|
</li></ul></dd>
|
||||||
|
|
||||||
|
|
||||||
@@ -7629,7 +7629,7 @@ This method can be significantly faster than the getAttribute()
|
|||||||
|
|
||||||
<dt class="tag-source">Source:</dt>
|
<dt class="tag-source">Source:</dt>
|
||||||
<dd class="tag-source"><ul class="dummy"><li>
|
<dd class="tag-source"><ul class="dummy"><li>
|
||||||
<a href="becca_entities_note.js.html">becca/entities/note.js</a>, <a href="becca_entities_note.js.html#line459">line 459</a>
|
<a href="becca_entities_note.js.html">becca/entities/note.js</a>, <a href="becca_entities_note.js.html#line461">line 461</a>
|
||||||
</li></ul></dd>
|
</li></ul></dd>
|
||||||
|
|
||||||
|
|
||||||
@@ -7784,7 +7784,7 @@ This method can be significantly faster than the getAttribute()
|
|||||||
|
|
||||||
<dt class="tag-source">Source:</dt>
|
<dt class="tag-source">Source:</dt>
|
||||||
<dd class="tag-source"><ul class="dummy"><li>
|
<dd class="tag-source"><ul class="dummy"><li>
|
||||||
<a href="becca_entities_note.js.html">becca/entities/note.js</a>, <a href="becca_entities_note.js.html#line453">line 453</a>
|
<a href="becca_entities_note.js.html">becca/entities/note.js</a>, <a href="becca_entities_note.js.html#line455">line 455</a>
|
||||||
</li></ul></dd>
|
</li></ul></dd>
|
||||||
|
|
||||||
|
|
||||||
@@ -7934,7 +7934,7 @@ This method can be significantly faster than the getAttribute()
|
|||||||
|
|
||||||
<dt class="tag-source">Source:</dt>
|
<dt class="tag-source">Source:</dt>
|
||||||
<dd class="tag-source"><ul class="dummy"><li>
|
<dd class="tag-source"><ul class="dummy"><li>
|
||||||
<a href="becca_entities_note.js.html">becca/entities/note.js</a>, <a href="becca_entities_note.js.html#line966">line 966</a>
|
<a href="becca_entities_note.js.html">becca/entities/note.js</a>, <a href="becca_entities_note.js.html#line968">line 968</a>
|
||||||
</li></ul></dd>
|
</li></ul></dd>
|
||||||
|
|
||||||
|
|
||||||
@@ -8040,7 +8040,7 @@ This method can be significantly faster than the getAttribute()
|
|||||||
|
|
||||||
<dt class="tag-source">Source:</dt>
|
<dt class="tag-source">Source:</dt>
|
||||||
<dd class="tag-source"><ul class="dummy"><li>
|
<dd class="tag-source"><ul class="dummy"><li>
|
||||||
<a href="becca_entities_note.js.html">becca/entities/note.js</a>, <a href="becca_entities_note.js.html#line304">line 304</a>
|
<a href="becca_entities_note.js.html">becca/entities/note.js</a>, <a href="becca_entities_note.js.html#line306">line 306</a>
|
||||||
</li></ul></dd>
|
</li></ul></dd>
|
||||||
|
|
||||||
|
|
||||||
@@ -8146,7 +8146,7 @@ This method can be significantly faster than the getAttribute()
|
|||||||
|
|
||||||
<dt class="tag-source">Source:</dt>
|
<dt class="tag-source">Source:</dt>
|
||||||
<dd class="tag-source"><ul class="dummy"><li>
|
<dd class="tag-source"><ul class="dummy"><li>
|
||||||
<a href="becca_entities_note.js.html">becca/entities/note.js</a>, <a href="becca_entities_note.js.html#line296">line 296</a>
|
<a href="becca_entities_note.js.html">becca/entities/note.js</a>, <a href="becca_entities_note.js.html#line298">line 298</a>
|
||||||
</li></ul></dd>
|
</li></ul></dd>
|
||||||
|
|
||||||
|
|
||||||
@@ -8252,7 +8252,7 @@ This method can be significantly faster than the getAttribute()
|
|||||||
|
|
||||||
<dt class="tag-source">Source:</dt>
|
<dt class="tag-source">Source:</dt>
|
||||||
<dd class="tag-source"><ul class="dummy"><li>
|
<dd class="tag-source"><ul class="dummy"><li>
|
||||||
<a href="becca_entities_note.js.html">becca/entities/note.js</a>, <a href="becca_entities_note.js.html#line291">line 291</a>
|
<a href="becca_entities_note.js.html">becca/entities/note.js</a>, <a href="becca_entities_note.js.html#line293">line 293</a>
|
||||||
</li></ul></dd>
|
</li></ul></dd>
|
||||||
|
|
||||||
|
|
||||||
@@ -8358,7 +8358,7 @@ This method can be significantly faster than the getAttribute()
|
|||||||
|
|
||||||
<dt class="tag-source">Source:</dt>
|
<dt class="tag-source">Source:</dt>
|
||||||
<dd class="tag-source"><ul class="dummy"><li>
|
<dd class="tag-source"><ul class="dummy"><li>
|
||||||
<a href="becca_entities_note.js.html">becca/entities/note.js</a>, <a href="becca_entities_note.js.html#line286">line 286</a>
|
<a href="becca_entities_note.js.html">becca/entities/note.js</a>, <a href="becca_entities_note.js.html#line288">line 288</a>
|
||||||
</li></ul></dd>
|
</li></ul></dd>
|
||||||
|
|
||||||
|
|
||||||
@@ -8464,7 +8464,7 @@ This method can be significantly faster than the getAttribute()
|
|||||||
|
|
||||||
<dt class="tag-source">Source:</dt>
|
<dt class="tag-source">Source:</dt>
|
||||||
<dd class="tag-source"><ul class="dummy"><li>
|
<dd class="tag-source"><ul class="dummy"><li>
|
||||||
<a href="becca_entities_note.js.html">becca/entities/note.js</a>, <a href="becca_entities_note.js.html#line310">line 310</a>
|
<a href="becca_entities_note.js.html">becca/entities/note.js</a>, <a href="becca_entities_note.js.html#line312">line 312</a>
|
||||||
</li></ul></dd>
|
</li></ul></dd>
|
||||||
|
|
||||||
|
|
||||||
@@ -8856,7 +8856,7 @@ This is a low level method, for notes and branches use `note.deleteNote()` and '
|
|||||||
|
|
||||||
<dt class="tag-source">Source:</dt>
|
<dt class="tag-source">Source:</dt>
|
||||||
<dd class="tag-source"><ul class="dummy"><li>
|
<dd class="tag-source"><ul class="dummy"><li>
|
||||||
<a href="becca_entities_note.js.html">becca/entities/note.js</a>, <a href="becca_entities_note.js.html#line1010">line 1010</a>
|
<a href="becca_entities_note.js.html">becca/entities/note.js</a>, <a href="becca_entities_note.js.html#line1012">line 1012</a>
|
||||||
</li></ul></dd>
|
</li></ul></dd>
|
||||||
|
|
||||||
|
|
||||||
@@ -9036,7 +9036,7 @@ This is a low level method, for notes and branches use `note.deleteNote()` and '
|
|||||||
|
|
||||||
<dt class="tag-source">Source:</dt>
|
<dt class="tag-source">Source:</dt>
|
||||||
<dd class="tag-source"><ul class="dummy"><li>
|
<dd class="tag-source"><ul class="dummy"><li>
|
||||||
<a href="becca_entities_note.js.html">becca/entities/note.js</a>, <a href="becca_entities_note.js.html#line1101">line 1101</a>
|
<a href="becca_entities_note.js.html">becca/entities/note.js</a>, <a href="becca_entities_note.js.html#line1103">line 1103</a>
|
||||||
</li></ul></dd>
|
</li></ul></dd>
|
||||||
|
|
||||||
|
|
||||||
@@ -9216,7 +9216,7 @@ This is a low level method, for notes and branches use `note.deleteNote()` and '
|
|||||||
|
|
||||||
<dt class="tag-source">Source:</dt>
|
<dt class="tag-source">Source:</dt>
|
||||||
<dd class="tag-source"><ul class="dummy"><li>
|
<dd class="tag-source"><ul class="dummy"><li>
|
||||||
<a href="becca_entities_note.js.html">becca/entities/note.js</a>, <a href="becca_entities_note.js.html#line1109">line 1109</a>
|
<a href="becca_entities_note.js.html">becca/entities/note.js</a>, <a href="becca_entities_note.js.html#line1111">line 1111</a>
|
||||||
</li></ul></dd>
|
</li></ul></dd>
|
||||||
|
|
||||||
|
|
||||||
@@ -9363,6 +9363,111 @@ This is a low level method, for notes and branches use `note.deleteNote()` and '
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<h4 class="name" id="saveNoteRevision"><span class="type-signature"></span>saveNoteRevision<span class="signature">()</span><span class="type-signature"> → {<a href="NoteRevision.html">NoteRevision</a>|null}</span></h4>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<dl class="details">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<dt class="tag-source">Source:</dt>
|
||||||
|
<dd class="tag-source"><ul class="dummy"><li>
|
||||||
|
<a href="becca_entities_note.js.html">becca/entities/note.js</a>, <a href="becca_entities_note.js.html#line1172">line 1172</a>
|
||||||
|
</li></ul></dd>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
</dl>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<h5>Returns:</h5>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<dl>
|
||||||
|
<dt>
|
||||||
|
Type
|
||||||
|
</dt>
|
||||||
|
<dd>
|
||||||
|
|
||||||
|
<span class="param-type"><a href="NoteRevision.html">NoteRevision</a></span>
|
||||||
|
|
|
||||||
|
|
||||||
|
<span class="param-type">null</span>
|
||||||
|
|
||||||
|
|
||||||
|
</dd>
|
||||||
|
</dl>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<h4 class="name" id="setAttribute"><span class="type-signature"></span>setAttribute<span class="signature">(type, name, value<span class="signature-attributes">opt</span>)</span><span class="type-signature"></span></h4>
|
<h4 class="name" id="setAttribute"><span class="type-signature"></span>setAttribute<span class="signature">(type, name, value<span class="signature-attributes">opt</span>)</span><span class="type-signature"></span></h4>
|
||||||
|
|
||||||
|
|
||||||
@@ -9538,7 +9643,7 @@ This is a low level method, for notes and branches use `note.deleteNote()` and '
|
|||||||
|
|
||||||
<dt class="tag-source">Source:</dt>
|
<dt class="tag-source">Source:</dt>
|
||||||
<dd class="tag-source"><ul class="dummy"><li>
|
<dd class="tag-source"><ul class="dummy"><li>
|
||||||
<a href="becca_entities_note.js.html">becca/entities/note.js</a>, <a href="becca_entities_note.js.html#line979">line 979</a>
|
<a href="becca_entities_note.js.html">becca/entities/note.js</a>, <a href="becca_entities_note.js.html#line981">line 981</a>
|
||||||
</li></ul></dd>
|
</li></ul></dd>
|
||||||
|
|
||||||
|
|
||||||
@@ -9718,7 +9823,7 @@ This is a low level method, for notes and branches use `note.deleteNote()` and '
|
|||||||
|
|
||||||
<dt class="tag-source">Source:</dt>
|
<dt class="tag-source">Source:</dt>
|
||||||
<dd class="tag-source"><ul class="dummy"><li>
|
<dd class="tag-source"><ul class="dummy"><li>
|
||||||
<a href="becca_entities_note.js.html">becca/entities/note.js</a>, <a href="becca_entities_note.js.html#line1085">line 1085</a>
|
<a href="becca_entities_note.js.html">becca/entities/note.js</a>, <a href="becca_entities_note.js.html#line1087">line 1087</a>
|
||||||
</li></ul></dd>
|
</li></ul></dd>
|
||||||
|
|
||||||
|
|
||||||
@@ -9878,7 +9983,7 @@ This is a low level method, for notes and branches use `note.deleteNote()` and '
|
|||||||
|
|
||||||
<dt class="tag-source">Source:</dt>
|
<dt class="tag-source">Source:</dt>
|
||||||
<dd class="tag-source"><ul class="dummy"><li>
|
<dd class="tag-source"><ul class="dummy"><li>
|
||||||
<a href="becca_entities_note.js.html">becca/entities/note.js</a>, <a href="becca_entities_note.js.html#line1093">line 1093</a>
|
<a href="becca_entities_note.js.html">becca/entities/note.js</a>, <a href="becca_entities_note.js.html#line1095">line 1095</a>
|
||||||
</li></ul></dd>
|
</li></ul></dd>
|
||||||
|
|
||||||
|
|
||||||
@@ -10120,7 +10225,7 @@ This is a low level method, for notes and branches use `note.deleteNote()` and '
|
|||||||
|
|
||||||
<dt class="tag-source">Source:</dt>
|
<dt class="tag-source">Source:</dt>
|
||||||
<dd class="tag-source"><ul class="dummy"><li>
|
<dd class="tag-source"><ul class="dummy"><li>
|
||||||
<a href="becca_entities_note.js.html">becca/entities/note.js</a>, <a href="becca_entities_note.js.html#line1052">line 1052</a>
|
<a href="becca_entities_note.js.html">becca/entities/note.js</a>, <a href="becca_entities_note.js.html#line1054">line 1054</a>
|
||||||
</li></ul></dd>
|
</li></ul></dd>
|
||||||
|
|
||||||
|
|
||||||
@@ -10331,7 +10436,7 @@ This is a low level method, for notes and branches use `note.deleteNote()` and '
|
|||||||
|
|
||||||
<dt class="tag-source">Source:</dt>
|
<dt class="tag-source">Source:</dt>
|
||||||
<dd class="tag-source"><ul class="dummy"><li>
|
<dd class="tag-source"><ul class="dummy"><li>
|
||||||
<a href="becca_entities_note.js.html">becca/entities/note.js</a>, <a href="becca_entities_note.js.html#line1068">line 1068</a>
|
<a href="becca_entities_note.js.html">becca/entities/note.js</a>, <a href="becca_entities_note.js.html#line1070">line 1070</a>
|
||||||
</li></ul></dd>
|
</li></ul></dd>
|
||||||
|
|
||||||
|
|
||||||
@@ -10542,7 +10647,7 @@ This is a low level method, for notes and branches use `note.deleteNote()` and '
|
|||||||
|
|
||||||
<dt class="tag-source">Source:</dt>
|
<dt class="tag-source">Source:</dt>
|
||||||
<dd class="tag-source"><ul class="dummy"><li>
|
<dd class="tag-source"><ul class="dummy"><li>
|
||||||
<a href="becca_entities_note.js.html">becca/entities/note.js</a>, <a href="becca_entities_note.js.html#line1077">line 1077</a>
|
<a href="becca_entities_note.js.html">becca/entities/note.js</a>, <a href="becca_entities_note.js.html#line1079">line 1079</a>
|
||||||
</li></ul></dd>
|
</li></ul></dd>
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -37,6 +37,8 @@ const entityChangesService = require('../../services/entity_changes');
|
|||||||
const AbstractEntity = require("./abstract_entity");
|
const AbstractEntity = require("./abstract_entity");
|
||||||
const NoteRevision = require("./note_revision");
|
const NoteRevision = require("./note_revision");
|
||||||
const TaskContext = require("../../services/task_context.js");
|
const TaskContext = require("../../services/task_context.js");
|
||||||
|
const optionService = require("../../services/options.js");
|
||||||
|
const noteRevisionService = require("../../services/note_revisions.js");
|
||||||
|
|
||||||
const LABEL = 'label';
|
const LABEL = 'label';
|
||||||
const RELATION = 'relation';
|
const RELATION = 'relation';
|
||||||
@@ -266,7 +268,7 @@ class Note extends AbstractEntity {
|
|||||||
|
|
||||||
setContent(content, ignoreMissingProtectedSession = false) {
|
setContent(content, ignoreMissingProtectedSession = false) {
|
||||||
if (content === null || content === undefined) {
|
if (content === null || content === undefined) {
|
||||||
throw new Error(`Cannot set null content to note ${this.noteId}`);
|
throw new Error(`Cannot set null content to note '${this.noteId}'`);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.isStringNote()) {
|
if (this.isStringNote()) {
|
||||||
@@ -288,7 +290,7 @@ class Note extends AbstractEntity {
|
|||||||
pojo.content = protectedSessionService.encrypt(pojo.content);
|
pojo.content = protectedSessionService.encrypt(pojo.content);
|
||||||
}
|
}
|
||||||
else if (!ignoreMissingProtectedSession) {
|
else if (!ignoreMissingProtectedSession) {
|
||||||
throw new Error(`Cannot update content of noteId=${this.noteId} since we're out of protected session.`);
|
throw new Error(`Cannot update content of noteId '${this.noteId}' since we're out of protected session.`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1192,6 +1194,41 @@ class Note extends AbstractEntity {
|
|||||||
return !(this.noteId in this.becca.notes);
|
return !(this.noteId in this.becca.notes);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return {NoteRevision|null}
|
||||||
|
*/
|
||||||
|
saveNoteRevision() {
|
||||||
|
const content = this.getContent();
|
||||||
|
|
||||||
|
if (!content || (Buffer.isBuffer(content) && content.byteLength === 0)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const contentMetadata = this.getContentMetadata();
|
||||||
|
|
||||||
|
const noteRevision = new NoteRevision({
|
||||||
|
noteId: this.noteId,
|
||||||
|
// title and text should be decrypted now
|
||||||
|
title: this.title,
|
||||||
|
type: this.type,
|
||||||
|
mime: this.mime,
|
||||||
|
isProtected: false, // will be fixed in the protectNoteRevisions() call
|
||||||
|
utcDateLastEdited: this.utcDateModified > contentMetadata.utcDateModified
|
||||||
|
? this.utcDateModified
|
||||||
|
: contentMetadata.utcDateModified,
|
||||||
|
utcDateCreated: dateUtils.utcNowDateTime(),
|
||||||
|
utcDateModified: dateUtils.utcNowDateTime(),
|
||||||
|
dateLastEdited: this.dateModified > contentMetadata.dateModified
|
||||||
|
? this.dateModified
|
||||||
|
: contentMetadata.dateModified,
|
||||||
|
dateCreated: dateUtils.localNowDateTime()
|
||||||
|
}).save();
|
||||||
|
|
||||||
|
noteRevision.setContent(content);
|
||||||
|
|
||||||
|
return noteRevision;
|
||||||
|
}
|
||||||
|
|
||||||
beforeSaving() {
|
beforeSaving() {
|
||||||
super.beforeSaving();
|
super.beforeSaving();
|
||||||
|
|
||||||
|
|||||||
@@ -237,7 +237,7 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
<td class="description last">text, code, file, image, search, book, relation-map - MANDATORY</td>
|
<td class="description last">text, code, file, image, search, book, relation-map, canvas - MANDATORY</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -238,7 +238,7 @@ function BackendScriptApi(currentNote, apiParams) {
|
|||||||
* @property {string} parentNoteId - MANDATORY
|
* @property {string} parentNoteId - MANDATORY
|
||||||
* @property {string} title - MANDATORY
|
* @property {string} title - MANDATORY
|
||||||
* @property {string|buffer} content - MANDATORY
|
* @property {string|buffer} content - MANDATORY
|
||||||
* @property {string} type - text, code, file, image, search, book, relation-map - MANDATORY
|
* @property {string} type - text, code, file, image, search, book, relation-map, canvas - MANDATORY
|
||||||
* @property {string} mime - value is derived from default mimes for type
|
* @property {string} mime - value is derived from default mimes for type
|
||||||
* @property {boolean} isProtected - default is false
|
* @property {boolean} isProtected - default is false
|
||||||
* @property {boolean} isExpanded - default is false
|
* @property {boolean} isExpanded - default is false
|
||||||
@@ -454,6 +454,15 @@ function BackendScriptApi(currentNote, apiParams) {
|
|||||||
* @return {{syncVersion, appVersion, buildRevision, dbVersion, dataDirectory, buildDate}|*} - object representing basic info about running Trilium version
|
* @return {{syncVersion, appVersion, buildRevision, dbVersion, dataDirectory, buildDate}|*} - object representing basic info about running Trilium version
|
||||||
*/
|
*/
|
||||||
this.getAppInfo = () => appInfo
|
this.getAppInfo = () => appInfo
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This object contains "at your risk" and "no BC guarantees" objects for advanced use cases.
|
||||||
|
*
|
||||||
|
* @type {{becca: Becca}}
|
||||||
|
*/
|
||||||
|
this.__private = {
|
||||||
|
becca
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = BackendScriptApi;
|
module.exports = BackendScriptApi;
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -167,7 +167,7 @@ This note's representation is used in note tree and is kept in Froca.</div>
|
|||||||
|
|
||||||
<dt class="tag-source">Source:</dt>
|
<dt class="tag-source">Source:</dt>
|
||||||
<dd class="tag-source"><ul class="dummy"><li>
|
<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#line28">line 28</a>
|
||||||
</li></ul></dd>
|
</li></ul></dd>
|
||||||
|
|
||||||
|
|
||||||
@@ -267,7 +267,7 @@ This note's representation is used in note tree and is kept in Froca.</div>
|
|||||||
|
|
||||||
<dt class="tag-source">Source:</dt>
|
<dt class="tag-source">Source:</dt>
|
||||||
<dd class="tag-source"><ul class="dummy"><li>
|
<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#line36">line 36</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>
|
</li></ul></dd>
|
||||||
|
|
||||||
|
|
||||||
@@ -335,7 +335,7 @@ This note's representation is used in note tree and is kept in Froca.</div>
|
|||||||
|
|
||||||
<dt class="tag-source">Source:</dt>
|
<dt class="tag-source">Source:</dt>
|
||||||
<dd class="tag-source"><ul class="dummy"><li>
|
<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>
|
<a href="entities_note_short.js.html">entities/note_short.js</a>, <a href="entities_note_short.js.html#line45">line 45</a>
|
||||||
</li></ul></dd>
|
</li></ul></dd>
|
||||||
|
|
||||||
|
|
||||||
@@ -403,7 +403,7 @@ This note's representation is used in note tree and is kept in Froca.</div>
|
|||||||
|
|
||||||
<dt class="tag-source">Source:</dt>
|
<dt class="tag-source">Source:</dt>
|
||||||
<dd class="tag-source"><ul class="dummy"><li>
|
<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>
|
<a href="entities_note_short.js.html">entities/note_short.js</a>, <a href="entities_note_short.js.html#line51">line 51</a>
|
||||||
</li></ul></dd>
|
</li></ul></dd>
|
||||||
|
|
||||||
|
|
||||||
@@ -471,7 +471,7 @@ This note's representation is used in note tree and is kept in Froca.</div>
|
|||||||
|
|
||||||
<dt class="tag-source">Source:</dt>
|
<dt class="tag-source">Source:</dt>
|
||||||
<dd class="tag-source"><ul class="dummy"><li>
|
<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#line61">line 61</a>
|
<a href="entities_note_short.js.html">entities/note_short.js</a>, <a href="entities_note_short.js.html#line62">line 62</a>
|
||||||
</li></ul></dd>
|
</li></ul></dd>
|
||||||
|
|
||||||
|
|
||||||
@@ -543,7 +543,7 @@ This note's representation is used in note tree and is kept in Froca.</div>
|
|||||||
|
|
||||||
<dt class="tag-source">Source:</dt>
|
<dt class="tag-source">Source:</dt>
|
||||||
<dd class="tag-source"><ul class="dummy"><li>
|
<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#line71">line 71</a>
|
<a href="entities_note_short.js.html">entities/note_short.js</a>, <a href="entities_note_short.js.html#line72">line 72</a>
|
||||||
</li></ul></dd>
|
</li></ul></dd>
|
||||||
|
|
||||||
|
|
||||||
@@ -611,7 +611,7 @@ This note's representation is used in note tree and is kept in Froca.</div>
|
|||||||
|
|
||||||
<dt class="tag-source">Source:</dt>
|
<dt class="tag-source">Source:</dt>
|
||||||
<dd class="tag-source"><ul class="dummy"><li>
|
<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#line57">line 57</a>
|
<a href="entities_note_short.js.html">entities/note_short.js</a>, <a href="entities_note_short.js.html#line58">line 58</a>
|
||||||
</li></ul></dd>
|
</li></ul></dd>
|
||||||
|
|
||||||
|
|
||||||
@@ -679,7 +679,7 @@ This note's representation is used in note tree and is kept in Froca.</div>
|
|||||||
|
|
||||||
<dt class="tag-source">Source:</dt>
|
<dt class="tag-source">Source:</dt>
|
||||||
<dd class="tag-source"><ul class="dummy"><li>
|
<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>
|
<a href="entities_note_short.js.html">entities/note_short.js</a>, <a href="entities_note_short.js.html#line43">line 43</a>
|
||||||
</li></ul></dd>
|
</li></ul></dd>
|
||||||
|
|
||||||
|
|
||||||
@@ -747,7 +747,7 @@ This note's representation is used in note tree and is kept in Froca.</div>
|
|||||||
|
|
||||||
<dt class="tag-source">Source:</dt>
|
<dt class="tag-source">Source:</dt>
|
||||||
<dd class="tag-source"><ul class="dummy"><li>
|
<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>
|
<a href="entities_note_short.js.html">entities/note_short.js</a>, <a href="entities_note_short.js.html#line48">line 48</a>
|
||||||
</li></ul></dd>
|
</li></ul></dd>
|
||||||
|
|
||||||
|
|
||||||
@@ -815,7 +815,7 @@ This note's representation is used in note tree and is kept in Froca.</div>
|
|||||||
|
|
||||||
<dt class="tag-source">Source:</dt>
|
<dt class="tag-source">Source:</dt>
|
||||||
<dd class="tag-source"><ul class="dummy"><li>
|
<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#line39">line 39</a>
|
<a href="entities_note_short.js.html">entities/note_short.js</a>, <a href="entities_note_short.js.html#line40">line 40</a>
|
||||||
</li></ul></dd>
|
</li></ul></dd>
|
||||||
|
|
||||||
|
|
||||||
@@ -883,7 +883,7 @@ This note's representation is used in note tree and is kept in Froca.</div>
|
|||||||
|
|
||||||
<dt class="tag-source">Source:</dt>
|
<dt class="tag-source">Source:</dt>
|
||||||
<dd class="tag-source"><ul class="dummy"><li>
|
<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#line59">line 59</a>
|
<a href="entities_note_short.js.html">entities/note_short.js</a>, <a href="entities_note_short.js.html#line60">line 60</a>
|
||||||
</li></ul></dd>
|
</li></ul></dd>
|
||||||
|
|
||||||
|
|
||||||
@@ -955,7 +955,7 @@ This note's representation is used in note tree and is kept in Froca.</div>
|
|||||||
|
|
||||||
<dt class="tag-source">Source:</dt>
|
<dt class="tag-source">Source:</dt>
|
||||||
<dd class="tag-source"><ul class="dummy"><li>
|
<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#line66">line 66</a>
|
<a href="entities_note_short.js.html">entities/note_short.js</a>, <a href="entities_note_short.js.html#line67">line 67</a>
|
||||||
</li></ul></dd>
|
</li></ul></dd>
|
||||||
|
|
||||||
|
|
||||||
@@ -1103,7 +1103,7 @@ This note's representation is used in note tree and is kept in Froca.</div>
|
|||||||
|
|
||||||
<dt class="tag-source">Source:</dt>
|
<dt class="tag-source">Source:</dt>
|
||||||
<dd class="tag-source"><ul class="dummy"><li>
|
<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#line498">line 498</a>
|
<a href="entities_note_short.js.html">entities/note_short.js</a>, <a href="entities_note_short.js.html#line499">line 499</a>
|
||||||
</li></ul></dd>
|
</li></ul></dd>
|
||||||
|
|
||||||
|
|
||||||
@@ -1303,7 +1303,7 @@ This note's representation is used in note tree and is kept in Froca.</div>
|
|||||||
|
|
||||||
<dt class="tag-source">Source:</dt>
|
<dt class="tag-source">Source:</dt>
|
||||||
<dd class="tag-source"><ul class="dummy"><li>
|
<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#line237">line 237</a>
|
||||||
</li></ul></dd>
|
</li></ul></dd>
|
||||||
|
|
||||||
|
|
||||||
@@ -1481,7 +1481,7 @@ This note's representation is used in note tree and is kept in Froca.</div>
|
|||||||
|
|
||||||
<dt class="tag-source">Source:</dt>
|
<dt class="tag-source">Source:</dt>
|
||||||
<dd class="tag-source"><ul class="dummy"><li>
|
<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#line520">line 520</a>
|
<a href="entities_note_short.js.html">entities/note_short.js</a>, <a href="entities_note_short.js.html#line521">line 521</a>
|
||||||
</li></ul></dd>
|
</li></ul></dd>
|
||||||
|
|
||||||
|
|
||||||
@@ -1589,7 +1589,7 @@ This note's representation is used in note tree and is kept in Froca.</div>
|
|||||||
|
|
||||||
<dt class="tag-source">Source:</dt>
|
<dt class="tag-source">Source:</dt>
|
||||||
<dd class="tag-source"><ul class="dummy"><li>
|
<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#line161">line 161</a>
|
<a href="entities_note_short.js.html">entities/note_short.js</a>, <a href="entities_note_short.js.html#line162">line 162</a>
|
||||||
</li></ul></dd>
|
</li></ul></dd>
|
||||||
|
|
||||||
|
|
||||||
@@ -1693,7 +1693,7 @@ This note's representation is used in note tree and is kept in Froca.</div>
|
|||||||
|
|
||||||
<dt class="tag-source">Source:</dt>
|
<dt class="tag-source">Source:</dt>
|
||||||
<dd class="tag-source"><ul class="dummy"><li>
|
<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#line144">line 144</a>
|
<a href="entities_note_short.js.html">entities/note_short.js</a>, <a href="entities_note_short.js.html#line145">line 145</a>
|
||||||
</li></ul></dd>
|
</li></ul></dd>
|
||||||
|
|
||||||
|
|
||||||
@@ -1795,7 +1795,7 @@ This note's representation is used in note tree and is kept in Froca.</div>
|
|||||||
|
|
||||||
<dt class="tag-source">Source:</dt>
|
<dt class="tag-source">Source:</dt>
|
||||||
<dd class="tag-source"><ul class="dummy"><li>
|
<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#line171">line 171</a>
|
<a href="entities_note_short.js.html">entities/note_short.js</a>, <a href="entities_note_short.js.html#line172">line 172</a>
|
||||||
</li></ul></dd>
|
</li></ul></dd>
|
||||||
|
|
||||||
|
|
||||||
@@ -1897,7 +1897,7 @@ This note's representation is used in note tree and is kept in Froca.</div>
|
|||||||
|
|
||||||
<dt class="tag-source">Source:</dt>
|
<dt class="tag-source">Source:</dt>
|
||||||
<dd class="tag-source"><ul class="dummy"><li>
|
<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#line209">line 209</a>
|
<a href="entities_note_short.js.html">entities/note_short.js</a>, <a href="entities_note_short.js.html#line210">line 210</a>
|
||||||
</li></ul></dd>
|
</li></ul></dd>
|
||||||
|
|
||||||
|
|
||||||
@@ -1999,7 +1999,7 @@ This note's representation is used in note tree and is kept in Froca.</div>
|
|||||||
|
|
||||||
<dt class="tag-source">Source:</dt>
|
<dt class="tag-source">Source:</dt>
|
||||||
<dd class="tag-source"><ul class="dummy"><li>
|
<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#line215">line 215</a>
|
||||||
</li></ul></dd>
|
</li></ul></dd>
|
||||||
|
|
||||||
|
|
||||||
@@ -2150,7 +2150,7 @@ This note's representation is used in note tree and is kept in Froca.</div>
|
|||||||
|
|
||||||
<dt class="tag-source">Source:</dt>
|
<dt class="tag-source">Source:</dt>
|
||||||
<dd class="tag-source"><ul class="dummy"><li>
|
<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#line560">line 560</a>
|
<a href="entities_note_short.js.html">entities/note_short.js</a>, <a href="entities_note_short.js.html#line561">line 561</a>
|
||||||
</li></ul></dd>
|
</li></ul></dd>
|
||||||
|
|
||||||
|
|
||||||
@@ -2317,7 +2317,7 @@ This note's representation is used in note tree and is kept in Froca.</div>
|
|||||||
|
|
||||||
<dt class="tag-source">Source:</dt>
|
<dt class="tag-source">Source:</dt>
|
||||||
<dd class="tag-source"><ul class="dummy"><li>
|
<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#line384">line 384</a>
|
<a href="entities_note_short.js.html">entities/note_short.js</a>, <a href="entities_note_short.js.html#line385">line 385</a>
|
||||||
</li></ul></dd>
|
</li></ul></dd>
|
||||||
|
|
||||||
|
|
||||||
@@ -2472,7 +2472,7 @@ This note's representation is used in note tree and is kept in Froca.</div>
|
|||||||
|
|
||||||
<dt class="tag-source">Source:</dt>
|
<dt class="tag-source">Source:</dt>
|
||||||
<dd class="tag-source"><ul class="dummy"><li>
|
<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#line584">line 584</a>
|
<a href="entities_note_short.js.html">entities/note_short.js</a>, <a href="entities_note_short.js.html#line585">line 585</a>
|
||||||
</li></ul></dd>
|
</li></ul></dd>
|
||||||
|
|
||||||
|
|
||||||
@@ -2582,7 +2582,7 @@ This note's representation is used in note tree and is kept in Froca.</div>
|
|||||||
|
|
||||||
<dt class="tag-source">Source:</dt>
|
<dt class="tag-source">Source:</dt>
|
||||||
<dd class="tag-source"><ul class="dummy"><li>
|
<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#line706">line 706</a>
|
<a href="entities_note_short.js.html">entities/note_short.js</a>, <a href="entities_note_short.js.html#line707">line 707</a>
|
||||||
</li></ul></dd>
|
</li></ul></dd>
|
||||||
|
|
||||||
|
|
||||||
@@ -2756,7 +2756,7 @@ This note's representation is used in note tree and is kept in Froca.</div>
|
|||||||
|
|
||||||
<dt class="tag-source">Source:</dt>
|
<dt class="tag-source">Source:</dt>
|
||||||
<dd class="tag-source"><ul class="dummy"><li>
|
<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#line487">line 487</a>
|
<a href="entities_note_short.js.html">entities/note_short.js</a>, <a href="entities_note_short.js.html#line488">line 488</a>
|
||||||
</li></ul></dd>
|
</li></ul></dd>
|
||||||
|
|
||||||
|
|
||||||
@@ -2956,7 +2956,7 @@ This note's representation is used in note tree and is kept in Froca.</div>
|
|||||||
|
|
||||||
<dt class="tag-source">Source:</dt>
|
<dt class="tag-source">Source:</dt>
|
||||||
<dd class="tag-source"><ul class="dummy"><li>
|
<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#line223">line 223</a>
|
<a href="entities_note_short.js.html">entities/note_short.js</a>, <a href="entities_note_short.js.html#line224">line 224</a>
|
||||||
</li></ul></dd>
|
</li></ul></dd>
|
||||||
|
|
||||||
|
|
||||||
@@ -3134,7 +3134,7 @@ This note's representation is used in note tree and is kept in Froca.</div>
|
|||||||
|
|
||||||
<dt class="tag-source">Source:</dt>
|
<dt class="tag-source">Source:</dt>
|
||||||
<dd class="tag-source"><ul class="dummy"><li>
|
<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#line509">line 509</a>
|
<a href="entities_note_short.js.html">entities/note_short.js</a>, <a href="entities_note_short.js.html#line510">line 510</a>
|
||||||
</li></ul></dd>
|
</li></ul></dd>
|
||||||
|
|
||||||
|
|
||||||
@@ -3289,7 +3289,7 @@ This note's representation is used in note tree and is kept in Froca.</div>
|
|||||||
|
|
||||||
<dt class="tag-source">Source:</dt>
|
<dt class="tag-source">Source:</dt>
|
||||||
<dd class="tag-source"><ul class="dummy"><li>
|
<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#line554">line 554</a>
|
<a href="entities_note_short.js.html">entities/note_short.js</a>, <a href="entities_note_short.js.html#line555">line 555</a>
|
||||||
</li></ul></dd>
|
</li></ul></dd>
|
||||||
|
|
||||||
|
|
||||||
@@ -3456,7 +3456,7 @@ This note's representation is used in note tree and is kept in Froca.</div>
|
|||||||
|
|
||||||
<dt class="tag-source">Source:</dt>
|
<dt class="tag-source">Source:</dt>
|
||||||
<dd class="tag-source"><ul class="dummy"><li>
|
<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#line376">line 376</a>
|
<a href="entities_note_short.js.html">entities/note_short.js</a>, <a href="entities_note_short.js.html#line377">line 377</a>
|
||||||
</li></ul></dd>
|
</li></ul></dd>
|
||||||
|
|
||||||
|
|
||||||
@@ -3611,7 +3611,7 @@ This note's representation is used in note tree and is kept in Froca.</div>
|
|||||||
|
|
||||||
<dt class="tag-source">Source:</dt>
|
<dt class="tag-source">Source:</dt>
|
||||||
<dd class="tag-source"><ul class="dummy"><li>
|
<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#line578">line 578</a>
|
<a href="entities_note_short.js.html">entities/note_short.js</a>, <a href="entities_note_short.js.html#line579">line 579</a>
|
||||||
</li></ul></dd>
|
</li></ul></dd>
|
||||||
|
|
||||||
|
|
||||||
@@ -3766,7 +3766,7 @@ This note's representation is used in note tree and is kept in Froca.</div>
|
|||||||
|
|
||||||
<dt class="tag-source">Source:</dt>
|
<dt class="tag-source">Source:</dt>
|
||||||
<dd class="tag-source"><ul class="dummy"><li>
|
<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#line566">line 566</a>
|
<a href="entities_note_short.js.html">entities/note_short.js</a>, <a href="entities_note_short.js.html#line567">line 567</a>
|
||||||
</li></ul></dd>
|
</li></ul></dd>
|
||||||
|
|
||||||
|
|
||||||
@@ -3933,7 +3933,7 @@ This note's representation is used in note tree and is kept in Froca.</div>
|
|||||||
|
|
||||||
<dt class="tag-source">Source:</dt>
|
<dt class="tag-source">Source:</dt>
|
||||||
<dd class="tag-source"><ul class="dummy"><li>
|
<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#line452">line 452</a>
|
<a href="entities_note_short.js.html">entities/note_short.js</a>, <a href="entities_note_short.js.html#line453">line 453</a>
|
||||||
</li></ul></dd>
|
</li></ul></dd>
|
||||||
|
|
||||||
|
|
||||||
@@ -4088,7 +4088,7 @@ This note's representation is used in note tree and is kept in Froca.</div>
|
|||||||
|
|
||||||
<dt class="tag-source">Source:</dt>
|
<dt class="tag-source">Source:</dt>
|
||||||
<dd class="tag-source"><ul class="dummy"><li>
|
<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#line590">line 590</a>
|
<a href="entities_note_short.js.html">entities/note_short.js</a>, <a href="entities_note_short.js.html#line591">line 591</a>
|
||||||
</li></ul></dd>
|
</li></ul></dd>
|
||||||
|
|
||||||
|
|
||||||
@@ -4194,7 +4194,7 @@ This note's representation is used in note tree and is kept in Froca.</div>
|
|||||||
|
|
||||||
<dt class="tag-source">Source:</dt>
|
<dt class="tag-source">Source:</dt>
|
||||||
<dd class="tag-source"><ul class="dummy"><li>
|
<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#line152">line 152</a>
|
||||||
</li></ul></dd>
|
</li></ul></dd>
|
||||||
|
|
||||||
|
|
||||||
@@ -4296,7 +4296,7 @@ This note's representation is used in note tree and is kept in Froca.</div>
|
|||||||
|
|
||||||
<dt class="tag-source">Source:</dt>
|
<dt class="tag-source">Source:</dt>
|
||||||
<dd class="tag-source"><ul class="dummy"><li>
|
<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#line136">line 136</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>
|
</li></ul></dd>
|
||||||
|
|
||||||
|
|
||||||
@@ -4398,7 +4398,7 @@ This note's representation is used in note tree and is kept in Froca.</div>
|
|||||||
|
|
||||||
<dt class="tag-source">Source:</dt>
|
<dt class="tag-source">Source:</dt>
|
||||||
<dd class="tag-source"><ul class="dummy"><li>
|
<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#line179">line 179</a>
|
<a href="entities_note_short.js.html">entities/note_short.js</a>, <a href="entities_note_short.js.html#line180">line 180</a>
|
||||||
</li></ul></dd>
|
</li></ul></dd>
|
||||||
|
|
||||||
|
|
||||||
@@ -4500,7 +4500,7 @@ This note's representation is used in note tree and is kept in Froca.</div>
|
|||||||
|
|
||||||
<dt class="tag-source">Source:</dt>
|
<dt class="tag-source">Source:</dt>
|
||||||
<dd class="tag-source"><ul class="dummy"><li>
|
<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#line184">line 184</a>
|
<a href="entities_note_short.js.html">entities/note_short.js</a>, <a href="entities_note_short.js.html#line185">line 185</a>
|
||||||
</li></ul></dd>
|
</li></ul></dd>
|
||||||
|
|
||||||
|
|
||||||
@@ -4651,7 +4651,7 @@ This note's representation is used in note tree and is kept in Froca.</div>
|
|||||||
|
|
||||||
<dt class="tag-source">Source:</dt>
|
<dt class="tag-source">Source:</dt>
|
||||||
<dd class="tag-source"><ul class="dummy"><li>
|
<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#line572">line 572</a>
|
<a href="entities_note_short.js.html">entities/note_short.js</a>, <a href="entities_note_short.js.html#line573">line 573</a>
|
||||||
</li></ul></dd>
|
</li></ul></dd>
|
||||||
|
|
||||||
|
|
||||||
@@ -4818,7 +4818,7 @@ This note's representation is used in note tree and is kept in Froca.</div>
|
|||||||
|
|
||||||
<dt class="tag-source">Source:</dt>
|
<dt class="tag-source">Source:</dt>
|
||||||
<dd class="tag-source"><ul class="dummy"><li>
|
<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#line460">line 460</a>
|
<a href="entities_note_short.js.html">entities/note_short.js</a>, <a href="entities_note_short.js.html#line461">line 461</a>
|
||||||
</li></ul></dd>
|
</li></ul></dd>
|
||||||
|
|
||||||
|
|
||||||
@@ -4973,7 +4973,7 @@ This note's representation is used in note tree and is kept in Froca.</div>
|
|||||||
|
|
||||||
<dt class="tag-source">Source:</dt>
|
<dt class="tag-source">Source:</dt>
|
||||||
<dd class="tag-source"><ul class="dummy"><li>
|
<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#line602">line 602</a>
|
<a href="entities_note_short.js.html">entities/note_short.js</a>, <a href="entities_note_short.js.html#line603">line 603</a>
|
||||||
</li></ul></dd>
|
</li></ul></dd>
|
||||||
|
|
||||||
|
|
||||||
@@ -5143,7 +5143,7 @@ This note's representation is used in note tree and is kept in Froca.</div>
|
|||||||
|
|
||||||
<dt class="tag-source">Source:</dt>
|
<dt class="tag-source">Source:</dt>
|
||||||
<dd class="tag-source"><ul class="dummy"><li>
|
<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#line612">line 612</a>
|
<a href="entities_note_short.js.html">entities/note_short.js</a>, <a href="entities_note_short.js.html#line613">line 613</a>
|
||||||
</li></ul></dd>
|
</li></ul></dd>
|
||||||
|
|
||||||
|
|
||||||
@@ -5294,7 +5294,7 @@ This note's representation is used in note tree and is kept in Froca.</div>
|
|||||||
|
|
||||||
<dt class="tag-source">Source:</dt>
|
<dt class="tag-source">Source:</dt>
|
||||||
<dd class="tag-source"><ul class="dummy"><li>
|
<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#line596">line 596</a>
|
<a href="entities_note_short.js.html">entities/note_short.js</a>, <a href="entities_note_short.js.html#line597">line 597</a>
|
||||||
</li></ul></dd>
|
</li></ul></dd>
|
||||||
|
|
||||||
|
|
||||||
@@ -5400,7 +5400,7 @@ This note's representation is used in note tree and is kept in Froca.</div>
|
|||||||
|
|
||||||
<dt class="tag-source">Source:</dt>
|
<dt class="tag-source">Source:</dt>
|
||||||
<dd class="tag-source"><ul class="dummy"><li>
|
<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#line750">line 750</a>
|
<a href="entities_note_short.js.html">entities/note_short.js</a>, <a href="entities_note_short.js.html#line751">line 751</a>
|
||||||
</li></ul></dd>
|
</li></ul></dd>
|
||||||
|
|
||||||
|
|
||||||
@@ -5513,7 +5513,7 @@ This note's representation is used in note tree and is kept in Froca.</div>
|
|||||||
|
|
||||||
<dt class="tag-source">Source:</dt>
|
<dt class="tag-source">Source:</dt>
|
||||||
<dd class="tag-source"><ul class="dummy"><li>
|
<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#line685">line 685</a>
|
<a href="entities_note_short.js.html">entities/note_short.js</a>, <a href="entities_note_short.js.html#line686">line 686</a>
|
||||||
</li></ul></dd>
|
</li></ul></dd>
|
||||||
|
|
||||||
|
|
||||||
@@ -5619,7 +5619,7 @@ This note's representation is used in note tree and is kept in Froca.</div>
|
|||||||
|
|
||||||
<dt class="tag-source">Source:</dt>
|
<dt class="tag-source">Source:</dt>
|
||||||
<dd class="tag-source"><ul class="dummy"><li>
|
<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#line695">line 695</a>
|
<a href="entities_note_short.js.html">entities/note_short.js</a>, <a href="entities_note_short.js.html#line696">line 696</a>
|
||||||
</li></ul></dd>
|
</li></ul></dd>
|
||||||
|
|
||||||
|
|
||||||
@@ -5721,7 +5721,7 @@ This note's representation is used in note tree and is kept in Froca.</div>
|
|||||||
|
|
||||||
<dt class="tag-source">Source:</dt>
|
<dt class="tag-source">Source:</dt>
|
||||||
<dd class="tag-source"><ul class="dummy"><li>
|
<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#line626">line 626</a>
|
<a href="entities_note_short.js.html">entities/note_short.js</a>, <a href="entities_note_short.js.html#line627">line 627</a>
|
||||||
</li></ul></dd>
|
</li></ul></dd>
|
||||||
|
|
||||||
|
|
||||||
@@ -5895,7 +5895,7 @@ This note's representation is used in note tree and is kept in Froca.</div>
|
|||||||
|
|
||||||
<dt class="tag-source">Source:</dt>
|
<dt class="tag-source">Source:</dt>
|
||||||
<dd class="tag-source"><ul class="dummy"><li>
|
<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#line469">line 469</a>
|
<a href="entities_note_short.js.html">entities/note_short.js</a>, <a href="entities_note_short.js.html#line470">line 470</a>
|
||||||
</li></ul></dd>
|
</li></ul></dd>
|
||||||
|
|
||||||
|
|
||||||
@@ -6001,7 +6001,7 @@ This note's representation is used in note tree and is kept in Froca.</div>
|
|||||||
|
|
||||||
<dt class="tag-source">Source:</dt>
|
<dt class="tag-source">Source:</dt>
|
||||||
<dd class="tag-source"><ul class="dummy"><li>
|
<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#line166">line 166</a>
|
<a href="entities_note_short.js.html">entities/note_short.js</a>, <a href="entities_note_short.js.html#line167">line 167</a>
|
||||||
</li></ul></dd>
|
</li></ul></dd>
|
||||||
|
|
||||||
|
|
||||||
@@ -6152,7 +6152,7 @@ This note's representation is used in note tree and is kept in Froca.</div>
|
|||||||
|
|
||||||
<dt class="tag-source">Source:</dt>
|
<dt class="tag-source">Source:</dt>
|
||||||
<dd class="tag-source"><ul class="dummy"><li>
|
<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#line536">line 536</a>
|
<a href="entities_note_short.js.html">entities/note_short.js</a>, <a href="entities_note_short.js.html#line537">line 537</a>
|
||||||
</li></ul></dd>
|
</li></ul></dd>
|
||||||
|
|
||||||
|
|
||||||
@@ -6330,7 +6330,7 @@ This note's representation is used in note tree and is kept in Froca.</div>
|
|||||||
|
|
||||||
<dt class="tag-source">Source:</dt>
|
<dt class="tag-source">Source:</dt>
|
||||||
<dd class="tag-source"><ul class="dummy"><li>
|
<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#line478">line 478</a>
|
<a href="entities_note_short.js.html">entities/note_short.js</a>, <a href="entities_note_short.js.html#line479">line 479</a>
|
||||||
</li></ul></dd>
|
</li></ul></dd>
|
||||||
|
|
||||||
|
|
||||||
@@ -6485,7 +6485,7 @@ This note's representation is used in note tree and is kept in Froca.</div>
|
|||||||
|
|
||||||
<dt class="tag-source">Source:</dt>
|
<dt class="tag-source">Source:</dt>
|
||||||
<dd class="tag-source"><ul class="dummy"><li>
|
<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#line530">line 530</a>
|
<a href="entities_note_short.js.html">entities/note_short.js</a>, <a href="entities_note_short.js.html#line531">line 531</a>
|
||||||
</li></ul></dd>
|
</li></ul></dd>
|
||||||
|
|
||||||
|
|
||||||
@@ -6640,7 +6640,7 @@ This note's representation is used in note tree and is kept in Froca.</div>
|
|||||||
|
|
||||||
<dt class="tag-source">Source:</dt>
|
<dt class="tag-source">Source:</dt>
|
||||||
<dd class="tag-source"><ul class="dummy"><li>
|
<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#line542">line 542</a>
|
<a href="entities_note_short.js.html">entities/note_short.js</a>, <a href="entities_note_short.js.html#line543">line 543</a>
|
||||||
</li></ul></dd>
|
</li></ul></dd>
|
||||||
|
|
||||||
|
|
||||||
@@ -6795,7 +6795,7 @@ This note's representation is used in note tree and is kept in Froca.</div>
|
|||||||
|
|
||||||
<dt class="tag-source">Source:</dt>
|
<dt class="tag-source">Source:</dt>
|
||||||
<dd class="tag-source"><ul class="dummy"><li>
|
<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#line548">line 548</a>
|
<a href="entities_note_short.js.html">entities/note_short.js</a>, <a href="entities_note_short.js.html#line549">line 549</a>
|
||||||
</li></ul></dd>
|
</li></ul></dd>
|
||||||
|
|
||||||
|
|
||||||
@@ -6903,7 +6903,7 @@ This note's representation is used in note tree and is kept in Froca.</div>
|
|||||||
|
|
||||||
<dt class="tag-source">Source:</dt>
|
<dt class="tag-source">Source:</dt>
|
||||||
<dd class="tag-source"><ul class="dummy"><li>
|
<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#line678">line 678</a>
|
<a href="entities_note_short.js.html">entities/note_short.js</a>, <a href="entities_note_short.js.html#line679">line 679</a>
|
||||||
</li></ul></dd>
|
</li></ul></dd>
|
||||||
|
|
||||||
|
|
||||||
@@ -6987,7 +6987,7 @@ This note's representation is used in note tree and is kept in Froca.</div>
|
|||||||
|
|
||||||
<dt class="tag-source">Source:</dt>
|
<dt class="tag-source">Source:</dt>
|
||||||
<dd class="tag-source"><ul class="dummy"><li>
|
<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#line745">line 745</a>
|
<a href="entities_note_short.js.html">entities/note_short.js</a>, <a href="entities_note_short.js.html#line746">line 746</a>
|
||||||
</li></ul></dd>
|
</li></ul></dd>
|
||||||
|
|
||||||
|
|
||||||
@@ -7093,7 +7093,7 @@ This note's representation is used in note tree and is kept in Froca.</div>
|
|||||||
|
|
||||||
<dt class="tag-source">Source:</dt>
|
<dt class="tag-source">Source:</dt>
|
||||||
<dd class="tag-source"><ul class="dummy"><li>
|
<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#line737">line 737</a>
|
<a href="entities_note_short.js.html">entities/note_short.js</a>, <a href="entities_note_short.js.html#line738">line 738</a>
|
||||||
</li></ul></dd>
|
</li></ul></dd>
|
||||||
|
|
||||||
|
|
||||||
@@ -7199,7 +7199,7 @@ This note's representation is used in note tree and is kept in Froca.</div>
|
|||||||
|
|
||||||
<dt class="tag-source">Source:</dt>
|
<dt class="tag-source">Source:</dt>
|
||||||
<dd class="tag-source"><ul class="dummy"><li>
|
<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#line110">line 110</a>
|
||||||
</li></ul></dd>
|
</li></ul></dd>
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -44,7 +44,8 @@ const NOTE_TYPE_ICONS = {
|
|||||||
"relation-map": "bx bx-map-alt",
|
"relation-map": "bx bx-map-alt",
|
||||||
"book": "bx bx-book",
|
"book": "bx bx-book",
|
||||||
"note-map": "bx bx-map-alt",
|
"note-map": "bx bx-map-alt",
|
||||||
"mermaid": "bx bx-selection"
|
"mermaid": "bx bx-selection",
|
||||||
|
"canvas": "bx bx-pen"
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -152,7 +153,7 @@ class NoteShort {
|
|||||||
return JSON.parse(content);
|
return JSON.parse(content);
|
||||||
}
|
}
|
||||||
catch (e) {
|
catch (e) {
|
||||||
console.log(`Cannot parse content of note ${this.noteId}: `, e.message);
|
console.log(`Cannot parse content of note '${this.noteId}': `, e.message);
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -395,7 +395,7 @@
|
|||||||
|
|
||||||
<dt class="tag-source">Source:</dt>
|
<dt class="tag-source">Source:</dt>
|
||||||
<dd class="tag-source"><ul class="dummy"><li>
|
<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#line104">line 104</a>
|
<a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line124">line 124</a>
|
||||||
</li></ul></dd>
|
</li></ul></dd>
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -129,6 +129,26 @@ function FrontendScriptApi(startNote, currentNote, originEntity = null, $contain
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Open a note in a new split.
|
||||||
|
*
|
||||||
|
* @param {string} notePath (or noteId)
|
||||||
|
* @param {boolean} activate - set to true to activate the new split, false to stay on the current split
|
||||||
|
* @return {Promise<void>}
|
||||||
|
*/
|
||||||
|
this.openSplitWithNote = async (notePath, activate) => {
|
||||||
|
await ws.waitForMaxKnownEntityChangeId();
|
||||||
|
|
||||||
|
const subContexts = appContext.tabManager.getActiveContext().getSubContexts();
|
||||||
|
const {ntxId} = subContexts[subContexts.length - 1];
|
||||||
|
|
||||||
|
appContext.triggerCommand("openNewNoteSplit", {ntxId, notePath});
|
||||||
|
|
||||||
|
if (activate) {
|
||||||
|
appContext.triggerEvent('focusAndSelectTitle');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @typedef {Object} ToolbarButtonOptions
|
* @typedef {Object} ToolbarButtonOptions
|
||||||
* @property {string} title
|
* @property {string} title
|
||||||
@@ -327,6 +347,24 @@ function FrontendScriptApi(startNote, currentNote, originEntity = null, $contain
|
|||||||
*/
|
*/
|
||||||
this.showError = toastService.showError;
|
this.showError = toastService.showError;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Trigger command.
|
||||||
|
*
|
||||||
|
* @method
|
||||||
|
* @param {string} name
|
||||||
|
* @param {object} data
|
||||||
|
*/
|
||||||
|
this.triggerCommand = (name, data) => appContext.triggerCommand(name, data);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Trigger event.
|
||||||
|
*
|
||||||
|
* @method
|
||||||
|
* @param {string} name
|
||||||
|
* @param {object} data
|
||||||
|
*/
|
||||||
|
this.triggerEvent = (name, data) => appContext.triggerEvent(name, data);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @method
|
* @method
|
||||||
* @deprecated - this is now no-op since all the changes should be gracefully handled per widget
|
* @deprecated - this is now no-op since all the changes should be gracefully handled per widget
|
||||||
@@ -349,30 +387,104 @@ function FrontendScriptApi(startNote, currentNote, originEntity = null, $contain
|
|||||||
/**
|
/**
|
||||||
* Adds given text to the editor cursor
|
* Adds given text to the editor cursor
|
||||||
*
|
*
|
||||||
|
* @deprecated use addTextToActiveContextEditor() instead
|
||||||
* @param {string} text - this must be clear text, HTML is not supported.
|
* @param {string} text - this must be clear text, HTML is not supported.
|
||||||
* @method
|
* @method
|
||||||
*/
|
*/
|
||||||
this.addTextToActiveTabEditor = text => appContext.triggerCommand('addTextToActiveEditor', {text});
|
this.addTextToActiveTabEditor = text => {
|
||||||
|
console.warn("api.addTextToActiveTabEditor() is deprecated, use addTextToActiveContextEditor() instead.");
|
||||||
|
|
||||||
|
return appContext.triggerCommand('addTextToActiveEditor', {text});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds given text to the editor cursor
|
||||||
|
*
|
||||||
|
* @param {string} text - this must be clear text, HTML is not supported.
|
||||||
|
* @method
|
||||||
|
*/
|
||||||
|
this.addTextToActiveContextEditor = text => appContext.triggerCommand('addTextToActiveEditor', {text});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @method
|
||||||
|
* @deprecated use getActiveContextNote() instead
|
||||||
|
* @returns {NoteShort} active note (loaded into right pane)
|
||||||
|
*/
|
||||||
|
this.getActiveTabNote = () => {
|
||||||
|
console.warn("api.getActiveTabNote() is deprecated, use getActiveContextNote() instead.");
|
||||||
|
|
||||||
|
return appContext.tabManager.getActiveContextNote();
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @method
|
* @method
|
||||||
* @returns {NoteShort} active note (loaded into right pane)
|
* @returns {NoteShort} active note (loaded into right pane)
|
||||||
*/
|
*/
|
||||||
this.getActiveTabNote = () => appContext.tabManager.getActiveContextNote();
|
this.getActiveContextNote = () => appContext.tabManager.getActiveContextNote();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* See https://ckeditor.com/docs/ckeditor5/latest/api/module_core_editor_editor-Editor.html for a documentation on the returned instance.
|
||||||
|
*
|
||||||
|
* @deprecated use getActiveContextTextEditor()
|
||||||
|
* @method
|
||||||
|
* @param [callback] - callback receiving "textEditor" instance
|
||||||
|
*/
|
||||||
|
this.getActiveTabTextEditor = callback => {
|
||||||
|
console.warn("api.getActiveTabTextEditor() is deprecated, use getActiveContextTextEditor() instead.");
|
||||||
|
|
||||||
|
return appContext.tabManager.getActiveContext()?.getTextEditor(callback);
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* See https://ckeditor.com/docs/ckeditor5/latest/api/module_core_editor_editor-Editor.html for a documentation on the returned instance.
|
* See https://ckeditor.com/docs/ckeditor5/latest/api/module_core_editor_editor-Editor.html for a documentation on the returned instance.
|
||||||
*
|
*
|
||||||
* @method
|
* @method
|
||||||
* @param callback - method receiving "textEditor" instance
|
* @returns {Promise<CKEditor>} instance of CKEditor
|
||||||
*/
|
*/
|
||||||
this.getActiveTabTextEditor = callback => appContext.triggerCommand('executeInActiveEditor', {callback});
|
this.getActiveContextTextEditor = () => appContext.tabManager.getActiveContext()?.getTextEditor();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* See https://codemirror.net/doc/manual.html#api
|
||||||
|
*
|
||||||
|
* @method
|
||||||
|
* @returns {Promise<CodeMirror>} instance of CodeMirror
|
||||||
|
*/
|
||||||
|
this.getActiveContextCodeEditor = () => appContext.tabManager.getActiveContext()?.getCodeEditor();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get access to the widget handling note detail. Methods like `getWidgetType()` and `getTypeWidget()` to get to the
|
||||||
|
* implementation of actual widget type.
|
||||||
|
*
|
||||||
|
* @method
|
||||||
|
* @returns {Promise<NoteDetailWidget>}
|
||||||
|
*/
|
||||||
|
this.getActiveNoteDetailWidget = () => new Promise(resolve => appContext.triggerCommand('executeInActiveNoteDetailWidget', {callback: resolve}));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @method
|
||||||
|
* @deprecated use getActiveContextNotePath() instead
|
||||||
|
* @returns {Promise<string|null>} returns note path of active note or null if there isn't active note
|
||||||
|
*/
|
||||||
|
this.getActiveTabNotePath = () => {
|
||||||
|
console.warn("api.getActiveTabNotePath() is deprecated, use getActiveContextNotePath() instead.");
|
||||||
|
|
||||||
|
return appContext.tabManager.getActiveContextNotePath();
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @method
|
* @method
|
||||||
* @returns {Promise<string|null>} returns note path of active note or null if there isn't active note
|
* @returns {Promise<string|null>} returns note path of active note or null if there isn't active note
|
||||||
*/
|
*/
|
||||||
this.getActiveTabNotePath = () => appContext.tabManager.getActiveContextNotePath();
|
this.getActiveContextNotePath = () => appContext.tabManager.getActiveContextNotePath();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns component which owns given DOM element (the nearest parent component in DOM tree)
|
||||||
|
*
|
||||||
|
* @method
|
||||||
|
* @param {Element} el - DOM element
|
||||||
|
* @returns {Component}
|
||||||
|
*/
|
||||||
|
this.getComponentByEl = el => appContext.getComponentByEl(el);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @method
|
* @method
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ It is meant as a last resort solution when the standard mean to access your data
|
|||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
This tool requires node.js, testing has been done on 16.14.2, but it will probably work on other versions as well.
|
This tool requires node.js, testing has been done on 16.15.0, but it will probably work on other versions as well.
|
||||||
|
|
||||||
```
|
```
|
||||||
npm install
|
npm install
|
||||||
|
|||||||
2
libraries/ckeditor/ckeditor.js
vendored
2
libraries/ckeditor/ckeditor.js
vendored
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
7
libraries/jquery.mark.es6.min.js
vendored
Normal file
7
libraries/jquery.mark.es6.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
2518
package-lock.json
generated
2518
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
35
package.json
35
package.json
@@ -2,7 +2,7 @@
|
|||||||
"name": "trilium",
|
"name": "trilium",
|
||||||
"productName": "Trilium Notes",
|
"productName": "Trilium Notes",
|
||||||
"description": "Trilium Notes",
|
"description": "Trilium Notes",
|
||||||
"version": "0.51.2",
|
"version": "0.52.3",
|
||||||
"license": "AGPL-3.0-only",
|
"license": "AGPL-3.0-only",
|
||||||
"main": "electron.js",
|
"main": "electron.js",
|
||||||
"bin": {
|
"bin": {
|
||||||
@@ -21,31 +21,32 @@
|
|||||||
"webpack": "npx webpack -c webpack-desktop.config.js && npx webpack -c webpack-mobile.config.js && npx webpack -c webpack-setup.config.js",
|
"webpack": "npx webpack -c webpack-desktop.config.js && npx webpack -c webpack-mobile.config.js && npx webpack -c webpack-setup.config.js",
|
||||||
"test": "jasmine",
|
"test": "jasmine",
|
||||||
"test-es6": "node -r esm spec-es6/attribute_parser.spec.js ",
|
"test-es6": "node -r esm spec-es6/attribute_parser.spec.js ",
|
||||||
"test-all": "npm run test && npm run test-es6"
|
"test-all": "npm run test && npm run test-es6",
|
||||||
|
"postinstall": "rimraf ./node_modules/canvas"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@electron/remote": "2.0.8",
|
||||||
|
"@excalidraw/excalidraw": "0.11.0",
|
||||||
"archiver": "5.3.1",
|
"archiver": "5.3.1",
|
||||||
"async-mutex": "0.3.2",
|
"async-mutex": "0.3.2",
|
||||||
"axios": "0.26.1",
|
"axios": "0.27.2",
|
||||||
"better-sqlite3": "7.4.5",
|
"better-sqlite3": "7.4.5",
|
||||||
"chokidar": "3.5.3",
|
"chokidar": "3.5.3",
|
||||||
"cls-hooked": "4.2.2",
|
"cls-hooked": "4.2.2",
|
||||||
"commonmark": "0.30.0",
|
"commonmark": "0.30.0",
|
||||||
"cookie-parser": "1.4.6",
|
"cookie-parser": "1.4.6",
|
||||||
"csurf": "1.11.0",
|
"csurf": "1.11.0",
|
||||||
"dayjs": "1.11.1",
|
"dayjs": "1.11.3",
|
||||||
"ejs": "3.1.6",
|
"ejs": "3.1.8",
|
||||||
"electron-debug": "3.2.0",
|
"electron-debug": "3.2.0",
|
||||||
"electron-dl": "3.3.1",
|
"electron-dl": "3.3.1",
|
||||||
"electron-find": "1.0.7",
|
|
||||||
"electron-window-state": "5.0.3",
|
"electron-window-state": "5.0.3",
|
||||||
"@electron/remote": "2.0.8",
|
"express": "4.18.1",
|
||||||
"express": "4.17.3",
|
|
||||||
"express-partial-content": "1.0.2",
|
"express-partial-content": "1.0.2",
|
||||||
"express-rate-limit": "6.3.0",
|
"express-rate-limit": "6.4.0",
|
||||||
"express-session": "1.17.2",
|
"express-session": "1.17.3",
|
||||||
"fs-extra": "10.1.0",
|
"fs-extra": "10.1.0",
|
||||||
"helmet": "5.0.2",
|
"helmet": "5.1.0",
|
||||||
"html": "1.0.0",
|
"html": "1.0.0",
|
||||||
"html2plaintext": "2.1.4",
|
"html2plaintext": "2.1.4",
|
||||||
"http-proxy-agent": "5.0.0",
|
"http-proxy-agent": "5.0.0",
|
||||||
@@ -59,11 +60,13 @@
|
|||||||
"jsdom": "19.0.0",
|
"jsdom": "19.0.0",
|
||||||
"mime-types": "2.1.35",
|
"mime-types": "2.1.35",
|
||||||
"multer": "1.4.4",
|
"multer": "1.4.4",
|
||||||
"node-abi": "3.15.0",
|
"node-abi": "3.21.0",
|
||||||
"normalize-strings": "1.1.1",
|
"normalize-strings": "1.1.1",
|
||||||
"open": "8.4.0",
|
"open": "8.4.0",
|
||||||
"portscanner": "2.2.0",
|
"portscanner": "2.2.0",
|
||||||
"rand-token": "1.0.1",
|
"rand-token": "1.0.1",
|
||||||
|
"react": "17.0.2",
|
||||||
|
"react-dom": "17.0.2",
|
||||||
"request": "2.88.2",
|
"request": "2.88.2",
|
||||||
"rimraf": "3.0.2",
|
"rimraf": "3.0.2",
|
||||||
"sanitize-filename": "1.6.3",
|
"sanitize-filename": "1.6.3",
|
||||||
@@ -77,21 +80,21 @@
|
|||||||
"tmp": "0.2.1",
|
"tmp": "0.2.1",
|
||||||
"turndown": "7.1.1",
|
"turndown": "7.1.1",
|
||||||
"unescape": "1.0.1",
|
"unescape": "1.0.1",
|
||||||
"ws": "8.5.0",
|
"ws": "8.7.0",
|
||||||
"yauzl": "2.10.0"
|
"yauzl": "2.10.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"cross-env": "7.0.3",
|
"cross-env": "7.0.3",
|
||||||
"electron": "16.2.1",
|
"electron": "16.2.8",
|
||||||
"electron-builder": "23.0.3",
|
"electron-builder": "23.0.3",
|
||||||
"electron-packager": "15.5.0",
|
"electron-packager": "15.5.1",
|
||||||
"electron-rebuild": "3.2.7",
|
"electron-rebuild": "3.2.7",
|
||||||
"esm": "3.2.25",
|
"esm": "3.2.25",
|
||||||
"jasmine": "4.1.0",
|
"jasmine": "4.1.0",
|
||||||
"jsdoc": "3.6.10",
|
"jsdoc": "3.6.10",
|
||||||
"lorem-ipsum": "2.0.4",
|
"lorem-ipsum": "2.0.4",
|
||||||
"rcedit": "3.0.1",
|
"rcedit": "3.0.1",
|
||||||
"webpack": "5.72.0",
|
"webpack": "5.73.0",
|
||||||
"webpack-cli": "4.9.2"
|
"webpack-cli": "4.9.2"
|
||||||
},
|
},
|
||||||
"optionalDependencies": {
|
"optionalDependencies": {
|
||||||
|
|||||||
@@ -58,11 +58,8 @@ describe("Parser", () => {
|
|||||||
expect(subs[0].constructor.name).toEqual("NoteFlatTextExp");
|
expect(subs[0].constructor.name).toEqual("NoteFlatTextExp");
|
||||||
expect(subs[0].tokens).toEqual(["hello", "hi"]);
|
expect(subs[0].tokens).toEqual(["hello", "hi"]);
|
||||||
|
|
||||||
expect(subs[1].constructor.name).toEqual("NoteContentProtectedFulltextExp");
|
expect(subs[1].constructor.name).toEqual("NoteContentFulltextExp");
|
||||||
expect(subs[1].tokens).toEqual(["hello", "hi"]);
|
expect(subs[1].tokens).toEqual(["hello", "hi"]);
|
||||||
|
|
||||||
expect(subs[2].constructor.name).toEqual("NoteContentUnprotectedFulltextExp");
|
|
||||||
expect(subs[2].tokens).toEqual(["hello", "hi"]);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it("simple label comparison", () => {
|
it("simple label comparison", () => {
|
||||||
|
|||||||
@@ -20,7 +20,8 @@ app.set('view engine', 'ejs');
|
|||||||
|
|
||||||
app.use(helmet({
|
app.use(helmet({
|
||||||
hidePoweredBy: false, // errors out in electron
|
hidePoweredBy: false, // errors out in electron
|
||||||
contentSecurityPolicy: false
|
contentSecurityPolicy: false,
|
||||||
|
crossOriginEmbedderPolicy: false
|
||||||
}));
|
}));
|
||||||
|
|
||||||
app.use(express.text({limit: '500mb'}));
|
app.use(express.text({limit: '500mb'}));
|
||||||
@@ -30,6 +31,11 @@ app.use(express.urlencoded({extended: false}));
|
|||||||
app.use(cookieParser());
|
app.use(cookieParser());
|
||||||
app.use(express.static(path.join(__dirname, 'public')));
|
app.use(express.static(path.join(__dirname, 'public')));
|
||||||
app.use('/libraries', express.static(path.join(__dirname, '..', 'libraries')));
|
app.use('/libraries', express.static(path.join(__dirname, '..', 'libraries')));
|
||||||
|
// excalidraw-view mode in shared notes
|
||||||
|
app.use('/node_modules/react/umd/react.production.min.js', express.static(path.join(__dirname, '..', 'node_modules/react/umd/react.production.min.js')));
|
||||||
|
app.use('/node_modules/react-dom/umd/react-dom.production.min.js', express.static(path.join(__dirname, '..', 'node_modules/react-dom/umd/react-dom.production.min.js')));
|
||||||
|
// expose whole dist folder since complete assets are needed in edit and share
|
||||||
|
app.use('/node_modules/@excalidraw/excalidraw/dist/', express.static(path.join(__dirname, '..', 'node_modules/@excalidraw/excalidraw/dist/')));
|
||||||
app.use('/images', express.static(path.join(__dirname, '..', 'images')));
|
app.use('/images', express.static(path.join(__dirname, '..', 'images')));
|
||||||
const sessionParser = session({
|
const sessionParser = session({
|
||||||
secret: sessionSecret,
|
secret: sessionSecret,
|
||||||
|
|||||||
@@ -5,9 +5,9 @@ const AbstractEntity = require("./abstract_entity");
|
|||||||
const sql = require("../../services/sql");
|
const sql = require("../../services/sql");
|
||||||
const dateUtils = require("../../services/date_utils");
|
const dateUtils = require("../../services/date_utils");
|
||||||
const utils = require("../../services/utils.js");
|
const utils = require("../../services/utils.js");
|
||||||
const TaskContext = require("../../services/task_context.js");
|
const TaskContext = require("../../services/task_context");
|
||||||
const cls = require("../../services/cls.js");
|
const cls = require("../../services/cls");
|
||||||
const log = require("../../services/log.js");
|
const log = require("../../services/log");
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Branch represents a relationship between a child note and its parent note. Trilium allows a note to have multiple
|
* Branch represents a relationship between a child note and its parent note. Trilium allows a note to have multiple
|
||||||
@@ -137,6 +137,18 @@ class Branch extends AbstractEntity {
|
|||||||
|
|
||||||
taskContext.increaseProgressCount();
|
taskContext.increaseProgressCount();
|
||||||
|
|
||||||
|
const note = this.getNote();
|
||||||
|
|
||||||
|
if (!taskContext.noteDeletionHandlerTriggered) {
|
||||||
|
const parentBranches = note.getParentBranches();
|
||||||
|
|
||||||
|
if (parentBranches.length === 1 && parentBranches[0] === this) {
|
||||||
|
// needs to be run before branches and attributes are deleted and thus attached relations disappear
|
||||||
|
const handlers = require("../../services/handlers");
|
||||||
|
handlers.runAttachedRelations(note, 'runOnNoteDeletion', note);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (this.branchId === 'root'
|
if (this.branchId === 'root'
|
||||||
|| this.noteId === 'root'
|
|| this.noteId === 'root'
|
||||||
|| this.noteId === cls.getHoistedNoteId()) {
|
|| this.noteId === cls.getHoistedNoteId()) {
|
||||||
@@ -146,7 +158,6 @@ class Branch extends AbstractEntity {
|
|||||||
|
|
||||||
this.markAsDeleted(deleteId);
|
this.markAsDeleted(deleteId);
|
||||||
|
|
||||||
const note = this.getNote();
|
|
||||||
const notDeletedBranches = note.getParentBranches();
|
const notDeletedBranches = note.getParentBranches();
|
||||||
|
|
||||||
if (notDeletedBranches.length === 0) {
|
if (notDeletedBranches.length === 0) {
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ const dateUtils = require('../../services/date_utils');
|
|||||||
const entityChangesService = require('../../services/entity_changes');
|
const entityChangesService = require('../../services/entity_changes');
|
||||||
const AbstractEntity = require("./abstract_entity");
|
const AbstractEntity = require("./abstract_entity");
|
||||||
const NoteRevision = require("./note_revision");
|
const NoteRevision = require("./note_revision");
|
||||||
const TaskContext = require("../../services/task_context.js");
|
const TaskContext = require("../../services/task_context");
|
||||||
|
|
||||||
const LABEL = 'label';
|
const LABEL = 'label';
|
||||||
const RELATION = 'relation';
|
const RELATION = 'relation';
|
||||||
@@ -1133,6 +1133,10 @@ class Note extends AbstractEntity {
|
|||||||
* @param {TaskContext} [taskContext]
|
* @param {TaskContext} [taskContext]
|
||||||
*/
|
*/
|
||||||
deleteNote(deleteId, taskContext) {
|
deleteNote(deleteId, taskContext) {
|
||||||
|
if (this.isDeleted) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (!deleteId) {
|
if (!deleteId) {
|
||||||
deleteId = utils.randomString(10);
|
deleteId = utils.randomString(10);
|
||||||
}
|
}
|
||||||
@@ -1141,6 +1145,11 @@ class Note extends AbstractEntity {
|
|||||||
taskContext = new TaskContext('no-progress-reporting');
|
taskContext = new TaskContext('no-progress-reporting');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// needs to be run before branches and attributes are deleted and thus attached relations disappear
|
||||||
|
const handlers = require("../../services/handlers");
|
||||||
|
handlers.runAttachedRelations(this, 'runOnNoteDeletion', this);
|
||||||
|
taskContext.noteDeletionHandlerTriggered = true;
|
||||||
|
|
||||||
for (const branch of this.getParentBranches()) {
|
for (const branch of this.getParentBranches()) {
|
||||||
branch.deleteBranch(deleteId, taskContext);
|
branch.deleteBranch(deleteId, taskContext);
|
||||||
}
|
}
|
||||||
@@ -1164,6 +1173,41 @@ class Note extends AbstractEntity {
|
|||||||
return !(this.noteId in this.becca.notes);
|
return !(this.noteId in this.becca.notes);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return {NoteRevision|null}
|
||||||
|
*/
|
||||||
|
saveNoteRevision() {
|
||||||
|
const content = this.getContent();
|
||||||
|
|
||||||
|
if (!content || (Buffer.isBuffer(content) && content.byteLength === 0)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const contentMetadata = this.getContentMetadata();
|
||||||
|
|
||||||
|
const noteRevision = new NoteRevision({
|
||||||
|
noteId: this.noteId,
|
||||||
|
// title and text should be decrypted now
|
||||||
|
title: this.title,
|
||||||
|
type: this.type,
|
||||||
|
mime: this.mime,
|
||||||
|
isProtected: false, // will be fixed in the protectNoteRevisions() call
|
||||||
|
utcDateLastEdited: this.utcDateModified > contentMetadata.utcDateModified
|
||||||
|
? this.utcDateModified
|
||||||
|
: contentMetadata.utcDateModified,
|
||||||
|
utcDateCreated: dateUtils.utcNowDateTime(),
|
||||||
|
utcDateModified: dateUtils.utcNowDateTime(),
|
||||||
|
dateLastEdited: this.dateModified > contentMetadata.dateModified
|
||||||
|
? this.dateModified
|
||||||
|
: contentMetadata.dateModified,
|
||||||
|
dateCreated: dateUtils.localNowDateTime()
|
||||||
|
}).save();
|
||||||
|
|
||||||
|
noteRevision.setContent(content);
|
||||||
|
|
||||||
|
return noteRevision;
|
||||||
|
}
|
||||||
|
|
||||||
beforeSaving() {
|
beforeSaving() {
|
||||||
super.beforeSaving();
|
super.beforeSaving();
|
||||||
|
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ async function convertMarkdownToHtml(text) {
|
|||||||
|
|
||||||
const result = writer.render(parsed);
|
const result = writer.render(parsed);
|
||||||
|
|
||||||
appContext.triggerCommand('executeInActiveEditor', {
|
appContext.triggerCommand('executeWithTextEditor', {
|
||||||
callback: textEditor => {
|
callback: textEditor => {
|
||||||
const viewFragment = textEditor.data.processor.toView(result);
|
const viewFragment = textEditor.data.processor.toView(result);
|
||||||
const modelFragment = textEditor.data.toModel(viewFragment);
|
const modelFragment = textEditor.data.toModel(viewFragment);
|
||||||
@@ -24,7 +24,8 @@ async function convertMarkdownToHtml(text) {
|
|||||||
textEditor.model.insertContent(modelFragment, textEditor.model.document.selection);
|
textEditor.model.insertContent(modelFragment, textEditor.model.document.selection);
|
||||||
|
|
||||||
toastService.showMessage("Markdown content has been imported into the document.");
|
toastService.showMessage("Markdown content has been imported into the document.");
|
||||||
}
|
},
|
||||||
|
ntxId: this.ntxId
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -171,6 +171,28 @@ async function setContentPane() {
|
|||||||
|
|
||||||
$content.html($table);
|
$content.html($table);
|
||||||
}
|
}
|
||||||
|
else if (revisionItem.type === 'canvas') {
|
||||||
|
/**
|
||||||
|
* FIXME: We load a font called Virgil.wof2, which originates from excalidraw.com
|
||||||
|
* REMOVE external dependency!!!! This is defined in the svg in defs.style
|
||||||
|
*/
|
||||||
|
const content = fullNoteRevision.content;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const data = JSON.parse(content)
|
||||||
|
const svg = data.svg || "no svg present."
|
||||||
|
|
||||||
|
/**
|
||||||
|
* maxWidth: 100% use full width of container but do not enlarge!
|
||||||
|
* height:auto to ensure that height scales with width
|
||||||
|
*/
|
||||||
|
const $svgHtml = $(svg).css({maxWidth: "100%", height: "auto"});
|
||||||
|
$content.html($('<div>').append($svgHtml));
|
||||||
|
} catch(err) {
|
||||||
|
console.error("error parsing fullNoteRevision.content as JSON", fullNoteRevision.content, err);
|
||||||
|
$content.html($("<div>").text("Error parsing content. Please check console.error() for more details."));
|
||||||
|
}
|
||||||
|
}
|
||||||
else {
|
else {
|
||||||
$content.text("Preview isn't available for this note type.");
|
$content.text("Preview isn't available for this note type.");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,7 +33,13 @@ const TPL = `
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<h4>Image compression</h4>
|
<h4>Images</h4>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<input id="download-images-automatically" type="checkbox" name="download-images-automatically">
|
||||||
|
<label for="download-images-automatically">Download images automatically for offline use.</label>
|
||||||
|
<p>(pasted HTML can contain references to online images, Trilium will find those references and download the images so that they are available offline)</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<input id="image-compresion-enabled" type="checkbox" name="image-compression-enabled">
|
<input id="image-compresion-enabled" type="checkbox" name="image-compression-enabled">
|
||||||
@@ -216,6 +222,15 @@ export default class ProtectedSessionOptions {
|
|||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.$downloadImagesAutomatically = $("#download-images-automatically");
|
||||||
|
|
||||||
|
this.$downloadImagesAutomatically.on("change", () => {
|
||||||
|
const isChecked = this.$downloadImagesAutomatically.prop("checked");
|
||||||
|
const opts = { 'downloadImagesAutomatically': isChecked ? 'true' : 'false' };
|
||||||
|
|
||||||
|
server.put('options', opts).then(() => toastService.showMessage("Options changed have been saved."));
|
||||||
|
});
|
||||||
|
|
||||||
this.$enableImageCompression = $("#image-compresion-enabled");
|
this.$enableImageCompression = $("#image-compresion-enabled");
|
||||||
this.$imageCompressionWrapper = $("#image-compression-enabled-wraper");
|
this.$imageCompressionWrapper = $("#image-compression-enabled-wraper");
|
||||||
|
|
||||||
@@ -225,7 +240,7 @@ export default class ProtectedSessionOptions {
|
|||||||
} else {
|
} else {
|
||||||
this.$imageCompressionWrapper.addClass("disabled-field");
|
this.$imageCompressionWrapper.addClass("disabled-field");
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
this.$enableImageCompression.on("change", () => {
|
this.$enableImageCompression.on("change", () => {
|
||||||
const isChecked = this.$enableImageCompression.prop("checked");
|
const isChecked = this.$enableImageCompression.prop("checked");
|
||||||
@@ -234,7 +249,7 @@ export default class ProtectedSessionOptions {
|
|||||||
server.put('options', opts).then(() => toastService.showMessage("Options changed have been saved."));
|
server.put('options', opts).then(() => toastService.showMessage("Options changed have been saved."));
|
||||||
|
|
||||||
this.setImageCompression(isChecked);
|
this.setImageCompression(isChecked);
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
optionsLoaded(options) {
|
optionsLoaded(options) {
|
||||||
@@ -251,6 +266,9 @@ export default class ProtectedSessionOptions {
|
|||||||
this.$autoReadonlySizeText.val(options['autoReadonlySizeText']);
|
this.$autoReadonlySizeText.val(options['autoReadonlySizeText']);
|
||||||
this.$autoReadonlySizeCode.val(options['autoReadonlySizeCode']);
|
this.$autoReadonlySizeCode.val(options['autoReadonlySizeCode']);
|
||||||
|
|
||||||
|
const downloadImagesAutomatically = options['downloadImagesAutomatically'] === 'true';
|
||||||
|
this.$downloadImagesAutomatically.prop('checked', downloadImagesAutomatically);
|
||||||
|
|
||||||
const compressImages = options['compressImages'] === 'true';
|
const compressImages = options['compressImages'] === 'true';
|
||||||
this.$enableImageCompression.prop('checked', compressImages);
|
this.$enableImageCompression.prop('checked', compressImages);
|
||||||
this.setImageCompression(compressImages);
|
this.setImageCompression(compressImages);
|
||||||
|
|||||||
@@ -16,7 +16,8 @@ const NOTE_TYPE_ICONS = {
|
|||||||
"relation-map": "bx bx-map-alt",
|
"relation-map": "bx bx-map-alt",
|
||||||
"book": "bx bx-book",
|
"book": "bx bx-book",
|
||||||
"note-map": "bx bx-map-alt",
|
"note-map": "bx bx-map-alt",
|
||||||
"mermaid": "bx bx-selection"
|
"mermaid": "bx bx-selection",
|
||||||
|
"canvas": "bx bx-pen"
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -48,6 +48,7 @@ import BookmarkButtons from "../widgets/bookmark_buttons.js";
|
|||||||
import NoteWrapperWidget from "../widgets/note_wrapper.js";
|
import NoteWrapperWidget from "../widgets/note_wrapper.js";
|
||||||
import BacklinksWidget from "../widgets/backlinks.js";
|
import BacklinksWidget from "../widgets/backlinks.js";
|
||||||
import SharedInfoWidget from "../widgets/shared_info.js";
|
import SharedInfoWidget from "../widgets/shared_info.js";
|
||||||
|
import FindWidget from "../widgets/find.js";
|
||||||
|
|
||||||
export default class DesktopLayout {
|
export default class DesktopLayout {
|
||||||
constructor(customWidgets) {
|
constructor(customWidgets) {
|
||||||
@@ -161,6 +162,7 @@ export default class DesktopLayout {
|
|||||||
.child(new SearchResultWidget())
|
.child(new SearchResultWidget())
|
||||||
.child(new SqlResultWidget())
|
.child(new SqlResultWidget())
|
||||||
)
|
)
|
||||||
|
.child(new FindWidget())
|
||||||
.child(...this.customWidgets.get('node-detail-pane'))
|
.child(...this.customWidgets.get('node-detail-pane'))
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ import Component from "../widgets/component.js";
|
|||||||
import keyboardActionsService from "./keyboard_actions.js";
|
import keyboardActionsService from "./keyboard_actions.js";
|
||||||
import MobileScreenSwitcherExecutor from "../widgets/mobile_widgets/mobile_screen_switcher.js";
|
import MobileScreenSwitcherExecutor from "../widgets/mobile_widgets/mobile_screen_switcher.js";
|
||||||
import MainTreeExecutors from "./main_tree_executors.js";
|
import MainTreeExecutors from "./main_tree_executors.js";
|
||||||
import protectedSessionHolder from "./protected_session_holder.js";
|
|
||||||
import toast from "./toast.js";
|
import toast from "./toast.js";
|
||||||
|
|
||||||
class AppContext extends Component {
|
class AppContext extends Component {
|
||||||
|
|||||||
70
src/public/app/services/debounce.js
Normal file
70
src/public/app/services/debounce.js
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
/**
|
||||||
|
* Returns a function, that, as long as it continues to be invoked, will not
|
||||||
|
* be triggered. The function will be called after it stops being called for
|
||||||
|
* N milliseconds. If `immediate` is passed, trigger the function on the
|
||||||
|
* leading edge, instead of the trailing. The function also has a property 'clear'
|
||||||
|
* that is a function which will clear the timer to prevent previously scheduled executions.
|
||||||
|
*
|
||||||
|
* @source underscore.js
|
||||||
|
* @see http://unscriptable.com/2009/03/20/debouncing-javascript-methods/
|
||||||
|
* @param {Function} function to wrap
|
||||||
|
* @param {Number} timeout in ms (`100`)
|
||||||
|
* @param {Boolean} whether to execute at the beginning (`false`)
|
||||||
|
* @api public
|
||||||
|
*/
|
||||||
|
function debounce(func, wait_ms, immediate){
|
||||||
|
var timeout, args, context, timestamp, result;
|
||||||
|
if (null == wait_ms) wait_ms = 100;
|
||||||
|
|
||||||
|
function later() {
|
||||||
|
var last = Date.now() - timestamp;
|
||||||
|
|
||||||
|
if (last < wait_ms && last >= 0) {
|
||||||
|
timeout = setTimeout(later, wait_ms - last);
|
||||||
|
} else {
|
||||||
|
timeout = null;
|
||||||
|
if (!immediate) {
|
||||||
|
result = func.apply(context, args);
|
||||||
|
context = args = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var debounced = function(){
|
||||||
|
context = this;
|
||||||
|
args = arguments;
|
||||||
|
timestamp = Date.now();
|
||||||
|
var callNow = immediate && !timeout;
|
||||||
|
if (!timeout) timeout = setTimeout(later, wait_ms);
|
||||||
|
if (callNow) {
|
||||||
|
result = func.apply(context, args);
|
||||||
|
context = args = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
|
||||||
|
debounced.clear = function() {
|
||||||
|
if (timeout) {
|
||||||
|
clearTimeout(timeout);
|
||||||
|
timeout = null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
debounced.flush = function() {
|
||||||
|
if (timeout) {
|
||||||
|
result = func.apply(context, args);
|
||||||
|
context = args = null;
|
||||||
|
|
||||||
|
clearTimeout(timeout);
|
||||||
|
timeout = null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return debounced;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Adds compatibility for ES modules
|
||||||
|
debounce.debounce = debounce;
|
||||||
|
|
||||||
|
export default debounce;
|
||||||
@@ -39,34 +39,10 @@ export default class Entrypoints extends Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
findInTextCommand() {
|
|
||||||
if (!utils.isElectron()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const remote = utils.dynamicRequire('@electron/remote');
|
|
||||||
const {FindInPage} = utils.dynamicRequire('electron-find');
|
|
||||||
const findInPage = new FindInPage(remote.getCurrentWebContents(), {
|
|
||||||
offsetTop: 10,
|
|
||||||
offsetRight: 10,
|
|
||||||
boxBgColor: 'var(--main-background-color)',
|
|
||||||
boxShadowColor: '#000',
|
|
||||||
inputColor: 'var(--input-text-color)',
|
|
||||||
inputBgColor: 'var(--input-background-color)',
|
|
||||||
inputFocusColor: '#555',
|
|
||||||
textColor: 'var(--main-text-color)',
|
|
||||||
textHoverBgColor: '#555',
|
|
||||||
caseSelectedColor: 'var(--main-border-color)'
|
|
||||||
});
|
|
||||||
|
|
||||||
findInPage.openFindWindow();
|
|
||||||
}
|
|
||||||
|
|
||||||
async createNoteIntoInboxCommand() {
|
async createNoteIntoInboxCommand() {
|
||||||
const inboxNote = await dateNoteService.getInboxNote();
|
const inboxNote = await dateNoteService.getInboxNote();
|
||||||
|
|
||||||
const {note} = await server.post(`notes/${inboxNote.noteId}/children?target=into`, {
|
const {note} = await server.post(`notes/${inboxNote.noteId}/children?target=into`, {
|
||||||
title: 'new note',
|
|
||||||
content: '',
|
content: '',
|
||||||
type: 'text',
|
type: 'text',
|
||||||
isProtected: inboxNote.isProtected && protectedSessionHolder.isProtectedSessionAvailable()
|
isProtected: inboxNote.isProtected && protectedSessionHolder.isProtectedSessionAvailable()
|
||||||
|
|||||||
@@ -101,6 +101,26 @@ function FrontendScriptApi(startNote, currentNote, originEntity = null, $contain
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Open a note in a new split.
|
||||||
|
*
|
||||||
|
* @param {string} notePath (or noteId)
|
||||||
|
* @param {boolean} activate - set to true to activate the new split, false to stay on the current split
|
||||||
|
* @return {Promise<void>}
|
||||||
|
*/
|
||||||
|
this.openSplitWithNote = async (notePath, activate) => {
|
||||||
|
await ws.waitForMaxKnownEntityChangeId();
|
||||||
|
|
||||||
|
const subContexts = appContext.tabManager.getActiveContext().getSubContexts();
|
||||||
|
const {ntxId} = subContexts[subContexts.length - 1];
|
||||||
|
|
||||||
|
appContext.triggerCommand("openNewNoteSplit", {ntxId, notePath});
|
||||||
|
|
||||||
|
if (activate) {
|
||||||
|
appContext.triggerEvent('focusAndSelectTitle');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @typedef {Object} ToolbarButtonOptions
|
* @typedef {Object} ToolbarButtonOptions
|
||||||
* @property {string} title
|
* @property {string} title
|
||||||
@@ -299,6 +319,24 @@ function FrontendScriptApi(startNote, currentNote, originEntity = null, $contain
|
|||||||
*/
|
*/
|
||||||
this.showError = toastService.showError;
|
this.showError = toastService.showError;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Trigger command.
|
||||||
|
*
|
||||||
|
* @method
|
||||||
|
* @param {string} name
|
||||||
|
* @param {object} data
|
||||||
|
*/
|
||||||
|
this.triggerCommand = (name, data) => appContext.triggerCommand(name, data);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Trigger event.
|
||||||
|
*
|
||||||
|
* @method
|
||||||
|
* @param {string} name
|
||||||
|
* @param {object} data
|
||||||
|
*/
|
||||||
|
this.triggerEvent = (name, data) => appContext.triggerEvent(name, data);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @method
|
* @method
|
||||||
* @deprecated - this is now no-op since all the changes should be gracefully handled per widget
|
* @deprecated - this is now no-op since all the changes should be gracefully handled per widget
|
||||||
@@ -321,30 +359,104 @@ function FrontendScriptApi(startNote, currentNote, originEntity = null, $contain
|
|||||||
/**
|
/**
|
||||||
* Adds given text to the editor cursor
|
* Adds given text to the editor cursor
|
||||||
*
|
*
|
||||||
|
* @deprecated use addTextToActiveContextEditor() instead
|
||||||
* @param {string} text - this must be clear text, HTML is not supported.
|
* @param {string} text - this must be clear text, HTML is not supported.
|
||||||
* @method
|
* @method
|
||||||
*/
|
*/
|
||||||
this.addTextToActiveTabEditor = text => appContext.triggerCommand('addTextToActiveEditor', {text});
|
this.addTextToActiveTabEditor = text => {
|
||||||
|
console.warn("api.addTextToActiveTabEditor() is deprecated, use addTextToActiveContextEditor() instead.");
|
||||||
|
|
||||||
|
return appContext.triggerCommand('addTextToActiveEditor', {text});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds given text to the editor cursor
|
||||||
|
*
|
||||||
|
* @param {string} text - this must be clear text, HTML is not supported.
|
||||||
|
* @method
|
||||||
|
*/
|
||||||
|
this.addTextToActiveContextEditor = text => appContext.triggerCommand('addTextToActiveEditor', {text});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @method
|
||||||
|
* @deprecated use getActiveContextNote() instead
|
||||||
|
* @returns {NoteShort} active note (loaded into right pane)
|
||||||
|
*/
|
||||||
|
this.getActiveTabNote = () => {
|
||||||
|
console.warn("api.getActiveTabNote() is deprecated, use getActiveContextNote() instead.");
|
||||||
|
|
||||||
|
return appContext.tabManager.getActiveContextNote();
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @method
|
* @method
|
||||||
* @returns {NoteShort} active note (loaded into right pane)
|
* @returns {NoteShort} active note (loaded into right pane)
|
||||||
*/
|
*/
|
||||||
this.getActiveTabNote = () => appContext.tabManager.getActiveContextNote();
|
this.getActiveContextNote = () => appContext.tabManager.getActiveContextNote();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* See https://ckeditor.com/docs/ckeditor5/latest/api/module_core_editor_editor-Editor.html for a documentation on the returned instance.
|
||||||
|
*
|
||||||
|
* @deprecated use getActiveContextTextEditor()
|
||||||
|
* @method
|
||||||
|
* @param [callback] - callback receiving "textEditor" instance
|
||||||
|
*/
|
||||||
|
this.getActiveTabTextEditor = callback => {
|
||||||
|
console.warn("api.getActiveTabTextEditor() is deprecated, use getActiveContextTextEditor() instead.");
|
||||||
|
|
||||||
|
return appContext.tabManager.getActiveContext()?.getTextEditor(callback);
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* See https://ckeditor.com/docs/ckeditor5/latest/api/module_core_editor_editor-Editor.html for a documentation on the returned instance.
|
* See https://ckeditor.com/docs/ckeditor5/latest/api/module_core_editor_editor-Editor.html for a documentation on the returned instance.
|
||||||
*
|
*
|
||||||
* @method
|
* @method
|
||||||
* @param callback - method receiving "textEditor" instance
|
* @returns {Promise<CKEditor>} instance of CKEditor
|
||||||
*/
|
*/
|
||||||
this.getActiveTabTextEditor = callback => appContext.triggerCommand('executeInActiveEditor', {callback});
|
this.getActiveContextTextEditor = () => appContext.tabManager.getActiveContext()?.getTextEditor();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* See https://codemirror.net/doc/manual.html#api
|
||||||
|
*
|
||||||
|
* @method
|
||||||
|
* @returns {Promise<CodeMirror>} instance of CodeMirror
|
||||||
|
*/
|
||||||
|
this.getActiveContextCodeEditor = () => appContext.tabManager.getActiveContext()?.getCodeEditor();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get access to the widget handling note detail. Methods like `getWidgetType()` and `getTypeWidget()` to get to the
|
||||||
|
* implementation of actual widget type.
|
||||||
|
*
|
||||||
|
* @method
|
||||||
|
* @returns {Promise<NoteDetailWidget>}
|
||||||
|
*/
|
||||||
|
this.getActiveNoteDetailWidget = () => new Promise(resolve => appContext.triggerCommand('executeInActiveNoteDetailWidget', {callback: resolve}));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @method
|
||||||
|
* @deprecated use getActiveContextNotePath() instead
|
||||||
|
* @returns {Promise<string|null>} returns note path of active note or null if there isn't active note
|
||||||
|
*/
|
||||||
|
this.getActiveTabNotePath = () => {
|
||||||
|
console.warn("api.getActiveTabNotePath() is deprecated, use getActiveContextNotePath() instead.");
|
||||||
|
|
||||||
|
return appContext.tabManager.getActiveContextNotePath();
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @method
|
* @method
|
||||||
* @returns {Promise<string|null>} returns note path of active note or null if there isn't active note
|
* @returns {Promise<string|null>} returns note path of active note or null if there isn't active note
|
||||||
*/
|
*/
|
||||||
this.getActiveTabNotePath = () => appContext.tabManager.getActiveContextNotePath();
|
this.getActiveContextNotePath = () => appContext.tabManager.getActiveContextNotePath();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns component which owns given DOM element (the nearest parent component in DOM tree)
|
||||||
|
*
|
||||||
|
* @method
|
||||||
|
* @param {Element} el - DOM element
|
||||||
|
* @returns {Component}
|
||||||
|
*/
|
||||||
|
this.getComponentByEl = el => appContext.getComponentByEl(el);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @method
|
* @method
|
||||||
|
|||||||
@@ -56,6 +56,20 @@ const MERMAID = {
|
|||||||
js: [ "libraries/mermaid.min.js" ]
|
js: [ "libraries/mermaid.min.js" ]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const EXCALIDRAW = {
|
||||||
|
js: [
|
||||||
|
"node_modules/react/umd/react.production.min.js",
|
||||||
|
"node_modules/react-dom/umd/react-dom.production.min.js",
|
||||||
|
"node_modules/@excalidraw/excalidraw/dist/excalidraw.production.min.js",
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
const MARKJS = {
|
||||||
|
js: [
|
||||||
|
"libraries/jquery.mark.es6.min.js"
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
async function requireLibrary(library) {
|
async function requireLibrary(library) {
|
||||||
if (library.css) {
|
if (library.css) {
|
||||||
library.css.map(cssUrl => requireCss(cssUrl));
|
library.css.map(cssUrl => requireCss(cssUrl));
|
||||||
@@ -106,5 +120,7 @@ export default {
|
|||||||
KATEX,
|
KATEX,
|
||||||
WHEEL_ZOOM,
|
WHEEL_ZOOM,
|
||||||
FORCE_GRAPH,
|
FORCE_GRAPH,
|
||||||
MERMAID
|
MERMAID,
|
||||||
|
EXCALIDRAW,
|
||||||
|
MARKJS
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -141,6 +141,27 @@ async function getRenderedContent(note, options = {}) {
|
|||||||
|
|
||||||
$renderedContent.append($content);
|
$renderedContent.append($content);
|
||||||
}
|
}
|
||||||
|
else if (type === 'canvas') {
|
||||||
|
// make sure surrounding container has size of what is visible. Then image is shrinked to its boundaries
|
||||||
|
$renderedContent.css({height: "100%", width:"100%"});
|
||||||
|
|
||||||
|
const noteComplement = await froca.getNoteComplement(note.noteId);
|
||||||
|
const content = noteComplement.content || "";
|
||||||
|
|
||||||
|
try {
|
||||||
|
const placeHolderSVG = "<svg />";
|
||||||
|
const data = JSON.parse(content)
|
||||||
|
const svg = data.svg || placeHolderSVG;
|
||||||
|
/**
|
||||||
|
* maxWidth: size down to 100% (full) width of container but do not enlarge!
|
||||||
|
* height:auto to ensure that height scales with width
|
||||||
|
*/
|
||||||
|
$renderedContent.append($(svg).css({maxWidth: "100%", maxHeight: "100%", height: "auto", width: "auto"}));
|
||||||
|
} catch(err) {
|
||||||
|
console.error("error parsing content as JSON", content, err);
|
||||||
|
$renderedContent.append($("<div>").text("Error parsing content. Please check console.error() for more details."));
|
||||||
|
}
|
||||||
|
}
|
||||||
else if (!options.tooltip && type === 'protected-session') {
|
else if (!options.tooltip && type === 'protected-session') {
|
||||||
const $button = $(`<button class="btn btn-sm"><span class="bx bx-log-in"></span> Enter protected session</button>`)
|
const $button = $(`<button class="btn btn-sm"><span class="bx bx-log-in"></span> Enter protected session</button>`)
|
||||||
.on('click', protectedSessionService.enterProtectedSession);
|
.on('click', protectedSessionService.enterProtectedSession);
|
||||||
|
|||||||
@@ -226,6 +226,35 @@ class NoteContext extends Component {
|
|||||||
&& this.note.mime !== 'text/x-sqlite;schema=trilium'
|
&& this.note.mime !== 'text/x-sqlite;schema=trilium'
|
||||||
&& !this.note.hasLabel('hideChildrenOverview');
|
&& !this.note.hasLabel('hideChildrenOverview');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async getTextEditor(callback) {
|
||||||
|
return new Promise(resolve => appContext.triggerCommand('executeWithTextEditor', {
|
||||||
|
callback,
|
||||||
|
resolve,
|
||||||
|
ntxId: this.ntxId
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
async getCodeEditor() {
|
||||||
|
return new Promise(resolve => appContext.triggerCommand('executeWithCodeEditor', {
|
||||||
|
resolve,
|
||||||
|
ntxId: this.ntxId
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
async getContentElement() {
|
||||||
|
return new Promise(resolve => appContext.triggerCommand('executeWithContentElement', {
|
||||||
|
resolve,
|
||||||
|
ntxId: this.ntxId
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
async getTypeWidget() {
|
||||||
|
return new Promise(resolve => appContext.triggerCommand('executeWithTypeWidget', {
|
||||||
|
resolve,
|
||||||
|
ntxId: this.ntxId
|
||||||
|
}));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default NoteContext;
|
export default NoteContext;
|
||||||
|
|||||||
@@ -24,12 +24,10 @@ async function createNote(parentNotePath, options = {}) {
|
|||||||
options.saveSelection = false;
|
options.saveSelection = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (options.saveSelection && utils.isCKEditorInitialized()) {
|
if (options.saveSelection) {
|
||||||
[options.title, options.content] = parseSelectedHtml(window.cutToNote.getSelectedHtml());
|
[options.title, options.content] = parseSelectedHtml(options.textEditor.getSelectedHtml());
|
||||||
}
|
}
|
||||||
|
|
||||||
const newNoteName = options.title || "new note";
|
|
||||||
|
|
||||||
const parentNoteId = treeService.getNoteIdFromNotePath(parentNotePath);
|
const parentNoteId = treeService.getNoteIdFromNotePath(parentNotePath);
|
||||||
|
|
||||||
if (options.type === 'mermaid' && !options.content) {
|
if (options.type === 'mermaid' && !options.content) {
|
||||||
@@ -41,16 +39,16 @@ async function createNote(parentNotePath, options = {}) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const {note, branch} = await server.post(`notes/${parentNoteId}/children?target=${options.target}&targetBranchId=${options.targetBranchId || ""}`, {
|
const {note, branch} = await server.post(`notes/${parentNoteId}/children?target=${options.target}&targetBranchId=${options.targetBranchId || ""}`, {
|
||||||
title: newNoteName,
|
title: options.title,
|
||||||
content: options.content || "",
|
content: options.content || "",
|
||||||
isProtected: options.isProtected,
|
isProtected: options.isProtected,
|
||||||
type: options.type,
|
type: options.type,
|
||||||
mime: options.mime
|
mime: options.mime
|
||||||
});
|
});
|
||||||
|
|
||||||
if (options.saveSelection && utils.isCKEditorInitialized()) {
|
if (options.saveSelection) {
|
||||||
// we remove the selection only after it was saved to server to make sure we don't lose anything
|
// we remove the selection only after it was saved to server to make sure we don't lose anything
|
||||||
window.cutToNote.removeSelection();
|
options.textEditor.removeSelection();
|
||||||
}
|
}
|
||||||
|
|
||||||
await ws.waitForMaxKnownEntityChangeId();
|
await ws.waitForMaxKnownEntityChangeId();
|
||||||
|
|||||||
@@ -99,13 +99,15 @@ const TPL = `
|
|||||||
padding: 10px;
|
padding: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.note-book-content.type-image img {
|
.note-book-content.type-image img, .note-book-content.type-canvas svg {
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
max-height: 100%;
|
max-height: 100%;
|
||||||
object-fit: contain;
|
object-fit: contain;
|
||||||
}
|
}
|
||||||
|
|
||||||
.note-book-card.type-image .note-book-content img, .note-book-card.type-text .note-book-content img {
|
.note-book-card.type-image .note-book-content img,
|
||||||
|
.note-book-card.type-text .note-book-content img,
|
||||||
|
.note-book-card.type-canvas .note-book-content img {
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
max-height: 100%;
|
max-height: 100%;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,17 +1,5 @@
|
|||||||
import options from './options.js';
|
|
||||||
import server from "./server.js";
|
import server from "./server.js";
|
||||||
|
|
||||||
let lastProtectedSessionOperationDate = 0;
|
|
||||||
|
|
||||||
setInterval(() => {
|
|
||||||
const protectedSessionTimeout = options.getInt('protectedSessionTimeout');
|
|
||||||
if (lastProtectedSessionOperationDate
|
|
||||||
&& Date.now() - lastProtectedSessionOperationDate > protectedSessionTimeout * 1000) {
|
|
||||||
|
|
||||||
resetProtectedSession();
|
|
||||||
}
|
|
||||||
}, 10000);
|
|
||||||
|
|
||||||
function enableProtectedSession() {
|
function enableProtectedSession() {
|
||||||
glob.isProtectedSessionAvailable = true;
|
glob.isProtectedSessionAvailable = true;
|
||||||
|
|
||||||
@@ -26,9 +14,9 @@ function isProtectedSessionAvailable() {
|
|||||||
return glob.isProtectedSessionAvailable;
|
return glob.isProtectedSessionAvailable;
|
||||||
}
|
}
|
||||||
|
|
||||||
function touchProtectedSession() {
|
async function touchProtectedSession() {
|
||||||
if (isProtectedSessionAvailable()) {
|
if (isProtectedSessionAvailable()) {
|
||||||
lastProtectedSessionOperationDate = Date.now();
|
await server.post("login/protected/touch");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -306,7 +306,8 @@ export default class TabManager extends Component {
|
|||||||
const mainNoteContexts = this.getNoteContexts().filter(nc => nc.isMainContext());
|
const mainNoteContexts = this.getNoteContexts().filter(nc => nc.isMainContext());
|
||||||
|
|
||||||
if (mainNoteContexts.length === 1) {
|
if (mainNoteContexts.length === 1) {
|
||||||
mainNoteContexts[0].setEmpty();
|
await this.clearLastMainNoteContext(noteContextToRemove);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -317,7 +318,7 @@ export default class TabManager extends Component {
|
|||||||
const noteContextsToRemove = noteContextToRemove.getSubContexts();
|
const noteContextsToRemove = noteContextToRemove.getSubContexts();
|
||||||
const ntxIdsToRemove = noteContextsToRemove.map(nc => nc.ntxId);
|
const ntxIdsToRemove = noteContextsToRemove.map(nc => nc.ntxId);
|
||||||
|
|
||||||
await this.triggerEvent('beforeTabRemove', { ntxIds: ntxIdsToRemove });
|
await this.triggerEvent('beforeNoteContextRemove', { ntxIds: ntxIdsToRemove });
|
||||||
|
|
||||||
if (!noteContextToRemove.isMainContext()) {
|
if (!noteContextToRemove.isMainContext()) {
|
||||||
await this.activateNoteContext(noteContextToRemove.getMainContext().ntxId);
|
await this.activateNoteContext(noteContextToRemove.getMainContext().ntxId);
|
||||||
@@ -336,16 +337,39 @@ export default class TabManager extends Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.children = this.children.filter(nc => !ntxIdsToRemove.includes(nc.ntxId));
|
this.removeNoteContexts(noteContextsToRemove);
|
||||||
|
|
||||||
this.recentlyClosedTabs.push(noteContextsToRemove);
|
|
||||||
|
|
||||||
this.triggerEvent('noteContextRemoved', {ntxIds: ntxIdsToRemove});
|
|
||||||
|
|
||||||
this.tabsUpdate.scheduleUpdate();
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async clearLastMainNoteContext(noteContextToClear) {
|
||||||
|
noteContextToClear.setEmpty();
|
||||||
|
|
||||||
|
// activate main split
|
||||||
|
await this.activateNoteContext(noteContextToClear.ntxId);
|
||||||
|
|
||||||
|
// remove all other splits
|
||||||
|
const noteContextsToRemove = noteContextToClear.getSubContexts()
|
||||||
|
.filter(ntx => ntx.ntxId !== noteContextToClear.ntxId);
|
||||||
|
|
||||||
|
const ntxIdsToRemove = noteContextsToRemove.map(ntx => ntx.ntxId);
|
||||||
|
|
||||||
|
await this.triggerEvent('beforeNoteContextRemove', {ntxIds: ntxIdsToRemove});
|
||||||
|
|
||||||
|
this.removeNoteContexts(noteContextsToRemove);
|
||||||
|
}
|
||||||
|
|
||||||
|
removeNoteContexts(noteContextsToRemove) {
|
||||||
|
const ntxIdsToRemove = noteContextsToRemove.map(nc => nc.ntxId);
|
||||||
|
|
||||||
|
this.children = this.children.filter(nc => !ntxIdsToRemove.includes(nc.ntxId));
|
||||||
|
|
||||||
|
this.recentlyClosedTabs.push(noteContextsToRemove);
|
||||||
|
|
||||||
|
this.triggerEvent('noteContextRemoved', {ntxIds: ntxIdsToRemove});
|
||||||
|
|
||||||
|
this.tabsUpdate.scheduleUpdate();
|
||||||
|
}
|
||||||
|
|
||||||
tabReorderEvent({ntxIdsInOrder}) {
|
tabReorderEvent({ntxIdsInOrder}) {
|
||||||
const order = {};
|
const order = {};
|
||||||
|
|
||||||
|
|||||||
@@ -33,7 +33,8 @@ class TreeContextMenu {
|
|||||||
{ title: "Note Map", command: command, type: "note-map", uiIcon: "map-alt" },
|
{ title: "Note Map", command: command, type: "note-map", uiIcon: "map-alt" },
|
||||||
{ title: "Render HTML note", command: command, type: "render", uiIcon: "extension" },
|
{ title: "Render HTML note", command: command, type: "render", uiIcon: "extension" },
|
||||||
{ title: "Book", command: command, type: "book", uiIcon: "book" },
|
{ title: "Book", command: command, type: "book", uiIcon: "book" },
|
||||||
{ title: "Mermaid diagram", command: command, type: "mermaid", uiIcon: "selection" }
|
{ title: "Mermaid diagram", command: command, type: "mermaid", uiIcon: "selection" },
|
||||||
|
{ title: "Canvas", command: command, type: "canvas", uiIcon: "pen" },
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -280,7 +280,7 @@ function isHtmlEmpty(html) {
|
|||||||
|
|
||||||
async function clearBrowserCache() {
|
async function clearBrowserCache() {
|
||||||
if (isElectron()) {
|
if (isElectron()) {
|
||||||
const win = utils.dynamicRequire('@electron/remote').getCurrentWindow();
|
const win = dynamicRequire('@electron/remote').getCurrentWindow();
|
||||||
await win.webContents.session.clearCache();
|
await win.webContents.session.clearCache();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -292,10 +292,6 @@ function copySelectionToClipboard() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function isCKEditorInitialized() {
|
|
||||||
return !!(window && window.cutToNote);
|
|
||||||
}
|
|
||||||
|
|
||||||
function dynamicRequire(moduleName) {
|
function dynamicRequire(moduleName) {
|
||||||
if (typeof __non_webpack_require__ !== 'undefined') {
|
if (typeof __non_webpack_require__ !== 'undefined') {
|
||||||
return __non_webpack_require__(moduleName);
|
return __non_webpack_require__(moduleName);
|
||||||
@@ -359,6 +355,16 @@ function isValidAttributeName(name) {
|
|||||||
return ATTR_NAME_MATCHER.test(name);
|
return ATTR_NAME_MATCHER.test(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function sleep(time_ms) {
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
setTimeout(resolve, time_ms);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function escapeRegExp(str) {
|
||||||
|
return str.replace(/([.*+?^=!:${}()|\[\]\/\\])/g, "\\$1");
|
||||||
|
}
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
reloadFrontendApp,
|
reloadFrontendApp,
|
||||||
parseDate,
|
parseDate,
|
||||||
@@ -395,12 +401,13 @@ export default {
|
|||||||
clearBrowserCache,
|
clearBrowserCache,
|
||||||
normalizeShortcut,
|
normalizeShortcut,
|
||||||
copySelectionToClipboard,
|
copySelectionToClipboard,
|
||||||
isCKEditorInitialized,
|
|
||||||
dynamicRequire,
|
dynamicRequire,
|
||||||
timeLimit,
|
timeLimit,
|
||||||
initHelpDropdown,
|
initHelpDropdown,
|
||||||
initHelpButtons,
|
initHelpButtons,
|
||||||
openHelp,
|
openHelp,
|
||||||
filterAttributeName,
|
filterAttributeName,
|
||||||
isValidAttributeName
|
isValidAttributeName,
|
||||||
|
sleep,
|
||||||
|
escapeRegExp
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -219,11 +219,23 @@ const ATTR_HELP = {
|
|||||||
"shareDisallowRobotIndexing": `will forbid robot indexing of this note via <code>X-Robots-Tag: noindex</code> header`,
|
"shareDisallowRobotIndexing": `will forbid robot indexing of this note via <code>X-Robots-Tag: noindex</code> header`,
|
||||||
"displayRelations": "comma delimited names of relations which should be displayed. All other ones will be hidden.",
|
"displayRelations": "comma delimited names of relations which should be displayed. All other ones will be hidden.",
|
||||||
"hideRelations": "comma delimited names of relations which should be hidden. All other ones will be displayed.",
|
"hideRelations": "comma delimited names of relations which should be hidden. All other ones will be displayed.",
|
||||||
|
"titleTemplate": `default title of notes created as children of this note. The value is evaluated as JavaScript string
|
||||||
|
and thus can be enriched with dynamic content via the injected <code>now</code> and <code>parentNote</code> variables. Examples:
|
||||||
|
|
||||||
|
<ul>
|
||||||
|
<li><code>\${parentNote.getLabelValue('authorName')}'s literary works</code></li>
|
||||||
|
<li><code>Log for \${now.format('YYYY-MM-DD HH:mm:ss')}</code></li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
See <a href="https://github.com/zadam/trilium/wiki/Default-note-title">wiki with details</a>, API docs for <a href="https://zadam.github.io/trilium/backend_api/Note.html">parentNote</a> and <a href="https://day.js.org/docs/en/display/format">now</a> for details.`
|
||||||
},
|
},
|
||||||
"relation": {
|
"relation": {
|
||||||
"runOnNoteCreation": "executes when note is created on backend",
|
"runOnNoteCreation": "executes when note is created on backend",
|
||||||
"runOnNoteTitleChange": "executes when note title is changed (includes note creation as well)",
|
"runOnNoteTitleChange": "executes when note title is changed (includes note creation as well)",
|
||||||
"runOnNoteChange": "executes when note is changed (includes note creation as well)",
|
"runOnNoteChange": "executes when note is changed (includes note creation as well)",
|
||||||
|
"runOnNoteDeletion": "executes when note is being deleted",
|
||||||
|
"runOnBranchCreation": "executes when a branch is created. Branch is a link between parent note and child note and is created e.g. when cloning or moving note.",
|
||||||
|
"runOnBranchDeletion": "executes when a branch is deleted. Branch is a link between parent note and child note and is deleted e.g. when moving note (old branch/link is deleted).",
|
||||||
"runOnChildNoteCreation": "executes when new note is created under this note",
|
"runOnChildNoteCreation": "executes when new note is created under this note",
|
||||||
"runOnAttributeCreation": "executes when new attribute is created under this note",
|
"runOnAttributeCreation": "executes when new attribute is created under this note",
|
||||||
"runOnAttributeChange": "executes when attribute is changed under this note",
|
"runOnAttributeChange": "executes when attribute is changed under this note",
|
||||||
|
|||||||
@@ -41,8 +41,8 @@ const TPL = `
|
|||||||
right: 10px;
|
right: 10px;
|
||||||
width: 400px;
|
width: 400px;
|
||||||
border-radius: 10px;
|
border-radius: 10px;
|
||||||
background-color: #eeeeee;
|
background-color: var(--more-accented-background-color);
|
||||||
color: #444;
|
color: var(--main-text-color);
|
||||||
padding: 20px;
|
padding: 20px;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ export default class ScrollingContainer extends Container {
|
|||||||
super();
|
super();
|
||||||
|
|
||||||
this.css('overflow', 'auto');
|
this.css('overflow', 'auto');
|
||||||
|
this.css('position', 'relative');
|
||||||
}
|
}
|
||||||
|
|
||||||
setNoteContextEvent({noteContext}) {
|
setNoteContextEvent({noteContext}) {
|
||||||
@@ -35,7 +36,7 @@ export default class ScrollingContainer extends Container {
|
|||||||
|
|
||||||
const promise = super.handleEventInChildren(name, data);
|
const promise = super.handleEventInChildren(name, data);
|
||||||
|
|
||||||
// there seems to be some asynchronicity and we need to wait a bit before scrolling
|
// there seems to be some asynchronicity, and we need to wait a bit before scrolling
|
||||||
promise.then(() => setTimeout(() => this.$widget.scrollTop(scrollTop), 500));
|
promise.then(() => setTimeout(() => this.$widget.scrollTop(scrollTop), 500));
|
||||||
|
|
||||||
return promise;
|
return promise;
|
||||||
@@ -44,4 +45,8 @@ export default class ScrollingContainer extends Container {
|
|||||||
return super.handleEventInChildren(name, data);
|
return super.handleEventInChildren(name, data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
scrollContainerToCommand({position}) {
|
||||||
|
this.$widget.scrollTop(position);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
255
src/public/app/widgets/find.js
Normal file
255
src/public/app/widgets/find.js
Normal file
@@ -0,0 +1,255 @@
|
|||||||
|
/**
|
||||||
|
* (c) Antonio Tejada 2022
|
||||||
|
* https://github.com/antoniotejada/Trilium-FindWidget
|
||||||
|
*/
|
||||||
|
|
||||||
|
import NoteContextAwareWidget from "./note_context_aware_widget.js";
|
||||||
|
import FindInText from "./find_in_text.js";
|
||||||
|
import FindInCode from "./find_in_code.js";
|
||||||
|
import FindInHtml from "./find_in_html.js";
|
||||||
|
|
||||||
|
const findWidgetDelayMillis = 200;
|
||||||
|
const waitForEnter = (findWidgetDelayMillis < 0);
|
||||||
|
|
||||||
|
// tabIndex=-1 on the checkbox labels is necessary so when clicking on the label
|
||||||
|
// the focusout handler is called with relatedTarget equal to the label instead
|
||||||
|
// of undefined. It's -1 instead of > 0, so they don't tabstop
|
||||||
|
const TPL = `
|
||||||
|
<div style="contain: none;">
|
||||||
|
<style>
|
||||||
|
.find-widget-box {
|
||||||
|
padding: 10px;
|
||||||
|
border-top: 1px solid var(--main-border-color);
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.find-widget-box > * {
|
||||||
|
margin-right: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.find-widget-box {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
.find-widget-found-wrapper {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.find-widget-search-term-input-group {
|
||||||
|
max-width: 300px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.find-widget-spacer {
|
||||||
|
flex-grow: 1;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<div class="find-widget-box">
|
||||||
|
<div class="input-group find-widget-search-term-input-group">
|
||||||
|
<input type="text" class="form-control find-widget-search-term-input">
|
||||||
|
<div class="input-group-append">
|
||||||
|
<button class="btn btn-outline-secondary bx bxs-chevron-up find-widget-previous-button" type="button"></button>
|
||||||
|
<button class="btn btn-outline-secondary bx bxs-chevron-down find-widget-next-button" type="button"></button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-check">
|
||||||
|
<label tabIndex="-1" class="form-check-label">
|
||||||
|
<input type="checkbox" class="form-check-input find-widget-case-sensitive-checkbox">
|
||||||
|
case sensitive
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-check">
|
||||||
|
<label tabIndex="-1" class="form-check-label">
|
||||||
|
<input type="checkbox" class="form-check-input find-widget-match-words-checkbox">
|
||||||
|
match words
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="find-widget-found-wrapper">
|
||||||
|
<span class="find-widget-current-found">0</span>
|
||||||
|
/
|
||||||
|
<span class="find-widget-total-found">0</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="find-widget-spacer"></div>
|
||||||
|
|
||||||
|
<div class="find-widget-close-button"><button class="btn icon-action bx bx-x"></button></div>
|
||||||
|
</div>
|
||||||
|
</div>`;
|
||||||
|
|
||||||
|
export default class FindWidget extends NoteContextAwareWidget {
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
|
||||||
|
this.searchTerm = null;
|
||||||
|
|
||||||
|
this.textHandler = new FindInText(this);
|
||||||
|
this.codeHandler = new FindInCode(this);
|
||||||
|
this.htmlHandler = new FindInHtml(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
async noteSwitched() {
|
||||||
|
await super.noteSwitched();
|
||||||
|
|
||||||
|
await this.closeSearch();
|
||||||
|
}
|
||||||
|
|
||||||
|
doRender() {
|
||||||
|
this.$widget = $(TPL);
|
||||||
|
this.$findBox = this.$widget.find('.find-widget-box');
|
||||||
|
this.$findBox.hide();
|
||||||
|
this.$input = this.$widget.find('.find-widget-search-term-input');
|
||||||
|
this.$currentFound = this.$widget.find('.find-widget-current-found');
|
||||||
|
this.$totalFound = this.$widget.find('.find-widget-total-found');
|
||||||
|
this.$caseSensitiveCheckbox = this.$widget.find(".find-widget-case-sensitive-checkbox");
|
||||||
|
this.$caseSensitiveCheckbox.change(() => this.performFind());
|
||||||
|
this.$matchWordsCheckbox = this.$widget.find(".find-widget-match-words-checkbox");
|
||||||
|
this.$matchWordsCheckbox.change(() => this.performFind());
|
||||||
|
this.$previousButton = this.$widget.find(".find-widget-previous-button");
|
||||||
|
this.$previousButton.on("click", () => this.findNext(-1));
|
||||||
|
this.$nextButton = this.$widget.find(".find-widget-next-button");
|
||||||
|
this.$nextButton.on("click", () => this.findNext(1));
|
||||||
|
this.$closeButton = this.$widget.find(".find-widget-close-button");
|
||||||
|
this.$closeButton.on("click", () => this.closeSearch());
|
||||||
|
|
||||||
|
this.$input.keydown(async e => {
|
||||||
|
if ((e.metaKey || e.ctrlKey) && (e.key === 'F' || e.key === 'f')) {
|
||||||
|
// If ctrl+f is pressed when the findbox is shown, select the
|
||||||
|
// whole input to find
|
||||||
|
this.$input.select();
|
||||||
|
} else if (e.key === 'Enter' || e.key === 'F3') {
|
||||||
|
await this.findNext(e?.shiftKey ? -1 : 1);
|
||||||
|
e.preventDefault();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
this.$findBox.keydown(async e => {
|
||||||
|
if (e.key === 'Escape') {
|
||||||
|
await this.closeSearch();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
this.$input.on('input', () => this.startSearch());
|
||||||
|
|
||||||
|
return this.$widget;
|
||||||
|
}
|
||||||
|
|
||||||
|
async findInTextEvent() {
|
||||||
|
if (!this.isActiveNoteContext()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!['text', 'code'].includes(this.note.type) || !this.$findBox.is(":hidden")) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const readOnly = await this.noteContext.isReadOnly();
|
||||||
|
|
||||||
|
if (readOnly) {
|
||||||
|
this.handler = this.htmlHandler;
|
||||||
|
} else {
|
||||||
|
this.handler = this.note.type === "code"
|
||||||
|
? this.codeHandler
|
||||||
|
: this.textHandler;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.$findBox.show();
|
||||||
|
this.$input.focus();
|
||||||
|
this.$totalFound.text(0);
|
||||||
|
this.$currentFound.text(0);
|
||||||
|
|
||||||
|
const searchTerm = await this.handler.getInitialSearchTerm();
|
||||||
|
|
||||||
|
this.$input.val(searchTerm || "");
|
||||||
|
|
||||||
|
// Directly perform the search if there's some text to
|
||||||
|
// find, without delaying or waiting for enter
|
||||||
|
if (searchTerm !== "") {
|
||||||
|
this.$input.select();
|
||||||
|
await this.performFind();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
startSearch() {
|
||||||
|
// XXX This should clear the previous search immediately in all cases
|
||||||
|
// (the search is stale when waitforenter but also while the
|
||||||
|
// delay is running for non waitforenter case)
|
||||||
|
if (!waitForEnter) {
|
||||||
|
// Clear the previous timeout if any, it's ok if timeoutId is
|
||||||
|
// null or undefined
|
||||||
|
clearTimeout(this.timeoutId);
|
||||||
|
|
||||||
|
// Defer the search a few millis so the search doesn't start
|
||||||
|
// immediately, as this can cause search word typing lag with
|
||||||
|
// one or two-char searchwords and long notes
|
||||||
|
// See https://github.com/antoniotejada/Trilium-FindWidget/issues/1
|
||||||
|
this.timeoutId = setTimeout(async () => {
|
||||||
|
this.timeoutId = null;
|
||||||
|
await this.performFind();
|
||||||
|
}, findWidgetDelayMillis);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param direction +1 for next, -1 for previous
|
||||||
|
* @returns {Promise<void>}
|
||||||
|
*/
|
||||||
|
async findNext(direction) {
|
||||||
|
const searchTerm = this.$input.val();
|
||||||
|
if (waitForEnter && this.searchTerm !== searchTerm) {
|
||||||
|
await this.performFind();
|
||||||
|
}
|
||||||
|
const totalFound = parseInt(this.$totalFound.text());
|
||||||
|
const currentFound = parseInt(this.$currentFound.text()) - 1;
|
||||||
|
|
||||||
|
if (totalFound > 0) {
|
||||||
|
let nextFound = currentFound + direction;
|
||||||
|
// Wrap around
|
||||||
|
if (nextFound > totalFound - 1) {
|
||||||
|
nextFound = 0;
|
||||||
|
} else if (nextFound < 0) {
|
||||||
|
nextFound = totalFound - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.$currentFound.text(nextFound + 1);
|
||||||
|
|
||||||
|
await this.handler.findNext(direction, currentFound, nextFound);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Perform the find and highlight the find results. */
|
||||||
|
async performFind() {
|
||||||
|
const searchTerm = this.$input.val();
|
||||||
|
const matchCase = this.$caseSensitiveCheckbox.prop("checked");
|
||||||
|
const wholeWord = this.$matchWordsCheckbox.prop("checked");
|
||||||
|
|
||||||
|
const {totalFound, currentFound} = await this.handler.performFind(searchTerm, matchCase, wholeWord);
|
||||||
|
|
||||||
|
this.$totalFound.text(totalFound);
|
||||||
|
this.$currentFound.text(currentFound);
|
||||||
|
|
||||||
|
this.searchTerm = searchTerm;
|
||||||
|
}
|
||||||
|
|
||||||
|
async closeSearch() {
|
||||||
|
if (this.$findBox.is(":visible")) {
|
||||||
|
this.$findBox.hide();
|
||||||
|
|
||||||
|
// Restore any state, if there's a current occurrence clear markers
|
||||||
|
// and scroll to and select the last occurrence
|
||||||
|
const totalFound = parseInt(this.$totalFound.text());
|
||||||
|
const currentFound = parseInt(this.$currentFound.text()) - 1;
|
||||||
|
|
||||||
|
this.searchTerm = null;
|
||||||
|
|
||||||
|
await this.handler.findBoxClosed(totalFound, currentFound);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
isEnabled() {
|
||||||
|
return super.isEnabled() && ['text', 'code'].includes(this.note.type);
|
||||||
|
}
|
||||||
|
}
|
||||||
196
src/public/app/widgets/find_in_code.js
Normal file
196
src/public/app/widgets/find_in_code.js
Normal file
@@ -0,0 +1,196 @@
|
|||||||
|
// ck-find-result and ck-find-result_selected are the styles ck-editor
|
||||||
|
// uses for highlighting matches, use the same one on CodeMirror
|
||||||
|
// for consistency
|
||||||
|
import utils from "../services/utils.js";
|
||||||
|
|
||||||
|
const FIND_RESULT_SELECTED_CSS_CLASSNAME = "ck-find-result_selected";
|
||||||
|
const FIND_RESULT_CSS_CLASSNAME = "ck-find-result";
|
||||||
|
|
||||||
|
export default class FindInCode {
|
||||||
|
constructor(parent) {
|
||||||
|
/** @property {FindWidget} */
|
||||||
|
this.parent = parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
async getCodeEditor() {
|
||||||
|
return this.parent.noteContext.getCodeEditor();
|
||||||
|
}
|
||||||
|
|
||||||
|
async getInitialSearchTerm() {
|
||||||
|
const codeEditor = await this.getCodeEditor();
|
||||||
|
|
||||||
|
// highlightSelectionMatches is the overlay that highlights
|
||||||
|
// the words under the cursor. This occludes the search
|
||||||
|
// markers style, save it, disable it. Will be restored when
|
||||||
|
// the focus is back into the note
|
||||||
|
this.oldHighlightSelectionMatches = codeEditor.getOption("highlightSelectionMatches");
|
||||||
|
codeEditor.setOption("highlightSelectionMatches", false);
|
||||||
|
|
||||||
|
// Fill in the findbox with the current selection if any
|
||||||
|
const selectedText = codeEditor.getSelection()
|
||||||
|
if (selectedText !== "") {
|
||||||
|
return selectedText;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async performFind(searchTerm, matchCase, wholeWord) {
|
||||||
|
let findResult = null;
|
||||||
|
let totalFound = 0;
|
||||||
|
let currentFound = -1;
|
||||||
|
|
||||||
|
// See https://codemirror.net/addon/search/searchcursor.js for tips
|
||||||
|
const codeEditor = await this.getCodeEditor();
|
||||||
|
const doc = codeEditor.doc;
|
||||||
|
const text = doc.getValue();
|
||||||
|
|
||||||
|
// Clear all markers
|
||||||
|
if (this.findResult != null) {
|
||||||
|
codeEditor.operation(() => {
|
||||||
|
for (let i = 0; i < this.findResult.length; ++i) {
|
||||||
|
const marker = this.findResult[i];
|
||||||
|
marker.clear();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (searchTerm !== "") {
|
||||||
|
searchTerm = utils.escapeRegExp(searchTerm);
|
||||||
|
|
||||||
|
// Find and highlight matches
|
||||||
|
// Find and highlight matches
|
||||||
|
// XXX Using \\b and not using the unicode flag probably doesn't
|
||||||
|
// work with non ascii alphabets, findAndReplace uses a more
|
||||||
|
// complicated regexp, see
|
||||||
|
// https://github.com/ckeditor/ckeditor5/blob/b95e2faf817262ac0e1e21993d9c0bde3f1be594/packages/ckeditor5-find-and-replace/src/utils.js#L145
|
||||||
|
const wholeWordChar = wholeWord ? "\\b" : "";
|
||||||
|
const re = new RegExp(wholeWordChar + searchTerm + wholeWordChar,
|
||||||
|
'g' + (matchCase ? '' : 'i'));
|
||||||
|
let curLine = 0;
|
||||||
|
let curChar = 0;
|
||||||
|
let curMatch = null;
|
||||||
|
findResult = [];
|
||||||
|
// All those markText take several seconds on eg this ~500-line
|
||||||
|
// script, batch them inside an operation so they become
|
||||||
|
// unnoticeable. Alternatively, an overlay could be used, see
|
||||||
|
// https://codemirror.net/addon/search/match-highlighter.js ?
|
||||||
|
codeEditor.operation(() => {
|
||||||
|
for (let i = 0; i < text.length; ++i) {
|
||||||
|
// Fetch next match if it's the first time or
|
||||||
|
// if past the current match start
|
||||||
|
if ((curMatch == null) || (curMatch.index < i)) {
|
||||||
|
curMatch = re.exec(text);
|
||||||
|
if (curMatch == null) {
|
||||||
|
// No more matches
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Create a non-selected highlight marker for the match, the
|
||||||
|
// selected marker highlight will be done later
|
||||||
|
if (i === curMatch.index) {
|
||||||
|
let fromPos = { "line" : curLine, "ch" : curChar };
|
||||||
|
// XXX If multiline is supported, this needs to
|
||||||
|
// recalculate curLine since the match may span
|
||||||
|
// lines
|
||||||
|
let toPos = { "line" : curLine, "ch" : curChar + curMatch[0].length};
|
||||||
|
// XXX or css = "color: #f3"
|
||||||
|
let marker = doc.markText( fromPos, toPos, { "className" : FIND_RESULT_CSS_CLASSNAME });
|
||||||
|
findResult.push(marker);
|
||||||
|
|
||||||
|
// Set the first match beyond the cursor as current
|
||||||
|
// match
|
||||||
|
if (currentFound === -1) {
|
||||||
|
const cursorPos = codeEditor.getCursor();
|
||||||
|
if ((fromPos.line > cursorPos.line) ||
|
||||||
|
((fromPos.line === cursorPos.line) &&
|
||||||
|
(fromPos.ch >= cursorPos.ch))){
|
||||||
|
currentFound = totalFound;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
totalFound++;
|
||||||
|
}
|
||||||
|
// Do line and char position tracking
|
||||||
|
if (text[i] === "\n") {
|
||||||
|
curLine++;
|
||||||
|
curChar = 0;
|
||||||
|
} else {
|
||||||
|
curChar++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
this.findResult = findResult;
|
||||||
|
|
||||||
|
// Calculate curfound if not already, highlight it as selected
|
||||||
|
if (totalFound > 0) {
|
||||||
|
currentFound = Math.max(0, currentFound)
|
||||||
|
let marker = findResult[currentFound];
|
||||||
|
let pos = marker.find();
|
||||||
|
codeEditor.scrollIntoView(pos.to);
|
||||||
|
marker.clear();
|
||||||
|
findResult[currentFound] = doc.markText( pos.from, pos.to,
|
||||||
|
{ "className" : FIND_RESULT_SELECTED_CSS_CLASSNAME }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
totalFound,
|
||||||
|
currentFound: currentFound + 1
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
async findNext(direction, currentFound, nextFound) {
|
||||||
|
const codeEditor = await this.getCodeEditor();
|
||||||
|
const doc = codeEditor.doc;
|
||||||
|
|
||||||
|
//
|
||||||
|
// Dehighlight current, highlight & scrollIntoView next
|
||||||
|
//
|
||||||
|
|
||||||
|
let marker = this.findResult[currentFound];
|
||||||
|
let pos = marker.find();
|
||||||
|
marker.clear();
|
||||||
|
marker = doc.markText(
|
||||||
|
pos.from, pos.to,
|
||||||
|
{ "className" : FIND_RESULT_CSS_CLASSNAME }
|
||||||
|
);
|
||||||
|
this.findResult[currentFound] = marker;
|
||||||
|
|
||||||
|
marker = this.findResult[nextFound];
|
||||||
|
pos = marker.find();
|
||||||
|
marker.clear();
|
||||||
|
marker = doc.markText(
|
||||||
|
pos.from, pos.to,
|
||||||
|
{ "className" : FIND_RESULT_SELECTED_CSS_CLASSNAME }
|
||||||
|
);
|
||||||
|
this.findResult[nextFound] = marker;
|
||||||
|
|
||||||
|
codeEditor.scrollIntoView(pos.from);
|
||||||
|
}
|
||||||
|
|
||||||
|
async findBoxClosed(totalFound, currentFound) {
|
||||||
|
const codeEditor = await this.getCodeEditor();
|
||||||
|
|
||||||
|
if (totalFound > 0) {
|
||||||
|
const doc = codeEditor.doc;
|
||||||
|
const pos = this.findResult[currentFound].find();
|
||||||
|
// Note setting the selection sets the cursor to
|
||||||
|
// the end of the selection and scrolls it into
|
||||||
|
// view
|
||||||
|
doc.setSelection(pos.from, pos.to);
|
||||||
|
// Clear all markers
|
||||||
|
codeEditor.operation(() => {
|
||||||
|
for (let i = 0; i < this.findResult.length; ++i) {
|
||||||
|
let marker = this.findResult[i];
|
||||||
|
marker.clear();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// Restore the highlightSelectionMatches setting
|
||||||
|
codeEditor.setOption("highlightSelectionMatches", this.oldHighlightSelectionMatches);
|
||||||
|
this.findResult = null;
|
||||||
|
|
||||||
|
codeEditor.focus();
|
||||||
|
}
|
||||||
|
}
|
||||||
93
src/public/app/widgets/find_in_html.js
Normal file
93
src/public/app/widgets/find_in_html.js
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
// ck-find-result and ck-find-result_selected are the styles ck-editor
|
||||||
|
// uses for highlighting matches, use the same one on CodeMirror
|
||||||
|
// for consistency
|
||||||
|
import libraryLoader from "../services/library_loader.js";
|
||||||
|
import utils from "../services/utils.js";
|
||||||
|
import appContext from "../services/app_context.js";
|
||||||
|
|
||||||
|
const FIND_RESULT_SELECTED_CSS_CLASSNAME = "ck-find-result_selected";
|
||||||
|
const FIND_RESULT_CSS_CLASSNAME = "ck-find-result";
|
||||||
|
|
||||||
|
export default class FindInHtml {
|
||||||
|
constructor(parent) {
|
||||||
|
/** @property {FindWidget} */
|
||||||
|
this.parent = parent;
|
||||||
|
this.currentIndex = 0;
|
||||||
|
this.$results = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
async getInitialSearchTerm() {
|
||||||
|
return ""; // FIXME
|
||||||
|
}
|
||||||
|
|
||||||
|
async performFind(searchTerm, matchCase, wholeWord) {
|
||||||
|
await libraryLoader.requireLibrary(libraryLoader.MARKJS);
|
||||||
|
|
||||||
|
const $content = await this.parent.noteContext.getContentElement();
|
||||||
|
|
||||||
|
const wholeWordChar = wholeWord ? "\\b" : "";
|
||||||
|
const regExp = new RegExp(wholeWordChar + utils.escapeRegExp(searchTerm) + wholeWordChar, matchCase ? "g" : "gi");
|
||||||
|
|
||||||
|
return new Promise(res => {
|
||||||
|
$content.unmark({
|
||||||
|
done: () => {
|
||||||
|
$content.markRegExp(regExp, {
|
||||||
|
element: "span",
|
||||||
|
className: FIND_RESULT_CSS_CLASSNAME,
|
||||||
|
separateWordSearch: false,
|
||||||
|
caseSensitive: matchCase,
|
||||||
|
done: async () => {
|
||||||
|
this.$results = $content.find("." + FIND_RESULT_CSS_CLASSNAME);
|
||||||
|
this.currentIndex = 0;
|
||||||
|
await this.jumpTo();
|
||||||
|
|
||||||
|
res({
|
||||||
|
totalFound: this.$results.length,
|
||||||
|
currentFound: 1
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async findNext(direction, currentFound, nextFound) {
|
||||||
|
if (this.$results.length) {
|
||||||
|
this.currentIndex += direction;
|
||||||
|
|
||||||
|
if (this.currentIndex < 0) {
|
||||||
|
this.currentIndex = this.$results.length - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.currentIndex > this.$results.length - 1) {
|
||||||
|
this.currentIndex = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
await this.jumpTo();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async findBoxClosed(totalFound, currentFound) {
|
||||||
|
const $content = await this.parent.noteContext.getContentElement();
|
||||||
|
$content.unmark();
|
||||||
|
}
|
||||||
|
|
||||||
|
async jumpTo() {
|
||||||
|
if (this.$results.length) {
|
||||||
|
const offsetTop = 100;
|
||||||
|
const $current = this.$results.eq(this.currentIndex);
|
||||||
|
this.$results.removeClass(FIND_RESULT_SELECTED_CSS_CLASSNAME);
|
||||||
|
|
||||||
|
if ($current.length) {
|
||||||
|
$current.addClass(FIND_RESULT_SELECTED_CSS_CLASSNAME);
|
||||||
|
const position = $current.position().top - offsetTop;
|
||||||
|
|
||||||
|
const $content = await this.parent.noteContext.getContentElement();
|
||||||
|
const $contentWiget = appContext.getComponentByEl($content);
|
||||||
|
|
||||||
|
$contentWiget.triggerCommand("scrollContainerTo", {position});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
119
src/public/app/widgets/find_in_text.js
Normal file
119
src/public/app/widgets/find_in_text.js
Normal file
@@ -0,0 +1,119 @@
|
|||||||
|
export default class FindInText {
|
||||||
|
constructor(parent) {
|
||||||
|
/** @property {FindWidget} */
|
||||||
|
this.parent = parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
async getTextEditor() {
|
||||||
|
return this.parent.noteContext.getTextEditor();
|
||||||
|
}
|
||||||
|
|
||||||
|
async getInitialSearchTerm() {
|
||||||
|
const textEditor = await this.getTextEditor();
|
||||||
|
|
||||||
|
const selection = textEditor.model.document.selection;
|
||||||
|
const range = selection.getFirstRange();
|
||||||
|
|
||||||
|
for (const item of range.getItems()) {
|
||||||
|
// Fill in the findbox with the current selection if
|
||||||
|
// any
|
||||||
|
return item.data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async performFind(searchTerm, matchCase, wholeWord) {
|
||||||
|
// Do this even if the searchTerm is empty so the markers are cleared and
|
||||||
|
// the counters updated
|
||||||
|
const textEditor = await this.getTextEditor();
|
||||||
|
const model = textEditor.model;
|
||||||
|
let findResult = null;
|
||||||
|
let totalFound = 0;
|
||||||
|
let currentFound = -1;
|
||||||
|
|
||||||
|
// Clear
|
||||||
|
const findAndReplaceEditing = textEditor.plugins.get('FindAndReplaceEditing');
|
||||||
|
findAndReplaceEditing.state.clear(model);
|
||||||
|
findAndReplaceEditing.stop();
|
||||||
|
if (searchTerm !== "") {
|
||||||
|
// Parameters are callback/text, options.matchCase=false, options.wholeWords=false
|
||||||
|
// See https://github.com/ckeditor/ckeditor5/blob/b95e2faf817262ac0e1e21993d9c0bde3f1be594/packages/ckeditor5-find-and-replace/src/findcommand.js#L44
|
||||||
|
// XXX Need to use the callback version for regexp
|
||||||
|
// searchTerm = escapeRegExp(searchTerm);
|
||||||
|
// let re = new RegExp(searchTerm, 'gi');
|
||||||
|
// let m = text.match(re);
|
||||||
|
// totalFound = m ? m.length : 0;
|
||||||
|
const options = { "matchCase" : matchCase, "wholeWords" : wholeWord };
|
||||||
|
findResult = textEditor.execute('find', searchTerm, options);
|
||||||
|
totalFound = findResult.results.length;
|
||||||
|
// Find the result beyond the cursor
|
||||||
|
const cursorPos = model.document.selection.getLastPosition();
|
||||||
|
for (let i = 0; i < findResult.results.length; ++i) {
|
||||||
|
const marker = findResult.results.get(i).marker;
|
||||||
|
const fromPos = marker.getStart();
|
||||||
|
if (fromPos.compareWith(cursorPos) !== "before") {
|
||||||
|
currentFound = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.findResult = findResult;
|
||||||
|
|
||||||
|
// Calculate curfound if not already, highlight it as
|
||||||
|
// selected
|
||||||
|
if (totalFound > 0) {
|
||||||
|
currentFound = Math.max(0, currentFound);
|
||||||
|
// XXX Do this accessing the private data?
|
||||||
|
// See
|
||||||
|
// https://github.com/ckeditor/ckeditor5/blob/b95e2faf817262ac0e1e21993d9c0bde3f1be594/packages/ckeditor5-find-and-replace/src/findnextcommand.js
|
||||||
|
for (let i = 0 ; i < currentFound; ++i) {
|
||||||
|
textEditor.execute('findNext', searchTerm);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
totalFound,
|
||||||
|
currentFound: currentFound + 1
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
async findNext(direction, currentFound, nextFound) {
|
||||||
|
const textEditor = await this.getTextEditor();
|
||||||
|
|
||||||
|
// There are no parameters for findNext/findPrev
|
||||||
|
// See https://github.com/ckeditor/ckeditor5/blob/b95e2faf817262ac0e1e21993d9c0bde3f1be594/packages/ckeditor5-find-and-replace/src/findnextcommand.js#L57
|
||||||
|
// curFound wrap around above assumes findNext and
|
||||||
|
// findPrevious wraparound, which is what they do
|
||||||
|
if (direction > 0) {
|
||||||
|
textEditor.execute('findNext');
|
||||||
|
} else {
|
||||||
|
textEditor.execute('findPrevious');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async findBoxClosed(totalFound, currentFound) {
|
||||||
|
const textEditor = await this.getTextEditor();
|
||||||
|
|
||||||
|
if (totalFound > 0) {
|
||||||
|
// Clear the markers and set the caret to the
|
||||||
|
// current occurrence
|
||||||
|
const model = textEditor.model;
|
||||||
|
const range = this.findResult.results.get(currentFound).marker.getRange();
|
||||||
|
// From
|
||||||
|
// https://github.com/ckeditor/ckeditor5/blob/b95e2faf817262ac0e1e21993d9c0bde3f1be594/packages/ckeditor5-find-and-replace/src/findandreplace.js#L92
|
||||||
|
// XXX Roll our own since already done for codeEditor and
|
||||||
|
// will probably allow more refactoring?
|
||||||
|
let findAndReplaceEditing = textEditor.plugins.get('FindAndReplaceEditing');
|
||||||
|
findAndReplaceEditing.state.clear(model);
|
||||||
|
findAndReplaceEditing.stop();
|
||||||
|
model.change(writer => {
|
||||||
|
writer.setSelection(range, 0);
|
||||||
|
});
|
||||||
|
textEditor.editing.view.scrollToTheSelection();
|
||||||
|
}
|
||||||
|
|
||||||
|
this.findResult = null;
|
||||||
|
|
||||||
|
textEditor.focus();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -10,6 +10,7 @@ import FileTypeWidget from "./type_widgets/file.js";
|
|||||||
import ImageTypeWidget from "./type_widgets/image.js";
|
import ImageTypeWidget from "./type_widgets/image.js";
|
||||||
import RenderTypeWidget from "./type_widgets/render.js";
|
import RenderTypeWidget from "./type_widgets/render.js";
|
||||||
import RelationMapTypeWidget from "./type_widgets/relation_map.js";
|
import RelationMapTypeWidget from "./type_widgets/relation_map.js";
|
||||||
|
import CanvasTypeWidget from "./type_widgets/canvas.js";
|
||||||
import ProtectedSessionTypeWidget from "./type_widgets/protected_session.js";
|
import ProtectedSessionTypeWidget from "./type_widgets/protected_session.js";
|
||||||
import BookTypeWidget from "./type_widgets/book.js";
|
import BookTypeWidget from "./type_widgets/book.js";
|
||||||
import appContext from "../services/app_context.js";
|
import appContext from "../services/app_context.js";
|
||||||
@@ -50,6 +51,7 @@ const typeWidgetClasses = {
|
|||||||
'search': NoneTypeWidget,
|
'search': NoneTypeWidget,
|
||||||
'render': RenderTypeWidget,
|
'render': RenderTypeWidget,
|
||||||
'relation-map': RelationMapTypeWidget,
|
'relation-map': RelationMapTypeWidget,
|
||||||
|
'canvas': CanvasTypeWidget,
|
||||||
'protected-session': ProtectedSessionTypeWidget,
|
'protected-session': ProtectedSessionTypeWidget,
|
||||||
'book': BookTypeWidget,
|
'book': BookTypeWidget,
|
||||||
'note-map': NoteMapTypeWidget
|
'note-map': NoteMapTypeWidget
|
||||||
@@ -66,7 +68,7 @@ export default class NoteDetailWidget extends NoteContextAwareWidget {
|
|||||||
const {noteId} = note;
|
const {noteId} = note;
|
||||||
|
|
||||||
const dto = note.dto;
|
const dto = note.dto;
|
||||||
dto.content = this.getTypeWidget().getContent();
|
dto.content = await this.getTypeWidget().getContent();
|
||||||
|
|
||||||
// for read only notes
|
// for read only notes
|
||||||
if (dto.content === undefined) {
|
if (dto.content === undefined) {
|
||||||
@@ -145,11 +147,15 @@ export default class NoteDetailWidget extends NoteContextAwareWidget {
|
|||||||
this.checkFullHeight();
|
this.checkFullHeight();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* sets full height of container that contains note content for a subset of note-types
|
||||||
|
*/
|
||||||
checkFullHeight() {
|
checkFullHeight() {
|
||||||
// https://github.com/zadam/trilium/issues/2522
|
// https://github.com/zadam/trilium/issues/2522
|
||||||
this.$widget.toggleClass("full-height",
|
this.$widget.toggleClass("full-height",
|
||||||
!this.noteContext.hasNoteList()
|
!this.noteContext.hasNoteList()
|
||||||
&& ['editable-text', 'editable-code'].includes(this.type));
|
&& ['editable-text', 'editable-code', 'canvas'].includes(this.type)
|
||||||
|
&& this.mime !== 'text/x-sqlite;schema=trilium');
|
||||||
}
|
}
|
||||||
|
|
||||||
getTypeWidget() {
|
getTypeWidget() {
|
||||||
@@ -210,7 +216,7 @@ export default class NoteDetailWidget extends NoteContextAwareWidget {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async beforeTabRemoveEvent({ntxIds}) {
|
async beforeNoteContextRemoveEvent({ntxIds}) {
|
||||||
if (this.isNoteContext(ntxIds)) {
|
if (this.isNoteContext(ntxIds)) {
|
||||||
await this.spacedUpdate.updateNowIfNecessary();
|
await this.spacedUpdate.updateNowIfNecessary();
|
||||||
}
|
}
|
||||||
@@ -301,6 +307,16 @@ export default class NoteDetailWidget extends NoteContextAwareWidget {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async executeInActiveNoteDetailWidgetEvent({callback}) {
|
||||||
|
if (!this.isActiveNoteContext()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await this.initialized;
|
||||||
|
|
||||||
|
callback(this);
|
||||||
|
}
|
||||||
|
|
||||||
async cutIntoNoteCommand() {
|
async cutIntoNoteCommand() {
|
||||||
const note = appContext.tabManager.getActiveContextNote();
|
const note = appContext.tabManager.getActiveContextNote();
|
||||||
|
|
||||||
@@ -311,7 +327,8 @@ export default class NoteDetailWidget extends NoteContextAwareWidget {
|
|||||||
// without await as this otherwise causes deadlock through component mutex
|
// without await as this otherwise causes deadlock through component mutex
|
||||||
noteCreateService.createNote(appContext.tabManager.getActiveContextNotePath(), {
|
noteCreateService.createNote(appContext.tabManager.getActiveContextNotePath(), {
|
||||||
isProtected: note.isProtected,
|
isProtected: note.isProtected,
|
||||||
saveSelection: true
|
saveSelection: true,
|
||||||
|
textEditor: await this.noteContext.getTextEditor()
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -325,4 +342,16 @@ export default class NoteDetailWidget extends NoteContextAwareWidget {
|
|||||||
this.refresh();
|
this.refresh();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async executeWithTypeWidgetEvent({resolve, ntxId}) {
|
||||||
|
if (!this.isNoteContext(ntxId)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await this.initialized;
|
||||||
|
|
||||||
|
await this.getWidgetType();
|
||||||
|
|
||||||
|
resolve(this.getTypeWidget());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -87,7 +87,7 @@ export default class NoteTitleWidget extends NoteContextAwareWidget {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async beforeTabRemoveEvent({ntxIds}) {
|
async beforeNoteContextRemoveEvent({ntxIds}) {
|
||||||
if (this.isNoteContext(ntxIds)) {
|
if (this.isNoteContext(ntxIds)) {
|
||||||
await this.spacedUpdate.updateNowIfNecessary();
|
await this.spacedUpdate.updateNowIfNecessary();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ const NOTE_TYPES = [
|
|||||||
{ type: "text", mime: "text/html", title: "Text", selectable: true },
|
{ type: "text", mime: "text/html", title: "Text", selectable: true },
|
||||||
{ type: "relation-map", mime: "application/json", title: "Relation Map", selectable: true },
|
{ type: "relation-map", mime: "application/json", title: "Relation Map", selectable: true },
|
||||||
{ type: "render", mime: '', title: "Render Note", selectable: true },
|
{ type: "render", mime: '', title: "Render Note", selectable: true },
|
||||||
|
{ type: "canvas", mime: 'application/json', title: "Canvas", selectable: true },
|
||||||
{ type: "book", mime: '', title: "Book", selectable: true },
|
{ type: "book", mime: '', title: "Book", selectable: true },
|
||||||
{ type: "mermaid", mime: 'text/mermaid', title: "Mermaid Diagram", selectable: true },
|
{ type: "mermaid", mime: 'text/mermaid', title: "Mermaid Diagram", selectable: true },
|
||||||
{ type: "code", mime: 'text/plain', title: "Code", selectable: true }
|
{ type: "code", mime: 'text/plain', title: "Code", selectable: true }
|
||||||
|
|||||||
@@ -15,25 +15,36 @@ export default class NoteWrapperWidget extends FlexContainer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
setNoteContextEvent({noteContext}) {
|
setNoteContextEvent({noteContext}) {
|
||||||
this.refresh(noteContext);
|
this.noteContext = noteContext;
|
||||||
|
|
||||||
|
this.refresh();
|
||||||
}
|
}
|
||||||
|
|
||||||
noteSwitchedAndActivatedEvent({noteContext}) {
|
noteSwitchedAndActivatedEvent() {
|
||||||
this.refresh(noteContext);
|
this.refresh();
|
||||||
}
|
}
|
||||||
|
|
||||||
noteSwitchedEvent({noteContext}) {
|
noteSwitchedEvent() {
|
||||||
this.refresh(noteContext);
|
this.refresh();
|
||||||
}
|
}
|
||||||
|
|
||||||
activeContextChangedEvent({noteContext}) {
|
activeContextChangedEvent() {
|
||||||
this.refresh(noteContext);
|
this.refresh();
|
||||||
}
|
}
|
||||||
|
|
||||||
refresh(noteContext) {
|
refresh() {
|
||||||
|
const note = this.noteContext?.note;
|
||||||
|
|
||||||
this.$widget.toggleClass("full-content-width",
|
this.$widget.toggleClass("full-content-width",
|
||||||
['image', 'mermaid', 'book', 'render'].includes(noteContext?.note?.type)
|
['image', 'mermaid', 'book', 'render', 'canvas'].includes(note?.type)
|
||||||
|| !!noteContext?.note?.hasLabel('fullContentWidth')
|
|| !!note?.hasLabel('fullContentWidth')
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async entitiesReloadedEvent({loadResults}) {
|
||||||
|
// listening on changes of note.type
|
||||||
|
if (loadResults.isNoteReloaded(this.noteContext?.noteId)) {
|
||||||
|
this.refresh();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -134,8 +134,6 @@ const TPL = `
|
|||||||
Delete note</a>
|
Delete note</a>
|
||||||
<a class="dropdown-item" href="#" data-action-add="deleteNoteRevisions">
|
<a class="dropdown-item" href="#" data-action-add="deleteNoteRevisions">
|
||||||
Delete note revisions</a>
|
Delete note revisions</a>
|
||||||
<a class="dropdown-item" href="#" data-action-add="moveNote">
|
|
||||||
Delete note revisions</a>
|
|
||||||
<a class="dropdown-item" href="#" data-action-add="deleteLabel">
|
<a class="dropdown-item" href="#" data-action-add="deleteLabel">
|
||||||
Delete label</a>
|
Delete label</a>
|
||||||
<a class="dropdown-item" href="#" data-action-add="deleteRelation">
|
<a class="dropdown-item" href="#" data-action-add="deleteRelation">
|
||||||
|
|||||||
444
src/public/app/widgets/type_widgets/canvas.js
Normal file
444
src/public/app/widgets/type_widgets/canvas.js
Normal file
@@ -0,0 +1,444 @@
|
|||||||
|
import libraryLoader from "../../services/library_loader.js";
|
||||||
|
import TypeWidget from "./type_widget.js";
|
||||||
|
import utils from '../../services/utils.js';
|
||||||
|
import froca from "../../services/froca.js";
|
||||||
|
import debounce from "../../services/debounce.js";
|
||||||
|
|
||||||
|
const {sleep} = utils;
|
||||||
|
|
||||||
|
const TPL = `
|
||||||
|
<div class="canvas-widget note-detail-canvas note-detail-printable note-detail">
|
||||||
|
<style type="text/css">
|
||||||
|
.excalidraw .App-menu_top .buttonList {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
.excalidraw-wrapper {
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
:root[dir="ltr"]
|
||||||
|
.excalidraw
|
||||||
|
.layer-ui__wrapper
|
||||||
|
.zen-mode-transition.App-menu_bottom--transition-left {
|
||||||
|
transform: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* collaboration not possible so hide the button */
|
||||||
|
.CollabButton {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
||||||
|
<!-- height here necessary. otherwise excalidraw not shown -->
|
||||||
|
<div class="canvas-render" style="height: 100%"></div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* # Canvas note with excalidraw
|
||||||
|
* @author thfrei 2022-05-11
|
||||||
|
*
|
||||||
|
* Background:
|
||||||
|
* excalidraw gives great support for hand drawn notes. It also allows to include images and support
|
||||||
|
* for sketching. Excalidraw has a vibrant and active community.
|
||||||
|
*
|
||||||
|
* Functionality:
|
||||||
|
* We store the excalidraw assets (elements, appState, files) in the note. In addition to that, we
|
||||||
|
* export the SVG from the canvas on every update. The SVG is also saved in the note. It is used
|
||||||
|
* for displaying any canvas note inside of a text note as an image.
|
||||||
|
*
|
||||||
|
* Paths not taken.
|
||||||
|
* - excalidraw-to-svg (node.js) could be used to avoid storing the svg in the backend.
|
||||||
|
* We could render the SVG on the fly. However, as of now, it does not render any hand drawn
|
||||||
|
* (freedraw) paths. There is an issue with Path2D object not present in node-canvas library
|
||||||
|
* used by jsdom. (See Trilium PR for samples and other issues in respective library.
|
||||||
|
* Link will be added later). Related links:
|
||||||
|
* - https://github.com/Automattic/node-canvas/pull/2013
|
||||||
|
* - https://github.com/google/canvas-5-polyfill
|
||||||
|
* - https://github.com/Automattic/node-canvas/issues/1116
|
||||||
|
* - https://www.npmjs.com/package/path2d-polyfill
|
||||||
|
* - excalidraw-to-svg (node.js) takes quite some time to load an image (1-2s)
|
||||||
|
* - excalidraw-utils (browser) does render freedraw, however NOT freedraw with background. It is not
|
||||||
|
* used, since it is a big dependency, and has the same functionality as react + excalidraw.
|
||||||
|
* - infinite-drawing-canvas with fabric.js. This library lacked a lot of feature, excalidraw already
|
||||||
|
* has.
|
||||||
|
*
|
||||||
|
* Known issues:
|
||||||
|
* - v0.11.0 of excalidraw does not render freedraw backgrounds in the svg
|
||||||
|
* - the 3 excalidraw fonts should be included in the share and everywhere, so that it is shown
|
||||||
|
* when requiring svg.
|
||||||
|
*
|
||||||
|
* Discussion of storing svg in the note:
|
||||||
|
* - Pro: we will combat bit-rot. Showing the SVG will be very fast and easy, since it is already there.
|
||||||
|
* - Con: The note will get bigger (~40-50%?), we will generate more bandwith. However, using trilium
|
||||||
|
* desktop instance mitigates that issue.
|
||||||
|
*
|
||||||
|
* Roadmap:
|
||||||
|
* - Support image-notes as reference in excalidraw
|
||||||
|
* - Support canvas note as reference (svg) in other canvas notes.
|
||||||
|
* - Make it easy to include a canvas note inside a text note
|
||||||
|
* - Support for excalidraw libraries. Maybe special code notes with a tag.
|
||||||
|
*/
|
||||||
|
export default class ExcalidrawTypeWidget extends TypeWidget {
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
|
||||||
|
// constants
|
||||||
|
this.SCENE_VERSION_INITIAL = -1;
|
||||||
|
this.SCENE_VERSION_ERROR = -2;
|
||||||
|
|
||||||
|
// config
|
||||||
|
this.DEBOUNCE_TIME_ONCHANGEHANDLER = 750; // ms
|
||||||
|
// ensure that assets are loaded from trilium
|
||||||
|
window.EXCALIDRAW_ASSET_PATH = `${window.location.origin}/node_modules/@excalidraw/excalidraw/dist/`;
|
||||||
|
|
||||||
|
// temporary vars
|
||||||
|
this.currentNoteId = "";
|
||||||
|
this.currentSceneVersion = this.SCENE_VERSION_INITIAL;
|
||||||
|
|
||||||
|
// will be overwritten
|
||||||
|
this.excalidrawRef;
|
||||||
|
this.$render;
|
||||||
|
this.$widget;
|
||||||
|
this.reactHandlers; // used to control react state
|
||||||
|
|
||||||
|
this.createExcalidrawReactApp = this.createExcalidrawReactApp.bind(this);
|
||||||
|
this.onChangeHandler = this.onChangeHandler.bind(this);
|
||||||
|
this.isNewSceneVersion = this.isNewSceneVersion.bind(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
static getType() {
|
||||||
|
return "canvas";
|
||||||
|
}
|
||||||
|
|
||||||
|
doRender() {
|
||||||
|
this.$widget = $(TPL);
|
||||||
|
|
||||||
|
this.$widget.toggleClass("full-height", true); // only add
|
||||||
|
this.$render = this.$widget.find('.canvas-render');
|
||||||
|
const documentStyle = window.getComputedStyle(document.documentElement);
|
||||||
|
this.themeStyle = documentStyle.getPropertyValue('--theme-style')?.trim();
|
||||||
|
|
||||||
|
libraryLoader
|
||||||
|
.requireLibrary(libraryLoader.EXCALIDRAW)
|
||||||
|
.then(() => {
|
||||||
|
const React = window.React;
|
||||||
|
const ReactDOM = window.ReactDOM;
|
||||||
|
const renderElement = this.$render.get(0);
|
||||||
|
|
||||||
|
ReactDOM.unmountComponentAtNode(renderElement);
|
||||||
|
ReactDOM.render(React.createElement(this.createExcalidrawReactApp), renderElement);
|
||||||
|
});
|
||||||
|
|
||||||
|
return this.$widget;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* called to populate the widget container with the note content
|
||||||
|
*
|
||||||
|
* @param {note} note
|
||||||
|
*/
|
||||||
|
async doRefresh(note) {
|
||||||
|
// see if note changed, since we do not get a new class for a new note
|
||||||
|
const noteChanged = this.currentNoteId !== note.noteId;
|
||||||
|
if (noteChanged) {
|
||||||
|
// reset scene to omit unnecessary onchange handler
|
||||||
|
this.currentSceneVersion = this.SCENE_VERSION_INITIAL;
|
||||||
|
}
|
||||||
|
this.currentNoteId = note.noteId;
|
||||||
|
|
||||||
|
// get note from backend and put into canvas
|
||||||
|
const noteComplement = await froca.getNoteComplement(note.noteId);
|
||||||
|
|
||||||
|
// before we load content into excalidraw, make sure excalidraw has loaded
|
||||||
|
while (!this.excalidrawRef || !this.excalidrawRef.current) {
|
||||||
|
console.log("excalidrawRef not yet loaded, sleep 200ms...");
|
||||||
|
await sleep(200);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* new and empty note - make sure that canvas is empty.
|
||||||
|
* If we do not set it manually, we occasionally get some "bleeding" from another
|
||||||
|
* note into this fresh note. Probably due to that this note-instance does not get
|
||||||
|
* newly instantiated?
|
||||||
|
*/
|
||||||
|
if (this.excalidrawRef.current && noteComplement.content?.trim() === "") {
|
||||||
|
const sceneData = {
|
||||||
|
elements: [],
|
||||||
|
appState: {
|
||||||
|
theme: this.themeStyle
|
||||||
|
},
|
||||||
|
collaborators: []
|
||||||
|
};
|
||||||
|
|
||||||
|
this.excalidrawRef.current.updateScene(sceneData);
|
||||||
|
}
|
||||||
|
else if (this.excalidrawRef.current && noteComplement.content) {
|
||||||
|
// load saved content into excalidraw canvas
|
||||||
|
let content;
|
||||||
|
|
||||||
|
try {
|
||||||
|
content = JSON.parse(noteComplement.content || "");
|
||||||
|
} catch(err) {
|
||||||
|
console.error("Error parsing content. Probably note.type changed",
|
||||||
|
"Starting with empty canvas"
|
||||||
|
, note, noteComplement, err);
|
||||||
|
|
||||||
|
content = {
|
||||||
|
elements: [],
|
||||||
|
appState: {},
|
||||||
|
files: [],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const {elements, appState, files} = content;
|
||||||
|
|
||||||
|
appState.theme = this.themeStyle;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* use widths and offsets of current view, since stored appState has the state from
|
||||||
|
* previous edit. using the stored state would lead to pointer mismatch.
|
||||||
|
*/
|
||||||
|
const boundingClientRect = this.excalidrawWrapperRef.current.getBoundingClientRect();
|
||||||
|
appState.width = boundingClientRect.width;
|
||||||
|
appState.height = boundingClientRect.height;
|
||||||
|
appState.offsetLeft = boundingClientRect.left;
|
||||||
|
appState.offsetTop = boundingClientRect.top;
|
||||||
|
|
||||||
|
const sceneData = {
|
||||||
|
elements,
|
||||||
|
appState,
|
||||||
|
collaborators: []
|
||||||
|
};
|
||||||
|
|
||||||
|
// files are expected in an array when loading. they are stored as an key-index object
|
||||||
|
// see example for loading here:
|
||||||
|
// https://github.com/excalidraw/excalidraw/blob/c5a7723185f6ca05e0ceb0b0d45c4e3fbcb81b2a/src/packages/excalidraw/example/App.js#L68
|
||||||
|
const fileArray = [];
|
||||||
|
for (const fileId in files) {
|
||||||
|
const file = files[fileId];
|
||||||
|
// TODO: dataURL is replaceable with a trilium image url
|
||||||
|
// maybe we can save normal images (pasted) with base64 data url, and trilium images
|
||||||
|
// with their respective url! nice
|
||||||
|
// file.dataURL = "http://localhost:8080/api/images/ltjOiU8nwoZx/start.png";
|
||||||
|
fileArray.push(file);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.excalidrawRef.current.updateScene(sceneData);
|
||||||
|
this.excalidrawRef.current.addFiles(fileArray);
|
||||||
|
}
|
||||||
|
|
||||||
|
// set initial scene version
|
||||||
|
if (this.currentSceneVersion === this.SCENE_VERSION_INITIAL) {
|
||||||
|
this.currentSceneVersion = this.getSceneVersion();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* gets data from widget container that will be sent via spacedUpdate.scheduleUpdate();
|
||||||
|
* this is automatically called after this.saveData();
|
||||||
|
*/
|
||||||
|
async getContent() {
|
||||||
|
const elements = this.excalidrawRef.current.getSceneElements();
|
||||||
|
const appState = this.excalidrawRef.current.getAppState();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A file is not deleted, even though removed from canvas. therefore we only keep
|
||||||
|
* files that are referenced by an element. Maybe this will change with new excalidraw version?
|
||||||
|
*/
|
||||||
|
const files = this.excalidrawRef.current.getFiles();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* parallel svg export to combat bitrot and enable rendering image for note inclusion,
|
||||||
|
* preview and share.
|
||||||
|
*/
|
||||||
|
const svg = await window.Excalidraw.exportToSvg({
|
||||||
|
elements,
|
||||||
|
appState,
|
||||||
|
exportPadding: 5, // 5 px padding
|
||||||
|
metadata: 'trilium-export',
|
||||||
|
files
|
||||||
|
});
|
||||||
|
const svgString = svg.outerHTML;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* workaround until https://github.com/excalidraw/excalidraw/pull/5065 is merged and published
|
||||||
|
*/
|
||||||
|
const svgSafeString = this.replaceExternalAssets(svgString);
|
||||||
|
|
||||||
|
const activeFiles = {};
|
||||||
|
elements.forEach((element) => {
|
||||||
|
if (element.fileId) {
|
||||||
|
activeFiles[element.fileId] = files[element.fileId];
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const content = {
|
||||||
|
_meta: "This note has type `canvas`. It uses excalidraw and stores an exported svg alongside.",
|
||||||
|
elements, // excalidraw
|
||||||
|
appState, // excalidraw
|
||||||
|
files: activeFiles, // excalidraw
|
||||||
|
svg: svgSafeString, // not needed for excalidraw, used for note_short, content, and image api
|
||||||
|
};
|
||||||
|
|
||||||
|
return JSON.stringify(content);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* save content to backend
|
||||||
|
* spacedUpdate is kind of a debouncer.
|
||||||
|
*/
|
||||||
|
saveData() {
|
||||||
|
this.spacedUpdate.scheduleUpdate();
|
||||||
|
}
|
||||||
|
|
||||||
|
onChangeHandler() {
|
||||||
|
// changeHandler is called upon any tiny change in excalidraw. button clicked, hover, etc.
|
||||||
|
// make sure only when a new element is added, we actually save something.
|
||||||
|
const isNewSceneVersion = this.isNewSceneVersion();
|
||||||
|
/**
|
||||||
|
* FIXME: however, we might want to make an exception, if viewport changed, since viewport
|
||||||
|
* is desired to save? (add) and appState background, and some things
|
||||||
|
*/
|
||||||
|
|
||||||
|
// upon updateScene, onchange is called, even though "nothing really changed" that is worth saving
|
||||||
|
const isNotInitialScene = this.currentSceneVersion !== this.SCENE_VERSION_INITIAL;
|
||||||
|
|
||||||
|
const shouldSave = isNewSceneVersion && isNotInitialScene;
|
||||||
|
|
||||||
|
if (shouldSave) {
|
||||||
|
this.updateSceneVersion();
|
||||||
|
this.saveData();
|
||||||
|
} else {
|
||||||
|
// do nothing
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
createExcalidrawReactApp() {
|
||||||
|
const React = window.React;
|
||||||
|
const Excalidraw = window.Excalidraw;
|
||||||
|
|
||||||
|
const excalidrawRef = React.useRef(null);
|
||||||
|
this.excalidrawRef = excalidrawRef;
|
||||||
|
const excalidrawWrapperRef = React.useRef(null);
|
||||||
|
this.excalidrawWrapperRef = excalidrawWrapperRef;
|
||||||
|
const [dimensions, setDimensions] = React.useState({
|
||||||
|
width: undefined,
|
||||||
|
height: undefined
|
||||||
|
});
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
const dimensions = {
|
||||||
|
width: excalidrawWrapperRef.current.getBoundingClientRect().width,
|
||||||
|
height: excalidrawWrapperRef.current.getBoundingClientRect().height
|
||||||
|
};
|
||||||
|
setDimensions(dimensions);
|
||||||
|
|
||||||
|
const onResize = () => {
|
||||||
|
const dimensions = {
|
||||||
|
width: excalidrawWrapperRef.current.getBoundingClientRect().width,
|
||||||
|
height: excalidrawWrapperRef.current.getBoundingClientRect().height
|
||||||
|
};
|
||||||
|
setDimensions(dimensions);
|
||||||
|
};
|
||||||
|
|
||||||
|
window.addEventListener("resize", onResize);
|
||||||
|
|
||||||
|
return () => window.removeEventListener("resize", onResize);
|
||||||
|
}, [excalidrawWrapperRef]);
|
||||||
|
|
||||||
|
const onLinkOpen = React.useCallback((element, event) => {
|
||||||
|
const link = element.link;
|
||||||
|
const { nativeEvent } = event.detail;
|
||||||
|
const isNewTab = nativeEvent.ctrlKey || nativeEvent.metaKey;
|
||||||
|
const isNewWindow = nativeEvent.shiftKey;
|
||||||
|
const isInternalLink = link.startsWith("/")
|
||||||
|
|| link.includes(window.location.origin);
|
||||||
|
|
||||||
|
if (isInternalLink && !isNewTab && !isNewWindow) {
|
||||||
|
// signal that we're handling the redirect ourselves
|
||||||
|
event.preventDefault();
|
||||||
|
// do a custom redirect, such as passing to react-router
|
||||||
|
// ...
|
||||||
|
} else {
|
||||||
|
// open in same tab
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return React.createElement(
|
||||||
|
React.Fragment,
|
||||||
|
null,
|
||||||
|
React.createElement(
|
||||||
|
"div",
|
||||||
|
{
|
||||||
|
className: "excalidraw-wrapper",
|
||||||
|
ref: excalidrawWrapperRef
|
||||||
|
},
|
||||||
|
React.createElement(Excalidraw.default, {
|
||||||
|
// this makes sure that 1) manual theme switch button is hidden 2) theme stays as it should after opening menu
|
||||||
|
theme: this.themeStyle,
|
||||||
|
ref: excalidrawRef,
|
||||||
|
width: dimensions.width,
|
||||||
|
height: dimensions.height,
|
||||||
|
onPaste: (data, event) => {
|
||||||
|
console.log("Verbose: excalidraw internal paste. No trilium action implemented.", data, event);
|
||||||
|
},
|
||||||
|
onChange: debounce(this.onChangeHandler, this.DEBOUNCE_TIME_ONCHANGEHANDLER),
|
||||||
|
viewModeEnabled: false,
|
||||||
|
zenModeEnabled: false,
|
||||||
|
gridModeEnabled: false,
|
||||||
|
isCollaborating: false,
|
||||||
|
detectScroll: false,
|
||||||
|
handleKeyboardGlobally: false,
|
||||||
|
autoFocus: true,
|
||||||
|
onLinkOpen,
|
||||||
|
})
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* needed to ensure, that multipleOnChangeHandler calls do not trigger a safe.
|
||||||
|
* we compare the scene version as suggested in:
|
||||||
|
* https://github.com/excalidraw/excalidraw/issues/3014#issuecomment-778115329
|
||||||
|
*
|
||||||
|
* info: sceneVersions are not incrementing. it seems to be a pseudo-random number
|
||||||
|
*/
|
||||||
|
isNewSceneVersion() {
|
||||||
|
const sceneVersion = this.getSceneVersion();
|
||||||
|
|
||||||
|
return this.currentSceneVersion === this.SCENE_VERSION_INITIAL // initial scene version update
|
||||||
|
|| this.currentSceneVersion !== sceneVersion // ensure scene changed
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
getSceneVersion() {
|
||||||
|
if (this.excalidrawRef) {
|
||||||
|
const elements = this.excalidrawRef.current.getSceneElements();
|
||||||
|
return window.Excalidraw.getSceneVersion(elements);
|
||||||
|
} else {
|
||||||
|
return this.SCENE_VERSION_ERROR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
updateSceneVersion() {
|
||||||
|
this.currentSceneVersion = this.getSceneVersion();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* replaces exlicraw.com with own assets
|
||||||
|
*
|
||||||
|
* workaround until https://github.com/excalidraw/excalidraw/pull/5065 is merged and published
|
||||||
|
* needed for v0.11.0
|
||||||
|
*
|
||||||
|
* @param {string} string
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
replaceExternalAssets = (string) => {
|
||||||
|
let result = string;
|
||||||
|
// exlidraw.com asset in react usage
|
||||||
|
result = result.replaceAll("https://excalidraw.com/", window.EXCALIDRAW_ASSET_PATH+"excalidraw-assets/");
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -170,4 +170,14 @@ export default class EditableCodeTypeWidget extends TypeWidget {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async executeWithCodeEditorEvent({resolve, ntxId}) {
|
||||||
|
if (!this.isNoteContext(ntxId)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await this.initialized;
|
||||||
|
|
||||||
|
resolve(this.codeEditor);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -229,14 +229,18 @@ export default class EditableTextTypeWidget extends AbstractTextTypeWidget {
|
|||||||
return !selection.isCollapsed;
|
return !selection.isCollapsed;
|
||||||
}
|
}
|
||||||
|
|
||||||
async executeInActiveEditorEvent({callback}) {
|
async executeWithTextEditorEvent({callback, resolve, ntxId}) {
|
||||||
if (!this.isActive()) {
|
if (!this.isNoteContext(ntxId)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
await this.initialized;
|
await this.initialized;
|
||||||
|
|
||||||
callback(this.textEditor);
|
if (callback) {
|
||||||
|
callback(this.textEditor);
|
||||||
|
}
|
||||||
|
|
||||||
|
resolve(this.textEditor);
|
||||||
}
|
}
|
||||||
|
|
||||||
addLinkToTextCommand() {
|
addLinkToTextCommand() {
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ const TPL = `
|
|||||||
<style>
|
<style>
|
||||||
.note-detail-readonly-code {
|
.note-detail-readonly-code {
|
||||||
min-height: 50px;
|
min-height: 50px;
|
||||||
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
.note-detail-readonly-code-content {
|
.note-detail-readonly-code-content {
|
||||||
@@ -30,4 +31,14 @@ export default class ReadOnlyCodeTypeWidget extends TypeWidget {
|
|||||||
|
|
||||||
this.$content.text(noteComplement.content);
|
this.$content.text(noteComplement.content);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async executeWithContentElementEvent({resolve, ntxId}) {
|
||||||
|
if (!this.isNoteContext(ntxId)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await this.initialized;
|
||||||
|
|
||||||
|
resolve(this.$content);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ const TPL = `
|
|||||||
padding-top: 10px;
|
padding-top: 10px;
|
||||||
font-family: var(--detail-font-family);
|
font-family: var(--detail-font-family);
|
||||||
min-height: 50px;
|
min-height: 50px;
|
||||||
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
.note-detail-readonly-text p:first-child, .note-detail-readonly-text::before {
|
.note-detail-readonly-text p:first-child, .note-detail-readonly-text::before {
|
||||||
@@ -114,4 +115,14 @@ export default class ReadOnlyTextTypeWidget extends AbstractTextTypeWidget {
|
|||||||
async refreshIncludedNoteEvent({noteId}) {
|
async refreshIncludedNoteEvent({noteId}) {
|
||||||
this.refreshIncludedNote(this.$content, noteId);
|
this.refreshIncludedNote(this.$content, noteId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async executeWithContentElementEvent({resolve, ntxId}) {
|
||||||
|
if (!this.isNoteContext(ntxId)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await this.initialized;
|
||||||
|
|
||||||
|
resolve(this.$content);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -38,6 +38,9 @@ export default class TypeWidget extends NoteContextAwareWidget {
|
|||||||
return this.$widget.is(":visible") && this.noteContext?.ntxId === appContext.tabManager.activeNtxId;
|
return this.$widget.is(":visible") && this.noteContext?.ntxId === appContext.tabManager.activeNtxId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @returns {Promise|*} promise resolving content or directly the content
|
||||||
|
*/
|
||||||
getContent() {}
|
getContent() {}
|
||||||
|
|
||||||
focus() {}
|
focus() {}
|
||||||
|
|||||||
@@ -241,8 +241,8 @@ body .CodeMirror {
|
|||||||
background-color: #eeeeee
|
background-color: #eeeeee
|
||||||
}
|
}
|
||||||
|
|
||||||
.CodeMirror pre.CodeMirror-placeholder {
|
.CodeMirror pre.CodeMirror-placeholder {
|
||||||
color: #999 !important;
|
color: #999 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
#sql-console-query {
|
#sql-console-query {
|
||||||
@@ -359,7 +359,9 @@ pre:not(.CodeMirror-line) {
|
|||||||
|
|
||||||
.go-to-selected-note-button.disabled, .go-to-selected-note-button.disabled:hover {
|
.go-to-selected-note-button.disabled, .go-to-selected-note-button.disabled:hover {
|
||||||
cursor: inherit;
|
cursor: inherit;
|
||||||
color: var(--button-disabled-background-color) !important;
|
color: var(--button-disabled-text-color) !important;
|
||||||
|
background-color: var(--button-disabled-background-color) !important;
|
||||||
|
text-decoration: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.note-autocomplete-input {
|
.note-autocomplete-input {
|
||||||
@@ -966,3 +968,8 @@ input {
|
|||||||
.note-split.full-content-width {
|
.note-split.full-content-width {
|
||||||
max-width: 999999px;
|
max-width: 999999px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
button.close:hover {
|
||||||
|
text-shadow: none;
|
||||||
|
color: currentColor;
|
||||||
|
}
|
||||||
|
|||||||
@@ -21,10 +21,11 @@
|
|||||||
--more-accented-background-color: #777;
|
--more-accented-background-color: #777;
|
||||||
|
|
||||||
--button-background-color: transparent;
|
--button-background-color: transparent;
|
||||||
--button-disabled-background-color: #222;
|
|
||||||
--button-border-color: #ccc;
|
--button-border-color: #ccc;
|
||||||
--button-text-color: currentColor;
|
--button-text-color: currentColor;
|
||||||
--button-border-radius: 5px;
|
--button-border-radius: 5px;
|
||||||
|
--button-disabled-background-color: transparent;
|
||||||
|
--button-disabled-text-color: #999;
|
||||||
|
|
||||||
--primary-button-background-color: #888;
|
--primary-button-background-color: #888;
|
||||||
--primary-button-text-color: white;
|
--primary-button-text-color: white;
|
||||||
@@ -79,3 +80,7 @@ body ::-webkit-calendar-picker-indicator {
|
|||||||
body .CodeMirror {
|
body .CodeMirror {
|
||||||
filter: invert(90%) hue-rotate(180deg);
|
filter: invert(90%) hue-rotate(180deg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.excalidraw.theme--dark {
|
||||||
|
--theme-filter: invert(80%) hue-rotate(180deg) !important;
|
||||||
|
}
|
||||||
|
|||||||
@@ -25,10 +25,11 @@ html {
|
|||||||
--more-accented-background-color: #ddd;
|
--more-accented-background-color: #ddd;
|
||||||
|
|
||||||
--button-background-color: transparent;
|
--button-background-color: transparent;
|
||||||
--button-disabled-background-color: #ddd;
|
|
||||||
--button-border-color: #ddd;
|
--button-border-color: #ddd;
|
||||||
--button-text-color: black;
|
--button-text-color: black;
|
||||||
--button-border-radius: 5px;
|
--button-border-radius: 5px;
|
||||||
|
--button-disabled-background-color: #ddd;
|
||||||
|
--button-disabled-text-color: black;
|
||||||
|
|
||||||
--primary-button-background-color: #6c757d;
|
--primary-button-background-color: #6c757d;
|
||||||
--primary-button-text-color: white;
|
--primary-button-text-color: white;
|
||||||
|
|||||||
@@ -157,28 +157,25 @@ span.fancytree-node.fancytree-active-clone:not(.fancytree-active) .fancytree-tit
|
|||||||
padding-left: 20px;
|
padding-left: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
span.fancytree-active {
|
||||||
|
color: var(--active-item-text-color) !important;
|
||||||
|
background-color: var(--active-item-background-color) !important;
|
||||||
|
border-color: transparent; /* invisible border */
|
||||||
|
border-radius: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
span.fancytree-active .fancytree-title {
|
span.fancytree-active .fancytree-title {
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
border: 0;
|
border: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
span.fancytree-active {
|
span.fancytree-selected {
|
||||||
border-color: var(--main-border-color) !important;
|
border-color: var(--main-border-color) !important;
|
||||||
border-radius: 5px;
|
border-radius: 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
span.fancytree-active, span.fancytree-active.fancytree-selected {
|
span.fancytree-selected .fancytree-title {
|
||||||
color: var(--active-item-text-color) !important;
|
text-decoration: underline;
|
||||||
background-color: var(--active-item-background-color) !important;
|
|
||||||
border-color: var(--main-background-color) !important; /* invisible border */
|
|
||||||
border-radius: 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
span.fancytree-selected {
|
|
||||||
color: var(--hover-item-text-color) !important;
|
|
||||||
background-color: var(--hover-item-background-color) !important;
|
|
||||||
border-color: var(--main-background-color) !important; /* invisible border */
|
|
||||||
border-radius: 5px;
|
|
||||||
font-style: italic;
|
font-style: italic;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -21,7 +21,8 @@ function updateFile(req) {
|
|||||||
return [404, `Note ${noteId} doesn't exist.`];
|
return [404, `Note ${noteId} doesn't exist.`];
|
||||||
}
|
}
|
||||||
|
|
||||||
noteRevisionService.createNoteRevision(note);
|
note.saveNoteRevision();
|
||||||
|
noteRevisionService.protectNoteRevisions(note);
|
||||||
|
|
||||||
note.mime = file.mimetype.toLowerCase();
|
note.mime = file.mimetype.toLowerCase();
|
||||||
note.save();
|
note.save();
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ function returnImage(req, res) {
|
|||||||
if (!image) {
|
if (!image) {
|
||||||
return res.sendStatus(404);
|
return res.sendStatus(404);
|
||||||
}
|
}
|
||||||
else if (image.type !== 'image') {
|
else if (!["image", "canvas"].includes(image.type)){
|
||||||
return res.sendStatus(400);
|
return res.sendStatus(400);
|
||||||
}
|
}
|
||||||
else if (image.isDeleted || image.data === null) {
|
else if (image.isDeleted || image.data === null) {
|
||||||
@@ -19,10 +19,27 @@ function returnImage(req, res) {
|
|||||||
return res.send(fs.readFileSync(RESOURCE_DIR + '/db/image-deleted.png'));
|
return res.send(fs.readFileSync(RESOURCE_DIR + '/db/image-deleted.png'));
|
||||||
}
|
}
|
||||||
|
|
||||||
res.set('Content-Type', image.mime);
|
/**
|
||||||
res.set("Cache-Control", "no-cache, no-store, must-revalidate");
|
* special "image" type. the canvas is actually type application/json
|
||||||
|
* to avoid bitrot and enable usage as referenced image the svg is included.
|
||||||
res.send(image.getContent());
|
*/
|
||||||
|
if (image.type === 'canvas') {
|
||||||
|
const content = image.getContent();
|
||||||
|
try {
|
||||||
|
const data = JSON.parse(content);
|
||||||
|
|
||||||
|
const svg = data.svg || '<svg />'
|
||||||
|
res.set('Content-Type', "image/svg+xml");
|
||||||
|
res.set("Cache-Control", "no-cache, no-store, must-revalidate");
|
||||||
|
res.send(svg);
|
||||||
|
} catch(err) {
|
||||||
|
res.status(500).send("there was an error parsing excalidraw to svg");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
res.set('Content-Type', image.mime);
|
||||||
|
res.set("Cache-Control", "no-cache, no-store, must-revalidate");
|
||||||
|
res.send(image.getContent());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function uploadImage(req) {
|
function uploadImage(req) {
|
||||||
|
|||||||
@@ -83,6 +83,10 @@ function logoutFromProtectedSession() {
|
|||||||
ws.sendMessageToAllClients({ type: 'protectedSessionLogout' });
|
ws.sendMessageToAllClients({ type: 'protectedSessionLogout' });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function touchProtectedSession() {
|
||||||
|
protectedSessionService.touchProtectedSession();
|
||||||
|
}
|
||||||
|
|
||||||
function token(req) {
|
function token(req) {
|
||||||
const password = req.body.password;
|
const password = req.body.password;
|
||||||
|
|
||||||
@@ -92,7 +96,7 @@ function token(req) {
|
|||||||
|
|
||||||
// for backwards compatibility with Sender which does not send the name
|
// for backwards compatibility with Sender which does not send the name
|
||||||
const tokenName = req.body.tokenName || "Trilium Sender / Web Clipper";
|
const tokenName = req.body.tokenName || "Trilium Sender / Web Clipper";
|
||||||
|
|
||||||
const {authToken} = etapiTokenService.createToken(tokenName);
|
const {authToken} = etapiTokenService.createToken(tokenName);
|
||||||
|
|
||||||
return { token: authToken };
|
return { token: authToken };
|
||||||
@@ -102,5 +106,6 @@ module.exports = {
|
|||||||
loginSync,
|
loginSync,
|
||||||
loginToProtectedSession,
|
loginToProtectedSession,
|
||||||
logoutFromProtectedSession,
|
logoutFromProtectedSession,
|
||||||
|
touchProtectedSession,
|
||||||
token
|
token
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -213,7 +213,7 @@ function findExcerpts(sourceNote, referencedNoteId) {
|
|||||||
|
|
||||||
let centerEl = linkEl;
|
let centerEl = linkEl;
|
||||||
|
|
||||||
while (centerEl.tagName !== 'BODY' && centerEl.parentElement.textContent.length <= EXCERPT_CHAR_LIMIT) {
|
while (centerEl.tagName !== 'BODY' && centerEl.parentElement?.textContent?.length <= EXCERPT_CHAR_LIMIT) {
|
||||||
centerEl = centerEl.parentElement;
|
centerEl = centerEl.parentElement;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -97,7 +97,8 @@ function restoreNoteRevision(req) {
|
|||||||
if (noteRevision) {
|
if (noteRevision) {
|
||||||
const note = noteRevision.getNote();
|
const note = noteRevision.getNote();
|
||||||
|
|
||||||
noteRevisionService.createNoteRevision(note);
|
note.saveNoteRevision();
|
||||||
|
noteRevisionService.protectNoteRevisions(note);
|
||||||
|
|
||||||
note.title = noteRevision.title;
|
note.title = noteRevision.title;
|
||||||
note.setContent(noteRevision.getContent());
|
note.setContent(noteRevision.getContent());
|
||||||
|
|||||||
@@ -206,7 +206,7 @@ function changeTitle(req) {
|
|||||||
const noteTitleChanged = note.title !== title;
|
const noteTitleChanged = note.title !== title;
|
||||||
|
|
||||||
if (noteTitleChanged) {
|
if (noteTitleChanged) {
|
||||||
noteService.saveNoteRevision(note);
|
noteService.saveNoteRevisionIfNeeded(note);
|
||||||
}
|
}
|
||||||
|
|
||||||
note.title = title;
|
note.title = title;
|
||||||
@@ -294,7 +294,8 @@ function uploadModifiedFile(req) {
|
|||||||
|
|
||||||
log.info(`Updating note '${noteId}' with content from ${filePath}`);
|
log.info(`Updating note '${noteId}' with content from ${filePath}`);
|
||||||
|
|
||||||
noteRevisionService.createNoteRevision(note);
|
note.saveNoteRevision();
|
||||||
|
noteRevisionService.protectNoteRevisions(note);
|
||||||
|
|
||||||
const fileContent = fs.readFileSync(filePath);
|
const fileContent = fs.readFileSync(filePath);
|
||||||
|
|
||||||
|
|||||||
@@ -55,7 +55,8 @@ const ALLOWED_OPTIONS = new Set([
|
|||||||
'weeklyBackupEnabled',
|
'weeklyBackupEnabled',
|
||||||
'monthlyBackupEnabled',
|
'monthlyBackupEnabled',
|
||||||
'maxContentWidth',
|
'maxContentWidth',
|
||||||
'compressImages'
|
'compressImages',
|
||||||
|
'downloadImagesAutomatically'
|
||||||
]);
|
]);
|
||||||
|
|
||||||
function getOptions() {
|
function getOptions() {
|
||||||
|
|||||||
@@ -9,15 +9,16 @@ const noteRevisionService = require("../../services/note_revisions");
|
|||||||
const branchService = require("../../services/branches");
|
const branchService = require("../../services/branches");
|
||||||
const cloningService = require("../../services/cloning");
|
const cloningService = require("../../services/cloning");
|
||||||
const {formatAttrForSearch} = require("../../services/attribute_formatter");
|
const {formatAttrForSearch} = require("../../services/attribute_formatter");
|
||||||
|
const utils = require("../../services/utils.js");
|
||||||
|
|
||||||
async function searchFromNoteInt(note) {
|
function searchFromNoteInt(note) {
|
||||||
let searchResultNoteIds;
|
let searchResultNoteIds;
|
||||||
|
|
||||||
const searchScript = note.getRelationValue('searchScript');
|
const searchScript = note.getRelationValue('searchScript');
|
||||||
const searchString = note.getLabelValue('searchString');
|
const searchString = note.getLabelValue('searchString');
|
||||||
|
|
||||||
if (searchScript) {
|
if (searchScript) {
|
||||||
searchResultNoteIds = await searchFromRelation(note, 'searchScript');
|
searchResultNoteIds = searchFromRelation(note, 'searchScript');
|
||||||
} else {
|
} else {
|
||||||
const searchContext = new SearchContext({
|
const searchContext = new SearchContext({
|
||||||
fastSearch: note.hasLabel('fastSearch'),
|
fastSearch: note.hasLabel('fastSearch'),
|
||||||
@@ -61,7 +62,9 @@ async function searchFromNote(req) {
|
|||||||
|
|
||||||
const ACTION_HANDLERS = {
|
const ACTION_HANDLERS = {
|
||||||
deleteNote: (action, note) => {
|
deleteNote: (action, note) => {
|
||||||
note.markAsDeleted();
|
const deleteId = 'searchbulkaction-' + utils.randomString(10);
|
||||||
|
|
||||||
|
note.deleteNote(deleteId);
|
||||||
},
|
},
|
||||||
deleteNoteRevisions: (action, note) => {
|
deleteNoteRevisions: (action, note) => {
|
||||||
noteRevisionService.eraseNoteRevisions(note.getNoteRevisions().map(rev => rev.noteRevisionId));
|
noteRevisionService.eraseNoteRevisions(note.getNoteRevisions().map(rev => rev.noteRevisionId));
|
||||||
@@ -149,7 +152,7 @@ function getActions(note) {
|
|||||||
.filter(a => !!a);
|
.filter(a => !!a);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function searchAndExecute(req) {
|
function searchAndExecute(req) {
|
||||||
const note = becca.getNote(req.params.noteId);
|
const note = becca.getNote(req.params.noteId);
|
||||||
|
|
||||||
if (!note) {
|
if (!note) {
|
||||||
@@ -165,7 +168,7 @@ async function searchAndExecute(req) {
|
|||||||
return [400, `Note ${req.params.noteId} is not a search note.`]
|
return [400, `Note ${req.params.noteId} is not a search note.`]
|
||||||
}
|
}
|
||||||
|
|
||||||
const searchResultNoteIds = await searchFromNoteInt(note);
|
const searchResultNoteIds = searchFromNoteInt(note);
|
||||||
|
|
||||||
const actions = getActions(note);
|
const actions = getActions(note);
|
||||||
|
|
||||||
|
|||||||
@@ -285,6 +285,7 @@ function register(app) {
|
|||||||
apiRoute(POST, '/api/special-notes/search-note', specialNotesRoute.createSearchNote);
|
apiRoute(POST, '/api/special-notes/search-note', specialNotesRoute.createSearchNote);
|
||||||
apiRoute(POST, '/api/special-notes/save-search-note', specialNotesRoute.saveSearchNote);
|
apiRoute(POST, '/api/special-notes/save-search-note', specialNotesRoute.saveSearchNote);
|
||||||
|
|
||||||
|
// :filename is not used by trilium, but instead used for "save as" to assign a human readable filename
|
||||||
route(GET, '/api/images/:noteId/:filename', [auth.checkApiAuthOrElectron], imageRoute.returnImage);
|
route(GET, '/api/images/:noteId/:filename', [auth.checkApiAuthOrElectron], imageRoute.returnImage);
|
||||||
route(POST, '/api/images', [auth.checkApiAuthOrElectron, uploadMiddleware, csrfMiddleware], imageRoute.uploadImage, apiResultHandler);
|
route(POST, '/api/images', [auth.checkApiAuthOrElectron, uploadMiddleware, csrfMiddleware], imageRoute.uploadImage, apiResultHandler);
|
||||||
route(PUT, '/api/images/:noteId', [auth.checkApiAuthOrElectron, uploadMiddleware, csrfMiddleware], imageRoute.updateImage, apiResultHandler);
|
route(PUT, '/api/images/:noteId', [auth.checkApiAuthOrElectron, uploadMiddleware, csrfMiddleware], imageRoute.updateImage, apiResultHandler);
|
||||||
@@ -358,6 +359,7 @@ function register(app) {
|
|||||||
route(POST, '/api/login/sync', [], loginApiRoute.loginSync, apiResultHandler);
|
route(POST, '/api/login/sync', [], loginApiRoute.loginSync, apiResultHandler);
|
||||||
// this is for entering protected mode so user has to be already logged-in (that's the reason we don't require username)
|
// this is for entering protected mode so user has to be already logged-in (that's the reason we don't require username)
|
||||||
apiRoute(POST, '/api/login/protected', loginApiRoute.loginToProtectedSession);
|
apiRoute(POST, '/api/login/protected', loginApiRoute.loginToProtectedSession);
|
||||||
|
apiRoute(POST, '/api/login/protected/touch', loginApiRoute.touchProtectedSession);
|
||||||
apiRoute(POST, '/api/logout/protected', loginApiRoute.logoutFromProtectedSession);
|
apiRoute(POST, '/api/logout/protected', loginApiRoute.logoutFromProtectedSession);
|
||||||
|
|
||||||
route(POST, '/api/login/token', [], loginApiRoute.token, apiResultHandler);
|
route(POST, '/api/login/token', [], loginApiRoute.token, apiResultHandler);
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ const build = require('./build');
|
|||||||
const packageJson = require('../../package');
|
const packageJson = require('../../package');
|
||||||
const {TRILIUM_DATA_DIR} = require('./data_dir');
|
const {TRILIUM_DATA_DIR} = require('./data_dir');
|
||||||
|
|
||||||
const APP_DB_VERSION = 194;
|
const APP_DB_VERSION = 195;
|
||||||
const SYNC_VERSION = 25;
|
const SYNC_VERSION = 25;
|
||||||
const CLIPPER_PROTOCOL_VERSION = "1.0";
|
const CLIPPER_PROTOCOL_VERSION = "1.0";
|
||||||
|
|
||||||
|
|||||||
@@ -210,7 +210,7 @@ function BackendScriptApi(currentNote, apiParams) {
|
|||||||
* @property {string} parentNoteId - MANDATORY
|
* @property {string} parentNoteId - MANDATORY
|
||||||
* @property {string} title - MANDATORY
|
* @property {string} title - MANDATORY
|
||||||
* @property {string|buffer} content - MANDATORY
|
* @property {string|buffer} content - MANDATORY
|
||||||
* @property {string} type - text, code, file, image, search, book, relation-map - MANDATORY
|
* @property {string} type - text, code, file, image, search, book, relation-map, canvas - MANDATORY
|
||||||
* @property {string} mime - value is derived from default mimes for type
|
* @property {string} mime - value is derived from default mimes for type
|
||||||
* @property {boolean} isProtected - default is false
|
* @property {boolean} isProtected - default is false
|
||||||
* @property {boolean} isExpanded - default is false
|
* @property {boolean} isExpanded - default is false
|
||||||
@@ -426,6 +426,15 @@ function BackendScriptApi(currentNote, apiParams) {
|
|||||||
* @return {{syncVersion, appVersion, buildRevision, dbVersion, dataDirectory, buildDate}|*} - object representing basic info about running Trilium version
|
* @return {{syncVersion, appVersion, buildRevision, dbVersion, dataDirectory, buildDate}|*} - object representing basic info about running Trilium version
|
||||||
*/
|
*/
|
||||||
this.getAppInfo = () => appInfo
|
this.getAppInfo = () => appInfo
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This object contains "at your risk" and "no BC guarantees" objects for advanced use cases.
|
||||||
|
*
|
||||||
|
* @type {{becca: Becca}}
|
||||||
|
*/
|
||||||
|
this.__private = {
|
||||||
|
becca
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = BackendScriptApi;
|
module.exports = BackendScriptApi;
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
module.exports = { buildDate:"2022-05-01T23:18:35+02:00", buildRevision: "b3763eed610fa3f2aabbcbdbd21efca704a5dd08" };
|
module.exports = { buildDate:"2022-06-13T23:41:52+02:00", buildRevision: "f0ab1fb5a1c45bf15eae46a72b46ebf132eee531" };
|
||||||
|
|||||||
@@ -50,6 +50,7 @@ module.exports = [
|
|||||||
{ type: 'label', name: 'shareDisallowRobotIndexing' },
|
{ type: 'label', name: 'shareDisallowRobotIndexing' },
|
||||||
{ type: 'label', name: 'displayRelations' },
|
{ type: 'label', name: 'displayRelations' },
|
||||||
{ type: 'label', name: 'hideRelations' },
|
{ type: 'label', name: 'hideRelations' },
|
||||||
|
{ type: 'label', name: 'titleTemplate', isDangerous: true },
|
||||||
|
|
||||||
// relation names
|
// relation names
|
||||||
{ type: 'relation', name: 'internalLink' },
|
{ type: 'relation', name: 'internalLink' },
|
||||||
@@ -59,6 +60,9 @@ module.exports = [
|
|||||||
{ type: 'relation', name: 'runOnNoteCreation', isDangerous: true },
|
{ type: 'relation', name: 'runOnNoteCreation', isDangerous: true },
|
||||||
{ type: 'relation', name: 'runOnNoteTitleChange', isDangerous: true },
|
{ type: 'relation', name: 'runOnNoteTitleChange', isDangerous: true },
|
||||||
{ type: 'relation', name: 'runOnNoteChange', isDangerous: true },
|
{ type: 'relation', name: 'runOnNoteChange', isDangerous: true },
|
||||||
|
{ type: 'relation', name: 'runOnNoteDeletion', isDangerous: true },
|
||||||
|
{ type: 'relation', name: 'runOnBranchCreation', isDangerous: true },
|
||||||
|
{ type: 'relation', name: 'runOnBranchDeletion', isDangerous: true },
|
||||||
{ type: 'relation', name: 'runOnChildNoteCreation', isDangerous: true },
|
{ type: 'relation', name: 'runOnChildNoteCreation', isDangerous: true },
|
||||||
{ type: 'relation', name: 'runOnAttributeCreation', isDangerous: true },
|
{ type: 'relation', name: 'runOnAttributeCreation', isDangerous: true },
|
||||||
{ type: 'relation', name: 'runOnAttributeChange', isDangerous: true },
|
{ type: 'relation', name: 'runOnAttributeChange', isDangerous: true },
|
||||||
|
|||||||
@@ -56,41 +56,66 @@ class ConsistencyChecks {
|
|||||||
childToParents[childNoteId].push(parentNoteId);
|
childToParents[childNoteId].push(parentNoteId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @returns {boolean} true if cycle was found and we should try again */
|
||||||
const checkTreeCycle = (noteId, path) => {
|
const checkTreeCycle = (noteId, path) => {
|
||||||
if (noteId === 'root') {
|
if (noteId === 'root') {
|
||||||
return;
|
return false;
|
||||||
}
|
|
||||||
|
|
||||||
if (!childToParents[noteId] || childToParents[noteId].length === 0) {
|
|
||||||
logError(`No parents found for note ${noteId}`);
|
|
||||||
|
|
||||||
this.unrecoveredConsistencyErrors = true;
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const parentNoteId of childToParents[noteId]) {
|
for (const parentNoteId of childToParents[noteId]) {
|
||||||
if (path.includes(parentNoteId)) {
|
if (path.includes(parentNoteId)) {
|
||||||
logError(`Tree cycle detected at parent-child relationship: ${parentNoteId} - ${noteId}, whole path: ${path}`);
|
if (this.autoFix) {
|
||||||
|
const branch = becca.getBranchFromChildAndParent(noteId, parentNoteId);
|
||||||
|
branch.markAsDeleted('cycle-autofix');
|
||||||
|
logFix(`Branch '${branch.branchId}' between child '${noteId}' and parent '${parentNoteId}' has been deleted since it was causing a tree cycle.`);
|
||||||
|
|
||||||
this.unrecoveredConsistencyErrors = true;
|
return true;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
logError(`Tree cycle detected at parent-child relationship: ${parentNoteId} - ${noteId}, whole path: ${path}`);
|
||||||
|
|
||||||
|
this.unrecoveredConsistencyErrors = true;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
const newPath = path.slice();
|
const newPath = path.slice();
|
||||||
newPath.push(noteId);
|
newPath.push(noteId);
|
||||||
|
|
||||||
checkTreeCycle(parentNoteId, newPath);
|
const retryNeeded = checkTreeCycle(parentNoteId, newPath);
|
||||||
|
|
||||||
|
if (retryNeeded) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
};
|
};
|
||||||
|
|
||||||
const noteIds = Object.keys(childToParents);
|
const noteIds = Object.keys(childToParents);
|
||||||
|
|
||||||
for (const noteId of noteIds) {
|
for (const noteId of noteIds) {
|
||||||
checkTreeCycle(noteId, []);
|
const retryNeeded = checkTreeCycle(noteId, []);
|
||||||
|
|
||||||
|
if (retryNeeded) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (childToParents['root'].length !== 1 || childToParents['root'][0] !== 'none') {
|
return false;
|
||||||
logError('Incorrect root parent: ' + JSON.stringify(childToParents['root']));
|
}
|
||||||
this.unrecoveredConsistencyErrors = true;
|
|
||||||
|
checkAndRepairTreeCycles() {
|
||||||
|
let treeFixed = false;
|
||||||
|
|
||||||
|
while (this.checkTreeCycles()) {
|
||||||
|
// fixing cycle means deleting branches, we might need to create a new branch to recover the note
|
||||||
|
this.findExistencyIssues();
|
||||||
|
|
||||||
|
treeFixed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (treeFixed) {
|
||||||
|
this.reloadNeeded = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -342,26 +367,30 @@ class ConsistencyChecks {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
this.findAndFixIssues(`
|
if (sqlInit.getDbSize() < 500000) {
|
||||||
SELECT notes.noteId, notes.type, notes.mime
|
// querying for "content IS NULL" is expensive since content is not indexed. See e.g. https://github.com/zadam/trilium/issues/2887
|
||||||
FROM notes
|
|
||||||
JOIN note_contents USING (noteId)
|
|
||||||
WHERE isDeleted = 0
|
|
||||||
AND isProtected = 0
|
|
||||||
AND content IS NULL`,
|
|
||||||
({noteId, type, mime}) => {
|
|
||||||
if (this.autoFix) {
|
|
||||||
const note = becca.getNote(noteId);
|
|
||||||
const blankContent = getBlankContent(false, type, mime);
|
|
||||||
note.setContent(blankContent);
|
|
||||||
|
|
||||||
this.reloadNeeded = true;
|
this.findAndFixIssues(`
|
||||||
|
SELECT notes.noteId, notes.type, notes.mime
|
||||||
|
FROM notes
|
||||||
|
JOIN note_contents USING (noteId)
|
||||||
|
WHERE isDeleted = 0
|
||||||
|
AND isProtected = 0
|
||||||
|
AND content IS NULL`,
|
||||||
|
({noteId, type, mime}) => {
|
||||||
|
if (this.autoFix) {
|
||||||
|
const note = becca.getNote(noteId);
|
||||||
|
const blankContent = getBlankContent(false, type, mime);
|
||||||
|
note.setContent(blankContent);
|
||||||
|
|
||||||
logFix(`Note ${noteId} content was set to "${blankContent}" since it was null even though it is not deleted`);
|
this.reloadNeeded = true;
|
||||||
} else {
|
|
||||||
logError(`Note ${noteId} content is null even though it is not deleted`);
|
logFix(`Note ${noteId} content was set to "${blankContent}" since it was null even though it is not deleted`);
|
||||||
}
|
} else {
|
||||||
});
|
logError(`Note ${noteId} content is null even though it is not deleted`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
this.findAndFixIssues(`
|
this.findAndFixIssues(`
|
||||||
SELECT note_revisions.noteRevisionId
|
SELECT note_revisions.noteRevisionId
|
||||||
@@ -646,7 +675,7 @@ class ConsistencyChecks {
|
|||||||
if (!this.unrecoveredConsistencyErrors) {
|
if (!this.unrecoveredConsistencyErrors) {
|
||||||
// we run this only if basic checks passed since this assumes basic data consistency
|
// we run this only if basic checks passed since this assumes basic data consistency
|
||||||
|
|
||||||
this.checkTreeCycles();
|
this.checkAndRepairTreeCycles();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.reloadNeeded) {
|
if (this.reloadNeeded) {
|
||||||
|
|||||||
@@ -135,7 +135,6 @@ function fillAllEntityChanges() {
|
|||||||
fillEntityChanges("branches", "branchId");
|
fillEntityChanges("branches", "branchId");
|
||||||
fillEntityChanges("note_revisions", "noteRevisionId");
|
fillEntityChanges("note_revisions", "noteRevisionId");
|
||||||
fillEntityChanges("note_revision_contents", "noteRevisionId");
|
fillEntityChanges("note_revision_contents", "noteRevisionId");
|
||||||
fillEntityChanges("recent_notes", "noteId");
|
|
||||||
fillEntityChanges("attributes", "attributeId");
|
fillEntityChanges("attributes", "attributeId");
|
||||||
fillEntityChanges("etapi_tokens", "etapiTokenId");
|
fillEntityChanges("etapi_tokens", "etapiTokenId");
|
||||||
fillEntityChanges("options", "name", 'isSynced = 1');
|
fillEntityChanges("options", "name", 'isSynced = 1');
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ function exportSingleNote(taskContext, branch, format, res) {
|
|||||||
extension = mimeTypes.extension(note.mime) || 'code';
|
extension = mimeTypes.extension(note.mime) || 'code';
|
||||||
mime = note.mime;
|
mime = note.mime;
|
||||||
}
|
}
|
||||||
else if (note.type === 'relation-map' || note.type === 'search') {
|
else if (note.type === 'relation-map' || note.type === 'canvas' || note.type === 'search') {
|
||||||
payload = content;
|
payload = content;
|
||||||
extension = 'json';
|
extension = 'json';
|
||||||
mime = 'application/json';
|
mime = 'application/json';
|
||||||
|
|||||||
@@ -49,6 +49,7 @@ eventService.subscribe([ eventService.ENTITY_CHANGED, eventService.ENTITY_DELETE
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (entityName === 'notes') {
|
else if (entityName === 'notes') {
|
||||||
|
// ENTITY_DELETED won't trigger anything since all branches/attributes are already deleted at this point
|
||||||
runAttachedRelations(entity, 'runOnNoteChange', entity);
|
runAttachedRelations(entity, 'runOnNoteChange', entity);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -94,6 +95,9 @@ eventService.subscribe(eventService.ENTITY_CREATED, ({ entityName, entity }) =>
|
|||||||
handleSortedAttribute(entity);
|
handleSortedAttribute(entity);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if (entityName === 'branches') {
|
||||||
|
runAttachedRelations(entity.getNote(), 'runOnBranchCreation', entity);
|
||||||
|
}
|
||||||
else if (entityName === 'notes') {
|
else if (entityName === 'notes') {
|
||||||
runAttachedRelations(entity, 'runOnNoteCreation', entity);
|
runAttachedRelations(entity, 'runOnNoteCreation', entity);
|
||||||
}
|
}
|
||||||
@@ -167,4 +171,12 @@ eventService.subscribe(eventService.ENTITY_DELETED, ({ entityName, entity }) =>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (entityName === 'branches') {
|
||||||
|
runAttachedRelations(entity.getNote(), 'runOnBranchDeletion', entity);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
runAttachedRelations
|
||||||
|
};
|
||||||
|
|||||||
@@ -68,7 +68,7 @@ function updateImage(noteId, uploadBuffer, originalName) {
|
|||||||
|
|
||||||
const note = becca.getNote(noteId);
|
const note = becca.getNote(noteId);
|
||||||
|
|
||||||
noteRevisionService.createNoteRevision(note);
|
note.saveNoteRevision();
|
||||||
noteRevisionService.protectNoteRevisions(note);
|
noteRevisionService.protectNoteRevisions(note);
|
||||||
|
|
||||||
note.setLabel('originalFileName', originalName);
|
note.setLabel('originalFileName', originalName);
|
||||||
|
|||||||
@@ -98,25 +98,25 @@ const DEFAULT_KEYBOARD_ACTIONS = [
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
actionName: "moveNoteUp",
|
actionName: "moveNoteUp",
|
||||||
defaultShortcuts: ["CommandOrControl+Up"],
|
defaultShortcuts: isMac ? ["Alt+Up"] : ["CommandOrControl+Up"],
|
||||||
description: "Move note up",
|
description: "Move note up",
|
||||||
scope: "note-tree"
|
scope: "note-tree"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
actionName: "moveNoteDown",
|
actionName: "moveNoteDown",
|
||||||
defaultShortcuts: ["CommandOrControl+Down"],
|
defaultShortcuts: isMac ? ["Alt+Down"] : ["CommandOrControl+Down"],
|
||||||
description: "Move note down",
|
description: "Move note down",
|
||||||
scope: "note-tree"
|
scope: "note-tree"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
actionName: "moveNoteUpInHierarchy",
|
actionName: "moveNoteUpInHierarchy",
|
||||||
defaultShortcuts: ["CommandOrControl+Left"],
|
defaultShortcuts: isMac ? ["Alt+Left"] : ["CommandOrControl+Left"],
|
||||||
description: "Move note up in hierarchy",
|
description: "Move note up in hierarchy",
|
||||||
scope: "note-tree"
|
scope: "note-tree"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
actionName: "moveNoteDownInHierarchy",
|
actionName: "moveNoteDownInHierarchy",
|
||||||
defaultShortcuts: ["CommandOrControl+Right"],
|
defaultShortcuts: isMac ? ["Alt+Right"] : ["CommandOrControl+Right"],
|
||||||
description: "Move note down in hierarchy",
|
description: "Move note down in hierarchy",
|
||||||
scope: "note-tree"
|
scope: "note-tree"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -102,7 +102,7 @@ function isDbUpToDate() {
|
|||||||
async function migrateIfNecessary() {
|
async function migrateIfNecessary() {
|
||||||
const currentDbVersion = getDbVersion();
|
const currentDbVersion = getDbVersion();
|
||||||
|
|
||||||
if (currentDbVersion > appInfo.dbVersion) {
|
if (currentDbVersion > appInfo.dbVersion && process.env.TRILIUM_IGNORE_DB_VERSION !== 'true') {
|
||||||
log.error(`Current DB version ${currentDbVersion} is newer than app db version ${appInfo.dbVersion} which means that it was created by newer and incompatible version of Trilium. Upgrade to latest version of Trilium to resolve this issue.`);
|
log.error(`Current DB version ${currentDbVersion} is newer than app db version ${appInfo.dbVersion} which means that it was created by newer and incompatible version of Trilium. Upgrade to latest version of Trilium to resolve this issue.`);
|
||||||
|
|
||||||
utils.crash();
|
utils.crash();
|
||||||
|
|||||||
@@ -1,9 +1,8 @@
|
|||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
const NoteRevision = require('../becca/entities/note_revision');
|
|
||||||
const dateUtils = require('./date_utils');
|
|
||||||
const log = require('./log');
|
const log = require('./log');
|
||||||
const sql = require('./sql');
|
const sql = require('./sql');
|
||||||
|
const protectedSession = require("./protected_session");
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {Note} note
|
* @param {Note} note
|
||||||
@@ -11,6 +10,12 @@ const sql = require('./sql');
|
|||||||
function protectNoteRevisions(note) {
|
function protectNoteRevisions(note) {
|
||||||
for (const revision of note.getNoteRevisions()) {
|
for (const revision of note.getNoteRevisions()) {
|
||||||
if (note.isProtected !== revision.isProtected) {
|
if (note.isProtected !== revision.isProtected) {
|
||||||
|
if (!protectedSession.isProtectedSessionAvailable()) {
|
||||||
|
log.error("Protected session is not available to fix note revisions.");
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const content = revision.getContent();
|
const content = revision.getContent();
|
||||||
|
|
||||||
@@ -30,46 +35,6 @@ function protectNoteRevisions(note) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {Note} note
|
|
||||||
* @return {NoteRevision|null}
|
|
||||||
*/
|
|
||||||
function createNoteRevision(note) {
|
|
||||||
if (note.hasLabel("disableVersioning")) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
const content = note.getContent();
|
|
||||||
|
|
||||||
if (!content || (Buffer.isBuffer(content) && content.byteLength === 0)) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
const contentMetadata = note.getContentMetadata();
|
|
||||||
|
|
||||||
const noteRevision = new NoteRevision({
|
|
||||||
noteId: note.noteId,
|
|
||||||
// title and text should be decrypted now
|
|
||||||
title: note.title,
|
|
||||||
type: note.type,
|
|
||||||
mime: note.mime,
|
|
||||||
isProtected: false, // will be fixed in the protectNoteRevisions() call
|
|
||||||
utcDateLastEdited: note.utcDateModified > contentMetadata.utcDateModified
|
|
||||||
? note.utcDateModified
|
|
||||||
: contentMetadata.utcDateModified,
|
|
||||||
utcDateCreated: dateUtils.utcNowDateTime(),
|
|
||||||
utcDateModified: dateUtils.utcNowDateTime(),
|
|
||||||
dateLastEdited: note.dateModified > contentMetadata.dateModified
|
|
||||||
? note.dateModified
|
|
||||||
: contentMetadata.dateModified,
|
|
||||||
dateCreated: dateUtils.localNowDateTime()
|
|
||||||
}).save();
|
|
||||||
|
|
||||||
noteRevision.setContent(content);
|
|
||||||
|
|
||||||
return noteRevision;
|
|
||||||
}
|
|
||||||
|
|
||||||
function eraseNoteRevisions(noteRevisionIdsToErase) {
|
function eraseNoteRevisions(noteRevisionIdsToErase) {
|
||||||
if (noteRevisionIdsToErase.length === 0) {
|
if (noteRevisionIdsToErase.length === 0) {
|
||||||
return;
|
return;
|
||||||
@@ -86,6 +51,5 @@ function eraseNoteRevisions(noteRevisionIdsToErase) {
|
|||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
protectNoteRevisions,
|
protectNoteRevisions,
|
||||||
createNoteRevision,
|
|
||||||
eraseNoteRevisions
|
eraseNoteRevisions
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ module.exports = [
|
|||||||
'search',
|
'search',
|
||||||
'relation-map',
|
'relation-map',
|
||||||
'book',
|
'book',
|
||||||
'note-map',
|
'note-map',
|
||||||
'mermaid'
|
'mermaid',
|
||||||
|
'canvas'
|
||||||
];
|
];
|
||||||
@@ -17,7 +17,7 @@ const becca = require('../becca/becca');
|
|||||||
const Branch = require('../becca/entities/branch');
|
const Branch = require('../becca/entities/branch');
|
||||||
const Note = require('../becca/entities/note');
|
const Note = require('../becca/entities/note');
|
||||||
const Attribute = require('../becca/entities/attribute');
|
const Attribute = require('../becca/entities/attribute');
|
||||||
const TaskContext = require("./task_context.js");
|
const dayjs = require("dayjs");
|
||||||
|
|
||||||
function getNewNotePosition(parentNoteId) {
|
function getNewNotePosition(parentNoteId) {
|
||||||
const note = becca.notes[parentNoteId];
|
const note = becca.notes[parentNoteId];
|
||||||
@@ -53,7 +53,7 @@ function deriveMime(type, mime) {
|
|||||||
mime = 'text/html';
|
mime = 'text/html';
|
||||||
} else if (type === 'code' || type === 'mermaid') {
|
} else if (type === 'code' || type === 'mermaid') {
|
||||||
mime = 'text/plain';
|
mime = 'text/plain';
|
||||||
} else if (['relation-map', 'search'].includes(type)) {
|
} else if (['relation-map', 'search', 'canvas'].includes(type)) {
|
||||||
mime = 'application/json';
|
mime = 'application/json';
|
||||||
} else if (['render', 'book'].includes(type)) {
|
} else if (['render', 'book'].includes(type)) {
|
||||||
mime = '';
|
mime = '';
|
||||||
@@ -79,12 +79,34 @@ function copyChildAttributes(parentNote, childNote) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getNewNoteTitle(parentNote) {
|
||||||
|
let title = "new note";
|
||||||
|
|
||||||
|
const titleTemplate = parentNote.getLabelValue('titleTemplate');
|
||||||
|
|
||||||
|
if (titleTemplate !== null) {
|
||||||
|
try {
|
||||||
|
const now = dayjs(cls.getLocalNowDateTime() || new Date());
|
||||||
|
|
||||||
|
// "officially" injected values:
|
||||||
|
// - now
|
||||||
|
// - parentNote
|
||||||
|
|
||||||
|
title = eval('`' + titleTemplate + '`');
|
||||||
|
} catch (e) {
|
||||||
|
log.error(`Title template of note '${parentNote.noteId}' failed with: ${e.message}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return title;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Following object properties are mandatory:
|
* Following object properties are mandatory:
|
||||||
* - {string} parentNoteId
|
* - {string} parentNoteId
|
||||||
* - {string} title
|
* - {string} title
|
||||||
* - {*} content
|
* - {*} content
|
||||||
* - {string} type - text, code, file, image, search, book, relation-map, render
|
* - {string} type - text, code, file, image, search, book, relation-map, canvas, render
|
||||||
*
|
*
|
||||||
* Following are optional (have defaults)
|
* Following are optional (have defaults)
|
||||||
* - {string} mime - value is derived from default mimes for type
|
* - {string} mime - value is derived from default mimes for type
|
||||||
@@ -104,8 +126,7 @@ function createNewNote(params) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (params.title === null || params.title === undefined) {
|
if (params.title === null || params.title === undefined) {
|
||||||
// empty title is allowed since it's possible to create such in the UI
|
params.title = getNewNoteTitle(parentNote);
|
||||||
throw new Error(`Note title must be set`);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (params.content === null || params.content === undefined) {
|
if (params.content === null || params.content === undefined) {
|
||||||
@@ -302,6 +323,10 @@ function replaceUrl(content, url, imageNote) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function downloadImages(noteId, content) {
|
function downloadImages(noteId, content) {
|
||||||
|
if (!optionService.getOptionBool("downloadImagesAutomatically")) {
|
||||||
|
return content;
|
||||||
|
}
|
||||||
|
|
||||||
const imageRe = /<img[^>]*?\ssrc=['"]([^'">]+)['"]/ig;
|
const imageRe = /<img[^>]*?\ssrc=['"]([^'">]+)['"]/ig;
|
||||||
let imageMatch;
|
let imageMatch;
|
||||||
|
|
||||||
@@ -466,7 +491,7 @@ function saveLinks(note, content) {
|
|||||||
return content;
|
return content;
|
||||||
}
|
}
|
||||||
|
|
||||||
function saveNoteRevision(note) {
|
function saveNoteRevisionIfNeeded(note) {
|
||||||
// files and images are versioned separately
|
// files and images are versioned separately
|
||||||
if (note.type === 'file' || note.type === 'image' || note.hasLabel('disableVersioning')) {
|
if (note.type === 'file' || note.type === 'image' || note.hasLabel('disableVersioning')) {
|
||||||
return;
|
return;
|
||||||
@@ -483,7 +508,7 @@ function saveNoteRevision(note) {
|
|||||||
const msSinceDateCreated = now.getTime() - dateUtils.parseDateTime(note.utcDateCreated).getTime();
|
const msSinceDateCreated = now.getTime() - dateUtils.parseDateTime(note.utcDateCreated).getTime();
|
||||||
|
|
||||||
if (!existingNoteRevisionId && msSinceDateCreated >= noteRevisionSnapshotTimeInterval * 1000) {
|
if (!existingNoteRevisionId && msSinceDateCreated >= noteRevisionSnapshotTimeInterval * 1000) {
|
||||||
noteRevisionService.createNoteRevision(note);
|
note.saveNoteRevision();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -494,7 +519,7 @@ function updateNote(noteId, noteUpdates) {
|
|||||||
throw new Error(`Note '${noteId}' is not available for change!`);
|
throw new Error(`Note '${noteId}' is not available for change!`);
|
||||||
}
|
}
|
||||||
|
|
||||||
saveNoteRevision(note);
|
saveNoteRevisionIfNeeded(note);
|
||||||
|
|
||||||
// if protected status changed, then we need to encrypt/decrypt the content anyway
|
// if protected status changed, then we need to encrypt/decrypt the content anyway
|
||||||
if (['file', 'image'].includes(note.type) && note.isProtected !== noteUpdates.isProtected) {
|
if (['file', 'image'].includes(note.type) && note.isProtected !== noteUpdates.isProtected) {
|
||||||
@@ -885,6 +910,6 @@ module.exports = {
|
|||||||
triggerNoteTitleChanged,
|
triggerNoteTitleChanged,
|
||||||
eraseDeletedNotesNow,
|
eraseDeletedNotesNow,
|
||||||
eraseNotesWithDeleteId,
|
eraseNotesWithDeleteId,
|
||||||
saveNoteRevision,
|
saveNoteRevisionIfNeeded,
|
||||||
downloadImages
|
downloadImages
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -29,13 +29,13 @@ function initNotSyncedOptions(initialized, opts = {}) {
|
|||||||
optionService.createOption('lastSyncedPush', '0', false);
|
optionService.createOption('lastSyncedPush', '0', false);
|
||||||
|
|
||||||
let theme = 'dark'; // default based on the poll in https://github.com/zadam/trilium/issues/2516
|
let theme = 'dark'; // default based on the poll in https://github.com/zadam/trilium/issues/2516
|
||||||
|
|
||||||
if (utils.isElectron()) {
|
if (utils.isElectron()) {
|
||||||
const {nativeTheme} = require('electron');
|
const {nativeTheme} = require('electron');
|
||||||
|
|
||||||
theme = nativeTheme.shouldUseDarkColors ? 'dark' : 'light';
|
theme = nativeTheme.shouldUseDarkColors ? 'dark' : 'light';
|
||||||
}
|
}
|
||||||
|
|
||||||
optionService.createOption('theme', theme, false);
|
optionService.createOption('theme', theme, false);
|
||||||
|
|
||||||
optionService.createOption('syncServerHost', opts.syncServerHost || '', false);
|
optionService.createOption('syncServerHost', opts.syncServerHost || '', false);
|
||||||
@@ -83,7 +83,8 @@ const defaultOptions = [
|
|||||||
{ name: 'weeklyBackupEnabled', value: 'true', isSynced: false },
|
{ name: 'weeklyBackupEnabled', value: 'true', isSynced: false },
|
||||||
{ name: 'monthlyBackupEnabled', value: 'true', isSynced: false },
|
{ name: 'monthlyBackupEnabled', value: 'true', isSynced: false },
|
||||||
{ name: 'maxContentWidth', value: '1200', isSynced: false },
|
{ name: 'maxContentWidth', value: '1200', isSynced: false },
|
||||||
{ name: 'compressImages', value: 'true', isSynced: true }
|
{ name: 'compressImages', value: 'true', isSynced: true },
|
||||||
|
{ name: 'downloadImagesAutomatically', value: 'true', isSynced: true }
|
||||||
];
|
];
|
||||||
|
|
||||||
function initStartupOptions() {
|
function initStartupOptions() {
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
const log = require('./log');
|
const log = require('./log');
|
||||||
const dataEncryptionService = require('./data_encryption');
|
const dataEncryptionService = require('./data_encryption');
|
||||||
|
const options = require("./options");
|
||||||
|
|
||||||
let dataKey = null;
|
let dataKey = null;
|
||||||
|
|
||||||
@@ -54,6 +55,28 @@ function decryptString(cipherText) {
|
|||||||
return dataEncryptionService.decryptString(getDataKey(), cipherText);
|
return dataEncryptionService.decryptString(getDataKey(), cipherText);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let lastProtectedSessionOperationDate = null;
|
||||||
|
|
||||||
|
function touchProtectedSession() {
|
||||||
|
if (isProtectedSessionAvailable()) {
|
||||||
|
lastProtectedSessionOperationDate = Date.now();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function checkProtectedSessionExpiration() {
|
||||||
|
const protectedSessionTimeout = options.getOptionInt('protectedSessionTimeout');
|
||||||
|
if (isProtectedSessionAvailable()
|
||||||
|
&& lastProtectedSessionOperationDate
|
||||||
|
&& Date.now() - lastProtectedSessionOperationDate > protectedSessionTimeout * 1000) {
|
||||||
|
|
||||||
|
resetDataKey();
|
||||||
|
|
||||||
|
log.info("Expiring protected session");
|
||||||
|
|
||||||
|
require('./ws').reloadFrontend();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
setDataKey,
|
setDataKey,
|
||||||
resetDataKey,
|
resetDataKey,
|
||||||
@@ -61,5 +84,7 @@ module.exports = {
|
|||||||
encrypt,
|
encrypt,
|
||||||
decrypt,
|
decrypt,
|
||||||
decryptString,
|
decryptString,
|
||||||
decryptNotes
|
decryptNotes,
|
||||||
|
touchProtectedSession,
|
||||||
|
checkProtectedSessionExpiration
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ const log = require('./log');
|
|||||||
const sql = require("./sql");
|
const sql = require("./sql");
|
||||||
const becca = require("../becca/becca");
|
const becca = require("../becca/becca");
|
||||||
const specialNotesService = require("../services/special_notes");
|
const specialNotesService = require("../services/special_notes");
|
||||||
|
const protectedSessionService = require("../services/protected_session");
|
||||||
|
|
||||||
function getRunAtHours(note) {
|
function getRunAtHours(note) {
|
||||||
try {
|
try {
|
||||||
@@ -59,4 +60,6 @@ sqlInit.dbReady.then(() => {
|
|||||||
|
|
||||||
setTimeout(cls.wrap(() => specialNotesService.createMissingSpecialNotes()), 10 * 1000);
|
setTimeout(cls.wrap(() => specialNotesService.createMissingSpecialNotes()), 10 * 1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setInterval(() => protectedSessionService.checkProtectedSessionExpiration(), 30000);
|
||||||
});
|
});
|
||||||
|
|||||||
116
src/services/search/expressions/note_content_fulltext.js
Normal file
116
src/services/search/expressions/note_content_fulltext.js
Normal file
@@ -0,0 +1,116 @@
|
|||||||
|
"use strict";
|
||||||
|
|
||||||
|
const Expression = require('./expression');
|
||||||
|
const NoteSet = require('../note_set');
|
||||||
|
const log = require('../../log');
|
||||||
|
const becca = require('../../../becca/becca');
|
||||||
|
const protectedSessionService = require('../../protected_session');
|
||||||
|
const striptags = require('striptags');
|
||||||
|
const utils = require("../../utils");
|
||||||
|
|
||||||
|
const ALLOWED_OPERATORS = ['*=*', '=', '*=', '=*', '%='];
|
||||||
|
|
||||||
|
const cachedRegexes = {};
|
||||||
|
|
||||||
|
function getRegex(str) {
|
||||||
|
if (!(str in cachedRegexes)) {
|
||||||
|
cachedRegexes[str] = new RegExp(str, 'ms'); // multiline, dot-all
|
||||||
|
}
|
||||||
|
|
||||||
|
return cachedRegexes[str];
|
||||||
|
}
|
||||||
|
|
||||||
|
class NoteContentFulltextExp extends Expression {
|
||||||
|
constructor(operator, {tokens, raw, flatText}) {
|
||||||
|
super();
|
||||||
|
|
||||||
|
if (!ALLOWED_OPERATORS.includes(operator)) {
|
||||||
|
throw new Error(`Note content can be searched only with operators: ` + ALLOWED_OPERATORS.join(", ") + `, operator ${operator} given.`);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.operator = operator;
|
||||||
|
this.tokens = tokens;
|
||||||
|
this.raw = !!raw;
|
||||||
|
this.flatText = !!flatText;
|
||||||
|
}
|
||||||
|
|
||||||
|
execute(inputNoteSet) {
|
||||||
|
const resultNoteSet = new NoteSet();
|
||||||
|
const sql = require('../../sql');
|
||||||
|
|
||||||
|
for (let {noteId, type, mime, content, isProtected} of sql.iterateRows(`
|
||||||
|
SELECT noteId, type, mime, content, isProtected
|
||||||
|
FROM notes JOIN note_contents USING (noteId)
|
||||||
|
WHERE type IN ('text', 'code', 'mermaid') AND isDeleted = 0`)) {
|
||||||
|
|
||||||
|
if (!inputNoteSet.hasNoteId(noteId) || !(noteId in becca.notes)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isProtected) {
|
||||||
|
if (!protectedSessionService.isProtectedSessionAvailable()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
content = protectedSessionService.decryptString(content);
|
||||||
|
} catch (e) {
|
||||||
|
log.info(`Cannot decrypt content of note ${noteId}`);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
content = this.preprocessContent(content, type, mime);
|
||||||
|
|
||||||
|
if (this.tokens.length === 1) {
|
||||||
|
const [token] = this.tokens;
|
||||||
|
|
||||||
|
if ((this.operator === '=' && token === content)
|
||||||
|
|| (this.operator === '*=' && content.endsWith(token))
|
||||||
|
|| (this.operator === '=*' && content.startsWith(token))
|
||||||
|
|| (this.operator === '*=*' && content.includes(token))
|
||||||
|
|| (this.operator === '%=' && getRegex(token).test(content))) {
|
||||||
|
|
||||||
|
resultNoteSet.add(becca.notes[noteId]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
const nonMatchingToken = this.tokens.find(token =>
|
||||||
|
!content.includes(token) &&
|
||||||
|
(
|
||||||
|
// in case of default fulltext search we should consider both title, attrs and content
|
||||||
|
// so e.g. "hello world" should match when "hello" is in title and "world" in content
|
||||||
|
!this.flatText
|
||||||
|
|| !becca.notes[noteId].getFlatText().includes(token)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!nonMatchingToken) {
|
||||||
|
resultNoteSet.add(becca.notes[noteId]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return resultNoteSet;
|
||||||
|
}
|
||||||
|
|
||||||
|
preprocessContent(content, type, mime) {
|
||||||
|
content = utils.normalize(content.toString());
|
||||||
|
|
||||||
|
if (type === 'text' && mime === 'text/html') {
|
||||||
|
if (!this.raw && content.length < 20000) { // striptags is slow for very large notes
|
||||||
|
// allow link to preserve URLs: https://github.com/zadam/trilium/issues/2412
|
||||||
|
content = striptags(content, ['a']);
|
||||||
|
|
||||||
|
// at least the closing tag can be easily stripped
|
||||||
|
content = content.replace(/<\/a>/ig, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
content = content.replace(/ /g, ' ');
|
||||||
|
}
|
||||||
|
|
||||||
|
return content.trim();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = NoteContentFulltextExp;
|
||||||
@@ -1,89 +0,0 @@
|
|||||||
"use strict";
|
|
||||||
|
|
||||||
const Expression = require('./expression');
|
|
||||||
const NoteSet = require('../note_set');
|
|
||||||
const log = require('../../log');
|
|
||||||
const becca = require('../../../becca/becca');
|
|
||||||
const protectedSessionService = require('../../protected_session');
|
|
||||||
const striptags = require('striptags');
|
|
||||||
const utils = require("../../utils");
|
|
||||||
|
|
||||||
// FIXME: create common subclass with NoteContentUnprotectedFulltextExp to avoid duplication
|
|
||||||
class NoteContentProtectedFulltextExp extends Expression {
|
|
||||||
constructor(operator, {tokens, raw, flatText}) {
|
|
||||||
super();
|
|
||||||
|
|
||||||
if (operator !== '*=*') {
|
|
||||||
throw new Error(`Note content can be searched only with *=* operator`);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.tokens = tokens;
|
|
||||||
this.raw = !!raw;
|
|
||||||
this.flatText = !!flatText;
|
|
||||||
}
|
|
||||||
|
|
||||||
execute(inputNoteSet) {
|
|
||||||
const resultNoteSet = new NoteSet();
|
|
||||||
|
|
||||||
if (!protectedSessionService.isProtectedSessionAvailable()) {
|
|
||||||
return resultNoteSet;
|
|
||||||
}
|
|
||||||
|
|
||||||
const sql = require('../../sql');
|
|
||||||
|
|
||||||
for (let {noteId, type, mime, content} of sql.iterateRows(`
|
|
||||||
SELECT noteId, type, mime, content
|
|
||||||
FROM notes JOIN note_contents USING (noteId)
|
|
||||||
WHERE type IN ('text', 'code', 'mermaid') AND isDeleted = 0 AND isProtected = 1`)) {
|
|
||||||
|
|
||||||
if (!inputNoteSet.hasNoteId(noteId) || !(noteId in becca.notes)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
content = protectedSessionService.decryptString(content);
|
|
||||||
}
|
|
||||||
catch (e) {
|
|
||||||
log.info(`Cannot decrypt content of note ${noteId}`);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
content = this.preprocessContent(content, type, mime);
|
|
||||||
|
|
||||||
const nonMatchingToken = this.tokens.find(token =>
|
|
||||||
!content.includes(token) &&
|
|
||||||
(
|
|
||||||
// in case of default fulltext search we should consider both title, attrs and content
|
|
||||||
// so e.g. "hello world" should match when "hello" is in title and "world" in content
|
|
||||||
!this.flatText
|
|
||||||
|| !becca.notes[noteId].getFlatText().includes(token)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!nonMatchingToken) {
|
|
||||||
resultNoteSet.add(becca.notes[noteId]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return resultNoteSet;
|
|
||||||
}
|
|
||||||
|
|
||||||
preprocessContent(content, type, mime) {
|
|
||||||
content = utils.normalize(content.toString());
|
|
||||||
|
|
||||||
if (type === 'text' && mime === 'text/html') {
|
|
||||||
if (!this.raw && content.length < 20000) { // striptags is slow for very large notes
|
|
||||||
// allow link to preserve URLs: https://github.com/zadam/trilium/issues/2412
|
|
||||||
content = striptags(content, ['a']);
|
|
||||||
|
|
||||||
// at least the closing tag can be easily stripped
|
|
||||||
content = content.replace(/<\/a>/ig, "");
|
|
||||||
}
|
|
||||||
|
|
||||||
content = content.replace(/ /g, ' ');
|
|
||||||
}
|
|
||||||
return content;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = NoteContentProtectedFulltextExp;
|
|
||||||
@@ -1,75 +0,0 @@
|
|||||||
"use strict";
|
|
||||||
|
|
||||||
const Expression = require('./expression');
|
|
||||||
const NoteSet = require('../note_set');
|
|
||||||
const becca = require('../../../becca/becca');
|
|
||||||
const striptags = require('striptags');
|
|
||||||
const utils = require("../../utils");
|
|
||||||
|
|
||||||
// FIXME: create common subclass with NoteContentProtectedFulltextExp to avoid duplication
|
|
||||||
class NoteContentUnprotectedFulltextExp extends Expression {
|
|
||||||
constructor(operator, {tokens, raw, flatText}) {
|
|
||||||
super();
|
|
||||||
|
|
||||||
if (operator !== '*=*') {
|
|
||||||
throw new Error(`Note content can be searched only with *=* operator`);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.tokens = tokens;
|
|
||||||
this.raw = !!raw;
|
|
||||||
this.flatText = !!flatText;
|
|
||||||
}
|
|
||||||
|
|
||||||
execute(inputNoteSet) {
|
|
||||||
const resultNoteSet = new NoteSet();
|
|
||||||
|
|
||||||
const sql = require('../../sql');
|
|
||||||
|
|
||||||
for (let {noteId, type, mime, content} of sql.iterateRows(`
|
|
||||||
SELECT noteId, type, mime, content
|
|
||||||
FROM notes JOIN note_contents USING (noteId)
|
|
||||||
WHERE type IN ('text', 'code', 'mermaid') AND isDeleted = 0 AND isProtected = 0`)) {
|
|
||||||
|
|
||||||
if (!inputNoteSet.hasNoteId(noteId) || !(noteId in becca.notes)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
content = this.preprocessContent(content, type, mime);
|
|
||||||
|
|
||||||
const nonMatchingToken = this.tokens.find(token =>
|
|
||||||
!content.includes(token) &&
|
|
||||||
(
|
|
||||||
// in case of default fulltext search we should consider both title, attrs and content
|
|
||||||
// so e.g. "hello world" should match when "hello" is in title and "world" in content
|
|
||||||
!this.flatText
|
|
||||||
|| !becca.notes[noteId].getFlatText().includes(token)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!nonMatchingToken) {
|
|
||||||
resultNoteSet.add(becca.notes[noteId]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return resultNoteSet;
|
|
||||||
}
|
|
||||||
|
|
||||||
preprocessContent(content, type, mime) {
|
|
||||||
content = utils.normalize(content.toString());
|
|
||||||
|
|
||||||
if (type === 'text' && mime === 'text/html') {
|
|
||||||
if (!this.raw && content.length < 20000) { // striptags is slow for very large notes
|
|
||||||
// allow link to preserve URLs: https://github.com/zadam/trilium/issues/2412
|
|
||||||
content = striptags(content, ['a']);
|
|
||||||
|
|
||||||
// at least the closing tag can be easily stripped
|
|
||||||
content = content.replace(/<\/a>/ig, "");
|
|
||||||
}
|
|
||||||
|
|
||||||
content = content.replace(/ /g, ' ');
|
|
||||||
}
|
|
||||||
return content;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = NoteContentUnprotectedFulltextExp;
|
|
||||||
@@ -1,3 +1,13 @@
|
|||||||
|
const cachedRegexes = {};
|
||||||
|
|
||||||
|
function getRegex(str) {
|
||||||
|
if (!(str in cachedRegexes)) {
|
||||||
|
cachedRegexes[str] = new RegExp(str);
|
||||||
|
}
|
||||||
|
|
||||||
|
return cachedRegexes[str];
|
||||||
|
}
|
||||||
|
|
||||||
const stringComparators = {
|
const stringComparators = {
|
||||||
"=": comparedValue => (val => val === comparedValue),
|
"=": comparedValue => (val => val === comparedValue),
|
||||||
"!=": comparedValue => (val => val !== comparedValue),
|
"!=": comparedValue => (val => val !== comparedValue),
|
||||||
@@ -8,6 +18,7 @@ const stringComparators = {
|
|||||||
"*=": comparedValue => (val => val && val.endsWith(comparedValue)),
|
"*=": comparedValue => (val => val && val.endsWith(comparedValue)),
|
||||||
"=*": comparedValue => (val => val && val.startsWith(comparedValue)),
|
"=*": comparedValue => (val => val && val.startsWith(comparedValue)),
|
||||||
"*=*": comparedValue => (val => val && val.includes(comparedValue)),
|
"*=*": comparedValue => (val => val && val.includes(comparedValue)),
|
||||||
|
"%=": comparedValue => (val => val && !!getRegex(comparedValue).test(val)),
|
||||||
};
|
};
|
||||||
|
|
||||||
const numericComparators = {
|
const numericComparators = {
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ function lex(str) {
|
|||||||
let currentWord = '';
|
let currentWord = '';
|
||||||
|
|
||||||
function isSymbolAnOperator(chr) {
|
function isSymbolAnOperator(chr) {
|
||||||
return ['=', '*', '>', '<', '!', "-", "+"].includes(chr);
|
return ['=', '*', '>', '<', '!', "-", "+", '%'].includes(chr);
|
||||||
}
|
}
|
||||||
|
|
||||||
function isPreviousSymbolAnOperator() {
|
function isPreviousSymbolAnOperator() {
|
||||||
@@ -83,7 +83,7 @@ function lex(str) {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
else if (!quotes) {
|
else if (!quotes) {
|
||||||
if (!fulltextEnded && currentWord === 'note' && chr === '.') {
|
if (!fulltextEnded && currentWord === 'note' && chr === '.' && i + 1 < str.length) {
|
||||||
fulltextEnded = true;
|
fulltextEnded = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -12,8 +12,7 @@ const PropertyComparisonExp = require('../expressions/property_comparison');
|
|||||||
const AttributeExistsExp = require('../expressions/attribute_exists');
|
const AttributeExistsExp = require('../expressions/attribute_exists');
|
||||||
const LabelComparisonExp = require('../expressions/label_comparison');
|
const LabelComparisonExp = require('../expressions/label_comparison');
|
||||||
const NoteFlatTextExp = require('../expressions/note_flat_text');
|
const NoteFlatTextExp = require('../expressions/note_flat_text');
|
||||||
const NoteContentProtectedFulltextExp = require('../expressions/note_content_protected_fulltext');
|
const NoteContentFulltextExp = require('../expressions/note_content_fulltext.js');
|
||||||
const NoteContentUnprotectedFulltextExp = require('../expressions/note_content_unprotected_fulltext');
|
|
||||||
const OrderByAndLimitExp = require('../expressions/order_by_and_limit');
|
const OrderByAndLimitExp = require('../expressions/order_by_and_limit');
|
||||||
const AncestorExp = require("../expressions/ancestor");
|
const AncestorExp = require("../expressions/ancestor");
|
||||||
const buildComparator = require('./build_comparator');
|
const buildComparator = require('./build_comparator');
|
||||||
@@ -32,8 +31,7 @@ function getFulltext(tokens, searchContext) {
|
|||||||
if (!searchContext.fastSearch) {
|
if (!searchContext.fastSearch) {
|
||||||
return new OrExp([
|
return new OrExp([
|
||||||
new NoteFlatTextExp(tokens),
|
new NoteFlatTextExp(tokens),
|
||||||
new NoteContentProtectedFulltextExp('*=*', {tokens, flatText: true}),
|
new NoteContentFulltextExp('*=*', {tokens, flatText: true})
|
||||||
new NoteContentUnprotectedFulltextExp('*=*', {tokens, flatText: true})
|
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@@ -42,7 +40,7 @@ function getFulltext(tokens, searchContext) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function isOperator(str) {
|
function isOperator(str) {
|
||||||
return str.match(/^[!=<>*]+$/);
|
return str.match(/^[!=<>*%]+$/);
|
||||||
}
|
}
|
||||||
|
|
||||||
function getExpression(tokens, searchContext, level = 0) {
|
function getExpression(tokens, searchContext, level = 0) {
|
||||||
@@ -140,10 +138,7 @@ function getExpression(tokens, searchContext, level = 0) {
|
|||||||
|
|
||||||
i++;
|
i++;
|
||||||
|
|
||||||
return new OrExp([
|
return new NoteContentFulltextExp(operator, {tokens: [tokens[i].token], raw });
|
||||||
new NoteContentUnprotectedFulltextExp(operator, {tokens: [tokens[i].token], raw }),
|
|
||||||
new NoteContentProtectedFulltextExp(operator, {tokens: [tokens[i].token], raw })
|
|
||||||
]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tokens[i].token === 'parents') {
|
if (tokens[i].token === 'parents') {
|
||||||
@@ -196,8 +191,7 @@ function getExpression(tokens, searchContext, level = 0) {
|
|||||||
|
|
||||||
return new OrExp([
|
return new OrExp([
|
||||||
new PropertyComparisonExp(searchContext, 'title', '*=*', tokens[i].token),
|
new PropertyComparisonExp(searchContext, 'title', '*=*', tokens[i].token),
|
||||||
new NoteContentProtectedFulltextExp('*=*', {tokens: [tokens[i].token]}),
|
new NoteContentFulltextExp('*=*', {tokens: [tokens[i].token]})
|
||||||
new NoteContentUnprotectedFulltextExp('*=*', {tokens: [tokens[i].token]})
|
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -78,6 +78,10 @@ function findResultsWithExpression(expression, searchContext) {
|
|||||||
|
|
||||||
const searchResults = noteSet.notes
|
const searchResults = noteSet.notes
|
||||||
.map(note => {
|
.map(note => {
|
||||||
|
if (note.isDeleted) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
const notePathArray = executionContext.noteIdToNotePath[note.noteId] || beccaService.getSomePath(note);
|
const notePathArray = executionContext.noteIdToNotePath[note.noteId] || beccaService.getSomePath(note);
|
||||||
|
|
||||||
if (!notePathArray) {
|
if (!notePathArray) {
|
||||||
@@ -85,7 +89,8 @@ function findResultsWithExpression(expression, searchContext) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return new SearchResult(notePathArray);
|
return new SearchResult(notePathArray);
|
||||||
});
|
})
|
||||||
|
.filter(note => !!note);
|
||||||
|
|
||||||
for (const res of searchResults) {
|
for (const res of searchResults) {
|
||||||
res.computeScore(searchContext.highlightedTokens);
|
res.computeScore(searchContext.highlightedTokens);
|
||||||
@@ -129,7 +134,7 @@ function parseQueryToExpression(query, searchContext) {
|
|||||||
structuredExpressionTokens,
|
structuredExpressionTokens,
|
||||||
expression
|
expression
|
||||||
};
|
};
|
||||||
|
|
||||||
log.info("Search debug: " + JSON.stringify(searchContext.debugInfo, null, 4));
|
log.info("Search debug: " + JSON.stringify(searchContext.debugInfo, null, 4));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -242,9 +242,9 @@ function transactional(func) {
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
catch (e) {
|
catch (e) {
|
||||||
const entityChanges = cls.getAndClearEntityChangeIds();
|
const entityChangeIds = cls.getAndClearEntityChangeIds();
|
||||||
|
|
||||||
if (entityChanges.length > 0) {
|
if (entityChangeIds.length > 0) {
|
||||||
log.info("Transaction rollback dirtied the becca, forcing reload.");
|
log.info("Transaction rollback dirtied the becca, forcing reload.");
|
||||||
|
|
||||||
require('../becca/becca_loader').load();
|
require('../becca/becca_loader').load();
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user