Compare commits
124 Commits
v0.10.1-be
...
v0.16.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7a9542b4fc | ||
|
|
3a95c9e1bc | ||
|
|
3d2ef6be01 | ||
|
|
d67246699a | ||
|
|
14c704d6db | ||
|
|
4c8eeb2e6f | ||
|
|
c1b245c8b1 | ||
|
|
74202d67bb | ||
|
|
26066f39b1 | ||
|
|
b255cf190c | ||
|
|
bc77b143b0 | ||
|
|
9f0ff6ae7a | ||
|
|
736704c7d6 | ||
|
|
654c116c58 | ||
|
|
89a5cab98f | ||
|
|
c39d0be8cd | ||
|
|
e75b4cd848 | ||
|
|
378e8f35e5 | ||
|
|
bdb5e2f13f | ||
|
|
8211bed449 | ||
|
|
b243632483 | ||
|
|
e4d2513451 | ||
|
|
385144451b | ||
|
|
c8c533844e | ||
|
|
0e69f0c079 | ||
|
|
aee60c444f | ||
|
|
e7a504c66b | ||
|
|
45d9c7164c | ||
|
|
bd913a63a8 | ||
|
|
5a1938c078 | ||
|
|
015cd68756 | ||
|
|
76c0e5b2b8 | ||
|
|
0f8f707acd | ||
|
|
083cccea28 | ||
|
|
31b76b23ce | ||
|
|
af529f82e5 | ||
|
|
fc6669d254 | ||
|
|
c07785be67 | ||
|
|
80d2457b23 | ||
|
|
5dde2752d2 | ||
|
|
8bf4633cd0 | ||
|
|
bd66b8a1c8 | ||
|
|
be51e533fc | ||
|
|
f47ae12019 | ||
|
|
cab54a458f | ||
|
|
a30734f1bc | ||
|
|
7ad9f7b129 | ||
|
|
40a32e6826 | ||
|
|
ab0486aaf1 | ||
|
|
874593a167 | ||
|
|
03bf33630e | ||
|
|
933cce1b94 | ||
|
|
4a6ff573f8 | ||
|
|
1a737f7d19 | ||
|
|
cb69914f09 | ||
|
|
a372cbb2df | ||
|
|
0ce5caefe8 | ||
|
|
94dabb81f6 | ||
|
|
cd45bcfd03 | ||
|
|
49a53f7a45 | ||
|
|
9fa6c0918c | ||
|
|
e8d089e37e | ||
|
|
a931ce25fa | ||
|
|
b507abb4f7 | ||
|
|
66e7c6de62 | ||
|
|
4ce5ea9886 | ||
|
|
8c54b62f07 | ||
|
|
85eb50ed0f | ||
|
|
5ffd621e9d | ||
|
|
df93cb09da | ||
|
|
bbf04209f0 | ||
|
|
834bfa39c7 | ||
|
|
52b445f70b | ||
|
|
7b9b4fbb0c | ||
|
|
5af0ba1fcb | ||
|
|
85a9748291 | ||
|
|
b4005a7ffe | ||
|
|
82de1c88d4 | ||
|
|
1687ed7e0b | ||
|
|
c8b9c7d936 | ||
|
|
d57057ba28 | ||
|
|
66cee8daa4 | ||
|
|
afd7df0942 | ||
|
|
bd6ae33d32 | ||
|
|
70660a0d68 | ||
|
|
cdad18551a | ||
|
|
592c51d1a5 | ||
|
|
6a57b8a7e7 | ||
|
|
7a94e21c54 | ||
|
|
5b43f321e2 | ||
|
|
a4eafb934f | ||
|
|
7b59a665dd | ||
|
|
3d15450ffc | ||
|
|
b0c6d52461 | ||
|
|
2dc16dd29f | ||
|
|
d8924c536b | ||
|
|
3ebbf2cc46 | ||
|
|
f4079604c9 | ||
|
|
1f96a6beab | ||
|
|
b277a250e5 | ||
|
|
5b0e1a644d | ||
|
|
6bb3cfa9a3 | ||
|
|
9720868f5a | ||
|
|
8d8ee2a87a | ||
|
|
542e82ee5d | ||
|
|
0104b19502 | ||
|
|
120888b53e | ||
|
|
d2e2caed62 | ||
|
|
63066802a8 | ||
|
|
6128bb4ff3 | ||
|
|
982796255d | ||
|
|
36b15f474d | ||
|
|
13f71f8967 | ||
|
|
64336ffbee | ||
|
|
b09463d1b2 | ||
|
|
b5e6f46b9c | ||
|
|
08af4a0465 | ||
|
|
8c5df6321f | ||
|
|
d19f044961 | ||
|
|
e378d9f645 | ||
|
|
39dc0f71b4 | ||
|
|
0cef5c6b8c | ||
|
|
9b5a44cef4 | ||
|
|
29769ed91d |
4
.dockerignore
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
node_modules
|
||||||
|
npm-debug.log
|
||||||
|
dist
|
||||||
|
.idea
|
||||||
@@ -1,7 +1,9 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<dataSource name="document.db">
|
<dataSource name="document.db">
|
||||||
<database-model serializer="dbm" rdbms="SQLITE" format-version="4.7">
|
<database-model serializer="dbm" rdbms="SQLITE" format-version="4.9">
|
||||||
<root id="1"/>
|
<root id="1">
|
||||||
|
<ServerVersion>3.16.1</ServerVersion>
|
||||||
|
</root>
|
||||||
<schema id="2" parent="1" name="main">
|
<schema id="2" parent="1" name="main">
|
||||||
<Current>1</Current>
|
<Current>1</Current>
|
||||||
<Visible>1</Visible>
|
<Visible>1</Visible>
|
||||||
@@ -48,539 +50,627 @@
|
|||||||
<NotNull>1</NotNull>
|
<NotNull>1</NotNull>
|
||||||
<DefaultExpression>0</DefaultExpression>
|
<DefaultExpression>0</DefaultExpression>
|
||||||
</column>
|
</column>
|
||||||
<index id="24" parent="6" name="sqlite_autoindex_api_tokens_1">
|
<column id="24" parent="6" name="hash">
|
||||||
|
<Position>5</Position>
|
||||||
|
<DataType>TEXT|0s</DataType>
|
||||||
|
<NotNull>1</NotNull>
|
||||||
|
<DefaultExpression>""</DefaultExpression>
|
||||||
|
</column>
|
||||||
|
<index id="25" parent="6" name="sqlite_autoindex_api_tokens_1">
|
||||||
<NameSurrogate>1</NameSurrogate>
|
<NameSurrogate>1</NameSurrogate>
|
||||||
<ColNames>apiTokenId</ColNames>
|
<ColNames>apiTokenId</ColNames>
|
||||||
<ColumnCollations></ColumnCollations>
|
<ColumnCollations></ColumnCollations>
|
||||||
<Unique>1</Unique>
|
<Unique>1</Unique>
|
||||||
</index>
|
</index>
|
||||||
<key id="25" parent="6">
|
<key id="26" parent="6">
|
||||||
<ColNames>apiTokenId</ColNames>
|
<ColNames>apiTokenId</ColNames>
|
||||||
<Primary>1</Primary>
|
<Primary>1</Primary>
|
||||||
<UnderlyingIndexName>sqlite_autoindex_api_tokens_1</UnderlyingIndexName>
|
<UnderlyingIndexName>sqlite_autoindex_api_tokens_1</UnderlyingIndexName>
|
||||||
</key>
|
</key>
|
||||||
<column id="26" parent="7" name="branchId">
|
<column id="27" parent="7" name="branchId">
|
||||||
<Position>1</Position>
|
<Position>1</Position>
|
||||||
<DataType>TEXT|0s</DataType>
|
<DataType>TEXT|0s</DataType>
|
||||||
<NotNull>1</NotNull>
|
<NotNull>1</NotNull>
|
||||||
</column>
|
</column>
|
||||||
<column id="27" parent="7" name="noteId">
|
<column id="28" parent="7" name="noteId">
|
||||||
<Position>2</Position>
|
<Position>2</Position>
|
||||||
<DataType>TEXT|0s</DataType>
|
<DataType>TEXT|0s</DataType>
|
||||||
<NotNull>1</NotNull>
|
<NotNull>1</NotNull>
|
||||||
</column>
|
</column>
|
||||||
<column id="28" parent="7" name="parentNoteId">
|
<column id="29" parent="7" name="parentNoteId">
|
||||||
<Position>3</Position>
|
<Position>3</Position>
|
||||||
<DataType>TEXT|0s</DataType>
|
<DataType>TEXT|0s</DataType>
|
||||||
<NotNull>1</NotNull>
|
<NotNull>1</NotNull>
|
||||||
</column>
|
</column>
|
||||||
<column id="29" parent="7" name="notePosition">
|
<column id="30" parent="7" name="notePosition">
|
||||||
<Position>4</Position>
|
<Position>4</Position>
|
||||||
<DataType>INTEGER|0s</DataType>
|
<DataType>INTEGER|0s</DataType>
|
||||||
<NotNull>1</NotNull>
|
<NotNull>1</NotNull>
|
||||||
</column>
|
</column>
|
||||||
<column id="30" parent="7" name="prefix">
|
<column id="31" parent="7" name="prefix">
|
||||||
<Position>5</Position>
|
<Position>5</Position>
|
||||||
<DataType>TEXT|0s</DataType>
|
<DataType>TEXT|0s</DataType>
|
||||||
</column>
|
</column>
|
||||||
<column id="31" parent="7" name="isExpanded">
|
<column id="32" parent="7" name="isExpanded">
|
||||||
<Position>6</Position>
|
<Position>6</Position>
|
||||||
<DataType>BOOLEAN|0s</DataType>
|
<DataType>BOOLEAN|0s</DataType>
|
||||||
</column>
|
</column>
|
||||||
<column id="32" parent="7" name="isDeleted">
|
<column id="33" parent="7" name="isDeleted">
|
||||||
<Position>7</Position>
|
<Position>7</Position>
|
||||||
<DataType>INTEGER|0s</DataType>
|
<DataType>INTEGER|0s</DataType>
|
||||||
<NotNull>1</NotNull>
|
<NotNull>1</NotNull>
|
||||||
<DefaultExpression>0</DefaultExpression>
|
<DefaultExpression>0</DefaultExpression>
|
||||||
</column>
|
</column>
|
||||||
<column id="33" parent="7" name="dateModified">
|
<column id="34" parent="7" name="dateModified">
|
||||||
<Position>8</Position>
|
<Position>8</Position>
|
||||||
<DataType>TEXT|0s</DataType>
|
<DataType>TEXT|0s</DataType>
|
||||||
<NotNull>1</NotNull>
|
<NotNull>1</NotNull>
|
||||||
</column>
|
</column>
|
||||||
<index id="34" parent="7" name="sqlite_autoindex_branches_1">
|
<column id="35" parent="7" name="hash">
|
||||||
|
<Position>9</Position>
|
||||||
|
<DataType>TEXT|0s</DataType>
|
||||||
|
<NotNull>1</NotNull>
|
||||||
|
<DefaultExpression>""</DefaultExpression>
|
||||||
|
</column>
|
||||||
|
<column id="36" parent="7" name="dateCreated">
|
||||||
|
<Position>10</Position>
|
||||||
|
<DataType>TEXT|0s</DataType>
|
||||||
|
<NotNull>1</NotNull>
|
||||||
|
<DefaultExpression>'1970-01-01T00:00:00.000Z'</DefaultExpression>
|
||||||
|
</column>
|
||||||
|
<index id="37" parent="7" name="sqlite_autoindex_branches_1">
|
||||||
<NameSurrogate>1</NameSurrogate>
|
<NameSurrogate>1</NameSurrogate>
|
||||||
<ColNames>branchId</ColNames>
|
<ColNames>branchId</ColNames>
|
||||||
<ColumnCollations></ColumnCollations>
|
<ColumnCollations></ColumnCollations>
|
||||||
<Unique>1</Unique>
|
<Unique>1</Unique>
|
||||||
</index>
|
</index>
|
||||||
<index id="35" parent="7" name="IDX_branches_noteId_parentNoteId">
|
<index id="38" parent="7" name="IDX_branches_noteId_parentNoteId">
|
||||||
<ColNames>noteId
|
<ColNames>noteId
|
||||||
parentNoteId</ColNames>
|
parentNoteId</ColNames>
|
||||||
<ColumnCollations>
|
<ColumnCollations></ColumnCollations>
|
||||||
</ColumnCollations>
|
|
||||||
</index>
|
</index>
|
||||||
<index id="36" parent="7" name="IDX_branches_noteId">
|
<index id="39" parent="7" name="IDX_branches_noteId">
|
||||||
<ColNames>noteId</ColNames>
|
<ColNames>noteId</ColNames>
|
||||||
<ColumnCollations></ColumnCollations>
|
<ColumnCollations></ColumnCollations>
|
||||||
</index>
|
</index>
|
||||||
<key id="37" parent="7">
|
<index id="40" parent="7" name="IDX_branches_parentNoteId">
|
||||||
|
<ColNames>parentNoteId</ColNames>
|
||||||
|
<ColumnCollations></ColumnCollations>
|
||||||
|
</index>
|
||||||
|
<key id="41" parent="7">
|
||||||
<ColNames>branchId</ColNames>
|
<ColNames>branchId</ColNames>
|
||||||
<Primary>1</Primary>
|
<Primary>1</Primary>
|
||||||
<UnderlyingIndexName>sqlite_autoindex_branches_1</UnderlyingIndexName>
|
<UnderlyingIndexName>sqlite_autoindex_branches_1</UnderlyingIndexName>
|
||||||
</key>
|
</key>
|
||||||
<column id="38" parent="8" name="id">
|
<column id="42" parent="8" name="eventId">
|
||||||
<Position>1</Position>
|
<Position>1</Position>
|
||||||
<DataType>INTEGER|0s</DataType>
|
<DataType>TEXT|0s</DataType>
|
||||||
<NotNull>1</NotNull>
|
<NotNull>1</NotNull>
|
||||||
<SequenceIdentity>1</SequenceIdentity>
|
|
||||||
</column>
|
</column>
|
||||||
<column id="39" parent="8" name="noteId">
|
<column id="43" parent="8" name="noteId">
|
||||||
<Position>2</Position>
|
<Position>2</Position>
|
||||||
<DataType>TEXT|0s</DataType>
|
<DataType>TEXT|0s</DataType>
|
||||||
</column>
|
</column>
|
||||||
<column id="40" parent="8" name="comment">
|
<column id="44" parent="8" name="comment">
|
||||||
<Position>3</Position>
|
<Position>3</Position>
|
||||||
<DataType>TEXT|0s</DataType>
|
<DataType>TEXT|0s</DataType>
|
||||||
</column>
|
</column>
|
||||||
<column id="41" parent="8" name="dateAdded">
|
<column id="45" parent="8" name="dateCreated">
|
||||||
<Position>4</Position>
|
<Position>4</Position>
|
||||||
<DataType>TEXT|0s</DataType>
|
<DataType>TEXT|0s</DataType>
|
||||||
<NotNull>1</NotNull>
|
<NotNull>1</NotNull>
|
||||||
</column>
|
</column>
|
||||||
<key id="42" parent="8">
|
<index id="46" parent="8" name="sqlite_autoindex_event_log_1">
|
||||||
<ColNames>id</ColNames>
|
<NameSurrogate>1</NameSurrogate>
|
||||||
|
<ColNames>eventId</ColNames>
|
||||||
|
<ColumnCollations></ColumnCollations>
|
||||||
|
<Unique>1</Unique>
|
||||||
|
</index>
|
||||||
|
<key id="47" parent="8">
|
||||||
|
<ColNames>eventId</ColNames>
|
||||||
<Primary>1</Primary>
|
<Primary>1</Primary>
|
||||||
|
<UnderlyingIndexName>sqlite_autoindex_event_log_1</UnderlyingIndexName>
|
||||||
</key>
|
</key>
|
||||||
<foreign-key id="43" parent="8">
|
<column id="48" parent="9" name="imageId">
|
||||||
<ColNames>noteId</ColNames>
|
|
||||||
<RefTableName>notes</RefTableName>
|
|
||||||
<RefColNames>noteId</RefColNames>
|
|
||||||
</foreign-key>
|
|
||||||
<column id="44" parent="9" name="imageId">
|
|
||||||
<Position>1</Position>
|
<Position>1</Position>
|
||||||
<DataType>TEXT|0s</DataType>
|
<DataType>TEXT|0s</DataType>
|
||||||
<NotNull>1</NotNull>
|
<NotNull>1</NotNull>
|
||||||
</column>
|
</column>
|
||||||
<column id="45" parent="9" name="format">
|
<column id="49" parent="9" name="format">
|
||||||
<Position>2</Position>
|
<Position>2</Position>
|
||||||
<DataType>TEXT|0s</DataType>
|
<DataType>TEXT|0s</DataType>
|
||||||
<NotNull>1</NotNull>
|
<NotNull>1</NotNull>
|
||||||
</column>
|
</column>
|
||||||
<column id="46" parent="9" name="checksum">
|
<column id="50" parent="9" name="checksum">
|
||||||
<Position>3</Position>
|
<Position>3</Position>
|
||||||
<DataType>TEXT|0s</DataType>
|
<DataType>TEXT|0s</DataType>
|
||||||
<NotNull>1</NotNull>
|
<NotNull>1</NotNull>
|
||||||
</column>
|
</column>
|
||||||
<column id="47" parent="9" name="name">
|
<column id="51" parent="9" name="name">
|
||||||
<Position>4</Position>
|
<Position>4</Position>
|
||||||
<DataType>TEXT|0s</DataType>
|
<DataType>TEXT|0s</DataType>
|
||||||
<NotNull>1</NotNull>
|
<NotNull>1</NotNull>
|
||||||
</column>
|
</column>
|
||||||
<column id="48" parent="9" name="data">
|
<column id="52" parent="9" name="data">
|
||||||
<Position>5</Position>
|
<Position>5</Position>
|
||||||
<DataType>BLOB|0s</DataType>
|
<DataType>BLOB|0s</DataType>
|
||||||
</column>
|
</column>
|
||||||
<column id="49" parent="9" name="isDeleted">
|
<column id="53" parent="9" name="isDeleted">
|
||||||
<Position>6</Position>
|
<Position>6</Position>
|
||||||
<DataType>INT|0s</DataType>
|
<DataType>INT|0s</DataType>
|
||||||
<NotNull>1</NotNull>
|
<NotNull>1</NotNull>
|
||||||
<DefaultExpression>0</DefaultExpression>
|
<DefaultExpression>0</DefaultExpression>
|
||||||
</column>
|
</column>
|
||||||
<column id="50" parent="9" name="dateModified">
|
<column id="54" parent="9" name="dateModified">
|
||||||
<Position>7</Position>
|
<Position>7</Position>
|
||||||
<DataType>TEXT|0s</DataType>
|
<DataType>TEXT|0s</DataType>
|
||||||
<NotNull>1</NotNull>
|
<NotNull>1</NotNull>
|
||||||
</column>
|
</column>
|
||||||
<column id="51" parent="9" name="dateCreated">
|
<column id="55" parent="9" name="dateCreated">
|
||||||
<Position>8</Position>
|
<Position>8</Position>
|
||||||
<DataType>TEXT|0s</DataType>
|
<DataType>TEXT|0s</DataType>
|
||||||
<NotNull>1</NotNull>
|
<NotNull>1</NotNull>
|
||||||
</column>
|
</column>
|
||||||
<index id="52" parent="9" name="sqlite_autoindex_images_1">
|
<column id="56" parent="9" name="hash">
|
||||||
|
<Position>9</Position>
|
||||||
|
<DataType>TEXT|0s</DataType>
|
||||||
|
<NotNull>1</NotNull>
|
||||||
|
<DefaultExpression>""</DefaultExpression>
|
||||||
|
</column>
|
||||||
|
<index id="57" parent="9" name="sqlite_autoindex_images_1">
|
||||||
<NameSurrogate>1</NameSurrogate>
|
<NameSurrogate>1</NameSurrogate>
|
||||||
<ColNames>imageId</ColNames>
|
<ColNames>imageId</ColNames>
|
||||||
<ColumnCollations></ColumnCollations>
|
<ColumnCollations></ColumnCollations>
|
||||||
<Unique>1</Unique>
|
<Unique>1</Unique>
|
||||||
</index>
|
</index>
|
||||||
<key id="53" parent="9">
|
<key id="58" parent="9">
|
||||||
<ColNames>imageId</ColNames>
|
<ColNames>imageId</ColNames>
|
||||||
<Primary>1</Primary>
|
<Primary>1</Primary>
|
||||||
<UnderlyingIndexName>sqlite_autoindex_images_1</UnderlyingIndexName>
|
<UnderlyingIndexName>sqlite_autoindex_images_1</UnderlyingIndexName>
|
||||||
</key>
|
</key>
|
||||||
<column id="54" parent="10" name="labelId">
|
<column id="59" parent="10" name="labelId">
|
||||||
<Position>1</Position>
|
<Position>1</Position>
|
||||||
<DataType>TEXT|0s</DataType>
|
<DataType>TEXT|0s</DataType>
|
||||||
<NotNull>1</NotNull>
|
<NotNull>1</NotNull>
|
||||||
</column>
|
</column>
|
||||||
<column id="55" parent="10" name="noteId">
|
<column id="60" parent="10" name="noteId">
|
||||||
<Position>2</Position>
|
<Position>2</Position>
|
||||||
<DataType>TEXT|0s</DataType>
|
<DataType>TEXT|0s</DataType>
|
||||||
<NotNull>1</NotNull>
|
<NotNull>1</NotNull>
|
||||||
</column>
|
</column>
|
||||||
<column id="56" parent="10" name="name">
|
<column id="61" parent="10" name="name">
|
||||||
<Position>3</Position>
|
<Position>3</Position>
|
||||||
<DataType>TEXT|0s</DataType>
|
<DataType>TEXT|0s</DataType>
|
||||||
<NotNull>1</NotNull>
|
<NotNull>1</NotNull>
|
||||||
</column>
|
</column>
|
||||||
<column id="57" parent="10" name="value">
|
<column id="62" parent="10" name="value">
|
||||||
<Position>4</Position>
|
<Position>4</Position>
|
||||||
<DataType>TEXT|0s</DataType>
|
<DataType>TEXT|0s</DataType>
|
||||||
<NotNull>1</NotNull>
|
<NotNull>1</NotNull>
|
||||||
<DefaultExpression>''</DefaultExpression>
|
<DefaultExpression>''</DefaultExpression>
|
||||||
</column>
|
</column>
|
||||||
<column id="58" parent="10" name="position">
|
<column id="63" parent="10" name="position">
|
||||||
<Position>5</Position>
|
<Position>5</Position>
|
||||||
<DataType>INT|0s</DataType>
|
<DataType>INT|0s</DataType>
|
||||||
<NotNull>1</NotNull>
|
<NotNull>1</NotNull>
|
||||||
<DefaultExpression>0</DefaultExpression>
|
<DefaultExpression>0</DefaultExpression>
|
||||||
</column>
|
</column>
|
||||||
<column id="59" parent="10" name="dateCreated">
|
<column id="64" parent="10" name="dateCreated">
|
||||||
<Position>6</Position>
|
<Position>6</Position>
|
||||||
<DataType>TEXT|0s</DataType>
|
<DataType>TEXT|0s</DataType>
|
||||||
<NotNull>1</NotNull>
|
<NotNull>1</NotNull>
|
||||||
</column>
|
</column>
|
||||||
<column id="60" parent="10" name="dateModified">
|
<column id="65" parent="10" name="dateModified">
|
||||||
<Position>7</Position>
|
<Position>7</Position>
|
||||||
<DataType>TEXT|0s</DataType>
|
<DataType>TEXT|0s</DataType>
|
||||||
<NotNull>1</NotNull>
|
<NotNull>1</NotNull>
|
||||||
</column>
|
</column>
|
||||||
<column id="61" parent="10" name="isDeleted">
|
<column id="66" parent="10" name="isDeleted">
|
||||||
<Position>8</Position>
|
<Position>8</Position>
|
||||||
<DataType>INT|0s</DataType>
|
<DataType>INT|0s</DataType>
|
||||||
<NotNull>1</NotNull>
|
<NotNull>1</NotNull>
|
||||||
</column>
|
</column>
|
||||||
<index id="62" parent="10" name="sqlite_autoindex_labels_1">
|
<column id="67" parent="10" name="hash">
|
||||||
|
<Position>9</Position>
|
||||||
|
<DataType>TEXT|0s</DataType>
|
||||||
|
<NotNull>1</NotNull>
|
||||||
|
<DefaultExpression>""</DefaultExpression>
|
||||||
|
</column>
|
||||||
|
<index id="68" parent="10" name="sqlite_autoindex_labels_1">
|
||||||
<NameSurrogate>1</NameSurrogate>
|
<NameSurrogate>1</NameSurrogate>
|
||||||
<ColNames>labelId</ColNames>
|
<ColNames>labelId</ColNames>
|
||||||
<ColumnCollations></ColumnCollations>
|
<ColumnCollations></ColumnCollations>
|
||||||
<Unique>1</Unique>
|
<Unique>1</Unique>
|
||||||
</index>
|
</index>
|
||||||
<index id="63" parent="10" name="IDX_labels_noteId">
|
<index id="69" parent="10" name="IDX_labels_noteId">
|
||||||
<ColNames>noteId</ColNames>
|
<ColNames>noteId</ColNames>
|
||||||
<ColumnCollations></ColumnCollations>
|
<ColumnCollations></ColumnCollations>
|
||||||
</index>
|
</index>
|
||||||
<index id="64" parent="10" name="IDX_labels_name_value">
|
<index id="70" parent="10" name="IDX_labels_name_value">
|
||||||
<ColNames>name
|
<ColNames>name
|
||||||
value</ColNames>
|
value</ColNames>
|
||||||
<ColumnCollations>
|
<ColumnCollations></ColumnCollations>
|
||||||
</ColumnCollations>
|
|
||||||
</index>
|
</index>
|
||||||
<key id="65" parent="10">
|
<key id="71" parent="10">
|
||||||
<ColNames>labelId</ColNames>
|
<ColNames>labelId</ColNames>
|
||||||
<Primary>1</Primary>
|
<Primary>1</Primary>
|
||||||
<UnderlyingIndexName>sqlite_autoindex_labels_1</UnderlyingIndexName>
|
<UnderlyingIndexName>sqlite_autoindex_labels_1</UnderlyingIndexName>
|
||||||
</key>
|
</key>
|
||||||
<column id="66" parent="11" name="noteImageId">
|
<column id="72" parent="11" name="noteImageId">
|
||||||
<Position>1</Position>
|
<Position>1</Position>
|
||||||
<DataType>TEXT|0s</DataType>
|
<DataType>TEXT|0s</DataType>
|
||||||
<NotNull>1</NotNull>
|
<NotNull>1</NotNull>
|
||||||
</column>
|
</column>
|
||||||
<column id="67" parent="11" name="noteId">
|
<column id="73" parent="11" name="noteId">
|
||||||
<Position>2</Position>
|
<Position>2</Position>
|
||||||
<DataType>TEXT|0s</DataType>
|
<DataType>TEXT|0s</DataType>
|
||||||
<NotNull>1</NotNull>
|
<NotNull>1</NotNull>
|
||||||
</column>
|
</column>
|
||||||
<column id="68" parent="11" name="imageId">
|
<column id="74" parent="11" name="imageId">
|
||||||
<Position>3</Position>
|
<Position>3</Position>
|
||||||
<DataType>TEXT|0s</DataType>
|
<DataType>TEXT|0s</DataType>
|
||||||
<NotNull>1</NotNull>
|
<NotNull>1</NotNull>
|
||||||
</column>
|
</column>
|
||||||
<column id="69" parent="11" name="isDeleted">
|
<column id="75" parent="11" name="isDeleted">
|
||||||
<Position>4</Position>
|
<Position>4</Position>
|
||||||
<DataType>INT|0s</DataType>
|
<DataType>INT|0s</DataType>
|
||||||
<NotNull>1</NotNull>
|
<NotNull>1</NotNull>
|
||||||
<DefaultExpression>0</DefaultExpression>
|
<DefaultExpression>0</DefaultExpression>
|
||||||
</column>
|
</column>
|
||||||
<column id="70" parent="11" name="dateModified">
|
<column id="76" parent="11" name="dateModified">
|
||||||
<Position>5</Position>
|
<Position>5</Position>
|
||||||
<DataType>TEXT|0s</DataType>
|
<DataType>TEXT|0s</DataType>
|
||||||
<NotNull>1</NotNull>
|
<NotNull>1</NotNull>
|
||||||
</column>
|
</column>
|
||||||
<column id="71" parent="11" name="dateCreated">
|
<column id="77" parent="11" name="dateCreated">
|
||||||
<Position>6</Position>
|
<Position>6</Position>
|
||||||
<DataType>TEXT|0s</DataType>
|
<DataType>TEXT|0s</DataType>
|
||||||
<NotNull>1</NotNull>
|
<NotNull>1</NotNull>
|
||||||
</column>
|
</column>
|
||||||
<index id="72" parent="11" name="sqlite_autoindex_note_images_1">
|
<column id="78" parent="11" name="hash">
|
||||||
|
<Position>7</Position>
|
||||||
|
<DataType>TEXT|0s</DataType>
|
||||||
|
<NotNull>1</NotNull>
|
||||||
|
<DefaultExpression>""</DefaultExpression>
|
||||||
|
</column>
|
||||||
|
<index id="79" parent="11" name="sqlite_autoindex_note_images_1">
|
||||||
<NameSurrogate>1</NameSurrogate>
|
<NameSurrogate>1</NameSurrogate>
|
||||||
<ColNames>noteImageId</ColNames>
|
<ColNames>noteImageId</ColNames>
|
||||||
<ColumnCollations></ColumnCollations>
|
<ColumnCollations></ColumnCollations>
|
||||||
<Unique>1</Unique>
|
<Unique>1</Unique>
|
||||||
</index>
|
</index>
|
||||||
<index id="73" parent="11" name="IDX_note_images_noteId_imageId">
|
<index id="80" parent="11" name="IDX_note_images_noteId_imageId">
|
||||||
<ColNames>noteId
|
<ColNames>noteId
|
||||||
imageId</ColNames>
|
imageId</ColNames>
|
||||||
<ColumnCollations>
|
<ColumnCollations></ColumnCollations>
|
||||||
</ColumnCollations>
|
|
||||||
</index>
|
</index>
|
||||||
<index id="74" parent="11" name="IDX_note_images_noteId">
|
<index id="81" parent="11" name="IDX_note_images_noteId">
|
||||||
<ColNames>noteId</ColNames>
|
<ColNames>noteId</ColNames>
|
||||||
<ColumnCollations></ColumnCollations>
|
<ColumnCollations></ColumnCollations>
|
||||||
</index>
|
</index>
|
||||||
<index id="75" parent="11" name="IDX_note_images_imageId">
|
<index id="82" parent="11" name="IDX_note_images_imageId">
|
||||||
<ColNames>imageId</ColNames>
|
<ColNames>imageId</ColNames>
|
||||||
<ColumnCollations></ColumnCollations>
|
<ColumnCollations></ColumnCollations>
|
||||||
</index>
|
</index>
|
||||||
<key id="76" parent="11">
|
<key id="83" parent="11">
|
||||||
<ColNames>noteImageId</ColNames>
|
<ColNames>noteImageId</ColNames>
|
||||||
<Primary>1</Primary>
|
<Primary>1</Primary>
|
||||||
<UnderlyingIndexName>sqlite_autoindex_note_images_1</UnderlyingIndexName>
|
<UnderlyingIndexName>sqlite_autoindex_note_images_1</UnderlyingIndexName>
|
||||||
</key>
|
</key>
|
||||||
<column id="77" parent="12" name="noteRevisionId">
|
<column id="84" parent="12" name="noteRevisionId">
|
||||||
<Position>1</Position>
|
<Position>1</Position>
|
||||||
<DataType>TEXT|0s</DataType>
|
<DataType>TEXT|0s</DataType>
|
||||||
<NotNull>1</NotNull>
|
<NotNull>1</NotNull>
|
||||||
</column>
|
</column>
|
||||||
<column id="78" parent="12" name="noteId">
|
<column id="85" parent="12" name="noteId">
|
||||||
<Position>2</Position>
|
<Position>2</Position>
|
||||||
<DataType>TEXT|0s</DataType>
|
<DataType>TEXT|0s</DataType>
|
||||||
<NotNull>1</NotNull>
|
<NotNull>1</NotNull>
|
||||||
</column>
|
</column>
|
||||||
<column id="79" parent="12" name="title">
|
<column id="86" parent="12" name="title">
|
||||||
<Position>3</Position>
|
<Position>3</Position>
|
||||||
<DataType>TEXT|0s</DataType>
|
<DataType>TEXT|0s</DataType>
|
||||||
</column>
|
</column>
|
||||||
<column id="80" parent="12" name="content">
|
<column id="87" parent="12" name="content">
|
||||||
<Position>4</Position>
|
<Position>4</Position>
|
||||||
<DataType>TEXT|0s</DataType>
|
<DataType>TEXT|0s</DataType>
|
||||||
</column>
|
</column>
|
||||||
<column id="81" parent="12" name="isProtected">
|
<column id="88" parent="12" name="isProtected">
|
||||||
<Position>5</Position>
|
<Position>5</Position>
|
||||||
<DataType>INT|0s</DataType>
|
<DataType>INT|0s</DataType>
|
||||||
<NotNull>1</NotNull>
|
<NotNull>1</NotNull>
|
||||||
<DefaultExpression>0</DefaultExpression>
|
<DefaultExpression>0</DefaultExpression>
|
||||||
</column>
|
</column>
|
||||||
<column id="82" parent="12" name="dateModifiedFrom">
|
<column id="89" parent="12" name="dateModifiedFrom">
|
||||||
<Position>6</Position>
|
<Position>6</Position>
|
||||||
<DataType>TEXT|0s</DataType>
|
<DataType>TEXT|0s</DataType>
|
||||||
<NotNull>1</NotNull>
|
<NotNull>1</NotNull>
|
||||||
</column>
|
</column>
|
||||||
<column id="83" parent="12" name="dateModifiedTo">
|
<column id="90" parent="12" name="dateModifiedTo">
|
||||||
<Position>7</Position>
|
<Position>7</Position>
|
||||||
<DataType>TEXT|0s</DataType>
|
<DataType>TEXT|0s</DataType>
|
||||||
<NotNull>1</NotNull>
|
<NotNull>1</NotNull>
|
||||||
</column>
|
</column>
|
||||||
<index id="84" parent="12" name="sqlite_autoindex_note_revisions_1">
|
<column id="91" parent="12" name="type">
|
||||||
|
<Position>8</Position>
|
||||||
|
<DataType>TEXT|0s</DataType>
|
||||||
|
<NotNull>1</NotNull>
|
||||||
|
<DefaultExpression>''</DefaultExpression>
|
||||||
|
</column>
|
||||||
|
<column id="92" parent="12" name="mime">
|
||||||
|
<Position>9</Position>
|
||||||
|
<DataType>TEXT|0s</DataType>
|
||||||
|
<NotNull>1</NotNull>
|
||||||
|
<DefaultExpression>''</DefaultExpression>
|
||||||
|
</column>
|
||||||
|
<column id="93" parent="12" name="hash">
|
||||||
|
<Position>10</Position>
|
||||||
|
<DataType>TEXT|0s</DataType>
|
||||||
|
<NotNull>1</NotNull>
|
||||||
|
<DefaultExpression>""</DefaultExpression>
|
||||||
|
</column>
|
||||||
|
<index id="94" parent="12" name="sqlite_autoindex_note_revisions_1">
|
||||||
<NameSurrogate>1</NameSurrogate>
|
<NameSurrogate>1</NameSurrogate>
|
||||||
<ColNames>noteRevisionId</ColNames>
|
<ColNames>noteRevisionId</ColNames>
|
||||||
<ColumnCollations></ColumnCollations>
|
<ColumnCollations></ColumnCollations>
|
||||||
<Unique>1</Unique>
|
<Unique>1</Unique>
|
||||||
</index>
|
</index>
|
||||||
<index id="85" parent="12" name="IDX_note_revisions_noteId">
|
<index id="95" parent="12" name="IDX_note_revisions_noteId">
|
||||||
<ColNames>noteId</ColNames>
|
<ColNames>noteId</ColNames>
|
||||||
<ColumnCollations></ColumnCollations>
|
<ColumnCollations></ColumnCollations>
|
||||||
</index>
|
</index>
|
||||||
<index id="86" parent="12" name="IDX_note_revisions_dateModifiedFrom">
|
<index id="96" parent="12" name="IDX_note_revisions_dateModifiedFrom">
|
||||||
<ColNames>dateModifiedFrom</ColNames>
|
<ColNames>dateModifiedFrom</ColNames>
|
||||||
<ColumnCollations></ColumnCollations>
|
<ColumnCollations></ColumnCollations>
|
||||||
</index>
|
</index>
|
||||||
<index id="87" parent="12" name="IDX_note_revisions_dateModifiedTo">
|
<index id="97" parent="12" name="IDX_note_revisions_dateModifiedTo">
|
||||||
<ColNames>dateModifiedTo</ColNames>
|
<ColNames>dateModifiedTo</ColNames>
|
||||||
<ColumnCollations></ColumnCollations>
|
<ColumnCollations></ColumnCollations>
|
||||||
</index>
|
</index>
|
||||||
<key id="88" parent="12">
|
<key id="98" parent="12">
|
||||||
<ColNames>noteRevisionId</ColNames>
|
<ColNames>noteRevisionId</ColNames>
|
||||||
<Primary>1</Primary>
|
<Primary>1</Primary>
|
||||||
<UnderlyingIndexName>sqlite_autoindex_note_revisions_1</UnderlyingIndexName>
|
<UnderlyingIndexName>sqlite_autoindex_note_revisions_1</UnderlyingIndexName>
|
||||||
</key>
|
</key>
|
||||||
<column id="89" parent="13" name="noteId">
|
<column id="99" parent="13" name="noteId">
|
||||||
<Position>1</Position>
|
<Position>1</Position>
|
||||||
<DataType>TEXT|0s</DataType>
|
<DataType>TEXT|0s</DataType>
|
||||||
<NotNull>1</NotNull>
|
<NotNull>1</NotNull>
|
||||||
</column>
|
</column>
|
||||||
<column id="90" parent="13" name="title">
|
<column id="100" parent="13" name="title">
|
||||||
<Position>2</Position>
|
<Position>2</Position>
|
||||||
<DataType>TEXT|0s</DataType>
|
<DataType>TEXT|0s</DataType>
|
||||||
|
<NotNull>1</NotNull>
|
||||||
|
<DefaultExpression>"unnamed"</DefaultExpression>
|
||||||
</column>
|
</column>
|
||||||
<column id="91" parent="13" name="content">
|
<column id="101" parent="13" name="content">
|
||||||
<Position>3</Position>
|
<Position>3</Position>
|
||||||
<DataType>TEXT|0s</DataType>
|
<DataType>TEXT|0s</DataType>
|
||||||
|
<NotNull>1</NotNull>
|
||||||
|
<DefaultExpression>""</DefaultExpression>
|
||||||
</column>
|
</column>
|
||||||
<column id="92" parent="13" name="isProtected">
|
<column id="102" parent="13" name="isProtected">
|
||||||
<Position>4</Position>
|
<Position>4</Position>
|
||||||
<DataType>INT|0s</DataType>
|
<DataType>INT|0s</DataType>
|
||||||
<NotNull>1</NotNull>
|
<NotNull>1</NotNull>
|
||||||
<DefaultExpression>0</DefaultExpression>
|
<DefaultExpression>0</DefaultExpression>
|
||||||
</column>
|
</column>
|
||||||
<column id="93" parent="13" name="isDeleted">
|
<column id="103" parent="13" name="isDeleted">
|
||||||
<Position>5</Position>
|
<Position>5</Position>
|
||||||
<DataType>INT|0s</DataType>
|
<DataType>INT|0s</DataType>
|
||||||
<NotNull>1</NotNull>
|
<NotNull>1</NotNull>
|
||||||
<DefaultExpression>0</DefaultExpression>
|
<DefaultExpression>0</DefaultExpression>
|
||||||
</column>
|
</column>
|
||||||
<column id="94" parent="13" name="dateCreated">
|
<column id="104" parent="13" name="dateCreated">
|
||||||
<Position>6</Position>
|
<Position>6</Position>
|
||||||
<DataType>TEXT|0s</DataType>
|
<DataType>TEXT|0s</DataType>
|
||||||
<NotNull>1</NotNull>
|
<NotNull>1</NotNull>
|
||||||
</column>
|
</column>
|
||||||
<column id="95" parent="13" name="dateModified">
|
<column id="105" parent="13" name="dateModified">
|
||||||
<Position>7</Position>
|
<Position>7</Position>
|
||||||
<DataType>TEXT|0s</DataType>
|
<DataType>TEXT|0s</DataType>
|
||||||
<NotNull>1</NotNull>
|
<NotNull>1</NotNull>
|
||||||
</column>
|
</column>
|
||||||
<column id="96" parent="13" name="type">
|
<column id="106" parent="13" name="type">
|
||||||
<Position>8</Position>
|
<Position>8</Position>
|
||||||
<DataType>TEXT|0s</DataType>
|
<DataType>TEXT|0s</DataType>
|
||||||
<NotNull>1</NotNull>
|
<NotNull>1</NotNull>
|
||||||
<DefaultExpression>'text'</DefaultExpression>
|
<DefaultExpression>'text'</DefaultExpression>
|
||||||
</column>
|
</column>
|
||||||
<column id="97" parent="13" name="mime">
|
<column id="107" parent="13" name="mime">
|
||||||
<Position>9</Position>
|
<Position>9</Position>
|
||||||
<DataType>TEXT|0s</DataType>
|
<DataType>TEXT|0s</DataType>
|
||||||
<NotNull>1</NotNull>
|
<NotNull>1</NotNull>
|
||||||
<DefaultExpression>'text/html'</DefaultExpression>
|
<DefaultExpression>'text/html'</DefaultExpression>
|
||||||
</column>
|
</column>
|
||||||
<index id="98" parent="13" name="sqlite_autoindex_notes_1">
|
<column id="108" parent="13" name="hash">
|
||||||
|
<Position>10</Position>
|
||||||
|
<DataType>TEXT|0s</DataType>
|
||||||
|
<NotNull>1</NotNull>
|
||||||
|
<DefaultExpression>""</DefaultExpression>
|
||||||
|
</column>
|
||||||
|
<index id="109" parent="13" name="sqlite_autoindex_notes_1">
|
||||||
<NameSurrogate>1</NameSurrogate>
|
<NameSurrogate>1</NameSurrogate>
|
||||||
<ColNames>noteId</ColNames>
|
<ColNames>noteId</ColNames>
|
||||||
<ColumnCollations></ColumnCollations>
|
<ColumnCollations></ColumnCollations>
|
||||||
<Unique>1</Unique>
|
<Unique>1</Unique>
|
||||||
</index>
|
</index>
|
||||||
<index id="99" parent="13" name="IDX_notes_isDeleted">
|
<index id="110" parent="13" name="IDX_notes_type">
|
||||||
<ColNames>isDeleted</ColNames>
|
<ColNames>type</ColNames>
|
||||||
<ColumnCollations></ColumnCollations>
|
<ColumnCollations></ColumnCollations>
|
||||||
</index>
|
</index>
|
||||||
<key id="100" parent="13">
|
<key id="111" parent="13">
|
||||||
<ColNames>noteId</ColNames>
|
<ColNames>noteId</ColNames>
|
||||||
<Primary>1</Primary>
|
<Primary>1</Primary>
|
||||||
<UnderlyingIndexName>sqlite_autoindex_notes_1</UnderlyingIndexName>
|
<UnderlyingIndexName>sqlite_autoindex_notes_1</UnderlyingIndexName>
|
||||||
</key>
|
</key>
|
||||||
<column id="101" parent="14" name="name">
|
<column id="112" parent="14" name="optionId">
|
||||||
<Position>1</Position>
|
<Position>1</Position>
|
||||||
<DataType>TEXT|0s</DataType>
|
<DataType>TEXT|0s</DataType>
|
||||||
<NotNull>1</NotNull>
|
<NotNull>1</NotNull>
|
||||||
</column>
|
</column>
|
||||||
<column id="102" parent="14" name="value">
|
<column id="113" parent="14" name="name">
|
||||||
<Position>2</Position>
|
<Position>2</Position>
|
||||||
<DataType>TEXT|0s</DataType>
|
<DataType>TEXT|0s</DataType>
|
||||||
|
<NotNull>1</NotNull>
|
||||||
</column>
|
</column>
|
||||||
<column id="103" parent="14" name="dateModified">
|
<column id="114" parent="14" name="value">
|
||||||
<Position>3</Position>
|
<Position>3</Position>
|
||||||
|
<DataType>TEXT|0s</DataType>
|
||||||
|
</column>
|
||||||
|
<column id="115" parent="14" name="dateModified">
|
||||||
|
<Position>4</Position>
|
||||||
<DataType>INT|0s</DataType>
|
<DataType>INT|0s</DataType>
|
||||||
</column>
|
</column>
|
||||||
<column id="104" parent="14" name="isSynced">
|
<column id="116" parent="14" name="isSynced">
|
||||||
<Position>4</Position>
|
<Position>5</Position>
|
||||||
<DataType>INTEGER|0s</DataType>
|
<DataType>INTEGER|0s</DataType>
|
||||||
<NotNull>1</NotNull>
|
<NotNull>1</NotNull>
|
||||||
<DefaultExpression>0</DefaultExpression>
|
<DefaultExpression>0</DefaultExpression>
|
||||||
</column>
|
</column>
|
||||||
<index id="105" parent="14" name="sqlite_autoindex_options_1">
|
<column id="117" parent="14" name="hash">
|
||||||
|
<Position>6</Position>
|
||||||
|
<DataType>TEXT|0s</DataType>
|
||||||
|
<NotNull>1</NotNull>
|
||||||
|
<DefaultExpression>""</DefaultExpression>
|
||||||
|
</column>
|
||||||
|
<column id="118" parent="14" name="dateCreated">
|
||||||
|
<Position>7</Position>
|
||||||
|
<DataType>TEXT|0s</DataType>
|
||||||
|
<NotNull>1</NotNull>
|
||||||
|
<DefaultExpression>'1970-01-01T00:00:00.000Z'</DefaultExpression>
|
||||||
|
</column>
|
||||||
|
<index id="119" parent="14" name="sqlite_autoindex_options_1">
|
||||||
<NameSurrogate>1</NameSurrogate>
|
<NameSurrogate>1</NameSurrogate>
|
||||||
<ColNames>name</ColNames>
|
<ColNames>optionId</ColNames>
|
||||||
<ColumnCollations></ColumnCollations>
|
<ColumnCollations></ColumnCollations>
|
||||||
<Unique>1</Unique>
|
<Unique>1</Unique>
|
||||||
</index>
|
</index>
|
||||||
<key id="106" parent="14">
|
<key id="120" parent="14">
|
||||||
<ColNames>name</ColNames>
|
<ColNames>optionId</ColNames>
|
||||||
<Primary>1</Primary>
|
<Primary>1</Primary>
|
||||||
<UnderlyingIndexName>sqlite_autoindex_options_1</UnderlyingIndexName>
|
<UnderlyingIndexName>sqlite_autoindex_options_1</UnderlyingIndexName>
|
||||||
</key>
|
</key>
|
||||||
<column id="107" parent="15" name="branchId">
|
<column id="121" parent="15" name="branchId">
|
||||||
<Position>1</Position>
|
<Position>1</Position>
|
||||||
<DataType>TEXT|0s</DataType>
|
<DataType>TEXT|0s</DataType>
|
||||||
<NotNull>1</NotNull>
|
<NotNull>1</NotNull>
|
||||||
</column>
|
</column>
|
||||||
<column id="108" parent="15" name="notePath">
|
<column id="122" parent="15" name="notePath">
|
||||||
<Position>2</Position>
|
<Position>2</Position>
|
||||||
<DataType>TEXT|0s</DataType>
|
<DataType>TEXT|0s</DataType>
|
||||||
<NotNull>1</NotNull>
|
<NotNull>1</NotNull>
|
||||||
</column>
|
</column>
|
||||||
<column id="109" parent="15" name="dateAccessed">
|
<column id="123" parent="15" name="hash">
|
||||||
<Position>3</Position>
|
<Position>3</Position>
|
||||||
<DataType>TEXT|0s</DataType>
|
<DataType>TEXT|0s</DataType>
|
||||||
<NotNull>1</NotNull>
|
<NotNull>1</NotNull>
|
||||||
|
<DefaultExpression>""</DefaultExpression>
|
||||||
</column>
|
</column>
|
||||||
<column id="110" parent="15" name="isDeleted">
|
<column id="124" parent="15" name="dateCreated">
|
||||||
<Position>4</Position>
|
<Position>4</Position>
|
||||||
|
<DataType>TEXT|0s</DataType>
|
||||||
|
<NotNull>1</NotNull>
|
||||||
|
</column>
|
||||||
|
<column id="125" parent="15" name="isDeleted">
|
||||||
|
<Position>5</Position>
|
||||||
<DataType>INT|0s</DataType>
|
<DataType>INT|0s</DataType>
|
||||||
</column>
|
</column>
|
||||||
<index id="111" parent="15" name="sqlite_autoindex_recent_notes_1">
|
<index id="126" parent="15" name="sqlite_autoindex_recent_notes_1">
|
||||||
<NameSurrogate>1</NameSurrogate>
|
<NameSurrogate>1</NameSurrogate>
|
||||||
<ColNames>branchId</ColNames>
|
<ColNames>branchId</ColNames>
|
||||||
<ColumnCollations></ColumnCollations>
|
<ColumnCollations></ColumnCollations>
|
||||||
<Unique>1</Unique>
|
<Unique>1</Unique>
|
||||||
</index>
|
</index>
|
||||||
<key id="112" parent="15">
|
<key id="127" parent="15">
|
||||||
<ColNames>branchId</ColNames>
|
<ColNames>branchId</ColNames>
|
||||||
<Primary>1</Primary>
|
<Primary>1</Primary>
|
||||||
<UnderlyingIndexName>sqlite_autoindex_recent_notes_1</UnderlyingIndexName>
|
<UnderlyingIndexName>sqlite_autoindex_recent_notes_1</UnderlyingIndexName>
|
||||||
</key>
|
</key>
|
||||||
<column id="113" parent="16" name="sourceId">
|
<column id="128" parent="16" name="sourceId">
|
||||||
<Position>1</Position>
|
<Position>1</Position>
|
||||||
<DataType>TEXT|0s</DataType>
|
<DataType>TEXT|0s</DataType>
|
||||||
<NotNull>1</NotNull>
|
<NotNull>1</NotNull>
|
||||||
</column>
|
</column>
|
||||||
<column id="114" parent="16" name="dateCreated">
|
<column id="129" parent="16" name="dateCreated">
|
||||||
<Position>2</Position>
|
<Position>2</Position>
|
||||||
<DataType>TEXT|0s</DataType>
|
<DataType>TEXT|0s</DataType>
|
||||||
<NotNull>1</NotNull>
|
<NotNull>1</NotNull>
|
||||||
</column>
|
</column>
|
||||||
<index id="115" parent="16" name="sqlite_autoindex_source_ids_1">
|
<index id="130" parent="16" name="sqlite_autoindex_source_ids_1">
|
||||||
<NameSurrogate>1</NameSurrogate>
|
<NameSurrogate>1</NameSurrogate>
|
||||||
<ColNames>sourceId</ColNames>
|
<ColNames>sourceId</ColNames>
|
||||||
<ColumnCollations></ColumnCollations>
|
<ColumnCollations></ColumnCollations>
|
||||||
<Unique>1</Unique>
|
<Unique>1</Unique>
|
||||||
</index>
|
</index>
|
||||||
<key id="116" parent="16">
|
<key id="131" parent="16">
|
||||||
<ColNames>sourceId</ColNames>
|
<ColNames>sourceId</ColNames>
|
||||||
<Primary>1</Primary>
|
<Primary>1</Primary>
|
||||||
<UnderlyingIndexName>sqlite_autoindex_source_ids_1</UnderlyingIndexName>
|
<UnderlyingIndexName>sqlite_autoindex_source_ids_1</UnderlyingIndexName>
|
||||||
</key>
|
</key>
|
||||||
<column id="117" parent="17" name="type">
|
<column id="132" parent="17" name="type">
|
||||||
<Position>1</Position>
|
<Position>1</Position>
|
||||||
<DataType>text|0s</DataType>
|
<DataType>text|0s</DataType>
|
||||||
</column>
|
</column>
|
||||||
<column id="118" parent="17" name="name">
|
<column id="133" parent="17" name="name">
|
||||||
<Position>2</Position>
|
<Position>2</Position>
|
||||||
<DataType>text|0s</DataType>
|
<DataType>text|0s</DataType>
|
||||||
</column>
|
</column>
|
||||||
<column id="119" parent="17" name="tbl_name">
|
<column id="134" parent="17" name="tbl_name">
|
||||||
<Position>3</Position>
|
<Position>3</Position>
|
||||||
<DataType>text|0s</DataType>
|
<DataType>text|0s</DataType>
|
||||||
</column>
|
</column>
|
||||||
<column id="120" parent="17" name="rootpage">
|
<column id="135" parent="17" name="rootpage">
|
||||||
<Position>4</Position>
|
<Position>4</Position>
|
||||||
<DataType>integer|0s</DataType>
|
<DataType>integer|0s</DataType>
|
||||||
</column>
|
</column>
|
||||||
<column id="121" parent="17" name="sql">
|
<column id="136" parent="17" name="sql">
|
||||||
<Position>5</Position>
|
<Position>5</Position>
|
||||||
<DataType>text|0s</DataType>
|
<DataType>text|0s</DataType>
|
||||||
</column>
|
</column>
|
||||||
<column id="122" parent="18" name="name">
|
<column id="137" parent="18" name="name">
|
||||||
<Position>1</Position>
|
<Position>1</Position>
|
||||||
</column>
|
</column>
|
||||||
<column id="123" parent="18" name="seq">
|
<column id="138" parent="18" name="seq">
|
||||||
<Position>2</Position>
|
<Position>2</Position>
|
||||||
</column>
|
</column>
|
||||||
<column id="124" parent="19" name="id">
|
<column id="139" parent="19" name="id">
|
||||||
<Position>1</Position>
|
<Position>1</Position>
|
||||||
<DataType>INTEGER|0s</DataType>
|
<DataType>INTEGER|0s</DataType>
|
||||||
<NotNull>1</NotNull>
|
<NotNull>1</NotNull>
|
||||||
<SequenceIdentity>1</SequenceIdentity>
|
<SequenceIdentity>1</SequenceIdentity>
|
||||||
</column>
|
</column>
|
||||||
<column id="125" parent="19" name="entityName">
|
<column id="140" parent="19" name="entityName">
|
||||||
<Position>2</Position>
|
<Position>2</Position>
|
||||||
<DataType>TEXT|0s</DataType>
|
<DataType>TEXT|0s</DataType>
|
||||||
<NotNull>1</NotNull>
|
<NotNull>1</NotNull>
|
||||||
</column>
|
</column>
|
||||||
<column id="126" parent="19" name="entityId">
|
<column id="141" parent="19" name="entityId">
|
||||||
<Position>3</Position>
|
<Position>3</Position>
|
||||||
<DataType>TEXT|0s</DataType>
|
<DataType>TEXT|0s</DataType>
|
||||||
<NotNull>1</NotNull>
|
<NotNull>1</NotNull>
|
||||||
</column>
|
</column>
|
||||||
<column id="127" parent="19" name="sourceId">
|
<column id="142" parent="19" name="sourceId">
|
||||||
<Position>4</Position>
|
<Position>4</Position>
|
||||||
<DataType>TEXT|0s</DataType>
|
<DataType>TEXT|0s</DataType>
|
||||||
<NotNull>1</NotNull>
|
<NotNull>1</NotNull>
|
||||||
</column>
|
</column>
|
||||||
<column id="128" parent="19" name="syncDate">
|
<column id="143" parent="19" name="syncDate">
|
||||||
<Position>5</Position>
|
<Position>5</Position>
|
||||||
<DataType>TEXT|0s</DataType>
|
<DataType>TEXT|0s</DataType>
|
||||||
<NotNull>1</NotNull>
|
<NotNull>1</NotNull>
|
||||||
</column>
|
</column>
|
||||||
<index id="129" parent="19" name="IDX_sync_entityName_entityId">
|
<index id="144" parent="19" name="IDX_sync_entityName_entityId">
|
||||||
<ColNames>entityName
|
<ColNames>entityName
|
||||||
entityId</ColNames>
|
entityId</ColNames>
|
||||||
<ColumnCollations>
|
<ColumnCollations></ColumnCollations>
|
||||||
</ColumnCollations>
|
|
||||||
<Unique>1</Unique>
|
<Unique>1</Unique>
|
||||||
</index>
|
</index>
|
||||||
<index id="130" parent="19" name="IDX_sync_syncDate">
|
<index id="145" parent="19" name="IDX_sync_syncDate">
|
||||||
<ColNames>syncDate</ColNames>
|
<ColNames>syncDate</ColNames>
|
||||||
<ColumnCollations></ColumnCollations>
|
<ColumnCollations></ColumnCollations>
|
||||||
</index>
|
</index>
|
||||||
<key id="131" parent="19">
|
<key id="146" parent="19">
|
||||||
<ColNames>id</ColNames>
|
<ColNames>id</ColNames>
|
||||||
<Primary>1</Primary>
|
<Primary>1</Primary>
|
||||||
</key>
|
</key>
|
||||||
|
|||||||
21
Dockerfile
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
FROM node:8.11.2
|
||||||
|
|
||||||
|
RUN apt-get update && apt-get install -y nasm
|
||||||
|
|
||||||
|
# Create app directory
|
||||||
|
WORKDIR /usr/src/app
|
||||||
|
|
||||||
|
# Install app dependencies
|
||||||
|
# A wildcard is used to ensure both package.json AND package-lock.json are copied
|
||||||
|
# where available (npm@5+)
|
||||||
|
COPY package*.json ./
|
||||||
|
|
||||||
|
RUN npm install --production
|
||||||
|
# If you are building your code for production
|
||||||
|
# RUN npm install --only=production
|
||||||
|
|
||||||
|
# Bundle app source
|
||||||
|
COPY . .
|
||||||
|
|
||||||
|
EXPOSE 8080
|
||||||
|
CMD [ "node", "src/www" ]
|
||||||
8
bin/build-docker.sh
Executable file
@@ -0,0 +1,8 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
if [[ $# -eq 0 ]] ; then
|
||||||
|
echo "Missing argument of new version"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
docker build -t zadam/trilium:latest -t zadam/trilium:$1 .
|
||||||
9
bin/push-docker-image.sh
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
if [[ $# -eq 0 ]] ; then
|
||||||
|
echo "Missing argument of new version"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
docker push zadam/trilium:latest
|
||||||
|
docker push zadam/trilium:$1
|
||||||
@@ -24,9 +24,9 @@ jq '.version = "'$VERSION'"' package.json|sponge package.json
|
|||||||
|
|
||||||
git add package.json
|
git add package.json
|
||||||
|
|
||||||
echo 'module.exports = { buildDate:"'`date --iso-8601=seconds`'", buildRevision: "'`git log -1 --format="%H"`'" };' > services/build.js
|
echo 'module.exports = { buildDate:"'`date --iso-8601=seconds`'", buildRevision: "'`git log -1 --format="%H"`'" };' > src/services/build.js
|
||||||
|
|
||||||
git add services/build.js
|
git add src/services/build.js
|
||||||
|
|
||||||
TAG=v$VERSION
|
TAG=v$VERSION
|
||||||
|
|
||||||
@@ -75,4 +75,8 @@ github-release upload \
|
|||||||
--name "$WINDOWS_X64_BUILD" \
|
--name "$WINDOWS_X64_BUILD" \
|
||||||
--file "dist/$WINDOWS_X64_BUILD"
|
--file "dist/$WINDOWS_X64_BUILD"
|
||||||
|
|
||||||
|
bin/build-docker.sh $VERSION
|
||||||
|
|
||||||
|
bin/push-docker-image.sh $VERSION
|
||||||
|
|
||||||
echo "Release finished!"
|
echo "Release finished!"
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
INSERT INTO branches (branchId, noteId, parentNoteId, notePosition, prefix, isExpanded, isDeleted, dateModified) VALUES ('root', 'root', 'none', 0, null, 1, 0, '2018-01-01T00:00:00.000Z');
|
||||||
INSERT INTO branches (branchId, noteId, parentNoteId, notePosition, prefix, isExpanded, isDeleted, dateModified) VALUES ('dLgtLUFn3GoN', '1Heh2acXfPNt', 'root', 21, null, 1, 0, '2017-12-23T00:46:39.304Z');
|
INSERT INTO branches (branchId, noteId, parentNoteId, notePosition, prefix, isExpanded, isDeleted, dateModified) VALUES ('dLgtLUFn3GoN', '1Heh2acXfPNt', 'root', 21, null, 1, 0, '2017-12-23T00:46:39.304Z');
|
||||||
INSERT INTO branches (branchId, noteId, parentNoteId, notePosition, prefix, isExpanded, isDeleted, dateModified) VALUES ('QLfS835GSfIh', '3RkyK9LI18dO', '1Heh2acXfPNt', 1, null, 1, 0, '2017-12-23T01:20:04.181Z');
|
INSERT INTO branches (branchId, noteId, parentNoteId, notePosition, prefix, isExpanded, isDeleted, dateModified) VALUES ('QLfS835GSfIh', '3RkyK9LI18dO', '1Heh2acXfPNt', 1, null, 1, 0, '2017-12-23T01:20:04.181Z');
|
||||||
INSERT INTO branches (branchId, noteId, parentNoteId, notePosition, prefix, isExpanded, isDeleted, dateModified) VALUES ('QJAcYJ1gGUh9', 'L1Ox40M1aEyy', '3RkyK9LI18dO', 0, null, 0, 0, '2017-12-23T01:20:45.365Z');
|
INSERT INTO branches (branchId, noteId, parentNoteId, notePosition, prefix, isExpanded, isDeleted, dateModified) VALUES ('QJAcYJ1gGUh9', 'L1Ox40M1aEyy', '3RkyK9LI18dO', 0, null, 0, 0, '2017-12-23T01:20:45.365Z');
|
||||||
|
|||||||
5
db/migrations/0087__add_type_mime_to_note_revision.sql
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
ALTER TABLE note_revisions ADD type TEXT DEFAULT '' NOT NULL;
|
||||||
|
ALTER TABLE note_revisions ADD mime TEXT DEFAULT '' NOT NULL;
|
||||||
|
|
||||||
|
UPDATE note_revisions SET type = (SELECT type FROM notes WHERE notes.noteId = note_revisions.noteId);
|
||||||
|
UPDATE note_revisions SET mime = (SELECT mime FROM notes WHERE notes.noteId = note_revisions.noteId);
|
||||||
34
db/migrations/0088__non_null_note_title_content.sql
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
CREATE TABLE event_logc027
|
||||||
|
(
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
|
||||||
|
noteId TEXT,
|
||||||
|
comment TEXT,
|
||||||
|
dateAdded TEXT NOT NULL
|
||||||
|
);
|
||||||
|
INSERT INTO event_logc027(id, noteId, comment, dateAdded) SELECT id, noteId, comment, dateAdded FROM event_log;
|
||||||
|
DROP TABLE event_log;
|
||||||
|
ALTER TABLE event_logc027 RENAME TO event_log;
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS "notes_mig" (
|
||||||
|
`noteId` TEXT NOT NULL,
|
||||||
|
`title` TEXT NOT NULL DEFAULT "unnamed",
|
||||||
|
`content` TEXT NOT NULL DEFAULT "",
|
||||||
|
`isProtected` INT NOT NULL DEFAULT 0,
|
||||||
|
`isDeleted` INT NOT NULL DEFAULT 0,
|
||||||
|
`dateCreated` TEXT NOT NULL,
|
||||||
|
`dateModified` TEXT NOT NULL,
|
||||||
|
type TEXT NOT NULL DEFAULT 'text',
|
||||||
|
mime TEXT NOT NULL DEFAULT 'text/html',
|
||||||
|
PRIMARY KEY(`noteId`)
|
||||||
|
);
|
||||||
|
|
||||||
|
INSERT INTO notes_mig (noteId, title, content, isProtected, isDeleted, dateCreated, dateModified, type, mime)
|
||||||
|
SELECT noteId, title, content, isProtected, isDeleted, dateCreated, dateModified, type, mime FROM notes;
|
||||||
|
|
||||||
|
DROP TABLE notes;
|
||||||
|
|
||||||
|
ALTER TABLE notes_mig RENAME TO notes;
|
||||||
|
|
||||||
|
CREATE INDEX `IDX_notes_isDeleted` ON `notes` (
|
||||||
|
`isDeleted`
|
||||||
|
);
|
||||||
5
db/migrations/0089__add_root_branch.sql
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
INSERT INTO branches (branchId, noteId, parentNoteId, notePosition, prefix, isExpanded, dateModified)
|
||||||
|
VALUES ('root', 'root', 'none', 0, null, 1, '2018-01-01T00:00:00.000Z');
|
||||||
|
|
||||||
|
INSERT INTO sync (entityName, entityId, sourceId, syncDate)
|
||||||
|
VALUES ('branches' ,'root', 'SYNC_FILL', '2018-01-01T00:00:00.000Z');
|
||||||
1
db/migrations/0090__branch_index.sql
Normal file
@@ -0,0 +1 @@
|
|||||||
|
CREATE INDEX IDX_branches_parentNoteId ON branches (parentNoteId);
|
||||||
2
db/migrations/0091__drop_isDeleted_index.sql
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
-- index confuses planner and is mostly useless anyway since we're mostly used in non-deleted notes (which are presumably majority)
|
||||||
|
DROP INDEX IDX_notes_isDeleted;
|
||||||
2
db/migrations/0092__add_type_index.sql
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
create index IDX_notes_type
|
||||||
|
on notes (type);
|
||||||
9
db/migrations/0093__add_hash_field.sql
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
ALTER TABLE notes ADD hash TEXT DEFAULT "" NOT NULL;
|
||||||
|
ALTER TABLE branches ADD hash TEXT DEFAULT "" NOT NULL;
|
||||||
|
ALTER TABLE note_revisions ADD hash TEXT DEFAULT "" NOT NULL;
|
||||||
|
ALTER TABLE recent_notes ADD hash TEXT DEFAULT "" NOT NULL;
|
||||||
|
ALTER TABLE options ADD hash TEXT DEFAULT "" NOT NULL;
|
||||||
|
ALTER TABLE note_images ADD hash TEXT DEFAULT "" NOT NULL;
|
||||||
|
ALTER TABLE images ADD hash TEXT DEFAULT "" NOT NULL;
|
||||||
|
ALTER TABLE labels ADD hash TEXT DEFAULT "" NOT NULL;
|
||||||
|
ALTER TABLE api_tokens ADD hash TEXT DEFAULT "" NOT NULL;
|
||||||
30
db/migrations/0094__unify_auditing_fields.sql
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
ALTER TABLE branches ADD dateCreated TEXT NOT NULL DEFAULT '1970-01-01T00:00:00.000Z';
|
||||||
|
|
||||||
|
CREATE TABLE `event_log_mig` (
|
||||||
|
`id` INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
|
||||||
|
`noteId` TEXT,
|
||||||
|
`comment` TEXT,
|
||||||
|
`dateCreated` TEXT NOT NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
INSERT INTO event_log_mig (id, noteId, comment, dateCreated)
|
||||||
|
SELECT id, noteId, comment, dateAdded FROM event_log;
|
||||||
|
|
||||||
|
DROP TABLE event_log;
|
||||||
|
ALTER TABLE event_log_mig RENAME TO event_log;
|
||||||
|
|
||||||
|
ALTER TABLE options ADD dateCreated TEXT NOT NULL DEFAULT '1970-01-01T00:00:00.000Z';
|
||||||
|
|
||||||
|
CREATE TABLE `recent_notes_mig` (
|
||||||
|
`branchId` TEXT NOT NULL PRIMARY KEY,
|
||||||
|
`notePath` TEXT NOT NULL,
|
||||||
|
hash TEXT DEFAULT "" NOT NULL,
|
||||||
|
`dateCreated` TEXT NOT NULL,
|
||||||
|
isDeleted INT
|
||||||
|
);
|
||||||
|
|
||||||
|
INSERT INTO recent_notes_mig (branchId, notePath, hash, dateCreated, isDeleted)
|
||||||
|
SELECT branchId, notePath, hash, dateAccessed, isDeleted FROM recent_notes;
|
||||||
|
|
||||||
|
DROP TABLE recent_notes;
|
||||||
|
ALTER TABLE recent_notes_mig RENAME TO recent_notes;
|
||||||
1
db/migrations/0095__mime_type_for_render.sql
Normal file
@@ -0,0 +1 @@
|
|||||||
|
UPDATE notes SET mime = 'text/html' WHERE type = 'render';
|
||||||
29
db/migrations/0096__unify_surrogate_keys.sql
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
CREATE TABLE `event_log_mig` (
|
||||||
|
`eventId` TEXT NOT NULL PRIMARY KEY,
|
||||||
|
`noteId` TEXT,
|
||||||
|
`comment` TEXT,
|
||||||
|
`dateCreated` TEXT NOT NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
INSERT INTO event_log_mig (eventId, noteId, comment, dateCreated)
|
||||||
|
SELECT id, noteId, comment, dateCreated FROM event_log;
|
||||||
|
|
||||||
|
DROP TABLE event_log;
|
||||||
|
ALTER TABLE event_log_mig RENAME TO event_log;
|
||||||
|
|
||||||
|
create table options_mig
|
||||||
|
(
|
||||||
|
optionId TEXT NOT NULL PRIMARY KEY,
|
||||||
|
name TEXT not null,
|
||||||
|
value TEXT,
|
||||||
|
dateModified INT,
|
||||||
|
isSynced INTEGER default 0 not null,
|
||||||
|
hash TEXT default "" not null,
|
||||||
|
dateCreated TEXT default '1970-01-01T00:00:00.000Z' not null
|
||||||
|
);
|
||||||
|
|
||||||
|
INSERT INTO options_mig (optionId, name, value, dateModified, isSynced, hash, dateCreated)
|
||||||
|
SELECT name || "_key", name, value, dateModified, isSynced, hash, dateCreated FROM options;
|
||||||
|
|
||||||
|
DROP TABLE options;
|
||||||
|
ALTER TABLE options_mig RENAME TO options;
|
||||||
2
db/migrations/0097__add_zoomFactor.sql
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
INSERT INTO options (optionId, name, value, dateCreated, dateModified, isSynced)
|
||||||
|
VALUES ('zoomFactor_key', 'zoomFactor', '1.0', '2018-06-01T03:35:55.041Z', '2018-06-01T03:35:55.041Z', 0);
|
||||||
1
db/migrations/0098__rename_hideInAutocomplete.sql
Normal file
@@ -0,0 +1 @@
|
|||||||
|
UPDATE labels SET name = 'archived' WHERE name = 'hideInAutocomplete'
|
||||||
2
db/migrations/0099__add_theme_option.sql
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
INSERT INTO options (optionId, name, value, dateCreated, dateModified, isSynced)
|
||||||
|
VALUES ('theme_key', 'theme', 'white', '2018-06-01T03:35:55.041Z', '2018-06-01T03:35:55.041Z', 0);
|
||||||
15
db/migrations/0100__remove_optionId.sql
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
create table options_mig
|
||||||
|
(
|
||||||
|
name TEXT not null PRIMARY KEY,
|
||||||
|
value TEXT,
|
||||||
|
dateModified INT,
|
||||||
|
isSynced INTEGER default 0 not null,
|
||||||
|
hash TEXT default "" not null,
|
||||||
|
dateCreated TEXT default '1970-01-01T00:00:00.000Z' not null
|
||||||
|
);
|
||||||
|
|
||||||
|
INSERT INTO options_mig (name, value, dateModified, isSynced, hash, dateCreated)
|
||||||
|
SELECT name, value, dateModified, isSynced, hash, dateCreated FROM options;
|
||||||
|
|
||||||
|
DROP TABLE options;
|
||||||
|
ALTER TABLE options_mig RENAME TO options;
|
||||||
@@ -1,8 +1,3 @@
|
|||||||
CREATE TABLE IF NOT EXISTS "options" (
|
|
||||||
`name` TEXT NOT NULL PRIMARY KEY,
|
|
||||||
`value` TEXT,
|
|
||||||
`dateModified` INT,
|
|
||||||
isSynced INTEGER NOT NULL DEFAULT 0);
|
|
||||||
CREATE TABLE IF NOT EXISTS "sync" (
|
CREATE TABLE IF NOT EXISTS "sync" (
|
||||||
`id` INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
|
`id` INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
|
||||||
`entityName` TEXT NOT NULL,
|
`entityName` TEXT NOT NULL,
|
||||||
@@ -21,28 +16,6 @@ CREATE TABLE IF NOT EXISTS "source_ids" (
|
|||||||
`dateCreated` TEXT NOT NULL,
|
`dateCreated` TEXT NOT NULL,
|
||||||
PRIMARY KEY(`sourceId`)
|
PRIMARY KEY(`sourceId`)
|
||||||
);
|
);
|
||||||
CREATE TABLE IF NOT EXISTS "notes" (
|
|
||||||
`noteId` TEXT NOT NULL,
|
|
||||||
`title` TEXT,
|
|
||||||
`content` TEXT,
|
|
||||||
`isProtected` INT NOT NULL DEFAULT 0,
|
|
||||||
`isDeleted` INT NOT NULL DEFAULT 0,
|
|
||||||
`dateCreated` TEXT NOT NULL,
|
|
||||||
`dateModified` TEXT NOT NULL,
|
|
||||||
type TEXT NOT NULL DEFAULT 'text',
|
|
||||||
mime TEXT NOT NULL DEFAULT 'text/html',
|
|
||||||
PRIMARY KEY(`noteId`)
|
|
||||||
);
|
|
||||||
CREATE INDEX `IDX_notes_isDeleted` ON `notes` (
|
|
||||||
`isDeleted`
|
|
||||||
);
|
|
||||||
CREATE TABLE IF NOT EXISTS "event_log" (
|
|
||||||
`id` INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
|
|
||||||
`noteId` TEXT,
|
|
||||||
`comment` TEXT,
|
|
||||||
`dateAdded` TEXT NOT NULL,
|
|
||||||
FOREIGN KEY(noteId) REFERENCES notes(noteId)
|
|
||||||
);
|
|
||||||
CREATE TABLE IF NOT EXISTS "note_revisions" (
|
CREATE TABLE IF NOT EXISTS "note_revisions" (
|
||||||
`noteRevisionId` TEXT NOT NULL PRIMARY KEY,
|
`noteRevisionId` TEXT NOT NULL PRIMARY KEY,
|
||||||
`noteId` TEXT NOT NULL,
|
`noteId` TEXT NOT NULL,
|
||||||
@@ -51,7 +24,7 @@ CREATE TABLE IF NOT EXISTS "note_revisions" (
|
|||||||
`isProtected` INT NOT NULL DEFAULT 0,
|
`isProtected` INT NOT NULL DEFAULT 0,
|
||||||
`dateModifiedFrom` TEXT NOT NULL,
|
`dateModifiedFrom` TEXT NOT NULL,
|
||||||
`dateModifiedTo` TEXT NOT NULL
|
`dateModifiedTo` TEXT NOT NULL
|
||||||
);
|
, type TEXT DEFAULT '' NOT NULL, mime TEXT DEFAULT '' NOT NULL, hash TEXT DEFAULT "" NOT NULL);
|
||||||
CREATE INDEX `IDX_note_revisions_noteId` ON `note_revisions` (
|
CREATE INDEX `IDX_note_revisions_noteId` ON `note_revisions` (
|
||||||
`noteId`
|
`noteId`
|
||||||
);
|
);
|
||||||
@@ -71,7 +44,7 @@ CREATE TABLE IF NOT EXISTS "images"
|
|||||||
isDeleted INT NOT NULL DEFAULT 0,
|
isDeleted INT NOT NULL DEFAULT 0,
|
||||||
dateModified TEXT NOT NULL,
|
dateModified TEXT NOT NULL,
|
||||||
dateCreated TEXT NOT NULL
|
dateCreated TEXT NOT NULL
|
||||||
);
|
, hash TEXT DEFAULT "" NOT NULL);
|
||||||
CREATE TABLE note_images
|
CREATE TABLE note_images
|
||||||
(
|
(
|
||||||
noteImageId TEXT PRIMARY KEY NOT NULL,
|
noteImageId TEXT PRIMARY KEY NOT NULL,
|
||||||
@@ -80,7 +53,7 @@ CREATE TABLE note_images
|
|||||||
isDeleted INT NOT NULL DEFAULT 0,
|
isDeleted INT NOT NULL DEFAULT 0,
|
||||||
dateModified TEXT NOT NULL,
|
dateModified TEXT NOT NULL,
|
||||||
dateCreated TEXT NOT NULL
|
dateCreated TEXT NOT NULL
|
||||||
);
|
, hash TEXT DEFAULT "" NOT NULL);
|
||||||
CREATE INDEX IDX_note_images_noteId ON note_images (noteId);
|
CREATE INDEX IDX_note_images_noteId ON note_images (noteId);
|
||||||
CREATE INDEX IDX_note_images_imageId ON note_images (imageId);
|
CREATE INDEX IDX_note_images_imageId ON note_images (imageId);
|
||||||
CREATE INDEX IDX_note_images_noteId_imageId ON note_images (noteId, imageId);
|
CREATE INDEX IDX_note_images_noteId_imageId ON note_images (noteId, imageId);
|
||||||
@@ -90,7 +63,7 @@ CREATE TABLE IF NOT EXISTS "api_tokens"
|
|||||||
token TEXT NOT NULL,
|
token TEXT NOT NULL,
|
||||||
dateCreated TEXT NOT NULL,
|
dateCreated TEXT NOT NULL,
|
||||||
isDeleted INT NOT NULL DEFAULT 0
|
isDeleted INT NOT NULL DEFAULT 0
|
||||||
);
|
, hash TEXT DEFAULT "" NOT NULL);
|
||||||
CREATE TABLE IF NOT EXISTS "branches" (
|
CREATE TABLE IF NOT EXISTS "branches" (
|
||||||
`branchId` TEXT NOT NULL,
|
`branchId` TEXT NOT NULL,
|
||||||
`noteId` TEXT NOT NULL,
|
`noteId` TEXT NOT NULL,
|
||||||
@@ -99,7 +72,7 @@ CREATE TABLE IF NOT EXISTS "branches" (
|
|||||||
`prefix` TEXT,
|
`prefix` TEXT,
|
||||||
`isExpanded` BOOLEAN,
|
`isExpanded` BOOLEAN,
|
||||||
`isDeleted` INTEGER NOT NULL DEFAULT 0,
|
`isDeleted` INTEGER NOT NULL DEFAULT 0,
|
||||||
`dateModified` TEXT NOT NULL,
|
`dateModified` TEXT NOT NULL, hash TEXT DEFAULT "" NOT NULL, dateCreated TEXT NOT NULL DEFAULT '1970-01-01T00:00:00.000Z',
|
||||||
PRIMARY KEY(`branchId`)
|
PRIMARY KEY(`branchId`)
|
||||||
);
|
);
|
||||||
CREATE INDEX `IDX_branches_noteId` ON `branches` (
|
CREATE INDEX `IDX_branches_noteId` ON `branches` (
|
||||||
@@ -109,12 +82,6 @@ CREATE INDEX `IDX_branches_noteId_parentNoteId` ON `branches` (
|
|||||||
`noteId`,
|
`noteId`,
|
||||||
`parentNoteId`
|
`parentNoteId`
|
||||||
);
|
);
|
||||||
CREATE TABLE IF NOT EXISTS "recent_notes" (
|
|
||||||
`branchId` TEXT NOT NULL PRIMARY KEY,
|
|
||||||
`notePath` TEXT NOT NULL,
|
|
||||||
`dateAccessed` TEXT NOT NULL,
|
|
||||||
isDeleted INT
|
|
||||||
);
|
|
||||||
CREATE TABLE labels
|
CREATE TABLE labels
|
||||||
(
|
(
|
||||||
labelId TEXT not null primary key,
|
labelId TEXT not null primary key,
|
||||||
@@ -125,8 +92,45 @@ CREATE TABLE labels
|
|||||||
dateCreated TEXT not null,
|
dateCreated TEXT not null,
|
||||||
dateModified TEXT not null,
|
dateModified TEXT not null,
|
||||||
isDeleted INT not null
|
isDeleted INT not null
|
||||||
);
|
, hash TEXT DEFAULT "" NOT NULL);
|
||||||
CREATE INDEX IDX_labels_name_value
|
CREATE INDEX IDX_labels_name_value
|
||||||
on labels (name, value);
|
on labels (name, value);
|
||||||
CREATE INDEX IDX_labels_noteId
|
CREATE INDEX IDX_labels_noteId
|
||||||
on labels (noteId);
|
on labels (noteId);
|
||||||
|
CREATE TABLE IF NOT EXISTS "notes" (
|
||||||
|
`noteId` TEXT NOT NULL,
|
||||||
|
`title` TEXT NOT NULL DEFAULT "unnamed",
|
||||||
|
`content` TEXT NOT NULL DEFAULT "",
|
||||||
|
`isProtected` INT NOT NULL DEFAULT 0,
|
||||||
|
`isDeleted` INT NOT NULL DEFAULT 0,
|
||||||
|
`dateCreated` TEXT NOT NULL,
|
||||||
|
`dateModified` TEXT NOT NULL,
|
||||||
|
type TEXT NOT NULL DEFAULT 'text',
|
||||||
|
mime TEXT NOT NULL DEFAULT 'text/html', hash TEXT DEFAULT "" NOT NULL,
|
||||||
|
PRIMARY KEY(`noteId`)
|
||||||
|
);
|
||||||
|
CREATE INDEX IDX_branches_parentNoteId ON branches (parentNoteId);
|
||||||
|
CREATE INDEX IDX_notes_type
|
||||||
|
on notes (type);
|
||||||
|
CREATE TABLE IF NOT EXISTS "recent_notes" (
|
||||||
|
`branchId` TEXT NOT NULL PRIMARY KEY,
|
||||||
|
`notePath` TEXT NOT NULL,
|
||||||
|
hash TEXT DEFAULT "" NOT NULL,
|
||||||
|
`dateCreated` TEXT NOT NULL,
|
||||||
|
isDeleted INT
|
||||||
|
);
|
||||||
|
CREATE TABLE IF NOT EXISTS "event_log" (
|
||||||
|
`eventId` TEXT NOT NULL PRIMARY KEY,
|
||||||
|
`noteId` TEXT,
|
||||||
|
`comment` TEXT,
|
||||||
|
`dateCreated` TEXT NOT NULL
|
||||||
|
);
|
||||||
|
CREATE TABLE IF NOT EXISTS "options"
|
||||||
|
(
|
||||||
|
name TEXT not null PRIMARY KEY,
|
||||||
|
value TEXT,
|
||||||
|
dateModified INT,
|
||||||
|
isSynced INTEGER default 0 not null,
|
||||||
|
hash TEXT default "" not null,
|
||||||
|
dateCreated TEXT default '1970-01-01T00:00:00.000Z' not null
|
||||||
|
);
|
||||||
|
|||||||
@@ -76,12 +76,12 @@ app.on('ready', () => {
|
|||||||
const dateNoteService = require('./src/services/date_notes');
|
const dateNoteService = require('./src/services/date_notes');
|
||||||
const dateUtils = require('./src/services/date_utils');
|
const dateUtils = require('./src/services/date_utils');
|
||||||
|
|
||||||
const parentNoteId = await dateNoteService.getDateNoteId(dateUtils.nowDate());
|
const parentNote = await dateNoteService.getDateNote(dateUtils.nowDate());
|
||||||
|
|
||||||
// window may be hidden / not in focus
|
// window may be hidden / not in focus
|
||||||
mainWindow.focus();
|
mainWindow.focus();
|
||||||
|
|
||||||
mainWindow.webContents.send('create-day-sub-note', parentNoteId);
|
mainWindow.webContents.send('create-day-sub-note', parentNote.noteId);
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!result) {
|
if (!result) {
|
||||||
|
|||||||
8507
package-lock.json
generated
44
package.json
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "trilium",
|
"name": "trilium",
|
||||||
"description": "Trilium Notes",
|
"description": "Trilium Notes",
|
||||||
"version": "0.10.1-beta",
|
"version": "0.16.0",
|
||||||
"license": "AGPL-3.0-only",
|
"license": "AGPL-3.0-only",
|
||||||
"main": "electron.js",
|
"main": "electron.js",
|
||||||
"repository": {
|
"repository": {
|
||||||
@@ -9,11 +9,11 @@
|
|||||||
"url": "https://github.com/zadam/trilium.git"
|
"url": "https://github.com/zadam/trilium.git"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "node ./bin/www",
|
"start": "node ./src/www",
|
||||||
"test-electron": "xo",
|
"test-electron": "xo",
|
||||||
"rebuild-electron": "electron-rebuild",
|
"rebuild-electron": "electron-rebuild",
|
||||||
"start-electron": "electron . --disable-gpu",
|
"start-electron": "electron . --disable-gpu",
|
||||||
"build-electron": "electron-packager . --out=dist --asar --overwrite --platform=win32,linux --arch=ia32,x64",
|
"build-electron": "electron-packager . --out=dist --asar --overwrite --platform=win32,linux --arch=ia32,x64 --app-version=",
|
||||||
"start-forge": "electron-forge start",
|
"start-forge": "electron-forge start",
|
||||||
"package-forge": "electron-forge package",
|
"package-forge": "electron-forge package",
|
||||||
"make-forge": "electron-forge make",
|
"make-forge": "electron-forge make",
|
||||||
@@ -21,22 +21,20 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"async-mutex": "^0.1.3",
|
"async-mutex": "^0.1.3",
|
||||||
"axios": "^0.17.1",
|
"axios": "^0.18",
|
||||||
"body-parser": "~1.18.2",
|
"body-parser": "^1.18.3",
|
||||||
"cls-hooked": "^4.2.2",
|
"cls-hooked": "^4.2.2",
|
||||||
"cookie-parser": "~1.4.3",
|
"cookie-parser": "~1.4.3",
|
||||||
"debug": "~3.1.0",
|
"debug": "~3.1.0",
|
||||||
"devtron": "^1.4.0",
|
"devtron": "^1.4.0",
|
||||||
"ejs": "~2.5.7",
|
"ejs": "~2.6.1",
|
||||||
"electron": "^2.0.0-beta.5",
|
|
||||||
"electron-debug": "^1.5.0",
|
"electron-debug": "^1.5.0",
|
||||||
"electron-dl": "^1.11.0",
|
"electron-dl": "^1.12.0",
|
||||||
"electron-in-page-search": "^1.2.4",
|
"electron-in-page-search": "^1.3.2",
|
||||||
"electron-rebuild": "^1.7.3",
|
|
||||||
"express": "~4.16.3",
|
"express": "~4.16.3",
|
||||||
"express-session": "^1.15.6",
|
"express-session": "^1.15.6",
|
||||||
"fs-extra": "^4.0.3",
|
"fs-extra": "^6.0.1",
|
||||||
"helmet": "^3.12.0",
|
"helmet": "^3.12.1",
|
||||||
"html": "^1.0.0",
|
"html": "^1.0.0",
|
||||||
"image-type": "^3.0.0",
|
"image-type": "^3.0.0",
|
||||||
"imagemin": "^5.3.1",
|
"imagemin": "^5.3.1",
|
||||||
@@ -45,30 +43,34 @@
|
|||||||
"imagemin-pngquant": "^5.1.0",
|
"imagemin-pngquant": "^5.1.0",
|
||||||
"ini": "^1.3.5",
|
"ini": "^1.3.5",
|
||||||
"jimp": "^0.2.28",
|
"jimp": "^0.2.28",
|
||||||
"moment": "^2.21.0",
|
"moment": "^2.22.1",
|
||||||
"multer": "^1.3.0",
|
"multer": "^1.3.0",
|
||||||
"open": "0.0.5",
|
"open": "0.0.5",
|
||||||
"rand-token": "^0.4.0",
|
"rand-token": "^0.4.0",
|
||||||
"request": "^2.85.0",
|
"rcedit": "^1.1.0",
|
||||||
|
"request": "^2.87.0",
|
||||||
"request-promise": "^4.2.2",
|
"request-promise": "^4.2.2",
|
||||||
"rimraf": "^2.6.2",
|
"rimraf": "^2.6.2",
|
||||||
"sanitize-filename": "^1.6.1",
|
"sanitize-filename": "^1.6.1",
|
||||||
"scrypt": "^6.0.3",
|
"scrypt": "^6.0.3",
|
||||||
"serve-favicon": "~2.4.5",
|
"serve-favicon": "~2.5.0",
|
||||||
"session-file-store": "^1.2.0",
|
"session-file-store": "^1.2.0",
|
||||||
"simple-node-logger": "^0.93.37",
|
"simple-node-logger": "^0.93.37",
|
||||||
"sqlite": "^2.9.1",
|
"sqlite": "^2.9.2",
|
||||||
"tar-stream": "^1.5.5",
|
"tar-stream": "^1.6.1",
|
||||||
"unescape": "^1.0.1",
|
"unescape": "^1.0.1",
|
||||||
"ws": "^3.3.3"
|
"ws": "^5.2.0",
|
||||||
|
"xml2js": "^0.4.19"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"electron": "^2.0.1",
|
||||||
"electron-compile": "^6.4.2",
|
"electron-compile": "^6.4.2",
|
||||||
"electron-packager": "^11.1.0",
|
"electron-packager": "^12.1.0",
|
||||||
"electron-prebuilt-compile": "2.0.0-beta.5",
|
"electron-prebuilt-compile": "2.0.0",
|
||||||
|
"electron-rebuild": "^1.7.3",
|
||||||
"lorem-ipsum": "^1.0.4",
|
"lorem-ipsum": "^1.0.4",
|
||||||
"tape": "^4.9.0",
|
"tape": "^4.9.0",
|
||||||
"xo": "^0.18.0"
|
"xo": "^0.21.1"
|
||||||
},
|
},
|
||||||
"config": {
|
"config": {
|
||||||
"forge": {
|
"forge": {
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ const dateUtils = require('../services/date_utils');
|
|||||||
class ApiToken extends Entity {
|
class ApiToken extends Entity {
|
||||||
static get tableName() { return "api_tokens"; }
|
static get tableName() { return "api_tokens"; }
|
||||||
static get primaryKeyName() { return "apiTokenId"; }
|
static get primaryKeyName() { return "apiTokenId"; }
|
||||||
|
static get hashedProperties() { return ["apiTokenId", "token", "dateCreated", "isDeleted"]; }
|
||||||
|
|
||||||
beforeSaving() {
|
beforeSaving() {
|
||||||
super.beforeSaving();
|
super.beforeSaving();
|
||||||
|
|||||||
@@ -8,6 +8,8 @@ const sql = require('../services/sql');
|
|||||||
class Branch extends Entity {
|
class Branch extends Entity {
|
||||||
static get tableName() { return "branches"; }
|
static get tableName() { return "branches"; }
|
||||||
static get primaryKeyName() { return "branchId"; }
|
static get primaryKeyName() { return "branchId"; }
|
||||||
|
// notePosition is not part of hash because it would produce a lot of updates in case of reordering
|
||||||
|
static get hashedProperties() { return ["branchId", "noteId", "parentNoteId", "dateModified", "isDeleted", "prefix"]; }
|
||||||
|
|
||||||
async getNote() {
|
async getNote() {
|
||||||
return await repository.getEntity("SELECT * FROM notes WHERE noteId = ?", [this.noteId]);
|
return await repository.getEntity("SELECT * FROM notes WHERE noteId = ?", [this.noteId]);
|
||||||
@@ -25,7 +27,11 @@ class Branch extends Entity {
|
|||||||
this.isDeleted = false;
|
this.isDeleted = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.dateModified = dateUtils.nowDate()
|
if (!this.dateCreated) {
|
||||||
|
this.dateCreated = dateUtils.nowDate();
|
||||||
|
}
|
||||||
|
|
||||||
|
this.dateModified = dateUtils.nowDate();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -14,6 +14,14 @@ class Entity {
|
|||||||
if (!this[this.constructor.primaryKeyName]) {
|
if (!this[this.constructor.primaryKeyName]) {
|
||||||
this[this.constructor.primaryKeyName] = utils.newEntityId();
|
this[this.constructor.primaryKeyName] = utils.newEntityId();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let contentToHash = "";
|
||||||
|
|
||||||
|
for (const propertyName of this.constructor.hashedProperties) {
|
||||||
|
contentToHash += "|" + this[propertyName];
|
||||||
|
}
|
||||||
|
|
||||||
|
this["hash"] = utils.hash(contentToHash).substr(0, 10);
|
||||||
}
|
}
|
||||||
|
|
||||||
async save() {
|
async save() {
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ const Branch = require('../entities/branch');
|
|||||||
const Label = require('../entities/label');
|
const Label = require('../entities/label');
|
||||||
const RecentNote = require('../entities/recent_note');
|
const RecentNote = require('../entities/recent_note');
|
||||||
const ApiToken = require('../entities/api_token');
|
const ApiToken = require('../entities/api_token');
|
||||||
|
const Option = require('../entities/option');
|
||||||
const repository = require('../services/repository');
|
const repository = require('../services/repository');
|
||||||
|
|
||||||
function createEntityFromRow(row) {
|
function createEntityFromRow(row) {
|
||||||
@@ -35,6 +36,9 @@ function createEntityFromRow(row) {
|
|||||||
else if (row.noteId) {
|
else if (row.noteId) {
|
||||||
entity = new Note(row);
|
entity = new Note(row);
|
||||||
}
|
}
|
||||||
|
else if (row.name) {
|
||||||
|
entity = new Option(row);
|
||||||
|
}
|
||||||
else {
|
else {
|
||||||
throw new Error('Unknown entity type for row: ' + JSON.stringify(row));
|
throw new Error('Unknown entity type for row: ' + JSON.stringify(row));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ const dateUtils = require('../services/date_utils');
|
|||||||
class Image extends Entity {
|
class Image extends Entity {
|
||||||
static get tableName() { return "images"; }
|
static get tableName() { return "images"; }
|
||||||
static get primaryKeyName() { return "imageId"; }
|
static get primaryKeyName() { return "imageId"; }
|
||||||
|
static get hashedProperties() { return ["imageId", "format", "checksum", "name", "isDeleted", "dateModified", "dateCreated"]; }
|
||||||
|
|
||||||
beforeSaving() {
|
beforeSaving() {
|
||||||
super.beforeSaving();
|
super.beforeSaving();
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ const sql = require('../services/sql');
|
|||||||
class Label extends Entity {
|
class Label extends Entity {
|
||||||
static get tableName() { return "labels"; }
|
static get tableName() { return "labels"; }
|
||||||
static get primaryKeyName() { return "labelId"; }
|
static get primaryKeyName() { return "labelId"; }
|
||||||
|
static get hashedProperties() { return ["labelId", "noteId", "name", "value", "dateModified", "dateCreated"]; }
|
||||||
|
|
||||||
async getNote() {
|
async getNote() {
|
||||||
return await repository.getEntity("SELECT * FROM notes WHERE noteId = ?", [this.noteId]);
|
return await repository.getEntity("SELECT * FROM notes WHERE noteId = ?", [this.noteId]);
|
||||||
|
|||||||
@@ -1,24 +1,33 @@
|
|||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
const Entity = require('./entity');
|
const Entity = require('./entity');
|
||||||
const protected_session = require('../services/protected_session');
|
const protectedSessionService = require('../services/protected_session');
|
||||||
const repository = require('../services/repository');
|
const repository = require('../services/repository');
|
||||||
const dateUtils = require('../services/date_utils');
|
const dateUtils = require('../services/date_utils');
|
||||||
|
|
||||||
class Note extends Entity {
|
class Note extends Entity {
|
||||||
static get tableName() { return "notes"; }
|
static get tableName() { return "notes"; }
|
||||||
static get primaryKeyName() { return "noteId"; }
|
static get primaryKeyName() { return "noteId"; }
|
||||||
|
static get hashedProperties() { return ["noteId", "title", "content", "type", "dateModified", "isProtected", "isDeleted"]; }
|
||||||
|
|
||||||
constructor(row) {
|
constructor(row) {
|
||||||
super(row);
|
super(row);
|
||||||
|
|
||||||
if (this.isProtected) {
|
// check if there's noteId, otherwise this is a new entity which wasn't encrypted yet
|
||||||
protected_session.decryptNote(this);
|
if (this.isProtected && this.noteId) {
|
||||||
|
protectedSessionService.decryptNote(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.isJson()) {
|
this.setContent(this.content);
|
||||||
|
}
|
||||||
|
|
||||||
|
setContent(content) {
|
||||||
|
this.content = content;
|
||||||
|
|
||||||
|
try {
|
||||||
this.jsonContent = JSON.parse(this.content);
|
this.jsonContent = JSON.parse(this.content);
|
||||||
}
|
}
|
||||||
|
catch(e) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
isJson() {
|
isJson() {
|
||||||
@@ -31,7 +40,7 @@ class Note extends Entity {
|
|||||||
}
|
}
|
||||||
|
|
||||||
isHtml() {
|
isHtml() {
|
||||||
return (this.type === "code" || this.type === "file") && this.mime === "text/html";
|
return (this.type === "code" || this.type === "file" || this.type === "render") && this.mime === "text/html";
|
||||||
}
|
}
|
||||||
|
|
||||||
getScriptEnv() {
|
getScriptEnv() {
|
||||||
@@ -133,12 +142,12 @@ class Note extends Entity {
|
|||||||
beforeSaving() {
|
beforeSaving() {
|
||||||
super.beforeSaving();
|
super.beforeSaving();
|
||||||
|
|
||||||
if (this.isJson()) {
|
if (this.isJson() && this.jsonContent) {
|
||||||
this.content = JSON.stringify(this.jsonContent, null, '\t');
|
this.content = JSON.stringify(this.jsonContent, null, '\t');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.isProtected) {
|
if (this.isProtected) {
|
||||||
protected_session.encryptNote(this);
|
protectedSessionService.encryptNote(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!this.isDeleted) {
|
if (!this.isDeleted) {
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ const dateUtils = require('../services/date_utils');
|
|||||||
class NoteImage extends Entity {
|
class NoteImage extends Entity {
|
||||||
static get tableName() { return "note_images"; }
|
static get tableName() { return "note_images"; }
|
||||||
static get primaryKeyName() { return "noteImageId"; }
|
static get primaryKeyName() { return "noteImageId"; }
|
||||||
|
static get hashedProperties() { return ["noteImageId", "noteId", "imageId", "isDeleted", "dateModified", "dateCreated"]; }
|
||||||
|
|
||||||
async getNote() {
|
async getNote() {
|
||||||
return await repository.getEntity("SELECT * FROM notes WHERE noteId = ?", [this.noteId]);
|
return await repository.getEntity("SELECT * FROM notes WHERE noteId = ?", [this.noteId]);
|
||||||
|
|||||||
@@ -1,19 +1,19 @@
|
|||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
const Entity = require('./entity');
|
const Entity = require('./entity');
|
||||||
const protected_session = require('../services/protected_session');
|
const protectedSessionService = require('../services/protected_session');
|
||||||
const utils = require('../services/utils');
|
|
||||||
const repository = require('../services/repository');
|
const repository = require('../services/repository');
|
||||||
|
|
||||||
class NoteRevision extends Entity {
|
class NoteRevision extends Entity {
|
||||||
static get tableName() { return "note_revisions"; }
|
static get tableName() { return "note_revisions"; }
|
||||||
static get primaryKeyName() { return "noteRevisionId"; }
|
static get primaryKeyName() { return "noteRevisionId"; }
|
||||||
|
static get hashedProperties() { return ["noteRevisionId", "noteId", "title", "content", "dateModifiedFrom", "dateModifiedTo"]; }
|
||||||
|
|
||||||
constructor(row) {
|
constructor(row) {
|
||||||
super(row);
|
super(row);
|
||||||
|
|
||||||
if (this.isProtected) {
|
if (this.isProtected) {
|
||||||
protected_session.decryptNoteRevision(this);
|
protectedSessionService.decryptNoteRevision(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -25,7 +25,7 @@ class NoteRevision extends Entity {
|
|||||||
super.beforeSaving();
|
super.beforeSaving();
|
||||||
|
|
||||||
if (this.isProtected) {
|
if (this.isProtected) {
|
||||||
protected_session.encryptNoteRevision(this);
|
protectedSessionService.encryptNoteRevision(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
18
src/entities/option.js
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
"use strict";
|
||||||
|
|
||||||
|
const Entity = require('./entity');
|
||||||
|
const dateUtils = require('../services/date_utils');
|
||||||
|
|
||||||
|
class Option extends Entity {
|
||||||
|
static get tableName() { return "options"; }
|
||||||
|
static get primaryKeyName() { return "name"; }
|
||||||
|
static get hashedProperties() { return ["name", "value"]; }
|
||||||
|
|
||||||
|
beforeSaving() {
|
||||||
|
super.beforeSaving();
|
||||||
|
|
||||||
|
this.dateModified = dateUtils.nowDate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = Option;
|
||||||
@@ -1,10 +1,24 @@
|
|||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
const Entity = require('./entity');
|
const Entity = require('./entity');
|
||||||
|
const dateUtils = require('../services/date_utils');
|
||||||
|
|
||||||
class RecentNote extends Entity {
|
class RecentNote extends Entity {
|
||||||
static get tableName() { return "recent_notes"; }
|
static get tableName() { return "recent_notes"; }
|
||||||
static get primaryKeyName() { return "branchId"; }
|
static get primaryKeyName() { return "branchId"; }
|
||||||
|
static get hashedProperties() { return ["branchId", "notePath", "dateCreated", "isDeleted"]; }
|
||||||
|
|
||||||
|
beforeSaving() {
|
||||||
|
super.beforeSaving();
|
||||||
|
|
||||||
|
if (!this.isDeleted) {
|
||||||
|
this.isDeleted = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this.dateCreated) {
|
||||||
|
this.dateCreated = dateUtils.nowDate();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = RecentNote;
|
module.exports = RecentNote;
|
||||||
BIN
src/public/images/icons/back-24.png
Normal file
|
After Width: | Height: | Size: 511 B |
|
Before Width: | Height: | Size: 245 B After Width: | Height: | Size: 245 B |
|
Before Width: | Height: | Size: 339 B After Width: | Height: | Size: 339 B |
|
Before Width: | Height: | Size: 463 B After Width: | Height: | Size: 463 B |
BIN
src/public/images/icons/edit-20.png
Normal file
|
After Width: | Height: | Size: 312 B |
|
Before Width: | Height: | Size: 288 B After Width: | Height: | Size: 288 B |
|
Before Width: | Height: | Size: 284 B After Width: | Height: | Size: 284 B |
|
Before Width: | Height: | Size: 292 B After Width: | Height: | Size: 292 B |
BIN
src/public/images/icons/forward-24.png
Normal file
|
After Width: | Height: | Size: 511 B |
|
Before Width: | Height: | Size: 155 B After Width: | Height: | Size: 155 B |
|
Before Width: | Height: | Size: 323 B |
|
Before Width: | Height: | Size: 358 B After Width: | Height: | Size: 358 B |
|
Before Width: | Height: | Size: 252 B After Width: | Height: | Size: 252 B |
BIN
src/public/images/icons/play-20.png
Normal file
|
After Width: | Height: | Size: 288 B |
BIN
src/public/images/icons/save-20.png
Normal file
|
After Width: | Height: | Size: 388 B |
BIN
src/public/images/icons/search-20.png
Normal file
|
After Width: | Height: | Size: 431 B |
|
Before Width: | Height: | Size: 419 B After Width: | Height: | Size: 419 B |
|
Before Width: | Height: | Size: 354 B After Width: | Height: | Size: 354 B |
BIN
src/public/images/icons/shield-20.png
Normal file
|
After Width: | Height: | Size: 388 B |
BIN
src/public/images/icons/shield-off-20.png
Normal file
|
After Width: | Height: | Size: 462 B |
BIN
src/public/images/icons/tree-root-16.png
Normal file
|
After Width: | Height: | Size: 240 B |
|
Before Width: | Height: | Size: 337 B |
BIN
src/public/images/icons/x-20.png
Normal file
|
After Width: | Height: | Size: 259 B |
1
src/public/images/trilium.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" x="0px" y="0px" viewBox="0 0 100 100" enable-background="new 0 0 100 100" xml:space="preserve"><path d="M63.966,45.043c0.008-0.009,0.021-0.021,0.027-0.029c0.938-1.156-0.823-13.453-5.063-20.125 c-1.389-2.186-2.239-3.423-3.219-4.719c-3.907-5.166-6-6.125-6-6.125S35.732,24.78,36.149,44.315 c-1.754,0.065-11.218,7.528-14.826,14.388c-1.206,2.291-1.856,3.645-2.493,5.141c-2.539,5.957-2.33,8.25-2.33,8.25 s16.271,6.79,33.014-3.294c0.007,0.021,0.013,0.046,0.02,0.063c0.537,1.389,12.08,5.979,19.976,5.621 c2.587-0.116,4.084-0.238,5.696-0.444c6.424-0.818,8.298-2.157,8.298-2.157S81.144,54.396,63.966,45.043z M50.787,65.343 c1.059-1.183,4.648-5.853,0.995-11.315c-0.253-0.377-0.496-0.236-0.496-0.236s0.063,10.822-5.162,12.359 c-5.225,1.537-13.886,4.4-20.427,0.455C25,66.186,26.924,53.606,38.544,47.229c0.546,1.599,2.836,6.854,9.292,6.409 c0.453-0.031,0.453-0.313,0.453-0.313s-9.422-5.328-8.156-10.625s3.089-14.236,9.766-17.948c0.714-0.397,10.746,7.593,10.417,20.94 c-1.606-0.319-7.377-1.004-10.226,4.864c-0.198,0.409,0.046,0.549,0.046,0.549s9.31-5.521,13.275-1.789 c3.965,3.733,10.813,9.763,10.71,17.4C74.111,67.533,62.197,72.258,50.787,65.343z M35.613,35.145c0,0-0.991,3.241-0.603,7.524 l-13.393-7.524C21.618,35.145,27.838,30.931,35.613,35.145z M21.193,36.03l13.344,7.612c-3.872,1.872-6.142,4.388-6.142,4.388 C20.78,43.531,21.193,36.03,21.193,36.03z M72.287,49.064c0,0-2.321-2.471-6.23-4.263l13.187-7.881 C79.243,36.92,79.808,44.413,72.287,49.064z M78.687,36.113l-13.237,7.794c0.3-4.291-0.754-7.511-0.754-7.511 C72.383,32.025,78.687,36.113,78.687,36.113z M42.076,73.778c0,0,3.309-0.737,6.845-3.185l0.056,15.361 C48.977,85.955,42.244,82.621,42.076,73.778z M49.956,85.888L50,70.526c3.539,2.445,6.846,3.181,6.846,3.181 C56.686,82.551,49.956,85.888,49.956,85.888z"></path></svg>
|
||||||
|
After Width: | Height: | Size: 1.8 KiB |
@@ -2,7 +2,8 @@ import cloningService from '../services/cloning.js';
|
|||||||
import linkService from '../services/link.js';
|
import linkService from '../services/link.js';
|
||||||
import noteDetailService from '../services/note_detail.js';
|
import noteDetailService from '../services/note_detail.js';
|
||||||
import treeUtils from '../services/tree_utils.js';
|
import treeUtils from '../services/tree_utils.js';
|
||||||
import autocompleteService from '../services/autocomplete.js';
|
import server from "../services/server.js";
|
||||||
|
import noteDetailText from "../services/note_detail_text.js";
|
||||||
|
|
||||||
const $dialog = $("#add-link-dialog");
|
const $dialog = $("#add-link-dialog");
|
||||||
const $form = $("#add-link-form");
|
const $form = $("#add-link-form");
|
||||||
@@ -11,6 +12,7 @@ const $linkTitle = $("#link-title");
|
|||||||
const $clonePrefix = $("#clone-prefix");
|
const $clonePrefix = $("#clone-prefix");
|
||||||
const $linkTitleFormGroup = $("#add-link-title-form-group");
|
const $linkTitleFormGroup = $("#add-link-title-form-group");
|
||||||
const $prefixFormGroup = $("#add-link-prefix-form-group");
|
const $prefixFormGroup = $("#add-link-prefix-form-group");
|
||||||
|
const $linkTypeDiv = $("#add-link-type-div");
|
||||||
const $linkTypes = $("input[name='add-link-type']");
|
const $linkTypes = $("input[name='add-link-type']");
|
||||||
const $linkTypeHtml = $linkTypes.filter('input[value="html"]');
|
const $linkTypeHtml = $linkTypes.filter('input[value="html"]');
|
||||||
|
|
||||||
@@ -45,16 +47,28 @@ async function showDialog() {
|
|||||||
$clonePrefix.val('');
|
$clonePrefix.val('');
|
||||||
$linkTitle.val('');
|
$linkTitle.val('');
|
||||||
|
|
||||||
function setDefaultLinkTitle(noteId) {
|
async function setDefaultLinkTitle(noteId) {
|
||||||
const noteTitle = treeUtils.getNoteTitle(noteId);
|
const noteTitle = await treeUtils.getNoteTitle(noteId);
|
||||||
|
|
||||||
$linkTitle.val(noteTitle);
|
$linkTitle.val(noteTitle);
|
||||||
}
|
}
|
||||||
|
|
||||||
$autoComplete.autocomplete({
|
$autoComplete.autocomplete({
|
||||||
source: await autocompleteService.getAutocompleteItems(),
|
source: async function(request, response) {
|
||||||
minLength: 0,
|
const result = await server.get('autocomplete?query=' + encodeURIComponent(request.term));
|
||||||
change: () => {
|
|
||||||
|
if (result.length > 0) {
|
||||||
|
response(result);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
response([{
|
||||||
|
label: "No results",
|
||||||
|
value: "No results"
|
||||||
|
}]);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
minLength: 2,
|
||||||
|
change: async () => {
|
||||||
const val = $autoComplete.val();
|
const val = $autoComplete.val();
|
||||||
const notePath = linkService.getNodePathFromLabel(val);
|
const notePath = linkService.getNodePathFromLabel(val);
|
||||||
if (!notePath) {
|
if (!notePath) {
|
||||||
@@ -64,16 +78,16 @@ async function showDialog() {
|
|||||||
const noteId = treeUtils.getNoteIdFromNotePath(notePath);
|
const noteId = treeUtils.getNoteIdFromNotePath(notePath);
|
||||||
|
|
||||||
if (noteId) {
|
if (noteId) {
|
||||||
setDefaultLinkTitle(noteId);
|
await setDefaultLinkTitle(noteId);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
// this is called when user goes through autocomplete list with keyboard
|
// this is called when user goes through autocomplete list with keyboard
|
||||||
// at this point the item isn't selected yet so we use supplied ui.item to see WHERE the cursor is
|
// at this point the item isn't selected yet so we use supplied ui.item to see WHERE the cursor is
|
||||||
focus: (event, ui) => {
|
focus: async (event, ui) => {
|
||||||
const notePath = linkService.getNodePathFromLabel(ui.item.value);
|
const notePath = linkService.getNodePathFromLabel(ui.item.value);
|
||||||
const noteId = treeUtils.getNoteIdFromNotePath(notePath);
|
const noteId = treeUtils.getNoteIdFromNotePath(notePath);
|
||||||
|
|
||||||
setDefaultLinkTitle(noteId);
|
await setDefaultLinkTitle(noteId);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -92,7 +106,16 @@ $form.submit(() => {
|
|||||||
|
|
||||||
$dialog.dialog("close");
|
$dialog.dialog("close");
|
||||||
|
|
||||||
linkService.addLinkToEditor(linkTitle, '#' + notePath);
|
const linkHref = '#' + notePath;
|
||||||
|
|
||||||
|
if (hasSelection()) {
|
||||||
|
const editor = noteDetailText.getEditor();
|
||||||
|
|
||||||
|
editor.execute('link', linkHref);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
linkService.addLinkToEditor(linkTitle, linkHref);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else if (linkType === 'selected-to-current') {
|
else if (linkType === 'selected-to-current') {
|
||||||
const prefix = $clonePrefix.val();
|
const prefix = $clonePrefix.val();
|
||||||
@@ -113,17 +136,21 @@ $form.submit(() => {
|
|||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// returns true if user selected some text, false if there's no selection
|
||||||
|
function hasSelection() {
|
||||||
|
const model = noteDetailText.getEditor().model;
|
||||||
|
const selection = model.document.selection;
|
||||||
|
|
||||||
|
return !selection.isCollapsed;
|
||||||
|
}
|
||||||
|
|
||||||
function linkTypeChanged() {
|
function linkTypeChanged() {
|
||||||
const value = $linkTypes.filter(":checked").val();
|
const value = $linkTypes.filter(":checked").val();
|
||||||
|
|
||||||
if (value === 'html') {
|
$linkTitleFormGroup.toggle(!hasSelection() && value === 'html');
|
||||||
$linkTitleFormGroup.show();
|
$prefixFormGroup.toggle(!hasSelection() && value !== 'html');
|
||||||
$prefixFormGroup.hide();
|
|
||||||
}
|
$linkTypeDiv.toggle(!hasSelection());
|
||||||
else {
|
|
||||||
$linkTitleFormGroup.hide();
|
|
||||||
$prefixFormGroup.show();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$linkTypes.change(linkTypeChanged);
|
$linkTypes.change(linkTypeChanged);
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ async function showDialog() {
|
|||||||
|
|
||||||
$treePrefixInput.val(branch.prefix).focus();
|
$treePrefixInput.val(branch.prefix).focus();
|
||||||
|
|
||||||
const noteTitle = treeUtils.getNoteTitle(currentNode.data.noteId);
|
const noteTitle = await treeUtils.getNoteTitle(currentNode.data.noteId);
|
||||||
|
|
||||||
$noteTitle.html(noteTitle);
|
$noteTitle.html(noteTitle);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,10 +19,10 @@ async function showDialog() {
|
|||||||
$list.html('');
|
$list.html('');
|
||||||
|
|
||||||
for (const event of result) {
|
for (const event of result) {
|
||||||
const dateTime = utils.formatDateTime(utils.parseDate(event.dateAdded));
|
const dateTime = utils.formatDateTime(utils.parseDate(event.dateCreated));
|
||||||
|
|
||||||
if (event.noteId) {
|
if (event.noteId) {
|
||||||
const noteLink = linkService.createNoteLink(event.noteId).prop('outerHTML');
|
const noteLink = await linkService.createNoteLink(event.noteId).prop('outerHTML');
|
||||||
|
|
||||||
event.comment = event.comment.replace('<note>', noteLink);
|
event.comment = event.comment.replace('<note>', noteLink);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,13 @@
|
|||||||
import treeService from '../services/tree.js';
|
import treeService from '../services/tree.js';
|
||||||
import linkService from '../services/link.js';
|
import linkService from '../services/link.js';
|
||||||
import utils from '../services/utils.js';
|
import server from '../services/server.js';
|
||||||
import autocompleteService from '../services/autocomplete.js';
|
import searchNotesService from '../services/search_notes.js';
|
||||||
|
|
||||||
const $dialog = $("#jump-to-note-dialog");
|
const $dialog = $("#jump-to-note-dialog");
|
||||||
const $autoComplete = $("#jump-to-note-autocomplete");
|
const $autoComplete = $("#jump-to-note-autocomplete");
|
||||||
const $form = $("#jump-to-note-form");
|
const $form = $("#jump-to-note-form");
|
||||||
|
const $jumpToNoteButton = $("#jump-to-note-button");
|
||||||
|
const $showInFullTextButton = $("#show-in-full-text-button");
|
||||||
|
|
||||||
async function showDialog() {
|
async function showDialog() {
|
||||||
glob.activeDialog = $dialog;
|
glob.activeDialog = $dialog;
|
||||||
@@ -18,8 +20,23 @@ async function showDialog() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
await $autoComplete.autocomplete({
|
await $autoComplete.autocomplete({
|
||||||
source: await utils.stopWatch("building autocomplete", autocompleteService.getAutocompleteItems),
|
source: async function(request, response) {
|
||||||
minLength: 1
|
const result = await server.get('autocomplete?query=' + encodeURIComponent(request.term));
|
||||||
|
|
||||||
|
if (result.length > 0) {
|
||||||
|
response(result);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
response([{
|
||||||
|
label: "No results",
|
||||||
|
value: "No results"
|
||||||
|
}]);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
focus: function(event, ui) {
|
||||||
|
return $(ui.item).val() !== 'No results';
|
||||||
|
},
|
||||||
|
minLength: 2
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -38,12 +55,32 @@ function goToNote() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function showInFullText(e) {
|
||||||
|
// stop from propagating upwards (dangerous especially with ctrl+enter executable javascript notes)
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
|
||||||
|
const searchText = $autoComplete.val();
|
||||||
|
|
||||||
|
searchNotesService.resetSearch();
|
||||||
|
searchNotesService.showSearch();
|
||||||
|
searchNotesService.doSearch(searchText);
|
||||||
|
|
||||||
|
$dialog.dialog('close');
|
||||||
|
}
|
||||||
|
|
||||||
$form.submit(() => {
|
$form.submit(() => {
|
||||||
goToNote();
|
goToNote();
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
$jumpToNoteButton.click(goToNote);
|
||||||
|
|
||||||
|
$showInFullTextButton.click(showInFullText);
|
||||||
|
|
||||||
|
$dialog.bind('keydown', 'ctrl+return', showInFullText);
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
showDialog
|
showDialog
|
||||||
};
|
};
|
||||||
@@ -54,7 +54,13 @@ $list.on('change', () => {
|
|||||||
const revisionItem = revisionItems.find(r => r.noteRevisionId === optVal);
|
const revisionItem = revisionItems.find(r => r.noteRevisionId === optVal);
|
||||||
|
|
||||||
$title.html(revisionItem.title);
|
$title.html(revisionItem.title);
|
||||||
|
|
||||||
|
if (revisionItem.type === 'text') {
|
||||||
$content.html(revisionItem.content);
|
$content.html(revisionItem.content);
|
||||||
|
}
|
||||||
|
else if (revisionItem.type === 'code') {
|
||||||
|
$content.html($("<pre>").text(revisionItem.content));
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
$(document).on('click', "a[action='note-revision']", event => {
|
$(document).on('click', "a[action='note-revision']", event => {
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
import protectedSessionHolder from '../services/protected_session_holder.js';
|
import protectedSessionHolder from '../services/protected_session_holder.js';
|
||||||
import utils from '../services/utils.js';
|
|
||||||
import server from '../services/server.js';
|
import server from '../services/server.js';
|
||||||
import infoService from "../services/info.js";
|
import infoService from "../services/info.js";
|
||||||
|
import zoomService from "../services/zoom.js";
|
||||||
|
import utils from "../services/utils.js";
|
||||||
|
|
||||||
const $dialog = $("#options-dialog");
|
const $dialog = $("#options-dialog");
|
||||||
const $tabs = $("#options-tabs");
|
const $tabs = $("#options-tabs");
|
||||||
@@ -44,6 +45,41 @@ export default {
|
|||||||
saveOptions
|
saveOptions
|
||||||
};
|
};
|
||||||
|
|
||||||
|
addTabHandler((function() {
|
||||||
|
const $themeSelect = $("#theme-select");
|
||||||
|
const $zoomFactorSelect = $("#zoom-factor-select");
|
||||||
|
const $html = $("html");
|
||||||
|
|
||||||
|
function optionsLoaded(options) {
|
||||||
|
$themeSelect.val(options.theme);
|
||||||
|
|
||||||
|
if (utils.isElectron()) {
|
||||||
|
$zoomFactorSelect.val(options.zoomFactor);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$zoomFactorSelect.prop('disabled', true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$themeSelect.change(function() {
|
||||||
|
const newTheme = $(this).val();
|
||||||
|
|
||||||
|
$html.attr("class", "theme-" + newTheme);
|
||||||
|
|
||||||
|
server.put('options/theme/' + newTheme);
|
||||||
|
});
|
||||||
|
|
||||||
|
$zoomFactorSelect.change(function() {
|
||||||
|
const newZoomFactor = $(this).val();
|
||||||
|
|
||||||
|
zoomService.setZoomFactorAndSave(newZoomFactor);
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
optionsLoaded
|
||||||
|
};
|
||||||
|
})());
|
||||||
|
|
||||||
addTabHandler((function() {
|
addTabHandler((function() {
|
||||||
const $form = $("#change-password-form");
|
const $form = $("#change-password-form");
|
||||||
const $oldPassword = $("#old-password");
|
const $oldPassword = $("#old-password");
|
||||||
@@ -137,6 +173,7 @@ addTabHandler((function () {
|
|||||||
addTabHandler((async function () {
|
addTabHandler((async function () {
|
||||||
const $appVersion = $("#app-version");
|
const $appVersion = $("#app-version");
|
||||||
const $dbVersion = $("#db-version");
|
const $dbVersion = $("#db-version");
|
||||||
|
const $syncVersion = $("#sync-version");
|
||||||
const $buildDate = $("#build-date");
|
const $buildDate = $("#build-date");
|
||||||
const $buildRevision = $("#build-revision");
|
const $buildRevision = $("#build-revision");
|
||||||
|
|
||||||
@@ -144,6 +181,7 @@ addTabHandler((async function () {
|
|||||||
|
|
||||||
$appVersion.html(appInfo.appVersion);
|
$appVersion.html(appInfo.appVersion);
|
||||||
$dbVersion.html(appInfo.dbVersion);
|
$dbVersion.html(appInfo.dbVersion);
|
||||||
|
$syncVersion.html(appInfo.syncVersion);
|
||||||
$buildDate.html(appInfo.buildDate);
|
$buildDate.html(appInfo.buildDate);
|
||||||
$buildRevision.html(appInfo.buildRevision);
|
$buildRevision.html(appInfo.buildRevision);
|
||||||
$buildRevision.attr('href', 'https://github.com/zadam/trilium/commit/' + appInfo.buildRevision);
|
$buildRevision.attr('href', 'https://github.com/zadam/trilium/commit/' + appInfo.buildRevision);
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ async function showDialog() {
|
|||||||
noteLink = change.current_title;
|
noteLink = change.current_title;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
noteLink = linkService.createNoteLink(change.noteId, change.title);
|
noteLink = await linkService.createNoteLink(change.noteId, change.title);
|
||||||
}
|
}
|
||||||
|
|
||||||
changesListEl.append($('<li>')
|
changesListEl.append($('<li>')
|
||||||
|
|||||||
@@ -1,47 +1,18 @@
|
|||||||
import treeService from '../services/tree.js';
|
import treeService from '../services/tree.js';
|
||||||
import messagingService from '../services/messaging.js';
|
|
||||||
import server from '../services/server.js';
|
import server from '../services/server.js';
|
||||||
import utils from "../services/utils.js";
|
|
||||||
import treeUtils from "../services/tree_utils.js";
|
|
||||||
|
|
||||||
const $dialog = $("#recent-notes-dialog");
|
const $dialog = $("#recent-notes-dialog");
|
||||||
const $searchInput = $('#recent-notes-search-input');
|
const $searchInput = $('#recent-notes-search-input');
|
||||||
|
|
||||||
// list of recent note paths
|
|
||||||
let list = [];
|
|
||||||
|
|
||||||
async function reload() {
|
|
||||||
const result = await server.get('recent-notes');
|
|
||||||
|
|
||||||
list = result.map(r => r.notePath);
|
|
||||||
}
|
|
||||||
|
|
||||||
function addRecentNote(branchId, notePath) {
|
function addRecentNote(branchId, notePath) {
|
||||||
setTimeout(async () => {
|
setTimeout(async () => {
|
||||||
// we include the note into recent list only if the user stayed on the note at least 5 seconds
|
// we include the note into recent list only if the user stayed on the note at least 5 seconds
|
||||||
if (notePath && notePath === treeService.getCurrentNotePath()) {
|
if (notePath && notePath === treeService.getCurrentNotePath()) {
|
||||||
const result = await server.put('recent-notes/' + branchId + '/' + encodeURIComponent(notePath));
|
const result = await server.put('recent-notes/' + branchId + '/' + encodeURIComponent(notePath));
|
||||||
|
|
||||||
list = result.map(r => r.notePath);
|
|
||||||
}
|
}
|
||||||
}, 1500);
|
}, 1500);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getNoteTitle(notePath) {
|
|
||||||
let noteTitle;
|
|
||||||
|
|
||||||
try {
|
|
||||||
noteTitle = await treeUtils.getNotePathTitle(notePath);
|
|
||||||
}
|
|
||||||
catch (e) {
|
|
||||||
noteTitle = "[error - can't find note title]";
|
|
||||||
|
|
||||||
messagingService.logError("Could not find title for notePath=" + notePath + ", stack=" + e.stack);
|
|
||||||
}
|
|
||||||
|
|
||||||
return noteTitle;
|
|
||||||
}
|
|
||||||
|
|
||||||
async function showDialog() {
|
async function showDialog() {
|
||||||
glob.activeDialog = $dialog;
|
glob.activeDialog = $dialog;
|
||||||
|
|
||||||
@@ -54,16 +25,17 @@ async function showDialog() {
|
|||||||
|
|
||||||
$searchInput.val('');
|
$searchInput.val('');
|
||||||
|
|
||||||
// remove the current note
|
const result = await server.get('recent-notes');
|
||||||
const recNotes = list.filter(note => note !== treeService.getCurrentNotePath());
|
|
||||||
const items = [];
|
|
||||||
|
|
||||||
for (const notePath of recNotes) {
|
// remove the current note
|
||||||
items.push({
|
const recNotes = result.filter(note => note.notePath !== treeService.getCurrentNotePath());
|
||||||
label: await getNoteTitle(notePath),
|
|
||||||
value: notePath
|
const items = recNotes.map(rn => {
|
||||||
|
return {
|
||||||
|
label: rn.title,
|
||||||
|
value: rn.notePath
|
||||||
|
};
|
||||||
});
|
});
|
||||||
}
|
|
||||||
|
|
||||||
$searchInput.autocomplete({
|
$searchInput.autocomplete({
|
||||||
source: items,
|
source: items,
|
||||||
@@ -96,18 +68,7 @@ async function showDialog() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
setTimeout(reload, 100);
|
|
||||||
|
|
||||||
messagingService.subscribeToMessages(syncData => {
|
|
||||||
if (syncData.some(sync => sync.entityName === 'recent_notes')) {
|
|
||||||
console.log(utils.now(), "Reloading recent notes because of background changes");
|
|
||||||
|
|
||||||
reload();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
showDialog,
|
showDialog,
|
||||||
addRecentNote,
|
addRecentNote
|
||||||
reload
|
|
||||||
};
|
};
|
||||||
@@ -14,6 +14,10 @@ class Branch {
|
|||||||
return await this.treeCache.getNote(this.noteId);
|
return await this.treeCache.getNote(this.noteId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
isTopLevel() {
|
||||||
|
return this.parentNoteId === 'root';
|
||||||
|
}
|
||||||
|
|
||||||
get toString() {
|
get toString() {
|
||||||
return `Branch(branchId=${this.branchId})`;
|
return `Branch(branchId=${this.branchId})`;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,9 +6,12 @@ class NoteFull extends NoteShort {
|
|||||||
|
|
||||||
this.content = row.content;
|
this.content = row.content;
|
||||||
|
|
||||||
if (this.isJson()) {
|
if (this.content !== "" && this.isJson()) {
|
||||||
|
try {
|
||||||
this.jsonContent = JSON.parse(this.content);
|
this.jsonContent = JSON.parse(this.content);
|
||||||
}
|
}
|
||||||
|
catch(e) {}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ class NoteShort {
|
|||||||
this.isProtected = row.isProtected;
|
this.isProtected = row.isProtected;
|
||||||
this.type = row.type;
|
this.type = row.type;
|
||||||
this.mime = row.mime;
|
this.mime = row.mime;
|
||||||
this.hideInAutocomplete = row.hideInAutocomplete;
|
this.archived = row.archived;
|
||||||
}
|
}
|
||||||
|
|
||||||
isJson() {
|
isJson() {
|
||||||
@@ -14,36 +14,55 @@ class NoteShort {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async getBranches() {
|
async getBranches() {
|
||||||
const branches = [];
|
const branchIds = this.treeCache.parents[this.noteId].map(
|
||||||
|
parentNoteId => this.treeCache.getBranchIdByChildParent(this.noteId, parentNoteId));
|
||||||
|
|
||||||
for (const parent of this.treeCache.parents[this.noteId]) {
|
return this.treeCache.getBranches(branchIds);
|
||||||
branches.push(await this.treeCache.getBranchByChildParent(this.noteId, parent.noteId));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return branches;
|
hasChildren() {
|
||||||
|
return this.treeCache.children[this.noteId]
|
||||||
|
&& this.treeCache.children[this.noteId].length > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
async getChildBranches() {
|
async getChildBranches() {
|
||||||
const branches = [];
|
if (!this.treeCache.children[this.noteId]) {
|
||||||
|
return [];
|
||||||
for (const child of this.treeCache.children[this.noteId]) {
|
|
||||||
branches.push(await this.treeCache.getBranchByChildParent(child.noteId, this.noteId));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return branches;
|
const branchIds = this.treeCache.children[this.noteId].map(
|
||||||
|
childNoteId => this.treeCache.getBranchIdByChildParent(childNoteId, this.noteId));
|
||||||
|
|
||||||
|
return await this.treeCache.getBranches(branchIds);
|
||||||
}
|
}
|
||||||
|
|
||||||
async getParentNotes() {
|
getParentNoteIds() {
|
||||||
return this.treeCache.parents[this.noteId] || [];
|
return this.treeCache.parents[this.noteId] || [];
|
||||||
}
|
}
|
||||||
|
|
||||||
async getChildNotes() {
|
async getParentNotes() {
|
||||||
|
return await this.treeCache.getNotes(this.getParentNoteIds());
|
||||||
|
}
|
||||||
|
|
||||||
|
getChildNoteIds() {
|
||||||
return this.treeCache.children[this.noteId] || [];
|
return this.treeCache.children[this.noteId] || [];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async getChildNotes() {
|
||||||
|
return await this.treeCache.getNotes(this.getChildNoteIds());
|
||||||
|
}
|
||||||
|
|
||||||
get toString() {
|
get toString() {
|
||||||
return `Note(noteId=${this.noteId}, title=${this.title})`;
|
return `Note(noteId=${this.noteId}, title=${this.title})`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get dto() {
|
||||||
|
const dto = Object.assign({}, this);
|
||||||
|
delete dto.treeCache;
|
||||||
|
delete dto.archived;
|
||||||
|
|
||||||
|
return dto;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default NoteShort;
|
export default NoteShort;
|
||||||
@@ -1,46 +0,0 @@
|
|||||||
import server from './services/server.js';
|
|
||||||
|
|
||||||
$(document).ready(async () => {
|
|
||||||
const {appDbVersion, dbVersion} = await server.get('migration');
|
|
||||||
|
|
||||||
console.log("HI", {appDbVersion, dbVersion});
|
|
||||||
|
|
||||||
if (appDbVersion === dbVersion) {
|
|
||||||
$("#up-to-date").show();
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
$("#need-to-migrate").show();
|
|
||||||
|
|
||||||
$("#app-db-version").html(appDbVersion);
|
|
||||||
$("#db-version").html(dbVersion);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
$("#run-migration").click(async () => {
|
|
||||||
$("#run-migration").prop("disabled", true);
|
|
||||||
|
|
||||||
$("#migration-result").show();
|
|
||||||
|
|
||||||
const result = await server.post('migration');
|
|
||||||
|
|
||||||
for (const migration of result.migrations) {
|
|
||||||
const row = $('<tr>')
|
|
||||||
.append($('<td>').html(migration.dbVersion))
|
|
||||||
.append($('<td>').html(migration.name))
|
|
||||||
.append($('<td>').html(migration.success ? 'Yes' : 'No'))
|
|
||||||
.append($('<td>').html(migration.success ? 'N/A' : migration.error));
|
|
||||||
|
|
||||||
if (!migration.success) {
|
|
||||||
row.addClass("danger");
|
|
||||||
}
|
|
||||||
|
|
||||||
$("#migration-table").append(row);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// copy of this shortcut to be able to debug migration problems
|
|
||||||
$(document).bind('keydown', 'ctrl+shift+i', () => {
|
|
||||||
require('electron').remote.getCurrentWindow().toggleDevTools();
|
|
||||||
|
|
||||||
return false;
|
|
||||||
});
|
|
||||||
@@ -1,104 +0,0 @@
|
|||||||
import treeCache from "./tree_cache.js";
|
|
||||||
import treeUtils from "./tree_utils.js";
|
|
||||||
|
|
||||||
async function getAutocompleteItems(parentNoteId, notePath, titlePath) {
|
|
||||||
if (!parentNoteId) {
|
|
||||||
parentNoteId = 'root';
|
|
||||||
}
|
|
||||||
|
|
||||||
const parentNote = await treeCache.getNote(parentNoteId);
|
|
||||||
const childNotes = await parentNote.getChildNotes();
|
|
||||||
|
|
||||||
if (!childNotes.length) {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!notePath) {
|
|
||||||
notePath = '';
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!titlePath) {
|
|
||||||
titlePath = '';
|
|
||||||
}
|
|
||||||
|
|
||||||
// https://github.com/zadam/trilium/issues/46
|
|
||||||
// unfortunately not easy to implement because we don't have an easy access to note's isProtected property
|
|
||||||
|
|
||||||
const autocompleteItems = [];
|
|
||||||
|
|
||||||
for (const childNote of childNotes) {
|
|
||||||
if (childNote.hideInAutocomplete) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
const childNotePath = (notePath ? (notePath + '/') : '') + childNote.noteId;
|
|
||||||
const childTitlePath = (titlePath ? (titlePath + ' / ') : '') + await treeUtils.getNoteTitle(childNote.noteId, parentNoteId);
|
|
||||||
|
|
||||||
autocompleteItems.push({
|
|
||||||
value: childTitlePath + ' (' + childNotePath + ')',
|
|
||||||
label: childTitlePath
|
|
||||||
});
|
|
||||||
|
|
||||||
const childItems = await getAutocompleteItems(childNote.noteId, childNotePath, childTitlePath);
|
|
||||||
|
|
||||||
for (const childItem of childItems) {
|
|
||||||
autocompleteItems.push(childItem);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (parentNoteId === 'root') {
|
|
||||||
console.log(`Generated ${autocompleteItems.length} autocomplete items`);
|
|
||||||
}
|
|
||||||
|
|
||||||
return autocompleteItems;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Overrides the default autocomplete filter function to search for matched on atleast 1 word in each of the input term's words
|
|
||||||
$.ui.autocomplete.filter = (array, terms) => {
|
|
||||||
if (!terms) {
|
|
||||||
return array;
|
|
||||||
}
|
|
||||||
|
|
||||||
const startDate = new Date();
|
|
||||||
|
|
||||||
const results = [];
|
|
||||||
const tokens = terms.toLowerCase().split(" ");
|
|
||||||
|
|
||||||
for (const item of array) {
|
|
||||||
const lcLabel = item.label.toLowerCase();
|
|
||||||
|
|
||||||
const found = tokens.every(token => lcLabel.indexOf(token) !== -1);
|
|
||||||
if (!found) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// this is not completely correct and might cause minor problems with note with names containing this " / "
|
|
||||||
const lastSegmentIndex = lcLabel.lastIndexOf(" / ");
|
|
||||||
|
|
||||||
if (lastSegmentIndex !== -1) {
|
|
||||||
const lastSegment = lcLabel.substr(lastSegmentIndex + 3);
|
|
||||||
|
|
||||||
// at least some token needs to be in the last segment (leaf note), otherwise this
|
|
||||||
// particular note is not that interesting (query is satisfied by parent note)
|
|
||||||
const foundInLastSegment = tokens.some(token => lastSegment.indexOf(token) !== -1);
|
|
||||||
|
|
||||||
if (!foundInLastSegment) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
results.push(item);
|
|
||||||
|
|
||||||
if (results.length > 100) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log("Search took " + (new Date().getTime() - startDate.getTime()) + "ms");
|
|
||||||
|
|
||||||
return results;
|
|
||||||
};
|
|
||||||
|
|
||||||
export default {
|
|
||||||
getAutocompleteItems
|
|
||||||
};
|
|
||||||
3
src/public/javascripts/services/bootstrap.js
vendored
@@ -17,7 +17,7 @@ import messagingService from './messaging.js';
|
|||||||
import noteDetailService from './note_detail.js';
|
import noteDetailService from './note_detail.js';
|
||||||
import noteType from './note_type.js';
|
import noteType from './note_type.js';
|
||||||
import protected_session from './protected_session.js';
|
import protected_session from './protected_session.js';
|
||||||
import searchTreeService from './search_tree.js';
|
import searchNotesService from './search_notes.js';
|
||||||
import ScriptApi from './script_api.js';
|
import ScriptApi from './script_api.js';
|
||||||
import ScriptContext from './script_context.js';
|
import ScriptContext from './script_context.js';
|
||||||
import sync from './sync.js';
|
import sync from './sync.js';
|
||||||
@@ -35,6 +35,7 @@ import libraryLoader from "./library_loader.js";
|
|||||||
// required for CKEditor image upload plugin
|
// required for CKEditor image upload plugin
|
||||||
window.glob.getCurrentNode = treeService.getCurrentNode;
|
window.glob.getCurrentNode = treeService.getCurrentNode;
|
||||||
window.glob.getHeaders = server.getHeaders;
|
window.glob.getHeaders = server.getHeaders;
|
||||||
|
window.glob.showAddLinkDialog = addLinkDialog.showDialog;
|
||||||
|
|
||||||
// required for ESLint plugin
|
// required for ESLint plugin
|
||||||
window.glob.getCurrentNote = noteDetailService.getCurrentNote;
|
window.glob.getCurrentNote = noteDetailService.getCurrentNote;
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import treeUtils from './tree_utils.js';
|
|||||||
import branchPrefixDialog from '../dialogs/branch_prefix.js';
|
import branchPrefixDialog from '../dialogs/branch_prefix.js';
|
||||||
import infoService from "./info.js";
|
import infoService from "./info.js";
|
||||||
import treeCache from "./tree_cache.js";
|
import treeCache from "./tree_cache.js";
|
||||||
|
import syncService from "./sync.js";
|
||||||
|
|
||||||
const $tree = $("#tree");
|
const $tree = $("#tree");
|
||||||
|
|
||||||
@@ -93,27 +94,34 @@ const contextMenuOptions = {
|
|||||||
{title: "Paste into <kbd>Ctrl+V</kbd>", cmd: "pasteInto", uiIcon: "ui-icon-clipboard"},
|
{title: "Paste into <kbd>Ctrl+V</kbd>", cmd: "pasteInto", uiIcon: "ui-icon-clipboard"},
|
||||||
{title: "Paste after", cmd: "pasteAfter", uiIcon: "ui-icon-clipboard"},
|
{title: "Paste after", cmd: "pasteAfter", uiIcon: "ui-icon-clipboard"},
|
||||||
{title: "----"},
|
{title: "----"},
|
||||||
{title: "Export branch", cmd: "exportBranch", uiIcon: " ui-icon-arrowthick-1-ne"},
|
{title: "Export branch", cmd: "exportBranch", uiIcon: " ui-icon-arrowthick-1-ne", children: [
|
||||||
{title: "Import into branch", cmd: "importBranch", uiIcon: "ui-icon-arrowthick-1-sw"},
|
{title: "Native Tar", cmd: "exportBranchToTar"},
|
||||||
|
{title: "OPML", cmd: "exportBranchToOpml"}
|
||||||
|
]},
|
||||||
|
{title: "Import into branch (tar, opml)", cmd: "importBranch", uiIcon: "ui-icon-arrowthick-1-sw"},
|
||||||
{title: "----"},
|
{title: "----"},
|
||||||
{title: "Collapse branch <kbd>Alt+-</kbd>", cmd: "collapseBranch", uiIcon: "ui-icon-minus"},
|
{title: "Collapse branch <kbd>Alt+-</kbd>", cmd: "collapseBranch", uiIcon: "ui-icon-minus"},
|
||||||
{title: "Force note sync", cmd: "forceNoteSync", uiIcon: "ui-icon-refresh"},
|
{title: "Force note sync", cmd: "forceNoteSync", uiIcon: "ui-icon-refresh"},
|
||||||
{title: "Sort alphabetically <kbd>Alt+S</kbd>", cmd: "sortAlphabetically", uiIcon: " ui-icon-arrowthick-2-n-s"}
|
{title: "Sort alphabetically <kbd>Alt+S</kbd>", cmd: "sortAlphabetically", uiIcon: " ui-icon-arrowthick-2-n-s"}
|
||||||
|
|
||||||
],
|
],
|
||||||
beforeOpen: async (event, ui) => {
|
beforeOpen: async (event, ui) => {
|
||||||
const node = $.ui.fancytree.getNode(ui.target);
|
const node = $.ui.fancytree.getNode(ui.target);
|
||||||
const branch = await treeCache.getBranch(branchId);
|
const branch = await treeCache.getBranch(node.data.branchId);
|
||||||
const note = await treeCache.getNote(node.data.noteId);
|
const note = await treeCache.getNote(node.data.noteId);
|
||||||
const parentNote = await treeCache.getNote(branch.parentNoteId);
|
const parentNote = await treeCache.getNote(branch.parentNoteId);
|
||||||
|
const isNotRoot = note.noteId !== 'root';
|
||||||
|
|
||||||
// Modify menu entries depending on node status
|
// Modify menu entries depending on node status
|
||||||
$tree.contextmenu("enableEntry", "pasteAfter", clipboardIds.length > 0 && (!parentNote || parentNote.type !== 'search'));
|
$tree.contextmenu("enableEntry", "insertNoteHere", isNotRoot && parentNote.type !== 'search');
|
||||||
$tree.contextmenu("enableEntry", "pasteInto", clipboardIds.length > 0 && note.type !== 'search');
|
|
||||||
$tree.contextmenu("enableEntry", "insertNoteHere", !parentNote || parentNote.type !== 'search');
|
|
||||||
$tree.contextmenu("enableEntry", "insertChildNote", note.type !== 'search');
|
$tree.contextmenu("enableEntry", "insertChildNote", note.type !== 'search');
|
||||||
|
$tree.contextmenu("enableEntry", "delete", isNotRoot && parentNote.type !== 'search');
|
||||||
|
$tree.contextmenu("enableEntry", "copy", isNotRoot);
|
||||||
|
$tree.contextmenu("enableEntry", "cut", isNotRoot);
|
||||||
|
$tree.contextmenu("enableEntry", "pasteAfter", clipboardIds.length > 0 && isNotRoot && parentNote.type !== 'search');
|
||||||
|
$tree.contextmenu("enableEntry", "pasteInto", clipboardIds.length > 0 && note.type !== 'search');
|
||||||
$tree.contextmenu("enableEntry", "importBranch", note.type !== 'search');
|
$tree.contextmenu("enableEntry", "importBranch", note.type !== 'search');
|
||||||
$tree.contextmenu("enableEntry", "exportBranch", note.type !== 'search');
|
$tree.contextmenu("enableEntry", "exportBranch", note.type !== 'search');
|
||||||
|
$tree.contextmenu("enableEntry", "editBranchPrefix", parentNote.type !== 'search');
|
||||||
|
|
||||||
// Activate node on right-click
|
// Activate node on right-click
|
||||||
node.setActive();
|
node.setActive();
|
||||||
@@ -158,8 +166,11 @@ const contextMenuOptions = {
|
|||||||
else if (ui.cmd === "delete") {
|
else if (ui.cmd === "delete") {
|
||||||
treeChangesService.deleteNodes(treeService.getSelectedNodes(true));
|
treeChangesService.deleteNodes(treeService.getSelectedNodes(true));
|
||||||
}
|
}
|
||||||
else if (ui.cmd === "exportBranch") {
|
else if (ui.cmd === "exportBranchToTar") {
|
||||||
exportService.exportBranch(node.data.noteId);
|
exportService.exportBranch(node.data.noteId, 'tar');
|
||||||
|
}
|
||||||
|
else if (ui.cmd === "exportBranchToOpml") {
|
||||||
|
exportService.exportBranch(node.data.noteId, 'opml');
|
||||||
}
|
}
|
||||||
else if (ui.cmd === "importBranch") {
|
else if (ui.cmd === "importBranch") {
|
||||||
exportService.importBranch(node.data.noteId);
|
exportService.importBranch(node.data.noteId);
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import utils from "./utils.js";
|
|||||||
import treeService from "./tree.js";
|
import treeService from "./tree.js";
|
||||||
import linkService from "./link.js";
|
import linkService from "./link.js";
|
||||||
import fileService from "./file.js";
|
import fileService from "./file.js";
|
||||||
|
import zoomService from "./zoom.js";
|
||||||
import noteRevisionsDialog from "../dialogs/note_revisions.js";
|
import noteRevisionsDialog from "../dialogs/note_revisions.js";
|
||||||
import optionsDialog from "../dialogs/options.js";
|
import optionsDialog from "../dialogs/options.js";
|
||||||
import addLinkDialog from "../dialogs/add_link.js";
|
import addLinkDialog from "../dialogs/add_link.js";
|
||||||
@@ -10,8 +11,9 @@ import jumpToNoteDialog from "../dialogs/jump_to_note.js";
|
|||||||
import noteSourceDialog from "../dialogs/note_source.js";
|
import noteSourceDialog from "../dialogs/note_source.js";
|
||||||
import recentChangesDialog from "../dialogs/recent_changes.js";
|
import recentChangesDialog from "../dialogs/recent_changes.js";
|
||||||
import sqlConsoleDialog from "../dialogs/sql_console.js";
|
import sqlConsoleDialog from "../dialogs/sql_console.js";
|
||||||
import searchTreeService from "./search_tree.js";
|
import searchNotesService from "./search_notes.js";
|
||||||
import labelsDialog from "../dialogs/labels.js";
|
import labelsDialog from "../dialogs/labels.js";
|
||||||
|
import protectedSessionService from "./protected_session.js";
|
||||||
|
|
||||||
function registerEntrypoints() {
|
function registerEntrypoints() {
|
||||||
// hot keys are active also inside inputs and content editables
|
// hot keys are active also inside inputs and content editables
|
||||||
@@ -21,7 +23,7 @@ function registerEntrypoints() {
|
|||||||
|
|
||||||
utils.bindShortcut('ctrl+l', addLinkDialog.showDialog);
|
utils.bindShortcut('ctrl+l', addLinkDialog.showDialog);
|
||||||
|
|
||||||
$("#jump-to-note-button").click(jumpToNoteDialog.showDialog);
|
$("#jump-to-note-dialog-button").click(jumpToNoteDialog.showDialog);
|
||||||
utils.bindShortcut('ctrl+j', jumpToNoteDialog.showDialog);
|
utils.bindShortcut('ctrl+j', jumpToNoteDialog.showDialog);
|
||||||
|
|
||||||
$("#show-note-revisions-button").click(noteRevisionsDialog.showCurrentNoteRevisions);
|
$("#show-note-revisions-button").click(noteRevisionsDialog.showCurrentNoteRevisions);
|
||||||
@@ -31,11 +33,14 @@ function registerEntrypoints() {
|
|||||||
|
|
||||||
$("#recent-changes-button").click(recentChangesDialog.showDialog);
|
$("#recent-changes-button").click(recentChangesDialog.showDialog);
|
||||||
|
|
||||||
|
$("#protected-session-on").click(protectedSessionService.enterProtectedSession);
|
||||||
|
$("#protected-session-off").click(protectedSessionService.leaveProtectedSession);
|
||||||
|
|
||||||
$("#recent-notes-button").click(recentNotesDialog.showDialog);
|
$("#recent-notes-button").click(recentNotesDialog.showDialog);
|
||||||
utils.bindShortcut('ctrl+e', recentNotesDialog.showDialog);
|
utils.bindShortcut('ctrl+e', recentNotesDialog.showDialog);
|
||||||
|
|
||||||
$("#toggle-search-button").click(searchTreeService.toggleSearch);
|
$("#toggle-search-button").click(searchNotesService.toggleSearch);
|
||||||
utils.bindShortcut('ctrl+s', searchTreeService.toggleSearch);
|
utils.bindShortcut('ctrl+s', searchNotesService.toggleSearch);
|
||||||
|
|
||||||
$(".show-labels-button").click(labelsDialog.showDialog);
|
$(".show-labels-button").click(labelsDialog.showDialog);
|
||||||
utils.bindShortcut('alt+l', labelsDialog.showDialog);
|
utils.bindShortcut('alt+l', labelsDialog.showDialog);
|
||||||
@@ -45,11 +50,21 @@ function registerEntrypoints() {
|
|||||||
utils.bindShortcut('alt+o', sqlConsoleDialog.showDialog);
|
utils.bindShortcut('alt+o', sqlConsoleDialog.showDialog);
|
||||||
|
|
||||||
if (utils.isElectron()) {
|
if (utils.isElectron()) {
|
||||||
|
$("#history-navigation").show();
|
||||||
|
$("#history-back-button").click(window.history.back);
|
||||||
|
$("#history-forward-button").click(window.history.forward);
|
||||||
|
|
||||||
utils.bindShortcut('alt+left', window.history.back);
|
utils.bindShortcut('alt+left', window.history.back);
|
||||||
utils.bindShortcut('alt+right', window.history.forward);
|
utils.bindShortcut('alt+right', window.history.forward);
|
||||||
}
|
}
|
||||||
|
|
||||||
utils.bindShortcut('alt+m', e => $(".hide-toggle").toggleClass("suppressed"));
|
utils.bindShortcut('alt+m', e => {
|
||||||
|
$(".hide-toggle").toggle();
|
||||||
|
|
||||||
|
// when hiding switch display to block, otherwise grid still tries to display columns which shows
|
||||||
|
// left empty column
|
||||||
|
$("#container").css("display", $("#container").css("display") === "grid" ? "block" : "grid");
|
||||||
|
});
|
||||||
|
|
||||||
// hide (toggle) everything except for the note content for distraction free writing
|
// hide (toggle) everything except for the note content for distraction free writing
|
||||||
utils.bindShortcut('alt+t', e => {
|
utils.bindShortcut('alt+t', e => {
|
||||||
@@ -101,28 +116,11 @@ function registerEntrypoints() {
|
|||||||
$("#note-detail-text").focus();
|
$("#note-detail-text").focus();
|
||||||
});
|
});
|
||||||
|
|
||||||
$(document).bind('keydown', 'ctrl+-', () => {
|
|
||||||
if (utils.isElectron()) {
|
if (utils.isElectron()) {
|
||||||
const webFrame = require('electron').webFrame;
|
$(document).bind('keydown', 'ctrl+-', zoomService.decreaseZoomFactor);
|
||||||
|
$(document).bind('keydown', 'ctrl+=', zoomService.increaseZoomFactor);
|
||||||
if (webFrame.getZoomFactor() > 0.2) {
|
|
||||||
webFrame.setZoomFactor(webFrame.getZoomFactor() - 0.1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
$(document).bind('keydown', 'ctrl+=', () => {
|
|
||||||
if (utils.isElectron()) {
|
|
||||||
const webFrame = require('electron').webFrame;
|
|
||||||
|
|
||||||
webFrame.setZoomFactor(webFrame.getZoomFactor() + 0.1);
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
$("#note-title").bind('keydown', 'return', () => $("#note-detail-text").focus());
|
$("#note-title").bind('keydown', 'return', () => $("#note-detail-text").focus());
|
||||||
|
|
||||||
$("#upload-file-button").click(fileService.uploadFile);
|
$("#upload-file-button").click(fileService.uploadFile);
|
||||||
|
|||||||
@@ -3,9 +3,9 @@ import protectedSessionHolder from './protected_session_holder.js';
|
|||||||
import utils from './utils.js';
|
import utils from './utils.js';
|
||||||
import server from './server.js';
|
import server from './server.js';
|
||||||
|
|
||||||
function exportBranch(noteId) {
|
function exportBranch(noteId, format) {
|
||||||
const url = utils.getHost() + "/api/notes/" + noteId + "/export?protectedSessionId="
|
const url = utils.getHost() + "/api/notes/" + noteId + "/export/" + format +
|
||||||
+ encodeURIComponent(protectedSessionHolder.getProtectedSessionId());
|
"?protectedSessionId=" + encodeURIComponent(protectedSessionHolder.getProtectedSessionId());
|
||||||
|
|
||||||
utils.download(url);
|
utils.download(url);
|
||||||
}
|
}
|
||||||
@@ -29,7 +29,7 @@ $("#import-upload").change(async function() {
|
|||||||
type: 'POST',
|
type: 'POST',
|
||||||
contentType: false, // NEEDED, DON'T OMIT THIS
|
contentType: false, // NEEDED, DON'T OMIT THIS
|
||||||
processData: false, // NEEDED, DON'T OMIT THIS
|
processData: false, // NEEDED, DON'T OMIT THIS
|
||||||
});
|
}).fail((xhr, status, error) => alert('Import error: ' + xhr.responseText));
|
||||||
|
|
||||||
await treeService.reload();
|
await treeService.reload();
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -32,18 +32,19 @@ async function requireLibrary(library) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const dynamicallyLoadedScripts = [];
|
// we save the promises in case of the same script being required concurrently multiple times
|
||||||
|
const loadedScriptPromises = {};
|
||||||
|
|
||||||
async function requireScript(url) {
|
async function requireScript(url) {
|
||||||
if (!dynamicallyLoadedScripts.includes(url)) {
|
if (!loadedScriptPromises[url]) {
|
||||||
dynamicallyLoadedScripts.push(url);
|
loadedScriptPromises[url] = $.ajax({
|
||||||
|
|
||||||
return await $.ajax({
|
|
||||||
url: url,
|
url: url,
|
||||||
dataType: "script",
|
dataType: "script",
|
||||||
cache: true
|
cache: true
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
await loadedScriptPromises[url];
|
||||||
}
|
}
|
||||||
|
|
||||||
async function requireCss(url) {
|
async function requireCss(url) {
|
||||||
|
|||||||
@@ -23,11 +23,11 @@ function getNodePathFromLabel(label) {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
function createNoteLink(notePath, noteTitle) {
|
async function createNoteLink(notePath, noteTitle) {
|
||||||
if (!noteTitle) {
|
if (!noteTitle) {
|
||||||
const noteId = treeUtils.getNoteIdFromNotePath(notePath);
|
const noteId = treeUtils.getNoteIdFromNotePath(notePath);
|
||||||
|
|
||||||
noteTitle = treeUtils.getNoteTitle(noteId);
|
noteTitle = await treeUtils.getNoteTitle(noteId);
|
||||||
}
|
}
|
||||||
|
|
||||||
const noteLink = $("<a>", {
|
const noteLink = $("<a>", {
|
||||||
@@ -76,9 +76,11 @@ function goToLink(e) {
|
|||||||
|
|
||||||
function addLinkToEditor(linkTitle, linkHref) {
|
function addLinkToEditor(linkTitle, linkHref) {
|
||||||
const editor = noteDetailText.getEditor();
|
const editor = noteDetailText.getEditor();
|
||||||
const doc = editor.document;
|
|
||||||
|
|
||||||
doc.enqueueChanges(() => editor.data.insertLink(linkTitle, linkHref), doc.selection);
|
editor.model.change( writer => {
|
||||||
|
const insertPosition = editor.model.document.selection.getFirstPosition();
|
||||||
|
writer.insertText(linkTitle, { linkHref: linkHref }, insertPosition);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function addTextToEditor(text) {
|
function addTextToEditor(text) {
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import treeService from './tree.js';
|
import treeService from './tree.js';
|
||||||
|
import treeUtils from './tree_utils.js';
|
||||||
import noteTypeService from './note_type.js';
|
import noteTypeService from './note_type.js';
|
||||||
import protectedSessionService from './protected_session.js';
|
import protectedSessionService from './protected_session.js';
|
||||||
import protectedSessionHolder from './protected_session_holder.js';
|
import protectedSessionHolder from './protected_session_holder.js';
|
||||||
@@ -21,9 +22,11 @@ const $noteDetailComponents = $(".note-detail-component");
|
|||||||
const $protectButton = $("#protect-button");
|
const $protectButton = $("#protect-button");
|
||||||
const $unprotectButton = $("#unprotect-button");
|
const $unprotectButton = $("#unprotect-button");
|
||||||
const $noteDetailWrapper = $("#note-detail-wrapper");
|
const $noteDetailWrapper = $("#note-detail-wrapper");
|
||||||
|
const $noteDetailComponentWrapper = $("#note-detail-component-wrapper");
|
||||||
const $noteIdDisplay = $("#note-id-display");
|
const $noteIdDisplay = $("#note-id-display");
|
||||||
const $labelList = $("#label-list");
|
const $labelList = $("#label-list");
|
||||||
const $labelListInner = $("#label-list-inner");
|
const $labelListInner = $("#label-list-inner");
|
||||||
|
const $childrenOverview = $("#children-overview");
|
||||||
|
|
||||||
let currentNote = null;
|
let currentNote = null;
|
||||||
|
|
||||||
@@ -73,58 +76,50 @@ function noteChanged() {
|
|||||||
async function reload() {
|
async function reload() {
|
||||||
// no saving here
|
// no saving here
|
||||||
|
|
||||||
await loadNoteToEditor(getCurrentNoteId());
|
await loadNoteDetail(getCurrentNoteId());
|
||||||
}
|
}
|
||||||
|
|
||||||
async function switchToNote(noteId) {
|
async function switchToNote(noteId) {
|
||||||
if (getCurrentNoteId() !== noteId) {
|
if (getCurrentNoteId() !== noteId) {
|
||||||
await saveNoteIfChanged();
|
await saveNoteIfChanged();
|
||||||
|
|
||||||
await loadNoteToEditor(noteId);
|
await loadNoteDetail(noteId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function saveNote() {
|
||||||
|
const note = getCurrentNote();
|
||||||
|
|
||||||
|
note.title = $noteTitle.val();
|
||||||
|
note.content = getComponent(note.type).getContent();
|
||||||
|
|
||||||
|
treeService.setNoteTitle(note.noteId, note.title);
|
||||||
|
|
||||||
|
await server.put('notes/' + note.noteId, note.dto);
|
||||||
|
|
||||||
|
isNoteChanged = false;
|
||||||
|
|
||||||
|
if (note.isProtected) {
|
||||||
|
protectedSessionHolder.touchProtectedSession();
|
||||||
|
}
|
||||||
|
|
||||||
|
infoService.showMessage("Saved!");
|
||||||
|
}
|
||||||
|
|
||||||
async function saveNoteIfChanged() {
|
async function saveNoteIfChanged() {
|
||||||
if (!isNoteChanged) {
|
if (!isNoteChanged) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const note = getCurrentNote();
|
await saveNote();
|
||||||
|
|
||||||
updateNoteFromInputs(note);
|
|
||||||
|
|
||||||
await saveNoteToServer(note);
|
|
||||||
|
|
||||||
if (note.isProtected) {
|
|
||||||
protectedSessionHolder.touchProtectedSession();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function updateNoteFromInputs(note) {
|
|
||||||
note.title = $noteTitle.val();
|
|
||||||
note.content = getComponent(note.type).getContent();
|
|
||||||
|
|
||||||
treeService.setNoteTitle(note.noteId, note.title);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function saveNoteToServer(note) {
|
|
||||||
const dto = Object.assign({}, note);
|
|
||||||
delete dto.treeCache;
|
|
||||||
delete dto.hideInAutocomplete;
|
|
||||||
|
|
||||||
await server.put('notes/' + dto.noteId, dto);
|
|
||||||
|
|
||||||
isNoteChanged = false;
|
|
||||||
|
|
||||||
infoService.showMessage("Saved!");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function setNoteBackgroundIfProtected(note) {
|
function setNoteBackgroundIfProtected(note) {
|
||||||
const isProtected = !!note.isProtected;
|
const isProtected = !!note.isProtected;
|
||||||
|
|
||||||
$noteDetailWrapper.toggleClass("protected", isProtected);
|
$noteDetailComponentWrapper.toggleClass("protected", isProtected);
|
||||||
$protectButton.toggle(!isProtected);
|
$protectButton.toggleClass("active", isProtected);
|
||||||
$unprotectButton.toggle(isProtected);
|
$unprotectButton.toggleClass("active", !isProtected);
|
||||||
}
|
}
|
||||||
|
|
||||||
let isNewNoteCreated = false;
|
let isNewNoteCreated = false;
|
||||||
@@ -145,7 +140,7 @@ async function handleProtectedSession() {
|
|||||||
protectedSessionService.ensureDialogIsClosed();
|
protectedSessionService.ensureDialogIsClosed();
|
||||||
}
|
}
|
||||||
|
|
||||||
async function loadNoteToEditor(noteId) {
|
async function loadNoteDetail(noteId) {
|
||||||
currentNote = await loadNote(noteId);
|
currentNote = await loadNote(noteId);
|
||||||
|
|
||||||
if (isNewNoteCreated) {
|
if (isNewNoteCreated) {
|
||||||
@@ -156,6 +151,8 @@ async function loadNoteToEditor(noteId) {
|
|||||||
|
|
||||||
$noteIdDisplay.html(noteId);
|
$noteIdDisplay.html(noteId);
|
||||||
|
|
||||||
|
setNoteBackgroundIfProtected(currentNote);
|
||||||
|
|
||||||
await handleProtectedSession();
|
await handleProtectedSession();
|
||||||
|
|
||||||
$noteDetailWrapper.show();
|
$noteDetailWrapper.show();
|
||||||
@@ -176,13 +173,40 @@ async function loadNoteToEditor(noteId) {
|
|||||||
noteChangeDisabled = false;
|
noteChangeDisabled = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
setNoteBackgroundIfProtected(currentNote);
|
|
||||||
treeService.setBranchBackgroundBasedOnProtectedStatus(noteId);
|
treeService.setBranchBackgroundBasedOnProtectedStatus(noteId);
|
||||||
|
|
||||||
// after loading new note make sure editor is scrolled to the top
|
// after loading new note make sure editor is scrolled to the top
|
||||||
$noteDetailWrapper.scrollTop(0);
|
$noteDetailWrapper.scrollTop(0);
|
||||||
|
|
||||||
await loadLabelList();
|
const labels = await loadLabelList();
|
||||||
|
|
||||||
|
const hideChildrenOverview = labels.some(label => label.name === 'hideChildrenOverview');
|
||||||
|
await showChildrenOverview(hideChildrenOverview);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function showChildrenOverview(hideChildrenOverview) {
|
||||||
|
if (hideChildrenOverview) {
|
||||||
|
$childrenOverview.hide();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const note = getCurrentNote();
|
||||||
|
|
||||||
|
$childrenOverview.empty();
|
||||||
|
|
||||||
|
const notePath = treeService.getCurrentNotePath();
|
||||||
|
|
||||||
|
for (const childBranch of await note.getChildBranches()) {
|
||||||
|
const link = $('<a>', {
|
||||||
|
href: 'javascript:',
|
||||||
|
text: await treeUtils.getNoteTitle(childBranch.noteId, childBranch.parentNoteId)
|
||||||
|
}).attr('action', 'note').attr('note-path', notePath + '/' + childBranch.noteId);
|
||||||
|
|
||||||
|
const childEl = $('<div class="child-overview">').html(link);
|
||||||
|
$childrenOverview.append(childEl);
|
||||||
|
}
|
||||||
|
|
||||||
|
$childrenOverview.show();
|
||||||
}
|
}
|
||||||
|
|
||||||
async function loadLabelList() {
|
async function loadLabelList() {
|
||||||
@@ -202,6 +226,8 @@ async function loadLabelList() {
|
|||||||
else {
|
else {
|
||||||
$labelList.hide();
|
$labelList.hide();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return labels;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function loadNote(noteId) {
|
async function loadNote(noteId) {
|
||||||
@@ -245,8 +271,6 @@ setInterval(saveNoteIfChanged, 5000);
|
|||||||
export default {
|
export default {
|
||||||
reload,
|
reload,
|
||||||
switchToNote,
|
switchToNote,
|
||||||
updateNoteFromInputs,
|
|
||||||
saveNoteToServer,
|
|
||||||
setNoteBackgroundIfProtected,
|
setNoteBackgroundIfProtected,
|
||||||
loadNote,
|
loadNote,
|
||||||
getCurrentNote,
|
getCurrentNote,
|
||||||
@@ -255,6 +279,7 @@ export default {
|
|||||||
newNoteCreated,
|
newNoteCreated,
|
||||||
focus,
|
focus,
|
||||||
loadLabelList,
|
loadLabelList,
|
||||||
|
saveNote,
|
||||||
saveNoteIfChanged,
|
saveNoteIfChanged,
|
||||||
noteChanged
|
noteChanged
|
||||||
};
|
};
|
||||||
@@ -1,4 +1,3 @@
|
|||||||
import utils from "./utils.js";
|
|
||||||
import libraryLoader from "./library_loader.js";
|
import libraryLoader from "./library_loader.js";
|
||||||
import bundleService from "./bundle.js";
|
import bundleService from "./bundle.js";
|
||||||
import infoService from "./info.js";
|
import infoService from "./info.js";
|
||||||
@@ -11,15 +10,19 @@ const $noteDetailCode = $('#note-detail-code');
|
|||||||
const $executeScriptButton = $("#execute-script-button");
|
const $executeScriptButton = $("#execute-script-button");
|
||||||
|
|
||||||
async function show() {
|
async function show() {
|
||||||
if (!codeEditor) {
|
|
||||||
await libraryLoader.requireLibrary(libraryLoader.CODE_MIRROR);
|
await libraryLoader.requireLibrary(libraryLoader.CODE_MIRROR);
|
||||||
|
|
||||||
|
if (!codeEditor) {
|
||||||
CodeMirror.keyMap.default["Shift-Tab"] = "indentLess";
|
CodeMirror.keyMap.default["Shift-Tab"] = "indentLess";
|
||||||
CodeMirror.keyMap.default["Tab"] = "indentMore";
|
CodeMirror.keyMap.default["Tab"] = "indentMore";
|
||||||
|
|
||||||
|
// these conflict with backward/forward navigation shortcuts
|
||||||
|
delete CodeMirror.keyMap.default["Alt-Left"];
|
||||||
|
delete CodeMirror.keyMap.default["Alt-Right"];
|
||||||
|
|
||||||
CodeMirror.modeURL = 'libraries/codemirror/mode/%N/%N.js';
|
CodeMirror.modeURL = 'libraries/codemirror/mode/%N/%N.js';
|
||||||
|
|
||||||
codeEditor = CodeMirror($("#note-detail-code")[0], {
|
codeEditor = CodeMirror($noteDetailCode[0], {
|
||||||
value: "",
|
value: "",
|
||||||
viewportMargin: Infinity,
|
viewportMargin: Infinity,
|
||||||
indentUnit: 4,
|
indentUnit: 4,
|
||||||
@@ -28,7 +31,8 @@ async function show() {
|
|||||||
highlightSelectionMatches: {showToken: /\w/, annotateScrollbar: false},
|
highlightSelectionMatches: {showToken: /\w/, annotateScrollbar: false},
|
||||||
lint: true,
|
lint: true,
|
||||||
gutters: ["CodeMirror-lint-markers"],
|
gutters: ["CodeMirror-lint-markers"],
|
||||||
lineNumbers: true
|
lineNumbers: true,
|
||||||
|
tabindex: 2 // so that tab from title will lead to code editor focus
|
||||||
});
|
});
|
||||||
|
|
||||||
codeEditor.on('change', noteDetailService.noteChanged);
|
codeEditor.on('change', noteDetailService.noteChanged);
|
||||||
@@ -38,7 +42,7 @@ async function show() {
|
|||||||
|
|
||||||
const currentNote = noteDetailService.getCurrentNote();
|
const currentNote = noteDetailService.getCurrentNote();
|
||||||
|
|
||||||
// this needs to happen after the element is shown, otherwise the editor won't be refresheds
|
// this needs to happen after the element is shown, otherwise the editor won't be refreshed
|
||||||
codeEditor.setValue(currentNote.content);
|
codeEditor.setValue(currentNote.content);
|
||||||
|
|
||||||
const info = CodeMirror.findModeByMIME(currentNote.mime);
|
const info = CodeMirror.findModeByMIME(currentNote.mime);
|
||||||
@@ -60,25 +64,28 @@ function focus() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function executeCurrentNote() {
|
async function executeCurrentNote() {
|
||||||
if (noteDetailService.getCurrentNoteType() === 'code') {
|
// ctrl+enter is also used elsewhere so make sure we're running only when appropriate
|
||||||
|
if (noteDetailService.getCurrentNoteType() !== 'code') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// make sure note is saved so we load latest changes
|
// make sure note is saved so we load latest changes
|
||||||
await noteDetailService.saveNoteIfChanged();
|
await noteDetailService.saveNoteIfChanged();
|
||||||
|
|
||||||
const currentNote = noteDetailService.getCurrentNote();
|
const currentNote = noteDetailService.getCurrentNote();
|
||||||
|
|
||||||
if (currentNote.mime.endsWith("env=frontend")) {
|
if (currentNote.mime.endsWith("env=frontend")) {
|
||||||
const bundle = await server.get('script/bundle/' + getCurrentNoteId());
|
const bundle = await server.get('script/bundle/' + noteDetailService.getCurrentNoteId());
|
||||||
|
|
||||||
bundleService.executeBundle(bundle);
|
bundleService.executeBundle(bundle);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (currentNote.mime.endsWith("env=backend")) {
|
if (currentNote.mime.endsWith("env=backend")) {
|
||||||
await server.post('script/run/' + getCurrentNoteId());
|
await server.post('script/run/' + noteDetailService.getCurrentNoteId());
|
||||||
}
|
}
|
||||||
|
|
||||||
infoService.showMessage("Note executed");
|
infoService.showMessage("Note executed");
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
$(document).bind('keydown', "ctrl+return", executeCurrentNote);
|
$(document).bind('keydown', "ctrl+return", executeCurrentNote);
|
||||||
|
|
||||||
|
|||||||
@@ -1,21 +1,73 @@
|
|||||||
import bundleService from "./bundle.js";
|
import bundleService from "./bundle.js";
|
||||||
import server from "./server.js";
|
import server from "./server.js";
|
||||||
import noteDetailService from "./note_detail.js";
|
import noteDetailService from "./note_detail.js";
|
||||||
|
import noteDetailCodeService from "./note_detail_code.js";
|
||||||
|
|
||||||
|
const $noteDetailCode = $('#note-detail-code');
|
||||||
const $noteDetailRender = $('#note-detail-render');
|
const $noteDetailRender = $('#note-detail-render');
|
||||||
|
const $toggleEditButton = $('#toggle-edit-button');
|
||||||
|
const $renderButton = $('#render-button');
|
||||||
|
|
||||||
|
let codeEditorInitialized;
|
||||||
|
|
||||||
async function show() {
|
async function show() {
|
||||||
|
codeEditorInitialized = false;
|
||||||
|
|
||||||
$noteDetailRender.show();
|
$noteDetailRender.show();
|
||||||
|
|
||||||
|
await render();
|
||||||
|
}
|
||||||
|
|
||||||
|
async function toggleEdit() {
|
||||||
|
if ($noteDetailCode.is(":visible")) {
|
||||||
|
$noteDetailCode.hide();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (!codeEditorInitialized) {
|
||||||
|
await noteDetailCodeService.show();
|
||||||
|
|
||||||
|
// because we can't properly scroll only the editor without scrolling the rendering
|
||||||
|
// we limit its height
|
||||||
|
$noteDetailCode.find('.CodeMirror').css('height', '300');
|
||||||
|
|
||||||
|
codeEditorInitialized = true;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$noteDetailCode.show();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$toggleEditButton.click(toggleEdit);
|
||||||
|
|
||||||
|
$renderButton.click(render);
|
||||||
|
|
||||||
|
async function render() {
|
||||||
|
// ctrl+enter is also used elsewhere so make sure we're running only when appropriate
|
||||||
|
if (noteDetailService.getCurrentNoteType() !== 'render') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (codeEditorInitialized) {
|
||||||
|
await noteDetailService.saveNoteIfChanged();
|
||||||
|
}
|
||||||
|
|
||||||
const bundle = await server.get('script/bundle/' + noteDetailService.getCurrentNoteId());
|
const bundle = await server.get('script/bundle/' + noteDetailService.getCurrentNoteId());
|
||||||
|
|
||||||
$noteDetailRender.html(bundle.html);
|
$noteDetailRender.html(bundle.html);
|
||||||
|
|
||||||
|
// if the note is empty, it doesn't make sense to do render-only since nothing will be rendered
|
||||||
|
if (!bundle.html.trim()) {
|
||||||
|
toggleEdit();
|
||||||
|
}
|
||||||
|
|
||||||
await bundleService.executeBundle(bundle);
|
await bundleService.executeBundle(bundle);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$(document).bind('keydown', "ctrl+return", render);
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
show,
|
show,
|
||||||
getContent: () => null,
|
getContent: noteDetailCodeService.getContent,
|
||||||
focus: () => null
|
focus: () => null
|
||||||
}
|
}
|
||||||
@@ -11,11 +11,16 @@ async function show() {
|
|||||||
|
|
||||||
textEditor = await BalloonEditor.create($noteDetailText[0], {});
|
textEditor = await BalloonEditor.create($noteDetailText[0], {});
|
||||||
|
|
||||||
textEditor.document.on('change', noteDetailService.noteChanged);
|
textEditor.model.document.on('change', () => {
|
||||||
|
// change is triggered on just marker/selection changes which is not interesting for us
|
||||||
|
if (textEditor.model.document.differ.getChanges().length > 0) {
|
||||||
|
noteDetailService.noteChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// temporary workaround for https://github.com/ckeditor/ckeditor5-enter/issues/49
|
textEditor.setData(noteDetailService.getCurrentNote().content);
|
||||||
textEditor.setData(noteDetailService.getCurrentNote().content || "<p></p>");
|
|
||||||
|
|
||||||
$noteDetailText.show();
|
$noteDetailText.show();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,12 @@
|
|||||||
import treeService from './tree.js';
|
import treeService from './tree.js';
|
||||||
import noteDetail from './note_detail.js';
|
import noteDetailService from './note_detail.js';
|
||||||
import server from './server.js';
|
import server from './server.js';
|
||||||
import infoService from "./info.js";
|
import infoService from "./info.js";
|
||||||
|
|
||||||
const $executeScriptButton = $("#execute-script-button");
|
const $executeScriptButton = $("#execute-script-button");
|
||||||
|
const $toggleEditButton = $('#toggle-edit-button');
|
||||||
|
const $renderButton = $('#render-button');
|
||||||
|
|
||||||
const noteTypeModel = new NoteTypeModel();
|
const noteTypeModel = new NoteTypeModel();
|
||||||
|
|
||||||
function NoteTypeModel() {
|
function NoteTypeModel() {
|
||||||
@@ -84,13 +87,13 @@ function NoteTypeModel() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
async function save() {
|
async function save() {
|
||||||
const note = noteDetail.getCurrentNote();
|
const note = noteDetailService.getCurrentNote();
|
||||||
|
|
||||||
await server.put('notes/' + note.noteId
|
await server.put('notes/' + note.noteId
|
||||||
+ '/type/' + encodeURIComponent(self.type())
|
+ '/type/' + encodeURIComponent(self.type())
|
||||||
+ '/mime/' + encodeURIComponent(self.mime()));
|
+ '/mime/' + encodeURIComponent(self.mime()));
|
||||||
|
|
||||||
await noteDetail.reload();
|
await noteDetailService.reload();
|
||||||
|
|
||||||
// for the note icon to be updated in the tree
|
// for the note icon to be updated in the tree
|
||||||
await treeService.reload();
|
await treeService.reload();
|
||||||
@@ -107,7 +110,7 @@ function NoteTypeModel() {
|
|||||||
|
|
||||||
this.selectRender = function() {
|
this.selectRender = function() {
|
||||||
self.type('render');
|
self.type('render');
|
||||||
self.mime('');
|
self.mime('text/html');
|
||||||
|
|
||||||
save();
|
save();
|
||||||
};
|
};
|
||||||
@@ -128,6 +131,9 @@ function NoteTypeModel() {
|
|||||||
|
|
||||||
this.updateExecuteScriptButtonVisibility = function() {
|
this.updateExecuteScriptButtonVisibility = function() {
|
||||||
$executeScriptButton.toggle(self.mime().startsWith('application/javascript'));
|
$executeScriptButton.toggle(self.mime().startsWith('application/javascript'));
|
||||||
|
|
||||||
|
$toggleEditButton.toggle(self.type() === 'render');
|
||||||
|
$renderButton.toggle(self.type() === 'render');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
9
src/public/javascripts/services/options_init.js
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
import server from "./server.js";
|
||||||
|
|
||||||
|
const optionsReady = new Promise((resolve, reject) => {
|
||||||
|
$(document).ready(() => server.get('options').then(resolve));
|
||||||
|
});
|
||||||
|
|
||||||
|
export default {
|
||||||
|
optionsReady
|
||||||
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
import treeService from './tree.js';
|
import treeService from './tree.js';
|
||||||
import noteDetail from './note_detail.js';
|
import noteDetailService from './note_detail.js';
|
||||||
import utils from './utils.js';
|
import utils from './utils.js';
|
||||||
import server from './server.js';
|
import server from './server.js';
|
||||||
import protectedSessionHolder from './protected_session_holder.js';
|
import protectedSessionHolder from './protected_session_holder.js';
|
||||||
@@ -11,9 +11,23 @@ const $password = $("#protected-session-password");
|
|||||||
const $noteDetailWrapper = $("#note-detail-wrapper");
|
const $noteDetailWrapper = $("#note-detail-wrapper");
|
||||||
const $protectButton = $("#protect-button");
|
const $protectButton = $("#protect-button");
|
||||||
const $unprotectButton = $("#unprotect-button");
|
const $unprotectButton = $("#unprotect-button");
|
||||||
|
const $protectedSessionOnButton = $("#protected-session-on");
|
||||||
|
const $protectedSessionOffButton = $("#protected-session-off");
|
||||||
|
|
||||||
let protectedSessionDeferred = null;
|
let protectedSessionDeferred = null;
|
||||||
|
|
||||||
|
async function enterProtectedSession() {
|
||||||
|
if (!protectedSessionHolder.isProtectedSessionAvailable()) {
|
||||||
|
await ensureProtectedSession(true, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function leaveProtectedSession() {
|
||||||
|
if (protectedSessionHolder.isProtectedSessionAvailable()) {
|
||||||
|
utils.reloadApp();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function ensureProtectedSession(requireProtectedSession, modal) {
|
function ensureProtectedSession(requireProtectedSession, modal) {
|
||||||
const dfd = $.Deferred();
|
const dfd = $.Deferred();
|
||||||
|
|
||||||
@@ -25,7 +39,10 @@ function ensureProtectedSession(requireProtectedSession, modal) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
$dialog.dialog({
|
$dialog.dialog({
|
||||||
modal: modal,
|
// modal: modal,
|
||||||
|
// everything is now non-modal, because modal dialog caused weird high CPU usage on opening
|
||||||
|
// and tearing of text input
|
||||||
|
modal: false,
|
||||||
width: 400,
|
width: 400,
|
||||||
open: () => {
|
open: () => {
|
||||||
if (!modal) {
|
if (!modal) {
|
||||||
@@ -46,7 +63,7 @@ async function setupProtectedSession() {
|
|||||||
const password = $password.val();
|
const password = $password.val();
|
||||||
$password.val("");
|
$password.val("");
|
||||||
|
|
||||||
const response = await enterProtectedSession(password);
|
const response = await enterProtectedSessionOnServer(password);
|
||||||
|
|
||||||
if (!response.success) {
|
if (!response.success) {
|
||||||
infoService.showError("Wrong password.");
|
infoService.showError("Wrong password.");
|
||||||
@@ -57,7 +74,7 @@ async function setupProtectedSession() {
|
|||||||
|
|
||||||
$dialog.dialog("close");
|
$dialog.dialog("close");
|
||||||
|
|
||||||
noteDetail.reload();
|
noteDetailService.reload();
|
||||||
treeService.reload();
|
treeService.reload();
|
||||||
|
|
||||||
if (protectedSessionDeferred !== null) {
|
if (protectedSessionDeferred !== null) {
|
||||||
@@ -66,8 +83,10 @@ async function setupProtectedSession() {
|
|||||||
$noteDetailWrapper.show();
|
$noteDetailWrapper.show();
|
||||||
|
|
||||||
protectedSessionDeferred.resolve();
|
protectedSessionDeferred.resolve();
|
||||||
|
|
||||||
protectedSessionDeferred = null;
|
protectedSessionDeferred = null;
|
||||||
|
|
||||||
|
$protectedSessionOnButton.addClass('active');
|
||||||
|
$protectedSessionOffButton.removeClass('active');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -81,42 +100,44 @@ function ensureDialogIsClosed() {
|
|||||||
$password.val('');
|
$password.val('');
|
||||||
}
|
}
|
||||||
|
|
||||||
async function enterProtectedSession(password) {
|
async function enterProtectedSessionOnServer(password) {
|
||||||
return await server.post('login/protected', {
|
return await server.post('login/protected', {
|
||||||
password: password
|
password: password
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async function protectNoteAndSendToServer() {
|
async function protectNoteAndSendToServer() {
|
||||||
|
if (noteDetailService.getCurrentNote().isProtected) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
await ensureProtectedSession(true, true);
|
await ensureProtectedSession(true, true);
|
||||||
|
|
||||||
const note = noteDetail.getCurrentNote();
|
const note = noteDetailService.getCurrentNote();
|
||||||
|
|
||||||
noteDetail.updateNoteFromInputs(note);
|
|
||||||
|
|
||||||
note.isProtected = true;
|
note.isProtected = true;
|
||||||
|
|
||||||
await noteDetail.saveNoteToServer(note);
|
await noteDetailService.saveNote(note);
|
||||||
|
|
||||||
treeService.setProtected(note.noteId, note.isProtected);
|
treeService.setProtected(note.noteId, note.isProtected);
|
||||||
|
|
||||||
noteDetail.setNoteBackgroundIfProtected(note);
|
noteDetailService.setNoteBackgroundIfProtected(note);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function unprotectNoteAndSendToServer() {
|
async function unprotectNoteAndSendToServer() {
|
||||||
|
if (!noteDetailService.getCurrentNote().isProtected) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
await ensureProtectedSession(true, true);
|
await ensureProtectedSession(true, true);
|
||||||
|
|
||||||
const note = noteDetail.getCurrentNote();
|
const note = noteDetailService.getCurrentNote();
|
||||||
|
|
||||||
noteDetail.updateNoteFromInputs(note);
|
|
||||||
|
|
||||||
note.isProtected = false;
|
note.isProtected = false;
|
||||||
|
|
||||||
await noteDetail.saveNoteToServer(note);
|
await noteDetailService.saveNote(note);
|
||||||
|
|
||||||
treeService.setProtected(note.noteId, note.isProtected);
|
treeService.setProtected(note.noteId, note.isProtected);
|
||||||
|
|
||||||
noteDetail.setNoteBackgroundIfProtected(note);
|
noteDetailService.setNoteBackgroundIfProtected(note);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function protectBranch(noteId, protect) {
|
async function protectBranch(noteId, protect) {
|
||||||
@@ -127,7 +148,7 @@ async function protectBranch(noteId, protect) {
|
|||||||
infoService.showMessage("Request to un/protect sub tree has finished successfully");
|
infoService.showMessage("Request to un/protect sub tree has finished successfully");
|
||||||
|
|
||||||
treeService.reload();
|
treeService.reload();
|
||||||
noteDetail.reload();
|
noteDetailService.reload();
|
||||||
}
|
}
|
||||||
|
|
||||||
$passwordForm.submit(() => {
|
$passwordForm.submit(() => {
|
||||||
@@ -144,5 +165,7 @@ export default {
|
|||||||
protectNoteAndSendToServer,
|
protectNoteAndSendToServer,
|
||||||
unprotectNoteAndSendToServer,
|
unprotectNoteAndSendToServer,
|
||||||
protectBranch,
|
protectBranch,
|
||||||
ensureDialogIsClosed
|
ensureDialogIsClosed,
|
||||||
|
enterProtectedSession,
|
||||||
|
leaveProtectedSession
|
||||||
};
|
};
|
||||||
@@ -1,13 +1,11 @@
|
|||||||
import utils from "./utils.js";
|
import utils from "./utils.js";
|
||||||
import server from "./server.js";
|
import optionsInitService from './options_init.js';
|
||||||
|
|
||||||
let lastProtectedSessionOperationDate = null;
|
let lastProtectedSessionOperationDate = null;
|
||||||
let protectedSessionTimeout = null;
|
let protectedSessionTimeout = null;
|
||||||
let protectedSessionId = null;
|
let protectedSessionId = null;
|
||||||
|
|
||||||
$(document).ready(() => {
|
optionsInitService.optionsReady.then(options => protectedSessionTimeout = options.protectedSessionTimeout);
|
||||||
server.get('options').then(options => protectedSessionTimeout = options.protectedSessionTimeout);
|
|
||||||
});
|
|
||||||
|
|
||||||
setInterval(() => {
|
setInterval(() => {
|
||||||
if (lastProtectedSessionOperationDate !== null && new Date().getTime() - lastProtectedSessionOperationDate.getTime() > protectedSessionTimeout * 1000) {
|
if (lastProtectedSessionOperationDate !== null && new Date().getTime() - lastProtectedSessionOperationDate.getTime() > protectedSessionTimeout * 1000) {
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import treeService from './tree.js';
|
import treeService from './tree.js';
|
||||||
import server from './server.js';
|
import server from './server.js';
|
||||||
import utils from './utils.js';
|
import utils from './utils.js';
|
||||||
|
import infoService from './info.js';
|
||||||
|
|
||||||
function ScriptApi(startNote, currentNote) {
|
function ScriptApi(startNote, currentNote) {
|
||||||
const $pluginButtons = $("#plugin-buttons");
|
const $pluginButtons = $("#plugin-buttons");
|
||||||
@@ -54,7 +55,11 @@ function ScriptApi(startNote, currentNote) {
|
|||||||
activateNote,
|
activateNote,
|
||||||
getInstanceName: () => window.glob.instanceName,
|
getInstanceName: () => window.glob.instanceName,
|
||||||
runOnServer,
|
runOnServer,
|
||||||
formatDateISO: utils.formatDateISO
|
formatDateISO: utils.formatDateISO,
|
||||||
|
parseDate: utils.parseDate,
|
||||||
|
showMessage: infoService.showMessage,
|
||||||
|
showError: infoService.showError,
|
||||||
|
reloadTree: treeService.reload
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import treeService from './tree.js';
|
import treeService from './tree.js';
|
||||||
import server from './server.js';
|
import server from './server.js';
|
||||||
|
import treeUtils from "./tree_utils.js";
|
||||||
|
|
||||||
const $tree = $("#tree");
|
const $tree = $("#tree");
|
||||||
const $searchInput = $("input[name='search-text']");
|
const $searchInput = $("input[name='search-text']");
|
||||||
@@ -7,40 +8,62 @@ const $resetSearchButton = $("#reset-search-button");
|
|||||||
const $doSearchButton = $("#do-search-button");
|
const $doSearchButton = $("#do-search-button");
|
||||||
const $saveSearchButton = $("#save-search-button");
|
const $saveSearchButton = $("#save-search-button");
|
||||||
const $searchBox = $("#search-box");
|
const $searchBox = $("#search-box");
|
||||||
|
const $searchResults = $("#search-results");
|
||||||
|
const $searchResultsInner = $("#search-results-inner");
|
||||||
|
const $closeSearchButton = $("#close-search-button");
|
||||||
|
|
||||||
function toggleSearch() {
|
function showSearch() {
|
||||||
if ($searchBox.is(":hidden")) {
|
|
||||||
$searchBox.show();
|
$searchBox.show();
|
||||||
$searchInput.focus();
|
$searchInput.focus();
|
||||||
}
|
}
|
||||||
else {
|
|
||||||
|
function hideSearch() {
|
||||||
resetSearch();
|
resetSearch();
|
||||||
|
|
||||||
|
$searchResults.hide();
|
||||||
$searchBox.hide();
|
$searchBox.hide();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function toggleSearch() {
|
||||||
|
if ($searchBox.is(":hidden")) {
|
||||||
|
showSearch();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
hideSearch();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function resetSearch() {
|
function resetSearch() {
|
||||||
$searchInput.val("");
|
$searchInput.val("");
|
||||||
|
|
||||||
getTree().clearFilter();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function getTree() {
|
function getTree() {
|
||||||
return $tree.fancytree('getTree');
|
return $tree.fancytree('getTree');
|
||||||
}
|
}
|
||||||
|
|
||||||
async function doSearch() {
|
async function doSearch(searchText) {
|
||||||
const searchText = $searchInput.val();
|
if (searchText) {
|
||||||
|
$searchInput.val(searchText);
|
||||||
const noteIds = await server.get('search/' + encodeURIComponent(searchText));
|
}
|
||||||
|
else {
|
||||||
for (const noteId of noteIds) {
|
searchText = $searchInput.val();
|
||||||
await treeService.expandToNote(noteId, {noAnimation: true, noEvents: true});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pass a string to perform case insensitive matching
|
const results = await server.get('search/' + encodeURIComponent(searchText));
|
||||||
getTree().filterBranches(node => noteIds.includes(node.data.noteId));
|
|
||||||
|
$searchResultsInner.empty();
|
||||||
|
$searchResults.show();
|
||||||
|
|
||||||
|
for (const result of results) {
|
||||||
|
const link = $('<a>', {
|
||||||
|
href: 'javascript:',
|
||||||
|
text: result.title
|
||||||
|
}).attr('action', 'note').attr('note-path', result.path);
|
||||||
|
|
||||||
|
const $result = $('<li>').append(link);
|
||||||
|
|
||||||
|
$searchResultsInner.append($result);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function saveSearch() {
|
async function saveSearch() {
|
||||||
@@ -71,6 +94,11 @@ $resetSearchButton.click(resetSearch);
|
|||||||
|
|
||||||
$saveSearchButton.click(saveSearch);
|
$saveSearchButton.click(saveSearch);
|
||||||
|
|
||||||
|
$closeSearchButton.click(hideSearch);
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
toggleSearch
|
toggleSearch,
|
||||||
|
resetSearch,
|
||||||
|
showSearch,
|
||||||
|
doSearch
|
||||||
};
|
};
|
||||||
@@ -5,7 +5,7 @@ import infoService from "./info.js";
|
|||||||
function getHeaders() {
|
function getHeaders() {
|
||||||
let protectedSessionId = null;
|
let protectedSessionId = null;
|
||||||
|
|
||||||
try { // this is because protected session might not be declared in some cases - like when it's included in migration page
|
try { // this is because protected session might not be declared in some cases
|
||||||
protectedSessionId = protectedSessionHolder.getProtectedSessionId();
|
protectedSessionId = protectedSessionHolder.getProtectedSessionId();
|
||||||
}
|
}
|
||||||
catch(e) {}
|
catch(e) {}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import utils from './utils.js';
|
import server from './server.js';
|
||||||
import infoService from "./info.js";
|
import infoService from "./info.js";
|
||||||
|
|
||||||
async function syncNow() {
|
async function syncNow() {
|
||||||
@@ -19,7 +19,7 @@ async function syncNow() {
|
|||||||
$("#sync-now-button").click(syncNow);
|
$("#sync-now-button").click(syncNow);
|
||||||
|
|
||||||
async function forceNoteSync(noteId) {
|
async function forceNoteSync(noteId) {
|
||||||
const result = await server.post('sync/force-note-sync/' + noteId);
|
await server.post('sync/force-note-sync/' + noteId);
|
||||||
|
|
||||||
infoService.showMessage("Note added to sync queue.");
|
infoService.showMessage("Note added to sync queue.");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,11 +17,11 @@ import Branch from '../entities/branch.js';
|
|||||||
import NoteShort from '../entities/note_short.js';
|
import NoteShort from '../entities/note_short.js';
|
||||||
|
|
||||||
const $tree = $("#tree");
|
const $tree = $("#tree");
|
||||||
const $parentList = $("#parent-list");
|
|
||||||
const $parentListList = $("#parent-list-inner");
|
|
||||||
const $createTopLevelNoteButton = $("#create-top-level-note-button");
|
const $createTopLevelNoteButton = $("#create-top-level-note-button");
|
||||||
const $collapseTreeButton = $("#collapse-tree-button");
|
const $collapseTreeButton = $("#collapse-tree-button");
|
||||||
const $scrollToCurrentNoteButton = $("#scroll-to-current-note-button");
|
const $scrollToCurrentNoteButton = $("#scroll-to-current-note-button");
|
||||||
|
const $notePathList = $("#note-path-list");
|
||||||
|
const $notePathCount = $("#note-path-count");
|
||||||
|
|
||||||
let startNotePath = null;
|
let startNotePath = null;
|
||||||
|
|
||||||
@@ -81,11 +81,15 @@ async function expandToNote(notePath, expandOpts) {
|
|||||||
|
|
||||||
const noteId = treeUtils.getNoteIdFromNotePath(notePath);
|
const noteId = treeUtils.getNoteIdFromNotePath(notePath);
|
||||||
|
|
||||||
let parentNoteId = 'root';
|
let parentNoteId = 'none';
|
||||||
|
|
||||||
for (const childNoteId of runPath) {
|
for (const childNoteId of runPath) {
|
||||||
const node = getNodesByNoteId(childNoteId).find(node => node.data.parentNoteId === parentNoteId);
|
const node = getNodesByNoteId(childNoteId).find(node => node.data.parentNoteId === parentNoteId);
|
||||||
|
|
||||||
|
if (!node) {
|
||||||
|
console.log(`Can't find node for noteId=${childNoteId} with parentNoteId=${parentNoteId}`);
|
||||||
|
}
|
||||||
|
|
||||||
if (childNoteId === noteId) {
|
if (childNoteId === noteId) {
|
||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
@@ -115,7 +119,10 @@ async function getRunPath(notePath) {
|
|||||||
utils.assertArguments(notePath);
|
utils.assertArguments(notePath);
|
||||||
|
|
||||||
const path = notePath.split("/").reverse();
|
const path = notePath.split("/").reverse();
|
||||||
|
|
||||||
|
if (!path.includes("root")) {
|
||||||
path.push('root');
|
path.push('root');
|
||||||
|
}
|
||||||
|
|
||||||
const effectivePath = [];
|
const effectivePath = [];
|
||||||
let childNoteId = null;
|
let childNoteId = null;
|
||||||
@@ -151,6 +158,8 @@ async function getRunPath(notePath) {
|
|||||||
for (const noteId of pathToRoot) {
|
for (const noteId of pathToRoot) {
|
||||||
effectivePath.push(noteId);
|
effectivePath.push(noteId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
effectivePath.push('root');
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
@@ -162,7 +171,7 @@ async function getRunPath(notePath) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (parentNoteId === 'root') {
|
if (parentNoteId === 'none') {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@@ -174,22 +183,15 @@ async function getRunPath(notePath) {
|
|||||||
return effectivePath.reverse();
|
return effectivePath.reverse();
|
||||||
}
|
}
|
||||||
|
|
||||||
async function showParentList(noteId, node) {
|
async function showPaths(noteId, node) {
|
||||||
utils.assertArguments(noteId, node);
|
utils.assertArguments(noteId, node);
|
||||||
|
|
||||||
const note = await treeCache.getNote(noteId);
|
const note = await treeCache.getNote(noteId);
|
||||||
const parents = await note.getParentNotes();
|
const parents = await note.getParentNotes();
|
||||||
|
|
||||||
if (!parents.length) {
|
$notePathCount.html(parents.length + " path" + (parents.length > 0 ? "s" : ""));
|
||||||
infoService.throwError("Can't find parents for noteId=" + noteId);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (parents.length <= 1) {
|
$notePathList.empty();
|
||||||
$parentList.hide();
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
$parentList.show();
|
|
||||||
$parentListList.empty();
|
|
||||||
|
|
||||||
for (const parentNote of parents) {
|
for (const parentNote of parents) {
|
||||||
const parentNotePath = await getSomeNotePath(parentNote);
|
const parentNotePath = await getSomeNotePath(parentNote);
|
||||||
@@ -197,17 +199,13 @@ async function showParentList(noteId, node) {
|
|||||||
const notePath = parentNotePath ? (parentNotePath + '/' + noteId) : noteId;
|
const notePath = parentNotePath ? (parentNotePath + '/' + noteId) : noteId;
|
||||||
const title = await treeUtils.getNotePathTitle(notePath);
|
const title = await treeUtils.getNotePathTitle(notePath);
|
||||||
|
|
||||||
let item;
|
const item = $("<li/>").append(await linkService.createNoteLink(notePath, title));
|
||||||
|
|
||||||
if (node.getParent().data.noteId === parentNote.noteId) {
|
if (node.getParent().data.noteId === parentNote.noteId) {
|
||||||
item = $("<span/>").attr("title", "Current note").append(title);
|
item.addClass("current");
|
||||||
}
|
|
||||||
else {
|
|
||||||
item = linkService.createNoteLink(notePath, title);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$parentListList.append($("<li/>").append(item));
|
$notePathList.append(item);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -285,15 +283,16 @@ async function treeInitialized() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function initFancyTree(branch) {
|
function initFancyTree(tree) {
|
||||||
utils.assertArguments(branch);
|
utils.assertArguments(tree);
|
||||||
|
|
||||||
$tree.fancytree({
|
$tree.fancytree({
|
||||||
autoScroll: true,
|
autoScroll: true,
|
||||||
keyboard: false, // we takover keyboard handling in the hotkeys plugin
|
keyboard: false, // we takover keyboard handling in the hotkeys plugin
|
||||||
extensions: ["hotkeys", "filter", "dnd", "clones"],
|
extensions: ["hotkeys", "filter", "dnd", "clones"],
|
||||||
source: branch,
|
source: tree,
|
||||||
scrollParent: $("#tree"),
|
scrollParent: $tree,
|
||||||
|
minExpandLevel: 2, // root can't be collapsed
|
||||||
click: (event, data) => {
|
click: (event, data) => {
|
||||||
const targetType = data.targetType;
|
const targetType = data.targetType;
|
||||||
const node = data.node;
|
const node = data.node;
|
||||||
@@ -319,7 +318,7 @@ function initFancyTree(branch) {
|
|||||||
|
|
||||||
noteDetailService.switchToNote(node.noteId);
|
noteDetailService.switchToNote(node.noteId);
|
||||||
|
|
||||||
showParentList(node.noteId, data.node);
|
showPaths(node.noteId, data.node);
|
||||||
},
|
},
|
||||||
expand: (event, data) => setExpandedToServer(data.node.data.branchId, true),
|
expand: (event, data) => setExpandedToServer(data.node.data.branchId, true),
|
||||||
collapse: (event, data) => setExpandedToServer(data.node.data.branchId, false),
|
collapse: (event, data) => setExpandedToServer(data.node.data.branchId, false),
|
||||||
@@ -375,7 +374,7 @@ async function loadTree() {
|
|||||||
startNotePath = getNotePathFromAddress();
|
startNotePath = getNotePathFromAddress();
|
||||||
}
|
}
|
||||||
|
|
||||||
return await treeBuilder.prepareTree(resp.notes, resp.branches);
|
return await treeBuilder.prepareTree(resp.notes, resp.branches, resp.relations);
|
||||||
}
|
}
|
||||||
|
|
||||||
function collapseTree(node = null) {
|
function collapseTree(node = null) {
|
||||||
@@ -541,9 +540,9 @@ $(window).bind('hashchange', function() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
utils.bindShortcut('alt+c', () => collapseTree()); // don't use shortened form since collapseTree() accepts argument
|
utils.bindShortcut('alt+c', () => collapseTree()); // don't use shortened form since collapseTree() accepts argument
|
||||||
|
$collapseTreeButton.click(() => collapseTree());
|
||||||
|
|
||||||
$createTopLevelNoteButton.click(createNewTopLevelNote);
|
$createTopLevelNoteButton.click(createNewTopLevelNote);
|
||||||
$collapseTreeButton.click(collapseTree);
|
|
||||||
$scrollToCurrentNoteButton.click(scrollToCurrentNote);
|
$scrollToCurrentNoteButton.click(scrollToCurrentNote);
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
|||||||
@@ -5,12 +5,12 @@ import server from "./server.js";
|
|||||||
import treeCache from "./tree_cache.js";
|
import treeCache from "./tree_cache.js";
|
||||||
import messagingService from "./messaging.js";
|
import messagingService from "./messaging.js";
|
||||||
|
|
||||||
async function prepareTree(noteRows, branchRows) {
|
async function prepareTree(noteRows, branchRows, relations) {
|
||||||
utils.assertArguments(noteRows);
|
utils.assertArguments(noteRows, branchRows, relations);
|
||||||
|
|
||||||
treeCache.load(noteRows, branchRows);
|
treeCache.load(noteRows, branchRows, relations);
|
||||||
|
|
||||||
return await prepareRealBranch(await treeCache.getNote('root'));
|
return [ await prepareNode(await treeCache.getBranch('root')) ];
|
||||||
}
|
}
|
||||||
|
|
||||||
async function prepareBranch(note) {
|
async function prepareBranch(note) {
|
||||||
@@ -22,6 +22,35 @@ async function prepareBranch(note) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function prepareNode(branch) {
|
||||||
|
const note = await branch.getNote();
|
||||||
|
const title = (branch.prefix ? (branch.prefix + " - ") : "") + note.title;
|
||||||
|
|
||||||
|
const node = {
|
||||||
|
noteId: note.noteId,
|
||||||
|
parentNoteId: branch.parentNoteId,
|
||||||
|
branchId: branch.branchId,
|
||||||
|
isProtected: note.isProtected,
|
||||||
|
title: utils.escapeHtml(title),
|
||||||
|
extraClasses: await getExtraClasses(note),
|
||||||
|
refKey: note.noteId,
|
||||||
|
expanded: note.type !== 'search' && branch.isExpanded
|
||||||
|
};
|
||||||
|
|
||||||
|
if (note.hasChildren() || note.type === 'search') {
|
||||||
|
node.folder = true;
|
||||||
|
|
||||||
|
if (node.expanded && note.type !== 'search') {
|
||||||
|
node.children = await prepareRealBranch(note);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
node.lazy = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
async function prepareRealBranch(parentNote) {
|
async function prepareRealBranch(parentNote) {
|
||||||
utils.assertArguments(parentNote);
|
utils.assertArguments(parentNote);
|
||||||
|
|
||||||
@@ -35,32 +64,7 @@ async function prepareRealBranch(parentNote) {
|
|||||||
const noteList = [];
|
const noteList = [];
|
||||||
|
|
||||||
for (const branch of childBranches) {
|
for (const branch of childBranches) {
|
||||||
const note = await branch.getNote();
|
const node = await prepareNode(branch);
|
||||||
const title = (branch.prefix ? (branch.prefix + " - ") : "") + note.title;
|
|
||||||
|
|
||||||
const node = {
|
|
||||||
noteId: note.noteId,
|
|
||||||
parentNoteId: branch.parentNoteId,
|
|
||||||
branchId: branch.branchId,
|
|
||||||
isProtected: note.isProtected,
|
|
||||||
title: utils.escapeHtml(title),
|
|
||||||
extraClasses: await getExtraClasses(note),
|
|
||||||
refKey: note.noteId,
|
|
||||||
expanded: note.type !== 'search' && branch.isExpanded
|
|
||||||
};
|
|
||||||
|
|
||||||
const hasChildren = (await note.getChildNotes()).length > 0;
|
|
||||||
|
|
||||||
if (hasChildren || note.type === 'search') {
|
|
||||||
node.folder = true;
|
|
||||||
|
|
||||||
if (node.expanded && note.type !== 'search') {
|
|
||||||
node.children = await prepareRealBranch(note);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
node.lazy = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
noteList.push(node);
|
noteList.push(node);
|
||||||
}
|
}
|
||||||
@@ -70,14 +74,21 @@ async function prepareRealBranch(parentNote) {
|
|||||||
|
|
||||||
async function prepareSearchBranch(note) {
|
async function prepareSearchBranch(note) {
|
||||||
const fullNote = await noteDetailService.loadNote(note.noteId);
|
const fullNote = await noteDetailService.loadNote(note.noteId);
|
||||||
const noteIds = await server.get('search/' + encodeURIComponent(fullNote.jsonContent.searchString));
|
const results = await server.get('search/' + encodeURIComponent(fullNote.jsonContent.searchString));
|
||||||
|
|
||||||
|
const noteIds = results.map(res => res.noteId);
|
||||||
|
|
||||||
|
// force to load all the notes at once instead of one by one
|
||||||
|
await treeCache.getNotes(noteIds);
|
||||||
|
|
||||||
|
for (const result of results) {
|
||||||
|
const origBranch = await treeCache.getBranch(result.branchId);
|
||||||
|
|
||||||
for (const noteId of noteIds) {
|
|
||||||
const branch = new Branch(treeCache, {
|
const branch = new Branch(treeCache, {
|
||||||
branchId: "virt" + utils.randomString(10),
|
branchId: "virt" + utils.randomString(10),
|
||||||
noteId: noteId,
|
noteId: result.noteId,
|
||||||
parentNoteId: note.noteId,
|
parentNoteId: note.noteId,
|
||||||
prefix: '',
|
prefix: origBranch.prefix,
|
||||||
virtual: true
|
virtual: true
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -92,11 +103,15 @@ async function getExtraClasses(note) {
|
|||||||
|
|
||||||
const extraClasses = [];
|
const extraClasses = [];
|
||||||
|
|
||||||
|
if (note.noteId === 'root') {
|
||||||
|
extraClasses.push("tree-root");
|
||||||
|
}
|
||||||
|
|
||||||
if (note.isProtected) {
|
if (note.isProtected) {
|
||||||
extraClasses.push("protected");
|
extraClasses.push("protected");
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((await note.getParentNotes()).length > 1) {
|
if (note.getParentNoteIds().length > 1) {
|
||||||
extraClasses.push("multiple-parents");
|
extraClasses.push("multiple-parents");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,45 +2,93 @@ import utils from "./utils.js";
|
|||||||
import Branch from "../entities/branch.js";
|
import Branch from "../entities/branch.js";
|
||||||
import NoteShort from "../entities/note_short.js";
|
import NoteShort from "../entities/note_short.js";
|
||||||
import infoService from "./info.js";
|
import infoService from "./info.js";
|
||||||
|
import server from "./server.js";
|
||||||
|
|
||||||
class TreeCache {
|
class TreeCache {
|
||||||
load(noteRows, branchRows) {
|
load(noteRows, branchRows, relations) {
|
||||||
this.parents = [];
|
this.parents = {};
|
||||||
this.children = [];
|
this.children = {};
|
||||||
this.childParentToBranch = {};
|
this.childParentToBranch = {};
|
||||||
|
|
||||||
/** @type {Object.<string, NoteShort>} */
|
/** @type {Object.<string, NoteShort>} */
|
||||||
this.notes = {};
|
this.notes = {};
|
||||||
|
|
||||||
|
/** @type {Object.<string, Branch>} */
|
||||||
|
this.branches = {};
|
||||||
|
|
||||||
|
this.addResp(noteRows, branchRows, relations);
|
||||||
|
}
|
||||||
|
|
||||||
|
addResp(noteRows, branchRows, relations) {
|
||||||
for (const noteRow of noteRows) {
|
for (const noteRow of noteRows) {
|
||||||
const note = new NoteShort(this, noteRow);
|
const note = new NoteShort(this, noteRow);
|
||||||
|
|
||||||
this.notes[note.noteId] = note;
|
this.notes[note.noteId] = note;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @type {Object.<string, Branch>} */
|
|
||||||
this.branches = {};
|
|
||||||
for (const branchRow of branchRows) {
|
for (const branchRow of branchRows) {
|
||||||
const branch = new Branch(this, branchRow);
|
const branch = new Branch(this, branchRow);
|
||||||
|
|
||||||
this.addBranch(branch);
|
this.addBranch(branch);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (const relation of relations) {
|
||||||
|
this.addBranchRelationship(relation.branchId, relation.childNoteId, relation.parentNoteId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async getNotes(noteIds) {
|
||||||
|
const missingNoteIds = noteIds.filter(noteId => this.notes[noteId] === undefined);
|
||||||
|
|
||||||
|
if (missingNoteIds.length > 0) {
|
||||||
|
const resp = await server.post('tree/load', { noteIds: missingNoteIds });
|
||||||
|
|
||||||
|
this.addResp(resp.notes, resp.branches, resp.relations);
|
||||||
|
}
|
||||||
|
|
||||||
|
return noteIds.map(noteId => {
|
||||||
|
if (!this.notes[noteId]) {
|
||||||
|
throw new Error(`Can't find note ${noteId}`);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return this.notes[noteId];
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @return NoteShort */
|
/** @return NoteShort */
|
||||||
async getNote(noteId) {
|
async getNote(noteId) {
|
||||||
return this.notes[noteId];
|
if (noteId === 'none') {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (await this.getNotes([noteId]))[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
addBranch(branch) {
|
addBranch(branch) {
|
||||||
this.branches[branch.branchId] = branch;
|
this.branches[branch.branchId] = branch;
|
||||||
|
|
||||||
this.parents[branch.noteId] = this.parents[branch.noteId] || [];
|
this.addBranchRelationship(branch.branchId, branch.noteId, branch.parentNoteId);
|
||||||
this.parents[branch.noteId].push(this.notes[branch.parentNoteId]);
|
}
|
||||||
|
|
||||||
this.children[branch.parentNoteId] = this.children[branch.parentNoteId] || [];
|
addBranchRelationship(branchId, childNoteId, parentNoteId) {
|
||||||
this.children[branch.parentNoteId].push(this.notes[branch.noteId]);
|
if (parentNoteId === 'none') { // applies only to root element
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
this.childParentToBranch[branch.noteId + '-' + branch.parentNoteId] = branch;
|
this.childParentToBranch[childNoteId + '-' + parentNoteId] = branchId;
|
||||||
|
|
||||||
|
this.parents[childNoteId] = this.parents[childNoteId] || [];
|
||||||
|
|
||||||
|
if (!this.parents[childNoteId].includes(parentNoteId)) {
|
||||||
|
this.parents[childNoteId].push(parentNoteId);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.children[parentNoteId] = this.children[parentNoteId] || [];
|
||||||
|
|
||||||
|
if (!this.children[parentNoteId].includes(childNoteId)) {
|
||||||
|
this.children[parentNoteId].push(childNoteId);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
add(note, branch) {
|
add(note, branch) {
|
||||||
@@ -49,21 +97,46 @@ class TreeCache {
|
|||||||
this.addBranch(branch);
|
this.addBranch(branch);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async getBranches(branchIds) {
|
||||||
|
const missingBranchIds = branchIds.filter(branchId => this.branches[branchId] === undefined);
|
||||||
|
|
||||||
|
if (missingBranchIds.length > 0) {
|
||||||
|
const resp = await server.post('tree/load', { branchIds: branchIds });
|
||||||
|
|
||||||
|
this.addResp(resp.notes, resp.branches, resp.relations);
|
||||||
|
}
|
||||||
|
|
||||||
|
return branchIds.map(branchId => {
|
||||||
|
if (!this.branches[branchId]) {
|
||||||
|
throw new Error(`Can't find branch ${branchId}`);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return this.branches[branchId];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/** @return Branch */
|
/** @return Branch */
|
||||||
async getBranch(branchId) {
|
async getBranch(branchId) {
|
||||||
return this.branches[branchId];
|
return (await this.getBranches([branchId]))[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @return Branch */
|
/** @return Branch */
|
||||||
async getBranchByChildParent(childNoteId, parentNoteId) {
|
async getBranchByChildParent(childNoteId, parentNoteId) {
|
||||||
const key = (childNoteId + '-' + parentNoteId);
|
const branchId = this.getBranchIdByChildParent(childNoteId, parentNoteId);
|
||||||
const branch = this.childParentToBranch[key];
|
|
||||||
|
|
||||||
if (!branch) {
|
return await this.getBranch(branchId);
|
||||||
|
}
|
||||||
|
|
||||||
|
getBranchIdByChildParent(childNoteId, parentNoteId) {
|
||||||
|
const key = childNoteId + '-' + parentNoteId;
|
||||||
|
const branchId = this.childParentToBranch[key];
|
||||||
|
|
||||||
|
if (!branchId) {
|
||||||
infoService.throwError("Cannot find branch for child-parent=" + key);
|
infoService.throwError("Cannot find branch for child-parent=" + key);
|
||||||
}
|
}
|
||||||
|
|
||||||
return branch;
|
return branchId;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Move note from one parent to another. */
|
/* Move note from one parent to another. */
|
||||||
@@ -78,33 +151,14 @@ class TreeCache {
|
|||||||
delete treeCache.childParentToBranch[childNoteId + '-' + oldParentNoteId]; // this is correct because we know that oldParentId isn't same as newParentId
|
delete treeCache.childParentToBranch[childNoteId + '-' + oldParentNoteId]; // this is correct because we know that oldParentId isn't same as newParentId
|
||||||
|
|
||||||
// remove old associations
|
// remove old associations
|
||||||
treeCache.parents[childNoteId] = treeCache.parents[childNoteId].filter(p => p.noteId !== oldParentNoteId);
|
treeCache.parents[childNoteId] = treeCache.parents[childNoteId].filter(p => p !== oldParentNoteId);
|
||||||
treeCache.children[oldParentNoteId] = treeCache.children[oldParentNoteId].filter(ch => ch.noteId !== childNoteId);
|
treeCache.children[oldParentNoteId] = treeCache.children[oldParentNoteId].filter(ch => ch !== childNoteId);
|
||||||
|
|
||||||
// add new associations
|
// add new associations
|
||||||
treeCache.parents[childNoteId].push(await treeCache.getNote(newParentNoteId));
|
treeCache.parents[childNoteId].push(newParentNoteId);
|
||||||
|
|
||||||
treeCache.children[newParentNoteId] = treeCache.children[newParentNoteId] || []; // this might be first child
|
treeCache.children[newParentNoteId] = treeCache.children[newParentNoteId] || []; // this might be first child
|
||||||
treeCache.children[newParentNoteId].push(await treeCache.getNote(childNoteId));
|
treeCache.children[newParentNoteId].push(childNoteId);
|
||||||
}
|
|
||||||
|
|
||||||
removeParentChildRelation(parentNoteId, childNoteId) {
|
|
||||||
utils.assertArguments(parentNoteId, childNoteId);
|
|
||||||
|
|
||||||
treeCache.parents[childNoteId] = treeCache.parents[childNoteId].filter(p => p.noteId !== parentNoteId);
|
|
||||||
treeCache.children[parentNoteId] = treeCache.children[parentNoteId].filter(ch => ch.noteId !== childNoteId);
|
|
||||||
|
|
||||||
delete treeCache.childParentToBranch[childNoteId + '-' + parentNoteId];
|
|
||||||
}
|
|
||||||
|
|
||||||
async setParentChildRelation(branchId, parentNoteId, childNoteId) {
|
|
||||||
treeCache.parents[childNoteId] = treeCache.parents[childNoteId] || [];
|
|
||||||
treeCache.parents[childNoteId].push(await treeCache.getNote(parentNoteId));
|
|
||||||
|
|
||||||
treeCache.children[parentNoteId] = treeCache.children[parentNoteId] || [];
|
|
||||||
treeCache.children[parentNoteId].push(await treeCache.getNote(childNoteId));
|
|
||||||
|
|
||||||
treeCache.childParentToBranch[childNoteId + '-' + parentNoteId] = await treeCache.getBranch(branchId);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -52,6 +52,15 @@ async function getNotePathTitle(notePath) {
|
|||||||
|
|
||||||
const titlePath = [];
|
const titlePath = [];
|
||||||
|
|
||||||
|
if (notePath.startsWith('root/')) {
|
||||||
|
notePath = notePath.substr(5);
|
||||||
|
}
|
||||||
|
|
||||||
|
// special case when we want just root's title
|
||||||
|
if (notePath === 'root') {
|
||||||
|
return await getNoteTitle(notePath);
|
||||||
|
}
|
||||||
|
|
||||||
let parentNoteId = 'root';
|
let parentNoteId = 'root';
|
||||||
|
|
||||||
for (const noteId of notePath.split('/')) {
|
for (const noteId of notePath.split('/')) {
|
||||||
|
|||||||
51
src/public/javascripts/services/zoom.js
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
import server from "./server.js";
|
||||||
|
import utils from "./utils.js";
|
||||||
|
import optionsInitService from "./options_init.js";
|
||||||
|
|
||||||
|
const MIN_ZOOM = 0.5;
|
||||||
|
const MAX_ZOOM = 2.0;
|
||||||
|
|
||||||
|
async function decreaseZoomFactor() {
|
||||||
|
await setZoomFactorAndSave(getCurrentZoom() - 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function increaseZoomFactor() {
|
||||||
|
await setZoomFactorAndSave(getCurrentZoom() + 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
function setZoomFactor(zoomFactor) {
|
||||||
|
zoomFactor = parseFloat(zoomFactor);
|
||||||
|
|
||||||
|
const webFrame = require('electron').webFrame;
|
||||||
|
webFrame.setZoomFactor(zoomFactor);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function setZoomFactorAndSave(zoomFactor) {
|
||||||
|
if (!utils.isElectron()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (zoomFactor >= MIN_ZOOM && zoomFactor <= MAX_ZOOM) {
|
||||||
|
setZoomFactor(zoomFactor);
|
||||||
|
|
||||||
|
await server.put('options/zoomFactor/' + zoomFactor);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
console.log(`Zoom factor ${zoomFactor} outside of the range, ignored.`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getCurrentZoom() {
|
||||||
|
return require('electron').webFrame.getZoomFactor();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (utils.isElectron()) {
|
||||||
|
optionsInitService.optionsReady.then(options => setZoomFactor(options.zoomFactor))
|
||||||
|
}
|
||||||
|
|
||||||
|
export default {
|
||||||
|
decreaseZoomFactor,
|
||||||
|
increaseZoomFactor,
|
||||||
|
setZoomFactor,
|
||||||
|
setZoomFactorAndSave
|
||||||
|
}
|
||||||