mirror of
				https://github.com/zadam/trilium.git
				synced 2025-11-02 19:36:12 +01:00 
			
		
		
		
	Compare commits
	
		
			227 Commits
		
	
	
		
			v0.27.0-be
			...
			v0.30.3-be
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					b48474998b | ||
| 
						 | 
					95d8f07458 | ||
| 
						 | 
					e628c30c89 | ||
| 
						 | 
					52a8aae74f | ||
| 
						 | 
					54b5898582 | ||
| 
						 | 
					64974d75d5 | ||
| 
						 | 
					bd118027fb | ||
| 
						 | 
					36de217835 | ||
| 
						 | 
					b5283d58bb | ||
| 
						 | 
					8414d97ffa | ||
| 
						 | 
					9f30d4e673 | ||
| 
						 | 
					8e0d1fa0df | ||
| 
						 | 
					5b251b9977 | ||
| 
						 | 
					8b3e721028 | ||
| 
						 | 
					7e2a2baa5d | ||
| 
						 | 
					003eed368b | ||
| 
						 | 
					4b1cf05c0e | ||
| 
						 | 
					d9429c4f4b | ||
| 
						 | 
					b7bd94b6b0 | ||
| 
						 | 
					51bbc10744 | ||
| 
						 | 
					fb5df33ee7 | ||
| 
						 | 
					d8ba0ccd7d | ||
| 
						 | 
					886ea6c68c | ||
| 
						 | 
					936f85c09e | ||
| 
						 | 
					b25deea21d | ||
| 
						 | 
					cf5ec44303 | ||
| 
						 | 
					6f956c2415 | ||
| 
						 | 
					3533160bef | ||
| 
						 | 
					60cbfdcabd | ||
| 
						 | 
					604f036a54 | ||
| 
						 | 
					dd4f3ec264 | ||
| 
						 | 
					ff67b8a0ba | ||
| 
						 | 
					1d5fb0b646 | ||
| 
						 | 
					80931a318f | ||
| 
						 | 
					16f16cb36a | ||
| 
						 | 
					899f24cde5 | ||
| 
						 | 
					9f29521ab8 | ||
| 
						 | 
					c1ce0c6b22 | ||
| 
						 | 
					a7fce33750 | ||
| 
						 | 
					6fd8e73150 | ||
| 
						 | 
					1359dd86c2 | ||
| 
						 | 
					a1b610fc50 | ||
| 
						 | 
					c849d719e9 | ||
| 
						 | 
					96de2e7008 | ||
| 
						 | 
					f140b77e7c | ||
| 
						 | 
					22228de63b | ||
| 
						 | 
					b0596c9eb2 | ||
| 
						 | 
					2a2319d434 | ||
| 
						 | 
					dad47d115f | ||
| 
						 | 
					502026359c | ||
| 
						 | 
					47b0e4e4d3 | ||
| 
						 | 
					6c927d9159 | ||
| 
						 | 
					ddc79b2517 | ||
| 
						 | 
					4c2e12d2cb | ||
| 
						 | 
					14f7a8b7b9 | ||
| 
						 | 
					caa7dd9619 | ||
| 
						 | 
					8aa7e2d0a0 | ||
| 
						 | 
					6be8a3f343 | ||
| 
						 | 
					a097cefba7 | ||
| 
						 | 
					e4c78f3887 | ||
| 
						 | 
					5baa251944 | ||
| 
						 | 
					cde68abec9 | ||
| 
						 | 
					51175e3676 | ||
| 
						 | 
					45ddfef30a | ||
| 
						 | 
					1e1d78999e | ||
| 
						 | 
					92fcd7b345 | ||
| 
						 | 
					e04f1cd574 | ||
| 
						 | 
					176c3a5d51 | ||
| 
						 | 
					c09570cf39 | ||
| 
						 | 
					4a093000be | ||
| 
						 | 
					6952b643ae | ||
| 
						 | 
					c487a95bc7 | ||
| 
						 | 
					8884177d9f | ||
| 
						 | 
					8b250ed523 | ||
| 
						 | 
					d8b78d8025 | ||
| 
						 | 
					42112b8053 | ||
| 
						 | 
					6cc0dd5a80 | ||
| 
						 | 
					afd5f4823f | ||
| 
						 | 
					b0cf82c91b | ||
| 
						 | 
					6a67cdd5af | ||
| 
						 | 
					bad7b84993 | ||
| 
						 | 
					d3ca6b5ae6 | ||
| 
						 | 
					da5009f089 | ||
| 
						 | 
					c08524c977 | ||
| 
						 | 
					f89537037e | ||
| 
						 | 
					c153793766 | ||
| 
						 | 
					0aec5927d5 | ||
| 
						 | 
					8aea9a1801 | ||
| 
						 | 
					73247e3220 | ||
| 
						 | 
					89344a6eda | ||
| 
						 | 
					40d2e6ea83 | ||
| 
						 | 
					910cfe9a17 | ||
| 
						 | 
					e58a80fc00 | ||
| 
						 | 
					4a2319cb33 | ||
| 
						 | 
					5619088c41 | ||
| 
						 | 
					60271993eb | ||
| 
						 | 
					6695e8b011 | ||
| 
						 | 
					707df18b93 | ||
| 
						 | 
					90895f1288 | ||
| 
						 | 
					ba1ca506af | ||
| 
						 | 
					f90ed99a40 | ||
| 
						 | 
					67630b1a22 | ||
| 
						 | 
					2c1580ea65 | ||
| 
						 | 
					840a0b5f64 | ||
| 
						 | 
					b39f6ef7ad | ||
| 
						 | 
					fb27088fcd | ||
| 
						 | 
					76fbff68ba | ||
| 
						 | 
					54de4d236d | ||
| 
						 | 
					e211dd65ad | ||
| 
						 | 
					a87f4d8653 | ||
| 
						 | 
					b59c175c2e | ||
| 
						 | 
					580104c4c5 | ||
| 
						 | 
					0fc3053b0a | ||
| 
						 | 
					929e0f69c2 | ||
| 
						 | 
					e70af1300a | ||
| 
						 | 
					4d0e46021b | ||
| 
						 | 
					1a9a49b739 | ||
| 
						 | 
					2ac560c56e | ||
| 
						 | 
					c23387c0fb | ||
| 
						 | 
					0ff250fe15 | ||
| 
						 | 
					52b1e58b26 | ||
| 
						 | 
					484715e440 | ||
| 
						 | 
					9d42c3d802 | ||
| 
						 | 
					c654172d33 | ||
| 
						 | 
					e17b26c883 | ||
| 
						 | 
					24d02d9cf5 | ||
| 
						 | 
					7208a311ac | ||
| 
						 | 
					ad7355372b | ||
| 
						 | 
					f18b5babad | ||
| 
						 | 
					9831ec0ca9 | ||
| 
						 | 
					596544eca3 | ||
| 
						 | 
					1f853024ee | ||
| 
						 | 
					0308b13460 | ||
| 
						 | 
					06b8a82f70 | ||
| 
						 | 
					91ca07929d | ||
| 
						 | 
					afabaa5fdb | ||
| 
						 | 
					a6fd3fa77c | ||
| 
						 | 
					58a2c08dcd | ||
| 
						 | 
					299bbff2f4 | ||
| 
						 | 
					19d8947123 | ||
| 
						 | 
					35edce7523 | ||
| 
						 | 
					bc4cec69a5 | ||
| 
						 | 
					cce8c1b674 | ||
| 
						 | 
					aa58788769 | ||
| 
						 | 
					6c62ab7a52 | ||
| 
						 | 
					d6ab638b30 | ||
| 
						 | 
					bd4db406de | ||
| 
						 | 
					e50f9cd0a3 | ||
| 
						 | 
					2b64cbce2c | ||
| 
						 | 
					2797c942ab | ||
| 
						 | 
					f1c3278874 | ||
| 
						 | 
					424c22dcde | ||
| 
						 | 
					5c223dfd12 | ||
| 
						 | 
					6a3e7a5a8e | ||
| 
						 | 
					f88cdac000 | ||
| 
						 | 
					eeead90f32 | ||
| 
						 | 
					b607857409 | ||
| 
						 | 
					9268f88bc3 | ||
| 
						 | 
					f7f0560a9f | ||
| 
						 | 
					3d8905207e | ||
| 
						 | 
					dbc312010b | ||
| 
						 | 
					b115a7cf19 | ||
| 
						 | 
					348562352c | ||
| 
						 | 
					70fd917e7c | ||
| 
						 | 
					d2b60764cd | ||
| 
						 | 
					581b1fdaa5 | ||
| 
						 | 
					3c19a712c0 | ||
| 
						 | 
					cc27f16088 | ||
| 
						 | 
					dffdb82288 | ||
| 
						 | 
					2b32addade | ||
| 
						 | 
					0b251530fa | ||
| 
						 | 
					f5b933149a | ||
| 
						 | 
					48bbfb8bdb | ||
| 
						 | 
					889971c4d6 | ||
| 
						 | 
					0722494d41 | ||
| 
						 | 
					4b977a3306 | ||
| 
						 | 
					3ff3021acd | ||
| 
						 | 
					99e56a9c42 | ||
| 
						 | 
					77279dfe16 | ||
| 
						 | 
					93f8050454 | ||
| 
						 | 
					31cfede7a7 | ||
| 
						 | 
					c8ec86e537 | ||
| 
						 | 
					05aee884b6 | ||
| 
						 | 
					012ba9e060 | ||
| 
						 | 
					8e8fd88857 | ||
| 
						 | 
					523ccdad6b | ||
| 
						 | 
					ded3f605be | ||
| 
						 | 
					62b44e3549 | ||
| 
						 | 
					030d12a465 | ||
| 
						 | 
					4d15628840 | ||
| 
						 | 
					81b849898c | ||
| 
						 | 
					3824486b85 | ||
| 
						 | 
					081ab00a0a | ||
| 
						 | 
					04f6af5c9a | ||
| 
						 | 
					4dc1f1f6eb | ||
| 
						 | 
					3930a02123 | ||
| 
						 | 
					3112de105e | ||
| 
						 | 
					3b8d7b8fba | ||
| 
						 | 
					9fca7f09a5 | ||
| 
						 | 
					fd39d6b3a9 | ||
| 
						 | 
					a103886ea5 | ||
| 
						 | 
					373408e401 | ||
| 
						 | 
					db44c1d8e6 | ||
| 
						 | 
					95a34c9e2d | ||
| 
						 | 
					6ce401f260 | ||
| 
						 | 
					5d74dcd256 | ||
| 
						 | 
					5a9fc1697b | ||
| 
						 | 
					927415838c | ||
| 
						 | 
					d72fcefdc7 | ||
| 
						 | 
					0be173a8f7 | ||
| 
						 | 
					c3913a8735 | ||
| 
						 | 
					e2dfe1b6de | ||
| 
						 | 
					fec3e47eb8 | ||
| 
						 | 
					d72efd2450 | ||
| 
						 | 
					ef1c840aa7 | ||
| 
						 | 
					1581464d8c | ||
| 
						 | 
					9de29584a4 | ||
| 
						 | 
					9e2e6fb50c | ||
| 
						 | 
					c85979b66b | ||
| 
						 | 
					ecdc5865a6 | ||
| 
						 | 
					1771ddb787 | ||
| 
						 | 
					3ab657fe46 | ||
| 
						 | 
					8785dae753 | ||
| 
						 | 
					2f1c5b29d4 | ||
| 
						 | 
					7135349a10 | ||
| 
						 | 
					66c639d5e3 | ||
| 
						 | 
					6704b755d8 | 
							
								
								
									
										7
									
								
								.gitpod.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								.gitpod.yml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,7 @@
 | 
			
		||||
tasks:
 | 
			
		||||
    - before: nvm install 10 && nvm use 10
 | 
			
		||||
      init: npm install
 | 
			
		||||
      command: npm run start
 | 
			
		||||
ports:
 | 
			
		||||
    - port: 8080
 | 
			
		||||
      onOpen: open-preview
 | 
			
		||||
@@ -16,586 +16,662 @@
 | 
			
		||||
    <table id="8" parent="2" name="branches"/>
 | 
			
		||||
    <table id="9" parent="2" name="event_log"/>
 | 
			
		||||
    <table id="10" parent="2" name="links"/>
 | 
			
		||||
    <table id="11" parent="2" name="note_revisions"/>
 | 
			
		||||
    <table id="12" parent="2" name="notes"/>
 | 
			
		||||
    <table id="13" parent="2" name="options"/>
 | 
			
		||||
    <table id="14" parent="2" name="recent_notes"/>
 | 
			
		||||
    <table id="15" parent="2" name="source_ids"/>
 | 
			
		||||
    <table id="16" parent="2" name="sqlite_master">
 | 
			
		||||
    <table id="11" parent="2" name="note_contents"/>
 | 
			
		||||
    <table id="12" parent="2" name="note_revisions"/>
 | 
			
		||||
    <table id="13" parent="2" name="notes"/>
 | 
			
		||||
    <table id="14" parent="2" name="options"/>
 | 
			
		||||
    <table id="15" parent="2" name="recent_notes"/>
 | 
			
		||||
    <table id="16" parent="2" name="source_ids"/>
 | 
			
		||||
    <table id="17" parent="2" name="sqlite_master">
 | 
			
		||||
      <System>1</System>
 | 
			
		||||
    </table>
 | 
			
		||||
    <table id="17" parent="2" name="sqlite_sequence">
 | 
			
		||||
    <table id="18" parent="2" name="sqlite_sequence">
 | 
			
		||||
      <System>1</System>
 | 
			
		||||
    </table>
 | 
			
		||||
    <table id="18" parent="2" name="sync"/>
 | 
			
		||||
    <column id="19" parent="6" name="apiTokenId">
 | 
			
		||||
    <table id="19" parent="2" name="sync"/>
 | 
			
		||||
    <column id="20" parent="6" name="apiTokenId">
 | 
			
		||||
      <Position>1</Position>
 | 
			
		||||
      <DataType>TEXT|0s</DataType>
 | 
			
		||||
      <NotNull>1</NotNull>
 | 
			
		||||
    </column>
 | 
			
		||||
    <column id="20" parent="6" name="token">
 | 
			
		||||
    <column id="21" parent="6" name="token">
 | 
			
		||||
      <Position>2</Position>
 | 
			
		||||
      <DataType>TEXT|0s</DataType>
 | 
			
		||||
      <NotNull>1</NotNull>
 | 
			
		||||
    </column>
 | 
			
		||||
    <column id="21" parent="6" name="dateCreated">
 | 
			
		||||
    <column id="22" parent="6" name="dateCreated">
 | 
			
		||||
      <Position>3</Position>
 | 
			
		||||
      <DataType>TEXT|0s</DataType>
 | 
			
		||||
      <NotNull>1</NotNull>
 | 
			
		||||
    </column>
 | 
			
		||||
    <column id="22" parent="6" name="isDeleted">
 | 
			
		||||
    <column id="23" parent="6" name="isDeleted">
 | 
			
		||||
      <Position>4</Position>
 | 
			
		||||
      <DataType>INT|0s</DataType>
 | 
			
		||||
      <NotNull>1</NotNull>
 | 
			
		||||
      <DefaultExpression>0</DefaultExpression>
 | 
			
		||||
    </column>
 | 
			
		||||
    <column id="23" parent="6" name="hash">
 | 
			
		||||
    <column id="24" parent="6" name="hash">
 | 
			
		||||
      <Position>5</Position>
 | 
			
		||||
      <DataType>TEXT|0s</DataType>
 | 
			
		||||
      <NotNull>1</NotNull>
 | 
			
		||||
      <DefaultExpression>""</DefaultExpression>
 | 
			
		||||
    </column>
 | 
			
		||||
    <index id="24" parent="6" name="sqlite_autoindex_api_tokens_1">
 | 
			
		||||
    <index id="25" parent="6" name="sqlite_autoindex_api_tokens_1">
 | 
			
		||||
      <NameSurrogate>1</NameSurrogate>
 | 
			
		||||
      <ColNames>apiTokenId</ColNames>
 | 
			
		||||
      <ColumnCollations></ColumnCollations>
 | 
			
		||||
      <Unique>1</Unique>
 | 
			
		||||
    </index>
 | 
			
		||||
    <key id="25" parent="6">
 | 
			
		||||
    <key id="26" parent="6">
 | 
			
		||||
      <ColNames>apiTokenId</ColNames>
 | 
			
		||||
      <Primary>1</Primary>
 | 
			
		||||
      <UnderlyingIndexName>sqlite_autoindex_api_tokens_1</UnderlyingIndexName>
 | 
			
		||||
    </key>
 | 
			
		||||
    <column id="26" parent="7" name="attributeId">
 | 
			
		||||
    <column id="27" parent="7" name="attributeId">
 | 
			
		||||
      <Position>1</Position>
 | 
			
		||||
      <DataType>TEXT|0s</DataType>
 | 
			
		||||
      <NotNull>1</NotNull>
 | 
			
		||||
    </column>
 | 
			
		||||
    <column id="27" parent="7" name="noteId">
 | 
			
		||||
    <column id="28" parent="7" name="noteId">
 | 
			
		||||
      <Position>2</Position>
 | 
			
		||||
      <DataType>TEXT|0s</DataType>
 | 
			
		||||
      <NotNull>1</NotNull>
 | 
			
		||||
    </column>
 | 
			
		||||
    <column id="28" parent="7" name="type">
 | 
			
		||||
    <column id="29" parent="7" name="type">
 | 
			
		||||
      <Position>3</Position>
 | 
			
		||||
      <DataType>TEXT|0s</DataType>
 | 
			
		||||
      <NotNull>1</NotNull>
 | 
			
		||||
    </column>
 | 
			
		||||
    <column id="29" parent="7" name="name">
 | 
			
		||||
    <column id="30" parent="7" name="name">
 | 
			
		||||
      <Position>4</Position>
 | 
			
		||||
      <DataType>TEXT|0s</DataType>
 | 
			
		||||
      <NotNull>1</NotNull>
 | 
			
		||||
    </column>
 | 
			
		||||
    <column id="30" parent="7" name="value">
 | 
			
		||||
    <column id="31" parent="7" name="value">
 | 
			
		||||
      <Position>5</Position>
 | 
			
		||||
      <DataType>TEXT|0s</DataType>
 | 
			
		||||
      <NotNull>1</NotNull>
 | 
			
		||||
      <DefaultExpression>''</DefaultExpression>
 | 
			
		||||
    </column>
 | 
			
		||||
    <column id="31" parent="7" name="position">
 | 
			
		||||
    <column id="32" parent="7" name="position">
 | 
			
		||||
      <Position>6</Position>
 | 
			
		||||
      <DataType>INT|0s</DataType>
 | 
			
		||||
      <NotNull>1</NotNull>
 | 
			
		||||
      <DefaultExpression>0</DefaultExpression>
 | 
			
		||||
    </column>
 | 
			
		||||
    <column id="32" parent="7" name="dateCreated">
 | 
			
		||||
    <column id="33" parent="7" name="dateCreated">
 | 
			
		||||
      <Position>7</Position>
 | 
			
		||||
      <DataType>TEXT|0s</DataType>
 | 
			
		||||
      <NotNull>1</NotNull>
 | 
			
		||||
    </column>
 | 
			
		||||
    <column id="33" parent="7" name="dateModified">
 | 
			
		||||
    <column id="34" parent="7" name="dateModified">
 | 
			
		||||
      <Position>8</Position>
 | 
			
		||||
      <DataType>TEXT|0s</DataType>
 | 
			
		||||
      <NotNull>1</NotNull>
 | 
			
		||||
    </column>
 | 
			
		||||
    <column id="34" parent="7" name="isDeleted">
 | 
			
		||||
    <column id="35" parent="7" name="isDeleted">
 | 
			
		||||
      <Position>9</Position>
 | 
			
		||||
      <DataType>INT|0s</DataType>
 | 
			
		||||
      <NotNull>1</NotNull>
 | 
			
		||||
    </column>
 | 
			
		||||
    <column id="35" parent="7" name="hash">
 | 
			
		||||
    <column id="36" parent="7" name="hash">
 | 
			
		||||
      <Position>10</Position>
 | 
			
		||||
      <DataType>TEXT|0s</DataType>
 | 
			
		||||
      <NotNull>1</NotNull>
 | 
			
		||||
      <DefaultExpression>""</DefaultExpression>
 | 
			
		||||
    </column>
 | 
			
		||||
    <column id="36" parent="7" name="isInheritable">
 | 
			
		||||
    <column id="37" parent="7" name="isInheritable">
 | 
			
		||||
      <Position>11</Position>
 | 
			
		||||
      <DataType>int|0s</DataType>
 | 
			
		||||
      <DefaultExpression>0</DefaultExpression>
 | 
			
		||||
    </column>
 | 
			
		||||
    <index id="37" parent="7" name="sqlite_autoindex_attributes_1">
 | 
			
		||||
    <index id="38" parent="7" name="sqlite_autoindex_attributes_1">
 | 
			
		||||
      <NameSurrogate>1</NameSurrogate>
 | 
			
		||||
      <ColNames>attributeId</ColNames>
 | 
			
		||||
      <ColumnCollations></ColumnCollations>
 | 
			
		||||
      <Unique>1</Unique>
 | 
			
		||||
    </index>
 | 
			
		||||
    <key id="38" parent="7">
 | 
			
		||||
    <index id="39" parent="7" name="IDX_attributes_noteId_index">
 | 
			
		||||
      <ColNames>noteId</ColNames>
 | 
			
		||||
      <ColumnCollations></ColumnCollations>
 | 
			
		||||
    </index>
 | 
			
		||||
    <index id="40" parent="7" name="IDX_attributes_name_value">
 | 
			
		||||
      <ColNames>name
 | 
			
		||||
value</ColNames>
 | 
			
		||||
      <ColumnCollations></ColumnCollations>
 | 
			
		||||
    </index>
 | 
			
		||||
    <index id="41" parent="7" name="IDX_attributes_name_index">
 | 
			
		||||
      <ColNames>name</ColNames>
 | 
			
		||||
      <ColumnCollations></ColumnCollations>
 | 
			
		||||
    </index>
 | 
			
		||||
    <index id="42" parent="7" name="IDX_attributes_value_index">
 | 
			
		||||
      <ColNames>value</ColNames>
 | 
			
		||||
      <ColumnCollations></ColumnCollations>
 | 
			
		||||
    </index>
 | 
			
		||||
    <key id="43" parent="7">
 | 
			
		||||
      <ColNames>attributeId</ColNames>
 | 
			
		||||
      <Primary>1</Primary>
 | 
			
		||||
      <UnderlyingIndexName>sqlite_autoindex_attributes_1</UnderlyingIndexName>
 | 
			
		||||
    </key>
 | 
			
		||||
    <column id="39" parent="8" name="branchId">
 | 
			
		||||
    <column id="44" parent="8" name="branchId">
 | 
			
		||||
      <Position>1</Position>
 | 
			
		||||
      <DataType>TEXT|0s</DataType>
 | 
			
		||||
      <NotNull>1</NotNull>
 | 
			
		||||
    </column>
 | 
			
		||||
    <column id="40" parent="8" name="noteId">
 | 
			
		||||
    <column id="45" parent="8" name="noteId">
 | 
			
		||||
      <Position>2</Position>
 | 
			
		||||
      <DataType>TEXT|0s</DataType>
 | 
			
		||||
      <NotNull>1</NotNull>
 | 
			
		||||
    </column>
 | 
			
		||||
    <column id="41" parent="8" name="parentNoteId">
 | 
			
		||||
    <column id="46" parent="8" name="parentNoteId">
 | 
			
		||||
      <Position>3</Position>
 | 
			
		||||
      <DataType>TEXT|0s</DataType>
 | 
			
		||||
      <NotNull>1</NotNull>
 | 
			
		||||
    </column>
 | 
			
		||||
    <column id="42" parent="8" name="notePosition">
 | 
			
		||||
    <column id="47" parent="8" name="notePosition">
 | 
			
		||||
      <Position>4</Position>
 | 
			
		||||
      <DataType>INTEGER|0s</DataType>
 | 
			
		||||
      <NotNull>1</NotNull>
 | 
			
		||||
    </column>
 | 
			
		||||
    <column id="43" parent="8" name="prefix">
 | 
			
		||||
    <column id="48" parent="8" name="prefix">
 | 
			
		||||
      <Position>5</Position>
 | 
			
		||||
      <DataType>TEXT|0s</DataType>
 | 
			
		||||
    </column>
 | 
			
		||||
    <column id="44" parent="8" name="isExpanded">
 | 
			
		||||
    <column id="49" parent="8" name="isExpanded">
 | 
			
		||||
      <Position>6</Position>
 | 
			
		||||
      <DataType>BOOLEAN|0s</DataType>
 | 
			
		||||
    </column>
 | 
			
		||||
    <column id="45" parent="8" name="isDeleted">
 | 
			
		||||
    <column id="50" parent="8" name="isDeleted">
 | 
			
		||||
      <Position>7</Position>
 | 
			
		||||
      <DataType>INTEGER|0s</DataType>
 | 
			
		||||
      <NotNull>1</NotNull>
 | 
			
		||||
      <DefaultExpression>0</DefaultExpression>
 | 
			
		||||
    </column>
 | 
			
		||||
    <column id="46" parent="8" name="dateModified">
 | 
			
		||||
    <column id="51" parent="8" name="dateModified">
 | 
			
		||||
      <Position>8</Position>
 | 
			
		||||
      <DataType>TEXT|0s</DataType>
 | 
			
		||||
      <NotNull>1</NotNull>
 | 
			
		||||
    </column>
 | 
			
		||||
    <column id="47" parent="8" name="hash">
 | 
			
		||||
    <column id="52" parent="8" name="hash">
 | 
			
		||||
      <Position>9</Position>
 | 
			
		||||
      <DataType>TEXT|0s</DataType>
 | 
			
		||||
      <NotNull>1</NotNull>
 | 
			
		||||
      <DefaultExpression>""</DefaultExpression>
 | 
			
		||||
    </column>
 | 
			
		||||
    <column id="48" parent="8" name="dateCreated">
 | 
			
		||||
    <column id="53" parent="8" name="dateCreated">
 | 
			
		||||
      <Position>10</Position>
 | 
			
		||||
      <DataType>TEXT|0s</DataType>
 | 
			
		||||
      <NotNull>1</NotNull>
 | 
			
		||||
      <DefaultExpression>'1970-01-01T00:00:00.000Z'</DefaultExpression>
 | 
			
		||||
    </column>
 | 
			
		||||
    <index id="49" parent="8" name="sqlite_autoindex_branches_1">
 | 
			
		||||
    <index id="54" parent="8" name="sqlite_autoindex_branches_1">
 | 
			
		||||
      <NameSurrogate>1</NameSurrogate>
 | 
			
		||||
      <ColNames>branchId</ColNames>
 | 
			
		||||
      <ColumnCollations></ColumnCollations>
 | 
			
		||||
      <Unique>1</Unique>
 | 
			
		||||
    </index>
 | 
			
		||||
    <index id="50" parent="8" name="IDX_branches_noteId_parentNoteId">
 | 
			
		||||
    <index id="55" parent="8" name="IDX_branches_noteId_parentNoteId">
 | 
			
		||||
      <ColNames>noteId
 | 
			
		||||
