| 
									
										
										
										
											2017-09-06 22:06:43 -04:00
										 |  |  | let globalEncryptionCallback = null; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | function handleEncryption(requireEncryption, modal, callback) { | 
					
						
							| 
									
										
										
										
											2017-09-17 00:18:03 -04:00
										 |  |  |     if (requireEncryption && globalDataKey === null) { | 
					
						
							| 
									
										
										
										
											2017-09-06 22:06:43 -04:00
										 |  |  |         globalEncryptionCallback = callback; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if (!modal) { | 
					
						
							|  |  |  |             $("#noteDetailWrapper").hide(); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         $("#encryptionPasswordDialog").dialog({ | 
					
						
							|  |  |  |             modal: modal, | 
					
						
							|  |  |  |             width: 400, | 
					
						
							|  |  |  |             open: function() { | 
					
						
							|  |  |  |                 if (!modal) { | 
					
						
							|  |  |  |                     // dialog steals focus for itself, which is not what we want for non-modal (viewing)
 | 
					
						
							| 
									
										
										
										
											2017-09-09 12:06:15 -04:00
										 |  |  |                     getNodeByKey(globalCurrentNote.detail.note_id).setFocus(); | 
					
						
							| 
									
										
										
										
											2017-09-06 22:06:43 -04:00
										 |  |  |                 } | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         }); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     else { | 
					
						
							|  |  |  |         callback(); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-09-17 00:18:03 -04:00
										 |  |  | let globalDataKey = null; | 
					
						
							| 
									
										
										
										
											2017-09-06 22:06:43 -04:00
										 |  |  | let globalLastEncryptionOperationDate = null; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-09-17 00:18:03 -04:00
										 |  |  | function getDataKey(password) { | 
					
						
							| 
									
										
										
										
											2017-09-16 23:21:46 -04:00
										 |  |  |     return computeScrypt(password, globalEncryptionSalt, (key, resolve, reject) => { | 
					
						
							|  |  |  |         const dataKeyAes = getDataKeyAes(key); | 
					
						
							| 
									
										
										
										
											2017-09-06 22:06:43 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-09-16 23:21:46 -04:00
										 |  |  |         const decryptedDataKey = decrypt(dataKeyAes, globalEncryptedDataKey); | 
					
						
							| 
									
										
										
										
											2017-09-08 20:55:24 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-09-16 23:21:46 -04:00
										 |  |  |         if (decryptedDataKey === false) { | 
					
						
							|  |  |  |             reject("Wrong password."); | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2017-09-08 20:55:24 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-09-17 00:18:03 -04:00
										 |  |  |         resolve(decryptedDataKey); | 
					
						
							| 
									
										
										
										
											2017-09-16 23:21:46 -04:00
										 |  |  |     }); | 
					
						
							| 
									
										
										
										
											2017-09-08 20:55:24 -04:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | function computeScrypt(password, salt, callback) { | 
					
						
							|  |  |  |     const normalizedPassword = password.normalize('NFKC'); | 
					
						
							| 
									
										
										
										
											2017-09-06 22:06:43 -04:00
										 |  |  |     const passwordBuffer = new buffer.SlowBuffer(normalizedPassword); | 
					
						
							|  |  |  |     const saltBuffer = new buffer.SlowBuffer(salt); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // this settings take ~500ms on my laptop
 | 
					
						
							| 
									
										
										
										
											2017-09-08 20:55:24 -04:00
										 |  |  |     const N = 16384, r = 8, p = 1; | 
					
						
							| 
									
										
										
										
											2017-09-06 22:06:43 -04:00
										 |  |  |     // 32 byte key - AES 256
 | 
					
						
							|  |  |  |     const dkLen = 32; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     const startedDate = new Date(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return new Promise((resolve, reject) => { | 
					
						
							|  |  |  |         scrypt(passwordBuffer, saltBuffer, N, r, p, dkLen, function (error, progress, key) { | 
					
						
							|  |  |  |             if (error) { | 
					
						
							|  |  |  |                 console.log("Error: " + error); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 reject(); | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             else if (key) { | 
					
						
							|  |  |  |                 console.log("Computation took " + (new Date().getTime() - startedDate.getTime()) + "ms"); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-09-08 20:55:24 -04:00
										 |  |  |                 callback(key, resolve, reject); | 
					
						
							| 
									
										
										
										
											2017-09-06 22:06:43 -04:00
										 |  |  |             } | 
					
						
							|  |  |  |             else { | 
					
						
							|  |  |  |                 // update UI with progress complete
 | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         }); | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | $("#encryptionPasswordForm").submit(function() { | 
					
						
							|  |  |  |     const password = $("#encryptionPassword").val(); | 
					
						
							|  |  |  |     $("#encryptionPassword").val(""); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-09-17 00:18:03 -04:00
										 |  |  |     getDataKey(password).then(key => { | 
					
						
							| 
									
										
										
										
											2017-09-06 22:06:43 -04:00
										 |  |  |         $("#noteDetailWrapper").show(); | 
					
						
							|  |  |  |         $("#encryptionPasswordDialog").dialog("close"); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-09-17 00:18:03 -04:00
										 |  |  |         globalDataKey = key; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-09-06 23:13:39 -04:00
										 |  |  |         for (const noteId of globalAllNoteIds) { | 
					
						
							|  |  |  |             const note = getNodeByKey(noteId); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             if (note.data.encryption > 0) { | 
					
						
							|  |  |  |                 const title = decryptString(note.data.note_title); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 note.setTitle(title); | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-09-06 22:06:43 -04:00
										 |  |  |         if (globalEncryptionCallback !== null) { | 
					
						
							|  |  |  |             globalEncryptionCallback(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             globalEncryptionCallback = null; | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2017-09-16 23:21:46 -04:00
										 |  |  |     }) | 
					
						
							| 
									
										
										
										
											2017-09-17 12:46:14 -04:00
										 |  |  |         .catch(reason => { | 
					
						
							|  |  |  |             console.log(reason); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             alert(reason); | 
					
						
							|  |  |  |         }); | 
					
						
							| 
									
										
										
										
											2017-09-06 22:06:43 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  |     return false; | 
					
						
							|  |  |  | }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-09-12 23:07:08 -04:00
										 |  |  | function resetEncryptionSession() { | 
					
						
							| 
									
										
										
										
											2017-09-17 00:18:03 -04:00
										 |  |  |     globalDataKey = null; | 
					
						
							| 
									
										
										
										
											2017-09-06 22:06:43 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-09-12 23:07:08 -04:00
										 |  |  |     if (globalCurrentNote.detail.encryption > 0) { | 
					
						
							|  |  |  |         loadNote(globalCurrentNote.detail.note_id); | 
					
						
							| 
									
										
										
										
											2017-09-06 22:06:43 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-09-12 23:07:08 -04:00
										 |  |  |         for (const noteId of globalAllNoteIds) { | 
					
						
							|  |  |  |             const note = getNodeByKey(noteId); | 
					
						
							| 
									
										
										
										
											2017-09-06 23:13:39 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-09-12 23:07:08 -04:00
										 |  |  |             if (note.data.encryption > 0) { | 
					
						
							|  |  |  |                 note.setTitle("[encrypted]"); | 
					
						
							| 
									
										
										
										
											2017-09-06 22:06:43 -04:00
										 |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2017-09-06 23:16:54 -04:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2017-09-12 23:07:08 -04:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | setInterval(function() { | 
					
						
							|  |  |  |     if (globalLastEncryptionOperationDate !== null && new Date().getTime() - globalLastEncryptionOperationDate.getTime() > globalEncryptionSessionTimeout * 1000) { | 
					
						
							|  |  |  |         resetEncryptionSession(); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2017-09-06 23:16:54 -04:00
										 |  |  | }, 5000); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-09-08 22:43:02 -04:00
										 |  |  | function isEncryptionAvailable() { | 
					
						
							| 
									
										
										
										
											2017-09-17 00:18:03 -04:00
										 |  |  |     return globalDataKey !== null; | 
					
						
							| 
									
										
										
										
											2017-09-08 22:43:02 -04:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-09-16 23:21:46 -04:00
										 |  |  | function getDataAes() { | 
					
						
							| 
									
										
										
										
											2017-09-06 23:16:54 -04:00
										 |  |  |     globalLastEncryptionOperationDate = new Date(); | 
					
						
							| 
									
										
										
										
											2017-09-06 22:06:43 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-09-17 00:18:03 -04:00
										 |  |  |     return new aesjs.ModeOfOperation.ctr(globalDataKey, new aesjs.Counter(5)); | 
					
						
							| 
									
										
										
										
											2017-09-06 22:06:43 -04:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-09-16 23:21:46 -04:00
										 |  |  | function getDataKeyAes(key) { | 
					
						
							|  |  |  |     return new aesjs.ModeOfOperation.ctr(key, new aesjs.Counter(5)); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-09-06 22:06:43 -04:00
										 |  |  | function encryptNoteIfNecessary(note) { | 
					
						
							|  |  |  |     if (note.detail.encryption === 0) { | 
					
						
							|  |  |  |         return note; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     else { | 
					
						
							|  |  |  |         return encryptNote(note); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-09-06 23:13:39 -04:00
										 |  |  | function encryptString(str) { | 
					
						
							| 
									
										
										
										
											2017-09-16 23:21:46 -04:00
										 |  |  |     return encrypt(getDataAes(), str); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | function encrypt(aes, str) { | 
					
						
							|  |  |  |     const payload = aesjs.utils.utf8.toBytes(str); | 
					
						
							|  |  |  |     const digest = sha256Array(payload).slice(0, 4); | 
					
						
							| 
									
										
										
										
											2017-09-06 23:13:39 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-09-16 23:21:46 -04:00
										 |  |  |     const digestWithPayload = concat(digest, payload); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     const encryptedBytes = aes.encrypt(digestWithPayload); | 
					
						
							| 
									
										
										
										
											2017-09-06 23:13:39 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  |     return uint8ToBase64(encryptedBytes); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | function decryptString(encryptedBase64) { | 
					
						
							| 
									
										
										
										
											2017-09-16 23:21:46 -04:00
										 |  |  |     const decryptedBytes = decrypt(getDataAes(), encryptedBase64); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return aesjs.utils.utf8.fromBytes(decryptedBytes); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | function decrypt(aes, encryptedBase64) { | 
					
						
							| 
									
										
										
										
											2017-09-06 23:13:39 -04:00
										 |  |  |     const encryptedBytes = base64ToUint8Array(encryptedBase64); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     const decryptedBytes = aes.decrypt(encryptedBytes); | 
					
						
							| 
									
										
										
										
											2017-09-06 22:06:43 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-09-16 23:21:46 -04:00
										 |  |  |     const digest = decryptedBytes.slice(0, 4); | 
					
						
							|  |  |  |     const payload = decryptedBytes.slice(4); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     const hashArray = sha256Array(payload); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     const computedDigest = hashArray.slice(0, 4); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (!arraysIdentical(digest, computedDigest)) { | 
					
						
							|  |  |  |         return false; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return payload; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | function concat(a, b) { | 
					
						
							|  |  |  |     const result = []; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     for (let key in a) { | 
					
						
							|  |  |  |         result.push(a[key]); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     for (let key in b) { | 
					
						
							|  |  |  |         result.push(b[key]); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return result; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | function sha256Array(content) { | 
					
						
							|  |  |  |     const hash = sha256.create(); | 
					
						
							|  |  |  |     hash.update(content); | 
					
						
							|  |  |  |     return hash.array(); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | function arraysIdentical(a, b) { | 
					
						
							|  |  |  |     let i = a.length; | 
					
						
							|  |  |  |     if (i !== b.length) return false; | 
					
						
							|  |  |  |     while (i--) { | 
					
						
							|  |  |  |         if (a[i] !== b[i]) return false; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     return true; | 
					
						
							| 
									
										
										
										
											2017-09-06 23:13:39 -04:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2017-09-06 22:06:43 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-09-06 23:13:39 -04:00
										 |  |  | function encryptNote(note) { | 
					
						
							|  |  |  |     note.detail.note_title = encryptString(note.detail.note_title); | 
					
						
							|  |  |  |     note.detail.note_text = encryptString(note.detail.note_text); | 
					
						
							| 
									
										
										
										
											2017-09-06 22:06:43 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-09-06 23:13:39 -04:00
										 |  |  |     note.detail.encryption = 1; | 
					
						
							| 
									
										
										
										
											2017-09-06 22:06:43 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  |     return note; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | function encryptNoteAndSendToServer() { | 
					
						
							|  |  |  |     handleEncryption(true, true, () => { | 
					
						
							| 
									
										
										
										
											2017-09-09 12:06:15 -04:00
										 |  |  |         const note = globalCurrentNote; | 
					
						
							| 
									
										
										
										
											2017-09-06 22:06:43 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  |         updateNoteFromInputs(note); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         encryptNote(note); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         saveNoteToServer(note); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         setNoteBackgroundIfEncrypted(note); | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | function decryptNoteAndSendToServer() { | 
					
						
							|  |  |  |     handleEncryption(true, true, () => { | 
					
						
							| 
									
										
										
										
											2017-09-09 12:06:15 -04:00
										 |  |  |         const note = globalCurrentNote; | 
					
						
							| 
									
										
										
										
											2017-09-06 22:06:43 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  |         updateNoteFromInputs(note); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         note.detail.encryption = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         saveNoteToServer(note); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         setNoteBackgroundIfEncrypted(note); | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | function decryptNoteIfNecessary(note) { | 
					
						
							| 
									
										
										
										
											2017-09-06 23:13:39 -04:00
										 |  |  |     if (note.detail.encryption > 0) { | 
					
						
							| 
									
										
										
										
											2017-09-16 11:37:50 -04:00
										 |  |  |         decryptNote(note); | 
					
						
							| 
									
										
										
										
											2017-09-06 22:06:43 -04:00
										 |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-09-06 23:13:39 -04:00
										 |  |  | function decryptNote(note) { | 
					
						
							|  |  |  |     note.detail.note_title = decryptString(note.detail.note_title); | 
					
						
							|  |  |  |     note.detail.note_text = decryptString(note.detail.note_text); | 
					
						
							| 
									
										
										
										
											2017-09-17 12:46:14 -04:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | function encryptSubTree(noteId) { | 
					
						
							|  |  |  |     handleEncryption(true, true, () => { | 
					
						
							|  |  |  |         updateSubTreeRecursively(noteId, note => { | 
					
						
							|  |  |  |             if (note.detail.encryption === null || note.detail.encryption === 0) { | 
					
						
							|  |  |  |                 encryptNote(note); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 note.detail.encryption = 1; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 return true; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             else { | 
					
						
							|  |  |  |                 return false; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         }, | 
					
						
							|  |  |  |             note => { | 
					
						
							|  |  |  |                 if (note.detail.note_id === globalCurrentNote.detail.note_id) { | 
					
						
							|  |  |  |                     loadNote(note.detail.note_id); | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |                 else { | 
					
						
							|  |  |  |                     setTreeBasedOnEncryption(note); | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |             }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         alert("Encryption finished."); | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | function decryptSubTree(noteId) { | 
					
						
							|  |  |  |     handleEncryption(true, true, () => { | 
					
						
							|  |  |  |         updateSubTreeRecursively(noteId, note => { | 
					
						
							|  |  |  |             if (note.detail.encryption === 1) { | 
					
						
							|  |  |  |                 decryptNote(note); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 note.detail.encryption = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 return true; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             else { | 
					
						
							|  |  |  |                 return false; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         }, | 
					
						
							|  |  |  |             note => { | 
					
						
							|  |  |  |                 if (note.detail.note_id === globalCurrentNote.detail.note_id) { | 
					
						
							|  |  |  |                     loadNote(note.detail.note_id); | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |                 else { | 
					
						
							|  |  |  |                     setTreeBasedOnEncryption(note); | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |             }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         alert("Decryption finished."); | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | function updateSubTreeRecursively(noteId, updateCallback, successCallback) { | 
					
						
							|  |  |  |     updateNoteSynchronously(noteId, updateCallback, successCallback); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     const node = getNodeByKey(noteId); | 
					
						
							|  |  |  |     if (!node || !node.getChildren()) { | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     for (const child of node.getChildren()) { | 
					
						
							|  |  |  |         updateSubTreeRecursively(child.key, updateCallback, successCallback); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | function updateNoteSynchronously(noteId, updateCallback, successCallback) { | 
					
						
							|  |  |  |     $.ajax({ | 
					
						
							|  |  |  |         url: baseUrl + 'notes/' + noteId, | 
					
						
							|  |  |  |         type: 'GET', | 
					
						
							|  |  |  |         async: false, | 
					
						
							|  |  |  |         success: function (note) { | 
					
						
							|  |  |  |             const needSave = updateCallback(note); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             if (!needSave) { | 
					
						
							|  |  |  |                 return; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             for (const link of note.links) { | 
					
						
							|  |  |  |                 delete link.type; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             $.ajax({ | 
					
						
							|  |  |  |                 url: baseUrl + 'notes/' + noteId, | 
					
						
							|  |  |  |                 type: 'PUT', | 
					
						
							|  |  |  |                 data: JSON.stringify(note), | 
					
						
							|  |  |  |                 contentType: "application/json", | 
					
						
							|  |  |  |                 async: false, | 
					
						
							|  |  |  |                 success: function () { | 
					
						
							|  |  |  |                     if (successCallback) { | 
					
						
							|  |  |  |                         successCallback(note); | 
					
						
							|  |  |  |                     } | 
					
						
							|  |  |  |                 }, | 
					
						
							|  |  |  |                 error: function () { | 
					
						
							|  |  |  |                     console.log("Updating " + noteId + " failed."); | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |             }); | 
					
						
							|  |  |  |         }, | 
					
						
							|  |  |  |         error: function () { | 
					
						
							|  |  |  |             console.log("Reading " + noteId + " failed."); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     }); | 
					
						
							| 
									
										
										
										
											2017-09-06 22:06:43 -04:00
										 |  |  | } |