parentNoteId</ColNames>
 | 
			
		||||
      <ColumnCollations></ColumnCollations>
 | 
			
		||||
    </index>
 | 
			
		||||
    <index id="51" parent="8" name="IDX_branches_noteId">
 | 
			
		||||
    <index id="56" parent="8" name="IDX_branches_noteId">
 | 
			
		||||
      <ColNames>noteId</ColNames>
 | 
			
		||||
      <ColumnCollations></ColumnCollations>
 | 
			
		||||
    </index>
 | 
			
		||||
    <index id="52" parent="8" name="IDX_branches_parentNoteId">
 | 
			
		||||
    <index id="57" parent="8" name="IDX_branches_parentNoteId">
 | 
			
		||||
      <ColNames>parentNoteId</ColNames>
 | 
			
		||||
      <ColumnCollations></ColumnCollations>
 | 
			
		||||
    </index>
 | 
			
		||||
    <key id="53" parent="8">
 | 
			
		||||
    <key id="58" parent="8">
 | 
			
		||||
      <ColNames>branchId</ColNames>
 | 
			
		||||
      <Primary>1</Primary>
 | 
			
		||||
      <UnderlyingIndexName>sqlite_autoindex_branches_1</UnderlyingIndexName>
 | 
			
		||||
    </key>
 | 
			
		||||
    <column id="54" parent="9" name="eventId">
 | 
			
		||||
    <column id="59" parent="9" name="eventId">
 | 
			
		||||
      <Position>1</Position>
 | 
			
		||||
      <DataType>TEXT|0s</DataType>
 | 
			
		||||
      <NotNull>1</NotNull>
 | 
			
		||||
    </column>
 | 
			
		||||
    <column id="55" parent="9" name="noteId">
 | 
			
		||||
    <column id="60" parent="9" name="noteId">
 | 
			
		||||
      <Position>2</Position>
 | 
			
		||||
      <DataType>TEXT|0s</DataType>
 | 
			
		||||
    </column>
 | 
			
		||||
    <column id="56" parent="9" name="comment">
 | 
			
		||||
    <column id="61" parent="9" name="comment">
 | 
			
		||||
      <Position>3</Position>
 | 
			
		||||
      <DataType>TEXT|0s</DataType>
 | 
			
		||||
    </column>
 | 
			
		||||
    <column id="57" parent="9" name="dateCreated">
 | 
			
		||||
    <column id="62" parent="9" name="dateCreated">
 | 
			
		||||
      <Position>4</Position>
 | 
			
		||||
      <DataType>TEXT|0s</DataType>
 | 
			
		||||
      <NotNull>1</NotNull>
 | 
			
		||||
    </column>
 | 
			
		||||
    <index id="58" parent="9" name="sqlite_autoindex_event_log_1">
 | 
			
		||||
    <index id="63" parent="9" name="sqlite_autoindex_event_log_1">
 | 
			
		||||
      <NameSurrogate>1</NameSurrogate>
 | 
			
		||||
      <ColNames>eventId</ColNames>
 | 
			
		||||
      <ColumnCollations></ColumnCollations>
 | 
			
		||||
      <Unique>1</Unique>
 | 
			
		||||
    </index>
 | 
			
		||||
    <key id="59" parent="9">
 | 
			
		||||
    <key id="64" parent="9">
 | 
			
		||||
      <ColNames>eventId</ColNames>
 | 
			
		||||
      <Primary>1</Primary>
 | 
			
		||||
      <UnderlyingIndexName>sqlite_autoindex_event_log_1</UnderlyingIndexName>
 | 
			
		||||
    </key>
 | 
			
		||||
    <column id="60" parent="10" name="linkId">
 | 
			
		||||
    <column id="65" parent="10" name="linkId">
 | 
			
		||||
      <Position>1</Position>
 | 
			
		||||
      <DataType>TEXT|0s</DataType>
 | 
			
		||||
      <NotNull>1</NotNull>
 | 
			
		||||
    </column>
 | 
			
		||||
    <column id="61" parent="10" name="noteId">
 | 
			
		||||
    <column id="66" parent="10" name="noteId">
 | 
			
		||||
      <Position>2</Position>
 | 
			
		||||
      <DataType>TEXT|0s</DataType>
 | 
			
		||||
      <NotNull>1</NotNull>
 | 
			
		||||
    </column>
 | 
			
		||||
    <column id="62" parent="10" name="targetNoteId">
 | 
			
		||||
    <column id="67" parent="10" name="targetNoteId">
 | 
			
		||||
      <Position>3</Position>
 | 
			
		||||
      <DataType>TEXT|0s</DataType>
 | 
			
		||||
      <NotNull>1</NotNull>
 | 
			
		||||
    </column>
 | 
			
		||||
    <column id="63" parent="10" name="type">
 | 
			
		||||
    <column id="68" parent="10" name="type">
 | 
			
		||||
      <Position>4</Position>
 | 
			
		||||
      <DataType>TEXT|0s</DataType>
 | 
			
		||||
      <NotNull>1</NotNull>
 | 
			
		||||
    </column>
 | 
			
		||||
    <column id="64" parent="10" name="isDeleted">
 | 
			
		||||
    <column id="69" parent="10" name="hash">
 | 
			
		||||
      <Position>5</Position>
 | 
			
		||||
      <DataType>INTEGER|0s</DataType>
 | 
			
		||||
      <NotNull>1</NotNull>
 | 
			
		||||
      <DefaultExpression>0</DefaultExpression>
 | 
			
		||||
    </column>
 | 
			
		||||
    <column id="65" parent="10" name="dateCreated">
 | 
			
		||||
      <Position>6</Position>
 | 
			
		||||
      <DataType>TEXT|0s</DataType>
 | 
			
		||||
      <NotNull>1</NotNull>
 | 
			
		||||
    </column>
 | 
			
		||||
    <column id="66" parent="10" name="dateModified">
 | 
			
		||||
      <Position>7</Position>
 | 
			
		||||
      <DataType>TEXT|0s</DataType>
 | 
			
		||||
      <NotNull>1</NotNull>
 | 
			
		||||
    </column>
 | 
			
		||||
    <column id="67" parent="10" name="hash">
 | 
			
		||||
      <Position>8</Position>
 | 
			
		||||
      <DataType>TEXT|0s</DataType>
 | 
			
		||||
      <NotNull>1</NotNull>
 | 
			
		||||
      <DefaultExpression>""</DefaultExpression>
 | 
			
		||||
    </column>
 | 
			
		||||
    <index id="68" parent="10" name="sqlite_autoindex_links_1">
 | 
			
		||||
    <column id="70" parent="10" name="isDeleted">
 | 
			
		||||
      <Position>6</Position>
 | 
			
		||||
      <DataType>INTEGER|0s</DataType>
 | 
			
		||||
      <NotNull>1</NotNull>
 | 
			
		||||
      <DefaultExpression>0</DefaultExpression>
 | 
			
		||||
    </column>
 | 
			
		||||
    <column id="71" parent="10" name="dateCreated">
 | 
			
		||||
      <Position>7</Position>
 | 
			
		||||
      <DataType>TEXT|0s</DataType>
 | 
			
		||||
      <NotNull>1</NotNull>
 | 
			
		||||
    </column>
 | 
			
		||||
    <column id="72" parent="10" name="dateModified">
 | 
			
		||||
      <Position>8</Position>
 | 
			
		||||
      <DataType>TEXT|0s</DataType>
 | 
			
		||||
      <NotNull>1</NotNull>
 | 
			
		||||
    </column>
 | 
			
		||||
    <index id="73" parent="10" name="sqlite_autoindex_links_1">
 | 
			
		||||
      <NameSurrogate>1</NameSurrogate>
 | 
			
		||||
      <ColNames>linkId</ColNames>
 | 
			
		||||
      <ColumnCollations></ColumnCollations>
 | 
			
		||||
      <Unique>1</Unique>
 | 
			
		||||
    </index>
 | 
			
		||||
    <key id="69" parent="10">
 | 
			
		||||
    <index id="74" parent="10" name="IDX_links_noteId_index">
 | 
			
		||||
      <ColNames>noteId</ColNames>
 | 
			
		||||
      <ColumnCollations></ColumnCollations>
 | 
			
		||||
    </index>
 | 
			
		||||
    <index id="75" parent="10" name="IDX_links_targetNoteId_index">
 | 
			
		||||
      <ColNames>targetNoteId</ColNames>
 | 
			
		||||
      <ColumnCollations></ColumnCollations>
 | 
			
		||||
    </index>
 | 
			
		||||
    <key id="76" parent="10">
 | 
			
		||||
      <ColNames>linkId</ColNames>
 | 
			
		||||
      <Primary>1</Primary>
 | 
			
		||||
      <UnderlyingIndexName>sqlite_autoindex_links_1</UnderlyingIndexName>
 | 
			
		||||
    </key>
 | 
			
		||||
    <column id="70" parent="11" name="noteRevisionId">
 | 
			
		||||
    <column id="77" parent="11" name="noteContentId">
 | 
			
		||||
      <Position>1</Position>
 | 
			
		||||
      <DataType>TEXT|0s</DataType>
 | 
			
		||||
      <NotNull>1</NotNull>
 | 
			
		||||
    </column>
 | 
			
		||||
    <column id="71" parent="11" name="noteId">
 | 
			
		||||
    <column id="78" parent="11" name="noteId">
 | 
			
		||||
      <Position>2</Position>
 | 
			
		||||
      <DataType>TEXT|0s</DataType>
 | 
			
		||||
      <NotNull>1</NotNull>
 | 
			
		||||
    </column>
 | 
			
		||||
    <column id="72" parent="11" name="title">
 | 
			
		||||
    <column id="79" parent="11" name="isProtected">
 | 
			
		||||
      <Position>3</Position>
 | 
			
		||||
      <DataType>INT|0s</DataType>
 | 
			
		||||
      <NotNull>1</NotNull>
 | 
			
		||||
      <DefaultExpression>0</DefaultExpression>
 | 
			
		||||
    </column>
 | 
			
		||||
    <column id="80" parent="11" name="content">
 | 
			
		||||
      <Position>4</Position>
 | 
			
		||||
      <DataType>TEXT|0s</DataType>
 | 
			
		||||
      <DefaultExpression>NULL</DefaultExpression>
 | 
			
		||||
    </column>
 | 
			
		||||
    <column id="81" parent="11" name="hash">
 | 
			
		||||
      <Position>5</Position>
 | 
			
		||||
      <DataType>TEXT|0s</DataType>
 | 
			
		||||
      <NotNull>1</NotNull>
 | 
			
		||||
      <DefaultExpression>""</DefaultExpression>
 | 
			
		||||
    </column>
 | 
			
		||||
    <column id="82" parent="11" name="dateCreated">
 | 
			
		||||
      <Position>6</Position>
 | 
			
		||||
      <DataType>TEXT|0s</DataType>
 | 
			
		||||
      <NotNull>1</NotNull>
 | 
			
		||||
      <DefaultExpression>'2018-05-08T23:41:15.225Z'</DefaultExpression>
 | 
			
		||||
    </column>
 | 
			
		||||
    <column id="83" parent="11" name="dateModified">
 | 
			
		||||
      <Position>7</Position>
 | 
			
		||||
      <DataType>TEXT|0s</DataType>
 | 
			
		||||
      <NotNull>1</NotNull>
 | 
			
		||||
      <DefaultExpression>'2018-05-08T23:41:15.225Z'</DefaultExpression>
 | 
			
		||||
    </column>
 | 
			
		||||
    <index id="84" parent="11" name="sqlite_autoindex_note_contents_1">
 | 
			
		||||
      <NameSurrogate>1</NameSurrogate>
 | 
			
		||||
      <ColNames>noteContentId</ColNames>
 | 
			
		||||
      <ColumnCollations></ColumnCollations>
 | 
			
		||||
      <Unique>1</Unique>
 | 
			
		||||
    </index>
 | 
			
		||||
    <index id="85" parent="11" name="IDX_note_contents_noteId">
 | 
			
		||||
      <ColNames>noteId</ColNames>
 | 
			
		||||
      <ColumnCollations></ColumnCollations>
 | 
			
		||||
      <Unique>1</Unique>
 | 
			
		||||
    </index>
 | 
			
		||||
    <key id="86" parent="11">
 | 
			
		||||
      <ColNames>noteContentId</ColNames>
 | 
			
		||||
      <Primary>1</Primary>
 | 
			
		||||
      <UnderlyingIndexName>sqlite_autoindex_note_contents_1</UnderlyingIndexName>
 | 
			
		||||
    </key>
 | 
			
		||||
    <column id="87" parent="12" name="noteRevisionId">
 | 
			
		||||
      <Position>1</Position>
 | 
			
		||||
      <DataType>TEXT|0s</DataType>
 | 
			
		||||
      <NotNull>1</NotNull>
 | 
			
		||||
    </column>
 | 
			
		||||
    <column id="88" parent="12" name="noteId">
 | 
			
		||||
      <Position>2</Position>
 | 
			
		||||
      <DataType>TEXT|0s</DataType>
 | 
			
		||||
      <NotNull>1</NotNull>
 | 
			
		||||
    </column>
 | 
			
		||||
    <column id="89" parent="12" name="title">
 | 
			
		||||
      <Position>3</Position>
 | 
			
		||||
      <DataType>TEXT|0s</DataType>
 | 
			
		||||
    </column>
 | 
			
		||||
    <column id="73" parent="11" name="content">
 | 
			
		||||
    <column id="90" parent="12" name="content">
 | 
			
		||||
      <Position>4</Position>
 | 
			
		||||
      <DataType>TEXT|0s</DataType>
 | 
			
		||||
    </column>
 | 
			
		||||
    <column id="74" parent="11" name="isProtected">
 | 
			
		||||
    <column id="91" parent="12" name="isProtected">
 | 
			
		||||
      <Position>5</Position>
 | 
			
		||||
      <DataType>INT|0s</DataType>
 | 
			
		||||
      <NotNull>1</NotNull>
 | 
			
		||||
      <DefaultExpression>0</DefaultExpression>
 | 
			
		||||
    </column>
 | 
			
		||||
    <column id="75" parent="11" name="dateModifiedFrom">
 | 
			
		||||
    <column id="92" parent="12" name="dateModifiedFrom">
 | 
			
		||||
      <Position>6</Position>
 | 
			
		||||
      <DataType>TEXT|0s</DataType>
 | 
			
		||||
      <NotNull>1</NotNull>
 | 
			
		||||
    </column>
 | 
			
		||||
    <column id="76" parent="11" name="dateModifiedTo">
 | 
			
		||||
    <column id="93" parent="12" name="dateModifiedTo">
 | 
			
		||||
      <Position>7</Position>
 | 
			
		||||
      <DataType>TEXT|0s</DataType>
 | 
			
		||||
      <NotNull>1</NotNull>
 | 
			
		||||
    </column>
 | 
			
		||||
    <column id="77" parent="11" name="type">
 | 
			
		||||
    <column id="94" parent="12" name="type">
 | 
			
		||||
      <Position>8</Position>
 | 
			
		||||
      <DataType>TEXT|0s</DataType>
 | 
			
		||||
      <NotNull>1</NotNull>
 | 
			
		||||
      <DefaultExpression>''</DefaultExpression>
 | 
			
		||||
    </column>
 | 
			
		||||
    <column id="78" parent="11" name="mime">
 | 
			
		||||
    <column id="95" parent="12" name="mime">
 | 
			
		||||
      <Position>9</Position>
 | 
			
		||||
      <DataType>TEXT|0s</DataType>
 | 
			
		||||
      <NotNull>1</NotNull>
 | 
			
		||||
      <DefaultExpression>''</DefaultExpression>
 | 
			
		||||
    </column>
 | 
			
		||||
    <column id="79" parent="11" name="hash">
 | 
			
		||||
    <column id="96" parent="12" name="hash">
 | 
			
		||||
      <Position>10</Position>
 | 
			
		||||
      <DataType>TEXT|0s</DataType>
 | 
			
		||||
      <NotNull>1</NotNull>
 | 
			
		||||
      <DefaultExpression>""</DefaultExpression>
 | 
			
		||||
    </column>
 | 
			
		||||
    <index id="80" parent="11" name="sqlite_autoindex_note_revisions_1">
 | 
			
		||||
    <index id="97" parent="12" name="sqlite_autoindex_note_revisions_1">
 | 
			
		||||
      <NameSurrogate>1</NameSurrogate>
 | 
			
		||||
      <ColNames>noteRevisionId</ColNames>
 | 
			
		||||
      <ColumnCollations></ColumnCollations>
 | 
			
		||||
      <Unique>1</Unique>
 | 
			
		||||
    </index>
 | 
			
		||||
    <index id="81" parent="11" name="IDX_note_revisions_noteId">
 | 
			
		||||
    <index id="98" parent="12" name="IDX_note_revisions_noteId">
 | 
			
		||||
      <ColNames>noteId</ColNames>
 | 
			
		||||
      <ColumnCollations></ColumnCollations>
 | 
			
		||||
    </index>
 | 
			
		||||
    <index id="82" parent="11" name="IDX_note_revisions_dateModifiedFrom">
 | 
			
		||||
    <index id="99" parent="12" name="IDX_note_revisions_dateModifiedFrom">
 | 
			
		||||
      <ColNames>dateModifiedFrom</ColNames>
 | 
			
		||||
      <ColumnCollations></ColumnCollations>
 | 
			
		||||
    </index>
 | 
			
		||||
    <index id="83" parent="11" name="IDX_note_revisions_dateModifiedTo">
 | 
			
		||||
    <index id="100" parent="12" name="IDX_note_revisions_dateModifiedTo">
 | 
			
		||||
      <ColNames>dateModifiedTo</ColNames>
 | 
			
		||||
      <ColumnCollations></ColumnCollations>
 | 
			
		||||
    </index>
 | 
			
		||||
    <key id="84" parent="11">
 | 
			
		||||
    <key id="101" parent="12">
 | 
			
		||||
      <ColNames>noteRevisionId</ColNames>
 | 
			
		||||
      <Primary>1</Primary>
 | 
			
		||||
      <UnderlyingIndexName>sqlite_autoindex_note_revisions_1</UnderlyingIndexName>
 | 
			
		||||
    </key>
 | 
			
		||||
    <column id="85" parent="12" name="noteId">
 | 
			
		||||
    <column id="102" parent="13" name="noteId">
 | 
			
		||||
      <Position>1</Position>
 | 
			
		||||
      <DataType>TEXT|0s</DataType>
 | 
			
		||||
      <NotNull>1</NotNull>
 | 
			
		||||
    </column>
 | 
			
		||||
    <column id="86" parent="12" name="title">
 | 
			
		||||
    <column id="103" parent="13" name="title">
 | 
			
		||||
      <Position>2</Position>
 | 
			
		||||
      <DataType>TEXT|0s</DataType>
 | 
			
		||||
      <NotNull>1</NotNull>
 | 
			
		||||
      <DefaultExpression>"note"</DefaultExpression>
 | 
			
		||||
    </column>
 | 
			
		||||
    <column id="87" parent="12" name="content">
 | 
			
		||||
    <column id="104" parent="13" name="isProtected">
 | 
			
		||||
      <Position>3</Position>
 | 
			
		||||
      <DataType>TEXT|0s</DataType>
 | 
			
		||||
      <DefaultExpression>NULL</DefaultExpression>
 | 
			
		||||
    </column>
 | 
			
		||||
    <column id="88" parent="12" name="isProtected">
 | 
			
		||||
      <Position>4</Position>
 | 
			
		||||
      <DataType>INT|0s</DataType>
 | 
			
		||||
      <NotNull>1</NotNull>
 | 
			
		||||
      <DefaultExpression>0</DefaultExpression>
 | 
			
		||||
    </column>
 | 
			
		||||
    <column id="89" parent="12" name="type">
 | 
			
		||||
      <Position>5</Position>
 | 
			
		||||
    <column id="105" parent="13" name="type">
 | 
			
		||||
      <Position>4</Position>
 | 
			
		||||
      <DataType>TEXT|0s</DataType>
 | 
			
		||||
      <NotNull>1</NotNull>
 | 
			
		||||
      <DefaultExpression>'text'</DefaultExpression>
 | 
			
		||||
    </column>
 | 
			
		||||
    <column id="90" parent="12" name="mime">
 | 
			
		||||
      <Position>6</Position>
 | 
			
		||||
    <column id="106" parent="13" name="mime">
 | 
			
		||||
      <Position>5</Position>
 | 
			
		||||
      <DataType>TEXT|0s</DataType>
 | 
			
		||||
      <NotNull>1</NotNull>
 | 
			
		||||
      <DefaultExpression>'text/html'</DefaultExpression>
 | 
			
		||||
    </column>
 | 
			
		||||
    <column id="91" parent="12" name="hash">
 | 
			
		||||
      <Position>7</Position>
 | 
			
		||||
    <column id="107" parent="13" name="hash">
 | 
			
		||||
      <Position>6</Position>
 | 
			
		||||
      <DataType>TEXT|0s</DataType>
 | 
			
		||||
      <NotNull>1</NotNull>
 | 
			
		||||
      <DefaultExpression>""</DefaultExpression>
 | 
			
		||||
    </column>
 | 
			
		||||
    <column id="92" parent="12" name="isDeleted">
 | 
			
		||||
      <Position>8</Position>
 | 
			
		||||
    <column id="108" parent="13" name="isDeleted">
 | 
			
		||||
      <Position>7</Position>
 | 
			
		||||
      <DataType>INT|0s</DataType>
 | 
			
		||||
      <NotNull>1</NotNull>
 | 
			
		||||
      <DefaultExpression>0</DefaultExpression>
 | 
			
		||||
    </column>
 | 
			
		||||
    <column id="93" parent="12" name="dateCreated">
 | 
			
		||||
    <column id="109" parent="13" name="dateCreated">
 | 
			
		||||
      <Position>8</Position>
 | 
			
		||||
      <DataType>TEXT|0s</DataType>
 | 
			
		||||
      <NotNull>1</NotNull>
 | 
			
		||||
    </column>
 | 
			
		||||
    <column id="110" parent="13" name="dateModified">
 | 
			
		||||
      <Position>9</Position>
 | 
			
		||||
      <DataType>TEXT|0s</DataType>
 | 
			
		||||
      <NotNull>1</NotNull>
 | 
			
		||||
    </column>
 | 
			
		||||
    <column id="94" parent="12" name="dateModified">
 | 
			
		||||
      <Position>10</Position>
 | 
			
		||||
      <DataType>TEXT|0s</DataType>
 | 
			
		||||
      <NotNull>1</NotNull>
 | 
			
		||||
    </column>
 | 
			
		||||
    <index id="95" parent="12" name="sqlite_autoindex_notes_1">
 | 
			
		||||
    <index id="111" parent="13" name="sqlite_autoindex_notes_1">
 | 
			
		||||
      <NameSurrogate>1</NameSurrogate>
 | 
			
		||||
      <ColNames>noteId</ColNames>
 | 
			
		||||
      <ColumnCollations></ColumnCollations>
 | 
			
		||||
      <Unique>1</Unique>
 | 
			
		||||
    </index>
 | 
			
		||||
    <key id="96" parent="12">
 | 
			
		||||
    <key id="112" parent="13">
 | 
			
		||||
      <ColNames>noteId</ColNames>
 | 
			
		||||
      <Primary>1</Primary>
 | 
			
		||||
      <UnderlyingIndexName>sqlite_autoindex_notes_1</UnderlyingIndexName>
 | 
			
		||||
    </key>
 | 
			
		||||
    <column id="97" parent="13" name="name">
 | 
			
		||||
    <column id="113" parent="14" name="name">
 | 
			
		||||
      <Position>1</Position>
 | 
			
		||||
      <DataType>TEXT|0s</DataType>
 | 
			
		||||
      <NotNull>1</NotNull>
 | 
			
		||||
    </column>
 | 
			
		||||
    <column id="98" parent="13" name="value">
 | 
			
		||||
    <column id="114" parent="14" name="value">
 | 
			
		||||
      <Position>2</Position>
 | 
			
		||||
      <DataType>TEXT|0s</DataType>
 | 
			
		||||
    </column>
 | 
			
		||||
    <column id="99" parent="13" name="dateModified">
 | 
			
		||||
    <column id="115" parent="14" name="dateModified">
 | 
			
		||||
      <Position>3</Position>
 | 
			
		||||
      <DataType>INT|0s</DataType>
 | 
			
		||||
    </column>
 | 
			
		||||
    <column id="100" parent="13" name="isSynced">
 | 
			
		||||
    <column id="116" parent="14" name="isSynced">
 | 
			
		||||
      <Position>4</Position>
 | 
			
		||||
      <DataType>INTEGER|0s</DataType>
 | 
			
		||||
      <NotNull>1</NotNull>
 | 
			
		||||
      <DefaultExpression>0</DefaultExpression>
 | 
			
		||||
    </column>
 | 
			
		||||
    <column id="101" parent="13" name="hash">
 | 
			
		||||
    <column id="117" parent="14" name="hash">
 | 
			
		||||
      <Position>5</Position>
 | 
			
		||||
      <DataType>TEXT|0s</DataType>
 | 
			
		||||
      <NotNull>1</NotNull>
 | 
			
		||||
      <DefaultExpression>""</DefaultExpression>
 | 
			
		||||
    </column>
 | 
			
		||||
    <column id="102" parent="13" name="dateCreated">
 | 
			
		||||
    <column id="118" parent="14" name="dateCreated">
 | 
			
		||||
      <Position>6</Position>
 | 
			
		||||
      <DataType>TEXT|0s</DataType>
 | 
			
		||||
      <NotNull>1</NotNull>
 | 
			
		||||
      <DefaultExpression>'1970-01-01T00:00:00.000Z'</DefaultExpression>
 | 
			
		||||
    </column>
 | 
			
		||||
    <index id="103" parent="13" name="sqlite_autoindex_options_1">
 | 
			
		||||
    <index id="119" parent="14" name="sqlite_autoindex_options_1">
 | 
			
		||||
      <NameSurrogate>1</NameSurrogate>
 | 
			
		||||
      <ColNames>name</ColNames>
 | 
			
		||||
      <ColumnCollations></ColumnCollations>
 | 
			
		||||
      <Unique>1</Unique>
 | 
			
		||||
    </index>
 | 
			
		||||
    <key id="104" parent="13">
 | 
			
		||||
    <key id="120" parent="14">
 | 
			
		||||
      <ColNames>name</ColNames>
 | 
			
		||||
      <Primary>1</Primary>
 | 
			
		||||
      <UnderlyingIndexName>sqlite_autoindex_options_1</UnderlyingIndexName>
 | 
			
		||||
    </key>
 | 
			
		||||
    <column id="105" parent="14" name="branchId">
 | 
			
		||||
    <column id="121" parent="15" name="branchId">
 | 
			
		||||
      <Position>1</Position>
 | 
			
		||||
      <DataType>TEXT|0s</DataType>
 | 
			
		||||
      <NotNull>1</NotNull>
 | 
			
		||||
    </column>
 | 
			
		||||
    <column id="106" parent="14" name="notePath">
 | 
			
		||||
    <column id="122" parent="15" name="notePath">
 | 
			
		||||
      <Position>2</Position>
 | 
			
		||||
      <DataType>TEXT|0s</DataType>
 | 
			
		||||
      <NotNull>1</NotNull>
 | 
			
		||||
    </column>
 | 
			
		||||
    <column id="107" parent="14" name="hash">
 | 
			
		||||
    <column id="123" parent="15" name="hash">
 | 
			
		||||
      <Position>3</Position>
 | 
			
		||||
      <DataType>TEXT|0s</DataType>
 | 
			
		||||
      <NotNull>1</NotNull>
 | 
			
		||||
      <DefaultExpression>""</DefaultExpression>
 | 
			
		||||
    </column>
 | 
			
		||||
    <column id="108" parent="14" name="dateCreated">
 | 
			
		||||
    <column id="124" parent="15" name="dateCreated">
 | 
			
		||||
      <Position>4</Position>
 | 
			
		||||
      <DataType>TEXT|0s</DataType>
 | 
			
		||||
      <NotNull>1</NotNull>
 | 
			
		||||
    </column>
 | 
			
		||||
    <column id="109" parent="14" name="isDeleted">
 | 
			
		||||
    <column id="125" parent="15" name="isDeleted">
 | 
			
		||||
      <Position>5</Position>
 | 
			
		||||
      <DataType>INT|0s</DataType>
 | 
			
		||||
    </column>
 | 
			
		||||
    <index id="110" parent="14" name="sqlite_autoindex_recent_notes_1">
 | 
			
		||||
    <index id="126" parent="15" name="sqlite_autoindex_recent_notes_1">
 | 
			
		||||
      <NameSurrogate>1</NameSurrogate>
 | 
			
		||||
      <ColNames>branchId</ColNames>
 | 
			
		||||
      <ColumnCollations></ColumnCollations>
 | 
			
		||||
      <Unique>1</Unique>
 | 
			
		||||
    </index>
 | 
			
		||||
    <key id="111" parent="14">
 | 
			
		||||
    <key id="127" parent="15">
 | 
			
		||||
      <ColNames>branchId</ColNames>
 | 
			
		||||
      <Primary>1</Primary>
 | 
			
		||||
      <UnderlyingIndexName>sqlite_autoindex_recent_notes_1</UnderlyingIndexName>
 | 
			
		||||
    </key>
 | 
			
		||||
    <column id="112" parent="15" name="sourceId">
 | 
			
		||||
    <column id="128" parent="16" name="sourceId">
 | 
			
		||||
      <Position>1</Position>
 | 
			
		||||
      <DataType>TEXT|0s</DataType>
 | 
			
		||||
      <NotNull>1</NotNull>
 | 
			
		||||
    </column>
 | 
			
		||||
    <column id="113" parent="15" name="dateCreated">
 | 
			
		||||
    <column id="129" parent="16" name="dateCreated">
 | 
			
		||||
      <Position>2</Position>
 | 
			
		||||
      <DataType>TEXT|0s</DataType>
 | 
			
		||||
      <NotNull>1</NotNull>
 | 
			
		||||
    </column>
 | 
			
		||||
    <index id="114" parent="15" name="sqlite_autoindex_source_ids_1">
 | 
			
		||||
    <index id="130" parent="16" name="sqlite_autoindex_source_ids_1">
 | 
			
		||||
      <NameSurrogate>1</NameSurrogate>
 | 
			
		||||
      <ColNames>sourceId</ColNames>
 | 
			
		||||
      <ColumnCollations></ColumnCollations>
 | 
			
		||||
      <Unique>1</Unique>
 | 
			
		||||
    </index>
 | 
			
		||||
    <key id="115" parent="15">
 | 
			
		||||
    <key id="131" parent="16">
 | 
			
		||||
      <ColNames>sourceId</ColNames>
 | 
			
		||||
      <Primary>1</Primary>
 | 
			
		||||
      <UnderlyingIndexName>sqlite_autoindex_source_ids_1</UnderlyingIndexName>
 | 
			
		||||
    </key>
 | 
			
		||||
    <column id="116" parent="16" name="type">
 | 
			
		||||
    <column id="132" parent="17" name="type">
 | 
			
		||||
      <Position>1</Position>
 | 
			
		||||
      <DataType>text|0s</DataType>
 | 
			
		||||
    </column>
 | 
			
		||||
    <column id="117" parent="16" name="name">
 | 
			
		||||
    <column id="133" parent="17" name="name">
 | 
			
		||||
      <Position>2</Position>
 | 
			
		||||
      <DataType>text|0s</DataType>
 | 
			
		||||
    </column>
 | 
			
		||||
    <column id="118" parent="16" name="tbl_name">
 | 
			
		||||
    <column id="134" parent="17" name="tbl_name">
 | 
			
		||||
      <Position>3</Position>
 | 
			
		||||
      <DataType>text|0s</DataType>
 | 
			
		||||
    </column>
 | 
			
		||||
    <column id="119" parent="16" name="rootpage">
 | 
			
		||||
    <column id="135" parent="17" name="rootpage">
 | 
			
		||||
      <Position>4</Position>
 | 
			
		||||
      <DataType>integer|0s</DataType>
 | 
			
		||||
    </column>
 | 
			
		||||
    <column id="120" parent="16" name="sql">
 | 
			
		||||
    <column id="136" parent="17" name="sql">
 | 
			
		||||
      <Position>5</Position>
 | 
			
		||||
      <DataType>text|0s</DataType>
 | 
			
		||||
    </column>
 | 
			
		||||
    <column id="121" parent="17" name="name">
 | 
			
		||||
    <column id="137" parent="18" name="name">
 | 
			
		||||
      <Position>1</Position>
 | 
			
		||||
    </column>
 | 
			
		||||
    <column id="122" parent="17" name="seq">
 | 
			
		||||
    <column id="138" parent="18" name="seq">
 | 
			
		||||
      <Position>2</Position>
 | 
			
		||||
    </column>
 | 
			
		||||
    <column id="123" parent="18" name="id">
 | 
			
		||||
    <column id="139" parent="19" name="id">
 | 
			
		||||
      <Position>1</Position>
 | 
			
		||||
      <DataType>INTEGER|0s</DataType>
 | 
			
		||||
      <NotNull>1</NotNull>
 | 
			
		||||
      <SequenceIdentity>1</SequenceIdentity>
 | 
			
		||||
    </column>
 | 
			
		||||
    <column id="124" parent="18" name="entityName">
 | 
			
		||||
    <column id="140" parent="19" name="entityName">
 | 
			
		||||
      <Position>2</Position>
 | 
			
		||||
      <DataType>TEXT|0s</DataType>
 | 
			
		||||
      <NotNull>1</NotNull>
 | 
			
		||||
    </column>
 | 
			
		||||
    <column id="125" parent="18" name="entityId">
 | 
			
		||||
    <column id="141" parent="19" name="entityId">
 | 
			
		||||
      <Position>3</Position>
 | 
			
		||||
      <DataType>TEXT|0s</DataType>
 | 
			
		||||
      <NotNull>1</NotNull>
 | 
			
		||||
    </column>
 | 
			
		||||
    <column id="126" parent="18" name="sourceId">
 | 
			
		||||
    <column id="142" parent="19" name="sourceId">
 | 
			
		||||
      <Position>4</Position>
 | 
			
		||||
      <DataType>TEXT|0s</DataType>
 | 
			
		||||
      <NotNull>1</NotNull>
 | 
			
		||||
    </column>
 | 
			
		||||
    <column id="127" parent="18" name="syncDate">
 | 
			
		||||
    <column id="143" parent="19" name="syncDate">
 | 
			
		||||
      <Position>5</Position>
 | 
			
		||||
      <DataType>TEXT|0s</DataType>
 | 
			
		||||
      <NotNull>1</NotNull>
 | 
			
		||||
    </column>
 | 
			
		||||
    <index id="128" parent="18" name="IDX_sync_entityName_entityId">
 | 
			
		||||
    <index id="144" parent="19" name="IDX_sync_entityName_entityId">
 | 
			
		||||
      <ColNames>entityName
 | 
			
		||||
entityId</ColNames>
 | 
			
		||||
      <ColumnCollations></ColumnCollations>
 | 
			
		||||
      <Unique>1</Unique>
 | 
			
		||||
    </index>
 | 
			
		||||
    <index id="129" parent="18" name="IDX_sync_syncDate">
 | 
			
		||||
    <index id="145" parent="19" name="IDX_sync_syncDate">
 | 
			
		||||
      <ColNames>syncDate</ColNames>
 | 
			
		||||
      <ColumnCollations></ColumnCollations>
 | 
			
		||||
    </index>
 | 
			
		||||
    <key id="130" parent="18">
 | 
			
		||||
    <key id="146" parent="19">
 | 
			
		||||
      <ColNames>id</ColNames>
 | 
			
		||||
      <Primary>1</Primary>
 | 
			
		||||
    </key>
 | 
			
		||||
 
 | 
			
		||||
@@ -17,6 +17,7 @@ RUN set -x \
 | 
			
		||||
        libtool \
 | 
			
		||||
        make \
 | 
			
		||||
        nasm \
 | 
			
		||||
        libpng-dev \
 | 
			
		||||
    && npm install --production \
 | 
			
		||||
    && apk del .build-dependencies
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										13
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										13
									
								
								README.md
									
									
									
									
									
								
							@@ -18,6 +18,7 @@ Trilium Notes is a hierarchical note taking application with focus on building l
 | 
			
		||||
* [Relation maps](https://github.com/zadam/trilium/wiki/Relation-map) for visualizing notes and their relations
 | 
			
		||||
* [Scripting](https://github.com/zadam/trilium/wiki/Scripts) - see [Advanced showcases](https://github.com/zadam/trilium/wiki/Advanced-showcases)
 | 
			
		||||
* Scales well in both usability and performance upwards of 100 000 notes
 | 
			
		||||
* Touch optimized [mobile frontend](https://github.com/zadam/trilium/wiki/Mobile-frontend) for smartphones and tablets
 | 
			
		||||
* [Night theme](https://github.com/zadam/trilium/wiki/Themes)
 | 
			
		||||
* [Evernote](https://github.com/zadam/trilium/wiki/Evernote-import) and [Markdown import & export](https://github.com/zadam/trilium/wiki/Markdown)
 | 
			
		||||
 | 
			
		||||
@@ -34,3 +35,15 @@ Trilium is provided as either desktop application (Linux, Windows, Mac) or web a
 | 
			
		||||
[See wiki for complete list of documentation pages.](https://github.com/zadam/trilium/wiki/)
 | 
			
		||||
 | 
			
		||||
You can also read [Patterns of personal knowledge base](https://github.com/zadam/trilium/wiki/Patterns-of-personal-knowledge-base) to get some inspiration on how you might use Trilium.
 | 
			
		||||
 | 
			
		||||
## Contribute
 | 
			
		||||
 | 
			
		||||
Use a browser based dev environment
 | 
			
		||||
 | 
			
		||||
[](https://gitpod.io/#https://github.com/zadam/trilium)
 | 
			
		||||
 | 
			
		||||
Or clone locally and run
 | 
			
		||||
```
 | 
			
		||||
npm install
 | 
			
		||||
npm run start
 | 
			
		||||
```
 | 
			
		||||
							
								
								
									
										7
									
								
								bin/build-debian.sh
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										7
									
								
								bin/build-debian.sh
									
									
									
									
									
										Executable file
									
								
							@@ -0,0 +1,7 @@
 | 
			
		||||
#!/usr/bin/env bash
 | 
			
		||||
 | 
			
		||||
echo "Packaging debian x64 distribution..."
 | 
			
		||||
 | 
			
		||||
VERSION=`jq -r ".version" package.json`
 | 
			
		||||
 | 
			
		||||
./node_modules/.bin/electron-installer-debian --config bin/deb-options.json --options.version=${VERSION} --arch amd64
 | 
			
		||||
@@ -3,22 +3,22 @@
 | 
			
		||||
BUILD_DIR=./dist/trilium-linux-x64
 | 
			
		||||
rm -rf $BUILD_DIR
 | 
			
		||||
 | 
			
		||||
# we build x64 as second so that we keep X64 binaries in node_modules for local development and server build
 | 
			
		||||
echo "Rebuilding binaries for linux-x64"
 | 
			
		||||
./node_modules/.bin/electron-rebuild --arch=x64
 | 
			
		||||
rm -r node_modules/sqlite3/lib/binding/*
 | 
			
		||||
 | 
			
		||||
./node_modules/.bin/electron-packager . --out=dist --executable-name=trilium --platform=linux --arch=x64 --overwrite
 | 
			
		||||
cp -r bin/deps/linux-x64/sqlite/* node_modules/sqlite3/lib/binding/
 | 
			
		||||
 | 
			
		||||
./node_modules/.bin/electron-packager . --asar --out=dist --executable-name=trilium --platform=linux --arch=x64 --overwrite
 | 
			
		||||
 | 
			
		||||
mv "./dist/Trilium Notes-linux-x64" $BUILD_DIR
 | 
			
		||||
 | 
			
		||||
rm -r "$BUILD_DIR/resources/app/node_modules/sqlite3/lib/binding/*"
 | 
			
		||||
cp src/public/images/app-icons/png/128x128.png $BUILD_DIR/icon.png
 | 
			
		||||
 | 
			
		||||
cp -r bin/deps/linux-x64/sqlite/electron* "$BUILD_DIR/resources/app/node_modules/sqlite3/lib/binding/"
 | 
			
		||||
 | 
			
		||||
rm -r $BUILD_DIR/resources/app/bin/deps
 | 
			
		||||
# removing software WebGL binaries because they are pretty huge and not necessary
 | 
			
		||||
rm -r $BUILD_DIR/swiftshader
 | 
			
		||||
 | 
			
		||||
echo "Packaging linux x64 electron distribution..."
 | 
			
		||||
VERSION=`jq -r ".version" package.json`
 | 
			
		||||
7z a $BUILD_DIR-${VERSION}.7z $BUILD_DIR
 | 
			
		||||
 | 
			
		||||
cd dist
 | 
			
		||||
 | 
			
		||||
tar cJf trilium-linux-x64-${VERSION}.tar.xz trilium-linux-x64
 | 
			
		||||
 
 | 
			
		||||
@@ -3,25 +3,30 @@
 | 
			
		||||
BUILD_DIR=./dist/trilium-mac-x64
 | 
			
		||||
rm -rf $BUILD_DIR
 | 
			
		||||
 | 
			
		||||
./node_modules/.bin/electron-packager . --out=dist --executable-name=trilium --platform=darwin  --arch=x64 --overwrite --icon=src/public/images/app-icons/mac/icon.icns
 | 
			
		||||
echo "Copying required mac binaries"
 | 
			
		||||
 | 
			
		||||
rm -r node_modules/sqlite3/lib/binding/*
 | 
			
		||||
rm -r node_modules/mozjpeg/vendor/*
 | 
			
		||||
rm -r node_modules/pngquant-bin/vendor/*
 | 
			
		||||
rm -r node_modules/giflossy/vendor/*
 | 
			
		||||
 | 
			
		||||
cp -r bin/deps/mac-x64/sqlite/* node_modules/sqlite3/lib/binding/
 | 
			
		||||
cp bin/deps/mac-x64/image/cjpeg node_modules/mozjpeg/vendor/
 | 
			
		||||
cp bin/deps/mac-x64/image/pngquant node_modules/pngquant-bin/vendor/
 | 
			
		||||
cp bin/deps/mac-x64/image/gifsicle node_modules/giflossy/vendor/
 | 
			
		||||
 | 
			
		||||
./node_modules/.bin/electron-packager . --asar --out=dist --executable-name=trilium --platform=darwin --arch=x64 --overwrite --icon=src/public/images/app-icons/mac/icon.icns
 | 
			
		||||
 | 
			
		||||
# Mac build has by default useless directory level
 | 
			
		||||
mv "./dist/Trilium Notes-darwin-x64" $BUILD_DIR
 | 
			
		||||
 | 
			
		||||
echo "Copying required mac binaries"
 | 
			
		||||
./bin/reset-local.sh
 | 
			
		||||
 | 
			
		||||
MAC_RES_DIR=$BUILD_DIR/Trilium\ Notes.app/Contents/Resources/app
 | 
			
		||||
 | 
			
		||||
rm -r "$MAC_RES_DIR/node_modules/sqlite3/lib/binding/*"
 | 
			
		||||
 | 
			
		||||
cp -r bin/deps/mac-x64/sqlite/* "$MAC_RES_DIR/node_modules/sqlite3/lib/binding/"
 | 
			
		||||
cp bin/deps/mac-x64/image/cjpeg "$MAC_RES_DIR/node_modules/mozjpeg/vendor/"
 | 
			
		||||
cp bin/deps/mac-x64/image/pngquant "$MAC_RES_DIR/node_modules/pngquant-bin/vendor/"
 | 
			
		||||
cp bin/deps/mac-x64/image/gifsicle "$MAC_RES_DIR/node_modules/giflossy/vendor/"
 | 
			
		||||
 | 
			
		||||
rm -r "$MAC_RES_DIR/bin/deps"
 | 
			
		||||
 | 
			
		||||
echo "Packaging mac x64 electron distribution..."
 | 
			
		||||
echo "Zipping mac x64 electron distribution..."
 | 
			
		||||
 | 
			
		||||
VERSION=`jq -r ".version" package.json`
 | 
			
		||||
7z a $BUILD_DIR-${VERSION}.7z $BUILD_DIR
 | 
			
		||||
 | 
			
		||||
cd dist
 | 
			
		||||
 | 
			
		||||
rm trilium-mac-x64-${VERSION}.zip
 | 
			
		||||
zip -r9 --symlinks trilium-mac-x64-${VERSION}.zip trilium-mac-x64
 | 
			
		||||
 
 | 
			
		||||
@@ -34,4 +34,5 @@ chmod 755 trilium.sh
 | 
			
		||||
cd ..
 | 
			
		||||
 | 
			
		||||
VERSION=`jq -r ".version" ../package.json`
 | 
			
		||||
7z a trilium-linux-x64-server-${VERSION}.7z trilium-linux-x64-server
 | 
			
		||||
 | 
			
		||||
tar cJf trilium-linux-x64-server-${VERSION}.tar.xz trilium-linux-x64-server
 | 
			
		||||
@@ -3,23 +3,30 @@
 | 
			
		||||
BUILD_DIR=./dist/trilium-windows-x64
 | 
			
		||||
rm -rf $BUILD_DIR
 | 
			
		||||
 | 
			
		||||
./node_modules/.bin/electron-packager . --out=dist --executable-name=trilium --platform=win32  --arch=x64 --overwrite --icon=src/public/images/app-icons/win/icon.ico
 | 
			
		||||
echo "Copying required windows binaries"
 | 
			
		||||
 | 
			
		||||
rm -r node_modules/sqlite3/lib/binding/*
 | 
			
		||||
rm -r node_modules/mozjpeg/vendor/*
 | 
			
		||||
rm -r node_modules/pngquant-bin/vendor/*
 | 
			
		||||
rm -r node_modules/giflossy/vendor/*
 | 
			
		||||
 | 
			
		||||
cp -r bin/deps/win-x64/sqlite/* node_modules/sqlite3/lib/binding/
 | 
			
		||||
cp bin/deps/win-x64/image/cjpeg.exe node_modules/mozjpeg/vendor/
 | 
			
		||||
cp bin/deps/win-x64/image/pngquant.exe node_modules/pngquant-bin/vendor/
 | 
			
		||||
cp bin/deps/win-x64/image/gifsicle.exe node_modules/giflossy/vendor/
 | 
			
		||||
 | 
			
		||||
./node_modules/.bin/electron-packager . --asar --out=dist --executable-name=trilium --platform=win32  --arch=x64 --overwrite --icon=src/public/images/app-icons/win/icon.ico
 | 
			
		||||
 | 
			
		||||
mv "./dist/Trilium Notes-win32-x64" $BUILD_DIR
 | 
			
		||||
 | 
			
		||||
echo "Copying required windows binaries"
 | 
			
		||||
 | 
			
		||||
WIN_RES_DIR=$BUILD_DIR/resources/app
 | 
			
		||||
 | 
			
		||||
cp -r bin/deps/win-x64/sqlite/* $WIN_RES_DIR/node_modules/sqlite3/lib/binding/
 | 
			
		||||
cp bin/deps/win-x64/image/cjpeg.exe $WIN_RES_DIR/node_modules/mozjpeg/vendor/
 | 
			
		||||
cp bin/deps/win-x64/image/pngquant.exe $WIN_RES_DIR/node_modules/pngquant-bin/vendor/
 | 
			
		||||
cp bin/deps/win-x64/image/gifsicle.exe $WIN_RES_DIR/node_modules/giflossy/vendor/
 | 
			
		||||
 | 
			
		||||
rm -r $WIN_RES_DIR/bin/deps
 | 
			
		||||
# removing software WebGL binaries because they are pretty huge and not necessary
 | 
			
		||||
rm -r $BUILD_DIR/swiftshader
 | 
			
		||||
 | 
			
		||||
echo "Packaging windows x64 electron distribution..."
 | 
			
		||||
./bin/reset-local.sh
 | 
			
		||||
 | 
			
		||||
echo "Zipping windows x64 electron distribution..."
 | 
			
		||||
VERSION=`jq -r ".version" package.json`
 | 
			
		||||
7z a $BUILD_DIR-${VERSION}.7z $BUILD_DIR
 | 
			
		||||
 | 
			
		||||
cd dist
 | 
			
		||||
 | 
			
		||||
zip -r9 trilium-windows-x64-${VERSION}.zip trilium-windows-x64
 | 
			
		||||
 
 | 
			
		||||
@@ -15,4 +15,7 @@ bin/build-mac-x64.sh
 | 
			
		||||
# building X64 linux as the last so electron-rebuild will prepare X64 binaries for local development
 | 
			
		||||
bin/build-linux-x64.sh
 | 
			
		||||
 | 
			
		||||
# this needs to be run after linux build
 | 
			
		||||
bin/build-debian.sh
 | 
			
		||||
 | 
			
		||||
bin/build-server.sh
 | 
			
		||||
							
								
								
									
										14
									
								
								bin/deb-options.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								bin/deb-options.json
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,14 @@
 | 
			
		||||
{
 | 
			
		||||
  "src": "dist/trilium-linux-x64",
 | 
			
		||||
  "dest": "dist/",
 | 
			
		||||
  "name": "trilium",
 | 
			
		||||
  "productName": "Trilium Notes",
 | 
			
		||||
  "genericName": "Note taker",
 | 
			
		||||
  "description": "Trilium Notes is a hierarchical note taking application with focus on building large personal knowledge bases.",
 | 
			
		||||
  "sections": "misc",
 | 
			
		||||
  "maintainer": "zadam.apps@gmail.com",
 | 
			
		||||
  "homepage": "https://github.com/zadam/trilium",
 | 
			
		||||
  "bin": "trilium",
 | 
			
		||||
  "icon": "dist/trilium-linux-x64/icon.png",
 | 
			
		||||
  "categories": [ "Office" ]
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										0
									
								
								bin/deps/mac-x64/image/cjpeg
									
									
									
									
									
										
										
										Normal file → Executable file
									
								
							
							
						
						
									
										0
									
								
								bin/deps/mac-x64/image/cjpeg
									
									
									
									
									
										
										
										Normal file → Executable file
									
								
							
							
								
								
									
										0
									
								
								bin/deps/win-x64/image/cjpeg.exe
									
									
									
									
									
										
										
										Normal file → Executable file
									
								
							
							
						
						
									
										0
									
								
								bin/deps/win-x64/image/cjpeg.exe
									
									
									
									
									
										
										
										Normal file → Executable file
									
								
							@@ -42,10 +42,11 @@ git push origin $TAG
 | 
			
		||||
 | 
			
		||||
bin/build.sh
 | 
			
		||||
 | 
			
		||||
LINUX_X64_BUILD=trilium-linux-x64-$VERSION.7z
 | 
			
		||||
WINDOWS_X64_BUILD=trilium-windows-x64-$VERSION.7z
 | 
			
		||||
MAC_X64_BUILD=trilium-mac-x64-$VERSION.7z
 | 
			
		||||
SERVER_BUILD=trilium-linux-x64-server-$VERSION.7z
 | 
			
		||||
LINUX_X64_BUILD=trilium-linux-x64-$VERSION.tar.xz
 | 
			
		||||
DEBIAN_X64_BUILD=trilium_${VERSION}_amd64.deb
 | 
			
		||||
WINDOWS_X64_BUILD=trilium-windows-x64-$VERSION.zip
 | 
			
		||||
MAC_X64_BUILD=trilium-mac-x64-$VERSION.zip
 | 
			
		||||
SERVER_BUILD=trilium-linux-x64-server-$VERSION.tar.xz
 | 
			
		||||
 | 
			
		||||
echo "Creating release in GitHub"
 | 
			
		||||
 | 
			
		||||
@@ -59,6 +60,13 @@ github-release release \
 | 
			
		||||
    --tag $TAG \
 | 
			
		||||
    --name "$TAG release" $EXTRA
 | 
			
		||||
 | 
			
		||||
echo "Uploading debian x64 package"
 | 
			
		||||
 | 
			
		||||
github-release upload \
 | 
			
		||||
    --tag $TAG \
 | 
			
		||||
    --name "$DEBIAN_X64_BUILD" \
 | 
			
		||||
    --file "dist/$DEBIAN_X64_BUILD"
 | 
			
		||||
 | 
			
		||||
echo "Uploading linux x64 build"
 | 
			
		||||
 | 
			
		||||
github-release upload \
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										3
									
								
								bin/reset-local.sh
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										3
									
								
								bin/reset-local.sh
									
									
									
									
									
										Executable file
									
								
							@@ -0,0 +1,3 @@
 | 
			
		||||
#!/usr/bin/env bash
 | 
			
		||||
 | 
			
		||||
./node_modules/.bin/electron-rebuild --arch=x64
 | 
			
		||||
							
								
								
									
										
											BIN
										
									
								
								db/demo.tar
									
									
									
									
									
								
							
							
						
						
									
										
											BIN
										
									
								
								db/demo.tar
									
									
									
									
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										62
									
								
								db/migrations/0122__add_iv_to_columns.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										62
									
								
								db/migrations/0122__add_iv_to_columns.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,62 @@
 | 
			
		||||
const sql = require('../../src/services/sql');
 | 
			
		||||
 | 
			
		||||
function prependIv(cipherText, ivText) {
 | 
			
		||||
    const arr = ivText.split("").map(c => parseInt(c) || 0);
 | 
			
		||||
    const iv = Buffer.from(arr);
 | 
			
		||||
    const payload = Buffer.from(cipherText, 'base64');
 | 
			
		||||
    const complete = Buffer.concat([iv, payload]);
 | 
			
		||||
 | 
			
		||||
    return complete.toString('base64');
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
async function updateEncryptedDataKey() {
 | 
			
		||||
    const encryptedDataKey = await sql.getValue("SELECT value FROM options WHERE name = 'encryptedDataKey'");
 | 
			
		||||
    const encryptedDataKeyIv = await sql.getValue("SELECT value FROM options WHERE name = 'encryptedDataKeyIv'");
 | 
			
		||||
 | 
			
		||||
    const newEncryptedDataKey = prependIv(encryptedDataKey, encryptedDataKeyIv);
 | 
			
		||||
 | 
			
		||||
    await sql.execute("UPDATE options SET value = ? WHERE name = 'encryptedDataKey'", [newEncryptedDataKey]);
 | 
			
		||||
 | 
			
		||||
    await sql.execute("DELETE FROM options WHERE name = 'encryptedDataKeyIv'");
 | 
			
		||||
    await sql.execute("DELETE FROM sync WHERE entityName = 'options' AND entityId = 'encryptedDataKeyIv'");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
async function updateNotes() {
 | 
			
		||||
    const protectedNotes = await sql.getRows("SELECT noteId, title, content FROM notes WHERE isProtected = 1");
 | 
			
		||||
 | 
			
		||||
    for (const note of protectedNotes) {
 | 
			
		||||
        if (note.title !== null) {
 | 
			
		||||
            note.title = prependIv(note.title, "0" + note.noteId);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (note.content !== null) {
 | 
			
		||||
            note.content = prependIv(note.content, "1" + note.noteId);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        await sql.execute("UPDATE notes SET title = ?, content = ? WHERE noteId = ?", [note.title, note.content, note.noteId]);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
async function updateNoteRevisions() {
 | 
			
		||||
    const protectedNoteRevisions = await sql.getRows("SELECT noteRevisionId, title, content FROM note_revisions WHERE isProtected = 1");
 | 
			
		||||
 | 
			
		||||
    for (const noteRevision of protectedNoteRevisions) {
 | 
			
		||||
        if (noteRevision.title !== null) {
 | 
			
		||||
            noteRevision.title = prependIv(noteRevision.title, "0" + noteRevision.noteRevisionId);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (noteRevision.content !== null) {
 | 
			
		||||
            noteRevision.content = prependIv(noteRevision.content, "1" + noteRevision.noteRevisionId);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        await sql.execute("UPDATE note_revisions SET title = ?, content = ? WHERE noteRevisionId = ?", [noteRevision.title, noteRevision.content, noteRevision.noteRevisionId]);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
module.exports = async () => {
 | 
			
		||||
    await updateEncryptedDataKey();
 | 
			
		||||
 | 
			
		||||
    await updateNotes();
 | 
			
		||||
 | 
			
		||||
    await updateNoteRevisions();
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										8
									
								
								db/migrations/0123__add_options_for_font_sizes.sql
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								db/migrations/0123__add_options_for_font_sizes.sql
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,8 @@
 | 
			
		||||
INSERT INTO options (name, value, dateCreated, dateModified, isSynced)
 | 
			
		||||
VALUES ('mainFontSize', '100', '2019-01-13T18:31:00.874Z', '2019-01-13T18:31:00.874Z', 0);
 | 
			
		||||
 | 
			
		||||
INSERT INTO options (name, value, dateCreated, dateModified, isSynced)
 | 
			
		||||
VALUES ('treeFontSize', '100', '2019-01-13T18:31:00.874Z', '2019-01-13T18:31:00.874Z', 0);
 | 
			
		||||
 | 
			
		||||
INSERT INTO options (name, value, dateCreated, dateModified, isSynced)
 | 
			
		||||
VALUES ('detailFontSize', '110', '2019-01-13T18:31:00.874Z', '2019-01-13T18:31:00.874Z', 0);
 | 
			
		||||
							
								
								
									
										11
									
								
								db/migrations/0124__readd_font_sizes.sql
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								db/migrations/0124__readd_font_sizes.sql
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,11 @@
 | 
			
		||||
INSERT INTO options (name, value, dateCreated, dateModified, isSynced)
 | 
			
		||||
  SELECT 'mainFontSize', '100', '2019-01-13T18:31:00.874Z', '2019-01-13T18:31:00.874Z', 0
 | 
			
		||||
    WHERE NOT EXISTS (SELECT 1 FROM options WHERE name = 'mainFontSize');
 | 
			
		||||
 | 
			
		||||
INSERT INTO options (name, value, dateCreated, dateModified, isSynced)
 | 
			
		||||
  SELECT 'treeFontSize', '100', '2019-01-13T18:31:00.874Z', '2019-01-13T18:31:00.874Z', 0
 | 
			
		||||
    WHERE NOT EXISTS (SELECT 1 FROM options WHERE name = 'treeFontSize');
 | 
			
		||||
 | 
			
		||||
INSERT INTO options (name, value, dateCreated, dateModified, isSynced)
 | 
			
		||||
  SELECT 'detailFontSize', '110', '2019-01-13T18:31:00.874Z', '2019-01-13T18:31:00.874Z', 0
 | 
			
		||||
    WHERE NOT EXISTS (SELECT 1 FROM options WHERE name = 'detailFontSize');
 | 
			
		||||
							
								
								
									
										35
									
								
								db/migrations/0125__create_note_content_table.sql
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								db/migrations/0125__create_note_content_table.sql
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,35 @@
 | 
			
		||||
CREATE TABLE IF NOT EXISTS "note_contents" (
 | 
			
		||||
  `noteContentId`	TEXT NOT NULL,
 | 
			
		||||
  `noteId`	TEXT NOT NULL,
 | 
			
		||||
  `isProtected`	INT NOT NULL DEFAULT 0,
 | 
			
		||||
  `content`	TEXT NULL DEFAULT NULL,
 | 
			
		||||
  `hash` TEXT DEFAULT "" NOT NULL,
 | 
			
		||||
  `dateCreated`	TEXT NOT NULL,
 | 
			
		||||
  `dateModified` TEXT NOT NULL,
 | 
			
		||||
  PRIMARY KEY(`noteContentId`)
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
CREATE UNIQUE INDEX `IDX_note_contents_noteId` ON `note_contents` (`noteId`);
 | 
			
		||||
 | 
			
		||||
INSERT INTO note_contents (noteContentId, noteId, isProtected, content, dateCreated, dateModified)
 | 
			
		||||
  SELECT 'C' || SUBSTR(noteId, 2), noteId, isProtected, content, dateCreated, dateModified FROM notes;
 | 
			
		||||
 | 
			
		||||
CREATE TABLE IF NOT EXISTS "notes_mig" (
 | 
			
		||||
  `noteId`	TEXT NOT NULL,
 | 
			
		||||
  `title`	TEXT NOT NULL DEFAULT "note",
 | 
			
		||||
  `isProtected`	INT NOT NULL DEFAULT 0,
 | 
			
		||||
  `type` TEXT NOT NULL DEFAULT 'text',
 | 
			
		||||
  `mime` TEXT NOT NULL DEFAULT 'text/html',
 | 
			
		||||
  `hash` TEXT DEFAULT "" NOT NULL,
 | 
			
		||||
  `isDeleted`	INT NOT NULL DEFAULT 0,
 | 
			
		||||
  `dateCreated`	TEXT NOT NULL,
 | 
			
		||||
  `dateModified`	TEXT NOT NULL,
 | 
			
		||||
  PRIMARY KEY(`noteId`)
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
INSERT INTO notes_mig (noteId, title, isProtected, isDeleted, dateCreated, dateModified, type, mime, hash)
 | 
			
		||||
SELECT noteId, title, isProtected, isDeleted, dateCreated, dateModified, type, mime, hash FROM notes;
 | 
			
		||||
 | 
			
		||||
DROP TABLE notes;
 | 
			
		||||
 | 
			
		||||
ALTER TABLE notes_mig RENAME TO notes;
 | 
			
		||||
							
								
								
									
										2
									
								
								db/migrations/0126__fill_sync_for_note_contents.sql
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								db/migrations/0126__fill_sync_for_note_contents.sql
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,2 @@
 | 
			
		||||
INSERT OR REPLACE INTO sync (entityName, entityId, sourceId, syncDate)
 | 
			
		||||
SELECT 'note_contents', noteContentId, '', '2019-03-02T18:07:29.182Z' FROM note_contents;
 | 
			
		||||
@@ -96,19 +96,6 @@ CREATE TABLE attributes
 | 
			
		||||
  hash         TEXT default "" not null, isInheritable int DEFAULT 0 NULL);
 | 
			
		||||
CREATE INDEX IDX_attributes_name_value
 | 
			
		||||
  on attributes (name, value);
 | 
			
		||||
CREATE TABLE IF NOT EXISTS "notes" (
 | 
			
		||||
  `noteId`	TEXT NOT NULL,
 | 
			
		||||
  `title`	TEXT NOT NULL DEFAULT "note",
 | 
			
		||||
  `content`	TEXT NULL DEFAULT NULL,
 | 
			
		||||
  `isProtected`	INT NOT NULL DEFAULT 0,
 | 
			
		||||
  `type` TEXT NOT NULL DEFAULT 'text',
 | 
			
		||||
  `mime` TEXT NOT NULL DEFAULT 'text/html',
 | 
			
		||||
  `hash` TEXT DEFAULT "" NOT NULL,
 | 
			
		||||
  `isDeleted`	INT NOT NULL DEFAULT 0,
 | 
			
		||||
  `dateCreated`	TEXT NOT NULL,
 | 
			
		||||
  `dateModified`	TEXT NOT NULL,
 | 
			
		||||
  PRIMARY KEY(`noteId`)
 | 
			
		||||
);
 | 
			
		||||
CREATE TABLE IF NOT EXISTS "links" (
 | 
			
		||||
  `linkId`	TEXT NOT NULL,
 | 
			
		||||
  `noteId`	TEXT NOT NULL,
 | 
			
		||||
@@ -130,3 +117,26 @@ CREATE INDEX IDX_attributes_noteId_index
 | 
			
		||||
  on attributes (noteId);
 | 
			
		||||
CREATE INDEX IDX_attributes_value_index
 | 
			
		||||
  on attributes (value);
 | 
			
		||||
CREATE TABLE IF NOT EXISTS "note_contents" (
 | 
			
		||||
  `noteContentId`	TEXT NOT NULL,
 | 
			
		||||
  `noteId`	TEXT NOT NULL,
 | 
			
		||||
  `isProtected`	INT NOT NULL DEFAULT 0,
 | 
			
		||||
  `content`	TEXT NULL DEFAULT NULL,
 | 
			
		||||
  `hash` TEXT DEFAULT "" NOT NULL,
 | 
			
		||||
  `dateCreated`	TEXT NOT NULL,
 | 
			
		||||
  `dateModified` TEXT NOT NULL,
 | 
			
		||||
  PRIMARY KEY(`noteContentId`)
 | 
			
		||||
);
 | 
			
		||||
CREATE UNIQUE INDEX `IDX_note_contents_noteId` ON `note_contents` (`noteId`);
 | 
			
		||||
CREATE TABLE IF NOT EXISTS "notes" (
 | 
			
		||||
  `noteId`	TEXT NOT NULL,
 | 
			
		||||
  `title`	TEXT NOT NULL DEFAULT "note",
 | 
			
		||||
  `isProtected`	INT NOT NULL DEFAULT 0,
 | 
			
		||||
  `type` TEXT NOT NULL DEFAULT 'text',
 | 
			
		||||
  `mime` TEXT NOT NULL DEFAULT 'text/html',
 | 
			
		||||
  `hash` TEXT DEFAULT "" NOT NULL,
 | 
			
		||||
  `isDeleted`	INT NOT NULL DEFAULT 0,
 | 
			
		||||
  `dateCreated`	TEXT NOT NULL,
 | 
			
		||||
  `dateModified`	TEXT NOT NULL,
 | 
			
		||||
  PRIMARY KEY(`noteId`)
 | 
			
		||||
);
 | 
			
		||||
 
 | 
			
		||||
@@ -282,7 +282,7 @@
 | 
			
		||||
</div>
 | 
			
		||||
 | 
			
		||||
<nav>
 | 
			
		||||
    <h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="ApiToken.html">ApiToken</a></li><li><a href="Attribute.html">Attribute</a></li><li><a href="BackendScriptApi.html">BackendScriptApi</a></li><li><a href="Branch.html">Branch</a></li><li><a href="Entity.html">Entity</a></li><li><a href="Link.html">Link</a></li><li><a href="Note.html">Note</a></li><li><a href="NoteRevision.html">NoteRevision</a></li><li><a href="Option.html">Option</a></li><li><a href="RecentNote.html">RecentNote</a></li></ul><h3><a href="global.html">Global</a></h3>
 | 
			
		||||
    <h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="ApiToken.html">ApiToken</a></li><li><a href="Attribute.html">Attribute</a></li><li><a href="BackendScriptApi.html">BackendScriptApi</a></li><li><a href="Branch.html">Branch</a></li><li><a href="Entity.html">Entity</a></li><li><a href="Link.html">Link</a></li><li><a href="Note.html">Note</a></li><li><a href="NoteContent.html">NoteContent</a></li><li><a href="NoteRevision.html">NoteRevision</a></li><li><a href="Option.html">Option</a></li><li><a href="RecentNote.html">RecentNote</a></li></ul><h3><a href="global.html">Global</a></h3>
 | 
			
		||||
</nav>
 | 
			
		||||
 | 
			
		||||
<br class="clear">
 | 
			
		||||
 
 | 
			
		||||
@@ -724,7 +724,7 @@
 | 
			
		||||
</div>
 | 
			
		||||
 | 
			
		||||
<nav>
 | 
			
		||||
    <h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="ApiToken.html">ApiToken</a></li><li><a href="Attribute.html">Attribute</a></li><li><a href="BackendScriptApi.html">BackendScriptApi</a></li><li><a href="Branch.html">Branch</a></li><li><a href="Entity.html">Entity</a></li><li><a href="Link.html">Link</a></li><li><a href="Note.html">Note</a></li><li><a href="NoteRevision.html">NoteRevision</a></li><li><a href="Option.html">Option</a></li><li><a href="RecentNote.html">RecentNote</a></li></ul><h3><a href="global.html">Global</a></h3>
 | 
			
		||||
    <h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="ApiToken.html">ApiToken</a></li><li><a href="Attribute.html">Attribute</a></li><li><a href="BackendScriptApi.html">BackendScriptApi</a></li><li><a href="Branch.html">Branch</a></li><li><a href="Entity.html">Entity</a></li><li><a href="Link.html">Link</a></li><li><a href="Note.html">Note</a></li><li><a href="NoteContent.html">NoteContent</a></li><li><a href="NoteRevision.html">NoteRevision</a></li><li><a href="Option.html">Option</a></li><li><a href="RecentNote.html">RecentNote</a></li></ul><h3><a href="global.html">Global</a></h3>
 | 
			
		||||
</nav>
 | 
			
		||||
 | 
			
		||||
<br class="clear">
 | 
			
		||||
 
 | 
			
		||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@@ -505,7 +505,7 @@ Each note can have multiple (at least one) branches, meaning it can be placed in
 | 
			
		||||
</div>
 | 
			
		||||
 | 
			
		||||
<nav>
 | 
			
		||||
    <h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="ApiToken.html">ApiToken</a></li><li><a href="Attribute.html">Attribute</a></li><li><a href="BackendScriptApi.html">BackendScriptApi</a></li><li><a href="Branch.html">Branch</a></li><li><a href="Entity.html">Entity</a></li><li><a href="Link.html">Link</a></li><li><a href="Note.html">Note</a></li><li><a href="NoteRevision.html">NoteRevision</a></li><li><a href="Option.html">Option</a></li><li><a href="RecentNote.html">RecentNote</a></li></ul><h3><a href="global.html">Global</a></h3>
 | 
			
		||||
    <h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="ApiToken.html">ApiToken</a></li><li><a href="Attribute.html">Attribute</a></li><li><a href="BackendScriptApi.html">BackendScriptApi</a></li><li><a href="Branch.html">Branch</a></li><li><a href="Entity.html">Entity</a></li><li><a href="Link.html">Link</a></li><li><a href="Note.html">Note</a></li><li><a href="NoteContent.html">NoteContent</a></li><li><a href="NoteRevision.html">NoteRevision</a></li><li><a href="Option.html">Option</a></li><li><a href="RecentNote.html">RecentNote</a></li></ul><h3><a href="global.html">Global</a></h3>
 | 
			
		||||
</nav>
 | 
			
		||||
 | 
			
		||||
<br class="clear">
 | 
			
		||||
 
 | 
			
		||||
@@ -210,7 +210,7 @@
 | 
			
		||||
</div>
 | 
			
		||||
 | 
			
		||||
<nav>
 | 
			
		||||
    <h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="ApiToken.html">ApiToken</a></li><li><a href="Attribute.html">Attribute</a></li><li><a href="BackendScriptApi.html">BackendScriptApi</a></li><li><a href="Branch.html">Branch</a></li><li><a href="Entity.html">Entity</a></li><li><a href="Link.html">Link</a></li><li><a href="Note.html">Note</a></li><li><a href="NoteRevision.html">NoteRevision</a></li><li><a href="Option.html">Option</a></li><li><a href="RecentNote.html">RecentNote</a></li></ul><h3><a href="global.html">Global</a></h3>
 | 
			
		||||
    <h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="ApiToken.html">ApiToken</a></li><li><a href="Attribute.html">Attribute</a></li><li><a href="BackendScriptApi.html">BackendScriptApi</a></li><li><a href="Branch.html">Branch</a></li><li><a href="Entity.html">Entity</a></li><li><a href="Link.html">Link</a></li><li><a href="Note.html">Note</a></li><li><a href="NoteContent.html">NoteContent</a></li><li><a href="NoteRevision.html">NoteRevision</a></li><li><a href="Option.html">Option</a></li><li><a href="RecentNote.html">RecentNote</a></li></ul><h3><a href="global.html">Global</a></h3>
 | 
			
		||||
</nav>
 | 
			
		||||
 | 
			
		||||
<br class="clear">
 | 
			
		||||
 
 | 
			
		||||
@@ -352,7 +352,7 @@ this is different concept than attribute/relation.</div>
 | 
			
		||||
</div>
 | 
			
		||||
 | 
			
		||||
<nav>
 | 
			
		||||
    <h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="ApiToken.html">ApiToken</a></li><li><a href="Attribute.html">Attribute</a></li><li><a href="BackendScriptApi.html">BackendScriptApi</a></li><li><a href="Branch.html">Branch</a></li><li><a href="Entity.html">Entity</a></li><li><a href="Link.html">Link</a></li><li><a href="Note.html">Note</a></li><li><a href="NoteRevision.html">NoteRevision</a></li><li><a href="Option.html">Option</a></li><li><a href="RecentNote.html">RecentNote</a></li></ul><h3><a href="global.html">Global</a></h3>
 | 
			
		||||
    <h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="ApiToken.html">ApiToken</a></li><li><a href="Attribute.html">Attribute</a></li><li><a href="BackendScriptApi.html">BackendScriptApi</a></li><li><a href="Branch.html">Branch</a></li><li><a href="Entity.html">Entity</a></li><li><a href="Link.html">Link</a></li><li><a href="Note.html">Note</a></li><li><a href="NoteContent.html">NoteContent</a></li><li><a href="NoteRevision.html">NoteRevision</a></li><li><a href="Option.html">Option</a></li><li><a href="RecentNote.html">RecentNote</a></li></ul><h3><a href="global.html">Global</a></h3>
 | 
			
		||||
</nav>
 | 
			
		||||
 | 
			
		||||
<br class="clear">
 | 
			
		||||
 
 | 
			
		||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										494
									
								
								docs/backend_api/NoteContent.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										494
									
								
								docs/backend_api/NoteContent.html
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,494 @@
 | 
			
		||||
<!DOCTYPE html>
 | 
			
		||||
<html lang="en">
 | 
			
		||||
<head>
 | 
			
		||||
    <meta charset="utf-8">
 | 
			
		||||
    <title>JSDoc: Class: NoteContent</title>
 | 
			
		||||
 | 
			
		||||
    <script src="scripts/prettify/prettify.js"> </script>
 | 
			
		||||
    <script src="scripts/prettify/lang-css.js"> </script>
 | 
			
		||||
    <!--[if lt IE 9]>
 | 
			
		||||
      <script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script>
 | 
			
		||||
    <![endif]-->
 | 
			
		||||
    <link type="text/css" rel="stylesheet" href="styles/prettify-tomorrow.css">
 | 
			
		||||
    <link type="text/css" rel="stylesheet" href="styles/jsdoc-default.css">
 | 
			
		||||
</head>
 | 
			
		||||
 | 
			
		||||
<body>
 | 
			
		||||
 | 
			
		||||
<div id="main">
 | 
			
		||||
 | 
			
		||||
    <h1 class="page-title">Class: NoteContent</h1>
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
<section>
 | 
			
		||||
 | 
			
		||||
<header>
 | 
			
		||||
    
 | 
			
		||||
        <h2><span class="attribs"><span class="type-signature"></span></span>NoteContent<span class="signature">(row)</span><span class="type-signature"></span></h2>
 | 
			
		||||
        
 | 
			
		||||
            <div class="class-description">This represents a Note which is a central object in the Trilium Notes project.</div>
 | 
			
		||||
        
 | 
			
		||||
    
 | 
			
		||||
</header>
 | 
			
		||||
 | 
			
		||||
<article>
 | 
			
		||||
    <div class="container-overview">
 | 
			
		||||
    
 | 
			
		||||
        
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
    <h2>Constructor</h2>
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
    <h4 class="name" id="NoteContent"><span class="type-signature"></span>new NoteContent<span class="signature">(row)</span><span class="type-signature"></span></h4>
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    <h5>Parameters:</h5>
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
<table class="params">
 | 
			
		||||
    <thead>
 | 
			
		||||
    <tr>
 | 
			
		||||
        
 | 
			
		||||
        <th>Name</th>
 | 
			
		||||
        
 | 
			
		||||
 | 
			
		||||
        <th>Type</th>
 | 
			
		||||
 | 
			
		||||
        
 | 
			
		||||
 | 
			
		||||
        
 | 
			
		||||
 | 
			
		||||
        <th class="last">Description</th>
 | 
			
		||||
    </tr>
 | 
			
		||||
    </thead>
 | 
			
		||||
 | 
			
		||||
    <tbody>
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
        <tr>
 | 
			
		||||
            
 | 
			
		||||
                <td class="name"><code>row</code></td>
 | 
			
		||||
            
 | 
			
		||||
 | 
			
		||||
            <td class="type">
 | 
			
		||||
            
 | 
			
		||||
            </td>
 | 
			
		||||
 | 
			
		||||
            
 | 
			
		||||
 | 
			
		||||
            
 | 
			
		||||
 | 
			
		||||
            <td class="description last">object containing database row from "note_contents" table</td>
 | 
			
		||||
        </tr>
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
    </tbody>
 | 
			
		||||
</table>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    <h5 class="subsection-title">Properties:</h5>
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
<table class="props">
 | 
			
		||||
    <thead>
 | 
			
		||||
    <tr>
 | 
			
		||||
        
 | 
			
		||||
        <th>Name</th>
 | 
			
		||||
        
 | 
			
		||||
 | 
			
		||||
        <th>Type</th>
 | 
			
		||||
 | 
			
		||||
        
 | 
			
		||||
 | 
			
		||||
        
 | 
			
		||||
 | 
			
		||||
        <th class="last">Description</th>
 | 
			
		||||
    </tr>
 | 
			
		||||
    </thead>
 | 
			
		||||
 | 
			
		||||
    <tbody>
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
        <tr>
 | 
			
		||||
            
 | 
			
		||||
                <td class="name"><code>noteContentId</code></td>
 | 
			
		||||
            
 | 
			
		||||
 | 
			
		||||
            <td class="type">
 | 
			
		||||
            
 | 
			
		||||
                
 | 
			
		||||
<span class="param-type">string</span>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
            
 | 
			
		||||
            </td>
 | 
			
		||||
 | 
			
		||||
            
 | 
			
		||||
 | 
			
		||||
            
 | 
			
		||||
 | 
			
		||||
            <td class="description last">primary key</td>
 | 
			
		||||
        </tr>
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
        <tr>
 | 
			
		||||
            
 | 
			
		||||
                <td class="name"><code>noteId</code></td>
 | 
			
		||||
            
 | 
			
		||||
 | 
			
		||||
            <td class="type">
 | 
			
		||||
            
 | 
			
		||||
                
 | 
			
		||||
<span class="param-type">string</span>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
            
 | 
			
		||||
            </td>
 | 
			
		||||
 | 
			
		||||
            
 | 
			
		||||
 | 
			
		||||
            
 | 
			
		||||
 | 
			
		||||
            <td class="description last">reference to owning note</td>
 | 
			
		||||
        </tr>
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
        <tr>
 | 
			
		||||
            
 | 
			
		||||
                <td class="name"><code>isProtected</code></td>
 | 
			
		||||
            
 | 
			
		||||
 | 
			
		||||
            <td class="type">
 | 
			
		||||
            
 | 
			
		||||
                
 | 
			
		||||
<span class="param-type">boolean</span>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
            
 | 
			
		||||
            </td>
 | 
			
		||||
 | 
			
		||||
            
 | 
			
		||||
 | 
			
		||||
            
 | 
			
		||||
 | 
			
		||||
            <td class="description last">true if note content is protected</td>
 | 
			
		||||
        </tr>
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
        <tr>
 | 
			
		||||
            
 | 
			
		||||
                <td class="name"><code>content</code></td>
 | 
			
		||||
            
 | 
			
		||||
 | 
			
		||||
            <td class="type">
 | 
			
		||||
            
 | 
			
		||||
                
 | 
			
		||||
<span class="param-type">blob</span>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
            
 | 
			
		||||
            </td>
 | 
			
		||||
 | 
			
		||||
            
 | 
			
		||||
 | 
			
		||||
            
 | 
			
		||||
 | 
			
		||||
            <td class="description last">note content - e.g. HTML text for text notes, file payload for files</td>
 | 
			
		||||
        </tr>
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
        <tr>
 | 
			
		||||
            
 | 
			
		||||
                <td class="name"><code>dateCreated</code></td>
 | 
			
		||||
            
 | 
			
		||||
 | 
			
		||||
            <td class="type">
 | 
			
		||||
            
 | 
			
		||||
                
 | 
			
		||||
<span class="param-type">string</span>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
            
 | 
			
		||||
            </td>
 | 
			
		||||
 | 
			
		||||
            
 | 
			
		||||
 | 
			
		||||
            
 | 
			
		||||
 | 
			
		||||
            <td class="description last"></td>
 | 
			
		||||
        </tr>
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
        <tr>
 | 
			
		||||
            
 | 
			
		||||
                <td class="name"><code>dateModified</code></td>
 | 
			
		||||
            
 | 
			
		||||
 | 
			
		||||
            <td class="type">
 | 
			
		||||
            
 | 
			
		||||
                
 | 
			
		||||
<span class="param-type">string</span>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
            
 | 
			
		||||
            </td>
 | 
			
		||||
 | 
			
		||||
            
 | 
			
		||||
 | 
			
		||||
            
 | 
			
		||||
 | 
			
		||||
            <td class="description last"></td>
 | 
			
		||||
        </tr>
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
    </tbody>
 | 
			
		||||
</table>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
<dl class="details">
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
    <dt class="tag-source">Source:</dt>
 | 
			
		||||
    <dd class="tag-source"><ul class="dummy"><li>
 | 
			
		||||
        <a href="entities_note_content.js.html">entities/note_content.js</a>, <a href="entities_note_content.js.html#line20">line 20</a>
 | 
			
		||||
    </li></ul></dd>
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
</dl>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
    </div>
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
        <h3 class="subsection-title">Extends</h3>
 | 
			
		||||
 | 
			
		||||
        
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    <ul>
 | 
			
		||||
        <li><a href="Entity.html">Entity</a></li>
 | 
			
		||||
    </ul>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
        <h3 class="subsection-title">Methods</h3>
 | 
			
		||||
 | 
			
		||||
        
 | 
			
		||||
            
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
    <h4 class="name" id="getNote"><span class="type-signature">(async) </span>getNote<span class="signature">()</span><span class="type-signature"> → {Promise.<<a href="Note.html">Note</a>>}</span></h4>
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
<dl class="details">
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
    <dt class="tag-source">Source:</dt>
 | 
			
		||||
    <dd class="tag-source"><ul class="dummy"><li>
 | 
			
		||||
        <a href="entities_note_content.js.html">entities/note_content.js</a>, <a href="entities_note_content.js.html#line63">line 63</a>
 | 
			
		||||
    </li></ul></dd>
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
</dl>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
<h5>Returns:</h5>
 | 
			
		||||
 | 
			
		||||
        
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
<dl>
 | 
			
		||||
    <dt>
 | 
			
		||||
        Type
 | 
			
		||||
    </dt>
 | 
			
		||||
    <dd>
 | 
			
		||||
        
 | 
			
		||||
<span class="param-type">Promise.<<a href="Note.html">Note</a>></span>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    </dd>
 | 
			
		||||
</dl>
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
</article>
 | 
			
		||||
 | 
			
		||||
</section>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
</div>
 | 
			
		||||
 | 
			
		||||
<nav>
 | 
			
		||||
    <h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="ApiToken.html">ApiToken</a></li><li><a href="Attribute.html">Attribute</a></li><li><a href="BackendScriptApi.html">BackendScriptApi</a></li><li><a href="Branch.html">Branch</a></li><li><a href="Entity.html">Entity</a></li><li><a href="Link.html">Link</a></li><li><a href="Note.html">Note</a></li><li><a href="NoteContent.html">NoteContent</a></li><li><a href="NoteRevision.html">NoteRevision</a></li><li><a href="Option.html">Option</a></li><li><a href="RecentNote.html">RecentNote</a></li></ul><h3><a href="global.html">Global</a></h3>
 | 
			
		||||
</nav>
 | 
			
		||||
 | 
			
		||||
<br class="clear">
 | 
			
		||||
 | 
			
		||||
<footer>
 | 
			
		||||
    Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 3.5.5</a>
 | 
			
		||||
</footer>
 | 
			
		||||
 | 
			
		||||
<script> prettyPrint(); </script>
 | 
			
		||||
<script src="scripts/linenumber.js"> </script>
 | 
			
		||||
</body>
 | 
			
		||||
</html>
 | 
			
		||||
@@ -397,7 +397,7 @@
 | 
			
		||||
</div>
 | 
			
		||||
 | 
			
		||||
<nav>
 | 
			
		||||
    <h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="ApiToken.html">ApiToken</a></li><li><a href="Attribute.html">Attribute</a></li><li><a href="BackendScriptApi.html">BackendScriptApi</a></li><li><a href="Branch.html">Branch</a></li><li><a href="Entity.html">Entity</a></li><li><a href="Link.html">Link</a></li><li><a href="Note.html">Note</a></li><li><a href="NoteRevision.html">NoteRevision</a></li><li><a href="Option.html">Option</a></li><li><a href="RecentNote.html">RecentNote</a></li></ul><h3><a href="global.html">Global</a></h3>
 | 
			
		||||
    <h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="ApiToken.html">ApiToken</a></li><li><a href="Attribute.html">Attribute</a></li><li><a href="BackendScriptApi.html">BackendScriptApi</a></li><li><a href="Branch.html">Branch</a></li><li><a href="Entity.html">Entity</a></li><li><a href="Link.html">Link</a></li><li><a href="Note.html">Note</a></li><li><a href="NoteContent.html">NoteContent</a></li><li><a href="NoteRevision.html">NoteRevision</a></li><li><a href="Option.html">Option</a></li><li><a href="RecentNote.html">RecentNote</a></li></ul><h3><a href="global.html">Global</a></h3>
 | 
			
		||||
</nav>
 | 
			
		||||
 | 
			
		||||
<br class="clear">
 | 
			
		||||
 
 | 
			
		||||
@@ -305,7 +305,7 @@
 | 
			
		||||
</div>
 | 
			
		||||
 | 
			
		||||
<nav>
 | 
			
		||||
    <h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="ApiToken.html">ApiToken</a></li><li><a href="Attribute.html">Attribute</a></li><li><a href="BackendScriptApi.html">BackendScriptApi</a></li><li><a href="Branch.html">Branch</a></li><li><a href="Entity.html">Entity</a></li><li><a href="Link.html">Link</a></li><li><a href="Note.html">Note</a></li><li><a href="NoteRevision.html">NoteRevision</a></li><li><a href="Option.html">Option</a></li><li><a href="RecentNote.html">RecentNote</a></li></ul><h3><a href="global.html">Global</a></h3>
 | 
			
		||||
    <h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="ApiToken.html">ApiToken</a></li><li><a href="Attribute.html">Attribute</a></li><li><a href="BackendScriptApi.html">BackendScriptApi</a></li><li><a href="Branch.html">Branch</a></li><li><a href="Entity.html">Entity</a></li><li><a href="Link.html">Link</a></li><li><a href="Note.html">Note</a></li><li><a href="NoteContent.html">NoteContent</a></li><li><a href="NoteRevision.html">NoteRevision</a></li><li><a href="Option.html">Option</a></li><li><a href="RecentNote.html">RecentNote</a></li></ul><h3><a href="global.html">Global</a></h3>
 | 
			
		||||
</nav>
 | 
			
		||||
 | 
			
		||||
<br class="clear">
 | 
			
		||||
 
 | 
			
		||||
@@ -282,7 +282,7 @@
 | 
			
		||||
</div>
 | 
			
		||||
 | 
			
		||||
<nav>
 | 
			
		||||
    <h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="ApiToken.html">ApiToken</a></li><li><a href="Attribute.html">Attribute</a></li><li><a href="BackendScriptApi.html">BackendScriptApi</a></li><li><a href="Branch.html">Branch</a></li><li><a href="Entity.html">Entity</a></li><li><a href="Link.html">Link</a></li><li><a href="Note.html">Note</a></li><li><a href="NoteRevision.html">NoteRevision</a></li><li><a href="Option.html">Option</a></li><li><a href="RecentNote.html">RecentNote</a></li></ul><h3><a href="global.html">Global</a></h3>
 | 
			
		||||
    <h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="ApiToken.html">ApiToken</a></li><li><a href="Attribute.html">Attribute</a></li><li><a href="BackendScriptApi.html">BackendScriptApi</a></li><li><a href="Branch.html">Branch</a></li><li><a href="Entity.html">Entity</a></li><li><a href="Link.html">Link</a></li><li><a href="Note.html">Note</a></li><li><a href="NoteContent.html">NoteContent</a></li><li><a href="NoteRevision.html">NoteRevision</a></li><li><a href="Option.html">Option</a></li><li><a href="RecentNote.html">RecentNote</a></li></ul><h3><a href="global.html">Global</a></h3>
 | 
			
		||||
</nav>
 | 
			
		||||
 | 
			
		||||
<br class="clear">
 | 
			
		||||
 
 | 
			
		||||
@@ -69,7 +69,7 @@ module.exports = ApiToken;</code></pre>
 | 
			
		||||
</div>
 | 
			
		||||
 | 
			
		||||
<nav>
 | 
			
		||||
    <h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="ApiToken.html">ApiToken</a></li><li><a href="Attribute.html">Attribute</a></li><li><a href="BackendScriptApi.html">BackendScriptApi</a></li><li><a href="Branch.html">Branch</a></li><li><a href="Entity.html">Entity</a></li><li><a href="Link.html">Link</a></li><li><a href="Note.html">Note</a></li><li><a href="NoteRevision.html">NoteRevision</a></li><li><a href="Option.html">Option</a></li><li><a href="RecentNote.html">RecentNote</a></li></ul><h3><a href="global.html">Global</a></h3>
 | 
			
		||||
    <h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="ApiToken.html">ApiToken</a></li><li><a href="Attribute.html">Attribute</a></li><li><a href="BackendScriptApi.html">BackendScriptApi</a></li><li><a href="Branch.html">Branch</a></li><li><a href="Entity.html">Entity</a></li><li><a href="Link.html">Link</a></li><li><a href="Note.html">Note</a></li><li><a href="NoteContent.html">NoteContent</a></li><li><a href="NoteRevision.html">NoteRevision</a></li><li><a href="Option.html">Option</a></li><li><a href="RecentNote.html">RecentNote</a></li></ul><h3><a href="global.html">Global</a></h3>
 | 
			
		||||
</nav>
 | 
			
		||||
 | 
			
		||||
<br class="clear">
 | 
			
		||||
 
 | 
			
		||||
@@ -150,7 +150,7 @@ module.exports = Attribute;</code></pre>
 | 
			
		||||
</div>
 | 
			
		||||
 | 
			
		||||
<nav>
 | 
			
		||||
    <h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="ApiToken.html">ApiToken</a></li><li><a href="Attribute.html">Attribute</a></li><li><a href="BackendScriptApi.html">BackendScriptApi</a></li><li><a href="Branch.html">Branch</a></li><li><a href="Entity.html">Entity</a></li><li><a href="Link.html">Link</a></li><li><a href="Note.html">Note</a></li><li><a href="NoteRevision.html">NoteRevision</a></li><li><a href="Option.html">Option</a></li><li><a href="RecentNote.html">RecentNote</a></li></ul><h3><a href="global.html">Global</a></h3>
 | 
			
		||||
    <h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="ApiToken.html">ApiToken</a></li><li><a href="Attribute.html">Attribute</a></li><li><a href="BackendScriptApi.html">BackendScriptApi</a></li><li><a href="Branch.html">Branch</a></li><li><a href="Entity.html">Entity</a></li><li><a href="Link.html">Link</a></li><li><a href="Note.html">Note</a></li><li><a href="NoteContent.html">NoteContent</a></li><li><a href="NoteRevision.html">NoteRevision</a></li><li><a href="Option.html">Option</a></li><li><a href="RecentNote.html">RecentNote</a></li></ul><h3><a href="global.html">Global</a></h3>
 | 
			
		||||
</nav>
 | 
			
		||||
 | 
			
		||||
<br class="clear">
 | 
			
		||||
 
 | 
			
		||||
@@ -104,7 +104,7 @@ module.exports = Branch;</code></pre>
 | 
			
		||||
</div>
 | 
			
		||||
 | 
			
		||||
<nav>
 | 
			
		||||
    <h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="ApiToken.html">ApiToken</a></li><li><a href="Attribute.html">Attribute</a></li><li><a href="BackendScriptApi.html">BackendScriptApi</a></li><li><a href="Branch.html">Branch</a></li><li><a href="Entity.html">Entity</a></li><li><a href="Link.html">Link</a></li><li><a href="Note.html">Note</a></li><li><a href="NoteRevision.html">NoteRevision</a></li><li><a href="Option.html">Option</a></li><li><a href="RecentNote.html">RecentNote</a></li></ul><h3><a href="global.html">Global</a></h3>
 | 
			
		||||
    <h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="ApiToken.html">ApiToken</a></li><li><a href="Attribute.html">Attribute</a></li><li><a href="BackendScriptApi.html">BackendScriptApi</a></li><li><a href="Branch.html">Branch</a></li><li><a href="Entity.html">Entity</a></li><li><a href="Link.html">Link</a></li><li><a href="Note.html">Note</a></li><li><a href="NoteContent.html">NoteContent</a></li><li><a href="NoteRevision.html">NoteRevision</a></li><li><a href="Option.html">Option</a></li><li><a href="RecentNote.html">RecentNote</a></li></ul><h3><a href="global.html">Global</a></h3>
 | 
			
		||||
</nav>
 | 
			
		||||
 | 
			
		||||
<br class="clear">
 | 
			
		||||
 
 | 
			
		||||
@@ -36,7 +36,10 @@ class Entity {
 | 
			
		||||
     */
 | 
			
		||||
    constructor(row = {}) {
 | 
			
		||||
        for (const key in row) {
 | 
			
		||||
            this[key] = row[key];
 | 
			
		||||
            // ! is used when joint-fetching notes and note_contents objects for performance
 | 
			
		||||
            if (!key.startsWith('!')) {
 | 
			
		||||
                this[key] = row[key];
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if ('isDeleted' in this) {
 | 
			
		||||
@@ -87,7 +90,7 @@ module.exports = Entity;</code></pre>
 | 
			
		||||
</div>
 | 
			
		||||
 | 
			
		||||
<nav>
 | 
			
		||||
    <h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="ApiToken.html">ApiToken</a></li><li><a href="Attribute.html">Attribute</a></li><li><a href="BackendScriptApi.html">BackendScriptApi</a></li><li><a href="Branch.html">Branch</a></li><li><a href="Entity.html">Entity</a></li><li><a href="Link.html">Link</a></li><li><a href="Note.html">Note</a></li><li><a href="NoteRevision.html">NoteRevision</a></li><li><a href="Option.html">Option</a></li><li><a href="RecentNote.html">RecentNote</a></li></ul><h3><a href="global.html">Global</a></h3>
 | 
			
		||||
    <h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="ApiToken.html">ApiToken</a></li><li><a href="Attribute.html">Attribute</a></li><li><a href="BackendScriptApi.html">BackendScriptApi</a></li><li><a href="Branch.html">Branch</a></li><li><a href="Entity.html">Entity</a></li><li><a href="Link.html">Link</a></li><li><a href="Note.html">Note</a></li><li><a href="NoteContent.html">NoteContent</a></li><li><a href="NoteRevision.html">NoteRevision</a></li><li><a href="Option.html">Option</a></li><li><a href="RecentNote.html">RecentNote</a></li></ul><h3><a href="global.html">Global</a></h3>
 | 
			
		||||
</nav>
 | 
			
		||||
 | 
			
		||||
<br class="clear">
 | 
			
		||||
 
 | 
			
		||||
@@ -86,7 +86,7 @@ module.exports = Link;</code></pre>
 | 
			
		||||
</div>
 | 
			
		||||
 | 
			
		||||
<nav>
 | 
			
		||||
    <h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="ApiToken.html">ApiToken</a></li><li><a href="Attribute.html">Attribute</a></li><li><a href="BackendScriptApi.html">BackendScriptApi</a></li><li><a href="Branch.html">Branch</a></li><li><a href="Entity.html">Entity</a></li><li><a href="Link.html">Link</a></li><li><a href="Note.html">Note</a></li><li><a href="NoteRevision.html">NoteRevision</a></li><li><a href="Option.html">Option</a></li><li><a href="RecentNote.html">RecentNote</a></li></ul><h3><a href="global.html">Global</a></h3>
 | 
			
		||||
    <h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="ApiToken.html">ApiToken</a></li><li><a href="Attribute.html">Attribute</a></li><li><a href="BackendScriptApi.html">BackendScriptApi</a></li><li><a href="Branch.html">Branch</a></li><li><a href="Entity.html">Entity</a></li><li><a href="Link.html">Link</a></li><li><a href="Note.html">Note</a></li><li><a href="NoteContent.html">NoteContent</a></li><li><a href="NoteRevision.html">NoteRevision</a></li><li><a href="Option.html">Option</a></li><li><a href="RecentNote.html">RecentNote</a></li></ul><h3><a href="global.html">Global</a></h3>
 | 
			
		||||
</nav>
 | 
			
		||||
 | 
			
		||||
<br class="clear">
 | 
			
		||||
 
 | 
			
		||||
@@ -30,6 +30,7 @@
 | 
			
		||||
 | 
			
		||||
const Entity = require('./entity');
 | 
			
		||||
const Attribute = require('./attribute');
 | 
			
		||||
const NoteContent = require('./note_content');
 | 
			
		||||
const protectedSessionService = require('../services/protected_session');
 | 
			
		||||
const repository = require('../services/repository');
 | 
			
		||||
const sql = require('../services/sql');
 | 
			
		||||
@@ -47,7 +48,6 @@ const RELATION_DEFINITION = 'relation-definition';
 | 
			
		||||
 * @property {string} type - one of "text", "code", "file" or "render"
 | 
			
		||||
 * @property {string} mime - MIME type, e.g. "text/html"
 | 
			
		||||
 * @property {string} title - note title
 | 
			
		||||
 * @property {string} content - note content - e.g. HTML text for text notes, file payload for files
 | 
			
		||||
 * @property {boolean} isProtected - true if note is protected
 | 
			
		||||
 * @property {boolean} isDeleted - true if note is deleted
 | 
			
		||||
 * @property {string} dateCreated
 | 
			
		||||
@@ -58,7 +58,7 @@ const RELATION_DEFINITION = 'relation-definition';
 | 
			
		||||
class Note extends Entity {
 | 
			
		||||
    static get entityName() { return "notes"; }
 | 
			
		||||
    static get primaryKeyName() { return "noteId"; }
 | 
			
		||||
    static get hashedProperties() { return ["noteId", "title", "content", "type", "isProtected", "isDeleted"]; }
 | 
			
		||||
    static get hashedProperties() { return ["noteId", "title", "type", "isProtected", "isDeleted"]; }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @param row - object containing database row from "notes" table
 | 
			
		||||
@@ -75,19 +75,64 @@ class Note extends Entity {
 | 
			
		||||
        if (this.isProtected && this.noteId) {
 | 
			
		||||
            this.isContentAvailable = protectedSessionService.isProtectedSessionAvailable();
 | 
			
		||||
 | 
			
		||||
            protectedSessionService.decryptNote(this);
 | 
			
		||||
            if (this.isContentAvailable) {
 | 
			
		||||
                protectedSessionService.decryptNote(this);
 | 
			
		||||
            }
 | 
			
		||||
            else {
 | 
			
		||||
                // saving ciphertexts in case we do want to update protected note outside of protected session
 | 
			
		||||
                // (which is allowed)
 | 
			
		||||
                this.titleCipherText = this.title;
 | 
			
		||||
                this.title = "[protected]";
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this.setContent(this.content);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    setContent(content) {
 | 
			
		||||
        this.content = content;
 | 
			
		||||
    /** @returns {Promise<NoteContent>} */
 | 
			
		||||
    async getNoteContent() {
 | 
			
		||||
        if (!this.noteContent) {
 | 
			
		||||
            this.noteContent = await repository.getEntity(`SELECT * FROM note_contents WHERE noteId = ?`, [this.noteId]);
 | 
			
		||||
 | 
			
		||||
        try {
 | 
			
		||||
            this.jsonContent = JSON.parse(this.content);
 | 
			
		||||
            if (!this.noteContent) {
 | 
			
		||||
                throw new Error("Note content not found for noteId=" + this.noteId);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (this.isStringNote()) {
 | 
			
		||||
                this.noteContent.content = this.noteContent.content.toString("UTF-8");
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        catch(e) {}
 | 
			
		||||
 | 
			
		||||
        return this.noteContent;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /** @returns {Promise<*>} */
 | 
			
		||||
    async getContent() {
 | 
			
		||||
        const noteContent = await this.getNoteContent();
 | 
			
		||||
 | 
			
		||||
        return noteContent.content;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /** @returns {Promise<*>} */
 | 
			
		||||
    async getJsonContent() {
 | 
			
		||||
        const content = await this.getContent();
 | 
			
		||||
 | 
			
		||||
        return JSON.parse(content);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /** @returns {Promise} */
 | 
			
		||||
    async setContent(content) {
 | 
			
		||||
        if (!this.noteContent) {
 | 
			
		||||
            // make sure it is loaded
 | 
			
		||||
            await this.getNoteContent();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this.noteContent.content = content;
 | 
			
		||||
 | 
			
		||||
        await this.noteContent.save();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /** @returns {Promise} */
 | 
			
		||||
    async setJsonContent(content) {
 | 
			
		||||
        await this.setContent(JSON.stringify(content));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /** @returns {boolean} true if this note is the root of the note tree. Root note has "root" noteId */
 | 
			
		||||
@@ -113,6 +158,11 @@ class Note extends Entity {
 | 
			
		||||
        return (this.type === "code" || this.type === "file" || this.type === "render") && this.mime === "text/html";
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /** @returns {boolean} true if the note has string content (not binary) */
 | 
			
		||||
    isStringNote() {
 | 
			
		||||
        return ["text", "code", "relation-map", "search"].includes(this.type) || this.mime.startsWith('text/');
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /** @returns {string} JS script environment - either "frontend" or "backend" */
 | 
			
		||||
    getScriptEnv() {
 | 
			
		||||
        if (this.isHtml() || (this.isJavaScript() && this.mime.endsWith('env=frontend'))) {
 | 
			
		||||
@@ -629,13 +679,6 @@ class Note extends Entity {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    beforeSaving() {
 | 
			
		||||
        if (this.isJson() && this.jsonContent) {
 | 
			
		||||
            this.content = JSON.stringify(this.jsonContent, null, '\t');
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // we do this here because encryption needs the note ID for the IV
 | 
			
		||||
        this.generateIdIfNecessary();
 | 
			
		||||
 | 
			
		||||
        if (!this.isDeleted) {
 | 
			
		||||
            this.isDeleted = false;
 | 
			
		||||
        }
 | 
			
		||||
@@ -654,12 +697,18 @@ class Note extends Entity {
 | 
			
		||||
    // cannot be static!
 | 
			
		||||
    updatePojo(pojo) {
 | 
			
		||||
        if (pojo.isProtected) {
 | 
			
		||||
            protectedSessionService.encryptNote(pojo);
 | 
			
		||||
            if (this.isContentAvailable) {
 | 
			
		||||
                protectedSessionService.encryptNote(pojo);
 | 
			
		||||
            }
 | 
			
		||||
            else {
 | 
			
		||||
                // updating protected note outside of protected session means we will keep original ciphertexts
 | 
			
		||||
                pojo.title = pojo.titleCipherText;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        delete pojo.jsonContent;
 | 
			
		||||
        delete pojo.isContentAvailable;
 | 
			
		||||
        delete pojo.__attributeCache;
 | 
			
		||||
        delete pojo.titleCipherText;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -673,7 +722,7 @@ module.exports = Note;</code></pre>
 | 
			
		||||
</div>
 | 
			
		||||
 | 
			
		||||
<nav>
 | 
			
		||||
    <h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="ApiToken.html">ApiToken</a></li><li><a href="Attribute.html">Attribute</a></li><li><a href="BackendScriptApi.html">BackendScriptApi</a></li><li><a href="Branch.html">Branch</a></li><li><a href="Entity.html">Entity</a></li><li><a href="Link.html">Link</a></li><li><a href="Note.html">Note</a></li><li><a href="NoteRevision.html">NoteRevision</a></li><li><a href="Option.html">Option</a></li><li><a href="RecentNote.html">RecentNote</a></li></ul><h3><a href="global.html">Global</a></h3>
 | 
			
		||||
    <h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="ApiToken.html">ApiToken</a></li><li><a href="Attribute.html">Attribute</a></li><li><a href="BackendScriptApi.html">BackendScriptApi</a></li><li><a href="Branch.html">Branch</a></li><li><a href="Entity.html">Entity</a></li><li><a href="Link.html">Link</a></li><li><a href="Note.html">Note</a></li><li><a href="NoteContent.html">NoteContent</a></li><li><a href="NoteRevision.html">NoteRevision</a></li><li><a href="Option.html">Option</a></li><li><a href="RecentNote.html">RecentNote</a></li></ul><h3><a href="global.html">Global</a></h3>
 | 
			
		||||
</nav>
 | 
			
		||||
 | 
			
		||||
<br class="clear">
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										146
									
								
								docs/backend_api/entities_note_content.js.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										146
									
								
								docs/backend_api/entities_note_content.js.html
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,146 @@
 | 
			
		||||
<!DOCTYPE html>
 | 
			
		||||
<html lang="en">
 | 
			
		||||
<head>
 | 
			
		||||
    <meta charset="utf-8">
 | 
			
		||||
    <title>JSDoc: Source: entities/note_content.js</title>
 | 
			
		||||
 | 
			
		||||
    <script src="scripts/prettify/prettify.js"> </script>
 | 
			
		||||
    <script src="scripts/prettify/lang-css.js"> </script>
 | 
			
		||||
    <!--[if lt IE 9]>
 | 
			
		||||
      <script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script>
 | 
			
		||||
    <![endif]-->
 | 
			
		||||
    <link type="text/css" rel="stylesheet" href="styles/prettify-tomorrow.css">
 | 
			
		||||
    <link type="text/css" rel="stylesheet" href="styles/jsdoc-default.css">
 | 
			
		||||
</head>
 | 
			
		||||
 | 
			
		||||
<body>
 | 
			
		||||
 | 
			
		||||
<div id="main">
 | 
			
		||||
 | 
			
		||||
    <h1 class="page-title">Source: entities/note_content.js</h1>
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
    <section>
 | 
			
		||||
        <article>
 | 
			
		||||
            <pre class="prettyprint source linenums"><code>"use strict";
 | 
			
		||||
 | 
			
		||||
const Entity = require('./entity');
 | 
			
		||||
const protectedSessionService = require('../services/protected_session');
 | 
			
		||||
const repository = require('../services/repository');
 | 
			
		||||
const dateUtils = require('../services/date_utils');
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * This represents a Note which is a central object in the Trilium Notes project.
 | 
			
		||||
 *
 | 
			
		||||
 * @property {string} noteContentId - primary key
 | 
			
		||||
 * @property {string} noteId - reference to owning note
 | 
			
		||||
 * @property {boolean} isProtected - true if note content is protected
 | 
			
		||||
 * @property {blob} content - note content - e.g. HTML text for text notes, file payload for files
 | 
			
		||||
 * @property {string} dateCreated
 | 
			
		||||
 * @property {string} dateModified
 | 
			
		||||
 *
 | 
			
		||||
 * @extends Entity
 | 
			
		||||
 */
 | 
			
		||||
class NoteContent extends Entity {
 | 
			
		||||
    static get entityName() {
 | 
			
		||||
        return "note_contents";
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    static get primaryKeyName() {
 | 
			
		||||
        return "noteContentId";
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    static get hashedProperties() {
 | 
			
		||||
        return ["noteContentId", "noteId", "isProtected", "content"];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @param row - object containing database row from "note_contents" table
 | 
			
		||||
     */
 | 
			
		||||
    constructor(row) {
 | 
			
		||||
        super(row);
 | 
			
		||||
 | 
			
		||||
        this.isProtected = !!this.isProtected;
 | 
			
		||||
        /* true if content (meaning any kind of potentially encrypted content) is either not encrypted
 | 
			
		||||
         * or encrypted, but with available protected session (so effectively decrypted) */
 | 
			
		||||
        this.isContentAvailable = true;
 | 
			
		||||
 | 
			
		||||
        // check if there's noteContentId, otherwise this is a new entity which wasn't encrypted yet
 | 
			
		||||
        if (this.isProtected && this.noteContentId) {
 | 
			
		||||
            this.isContentAvailable = protectedSessionService.isProtectedSessionAvailable();
 | 
			
		||||
 | 
			
		||||
            if (this.isContentAvailable) {
 | 
			
		||||
                protectedSessionService.decryptNoteContent(this);
 | 
			
		||||
            }
 | 
			
		||||
            else {
 | 
			
		||||
                // saving ciphertexts in case we do want to update protected note outside of protected session
 | 
			
		||||
                // (which is allowed)
 | 
			
		||||
                this.contentCipherText = this.content;
 | 
			
		||||
                this.content = "";
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @returns {Promise<Note>}
 | 
			
		||||
     */
 | 
			
		||||
    async getNote() {
 | 
			
		||||
        return await repository.getNote(this.noteId);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    beforeSaving() {
 | 
			
		||||
        if (!this.dateCreated) {
 | 
			
		||||
            this.dateCreated = dateUtils.nowDate();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        super.beforeSaving();
 | 
			
		||||
 | 
			
		||||
        if (this.isChanged) {
 | 
			
		||||
            this.dateModified = dateUtils.nowDate();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // cannot be static!
 | 
			
		||||
    updatePojo(pojo) {
 | 
			
		||||
        if (pojo.isProtected) {
 | 
			
		||||
            if (this.isContentAvailable) {
 | 
			
		||||
                protectedSessionService.encryptNoteContent(pojo);
 | 
			
		||||
            }
 | 
			
		||||
            else {
 | 
			
		||||
                // updating protected note outside of protected session means we will keep original ciphertext
 | 
			
		||||
                pojo.content = pojo.contentCipherText;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        delete pojo.isContentAvailable;
 | 
			
		||||
        delete pojo.contentCipherText;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
module.exports = NoteContent;</code></pre>
 | 
			
		||||
        </article>
 | 
			
		||||
    </section>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
</div>
 | 
			
		||||
 | 
			
		||||
<nav>
 | 
			
		||||
    <h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="ApiToken.html">ApiToken</a></li><li><a href="Attribute.html">Attribute</a></li><li><a href="BackendScriptApi.html">BackendScriptApi</a></li><li><a href="Branch.html">Branch</a></li><li><a href="Entity.html">Entity</a></li><li><a href="Link.html">Link</a></li><li><a href="Note.html">Note</a></li><li><a href="NoteContent.html">NoteContent</a></li><li><a href="NoteRevision.html">NoteRevision</a></li><li><a href="Option.html">Option</a></li><li><a href="RecentNote.html">RecentNote</a></li></ul><h3><a href="global.html">Global</a></h3>
 | 
			
		||||
</nav>
 | 
			
		||||
 | 
			
		||||
<br class="clear">
 | 
			
		||||
 | 
			
		||||
<footer>
 | 
			
		||||
    Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 3.5.5</a>
 | 
			
		||||
</footer>
 | 
			
		||||
 | 
			
		||||
<script> prettyPrint(); </script>
 | 
			
		||||
<script src="scripts/linenumber.js"> </script>
 | 
			
		||||
</body>
 | 
			
		||||
</html>
 | 
			
		||||
@@ -85,7 +85,7 @@ module.exports = NoteRevision;</code></pre>
 | 
			
		||||
</div>
 | 
			
		||||
 | 
			
		||||
<nav>
 | 
			
		||||
    <h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="ApiToken.html">ApiToken</a></li><li><a href="Attribute.html">Attribute</a></li><li><a href="BackendScriptApi.html">BackendScriptApi</a></li><li><a href="Branch.html">Branch</a></li><li><a href="Entity.html">Entity</a></li><li><a href="Link.html">Link</a></li><li><a href="Note.html">Note</a></li><li><a href="NoteRevision.html">NoteRevision</a></li><li><a href="Option.html">Option</a></li><li><a href="RecentNote.html">RecentNote</a></li></ul><h3><a href="global.html">Global</a></h3>
 | 
			
		||||
    <h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="ApiToken.html">ApiToken</a></li><li><a href="Attribute.html">Attribute</a></li><li><a href="BackendScriptApi.html">BackendScriptApi</a></li><li><a href="Branch.html">Branch</a></li><li><a href="Entity.html">Entity</a></li><li><a href="Link.html">Link</a></li><li><a href="Note.html">Note</a></li><li><a href="NoteContent.html">NoteContent</a></li><li><a href="NoteRevision.html">NoteRevision</a></li><li><a href="Option.html">Option</a></li><li><a href="RecentNote.html">RecentNote</a></li></ul><h3><a href="global.html">Global</a></h3>
 | 
			
		||||
</nav>
 | 
			
		||||
 | 
			
		||||
<br class="clear">
 | 
			
		||||
 
 | 
			
		||||
@@ -72,7 +72,7 @@ module.exports = Option;</code></pre>
 | 
			
		||||
</div>
 | 
			
		||||
 | 
			
		||||
<nav>
 | 
			
		||||
    <h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="ApiToken.html">ApiToken</a></li><li><a href="Attribute.html">Attribute</a></li><li><a href="BackendScriptApi.html">BackendScriptApi</a></li><li><a href="Branch.html">Branch</a></li><li><a href="Entity.html">Entity</a></li><li><a href="Link.html">Link</a></li><li><a href="Note.html">Note</a></li><li><a href="NoteRevision.html">NoteRevision</a></li><li><a href="Option.html">Option</a></li><li><a href="RecentNote.html">RecentNote</a></li></ul><h3><a href="global.html">Global</a></h3>
 | 
			
		||||
    <h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="ApiToken.html">ApiToken</a></li><li><a href="Attribute.html">Attribute</a></li><li><a href="BackendScriptApi.html">BackendScriptApi</a></li><li><a href="Branch.html">Branch</a></li><li><a href="Entity.html">Entity</a></li><li><a href="Link.html">Link</a></li><li><a href="Note.html">Note</a></li><li><a href="NoteContent.html">NoteContent</a></li><li><a href="NoteRevision.html">NoteRevision</a></li><li><a href="Option.html">Option</a></li><li><a href="RecentNote.html">RecentNote</a></li></ul><h3><a href="global.html">Global</a></h3>
 | 
			
		||||
</nav>
 | 
			
		||||
 | 
			
		||||
<br class="clear">
 | 
			
		||||
 
 | 
			
		||||
@@ -69,7 +69,7 @@ module.exports = RecentNote;</code></pre>
 | 
			
		||||
</div>
 | 
			
		||||
 | 
			
		||||
<nav>
 | 
			
		||||
    <h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="ApiToken.html">ApiToken</a></li><li><a href="Attribute.html">Attribute</a></li><li><a href="BackendScriptApi.html">BackendScriptApi</a></li><li><a href="Branch.html">Branch</a></li><li><a href="Entity.html">Entity</a></li><li><a href="Link.html">Link</a></li><li><a href="Note.html">Note</a></li><li><a href="NoteRevision.html">NoteRevision</a></li><li><a href="Option.html">Option</a></li><li><a href="RecentNote.html">RecentNote</a></li></ul><h3><a href="global.html">Global</a></h3>
 | 
			
		||||
    <h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="ApiToken.html">ApiToken</a></li><li><a href="Attribute.html">Attribute</a></li><li><a href="BackendScriptApi.html">BackendScriptApi</a></li><li><a href="Branch.html">Branch</a></li><li><a href="Entity.html">Entity</a></li><li><a href="Link.html">Link</a></li><li><a href="Note.html">Note</a></li><li><a href="NoteContent.html">NoteContent</a></li><li><a href="NoteRevision.html">NoteRevision</a></li><li><a href="Option.html">Option</a></li><li><a href="RecentNote.html">RecentNote</a></li></ul><h3><a href="global.html">Global</a></h3>
 | 
			
		||||
</nav>
 | 
			
		||||
 | 
			
		||||
<br class="clear">
 | 
			
		||||
 
 | 
			
		||||
@@ -272,7 +272,7 @@
 | 
			
		||||
    
 | 
			
		||||
    <dt class="tag-source">Source:</dt>
 | 
			
		||||
    <dd class="tag-source"><ul class="dummy"><li>
 | 
			
		||||
        <a href="services_backend_script_api.js.html">services/backend_script_api.js</a>, <a href="services_backend_script_api.js.html#line144">line 144</a>
 | 
			
		||||
        <a href="services_backend_script_api.js.html">services/backend_script_api.js</a>, <a href="services_backend_script_api.js.html#line149">line 149</a>
 | 
			
		||||
    </li></ul></dd>
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
@@ -558,7 +558,7 @@
 | 
			
		||||
    
 | 
			
		||||
    <dt class="tag-source">Source:</dt>
 | 
			
		||||
    <dd class="tag-source"><ul class="dummy"><li>
 | 
			
		||||
        <a href="services_backend_script_api.js.html">services/backend_script_api.js</a>, <a href="services_backend_script_api.js.html#line151">line 151</a>
 | 
			
		||||
        <a href="services_backend_script_api.js.html">services/backend_script_api.js</a>, <a href="services_backend_script_api.js.html#line156">line 156</a>
 | 
			
		||||
    </li></ul></dd>
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
@@ -588,7 +588,7 @@
 | 
			
		||||
</div>
 | 
			
		||||
 | 
			
		||||
<nav>
 | 
			
		||||
    <h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="ApiToken.html">ApiToken</a></li><li><a href="Attribute.html">Attribute</a></li><li><a href="BackendScriptApi.html">BackendScriptApi</a></li><li><a href="Branch.html">Branch</a></li><li><a href="Entity.html">Entity</a></li><li><a href="Link.html">Link</a></li><li><a href="Note.html">Note</a></li><li><a href="NoteRevision.html">NoteRevision</a></li><li><a href="Option.html">Option</a></li><li><a href="RecentNote.html">RecentNote</a></li></ul><h3><a href="global.html">Global</a></h3>
 | 
			
		||||
    <h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="ApiToken.html">ApiToken</a></li><li><a href="Attribute.html">Attribute</a></li><li><a href="BackendScriptApi.html">BackendScriptApi</a></li><li><a href="Branch.html">Branch</a></li><li><a href="Entity.html">Entity</a></li><li><a href="Link.html">Link</a></li><li><a href="Note.html">Note</a></li><li><a href="NoteContent.html">NoteContent</a></li><li><a href="NoteRevision.html">NoteRevision</a></li><li><a href="Option.html">Option</a></li><li><a href="RecentNote.html">RecentNote</a></li></ul><h3><a href="global.html">Global</a></h3>
 | 
			
		||||
</nav>
 | 
			
		||||
 | 
			
		||||
<br class="clear">
 | 
			
		||||
 
 | 
			
		||||
@@ -50,7 +50,7 @@
 | 
			
		||||
</div>
 | 
			
		||||
 | 
			
		||||
<nav>
 | 
			
		||||
    <h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="ApiToken.html">ApiToken</a></li><li><a href="Attribute.html">Attribute</a></li><li><a href="BackendScriptApi.html">BackendScriptApi</a></li><li><a href="Branch.html">Branch</a></li><li><a href="Entity.html">Entity</a></li><li><a href="Link.html">Link</a></li><li><a href="Note.html">Note</a></li><li><a href="NoteRevision.html">NoteRevision</a></li><li><a href="Option.html">Option</a></li><li><a href="RecentNote.html">RecentNote</a></li></ul><h3><a href="global.html">Global</a></h3>
 | 
			
		||||
    <h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="ApiToken.html">ApiToken</a></li><li><a href="Attribute.html">Attribute</a></li><li><a href="BackendScriptApi.html">BackendScriptApi</a></li><li><a href="Branch.html">Branch</a></li><li><a href="Entity.html">Entity</a></li><li><a href="Link.html">Link</a></li><li><a href="Note.html">Note</a></li><li><a href="NoteContent.html">NoteContent</a></li><li><a href="NoteRevision.html">NoteRevision</a></li><li><a href="Option.html">Option</a></li><li><a href="RecentNote.html">RecentNote</a></li></ul><h3><a href="global.html">Global</a></h3>
 | 
			
		||||
</nav>
 | 
			
		||||
 | 
			
		||||
<br class="clear">
 | 
			
		||||
 
 | 
			
		||||
@@ -39,6 +39,7 @@ const repository = require('./repository');
 | 
			
		||||
const axios = require('axios');
 | 
			
		||||
const cloningService = require('./cloning');
 | 
			
		||||
const messagingService = require('./messaging');
 | 
			
		||||
const appInfo = require('./app_info');
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * This is the main backend API interface for scripts. It's published in the local "api" object.
 | 
			
		||||
@@ -46,13 +47,17 @@ const messagingService = require('./messaging');
 | 
			
		||||
 * @constructor
 | 
			
		||||
 * @hideconstructor
 | 
			
		||||
 */
 | 
			
		||||
function BackendScriptApi(startNote, currentNote, originEntity) {
 | 
			
		||||
function BackendScriptApi(currentNote, apiParams) {
 | 
			
		||||
    /** @property {Note} note where script started executing */
 | 
			
		||||
    this.startNote = startNote;
 | 
			
		||||
    this.startNote = apiParams.startNote;
 | 
			
		||||
    /** @property {Note} note where script is currently executing */
 | 
			
		||||
    this.currentNote = currentNote;
 | 
			
		||||
    /** @property {Entity} entity whose event triggered this executions */
 | 
			
		||||
    this.originEntity = originEntity;
 | 
			
		||||
    this.originEntity = apiParams.originEntity;
 | 
			
		||||
 | 
			
		||||
    for (const key in apiParams) {
 | 
			
		||||
        this[key] = apiParams[key];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    this.axios = axios;
 | 
			
		||||
 | 
			
		||||
@@ -196,6 +201,23 @@ function BackendScriptApi(startNote, currentNote, originEntity) {
 | 
			
		||||
     */
 | 
			
		||||
    this.createNote = noteService.createNote;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Creates new note according to given params and force all connected clients to refresh their tree.
 | 
			
		||||
     *
 | 
			
		||||
     * @method
 | 
			
		||||
     *
 | 
			
		||||
     * @param {string} parentNoteId - create new note under this parent
 | 
			
		||||
     * @param {string} title
 | 
			
		||||
     * @param {string} [content=""]
 | 
			
		||||
     * @param {CreateNoteExtraOptions} [extraOptions={}]
 | 
			
		||||
     * @returns {Promise<{note: Note, branch: Branch}>} object contains newly created entities note and branch
 | 
			
		||||
     */
 | 
			
		||||
    this.createNoteAndRefresh = async function(parentNoteId, title, content, extraOptions) {
 | 
			
		||||
        await noteService.createNote(parentNoteId, title, content, extraOptions);
 | 
			
		||||
 | 
			
		||||
        messagingService.refreshTree();
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Log given message to trilium logs.
 | 
			
		||||
     *
 | 
			
		||||
@@ -212,14 +234,42 @@ function BackendScriptApi(startNote, currentNote, originEntity) {
 | 
			
		||||
    this.getRootCalendarNote = dateNoteService.getRootCalendarNote;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Returns day note for given date (YYYY-MM-DD format). If such note doesn't exist, it is created.
 | 
			
		||||
     * Returns day note for given date. If such note doesn't exist, it is created.
 | 
			
		||||
     *
 | 
			
		||||
     * @method
 | 
			
		||||
     * @param {string} date
 | 
			
		||||
     * @param {string} date in YYYY-MM-DD format
 | 
			
		||||
     * @returns {Promise<Note|null>}
 | 
			
		||||
     */
 | 
			
		||||
    this.getDateNote = dateNoteService.getDateNote;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Returns note for the first date of the week of the given date.
 | 
			
		||||
     *
 | 
			
		||||
     * @method
 | 
			
		||||
     * @param {string} date in YYYY-MM-DD format
 | 
			
		||||
     * @param {object} options - "startOfTheWeek" - either "monday" (default) or "sunday"
 | 
			
		||||
     * @returns {Promise<Note|null>}
 | 
			
		||||
     */
 | 
			
		||||
    this.getWeekNote = dateNoteService.getWeekNote;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Returns month note for given date. If such note doesn't exist, it is created.
 | 
			
		||||
     *
 | 
			
		||||
     * @method
 | 
			
		||||
     * @param {string} date in YYYY-MM format
 | 
			
		||||
     * @returns {Promise<Note|null>}
 | 
			
		||||
     */
 | 
			
		||||
    this.getMonthNote = dateNoteService.getMonthNote;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Returns year note for given year. If such note doesn't exist, it is created.
 | 
			
		||||
     *
 | 
			
		||||
     * @method
 | 
			
		||||
     * @param {string} year in YYYY format
 | 
			
		||||
     * @returns {Promise<Note|null>}
 | 
			
		||||
     */
 | 
			
		||||
    this.getYearNote = dateNoteService.getYearNote;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @method
 | 
			
		||||
     * @param {string} parentNoteId - this note's child notes will be sorted
 | 
			
		||||
@@ -261,7 +311,12 @@ function BackendScriptApi(startNote, currentNote, originEntity) {
 | 
			
		||||
     *
 | 
			
		||||
     * @returns {Promise<void>}
 | 
			
		||||
     */
 | 
			
		||||
    this.refreshTree = () => messagingService.sendMessageToAllClients({ type: 'refresh-tree' });
 | 
			
		||||
    this.refreshTree = messagingService.refreshTree;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @return {{syncVersion, appVersion, buildRevision, dbVersion, dataDirectory, buildDate}|*} - object representing basic info about running Trilium version
 | 
			
		||||
     */
 | 
			
		||||
    this.getAppInfo = () => appInfo
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
module.exports = BackendScriptApi;</code></pre>
 | 
			
		||||
@@ -274,7 +329,7 @@ module.exports = BackendScriptApi;</code></pre>
 | 
			
		||||
</div>
 | 
			
		||||
 | 
			
		||||
<nav>
 | 
			
		||||
    <h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="ApiToken.html">ApiToken</a></li><li><a href="Attribute.html">Attribute</a></li><li><a href="BackendScriptApi.html">BackendScriptApi</a></li><li><a href="Branch.html">Branch</a></li><li><a href="Entity.html">Entity</a></li><li><a href="Link.html">Link</a></li><li><a href="Note.html">Note</a></li><li><a href="NoteRevision.html">NoteRevision</a></li><li><a href="Option.html">Option</a></li><li><a href="RecentNote.html">RecentNote</a></li></ul><h3><a href="global.html">Global</a></h3>
 | 
			
		||||
    <h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="ApiToken.html">ApiToken</a></li><li><a href="Attribute.html">Attribute</a></li><li><a href="BackendScriptApi.html">BackendScriptApi</a></li><li><a href="Branch.html">Branch</a></li><li><a href="Entity.html">Entity</a></li><li><a href="Link.html">Link</a></li><li><a href="Note.html">Note</a></li><li><a href="NoteContent.html">NoteContent</a></li><li><a href="NoteRevision.html">NoteRevision</a></li><li><a href="Option.html">Option</a></li><li><a href="RecentNote.html">RecentNote</a></li></ul><h3><a href="global.html">Global</a></h3>
 | 
			
		||||
</nav>
 | 
			
		||||
 | 
			
		||||
<br class="clear">
 | 
			
		||||
 
 | 
			
		||||
@@ -81,7 +81,7 @@
 | 
			
		||||
    
 | 
			
		||||
    <dt class="tag-source">Source:</dt>
 | 
			
		||||
    <dd class="tag-source"><ul class="dummy"><li>
 | 
			
		||||
        <a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line17">line 17</a>
 | 
			
		||||
        <a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line18">line 18</a>
 | 
			
		||||
    </li></ul></dd>
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
@@ -221,7 +221,7 @@
 | 
			
		||||
    
 | 
			
		||||
    <dt class="tag-source">Source:</dt>
 | 
			
		||||
    <dd class="tag-source"><ul class="dummy"><li>
 | 
			
		||||
        <a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line23">line 23</a>
 | 
			
		||||
        <a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line24">line 24</a>
 | 
			
		||||
    </li></ul></dd>
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
@@ -334,7 +334,7 @@
 | 
			
		||||
    
 | 
			
		||||
    <dt class="tag-source">Source:</dt>
 | 
			
		||||
    <dd class="tag-source"><ul class="dummy"><li>
 | 
			
		||||
        <a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line25">line 25</a>
 | 
			
		||||
        <a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line26">line 26</a>
 | 
			
		||||
    </li></ul></dd>
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
@@ -444,7 +444,7 @@
 | 
			
		||||
    
 | 
			
		||||
    <dt class="tag-source">Source:</dt>
 | 
			
		||||
    <dd class="tag-source"><ul class="dummy"><li>
 | 
			
		||||
        <a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line21">line 21</a>
 | 
			
		||||
        <a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line22">line 22</a>
 | 
			
		||||
    </li></ul></dd>
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
@@ -573,7 +573,7 @@
 | 
			
		||||
    
 | 
			
		||||
    <dt class="tag-source">Source:</dt>
 | 
			
		||||
    <dd class="tag-source"><ul class="dummy"><li>
 | 
			
		||||
        <a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line42">line 42</a>
 | 
			
		||||
        <a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line43">line 43</a>
 | 
			
		||||
    </li></ul></dd>
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
@@ -726,7 +726,7 @@
 | 
			
		||||
    
 | 
			
		||||
    <dt class="tag-source">Source:</dt>
 | 
			
		||||
    <dd class="tag-source"><ul class="dummy"><li>
 | 
			
		||||
        <a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line34">line 34</a>
 | 
			
		||||
        <a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line35">line 35</a>
 | 
			
		||||
    </li></ul></dd>
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
@@ -879,7 +879,7 @@
 | 
			
		||||
    
 | 
			
		||||
    <dt class="tag-source">Source:</dt>
 | 
			
		||||
    <dd class="tag-source"><ul class="dummy"><li>
 | 
			
		||||
        <a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line61">line 61</a>
 | 
			
		||||
        <a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line62">line 62</a>
 | 
			
		||||
    </li></ul></dd>
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
@@ -1057,7 +1057,7 @@
 | 
			
		||||
    
 | 
			
		||||
    <dt class="tag-source">Source:</dt>
 | 
			
		||||
    <dd class="tag-source"><ul class="dummy"><li>
 | 
			
		||||
        <a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line198">line 198</a>
 | 
			
		||||
        <a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line199">line 199</a>
 | 
			
		||||
    </li></ul></dd>
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
@@ -1188,7 +1188,7 @@
 | 
			
		||||
    
 | 
			
		||||
    <dt class="tag-source">Source:</dt>
 | 
			
		||||
    <dd class="tag-source"><ul class="dummy"><li>
 | 
			
		||||
        <a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line158">line 158</a>
 | 
			
		||||
        <a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line159">line 159</a>
 | 
			
		||||
    </li></ul></dd>
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
@@ -1292,7 +1292,7 @@
 | 
			
		||||
    
 | 
			
		||||
    <dt class="tag-source">Source:</dt>
 | 
			
		||||
    <dd class="tag-source"><ul class="dummy"><li>
 | 
			
		||||
        <a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line222">line 222</a>
 | 
			
		||||
        <a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line236">line 236</a>
 | 
			
		||||
    </li></ul></dd>
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
@@ -1396,7 +1396,7 @@
 | 
			
		||||
    
 | 
			
		||||
    <dt class="tag-source">Source:</dt>
 | 
			
		||||
    <dd class="tag-source"><ul class="dummy"><li>
 | 
			
		||||
        <a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line204">line 204</a>
 | 
			
		||||
        <a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line205">line 205</a>
 | 
			
		||||
    </li></ul></dd>
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
@@ -1500,7 +1500,7 @@
 | 
			
		||||
    
 | 
			
		||||
    <dt class="tag-source">Source:</dt>
 | 
			
		||||
    <dd class="tag-source"><ul class="dummy"><li>
 | 
			
		||||
        <a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line216">line 216</a>
 | 
			
		||||
        <a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line230">line 230</a>
 | 
			
		||||
    </li></ul></dd>
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
@@ -1609,7 +1609,7 @@ if some action needs to happen on only one specific instance.
 | 
			
		||||
    
 | 
			
		||||
    <dt class="tag-source">Source:</dt>
 | 
			
		||||
    <dd class="tag-source"><ul class="dummy"><li>
 | 
			
		||||
        <a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line151">line 151</a>
 | 
			
		||||
        <a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line152">line 152</a>
 | 
			
		||||
    </li></ul></dd>
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
@@ -1808,7 +1808,7 @@ otherwise (by e.g. createNoteLink())
 | 
			
		||||
    
 | 
			
		||||
    <dt class="tag-source">Source:</dt>
 | 
			
		||||
    <dd class="tag-source"><ul class="dummy"><li>
 | 
			
		||||
        <a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line143">line 143</a>
 | 
			
		||||
        <a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line144">line 144</a>
 | 
			
		||||
    </li></ul></dd>
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
@@ -1860,6 +1860,117 @@ otherwise (by e.g. createNoteLink())
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
    <h4 class="name" id="isNoteStillLoaded"><span class="type-signature"></span>isNoteStillLoaded<span class="signature">()</span><span class="type-signature"> → {boolean}</span></h4>
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
<div class="description">
 | 
			
		||||
    This method checks whether user navigated away from the note from which the scripts has been started.
 | 
			
		||||
This is necessary because script execution is async and by the time it is finished, the user might have
 | 
			
		||||
already navigated away from this page - the end result would be that script might return data for the wrong
 | 
			
		||||
note.
 | 
			
		||||
</div>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
<dl class="details">
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
    <dt class="tag-source">Source:</dt>
 | 
			
		||||
    <dd class="tag-source"><ul class="dummy"><li>
 | 
			
		||||
        <a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line216">line 216</a>
 | 
			
		||||
    </li></ul></dd>
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
</dl>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
<h5>Returns:</h5>
 | 
			
		||||
 | 
			
		||||
        
 | 
			
		||||
<div class="param-desc">
 | 
			
		||||
    returns true if the original note is still loaded, false if user switched to another
 | 
			
		||||
</div>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
<dl>
 | 
			
		||||
    <dt>
 | 
			
		||||
        Type
 | 
			
		||||
    </dt>
 | 
			
		||||
    <dd>
 | 
			
		||||
        
 | 
			
		||||
<span class="param-type">boolean</span>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    </dd>
 | 
			
		||||
</dl>
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        
 | 
			
		||||
            
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
    <h4 class="name" id="onNoteChange"><span class="type-signature"></span>onNoteChange<span class="signature">(func)</span><span class="type-signature"></span></h4>
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
@@ -1957,7 +2068,7 @@ otherwise (by e.g. createNoteLink())
 | 
			
		||||
    
 | 
			
		||||
    <dt class="tag-source">Source:</dt>
 | 
			
		||||
    <dd class="tag-source"><ul class="dummy"><li>
 | 
			
		||||
        <a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line210">line 210</a>
 | 
			
		||||
        <a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line224">line 224</a>
 | 
			
		||||
    </li></ul></dd>
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
@@ -2088,7 +2199,7 @@ otherwise (by e.g. createNoteLink())
 | 
			
		||||
    
 | 
			
		||||
    <dt class="tag-source">Source:</dt>
 | 
			
		||||
    <dd class="tag-source"><ul class="dummy"><li>
 | 
			
		||||
        <a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line165">line 165</a>
 | 
			
		||||
        <a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line166">line 166</a>
 | 
			
		||||
    </li></ul></dd>
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
@@ -2140,6 +2251,88 @@ otherwise (by e.g. createNoteLink())
 | 
			
		||||
 | 
			
		||||
        
 | 
			
		||||
            
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
    <h4 class="name" id="protectCurrentNote"><span class="type-signature"></span>protectCurrentNote<span class="signature">()</span><span class="type-signature"></span></h4>
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
<dl class="details">
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
    <dt class="tag-source">Source:</dt>
 | 
			
		||||
    <dd class="tag-source"><ul class="dummy"><li>
 | 
			
		||||
        <a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line253">line 253</a>
 | 
			
		||||
    </li></ul></dd>
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
</dl>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        
 | 
			
		||||
            
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
@@ -2196,7 +2389,7 @@ otherwise (by e.g. createNoteLink())
 | 
			
		||||
    
 | 
			
		||||
    <dt class="tag-source">Source:</dt>
 | 
			
		||||
    <dd class="tag-source"><ul class="dummy"><li>
 | 
			
		||||
        <a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line189">line 189</a>
 | 
			
		||||
        <a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line190">line 190</a>
 | 
			
		||||
    </li></ul></dd>
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
@@ -2373,7 +2566,7 @@ Internally this serializes the anonymous function into string and sends it to ba
 | 
			
		||||
    
 | 
			
		||||
    <dt class="tag-source">Source:</dt>
 | 
			
		||||
    <dd class="tag-source"><ul class="dummy"><li>
 | 
			
		||||
        <a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line111">line 111</a>
 | 
			
		||||
        <a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line112">line 112</a>
 | 
			
		||||
    </li></ul></dd>
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
@@ -2526,7 +2719,7 @@ Internally this serializes the anonymous function into string and sends it to ba
 | 
			
		||||
    
 | 
			
		||||
    <dt class="tag-source">Source:</dt>
 | 
			
		||||
    <dd class="tag-source"><ul class="dummy"><li>
 | 
			
		||||
        <a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line228">line 228</a>
 | 
			
		||||
        <a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line242">line 242</a>
 | 
			
		||||
    </li></ul></dd>
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
@@ -2657,7 +2850,7 @@ Internally this serializes the anonymous function into string and sends it to ba
 | 
			
		||||
    
 | 
			
		||||
    <dt class="tag-source">Source:</dt>
 | 
			
		||||
    <dd class="tag-source"><ul class="dummy"><li>
 | 
			
		||||
        <a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line234">line 234</a>
 | 
			
		||||
        <a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line248">line 248</a>
 | 
			
		||||
    </li></ul></dd>
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
@@ -2792,7 +2985,7 @@ Internally this serializes the anonymous function into string and sends it to ba
 | 
			
		||||
    
 | 
			
		||||
    <dt class="tag-source">Source:</dt>
 | 
			
		||||
    <dd class="tag-source"><ul class="dummy"><li>
 | 
			
		||||
        <a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line181">line 181</a>
 | 
			
		||||
        <a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line182">line 182</a>
 | 
			
		||||
    </li></ul></dd>
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
@@ -2927,7 +3120,7 @@ Internally this serializes the anonymous function into string and sends it to ba
 | 
			
		||||
    
 | 
			
		||||
    <dt class="tag-source">Source:</dt>
 | 
			
		||||
    <dd class="tag-source"><ul class="dummy"><li>
 | 
			
		||||
        <a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line173">line 173</a>
 | 
			
		||||
        <a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line174">line 174</a>
 | 
			
		||||
    </li></ul></dd>
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -141,7 +141,123 @@
 | 
			
		||||
 | 
			
		||||
        
 | 
			
		||||
            
 | 
			
		||||
<h4 class="name" id="content"><span class="type-signature"></span>content<span class="type-signature"></span></h4>
 | 
			
		||||
<h4 class="name" id="dateCreated"><span class="type-signature"></span>dateCreated<span class="type-signature"></span></h4>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
<dl class="details">
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
    <dt class="tag-source">Source:</dt>
 | 
			
		||||
    <dd class="tag-source"><ul class="dummy"><li>
 | 
			
		||||
        <a href="entities_note_full.js.html">entities/note_full.js</a>, <a href="entities_note_full.js.html#line14">line 14</a>
 | 
			
		||||
    </li></ul></dd>
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
</dl>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        
 | 
			
		||||
            
 | 
			
		||||
<h4 class="name" id="dateModified"><span class="type-signature"></span>dateModified<span class="type-signature"></span></h4>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
<dl class="details">
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
    <dt class="tag-source">Source:</dt>
 | 
			
		||||
    <dd class="tag-source"><ul class="dummy"><li>
 | 
			
		||||
        <a href="entities_note_full.js.html">entities/note_full.js</a>, <a href="entities_note_full.js.html#line17">line 17</a>
 | 
			
		||||
    </li></ul></dd>
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
</dl>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        
 | 
			
		||||
            
 | 
			
		||||
<h4 class="name" id="noteContent"><span class="type-signature"></span>noteContent<span class="type-signature"></span></h4>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -199,64 +315,6 @@
 | 
			
		||||
 | 
			
		||||
        
 | 
			
		||||
    
 | 
			
		||||
<h4 class="name" id="jsonContent"><span class="type-signature"></span>jsonContent<span class="type-signature"></span></h4>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
<dl class="details">
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
    <dt class="tag-source">Source:</dt>
 | 
			
		||||
    <dd class="tag-source"><ul class="dummy"><li>
 | 
			
		||||
        <a href="entities_note_full.js.html">entities/note_full.js</a>, <a href="entities_note_full.js.html#line16">line 16</a>
 | 
			
		||||
    </li></ul></dd>
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
</dl>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -59,7 +59,9 @@
 | 
			
		||||
    get toString() {
 | 
			
		||||
        return `Attribute(attributeId=${this.attributeId}, type=${this.type}, name=${this.name})`;
 | 
			
		||||
    }
 | 
			
		||||
}</code></pre>
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export default Attribute;</code></pre>
 | 
			
		||||
        </article>
 | 
			
		||||
    </section>
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -36,15 +36,13 @@ class NoteFull extends NoteShort {
 | 
			
		||||
        super(treeCache, row);
 | 
			
		||||
 | 
			
		||||
        /** @param {string} */
 | 
			
		||||
        this.content = row.content;
 | 
			
		||||
        this.noteContent = row.noteContent;
 | 
			
		||||
 | 
			
		||||
        if (this.content !== "" && this.isJson()) {
 | 
			
		||||
            try {
 | 
			
		||||
                /** @param {object} */
 | 
			
		||||
                this.jsonContent = JSON.parse(this.content);
 | 
			
		||||
            }
 | 
			
		||||
            catch(e) {}
 | 
			
		||||
        }
 | 
			
		||||
        /** @param {string} */
 | 
			
		||||
        this.dateCreated = row.dateCreated;
 | 
			
		||||
 | 
			
		||||
        /** @param {string} */
 | 
			
		||||
        this.dateModified = row.dateModified;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -303,7 +303,7 @@
 | 
			
		||||
    
 | 
			
		||||
    <dt class="tag-source">Source:</dt>
 | 
			
		||||
    <dd class="tag-source"><ul class="dummy"><li>
 | 
			
		||||
        <a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line48">line 48</a>
 | 
			
		||||
        <a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line49">line 49</a>
 | 
			
		||||
    </li></ul></dd>
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -35,6 +35,7 @@ import treeCache from './tree_cache.js';
 | 
			
		||||
import noteDetailService from './note_detail.js';
 | 
			
		||||
import noteTypeService from './note_type.js';
 | 
			
		||||
import noteTooltipService from './note_tooltip.js';
 | 
			
		||||
import protectedSessionService from'./protected_session.js';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * This is the main frontend API interface for scripts. It's published in the local "api" object.
 | 
			
		||||
@@ -70,7 +71,7 @@ function FrontendScriptApi(startNote, currentNote, originEntity = null) {
 | 
			
		||||
    this.activateNewNote = async notePath => {
 | 
			
		||||
        await treeService.reload();
 | 
			
		||||
 | 
			
		||||
        await treeService.activateNote(notePath, true);
 | 
			
		||||
        await treeService.activateNote(notePath, noteDetailService.focusAndSelectTitle);
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
@@ -231,6 +232,19 @@ function FrontendScriptApi(startNote, currentNote, originEntity = null) {
 | 
			
		||||
     */
 | 
			
		||||
    this.getCurrentNoteContent = noteDetailService.getCurrentNoteContent;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * This method checks whether user navigated away from the note from which the scripts has been started.
 | 
			
		||||
     * This is necessary because script execution is async and by the time it is finished, the user might have
 | 
			
		||||
     * already navigated away from this page - the end result would be that script might return data for the wrong
 | 
			
		||||
     * note.
 | 
			
		||||
     *
 | 
			
		||||
     * @method
 | 
			
		||||
     * @return {boolean} returns true if the original note is still loaded, false if user switched to another
 | 
			
		||||
     */
 | 
			
		||||
    this.isNoteStillLoaded = () => {
 | 
			
		||||
        return this.originEntity.noteId === noteDetailService.getCurrentNoteId();
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @method
 | 
			
		||||
     * @param {function} func - callback called on note change as user is typing (not necessarily tied to save event)
 | 
			
		||||
@@ -259,7 +273,12 @@ function FrontendScriptApi(startNote, currentNote, originEntity = null) {
 | 
			
		||||
     * @method
 | 
			
		||||
     * @param {object} $el - jquery object on which to setup the tooltip
 | 
			
		||||
     */
 | 
			
		||||
    this.setupElementTooltip = noteTooltipService.setupElementTooltip
 | 
			
		||||
    this.setupElementTooltip = noteTooltipService.setupElementTooltip;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @method
 | 
			
		||||
     */
 | 
			
		||||
    this.protectCurrentNote = protectedSessionService.protectNoteAndSendToServer;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export default FrontendScriptApi;</code></pre>
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										18
									
								
								electron.js
									
									
									
									
									
								
							
							
						
						
									
										18
									
								
								electron.js
									
									
									
									
									
								
							@@ -7,6 +7,7 @@ const cls = require('./src/services/cls');
 | 
			
		||||
const url = require("url");
 | 
			
		||||
const port = require('./src/services/port');
 | 
			
		||||
const appIconService = require('./src/services/app_icon');
 | 
			
		||||
const windowStateKeeper = require('electron-window-state');
 | 
			
		||||
 | 
			
		||||
const app = electron.app;
 | 
			
		||||
const globalShortcut = electron.globalShortcut;
 | 
			
		||||
@@ -28,14 +29,23 @@ function onClosed() {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
async function createMainWindow() {
 | 
			
		||||
    let mainWindowState = windowStateKeeper({
 | 
			
		||||
        // default window width & height so it's usable on 1600 * 900 display (including some extra panels etc.)
 | 
			
		||||
        defaultWidth: 1200,
 | 
			
		||||
        defaultHeight: 800
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    const win = new electron.BrowserWindow({
 | 
			
		||||
        // initial window width & height so it's usable on 1600 * 900 display (including some extra panels etc.)
 | 
			
		||||
        width: 1200,
 | 
			
		||||
        height: 800,
 | 
			
		||||
        x: mainWindowState.x,
 | 
			
		||||
        y: mainWindowState.y,
 | 
			
		||||
        width: mainWindowState.width,
 | 
			
		||||
        height: mainWindowState.height,
 | 
			
		||||
        title: 'Trilium Notes',
 | 
			
		||||
        icon: path.join(__dirname, 'src/public/images/app-icons/png/256x256.png')
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    mainWindowState.manage(win);
 | 
			
		||||
 | 
			
		||||
    win.setMenu(null);
 | 
			
		||||
    win.loadURL('http://localhost:' + await port);
 | 
			
		||||
    win.on('closed', onClosed);
 | 
			
		||||
@@ -81,7 +91,7 @@ app.on('ready', async () => {
 | 
			
		||||
        const dateNoteService = require('./src/services/date_notes');
 | 
			
		||||
        const dateUtils = require('./src/services/date_utils');
 | 
			
		||||
 | 
			
		||||
        const parentNote = await dateNoteService.getDateNote(dateUtils.nowDate());
 | 
			
		||||
        const parentNote = await dateNoteService.getDateNote(dateUtils.nowLocalDate());
 | 
			
		||||
 | 
			
		||||
        // window may be hidden / not in focus
 | 
			
		||||
        mainWindow.focus();
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										5
									
								
								issue_template.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								issue_template.md
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,5 @@
 | 
			
		||||
For bug reports, please mention **version of the application** and include **log files** from following location:
 | 
			
		||||
 | 
			
		||||
* `/home/[user]/.local/share/trilium-data/log` for Linux
 | 
			
		||||
* `C:\Users\[user]\AppData\Roaming\trilium-data\log` for Windows Vista and up
 | 
			
		||||
* `/Users/[user]/Library/Application Support/trilium-data/log` for Mac OS
 | 
			
		||||
							
								
								
									
										3035
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										3035
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										54
									
								
								package.json
									
									
									
									
									
								
							
							
						
						
									
										54
									
								
								package.json
									
									
									
									
									
								
							@@ -2,7 +2,7 @@
 | 
			
		||||
  "name": "trilium",
 | 
			
		||||
  "productName": "Trilium Notes",
 | 
			
		||||
  "description": "Trilium Notes",
 | 
			
		||||
  "version": "0.27.0-beta",
 | 
			
		||||
  "version": "0.30.3-beta",
 | 
			
		||||
  "license": "AGPL-3.0-only",
 | 
			
		||||
  "main": "electron.js",
 | 
			
		||||
  "bin": {
 | 
			
		||||
@@ -17,7 +17,8 @@
 | 
			
		||||
    "start-electron": "electron . --disable-gpu",
 | 
			
		||||
    "build-backend-docs": "jsdoc -c jsdoc-conf.json -d ./docs/backend_api src/entities/*.js src/services/backend_script_api.js",
 | 
			
		||||
    "build-frontend-docs": "jsdoc -c jsdoc-conf.json -d ./docs/frontend_api src/public/javascripts/entities/*.js src/public/javascripts/services/frontend_script_api.js",
 | 
			
		||||
    "build-docs": "npm run build-backend-docs && npm run build-frontend-docs"
 | 
			
		||||
    "build-docs": "npm run build-backend-docs && npm run build-frontend-docs",
 | 
			
		||||
    "postinstall": "electron-builder install-app-deps"
 | 
			
		||||
  },
 | 
			
		||||
  "dependencies": {
 | 
			
		||||
    "async-mutex": "0.1.3",
 | 
			
		||||
@@ -25,54 +26,59 @@
 | 
			
		||||
    "body-parser": "1.18.3",
 | 
			
		||||
    "cls-hooked": "4.2.2",
 | 
			
		||||
    "commonmark": "0.28.1",
 | 
			
		||||
    "cookie-parser": "1.4.3",
 | 
			
		||||
    "cookie-parser": "1.4.4",
 | 
			
		||||
    "debug": "4.1.1",
 | 
			
		||||
    "ejs": "2.6.1",
 | 
			
		||||
    "electron-debug": "2.0.0",
 | 
			
		||||
    "electron-dl": "1.12.0",
 | 
			
		||||
    "electron-debug": "2.1.0",
 | 
			
		||||
    "electron-dl": "1.13.0",
 | 
			
		||||
    "electron-in-page-search": "1.3.2",
 | 
			
		||||
    "electron-window-state": "^5.0.3",
 | 
			
		||||
    "express": "4.16.4",
 | 
			
		||||
    "express-session": "1.15.6",
 | 
			
		||||
    "file-type": "10.8.0",
 | 
			
		||||
    "fs-extra": "7.0.1",
 | 
			
		||||
    "get-port": "4.1.0",
 | 
			
		||||
    "helmet": "3.15.0",
 | 
			
		||||
    "get-port": "4.2.0",
 | 
			
		||||
    "helmet": "3.15.1",
 | 
			
		||||
    "html": "1.0.0",
 | 
			
		||||
    "image-type": "3.0.0",
 | 
			
		||||
    "imagemin": "6.0.0",
 | 
			
		||||
    "imagemin": "6.1.0",
 | 
			
		||||
    "imagemin-giflossy": "5.1.10",
 | 
			
		||||
    "imagemin-mozjpeg": "8.0.0",
 | 
			
		||||
    "imagemin-pngquant": "6.0.0",
 | 
			
		||||
    "imagemin-pngquant": "7.0.0",
 | 
			
		||||
    "ini": "1.3.5",
 | 
			
		||||
    "jimp": "0.6.0",
 | 
			
		||||
    "mime-types": "^2.1.21",
 | 
			
		||||
    "moment": "2.23.0",
 | 
			
		||||
    "mime-types": "^2.1.22",
 | 
			
		||||
    "moment": "2.24.0",
 | 
			
		||||
    "multer": "1.4.1",
 | 
			
		||||
    "node-abi": "2.5.1",
 | 
			
		||||
    "node-abi": "2.7.1",
 | 
			
		||||
    "open": "0.0.5",
 | 
			
		||||
    "rand-token": "0.4.0",
 | 
			
		||||
    "rcedit": "1.1.1",
 | 
			
		||||
    "rimraf": "2.6.2",
 | 
			
		||||
    "rimraf": "2.6.3",
 | 
			
		||||
    "sanitize-filename": "1.6.1",
 | 
			
		||||
    "sax": "^1.2.4",
 | 
			
		||||
    "semver": "^5.6.0",
 | 
			
		||||
    "serve-favicon": "2.5.0",
 | 
			
		||||
    "session-file-store": "1.2.0",
 | 
			
		||||
    "simple-node-logger": "18.12.21",
 | 
			
		||||
    "sqlite": "3.0.0",
 | 
			
		||||
    "tar-stream": "1.6.2",
 | 
			
		||||
    "turndown": "5.0.1",
 | 
			
		||||
    "simple-node-logger": "18.12.22",
 | 
			
		||||
    "sqlite": "3.0.2",
 | 
			
		||||
    "tar-stream": "2.0.1",
 | 
			
		||||
    "turndown": "5.0.3",
 | 
			
		||||
    "unescape": "1.0.1",
 | 
			
		||||
    "ws": "6.1.2",
 | 
			
		||||
    "ws": "6.1.4",
 | 
			
		||||
    "xml2js": "0.4.19"
 | 
			
		||||
  },
 | 
			
		||||
  "devDependencies": {
 | 
			
		||||
    "devtron": "1.4.0",
 | 
			
		||||
    "electron": "4.0.0",
 | 
			
		||||
    "electron-compile": "6.4.3",
 | 
			
		||||
    "electron-packager": "13.0.1",
 | 
			
		||||
    "electron-rebuild": "1.8.2",
 | 
			
		||||
    "electron": "4.0.3",
 | 
			
		||||
    "electron-builder": "20.38.5",
 | 
			
		||||
    "electron-compile": "6.4.4",
 | 
			
		||||
    "electron-installer-debian": "^1.1.1",
 | 
			
		||||
    "electron-packager": "13.1.0",
 | 
			
		||||
    "electron-rebuild": "1.8.4",
 | 
			
		||||
    "lorem-ipsum": "1.0.6",
 | 
			
		||||
    "tape": "4.9.1",
 | 
			
		||||
    "xo": "0.23.0"
 | 
			
		||||
    "tape": "4.10.1",
 | 
			
		||||
    "xo": "0.24.0"
 | 
			
		||||
  },
 | 
			
		||||
  "xo": {
 | 
			
		||||
    "envs": [
 | 
			
		||||
 
 | 
			
		||||
@@ -39,7 +39,7 @@ app.use((req, res, next) => {
 | 
			
		||||
    });
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
app.use(bodyParser.json({limit: '50mb'}));
 | 
			
		||||
app.use(bodyParser.json({limit: '500mb'}));
 | 
			
		||||
app.use(bodyParser.urlencoded({extended: false}));
 | 
			
		||||
app.use(cookieParser());
 | 
			
		||||
app.use(express.static(path.join(__dirname, 'public')));
 | 
			
		||||
@@ -63,6 +63,8 @@ app.use(favicon(__dirname + '/public/images/app-icons/win/icon.ico'));
 | 
			
		||||
 | 
			
		||||
require('./routes/routes').register(app);
 | 
			
		||||
 | 
			
		||||
require('./routes/custom').register(app);
 | 
			
		||||
 | 
			
		||||
// catch 404 and forward to error handler
 | 
			
		||||
app.use((req, res, next) => {
 | 
			
		||||
    const err = new Error('Router not found for request ' + req.url);
 | 
			
		||||
 
 | 
			
		||||
@@ -8,7 +8,10 @@ class Entity {
 | 
			
		||||
     */
 | 
			
		||||
    constructor(row = {}) {
 | 
			
		||||
        for (const key in row) {
 | 
			
		||||
            this[key] = row[key];
 | 
			
		||||
            // ! is used when joint-fetching notes and note_contents objects for performance
 | 
			
		||||
            if (!key.startsWith('!')) {
 | 
			
		||||
                this[key] = row[key];
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if ('isDeleted' in this) {
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,5 @@
 | 
			
		||||
const Note = require('../entities/note');
 | 
			
		||||
const NoteContent = require('../entities/note_content');
 | 
			
		||||
const NoteRevision = require('../entities/note_revision');
 | 
			
		||||
const Link = require('../entities/link');
 | 
			
		||||
const Branch = require('../entities/branch');
 | 
			
		||||
@@ -12,10 +13,12 @@ const ENTITY_NAME_TO_ENTITY = {
 | 
			
		||||
    "attributes": Attribute,
 | 
			
		||||
    "branches": Branch,
 | 
			
		||||
    "notes": Note,
 | 
			
		||||
    "note_contents": NoteContent,
 | 
			
		||||
    "note_revisions": NoteRevision,
 | 
			
		||||
    "recent_notes": RecentNote,
 | 
			
		||||
    "options": Option,
 | 
			
		||||
    "api_tokens": ApiToken
 | 
			
		||||
    "api_tokens": ApiToken,
 | 
			
		||||
    "links": Link
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
function getEntityFromEntityName(entityName) {
 | 
			
		||||
@@ -47,6 +50,9 @@ function createEntityFromRow(row) {
 | 
			
		||||
    else if (row.branchId) {
 | 
			
		||||
        entity = new Branch(row);
 | 
			
		||||
    }
 | 
			
		||||
    else if (row.noteContentId) {
 | 
			
		||||
        entity = new NoteContent(row);
 | 
			
		||||
    }
 | 
			
		||||
    else if (row.noteId) {
 | 
			
		||||
        entity = new Note(row);
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -2,6 +2,7 @@
 | 
			
		||||
 | 
			
		||||
const Entity = require('./entity');
 | 
			
		||||
const Attribute = require('./attribute');
 | 
			
		||||
const NoteContent = require('./note_content');
 | 
			
		||||
const protectedSessionService = require('../services/protected_session');
 | 
			
		||||
const repository = require('../services/repository');
 | 
			
		||||
const sql = require('../services/sql');
 | 
			
		||||
@@ -12,6 +13,8 @@ const LABEL_DEFINITION = 'label-definition';
 | 
			
		||||
const RELATION = 'relation';
 | 
			
		||||
const RELATION_DEFINITION = 'relation-definition';
 | 
			
		||||
 | 
			
		||||
const STRING_MIME_TYPES = ["application/x-javascript"];
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * This represents a Note which is a central object in the Trilium Notes project.
 | 
			
		||||
 *
 | 
			
		||||
@@ -19,7 +22,6 @@ const RELATION_DEFINITION = 'relation-definition';
 | 
			
		||||
 * @property {string} type - one of "text", "code", "file" or "render"
 | 
			
		||||
 * @property {string} mime - MIME type, e.g. "text/html"
 | 
			
		||||
 * @property {string} title - note title
 | 
			
		||||
 * @property {string} content - note content - e.g. HTML text for text notes, file payload for files
 | 
			
		||||
 * @property {boolean} isProtected - true if note is protected
 | 
			
		||||
 * @property {boolean} isDeleted - true if note is deleted
 | 
			
		||||
 * @property {string} dateCreated
 | 
			
		||||
@@ -30,7 +32,7 @@ const RELATION_DEFINITION = 'relation-definition';
 | 
			
		||||
class Note extends Entity {
 | 
			
		||||
    static get entityName() { return "notes"; }
 | 
			
		||||
    static get primaryKeyName() { return "noteId"; }
 | 
			
		||||
    static get hashedProperties() { return ["noteId", "title", "content", "type", "isProtected", "isDeleted"]; }
 | 
			
		||||
    static get hashedProperties() { return ["noteId", "title", "type", "isProtected", "isDeleted"]; }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @param row - object containing database row from "notes" table
 | 
			
		||||
@@ -47,19 +49,64 @@ class Note extends Entity {
 | 
			
		||||
        if (this.isProtected && this.noteId) {
 | 
			
		||||
            this.isContentAvailable = protectedSessionService.isProtectedSessionAvailable();
 | 
			
		||||
 | 
			
		||||
            protectedSessionService.decryptNote(this);
 | 
			
		||||
            if (this.isContentAvailable) {
 | 
			
		||||
                protectedSessionService.decryptNote(this);
 | 
			
		||||
            }
 | 
			
		||||
            else {
 | 
			
		||||
                // saving ciphertexts in case we do want to update protected note outside of protected session
 | 
			
		||||
                // (which is allowed)
 | 
			
		||||
                this.titleCipherText = this.title;
 | 
			
		||||
                this.title = "[protected]";
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this.setContent(this.content);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    setContent(content) {
 | 
			
		||||
        this.content = content;
 | 
			
		||||
    /** @returns {Promise<NoteContent>} */
 | 
			
		||||
    async getNoteContent() {
 | 
			
		||||
        if (!this.noteContent) {
 | 
			
		||||
            this.noteContent = await repository.getEntity(`SELECT * FROM note_contents WHERE noteId = ?`, [this.noteId]);
 | 
			
		||||
 | 
			
		||||
        try {
 | 
			
		||||
            this.jsonContent = JSON.parse(this.content);
 | 
			
		||||
            if (!this.noteContent) {
 | 
			
		||||
                throw new Error("Note content not found for noteId=" + this.noteId);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (this.isStringNote()) {
 | 
			
		||||
                this.noteContent.content = this.noteContent.content.toString("UTF-8");
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        catch(e) {}
 | 
			
		||||
 | 
			
		||||
        return this.noteContent;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /** @returns {Promise<*>} */
 | 
			
		||||
    async getContent() {
 | 
			
		||||
        const noteContent = await this.getNoteContent();
 | 
			
		||||
 | 
			
		||||
        return noteContent.content;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /** @returns {Promise<*>} */
 | 
			
		||||
    async getJsonContent() {
 | 
			
		||||
        const content = await this.getContent();
 | 
			
		||||
 | 
			
		||||
        return JSON.parse(content);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /** @returns {Promise} */
 | 
			
		||||
    async setContent(content) {
 | 
			
		||||
        if (!this.noteContent) {
 | 
			
		||||
            // make sure it is loaded
 | 
			
		||||
            await this.getNoteContent();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this.noteContent.content = content;
 | 
			
		||||
 | 
			
		||||
        await this.noteContent.save();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /** @returns {Promise} */
 | 
			
		||||
    async setJsonContent(content) {
 | 
			
		||||
        await this.setContent(JSON.stringify(content));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /** @returns {boolean} true if this note is the root of the note tree. Root note has "root" noteId */
 | 
			
		||||
@@ -85,6 +132,13 @@ class Note extends Entity {
 | 
			
		||||
        return (this.type === "code" || this.type === "file" || this.type === "render") && this.mime === "text/html";
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /** @returns {boolean} true if the note has string content (not binary) */
 | 
			
		||||
    isStringNote() {
 | 
			
		||||
        return ["text", "code", "relation-map", "search"].includes(this.type)
 | 
			
		||||
            || this.mime.startsWith('text/')
 | 
			
		||||
            || STRING_MIME_TYPES.includes(this.mime);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /** @returns {string} JS script environment - either "frontend" or "backend" */
 | 
			
		||||
    getScriptEnv() {
 | 
			
		||||
        if (this.isHtml() || (this.isJavaScript() && this.mime.endsWith('env=frontend'))) {
 | 
			
		||||
@@ -601,13 +655,6 @@ class Note extends Entity {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    beforeSaving() {
 | 
			
		||||
        if (this.isJson() && this.jsonContent) {
 | 
			
		||||
            this.content = JSON.stringify(this.jsonContent, null, '\t');
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // we do this here because encryption needs the note ID for the IV
 | 
			
		||||
        this.generateIdIfNecessary();
 | 
			
		||||
 | 
			
		||||
        if (!this.isDeleted) {
 | 
			
		||||
            this.isDeleted = false;
 | 
			
		||||
        }
 | 
			
		||||
@@ -626,12 +673,19 @@ class Note extends Entity {
 | 
			
		||||
    // cannot be static!
 | 
			
		||||
    updatePojo(pojo) {
 | 
			
		||||
        if (pojo.isProtected) {
 | 
			
		||||
            protectedSessionService.encryptNote(pojo);
 | 
			
		||||
            if (this.isContentAvailable) {
 | 
			
		||||
                protectedSessionService.encryptNote(pojo);
 | 
			
		||||
            }
 | 
			
		||||
            else {
 | 
			
		||||
                // updating protected note outside of protected session means we will keep original ciphertexts
 | 
			
		||||
                pojo.title = pojo.titleCipherText;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        delete pojo.jsonContent;
 | 
			
		||||
        delete pojo.isContentAvailable;
 | 
			
		||||
        delete pojo.__attributeCache;
 | 
			
		||||
        delete pojo.titleCipherText;
 | 
			
		||||
        delete pojo.noteContent;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										96
									
								
								src/entities/note_content.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										96
									
								
								src/entities/note_content.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,96 @@
 | 
			
		||||
"use strict";
 | 
			
		||||
 | 
			
		||||
const Entity = require('./entity');
 | 
			
		||||
const protectedSessionService = require('../services/protected_session');
 | 
			
		||||
const repository = require('../services/repository');
 | 
			
		||||
const dateUtils = require('../services/date_utils');
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * This represents a Note which is a central object in the Trilium Notes project.
 | 
			
		||||
 *
 | 
			
		||||
 * @property {string} noteContentId - primary key
 | 
			
		||||
 * @property {string} noteId - reference to owning note
 | 
			
		||||
 * @property {boolean} isProtected - true if note content is protected
 | 
			
		||||
 * @property {blob} content - note content - e.g. HTML text for text notes, file payload for files
 | 
			
		||||
 * @property {string} dateCreated
 | 
			
		||||
 * @property {string} dateModified
 | 
			
		||||
 *
 | 
			
		||||
 * @extends Entity
 | 
			
		||||
 */
 | 
			
		||||
class NoteContent extends Entity {
 | 
			
		||||
    static get entityName() {
 | 
			
		||||
        return "note_contents";
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    static get primaryKeyName() {
 | 
			
		||||
        return "noteContentId";
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    static get hashedProperties() {
 | 
			
		||||
        return ["noteContentId", "noteId", "isProtected", "content"];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @param row - object containing database row from "note_contents" table
 | 
			
		||||
     */
 | 
			
		||||
    constructor(row) {
 | 
			
		||||
        super(row);
 | 
			
		||||
 | 
			
		||||
        this.isProtected = !!this.isProtected;
 | 
			
		||||
        /* true if content (meaning any kind of potentially encrypted content) is either not encrypted
 | 
			
		||||
         * or encrypted, but with available protected session (so effectively decrypted) */
 | 
			
		||||
        this.isContentAvailable = true;
 | 
			
		||||
 | 
			
		||||
        // check if there's noteContentId, otherwise this is a new entity which wasn't encrypted yet
 | 
			
		||||
        if (this.isProtected && this.noteContentId) {
 | 
			
		||||
            this.isContentAvailable = protectedSessionService.isProtectedSessionAvailable();
 | 
			
		||||
 | 
			
		||||
            if (this.isContentAvailable) {
 | 
			
		||||
                protectedSessionService.decryptNoteContent(this);
 | 
			
		||||
            }
 | 
			
		||||
            else {
 | 
			
		||||
                // saving ciphertexts in case we do want to update protected note outside of protected session
 | 
			
		||||
                // (which is allowed)
 | 
			
		||||
                this.contentCipherText = this.content;
 | 
			
		||||
                this.content = "";
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @returns {Promise<Note>}
 | 
			
		||||
     */
 | 
			
		||||
    async getNote() {
 | 
			
		||||
        return await repository.getNote(this.noteId);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    beforeSaving() {
 | 
			
		||||
        if (!this.dateCreated) {
 | 
			
		||||
            this.dateCreated = dateUtils.nowDate();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        super.beforeSaving();
 | 
			
		||||
 | 
			
		||||
        if (this.isChanged) {
 | 
			
		||||
            this.dateModified = dateUtils.nowDate();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // cannot be static!
 | 
			
		||||
    updatePojo(pojo) {
 | 
			
		||||
        if (pojo.isProtected) {
 | 
			
		||||
            if (this.isContentAvailable) {
 | 
			
		||||
                protectedSessionService.encryptNoteContent(pojo);
 | 
			
		||||
            }
 | 
			
		||||
            else {
 | 
			
		||||
                // updating protected note outside of protected session means we will keep original ciphertext
 | 
			
		||||
                pojo.content = pojo.contentCipherText;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        delete pojo.isContentAvailable;
 | 
			
		||||
        delete pojo.contentCipherText;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
module.exports = NoteContent;
 | 
			
		||||
							
								
								
									
										
											BIN
										
									
								
								src/public/images/app-icons/ios/apple-touch-icon.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								src/public/images/app-icons/ios/apple-touch-icon.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| 
		 After Width: | Height: | Size: 3.4 KiB  | 
@@ -8,11 +8,11 @@ import optionsDialog from './dialogs/options.js';
 | 
			
		||||
import sqlConsoleDialog from './dialogs/sql_console.js';
 | 
			
		||||
import markdownImportDialog from './dialogs/markdown_import.js';
 | 
			
		||||
import exportDialog from './dialogs/export.js';
 | 
			
		||||
import importDialog from './dialogs/import.js';
 | 
			
		||||
 | 
			
		||||
import cloning from './services/cloning.js';
 | 
			
		||||
import contextMenu from './services/tree_context_menu.js';
 | 
			
		||||
import dragAndDropSetup from './services/drag_and_drop.js';
 | 
			
		||||
import exportService from './services/export.js';
 | 
			
		||||
import link from './services/link.js';
 | 
			
		||||
import messagingService from './services/messaging.js';
 | 
			
		||||
import noteDetailService from './services/note_detail.js';
 | 
			
		||||
@@ -36,6 +36,8 @@ import hoistedNoteService from './services/hoisted_note.js';
 | 
			
		||||
import noteTypeService from './services/note_type.js';
 | 
			
		||||
import linkService from './services/link.js';
 | 
			
		||||
import noteAutocompleteService from './services/note_autocomplete.js';
 | 
			
		||||
import macInit from './services/mac_init.js';
 | 
			
		||||
import cssLoader from './services/css_loader.js';
 | 
			
		||||
 | 
			
		||||
// required for CKEditor image upload plugin
 | 
			
		||||
window.glob.getCurrentNode = treeService.getCurrentNode;
 | 
			
		||||
@@ -78,6 +80,10 @@ window.onerror = function (msg, url, lineNo, columnNo, error) {
 | 
			
		||||
    return false;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
for (const appCssNoteId of window.appCssNoteIds) {
 | 
			
		||||
    cssLoader.requireCss(`/api/notes/download/${appCssNoteId}`);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const wikiBaseUrl = "https://github.com/zadam/trilium/wiki/";
 | 
			
		||||
 | 
			
		||||
$(document).on("click", "button[data-help-page]", e => {
 | 
			
		||||
@@ -88,8 +94,19 @@ $(document).on("click", "button[data-help-page]", e => {
 | 
			
		||||
 | 
			
		||||
$("#logout-button").toggle(!utils.isElectron());
 | 
			
		||||
 | 
			
		||||
$("#logout-button").click(() => {
 | 
			
		||||
    const $logoutForm = $('<form action="logout" method="POST">');
 | 
			
		||||
 | 
			
		||||
    $("body").append($logoutForm);
 | 
			
		||||
    $logoutForm.submit();
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
$("#tree").on("click", ".unhoist-button", hoistedNoteService.unhoist);
 | 
			
		||||
 | 
			
		||||
$("body").on("click", "a.external", function () {
 | 
			
		||||
    window.open($(this).attr("href"), '_blank');
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
if (utils.isElectron()) {
 | 
			
		||||
    require('electron').ipcRenderer.on('create-day-sub-note', async function(event, parentNoteId) {
 | 
			
		||||
        // this might occur when day note had to be created
 | 
			
		||||
@@ -110,28 +127,6 @@ if (utils.isElectron()) {
 | 
			
		||||
    });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function exec(cmd) {
 | 
			
		||||
    document.execCommand(cmd);
 | 
			
		||||
 | 
			
		||||
    return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
if (utils.isElectron() && utils.isMac()) {
 | 
			
		||||
    utils.bindShortcut('ctrl+c', () => exec("copy"));
 | 
			
		||||
    utils.bindShortcut('ctrl+v', () => exec('paste'));
 | 
			
		||||
    utils.bindShortcut('ctrl+x', () => exec('cut'));
 | 
			
		||||
    utils.bindShortcut('ctrl+a', () => exec('selectAll'));
 | 
			
		||||
    utils.bindShortcut('ctrl+z', () => exec('undo'));
 | 
			
		||||
    utils.bindShortcut('ctrl+y', () => exec('redo'));
 | 
			
		||||
 | 
			
		||||
    utils.bindShortcut('meta+c', () => exec("copy"));
 | 
			
		||||
    utils.bindShortcut('meta+v', () => exec('paste'));
 | 
			
		||||
    utils.bindShortcut('meta+x', () => exec('cut'));
 | 
			
		||||
    utils.bindShortcut('meta+a', () => exec('selectAll'));
 | 
			
		||||
    utils.bindShortcut('meta+z', () => exec('undo'));
 | 
			
		||||
    utils.bindShortcut('meta+y', () => exec('redo'));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
$("#export-note-button").click(function () {
 | 
			
		||||
    if ($(this).hasClass("disabled")) {
 | 
			
		||||
        return;
 | 
			
		||||
@@ -140,6 +135,16 @@ $("#export-note-button").click(function () {
 | 
			
		||||
    exportDialog.showDialog('single');
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
$('[data-toggle="tooltip"]').tooltip({
 | 
			
		||||
    html: true
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
$("#import-files-button").click(importDialog.showDialog);
 | 
			
		||||
 | 
			
		||||
macInit.init();
 | 
			
		||||
 | 
			
		||||
searchNotesService.init(); // should be in front of treeService since that one manipulates address bar hash
 | 
			
		||||
 | 
			
		||||
treeService.showTree();
 | 
			
		||||
 | 
			
		||||
entrypoints.registerEntrypoints();
 | 
			
		||||
 
 | 
			
		||||
@@ -97,7 +97,7 @@ function AttributesModel() {
 | 
			
		||||
        await showAttributes(attributes);
 | 
			
		||||
 | 
			
		||||
        // attribute might not be rendered immediatelly so could not focus
 | 
			
		||||
        setTimeout(() => $(".attribute-type-select:last").focus(), 100);
 | 
			
		||||
        setTimeout(() => $(".attribute-type-select:last").focus(), 1000);
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    this.deleteAttribute = function(data, event) {
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,9 @@
 | 
			
		||||
import treeService from '../services/tree.js';
 | 
			
		||||
import treeUtils from "../services/tree_utils.js";
 | 
			
		||||
import exportService from "../services/export.js";
 | 
			
		||||
import utils from "../services/utils.js";
 | 
			
		||||
import protectedSessionHolder from "../services/protected_session_holder.js";
 | 
			
		||||
import messagingService from "../services/messaging.js";
 | 
			
		||||
import infoService from "../services/info.js";
 | 
			
		||||
 | 
			
		||||
const $dialog = $("#export-dialog");
 | 
			
		||||
const $form = $("#export-form");
 | 
			
		||||
@@ -9,10 +12,25 @@ const $subtreeFormats = $("#export-subtree-formats");
 | 
			
		||||
const $singleFormats = $("#export-single-formats");
 | 
			
		||||
const $subtreeType = $("#export-type-subtree");
 | 
			
		||||
const $singleType = $("#export-type-single");
 | 
			
		||||
const $exportProgressWrapper = $("#export-progress-count-wrapper");
 | 
			
		||||
const $exportProgressCount = $("#export-progress-count");
 | 
			
		||||
const $exportButton = $("#export-button");
 | 
			
		||||
const $opmlVersions = $("#opml-versions");
 | 
			
		||||
 | 
			
		||||
let exportId = '';
 | 
			
		||||
 | 
			
		||||
async function showDialog(defaultType) {
 | 
			
		||||
    // each opening of the dialog resets the exportId so we don't associate it with previous exports anymore
 | 
			
		||||
    exportId = '';
 | 
			
		||||
    $exportButton.removeAttr("disabled");
 | 
			
		||||
    $exportProgressWrapper.hide();
 | 
			
		||||
    $exportProgressCount.text('0');
 | 
			
		||||
 | 
			
		||||
    if (defaultType === 'subtree') {
 | 
			
		||||
        $subtreeType.prop("checked", true).change();
 | 
			
		||||
 | 
			
		||||
        // to show/hide OPML versions
 | 
			
		||||
        $("input[name=export-subtree-format]:checked").change();
 | 
			
		||||
    }
 | 
			
		||||
    else if (defaultType === 'single') {
 | 
			
		||||
        $singleType.prop("checked", true).change();
 | 
			
		||||
@@ -21,6 +39,8 @@ async function showDialog(defaultType) {
 | 
			
		||||
        throw new Error("Unrecognized type " + defaultType);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    $("#opml-v2").prop("checked", true); // setting default
 | 
			
		||||
 | 
			
		||||
    glob.activeDialog = $dialog;
 | 
			
		||||
 | 
			
		||||
    $dialog.modal();
 | 
			
		||||
@@ -32,6 +52,9 @@ async function showDialog(defaultType) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
$form.submit(() => {
 | 
			
		||||
    // disabling so export can't be triggered again
 | 
			
		||||
    $exportButton.attr("disabled", "disabled");
 | 
			
		||||
 | 
			
		||||
    const exportType = $dialog.find("input[name='export-type']:checked").val();
 | 
			
		||||
 | 
			
		||||
    if (!exportType) {
 | 
			
		||||
@@ -44,15 +67,23 @@ $form.submit(() => {
 | 
			
		||||
        ? $("input[name=export-subtree-format]:checked").val()
 | 
			
		||||
        : $("input[name=export-single-format]:checked").val();
 | 
			
		||||
 | 
			
		||||
    const exportVersion = exportFormat === 'opml' ? $dialog.find("input[name='opml-version']:checked").val() : "1.0";
 | 
			
		||||
 | 
			
		||||
    const currentNode = treeService.getCurrentNode();
 | 
			
		||||
 | 
			
		||||
    exportService.exportBranch(currentNode.data.branchId, exportType, exportFormat);
 | 
			
		||||
 | 
			
		||||
    $dialog.modal('hide');
 | 
			
		||||
    exportBranch(currentNode.data.branchId, exportType, exportFormat, exportVersion);
 | 
			
		||||
 | 
			
		||||
    return false;
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
function exportBranch(branchId, type, format, version) {
 | 
			
		||||
    exportId = utils.randomString(10);
 | 
			
		||||
 | 
			
		||||
    const url = utils.getHost() + `/api/notes/${branchId}/export/${type}/${format}/${version}/${exportId}?protectedSessionId=` + encodeURIComponent(protectedSessionHolder.getProtectedSessionId());
 | 
			
		||||
 | 
			
		||||
    utils.download(url);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
$('input[name=export-type]').change(function () {
 | 
			
		||||
    if (this.value === 'subtree') {
 | 
			
		||||
        if ($("input[name=export-subtree-format]:checked").length === 0) {
 | 
			
		||||
@@ -72,6 +103,39 @@ $('input[name=export-type]').change(function () {
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
$('input[name=export-subtree-format]').change(function () {
 | 
			
		||||
    if (this.value === 'opml') {
 | 
			
		||||
        $opmlVersions.slideDown();
 | 
			
		||||
    }
 | 
			
		||||
    else {
 | 
			
		||||
        $opmlVersions.slideUp();
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
messagingService.subscribeToMessages(async message => {
 | 
			
		||||
    if (message.type === 'export-error') {
 | 
			
		||||
        infoService.showError(message.message);
 | 
			
		||||
        $dialog.modal('hide');
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (!message.exportId || message.exportId !== exportId) {
 | 
			
		||||
        // incoming messages must correspond to this export instance
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (message.type === 'export-progress-count') {
 | 
			
		||||
        $exportProgressWrapper.slideDown();
 | 
			
		||||
 | 
			
		||||
        $exportProgressCount.text(message.progressCount);
 | 
			
		||||
    }
 | 
			
		||||
    else if (message.type === 'export-finished') {
 | 
			
		||||
        $dialog.modal('hide');
 | 
			
		||||
 | 
			
		||||
        infoService.showMessage("Export finished successfully.");
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
export default {
 | 
			
		||||
    showDialog
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										11
									
								
								src/public/javascripts/dialogs/help.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								src/public/javascripts/dialogs/help.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,11 @@
 | 
			
		||||
const $dialog = $("#help-dialog");
 | 
			
		||||
 | 
			
		||||
async function showDialog() {
 | 
			
		||||
    glob.activeDialog = $dialog;
 | 
			
		||||
 | 
			
		||||
    $dialog.modal();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export default {
 | 
			
		||||
    showDialog
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										145
									
								
								src/public/javascripts/dialogs/import.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										145
									
								
								src/public/javascripts/dialogs/import.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,145 @@
 | 
			
		||||
import treeService from '../services/tree.js';
 | 
			
		||||
import utils from '../services/utils.js';
 | 
			
		||||
import treeUtils from "../services/tree_utils.js";
 | 
			
		||||
import server from "../services/server.js";
 | 
			
		||||
import infoService from "../services/info.js";
 | 
			
		||||
import messagingService from "../services/messaging.js";
 | 
			
		||||
 | 
			
		||||
const $dialog = $("#import-dialog");
 | 
			
		||||
const $form = $("#import-form");
 | 
			
		||||
const $noteTitle = $dialog.find(".note-title");
 | 
			
		||||
const $fileUploadInput = $("#import-file-upload-input");
 | 
			
		||||
const $importProgressCountWrapper = $("#import-progress-count-wrapper");
 | 
			
		||||
const $importProgressCount = $("#import-progress-count");
 | 
			
		||||
const $importButton = $("#import-button");
 | 
			
		||||
const $safeImportCheckbox = $("#safe-import-checkbox");
 | 
			
		||||
const $shrinkImagesCheckbox = $("#shrink-images-checkbox");
 | 
			
		||||
const $textImportedAsTextCheckbox = $("#text-imported-as-text-checkbox");
 | 
			
		||||
const $codeImportedAsCodeCheckbox = $("#code-imported-as-code-checkbox");
 | 
			
		||||
const $explodeArchivesCheckbox = $("#explode-archives-checkbox");
 | 
			
		||||
 | 
			
		||||
let importId;
 | 
			
		||||
 | 
			
		||||
async function showDialog() {
 | 
			
		||||
    // each opening of the dialog resets the importId so we don't associate it with previous imports anymore
 | 
			
		||||
    importId = '';
 | 
			
		||||
    $importProgressCountWrapper.hide();
 | 
			
		||||
    $importProgressCount.text('0');
 | 
			
		||||
    $fileUploadInput.val('').change(); // to trigger Import button disabling listener below
 | 
			
		||||
 | 
			
		||||
    $safeImportCheckbox.prop("checked", true);
 | 
			
		||||
    $shrinkImagesCheckbox.prop("checked", true);
 | 
			
		||||
    $textImportedAsTextCheckbox.prop("checked", true);
 | 
			
		||||
    $codeImportedAsCodeCheckbox.prop("checked", true);
 | 
			
		||||
    $explodeArchivesCheckbox.prop("checked", true);
 | 
			
		||||
 | 
			
		||||
    glob.activeDialog = $dialog;
 | 
			
		||||
 | 
			
		||||
    const currentNode = treeService.getCurrentNode();
 | 
			
		||||
    $noteTitle.text(await treeUtils.getNoteTitle(currentNode.data.noteId));
 | 
			
		||||
 | 
			
		||||
    $dialog.modal();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
$form.submit(() => {
 | 
			
		||||
    const currentNode = treeService.getCurrentNode();
 | 
			
		||||
 | 
			
		||||
    // disabling so that import is not triggered again.
 | 
			
		||||
    $importButton.attr("disabled", "disabled");
 | 
			
		||||
 | 
			
		||||
    importIntoNote(currentNode.data.noteId);
 | 
			
		||||
 | 
			
		||||
    return false;
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
async function importIntoNote(importNoteId) {
 | 
			
		||||
    const files = Array.from($fileUploadInput[0].files); // shallow copy since we're resetting the upload button below
 | 
			
		||||
 | 
			
		||||
    // we generate it here (and not on opening) for the case when you try to import multiple times from the same
 | 
			
		||||
    // dialog (which shouldn't happen, but still ...)
 | 
			
		||||
    importId = utils.randomString(10);
 | 
			
		||||
 | 
			
		||||
    const options = {
 | 
			
		||||
        safeImport: boolToString($safeImportCheckbox),
 | 
			
		||||
        shrinkImages: boolToString($shrinkImagesCheckbox),
 | 
			
		||||
        textImportedAsText: boolToString($textImportedAsTextCheckbox),
 | 
			
		||||
        codeImportedAsCode: boolToString($codeImportedAsCodeCheckbox),
 | 
			
		||||
        explodeArchives: boolToString($explodeArchivesCheckbox)
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    await uploadFiles(importNoteId, files, options);
 | 
			
		||||
 | 
			
		||||
    $dialog.modal('hide');
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
async function uploadFiles(importNoteId, files, options) {
 | 
			
		||||
    let noteId;
 | 
			
		||||
 | 
			
		||||
    for (const file of files) {
 | 
			
		||||
        const formData = new FormData();
 | 
			
		||||
        formData.append('upload', file);
 | 
			
		||||
        formData.append('importId', importId);
 | 
			
		||||
 | 
			
		||||
        for (const key in options) {
 | 
			
		||||
            formData.append(key, options[key]);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        ({noteId} = await $.ajax({
 | 
			
		||||
            url: baseApiUrl + 'notes/' + importNoteId + '/import',
 | 
			
		||||
            headers: server.getHeaders(),
 | 
			
		||||
            data: formData,
 | 
			
		||||
            dataType: 'json',
 | 
			
		||||
            type: 'POST',
 | 
			
		||||
            timeout: 60 * 60 * 1000,
 | 
			
		||||
            contentType: false, // NEEDED, DON'T REMOVE THIS
 | 
			
		||||
            processData: false, // NEEDED, DON'T REMOVE THIS
 | 
			
		||||
        }));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    infoService.showMessage("Import finished successfully.");
 | 
			
		||||
 | 
			
		||||
    await treeService.reload();
 | 
			
		||||
 | 
			
		||||
    if (noteId) {
 | 
			
		||||
        const node = await treeService.activateNote(noteId);
 | 
			
		||||
 | 
			
		||||
        node.setExpanded(true);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function boolToString($el) {
 | 
			
		||||
    return $el.is(":checked") ? "true" : "false";
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
messagingService.subscribeToMessages(async message => {
 | 
			
		||||
    if (message.type === 'import-error') {
 | 
			
		||||
        infoService.showError(message.message);
 | 
			
		||||
        $dialog.modal('hide');
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (!message.importId || message.importId !== importId) {
 | 
			
		||||
        // incoming messages must correspond to this import instance
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (message.type === 'import-progress-count') {
 | 
			
		||||
        $importProgressCountWrapper.slideDown();
 | 
			
		||||
 | 
			
		||||
        $importProgressCount.text(message.progressCount);
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
$fileUploadInput.change(() => {
 | 
			
		||||
    if ($fileUploadInput.val()) {
 | 
			
		||||
        $importButton.removeAttr("disabled");
 | 
			
		||||
    }
 | 
			
		||||
    else {
 | 
			
		||||
        $importButton.attr("disabled", "disabled");
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
export default {
 | 
			
		||||
    showDialog,
 | 
			
		||||
    uploadFiles
 | 
			
		||||
}
 | 
			
		||||
@@ -6,8 +6,6 @@ const $dialog = $("#jump-to-note-dialog");
 | 
			
		||||
const $autoComplete = $("#jump-to-note-autocomplete");
 | 
			
		||||
const $showInFullTextButton = $("#show-in-full-text-button");
 | 
			
		||||
 | 
			
		||||
$dialog.on("shown.bs.modal", e => $autoComplete.focus());
 | 
			
		||||
 | 
			
		||||
async function showDialog() {
 | 
			
		||||
    glob.activeDialog = $dialog;
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										29
									
								
								src/public/javascripts/dialogs/note_info.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								src/public/javascripts/dialogs/note_info.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,29 @@
 | 
			
		||||
import noteDetailService from '../services/note_detail.js';
 | 
			
		||||
 | 
			
		||||
const $dialog = $("#note-info-dialog");
 | 
			
		||||
const $noteId = $("#note-info-note-id");
 | 
			
		||||
const $dateCreated = $("#note-info-date-created");
 | 
			
		||||
const $dateModified = $("#note-info-date-modified");
 | 
			
		||||
const $type = $("#note-info-type");
 | 
			
		||||
const $mime = $("#note-info-mime");
 | 
			
		||||
const $okButton = $("#note-info-ok-button");
 | 
			
		||||
 | 
			
		||||
function showDialog() {
 | 
			
		||||
    glob.activeDialog = $dialog;
 | 
			
		||||
 | 
			
		||||
    $dialog.modal();
 | 
			
		||||
 | 
			
		||||
    const currentNote = noteDetailService.getCurrentNote();
 | 
			
		||||
 | 
			
		||||
    $noteId.text(currentNote.noteId);
 | 
			
		||||
    $dateCreated.text(currentNote.dateCreated);
 | 
			
		||||
    $dateModified.text(currentNote.dateModified);
 | 
			
		||||
    $type.text(currentNote.type);
 | 
			
		||||
    $mime.text(currentNote.mime);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
$okButton.click(() => $dialog.modal('hide'));
 | 
			
		||||
 | 
			
		||||
export default {
 | 
			
		||||
    showDialog
 | 
			
		||||
};
 | 
			
		||||
@@ -8,7 +8,7 @@ function showDialog() {
 | 
			
		||||
 | 
			
		||||
    $dialog.modal();
 | 
			
		||||
 | 
			
		||||
    const noteText = noteDetailService.getCurrentNote().content;
 | 
			
		||||
    const noteText = noteDetailService.getCurrentNote().noteContent.content;
 | 
			
		||||
 | 
			
		||||
    $noteSource.text(formatHtml(noteText));
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -5,6 +5,7 @@ import server from '../services/server.js';
 | 
			
		||||
import infoService from "../services/info.js";
 | 
			
		||||
import zoomService from "../services/zoom.js";
 | 
			
		||||
import utils from "../services/utils.js";
 | 
			
		||||
import cssLoader from "../services/css_loader.js";
 | 
			
		||||
 | 
			
		||||
const $dialog = $("#options-dialog");
 | 
			
		||||
 | 
			
		||||
@@ -44,10 +45,28 @@ addTabHandler((function() {
 | 
			
		||||
    const $zoomFactorSelect = $("#zoom-factor-select");
 | 
			
		||||
    const $leftPaneMinWidth = $("#left-pane-min-width");
 | 
			
		||||
    const $leftPaneWidthPercent = $("#left-pane-width-percent");
 | 
			
		||||
    const $html = $("html");
 | 
			
		||||
    const $mainFontSize = $("#main-font-size");
 | 
			
		||||
    const $treeFontSize = $("#tree-font-size");
 | 
			
		||||
    const $detailFontSize = $("#detail-font-size");
 | 
			
		||||
    const $body = $("body");
 | 
			
		||||
    const $container = $("#container");
 | 
			
		||||
 | 
			
		||||
    function optionsLoaded(options) {
 | 
			
		||||
    async function optionsLoaded(options) {
 | 
			
		||||
        const themes = [
 | 
			
		||||
            { val: 'white', title: 'White' },
 | 
			
		||||
            { val: 'dark', title: 'Dark' },
 | 
			
		||||
            { val: 'black', title: 'Black' }
 | 
			
		||||
        ].concat(await server.get('options/user-themes'));
 | 
			
		||||
 | 
			
		||||
        $themeSelect.empty();
 | 
			
		||||
 | 
			
		||||
        for (const theme of themes) {
 | 
			
		||||
            $themeSelect.append($("<option>")
 | 
			
		||||
                .attr("value", theme.val)
 | 
			
		||||
                .attr("data-note-id", theme.noteId)
 | 
			
		||||
                .html(theme.title));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $themeSelect.val(options.theme);
 | 
			
		||||
 | 
			
		||||
        if (utils.isElectron()) {
 | 
			
		||||
@@ -59,21 +78,35 @@ addTabHandler((function() {
 | 
			
		||||
 | 
			
		||||
        $leftPaneMinWidth.val(options.leftPaneMinWidth);
 | 
			
		||||
        $leftPaneWidthPercent.val(options.leftPaneWidthPercent);
 | 
			
		||||
 | 
			
		||||
        $mainFontSize.val(options.mainFontSize);
 | 
			
		||||
        $treeFontSize.val(options.treeFontSize);
 | 
			
		||||
        $detailFontSize.val(options.detailFontSize);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    $themeSelect.change(function() {
 | 
			
		||||
        const newTheme = $(this).val();
 | 
			
		||||
 | 
			
		||||
        $html.attr("class", "theme-" + newTheme);
 | 
			
		||||
        for (const clazz of Array.from($body[0].classList)) { // create copy to safely iterate over while removing classes
 | 
			
		||||
            if (clazz.startsWith("theme-")) {
 | 
			
		||||
                $body.removeClass(clazz);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        const noteId = $(this).find(":selected").attr("data-note-id");
 | 
			
		||||
 | 
			
		||||
        if (noteId) {
 | 
			
		||||
            // make sure the CSS is loaded
 | 
			
		||||
            // if the CSS has been loaded and then updated then the changes won't take effect though
 | 
			
		||||
            cssLoader.requireCss(`/api/notes/download/${noteId}`);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $body.addClass("theme-" + newTheme);
 | 
			
		||||
 | 
			
		||||
        server.put('options/theme/' + newTheme);
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    $zoomFactorSelect.change(function() {
 | 
			
		||||
        const newZoomFactor = $(this).val();
 | 
			
		||||
 | 
			
		||||
        zoomService.setZoomFactorAndSave(newZoomFactor);
 | 
			
		||||
    });
 | 
			
		||||
    $zoomFactorSelect.change(function() { zoomService.setZoomFactorAndSave($(this).val()); });
 | 
			
		||||
 | 
			
		||||
    function resizeLeftPanel() {
 | 
			
		||||
        const leftPanePercent = parseInt($leftPaneWidthPercent.val());
 | 
			
		||||
@@ -83,20 +116,42 @@ addTabHandler((function() {
 | 
			
		||||
        $container.css("grid-template-columns", `minmax(${leftPaneMinWidth}px, ${leftPanePercent}fr) ${rightPanePercent}fr`);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    $leftPaneMinWidth.change(function() {
 | 
			
		||||
        const newMinWidth = $(this).val();
 | 
			
		||||
    $leftPaneMinWidth.change(async function() {
 | 
			
		||||
        await server.put('options/leftPaneMinWidth/' + $(this).val());
 | 
			
		||||
 | 
			
		||||
        resizeLeftPanel();
 | 
			
		||||
 | 
			
		||||
        server.put('options/leftPaneMinWidth/' + newMinWidth);
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    $leftPaneWidthPercent.change(function() {
 | 
			
		||||
        const newWidthPercent = $(this).val();
 | 
			
		||||
    $leftPaneWidthPercent.change(async function() {
 | 
			
		||||
        await server.put('options/leftPaneWidthPercent/' + $(this).val());
 | 
			
		||||
 | 
			
		||||
        resizeLeftPanel();
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
        server.put('options/leftPaneWidthPercent/' + newWidthPercent);
 | 
			
		||||
    function applyFontSizes() {
 | 
			
		||||
        console.log($mainFontSize.val() + "% !important");
 | 
			
		||||
 | 
			
		||||
        $body.get(0).style.setProperty("--main-font-size", $mainFontSize.val() + "%");
 | 
			
		||||
        $body.get(0).style.setProperty("--tree-font-size", $treeFontSize.val() + "%");
 | 
			
		||||
        $body.get(0).style.setProperty("--detail-font-size", $detailFontSize.val() + "%");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    $mainFontSize.change(async function() {
 | 
			
		||||
        await server.put('options/mainFontSize/' + $(this).val());
 | 
			
		||||
 | 
			
		||||
        applyFontSizes();
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    $treeFontSize.change(async function() {
 | 
			
		||||
        await server.put('options/treeFontSize/' + $(this).val());
 | 
			
		||||
 | 
			
		||||
        applyFontSizes();
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    $detailFontSize.change(async function() {
 | 
			
		||||
        await server.put('options/detailFontSize/' + $(this).val());
 | 
			
		||||
 | 
			
		||||
        applyFontSizes();
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    return {
 | 
			
		||||
@@ -198,15 +253,17 @@ addTabHandler((async function () {
 | 
			
		||||
    const $syncVersion = $("#sync-version");
 | 
			
		||||
    const $buildDate = $("#build-date");
 | 
			
		||||
    const $buildRevision = $("#build-revision");
 | 
			
		||||
    const $dataDirectory = $("#data-directory");
 | 
			
		||||
 | 
			
		||||
    const appInfo = await server.get('app-info');
 | 
			
		||||
 | 
			
		||||
    $appVersion.html(appInfo.appVersion);
 | 
			
		||||
    $dbVersion.html(appInfo.dbVersion);
 | 
			
		||||
    $syncVersion.html(appInfo.syncVersion);
 | 
			
		||||
    $buildDate.html(appInfo.buildDate);
 | 
			
		||||
    $buildRevision.html(appInfo.buildRevision);
 | 
			
		||||
    $appVersion.text(appInfo.appVersion);
 | 
			
		||||
    $dbVersion.text(appInfo.dbVersion);
 | 
			
		||||
    $syncVersion.text(appInfo.syncVersion);
 | 
			
		||||
    $buildDate.text(appInfo.buildDate);
 | 
			
		||||
    $buildRevision.text(appInfo.buildRevision);
 | 
			
		||||
    $buildRevision.attr('href', 'https://github.com/zadam/trilium/commit/' + appInfo.buildRevision);
 | 
			
		||||
    $dataDirectory.text(appInfo.dataDirectory);
 | 
			
		||||
 | 
			
		||||
    return {};
 | 
			
		||||
})());
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,3 @@
 | 
			
		||||
import utils from '../services/utils.js';
 | 
			
		||||
import libraryLoader from '../services/library_loader.js';
 | 
			
		||||
import server from '../services/server.js';
 | 
			
		||||
import infoService from "../services/info.js";
 | 
			
		||||
@@ -8,14 +7,17 @@ const $query = $('#sql-console-query');
 | 
			
		||||
const $executeButton = $('#sql-console-execute');
 | 
			
		||||
const $resultHead = $('#sql-console-results thead');
 | 
			
		||||
const $resultBody = $('#sql-console-results tbody');
 | 
			
		||||
const $tables = $("#sql-console-tables");
 | 
			
		||||
 | 
			
		||||
let codeEditor;
 | 
			
		||||
 | 
			
		||||
$dialog.on("shown.bs.modal", e => initEditor());
 | 
			
		||||
 | 
			
		||||
function showDialog() {
 | 
			
		||||
async function showDialog() {
 | 
			
		||||
    glob.activeDialog = $dialog;
 | 
			
		||||
 | 
			
		||||
    await showTables();
 | 
			
		||||
 | 
			
		||||
    $dialog.modal();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -96,6 +98,32 @@ async function execute(e) {
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
async function showTables() {
 | 
			
		||||
    const tables = await server.get('sql/schema');
 | 
			
		||||
 | 
			
		||||
    $tables.empty();
 | 
			
		||||
 | 
			
		||||
    for (const table of tables) {
 | 
			
		||||
        const $tableLink = $('<button class="btn">').text(table.name);
 | 
			
		||||
 | 
			
		||||
        const $columns = $("<table>");
 | 
			
		||||
 | 
			
		||||
        for (const column of table.columns) {
 | 
			
		||||
            $columns.append(
 | 
			
		||||
                $("<tr>")
 | 
			
		||||
                    .append($("<td>").text(column.name))
 | 
			
		||||
                    .append($("<td>").text(column.type))
 | 
			
		||||
            );
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $tables.append($tableLink).append(" ");
 | 
			
		||||
 | 
			
		||||
        $tableLink
 | 
			
		||||
            .tooltip({html: true, title: $columns.html()})
 | 
			
		||||
            .click(() => codeEditor.setValue("SELECT * FROM " + table.name + " LIMIT 100"));
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
$query.bind('keydown', 'ctrl+return', execute);
 | 
			
		||||
 | 
			
		||||
$executeButton.click(execute);
 | 
			
		||||
 
 | 
			
		||||
@@ -8,15 +8,13 @@ class NoteFull extends NoteShort {
 | 
			
		||||
        super(treeCache, row);
 | 
			
		||||
 | 
			
		||||
        /** @param {string} */
 | 
			
		||||
        this.content = row.content;
 | 
			
		||||
        this.noteContent = row.noteContent;
 | 
			
		||||
 | 
			
		||||
        if (this.content !== "" && this.isJson()) {
 | 
			
		||||
            try {
 | 
			
		||||
                /** @param {object} */
 | 
			
		||||
                this.jsonContent = JSON.parse(this.content);
 | 
			
		||||
            }
 | 
			
		||||
            catch(e) {}
 | 
			
		||||
        }
 | 
			
		||||
        /** @param {string} */
 | 
			
		||||
        this.dateCreated = row.dateCreated;
 | 
			
		||||
 | 
			
		||||
        /** @param {string} */
 | 
			
		||||
        this.dateModified = row.dateModified;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -85,8 +85,11 @@ async function showAttributes() {
 | 
			
		||||
        $promotedAttributesContainer.empty().append($tbody);
 | 
			
		||||
    }
 | 
			
		||||
    else if (note.type !== 'relation-map') {
 | 
			
		||||
        if (attributes.length > 0) {
 | 
			
		||||
            for (const attribute of attributes) {
 | 
			
		||||
        // display only "own" notes
 | 
			
		||||
        const ownedAttributes = attributes.filter(attr => attr.noteId === note.noteId);
 | 
			
		||||
 | 
			
		||||
        if (ownedAttributes.length > 0) {
 | 
			
		||||
            for (const attribute of ownedAttributes) {
 | 
			
		||||
                if (attribute.type === 'label') {
 | 
			
		||||
                    $attributeListInner.append(utils.formatLabel(attribute) + " ");
 | 
			
		||||
                }
 | 
			
		||||
@@ -132,7 +135,9 @@ async function createPromotedAttributeRow(definitionAttr, valueAttr) {
 | 
			
		||||
    const $inputCell = $("<td>").append($("<div>").addClass("input-group").append($input));
 | 
			
		||||
 | 
			
		||||
    const $actionCell = $("<td>");
 | 
			
		||||
    const $multiplicityCell = $("<td>").addClass("multiplicity");
 | 
			
		||||
    const $multiplicityCell = $("<td>")
 | 
			
		||||
        .addClass("multiplicity")
 | 
			
		||||
        .attr("nowrap", true);
 | 
			
		||||
 | 
			
		||||
    $tr
 | 
			
		||||
        .append($labelCell)
 | 
			
		||||
@@ -155,7 +160,7 @@ async function createPromotedAttributeRow(definitionAttr, valueAttr) {
 | 
			
		||||
                $input.autocomplete({
 | 
			
		||||
                    appendTo: document.querySelector('body'),
 | 
			
		||||
                    hint: false,
 | 
			
		||||
                    autoselect: true,
 | 
			
		||||
                    autoselect: false,
 | 
			
		||||
                    openOnFocus: true,
 | 
			
		||||
                    minLength: 0,
 | 
			
		||||
                    tabAutocomplete: false
 | 
			
		||||
 
 | 
			
		||||
@@ -9,7 +9,7 @@ async function getAndExecuteBundle(noteId, originEntity = null) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
async function executeBundle(bundle, originEntity) {
 | 
			
		||||
    const apiContext = ScriptContext(bundle.note, bundle.allNotes, originEntity);
 | 
			
		||||
    const apiContext = await ScriptContext(bundle.noteId, bundle.allNoteIds, originEntity);
 | 
			
		||||
 | 
			
		||||
    try {
 | 
			
		||||
        return await (function () {
 | 
			
		||||
@@ -30,9 +30,13 @@ async function executeStartupBundles() {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
async function executeRelationBundles(note, relationName) {
 | 
			
		||||
    const bundlesToRun = await server.get("script/relation/" + note.noteId + "/" + relationName);
 | 
			
		||||
    note.bundleCache = note.bundleCache || {};
 | 
			
		||||
 | 
			
		||||
    for (const bundle of bundlesToRun) {
 | 
			
		||||
    if (!note.bundleCache[relationName]) {
 | 
			
		||||
        note.bundleCache[relationName] = await server.get("script/relation/" + note.noteId + "/" + relationName);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    for (const bundle of note.bundleCache[relationName]) {
 | 
			
		||||
        await executeBundle(bundle, note);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										13
									
								
								src/public/javascripts/services/css_loader.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								src/public/javascripts/services/css_loader.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,13 @@
 | 
			
		||||
async function requireCss(url) {
 | 
			
		||||
    const css = Array
 | 
			
		||||
        .from(document.querySelectorAll('link'))
 | 
			
		||||
        .map(scr => scr.href);
 | 
			
		||||
 | 
			
		||||
    if (!css.includes(url)) {
 | 
			
		||||
        $('head').append($('<link rel="stylesheet" type="text/css" />').attr('href', url));
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export default {
 | 
			
		||||
    requireCss
 | 
			
		||||
}
 | 
			
		||||
@@ -1,5 +1,6 @@
 | 
			
		||||
import treeService from './tree.js';
 | 
			
		||||
import treeChangesService from './branches.js';
 | 
			
		||||
import importDialog from '../dialogs/import.js';
 | 
			
		||||
 | 
			
		||||
const dragAndDropSetup = {
 | 
			
		||||
    autoExpandMS: 600,
 | 
			
		||||
@@ -9,6 +10,14 @@ const dragAndDropSetup = {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (!data.originalEvent.ctrlKey) {
 | 
			
		||||
            // keep existing selection only if CTRL key is pressed
 | 
			
		||||
            for (const selectedNode of treeService.getSelectedNodes()) {
 | 
			
		||||
                selectedNode.setSelected(false);
 | 
			
		||||
                selectedNode.renderTitle();
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        node.setSelected(true);
 | 
			
		||||
 | 
			
		||||
        // this is for dragging notes into relation map
 | 
			
		||||
@@ -24,23 +33,34 @@ const dragAndDropSetup = {
 | 
			
		||||
        return true;
 | 
			
		||||
    },
 | 
			
		||||
    dragEnter: (node, data) => true, // allow drop on any node
 | 
			
		||||
    dragOver: (node, data) => true,
 | 
			
		||||
    dragDrop: (node, data) => {
 | 
			
		||||
        // This function MUST be defined to enable dropping of items on the tree.
 | 
			
		||||
        // data.hitMode is 'before', 'after', or 'over'.
 | 
			
		||||
        const dataTransfer = data.dataTransfer;
 | 
			
		||||
 | 
			
		||||
        const selectedNodes = treeService.getSelectedNodes();
 | 
			
		||||
 | 
			
		||||
        if (data.hitMode === "before") {
 | 
			
		||||
            treeChangesService.moveBeforeNode(selectedNodes, node);
 | 
			
		||||
        }
 | 
			
		||||
        else if (data.hitMode === "after") {
 | 
			
		||||
            treeChangesService.moveAfterNode(selectedNodes, node);
 | 
			
		||||
        }
 | 
			
		||||
        else if (data.hitMode === "over") {
 | 
			
		||||
            treeChangesService.moveToNode(selectedNodes, node);
 | 
			
		||||
        if (dataTransfer && dataTransfer.files && dataTransfer.files.length > 0) {
 | 
			
		||||
            importDialog.uploadFiles(node.data.noteId, dataTransfer.files, {
 | 
			
		||||
                safeImport: true,
 | 
			
		||||
                shrinkImages: true,
 | 
			
		||||
                textImportedAsText: true,
 | 
			
		||||
                codeImportedAsCode: true,
 | 
			
		||||
                explodeArchives: true
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
        else {
 | 
			
		||||
            throw new Error("Unknown hitMode=" + data.hitMode);
 | 
			
		||||
            // This function MUST be defined to enable dropping of items on the tree.
 | 
			
		||||
            // data.hitMode is 'before', 'after', or 'over'.
 | 
			
		||||
 | 
			
		||||
            const selectedNodes = treeService.getSelectedNodes();
 | 
			
		||||
 | 
			
		||||
            if (data.hitMode === "before") {
 | 
			
		||||
                treeChangesService.moveBeforeNode(selectedNodes, node);
 | 
			
		||||
            } else if (data.hitMode === "after") {
 | 
			
		||||
                treeChangesService.moveAfterNode(selectedNodes, node);
 | 
			
		||||
            } else if (data.hitMode === "over") {
 | 
			
		||||
                treeChangesService.moveToNode(selectedNodes, node);
 | 
			
		||||
            } else {
 | 
			
		||||
                throw new Error("Unknown hitMode=" + data.hitMode);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +1,6 @@
 | 
			
		||||
import utils from "./utils.js";
 | 
			
		||||
import treeService from "./tree.js";
 | 
			
		||||
import linkService from "./link.js";
 | 
			
		||||
import fileService from "./file.js";
 | 
			
		||||
import zoomService from "./zoom.js";
 | 
			
		||||
import noteRevisionsDialog from "../dialogs/note_revisions.js";
 | 
			
		||||
import optionsDialog from "../dialogs/options.js";
 | 
			
		||||
@@ -12,6 +11,8 @@ import recentChangesDialog from "../dialogs/recent_changes.js";
 | 
			
		||||
import sqlConsoleDialog from "../dialogs/sql_console.js";
 | 
			
		||||
import searchNotesService from "./search_notes.js";
 | 
			
		||||
import attributesDialog from "../dialogs/attributes.js";
 | 
			
		||||
import helpDialog from "../dialogs/help.js";
 | 
			
		||||
import noteInfoDialog from "../dialogs/note_info.js";
 | 
			
		||||
import protectedSessionService from "./protected_session.js";
 | 
			
		||||
 | 
			
		||||
function registerEntrypoints() {
 | 
			
		||||
@@ -54,15 +55,28 @@ function registerEntrypoints() {
 | 
			
		||||
 | 
			
		||||
    $("#options-button").click(optionsDialog.showDialog);
 | 
			
		||||
 | 
			
		||||
    $("#show-help-button").click(helpDialog.showDialog);
 | 
			
		||||
    utils.bindShortcut('f1', helpDialog.showDialog);
 | 
			
		||||
 | 
			
		||||
    $("#open-sql-console-button").click(sqlConsoleDialog.showDialog);
 | 
			
		||||
    utils.bindShortcut('alt+o', sqlConsoleDialog.showDialog);
 | 
			
		||||
 | 
			
		||||
    $("#show-note-info-button").click(noteInfoDialog.showDialog);
 | 
			
		||||
 | 
			
		||||
    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+right', window.history.forward);
 | 
			
		||||
        if (utils.isMac()) {
 | 
			
		||||
            // Mac has a different history navigation shortcuts - https://github.com/zadam/trilium/issues/376
 | 
			
		||||
            utils.bindShortcut('meta+left', window.history.back);
 | 
			
		||||
            utils.bindShortcut('meta+right', window.history.forward);
 | 
			
		||||
        }
 | 
			
		||||
        else {
 | 
			
		||||
            utils.bindShortcut('alt+left', window.history.back);
 | 
			
		||||
            utils.bindShortcut('alt+right', window.history.forward);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    utils.bindShortcut('alt+m', e => {
 | 
			
		||||
@@ -87,13 +101,18 @@ function registerEntrypoints() {
 | 
			
		||||
 | 
			
		||||
    utils.bindShortcut('ctrl+r', utils.reloadApp);
 | 
			
		||||
 | 
			
		||||
    $(document).bind('keydown', 'ctrl+shift+i', () => {
 | 
			
		||||
        if (utils.isElectron()) {
 | 
			
		||||
    $("#open-dev-tools-button").toggle(utils.isElectron());
 | 
			
		||||
 | 
			
		||||
    if (utils.isElectron()) {
 | 
			
		||||
        const openDevTools = () => {
 | 
			
		||||
            require('electron').remote.getCurrentWindow().toggleDevTools();
 | 
			
		||||
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
    });
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        utils.bindShortcut('ctrl+shift+i', openDevTools);
 | 
			
		||||
        $("#open-dev-tools-button").click(openDevTools);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function openInPageSearch() {
 | 
			
		||||
        if (utils.isElectron()) {
 | 
			
		||||
@@ -117,10 +136,6 @@ function registerEntrypoints() {
 | 
			
		||||
 | 
			
		||||
    utils.bindShortcut('ctrl+f', openInPageSearch);
 | 
			
		||||
 | 
			
		||||
    if (utils.isMac()) {
 | 
			
		||||
        utils.bindShortcut('meta+f', openInPageSearch);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // FIXME: do we really need these at this point?
 | 
			
		||||
    utils.bindShortcut("ctrl+shift+up", () => {
 | 
			
		||||
        const node = treeService.getCurrentNode();
 | 
			
		||||
@@ -139,13 +154,11 @@ function registerEntrypoints() {
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    if (utils.isElectron()) {
 | 
			
		||||
        $(document).bind('keydown', 'ctrl+-', zoomService.decreaseZoomFactor);
 | 
			
		||||
        $(document).bind('keydown', 'ctrl+=', zoomService.increaseZoomFactor);
 | 
			
		||||
        utils.bindShortcut('ctrl+-', zoomService.decreaseZoomFactor);
 | 
			
		||||
        utils.bindShortcut('ctrl+=', zoomService.increaseZoomFactor);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    $("#note-title").bind('keydown', 'return', () => $("#note-detail-text").focus());
 | 
			
		||||
 | 
			
		||||
    $("#upload-file-button").click(fileService.uploadFile);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export default {
 | 
			
		||||
 
 | 
			
		||||
@@ -1,50 +0,0 @@
 | 
			
		||||
import treeService from './tree.js';
 | 
			
		||||
import protectedSessionHolder from './protected_session_holder.js';
 | 
			
		||||
import utils from './utils.js';
 | 
			
		||||
import server from './server.js';
 | 
			
		||||
 | 
			
		||||
function exportBranch(branchId, type, format) {
 | 
			
		||||
    const url = utils.getHost() + `/api/notes/${branchId}/export/${type}/${format}?protectedSessionId=` + encodeURIComponent(protectedSessionHolder.getProtectedSessionId());
 | 
			
		||||
 | 
			
		||||
    console.log(url);
 | 
			
		||||
 | 
			
		||||
    utils.download(url);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
let importNoteId;
 | 
			
		||||
 | 
			
		||||
function importIntoNote(noteId) {
 | 
			
		||||
    importNoteId = noteId;
 | 
			
		||||
 | 
			
		||||
    $("#import-upload").trigger('click');
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
$("#import-upload").change(async function() {
 | 
			
		||||
    const formData = new FormData();
 | 
			
		||||
    formData.append('upload', this.files[0]);
 | 
			
		||||
 | 
			
		||||
    await $.ajax({
 | 
			
		||||
        url: baseApiUrl + 'notes/' + importNoteId + '/import',
 | 
			
		||||
        headers: server.getHeaders(),
 | 
			
		||||
        data: formData,
 | 
			
		||||
        dataType: 'json',
 | 
			
		||||
        type: 'POST',
 | 
			
		||||
        contentType: false, // NEEDED, DON'T REMOVE THIS
 | 
			
		||||
        processData: false, // NEEDED, DON'T REMOVE THIS
 | 
			
		||||
    })
 | 
			
		||||
        .fail((xhr, status, error) => alert('Import error: ' + xhr.responseText))
 | 
			
		||||
        .done(async note => {
 | 
			
		||||
            await treeService.reload();
 | 
			
		||||
 | 
			
		||||
            if (note) {
 | 
			
		||||
                const node = await treeService.activateNote(note.noteId);
 | 
			
		||||
 | 
			
		||||
                node.setExpanded(true);
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
export default {
 | 
			
		||||
    exportBranch,
 | 
			
		||||
    importIntoNote
 | 
			
		||||
};
 | 
			
		||||
@@ -1,29 +0,0 @@
 | 
			
		||||
import noteDetailService from "./note_detail.js";
 | 
			
		||||
import treeService from "./tree.js";
 | 
			
		||||
import server from "./server.js";
 | 
			
		||||
 | 
			
		||||
function uploadFile() {
 | 
			
		||||
    $("#file-upload").trigger('click');
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
$("#file-upload").change(async function() {
 | 
			
		||||
    const formData = new FormData();
 | 
			
		||||
    formData.append('upload', this.files[0]);
 | 
			
		||||
 | 
			
		||||
    const resp = await $.ajax({
 | 
			
		||||
        url: baseApiUrl + 'notes/' + noteDetailService.getCurrentNoteId() + '/upload',
 | 
			
		||||
        headers: server.getHeaders(),
 | 
			
		||||
        data: formData,
 | 
			
		||||
        type: 'POST',
 | 
			
		||||
        contentType: false, // NEEDED, DON'T OMIT THIS
 | 
			
		||||
        processData: false, // NEEDED, DON'T OMIT THIS
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    await treeService.reload();
 | 
			
		||||
 | 
			
		||||
    await treeService.activateNote(resp.noteId);
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
export default {
 | 
			
		||||
    uploadFile
 | 
			
		||||
}
 | 
			
		||||
@@ -7,6 +7,7 @@ import treeCache from './tree_cache.js';
 | 
			
		||||
import noteDetailService from './note_detail.js';
 | 
			
		||||
import noteTypeService from './note_type.js';
 | 
			
		||||
import noteTooltipService from './note_tooltip.js';
 | 
			
		||||
import protectedSessionService from'./protected_session.js';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * This is the main frontend API interface for scripts. It's published in the local "api" object.
 | 
			
		||||
@@ -42,7 +43,7 @@ function FrontendScriptApi(startNote, currentNote, originEntity = null) {
 | 
			
		||||
    this.activateNewNote = async notePath => {
 | 
			
		||||
        await treeService.reload();
 | 
			
		||||
 | 
			
		||||
        await treeService.activateNote(notePath, true);
 | 
			
		||||
        await treeService.activateNote(notePath, noteDetailService.focusAndSelectTitle);
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
@@ -244,7 +245,12 @@ function FrontendScriptApi(startNote, currentNote, originEntity = null) {
 | 
			
		||||
     * @method
 | 
			
		||||
     * @param {object} $el - jquery object on which to setup the tooltip
 | 
			
		||||
     */
 | 
			
		||||
    this.setupElementTooltip = noteTooltipService.setupElementTooltip
 | 
			
		||||
    this.setupElementTooltip = noteTooltipService.setupElementTooltip;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @method
 | 
			
		||||
     */
 | 
			
		||||
    this.protectCurrentNote = protectedSessionService.protectNoteAndSendToServer;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export default FrontendScriptApi;
 | 
			
		||||
@@ -1,3 +1,5 @@
 | 
			
		||||
import cssLoader from './css_loader.js';
 | 
			
		||||
 | 
			
		||||
const CKEDITOR = {"js": ["libraries/ckeditor/ckeditor.js"]};
 | 
			
		||||
 | 
			
		||||
const CODE_MIRROR = {
 | 
			
		||||
@@ -34,7 +36,7 @@ const RELATION_MAP = {
 | 
			
		||||
 | 
			
		||||
async function requireLibrary(library) {
 | 
			
		||||
    if (library.css) {
 | 
			
		||||
        library.css.map(cssUrl => requireCss(cssUrl));
 | 
			
		||||
        library.css.map(cssUrl => cssLoader.requireCss(cssUrl));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (library.js) {
 | 
			
		||||
@@ -59,16 +61,6 @@ async function requireScript(url) {
 | 
			
		||||
    await loadedScriptPromises[url];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
async function requireCss(url) {
 | 
			
		||||
    const css = Array
 | 
			
		||||
        .from(document.querySelectorAll('link'))
 | 
			
		||||
        .map(scr => scr.href);
 | 
			
		||||
 | 
			
		||||
    if (!css.includes(url)) {
 | 
			
		||||
        $('head').append($('<link rel="stylesheet" type="text/css" />').attr('href', url));
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export default {
 | 
			
		||||
    requireLibrary,
 | 
			
		||||
    CKEDITOR,
 | 
			
		||||
 
 | 
			
		||||
@@ -107,6 +107,7 @@ function init() {
 | 
			
		||||
$(document).on('click', "a[data-action='note']", goToLink);
 | 
			
		||||
$(document).on('click', 'div.popover-content a, div.ui-tooltip-content a', goToLink);
 | 
			
		||||
$(document).on('dblclick', '#note-detail-text a', goToLink);
 | 
			
		||||
$(document).on('click', '#note-detail-text.ck-read-only a', goToLink);
 | 
			
		||||
$(document).on('click', 'span.ck-button__label', e => {
 | 
			
		||||
    // this is a link preview dialog from CKEditor link editing
 | 
			
		||||
    // for some reason clicked element is span
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										25
									
								
								src/public/javascripts/services/mac_init.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								src/public/javascripts/services/mac_init.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,25 @@
 | 
			
		||||
/**
 | 
			
		||||
 * Mac specific initialization
 | 
			
		||||
 */
 | 
			
		||||
import utils from "./utils.js";
 | 
			
		||||
 | 
			
		||||
function init() {
 | 
			
		||||
    if (utils.isElectron() && utils.isMac()) {
 | 
			
		||||
        utils.bindShortcut('meta+c', () => exec("copy"));
 | 
			
		||||
        utils.bindShortcut('meta+v', () => exec('paste'));
 | 
			
		||||
        utils.bindShortcut('meta+x', () => exec('cut'));
 | 
			
		||||
        utils.bindShortcut('meta+a', () => exec('selectAll'));
 | 
			
		||||
        utils.bindShortcut('meta+z', () => exec('undo'));
 | 
			
		||||
        utils.bindShortcut('meta+y', () => exec('redo'));
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function exec(cmd) {
 | 
			
		||||
    document.execCommand(cmd);
 | 
			
		||||
 | 
			
		||||
    return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export default {
 | 
			
		||||
    init
 | 
			
		||||
}
 | 
			
		||||
@@ -38,7 +38,7 @@ function handleMessage(event) {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (message.type === 'sync') {
 | 
			
		||||
        lastPingTs = new Date().getTime();
 | 
			
		||||
        lastPingTs = Date.now();
 | 
			
		||||
 | 
			
		||||
        if (message.data.length > 0) {
 | 
			
		||||
            console.debug(utils.now(), "Sync data: ", message.data);
 | 
			
		||||
@@ -81,10 +81,10 @@ setTimeout(() => {
 | 
			
		||||
    ws = connectWebSocket();
 | 
			
		||||
 | 
			
		||||
    lastSyncId = glob.maxSyncIdAtLoad;
 | 
			
		||||
    lastPingTs = new Date().getTime();
 | 
			
		||||
    lastPingTs = Date.now();
 | 
			
		||||
 | 
			
		||||
    setInterval(async () => {
 | 
			
		||||
        if (new Date().getTime() - lastPingTs > 30000) {
 | 
			
		||||
        if (Date.now() - lastPingTs > 30000) {
 | 
			
		||||
            console.log("Lost connection to server");
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -28,7 +28,7 @@ function clearText($el) {
 | 
			
		||||
function showRecentNotes($el) {
 | 
			
		||||
    $el.setSelectedPath("");
 | 
			
		||||
    $el.autocomplete("val", "");
 | 
			
		||||
    $el.autocomplete("open");
 | 
			
		||||
    $el.focus();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function initNoteAutocomplete($el, options) {
 | 
			
		||||
@@ -61,7 +61,13 @@ function initNoteAutocomplete($el, options) {
 | 
			
		||||
 | 
			
		||||
        $clearTextButton.click(() => clearText($el));
 | 
			
		||||
 | 
			
		||||
        $showRecentNotesButton.click(() => showRecentNotes($el));
 | 
			
		||||
        $showRecentNotesButton.click(e => {
 | 
			
		||||
            showRecentNotes($el);
 | 
			
		||||
 | 
			
		||||
            // this will cause the click not give focus to the "show recent notes" button
 | 
			
		||||
            // this is important because otherwise input will lose focus immediatelly and not show the results
 | 
			
		||||
            return false;
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        $goToSelectedNoteButton.click(() => {
 | 
			
		||||
            if ($el.hasClass("disabled")) {
 | 
			
		||||
@@ -86,7 +92,9 @@ function initNoteAutocomplete($el, options) {
 | 
			
		||||
                    suggestion: function(suggestion) {
 | 
			
		||||
                        return suggestion.highlighted;
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                },
 | 
			
		||||
                // we can't cache identical searches because notes can be created / renamed, new recent notes can be added
 | 
			
		||||
                cache: false
 | 
			
		||||
            }
 | 
			
		||||
        ]);
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -18,6 +18,7 @@ import noteDetailRelationMap from './note_detail_relation_map.js';
 | 
			
		||||
import bundleService from "./bundle.js";
 | 
			
		||||
import attributeService from "./attributes.js";
 | 
			
		||||
import utils from "./utils.js";
 | 
			
		||||
import importDialog from "../dialogs/import.js";
 | 
			
		||||
 | 
			
		||||
const $noteTitle = $("#note-title");
 | 
			
		||||
 | 
			
		||||
@@ -26,10 +27,10 @@ const $noteDetailComponents = $(".note-detail-component");
 | 
			
		||||
const $protectButton = $("#protect-button");
 | 
			
		||||
const $unprotectButton = $("#unprotect-button");
 | 
			
		||||
const $noteDetailWrapper = $("#note-detail-wrapper");
 | 
			
		||||
const $noteIdDisplay = $("#note-id-display");
 | 
			
		||||
const $childrenOverview = $("#children-overview");
 | 
			
		||||
const $scriptArea = $("#note-detail-script-area");
 | 
			
		||||
const $savedIndicator = $("#saved-indicator");
 | 
			
		||||
const $body = $("body");
 | 
			
		||||
 | 
			
		||||
let currentNote = null;
 | 
			
		||||
 | 
			
		||||
@@ -37,6 +38,8 @@ let noteChangeDisabled = false;
 | 
			
		||||
 | 
			
		||||
let isNoteChanged = false;
 | 
			
		||||
 | 
			
		||||
let detailLoadedListeners = [];
 | 
			
		||||
 | 
			
		||||
const components = {
 | 
			
		||||
    'code': noteDetailCode,
 | 
			
		||||
    'text': noteDetailText,
 | 
			
		||||
@@ -114,7 +117,7 @@ async function saveNote() {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    note.title = $noteTitle.val();
 | 
			
		||||
    note.content = getCurrentNoteContent(note);
 | 
			
		||||
    note.noteContent.content = getCurrentNoteContent(note);
 | 
			
		||||
 | 
			
		||||
    // it's important to set the flag back to false immediatelly after retrieving title and content
 | 
			
		||||
    // otherwise we might overwrite another change (especially async code)
 | 
			
		||||
@@ -129,6 +132,9 @@ async function saveNote() {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    $savedIndicator.fadeIn();
 | 
			
		||||
 | 
			
		||||
    // run async
 | 
			
		||||
    bundleService.executeRelationBundles(getCurrentNote(), 'runOnNoteChange');
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
async function saveNoteIfChanged() {
 | 
			
		||||
@@ -140,17 +146,21 @@ async function saveNoteIfChanged() {
 | 
			
		||||
    $savedIndicator.fadeIn();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function setNoteBackgroundIfProtected(note) {
 | 
			
		||||
    $noteDetailWrapper.toggleClass("protected", note.isProtected);
 | 
			
		||||
    $protectButton.toggleClass("active", note.isProtected);
 | 
			
		||||
    $unprotectButton.toggleClass("active", !note.isProtected);
 | 
			
		||||
    $unprotectButton.prop("disabled", !protectedSessionHolder.isProtectedSessionAvailable());
 | 
			
		||||
}
 | 
			
		||||
function updateNoteView() {
 | 
			
		||||
    $noteDetailWrapper.toggleClass("protected", currentNote.isProtected);
 | 
			
		||||
    $protectButton.toggleClass("active", currentNote.isProtected);
 | 
			
		||||
    $protectButton.prop("disabled", currentNote.isProtected);
 | 
			
		||||
    $unprotectButton.toggleClass("active", !currentNote.isProtected);
 | 
			
		||||
    $unprotectButton.prop("disabled", !currentNote.isProtected || !protectedSessionHolder.isProtectedSessionAvailable());
 | 
			
		||||
 | 
			
		||||
let isNewNoteCreated = false;
 | 
			
		||||
    for (const clazz of Array.from($body[0].classList)) { // create copy to safely iterate over while removing classes
 | 
			
		||||
        if (clazz.startsWith("type-") || clazz.startsWith("mime-")) {
 | 
			
		||||
            $body.removeClass(clazz);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
function newNoteCreated() {
 | 
			
		||||
    isNewNoteCreated = true;
 | 
			
		||||
    $body.addClass(utils.getNoteTypeClass(currentNote.type));
 | 
			
		||||
    $body.addClass(utils.getMimeTypeClass(currentNote.mime));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
async function handleProtectedSession() {
 | 
			
		||||
@@ -191,15 +201,7 @@ async function loadNoteDetail(noteId) {
 | 
			
		||||
        attributeService.invalidateAttributes();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (isNewNoteCreated) {
 | 
			
		||||
        isNewNoteCreated = false;
 | 
			
		||||
 | 
			
		||||
        $noteTitle.focus().select();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    $noteIdDisplay.html(noteId);
 | 
			
		||||
 | 
			
		||||
    setNoteBackgroundIfProtected(currentNote);
 | 
			
		||||
    updateNoteView();
 | 
			
		||||
 | 
			
		||||
    $noteDetailWrapper.show();
 | 
			
		||||
 | 
			
		||||
@@ -240,11 +242,13 @@ async function loadNoteDetail(noteId) {
 | 
			
		||||
    // after loading new note make sure editor is scrolled to the top
 | 
			
		||||
    getComponent(currentNote.type).scrollToTop();
 | 
			
		||||
 | 
			
		||||
    fireDetailLoaded();
 | 
			
		||||
 | 
			
		||||
    $scriptArea.empty();
 | 
			
		||||
 | 
			
		||||
    await bundleService.executeRelationBundles(getCurrentNote(), 'runOnNoteView');
 | 
			
		||||
 | 
			
		||||
    if (utils.isDesktop()) {
 | 
			
		||||
        $scriptArea.empty();
 | 
			
		||||
 | 
			
		||||
        await bundleService.executeRelationBundles(getCurrentNote(), 'runOnNoteView');
 | 
			
		||||
 | 
			
		||||
        await attributeService.showAttributes();
 | 
			
		||||
 | 
			
		||||
        await showChildrenOverview();
 | 
			
		||||
@@ -274,7 +278,7 @@ async function showChildrenOverview() {
 | 
			
		||||
            text: await treeUtils.getNoteTitle(childBranch.noteId, childBranch.parentNoteId)
 | 
			
		||||
        }).attr('data-action', 'note').attr('data-note-path', notePath + '/' + childBranch.noteId);
 | 
			
		||||
 | 
			
		||||
        const childEl = $('<div class="child-overview">').html(link);
 | 
			
		||||
        const childEl = $('<div class="child-overview-item">').html(link);
 | 
			
		||||
        $childrenOverview.append(childEl);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -291,6 +295,34 @@ function focusOnTitle() {
 | 
			
		||||
    $noteTitle.focus();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function focusAndSelectTitle() {
 | 
			
		||||
    $noteTitle.focus().select();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Since detail loading may take some time and user might just browse through the notes using UP-DOWN keys,
 | 
			
		||||
 * we intentionally decouple activation of the note in the tree and full load of the note so just avaiting on
 | 
			
		||||
 * fancytree's activate() won't wait for the full load.
 | 
			
		||||
 *
 | 
			
		||||
 * This causes an issue where in some cases you want to do some action after detail is loaded. For this reason
 | 
			
		||||
 * we provide the listeners here which will be triggered after the detail is loaded and if the loaded note
 | 
			
		||||
 * is the one registered in the listener.
 | 
			
		||||
 */
 | 
			
		||||
function addDetailLoadedListener(noteId, callback) {
 | 
			
		||||
    detailLoadedListeners.push({ noteId, callback });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function fireDetailLoaded() {
 | 
			
		||||
    for (const {noteId, callback} of detailLoadedListeners) {
 | 
			
		||||
        if (noteId === currentNote.noteId) {
 | 
			
		||||
            callback();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // all the listeners are one time only
 | 
			
		||||
    detailLoadedListeners = [];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
messagingService.subscribeToSyncMessages(syncData => {
 | 
			
		||||
    if (syncData.some(sync => sync.entityName === 'notes' && sync.entityId === getCurrentNoteId())) {
 | 
			
		||||
        infoService.showMessage('Reloading note because of background changes');
 | 
			
		||||
@@ -299,6 +331,20 @@ messagingService.subscribeToSyncMessages(syncData => {
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
$noteDetailWrapper.on("dragover", e => e.preventDefault());
 | 
			
		||||
 | 
			
		||||
$noteDetailWrapper.on("dragleave", e => e.preventDefault());
 | 
			
		||||
 | 
			
		||||
$noteDetailWrapper.on("drop", e => {
 | 
			
		||||
    importDialog.uploadFiles(getCurrentNoteId(), e.originalEvent.dataTransfer.files, {
 | 
			
		||||
        safeImport: true,
 | 
			
		||||
        shrinkImages: true,
 | 
			
		||||
        textImportedAsText: true,
 | 
			
		||||
        codeImportedAsCode: true,
 | 
			
		||||
        explodeArchives: true
 | 
			
		||||
    });
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
$(document).ready(() => {
 | 
			
		||||
    $noteTitle.on('input', () => {
 | 
			
		||||
        noteChanged();
 | 
			
		||||
@@ -320,16 +366,17 @@ setInterval(saveNoteIfChanged, 3000);
 | 
			
		||||
export default {
 | 
			
		||||
    reload,
 | 
			
		||||
    switchToNote,
 | 
			
		||||
    setNoteBackgroundIfProtected,
 | 
			
		||||
    updateNoteView,
 | 
			
		||||
    loadNote,
 | 
			
		||||
    getCurrentNote,
 | 
			
		||||
    getCurrentNoteContent,
 | 
			
		||||
    getCurrentNoteType,
 | 
			
		||||
    getCurrentNoteId,
 | 
			
		||||
    newNoteCreated,
 | 
			
		||||
    focusOnTitle,
 | 
			
		||||
    focusAndSelectTitle,
 | 
			
		||||
    saveNote,
 | 
			
		||||
    saveNoteIfChanged,
 | 
			
		||||
    noteChanged,
 | 
			
		||||
    getCurrentNoteContent,
 | 
			
		||||
    onNoteChange
 | 
			
		||||
    onNoteChange,
 | 
			
		||||
    addDetailLoadedListener
 | 
			
		||||
};
 | 
			
		||||
@@ -49,7 +49,7 @@ async function show() {
 | 
			
		||||
    // this needs to happen after the element is shown, otherwise the editor won't be refreshed
 | 
			
		||||
    // CodeMirror breaks pretty badly on null so even though it shouldn't happen (guarded by consistency check)
 | 
			
		||||
    // we provide fallback
 | 
			
		||||
    codeEditor.setValue(currentNote.content || "");
 | 
			
		||||
    codeEditor.setValue(currentNote.noteContent.content || "");
 | 
			
		||||
 | 
			
		||||
    const info = CodeMirror.findModeByMIME(currentNote.mime);
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -5,6 +5,7 @@ import noteDetailService from "./note_detail.js";
 | 
			
		||||
 | 
			
		||||
const $component = $('#note-detail-file');
 | 
			
		||||
 | 
			
		||||
const $fileNoteId = $("#file-note-id");
 | 
			
		||||
const $fileName = $("#file-filename");
 | 
			
		||||
const $fileType = $("#file-filetype");
 | 
			
		||||
const $fileSize = $("#file-filesize");
 | 
			
		||||
@@ -21,12 +22,18 @@ async function show() {
 | 
			
		||||
 | 
			
		||||
    $component.show();
 | 
			
		||||
 | 
			
		||||
    $fileNoteId.text(currentNote.noteId);
 | 
			
		||||
    $fileName.text(attributeMap.originalFileName || "?");
 | 
			
		||||
    $fileSize.text((attributeMap.fileSize || "?") + " bytes");
 | 
			
		||||
    $fileType.text(currentNote.mime);
 | 
			
		||||
 | 
			
		||||
    $previewRow.toggle(!!currentNote.content);
 | 
			
		||||
    $previewContent.text(currentNote.content);
 | 
			
		||||
    if (currentNote.noteContent && currentNote.noteContent.content) {
 | 
			
		||||
        $previewRow.show();
 | 
			
		||||
        $previewContent.text(currentNote.noteContent.content);
 | 
			
		||||
    }
 | 
			
		||||
    else {
 | 
			
		||||
        $previewRow.hide();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
$downloadButton.click(() => utils.download(getFileUrl()));
 | 
			
		||||
 
 | 
			
		||||
@@ -26,7 +26,7 @@ async function show() {
 | 
			
		||||
    $fileSize.text((attributeMap.fileSize || "?") + " bytes");
 | 
			
		||||
    $fileType.text(currentNote.mime);
 | 
			
		||||
 | 
			
		||||
    $imageView.prop("src", `/api/images/${currentNote.noteId}/${currentNote.title}`);
 | 
			
		||||
    $imageView.prop("src", `api/images/${currentNote.noteId}/${currentNote.title}`);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
$imageDownloadButton.click(() => utils.download(getFileUrl()));
 | 
			
		||||
 
 | 
			
		||||
@@ -93,9 +93,9 @@ function loadMapData() {
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    if (currentNote.content) {
 | 
			
		||||
    if (currentNote.noteContent.content) {
 | 
			
		||||
        try {
 | 
			
		||||
            mapData = JSON.parse(currentNote.content);
 | 
			
		||||
            mapData = JSON.parse(currentNote.noteContent.content);
 | 
			
		||||
        } catch (e) {
 | 
			
		||||
            console.log("Could not parse content: ", e);
 | 
			
		||||
        }
 | 
			
		||||
 
 | 
			
		||||
@@ -1,19 +1,18 @@
 | 
			
		||||
import noteDetailService from "./note_detail.js";
 | 
			
		||||
import treeService from "./tree.js";
 | 
			
		||||
import infoService from './info.js';
 | 
			
		||||
 | 
			
		||||
const $searchString = $("#search-string");
 | 
			
		||||
const $component = $('#note-detail-search');
 | 
			
		||||
 | 
			
		||||
function getContent() {
 | 
			
		||||
    JSON.stringify({
 | 
			
		||||
        searchString: $searchString.val()
 | 
			
		||||
    });
 | 
			
		||||
}
 | 
			
		||||
const $refreshButton = $('#note-detail-search-refresh-results-button');
 | 
			
		||||
 | 
			
		||||
function show() {
 | 
			
		||||
    $component.show();
 | 
			
		||||
 | 
			
		||||
    console.log(noteDetailService.getCurrentNote());
 | 
			
		||||
 | 
			
		||||
    try {
 | 
			
		||||
        const json = JSON.parse(noteDetailService.getCurrentNote().content);
 | 
			
		||||
        const json = JSON.parse(noteDetailService.getCurrentNote().noteContent.content);
 | 
			
		||||
 | 
			
		||||
        $searchString.val(json.searchString);
 | 
			
		||||
    }
 | 
			
		||||
@@ -25,6 +24,20 @@ function show() {
 | 
			
		||||
    $searchString.on('input', noteDetailService.noteChanged);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function getContent() {
 | 
			
		||||
    return JSON.stringify({
 | 
			
		||||
        searchString: $searchString.val()
 | 
			
		||||
    });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
$refreshButton.click(async () => {
 | 
			
		||||
    await noteDetailService.saveNoteIfChanged();
 | 
			
		||||
 | 
			
		||||
    treeService.reload();
 | 
			
		||||
 | 
			
		||||
    infoService.showMessage('Tree has been refreshed.');
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
export default {
 | 
			
		||||
    getContent,
 | 
			
		||||
    show,
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,7 @@
 | 
			
		||||
import libraryLoader from "./library_loader.js";
 | 
			
		||||
import noteDetailService from './note_detail.js';
 | 
			
		||||
import treeService from './tree.js';
 | 
			
		||||
import attributeService from "./attributes.js";
 | 
			
		||||
 | 
			
		||||
const $component = $('#note-detail-text');
 | 
			
		||||
 | 
			
		||||
@@ -19,7 +20,9 @@ async function show() {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    textEditor.setData(noteDetailService.getCurrentNote().content);
 | 
			
		||||
    textEditor.isReadOnly = await isReadOnly();
 | 
			
		||||
 | 
			
		||||
    textEditor.setData(noteDetailService.getCurrentNote().noteContent.content);
 | 
			
		||||
 | 
			
		||||
    $component.show();
 | 
			
		||||
}
 | 
			
		||||
@@ -36,6 +39,12 @@ function getContent() {
 | 
			
		||||
    return content;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
async function isReadOnly() {
 | 
			
		||||
    const attributes = await attributeService.getAttributes();
 | 
			
		||||
 | 
			
		||||
    return attributes.some(attr => attr.type === 'label' && attr.name === 'readOnly');
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function focus() {
 | 
			
		||||
    $component.focus();
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -111,18 +111,18 @@ async function renderTooltip(note, attributes) {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (note.type === 'text') {
 | 
			
		||||
        // surround with <div> for a case when note.content is pure text (e.g. "[protected]") which
 | 
			
		||||
        // surround with <div> for a case when note's content is pure text (e.g. "[protected]") which
 | 
			
		||||
        // then fails the jquery non-empty text test
 | 
			
		||||
        content += '<div>' + note.content + '</div>';
 | 
			
		||||
        content += '<div>' + note.noteContent.content + '</div>';
 | 
			
		||||
    }
 | 
			
		||||
    else if (note.type === 'code') {
 | 
			
		||||
        content += $("<pre>")
 | 
			
		||||
            .text(note.content)
 | 
			
		||||
            .text(note.noteContent.content)
 | 
			
		||||
            .prop('outerHTML');
 | 
			
		||||
    }
 | 
			
		||||
    else if (note.type === 'image') {
 | 
			
		||||
        content += $("<img>")
 | 
			
		||||
            .prop("src", `/api/images/${note.noteId}/${note.title}`)
 | 
			
		||||
            .prop("src", `api/images/${note.noteId}/${note.title}`)
 | 
			
		||||
            .prop('outerHTML');
 | 
			
		||||
    }
 | 
			
		||||
    // other types of notes don't have tooltip preview
 | 
			
		||||
 
 | 
			
		||||
@@ -2,6 +2,7 @@ import treeService from './tree.js';
 | 
			
		||||
import noteDetailService from './note_detail.js';
 | 
			
		||||
import server from './server.js';
 | 
			
		||||
import infoService from "./info.js";
 | 
			
		||||
import confirmDialog from "../dialogs/confirm.js";
 | 
			
		||||
 | 
			
		||||
const $executeScriptButton = $("#execute-script-button");
 | 
			
		||||
const $toggleEditButton = $('#toggle-edit-button');
 | 
			
		||||
@@ -110,35 +111,63 @@ function NoteTypeModel() {
 | 
			
		||||
        self.updateExecuteScriptButtonVisibility();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    this.selectText = function() {
 | 
			
		||||
    function confirmChangeIfContent() {
 | 
			
		||||
        if (!noteDetailService.getCurrentNoteContent()) {
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return confirmDialog.confirm("It is not recommended to change note type when note content is not empty. Do you want to continue anyway?");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    this.selectText = async function() {
 | 
			
		||||
        if (!await confirmChangeIfContent()) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        self.type('text');
 | 
			
		||||
        self.mime('');
 | 
			
		||||
        self.mime('text/html');
 | 
			
		||||
 | 
			
		||||
        save();
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    this.selectRender = function() {
 | 
			
		||||
    this.selectRender = async function() {
 | 
			
		||||
        if (!await confirmChangeIfContent()) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        self.type('render');
 | 
			
		||||
        self.mime('text/html');
 | 
			
		||||
 | 
			
		||||
        save();
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    this.selectRelationMap = function() {
 | 
			
		||||
    this.selectRelationMap = async function() {
 | 
			
		||||
        if (!await confirmChangeIfContent()) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        self.type('relation-map');
 | 
			
		||||
        self.mime('application/json');
 | 
			
		||||
 | 
			
		||||
        save();
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    this.selectCode = function() {
 | 
			
		||||
    this.selectCode = async function() {
 | 
			
		||||
        if (!await confirmChangeIfContent()) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        self.type('code');
 | 
			
		||||
        self.mime('');
 | 
			
		||||
        self.mime('text/plain');
 | 
			
		||||
 | 
			
		||||
        save();
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    this.selectCodeMime = function(el) {
 | 
			
		||||
    this.selectCodeMime = async function(el) {
 | 
			
		||||
        if (!await confirmChangeIfContent()) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        self.type('code');
 | 
			
		||||
        self.mime(el.mime);
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -125,7 +125,7 @@ async function protectNoteAndSendToServer() {
 | 
			
		||||
 | 
			
		||||
    treeService.setProtected(note.noteId, note.isProtected);
 | 
			
		||||
 | 
			
		||||
    noteDetailService.setNoteBackgroundIfProtected(note);
 | 
			
		||||
    noteDetailService.updateNoteView();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
async function unprotectNoteAndSendToServer() {
 | 
			
		||||
@@ -152,7 +152,7 @@ async function unprotectNoteAndSendToServer() {
 | 
			
		||||
 | 
			
		||||
    treeService.setProtected(currentNote.noteId, currentNote.isProtected);
 | 
			
		||||
 | 
			
		||||
    noteDetailService.setNoteBackgroundIfProtected(currentNote);
 | 
			
		||||
    noteDetailService.updateNoteView();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
async function protectSubtree(noteId, protect) {
 | 
			
		||||
@@ -184,5 +184,6 @@ export default {
 | 
			
		||||
    protectSubtree,
 | 
			
		||||
    ensureDialogIsClosed,
 | 
			
		||||
    enterProtectedSession,
 | 
			
		||||
    leaveProtectedSession
 | 
			
		||||
    leaveProtectedSession,
 | 
			
		||||
    protectNoteAndSendToServer
 | 
			
		||||
};
 | 
			
		||||
@@ -8,7 +8,7 @@ let protectedSessionId = null;
 | 
			
		||||
optionsInitService.optionsReady.then(options => protectedSessionTimeout = options.protectedSessionTimeout);
 | 
			
		||||
 | 
			
		||||
setInterval(() => {
 | 
			
		||||
    if (lastProtectedSessionOperationDate !== null && new Date().getTime() - lastProtectedSessionOperationDate.getTime() > protectedSessionTimeout * 1000) {
 | 
			
		||||
    if (lastProtectedSessionOperationDate !== null && Date.now() - lastProtectedSessionOperationDate.getTime() > protectedSessionTimeout * 1000) {
 | 
			
		||||
        resetProtectedSession();
 | 
			
		||||
    }
 | 
			
		||||
}, 5000);
 | 
			
		||||
 
 | 
			
		||||
@@ -1,9 +1,13 @@
 | 
			
		||||
import FrontendScriptApi from './frontend_script_api.js';
 | 
			
		||||
import utils from './utils.js';
 | 
			
		||||
import treeCache from './tree_cache.js';
 | 
			
		||||
 | 
			
		||||
function ScriptContext(startNote, allNotes, originEntity = null) {
 | 
			
		||||
async function ScriptContext(startNoteId, allNoteIds, originEntity = null) {
 | 
			
		||||
    const modules = {};
 | 
			
		||||
 | 
			
		||||
    const startNote = await treeCache.getNote(startNoteId);
 | 
			
		||||
    const allNotes = await treeCache.getNotes(allNoteIds);
 | 
			
		||||
 | 
			
		||||
    return {
 | 
			
		||||
        modules: modules,
 | 
			
		||||
        notes: utils.toObject(allNotes, note => [note.noteId, note]),
 | 
			
		||||
 
 | 
			
		||||
@@ -76,6 +76,15 @@ async function saveSearch() {
 | 
			
		||||
    await treeService.activateNote(noteId);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function init() {
 | 
			
		||||
    const hashValue = treeService.getHashValueFromAddress();
 | 
			
		||||
 | 
			
		||||
    if (hashValue.startsWith("search=")) {
 | 
			
		||||
        showSearch();
 | 
			
		||||
        doSearch(hashValue.substr(7));
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
$searchInput.keyup(e => {
 | 
			
		||||
    const searchText = $searchInput.val();
 | 
			
		||||
 | 
			
		||||
@@ -87,7 +96,7 @@ $searchInput.keyup(e => {
 | 
			
		||||
    if (e && e.which === $.ui.keyCode.ENTER) {
 | 
			
		||||
        doSearch();
 | 
			
		||||
    }
 | 
			
		||||
}).focus();
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
$doSearchButton.click(() => doSearch()); // keep long form because of argument
 | 
			
		||||
$resetSearchButton.click(resetSearch);
 | 
			
		||||
@@ -100,5 +109,6 @@ export default {
 | 
			
		||||
    toggleSearch,
 | 
			
		||||
    resetSearch,
 | 
			
		||||
    showSearch,
 | 
			
		||||
    doSearch
 | 
			
		||||
    doSearch,
 | 
			
		||||
    init
 | 
			
		||||
};
 | 
			
		||||
@@ -83,6 +83,10 @@ async function setNodeTitleWithPrefix(node) {
 | 
			
		||||
    node.setTitle(utils.escapeHtml(title));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function getNode(childNoteId, parentNoteId) {
 | 
			
		||||
    return getNodesByNoteId(childNoteId).find(node => !parentNoteId || node.data.parentNoteId === parentNoteId);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
async function expandToNote(notePath, expandOpts) {
 | 
			
		||||
    utils.assertArguments(notePath);
 | 
			
		||||
 | 
			
		||||
@@ -90,33 +94,56 @@ async function expandToNote(notePath, expandOpts) {
 | 
			
		||||
 | 
			
		||||
    const noteId = treeUtils.getNoteIdFromNotePath(notePath);
 | 
			
		||||
 | 
			
		||||
    const hoistedNoteId = await hoistedNoteService.getHoistedNoteId();
 | 
			
		||||
    let hoistedNoteFound = false;
 | 
			
		||||
 | 
			
		||||
    let parentNoteId = null;
 | 
			
		||||
 | 
			
		||||
    for (const childNoteId of runPath) {
 | 
			
		||||
        // for first node (!parentNoteId) it doesn't matter which node is found
 | 
			
		||||
        const node = getNodesByNoteId(childNoteId).find(node => !parentNoteId || node.data.parentNoteId === parentNoteId);
 | 
			
		||||
 | 
			
		||||
        if (!node) {
 | 
			
		||||
            console.error(`Can't find node for noteId=${childNoteId} with parentNoteId=${parentNoteId}`);
 | 
			
		||||
        if (childNoteId === hoistedNoteId) {
 | 
			
		||||
            hoistedNoteFound = true;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (childNoteId === noteId) {
 | 
			
		||||
            return node;
 | 
			
		||||
        }
 | 
			
		||||
        else {
 | 
			
		||||
            await node.setExpanded(true, expandOpts);
 | 
			
		||||
        // we expand only after hoisted note since before then nodes are not actually present in the tree
 | 
			
		||||
        if (hoistedNoteFound) {
 | 
			
		||||
            // for first node (!parentNoteId) it doesn't matter which node is found
 | 
			
		||||
            let node = getNode(childNoteId, parentNoteId);
 | 
			
		||||
 | 
			
		||||
            if (!node && parentNoteId) {
 | 
			
		||||
                const parents = getNodesByNoteId(parentNoteId);
 | 
			
		||||
 | 
			
		||||
                for (const parent of parents) {
 | 
			
		||||
                    // force load parents. This is useful when fancytree doesn't contain recently created notes yet.
 | 
			
		||||
                    await parent.load(true);
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                node = getNode(childNoteId, parentNoteId);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (!node) {
 | 
			
		||||
                console.error(`Can't find node for noteId=${childNoteId} with parentNoteId=${parentNoteId}`);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (childNoteId === noteId) {
 | 
			
		||||
                return node;
 | 
			
		||||
            } else {
 | 
			
		||||
                await node.setExpanded(true, expandOpts);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        parentNoteId = childNoteId;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
async function activateNote(notePath, newNote) {
 | 
			
		||||
async function activateNote(notePath, noteLoadedListener) {
 | 
			
		||||
    utils.assertArguments(notePath);
 | 
			
		||||
 | 
			
		||||
    // notePath argument can contain only noteId which is not good when hoisted since
 | 
			
		||||
    // then we need to check the whole note path
 | 
			
		||||
    const runNotePath = await getRunPath(notePath);
 | 
			
		||||
    const hoistedNoteId = await hoistedNoteService.getHoistedNoteId();
 | 
			
		||||
 | 
			
		||||
    if (hoistedNoteId !== 'root' && !notePath.includes(hoistedNoteId)) {
 | 
			
		||||
    if (hoistedNoteId !== 'root' && !runNotePath.includes(hoistedNoteId)) {
 | 
			
		||||
        if (!await confirmDialog.confirm("Requested note is outside of hoisted note subtree. Do you want to unhoist?")) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
@@ -131,8 +158,8 @@ async function activateNote(notePath, newNote) {
 | 
			
		||||
 | 
			
		||||
    const node = await expandToNote(notePath);
 | 
			
		||||
 | 
			
		||||
    if (newNote) {
 | 
			
		||||
        noteDetailService.newNoteCreated();
 | 
			
		||||
    if (noteLoadedListener) {
 | 
			
		||||
        noteDetailService.addDetailLoadedListener(node.data.noteId, noteLoadedListener);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // we use noFocus because when we reload the tree because of background changes
 | 
			
		||||
@@ -304,7 +331,7 @@ function addRecentNote(branchId, notePath) {
 | 
			
		||||
    setTimeout(async () => {
 | 
			
		||||
        // we include the note into recent list only if the user stayed on the note at least 5 seconds
 | 
			
		||||
        if (notePath && notePath === getCurrentNotePath()) {
 | 
			
		||||
            await server.put('recent-notes/' + branchId + '/' + encodeURIComponent(notePath));
 | 
			
		||||
            await server.post('recent-notes', { branchId, notePath });
 | 
			
		||||
        }
 | 
			
		||||
    }, 1500);
 | 
			
		||||
}
 | 
			
		||||
@@ -337,6 +364,7 @@ function clearSelectedNodes() {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
async function treeInitialized() {
 | 
			
		||||
    // - is used in mobile to indicate that we don't want to activate any note after load
 | 
			
		||||
    if (startNotePath === '-') {
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
@@ -348,7 +376,6 @@ async function treeInitialized() {
 | 
			
		||||
        startNotePath = null;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // - is used in mobile to indicate that we don't want to activate any note after load
 | 
			
		||||
    if (startNotePath) {
 | 
			
		||||
        const node = await activateNote(startNotePath);
 | 
			
		||||
 | 
			
		||||
@@ -411,14 +438,28 @@ function initFancyTree(tree) {
 | 
			
		||||
        clones: {
 | 
			
		||||
            highlightActiveClones: true
 | 
			
		||||
        },
 | 
			
		||||
        renderNode: async function (event, data) {
 | 
			
		||||
        enhanceTitle: async function (event, data) {
 | 
			
		||||
            const node = data.node;
 | 
			
		||||
            const $span = $(node.span);
 | 
			
		||||
 | 
			
		||||
            if (node.data.noteId !== 'root'
 | 
			
		||||
                && node.data.noteId === await hoistedNoteService.getHoistedNoteId()
 | 
			
		||||
                && $span.find('.unhoist-button').length === 0) {
 | 
			
		||||
 | 
			
		||||
            if (node.data.noteId !== 'root' && node.data.noteId === await hoistedNoteService.getHoistedNoteId()) {
 | 
			
		||||
                const unhoistButton = $('<span>  (<a class="unhoist-button">unhoist</a>)</span>');
 | 
			
		||||
 | 
			
		||||
                $(node.span).append(unhoistButton);
 | 
			
		||||
                $span.append(unhoistButton);
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        // this is done to automatically lazy load all expanded search notes after tree load
 | 
			
		||||
        loadChildren: function(event, data) {
 | 
			
		||||
            data.node.visit(function(subNode){
 | 
			
		||||
                // Load all lazy/unloaded child nodes
 | 
			
		||||
                // (which will trigger `loadChildren` recursively)
 | 
			
		||||
                if( subNode.isUndefined() && subNode.isExpanded() ) {
 | 
			
		||||
                    subNode.load();
 | 
			
		||||
                }
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
@@ -442,24 +483,30 @@ async function reload() {
 | 
			
		||||
    await getTree().reload(notes);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function getNotePathFromAddress() {
 | 
			
		||||
    return document.location.hash.substr(1); // strip initial #
 | 
			
		||||
function isNotePathInAddress() {
 | 
			
		||||
    return getHashValueFromAddress().startsWith("root");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function getHashValueFromAddress() {
 | 
			
		||||
    return document.location.hash ? document.location.hash.substr(1) : ""; // strip initial #
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
async function loadTree() {
 | 
			
		||||
    const resp = await server.get('tree');
 | 
			
		||||
    startNotePath = resp.startNotePath;
 | 
			
		||||
 | 
			
		||||
    if (document.location.hash) {
 | 
			
		||||
        startNotePath = getNotePathFromAddress();
 | 
			
		||||
    if (isNotePathInAddress()) {
 | 
			
		||||
        startNotePath = getHashValueFromAddress();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return await treeBuilder.prepareTree(resp.notes, resp.branches, resp.relations);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function collapseTree(node = null) {
 | 
			
		||||
async function collapseTree(node = null) {
 | 
			
		||||
    if (!node) {
 | 
			
		||||
        node = $tree.fancytree("getRootNode");
 | 
			
		||||
        const hoistedNoteId = await hoistedNoteService.getHoistedNoteId();
 | 
			
		||||
 | 
			
		||||
        node = getNodesByNoteId(hoistedNoteId)[0];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    node.setExpanded(false);
 | 
			
		||||
@@ -500,9 +547,11 @@ async function setNoteTitle(noteId, title) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
async function createNewTopLevelNote() {
 | 
			
		||||
    const rootNode = getNodesByNoteId('root')[0];
 | 
			
		||||
    const hoistedNoteId = await hoistedNoteService.getHoistedNoteId();
 | 
			
		||||
 | 
			
		||||
    await createNote(rootNode, "root", "into", false);
 | 
			
		||||
    const rootNode = getNodesByNoteId(hoistedNoteId)[0];
 | 
			
		||||
 | 
			
		||||
    await createNote(rootNode, hoistedNoteId, "into", false);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
async function createNote(node, parentNoteId, target, isProtected, saveSelection = false) {
 | 
			
		||||
@@ -547,7 +596,7 @@ async function createNote(node, parentNoteId, target, isProtected, saveSelection
 | 
			
		||||
 | 
			
		||||
    await noteDetailService.saveNoteIfChanged();
 | 
			
		||||
 | 
			
		||||
    noteDetailService.newNoteCreated();
 | 
			
		||||
    noteDetailService.addDetailLoadedListener(note.noteId, noteDetailService.focusAndSelectTitle);
 | 
			
		||||
 | 
			
		||||
    const noteEntity = new NoteShort(treeCache, note);
 | 
			
		||||
    const branchEntity = new Branch(treeCache, branch);
 | 
			
		||||
@@ -578,7 +627,10 @@ async function createNote(node, parentNoteId, target, isProtected, saveSelection
 | 
			
		||||
 | 
			
		||||
        await node.getLastChild().setActive(true);
 | 
			
		||||
 | 
			
		||||
        const parentNoteEntity = await treeCache.getNote(node.data.noteId);
 | 
			
		||||
 | 
			
		||||
        node.folder = true;
 | 
			
		||||
        node.icon = await treeBuilder.getIcon(parentNoteEntity); // icon might change into folder
 | 
			
		||||
        node.renderTitle();
 | 
			
		||||
    }
 | 
			
		||||
    else {
 | 
			
		||||
@@ -634,11 +686,15 @@ messagingService.subscribeToSyncMessages(syncData => {
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
utils.bindShortcut('ctrl+o', () => {
 | 
			
		||||
utils.bindShortcut('ctrl+o', async () => {
 | 
			
		||||
    const node = getCurrentNode();
 | 
			
		||||
    const parentNoteId = node.data.parentNoteId;
 | 
			
		||||
    const isProtected = treeUtils.getParentProtectedStatus(node);
 | 
			
		||||
 | 
			
		||||
    if (node.data.noteId === 'root' || node.data.noteId === await hoistedNoteService.getHoistedNoteId()) {
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    createNote(node, parentNoteId, 'after', isProtected, true);
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
@@ -655,12 +711,14 @@ utils.bindShortcut('ctrl+p', createNoteInto);
 | 
			
		||||
utils.bindShortcut('ctrl+.', scrollToCurrentNote);
 | 
			
		||||
 | 
			
		||||
$(window).bind('hashchange', function() {
 | 
			
		||||
    const notePath = getNotePathFromAddress();
 | 
			
		||||
    if (isNotePathInAddress()) {
 | 
			
		||||
        const notePath = getHashValueFromAddress();
 | 
			
		||||
 | 
			
		||||
    if (notePath !== '-' && getCurrentNotePath() !== notePath) {
 | 
			
		||||
        console.debug("Switching to " + notePath + " because of hash change");
 | 
			
		||||
        if (notePath !== '-' && getCurrentNotePath() !== notePath) {
 | 
			
		||||
            console.debug("Switching to " + notePath + " because of hash change");
 | 
			
		||||
 | 
			
		||||
        activateNote(notePath);
 | 
			
		||||
            activateNote(notePath);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
@@ -693,5 +751,6 @@ export default {
 | 
			
		||||
    showTree,
 | 
			
		||||
    loadTree,
 | 
			
		||||
    treeInitialized,
 | 
			
		||||
    setExpandedToServer
 | 
			
		||||
    setExpandedToServer,
 | 
			
		||||
    getHashValueFromAddress
 | 
			
		||||
};
 | 
			
		||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user