Compare commits
10 Commits
5f049cf587
...
d767f80874
Author | SHA1 | Date |
---|---|---|
quenousimporte | d767f80874 | |
quenousimporte | c425725423 | |
quenousimporte | 77ddc4907f | |
quenousimporte | 717b2a1bce | |
quenousimporte | e8c5d38b3a | |
quenousimporte | 682a18132d | |
quenousimporte | e896275724 | |
quenousimporte | e0a365b612 | |
quenousimporte | a1c0033e26 | |
quenousimporte | 4f33d65317 |
344
main.js
344
main.js
|
@ -31,6 +31,7 @@ var workerid = null;
|
||||||
var backup = "";
|
var backup = "";
|
||||||
var settings = null;
|
var settings = null;
|
||||||
var tags = null;
|
var tags = null;
|
||||||
|
var pending = {};
|
||||||
|
|
||||||
var commands = [
|
var commands = [
|
||||||
{
|
{
|
||||||
|
@ -58,7 +59,7 @@ var commands = [
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
hint: "Force save",
|
hint: "Force save",
|
||||||
action: serialize,
|
action: flush,
|
||||||
shortcut: "ctrl+s"
|
shortcut: "ctrl+s"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -284,7 +285,7 @@ function seteditorcontent(content)
|
||||||
{
|
{
|
||||||
md.value = content;
|
md.value = content;
|
||||||
applycolors();
|
applycolors();
|
||||||
serialize();
|
flush();
|
||||||
resize();
|
resize();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -351,6 +352,46 @@ function currentrange()
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function pushitem(key, value)
|
||||||
|
{
|
||||||
|
unsavedmark.hidden = false;
|
||||||
|
pending[key] = true;
|
||||||
|
var name = metadata && metadata[key] ? metadata[key].title : key;
|
||||||
|
|
||||||
|
queryremote({action: "push", name: key, data: value})
|
||||||
|
.then( () =>
|
||||||
|
{
|
||||||
|
console.log("'" + name + "' pushed to server");
|
||||||
|
delete pending[key];
|
||||||
|
})
|
||||||
|
.catch( err =>
|
||||||
|
{
|
||||||
|
console.error("error while pushing '" + name + "': " + err);
|
||||||
|
showtemporaryinfo(msg);
|
||||||
|
setTimeout( () =>
|
||||||
|
{
|
||||||
|
serialize(key, value);
|
||||||
|
}, settings.savedelay);
|
||||||
|
})
|
||||||
|
.finally( () =>
|
||||||
|
{
|
||||||
|
unsavedmark.hidden = Object.keys(pending) == 0;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function serialize(key, value)
|
||||||
|
{
|
||||||
|
localStorage.setItem(key, value);
|
||||||
|
|
||||||
|
var name = metadata && metadata[key] ? metadata[key].title : key;
|
||||||
|
console.log("'" + name + "' serialized locally");
|
||||||
|
|
||||||
|
if (settings.sync)
|
||||||
|
{
|
||||||
|
pushitem(key, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function createsubnote()
|
function createsubnote()
|
||||||
{
|
{
|
||||||
var title = prompt("Subnote tite:");
|
var title = prompt("Subnote tite:");
|
||||||
|
@ -374,7 +415,7 @@ function createsubnote()
|
||||||
var range = currentrange();
|
var range = currentrange();
|
||||||
var content = getrangecontent(range);
|
var content = getrangecontent(range);
|
||||||
guid = createnote(title);
|
guid = createnote(title);
|
||||||
localStorage.setItem(guid, content);
|
serialize(guid, content);
|
||||||
|
|
||||||
seteditorcontent(md.value.substring(0, range.start)
|
seteditorcontent(md.value.substring(0, range.start)
|
||||||
+ "[[" + title + "]]"
|
+ "[[" + title + "]]"
|
||||||
|
@ -445,6 +486,7 @@ function showinfo()
|
||||||
showtemporaryinfo(
|
showtemporaryinfo(
|
||||||
[
|
[
|
||||||
"sync: " + (settings.sync ? "en" : "dis") + "abled",
|
"sync: " + (settings.sync ? "en" : "dis") + "abled",
|
||||||
|
"pending: " + Object.keys(pending).join(),
|
||||||
"title: " + title.value,
|
"title: " + title.value,
|
||||||
"line count: " + md.value.split("\n").length,
|
"line count: " + md.value.split("\n").length,
|
||||||
"word count: " + getwords(),
|
"word count: " + getwords(),
|
||||||
|
@ -457,7 +499,7 @@ function showinfo()
|
||||||
|
|
||||||
function savesettings()
|
function savesettings()
|
||||||
{
|
{
|
||||||
window.localStorage.setItem("settings", JSON.stringify(settings));
|
localStorage.setItem("settings", JSON.stringify(settings));
|
||||||
}
|
}
|
||||||
|
|
||||||
function descendants(note)
|
function descendants(note)
|
||||||
|
@ -1044,9 +1086,9 @@ function inserttodo(text)
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
localStorage.setItem(guid, content);
|
serialize(guid, content);
|
||||||
metadata[guid].lastchanged = Date.now();
|
metadata[guid].lastchanged = Date.now();
|
||||||
serialize();
|
flush();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1118,7 +1160,7 @@ function createnote(title)
|
||||||
};
|
};
|
||||||
metadata[guid] = item;
|
metadata[guid] = item;
|
||||||
serializeindex()
|
serializeindex()
|
||||||
localStorage.setItem(guid, content);
|
serialize(guid, content);
|
||||||
return guid;
|
return guid;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1202,10 +1244,10 @@ function loadsettings()
|
||||||
|
|
||||||
function checksaved()
|
function checksaved()
|
||||||
{
|
{
|
||||||
/*if (!saved)
|
if (!unsavedmark.hidden)
|
||||||
{
|
{
|
||||||
return "not saved";
|
return "not saved";
|
||||||
}*/
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function initsnippets()
|
function initsnippets()
|
||||||
|
@ -1262,7 +1304,8 @@ function migratelegacystorage()
|
||||||
var legacy = localStorage.getItem("data");
|
var legacy = localStorage.getItem("data");
|
||||||
if (legacy)
|
if (legacy)
|
||||||
{
|
{
|
||||||
alert("Old data model detected. Click ok to migrate, or close app.")
|
alert("Hey! I am about to migrate your notes to the brand new data model. No worries, I will keep a backup somewhere in case things go wrong. Take a deep breath, and click ok when you're ready.");
|
||||||
|
localStorage.setItem("legacy", legacy);
|
||||||
legacy = JSON.parse(legacy);
|
legacy = JSON.parse(legacy);
|
||||||
var index = {};
|
var index = {};
|
||||||
legacy.reverse().forEach( (note, i) =>
|
legacy.reverse().forEach( (note, i) =>
|
||||||
|
@ -1291,6 +1334,18 @@ function pushall()
|
||||||
return Promise.all(list);
|
return Promise.all(list);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function fetchandserialize(guid)
|
||||||
|
{
|
||||||
|
var name = (metadata && metadata[guid]) ? metadata[guid] : guid;
|
||||||
|
console.log("fetching '" + name + "'");
|
||||||
|
return queryremote({action: "fetch", name: guid})
|
||||||
|
.then(content =>
|
||||||
|
{
|
||||||
|
localStorage.setItem(guid, content);
|
||||||
|
console.log("'" + name + "' fetched and serialized");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
function fetch()
|
function fetch()
|
||||||
{
|
{
|
||||||
return new Promise(function(resolve)
|
return new Promise(function(resolve)
|
||||||
|
@ -1311,26 +1366,49 @@ function fetch()
|
||||||
{
|
{
|
||||||
if (!filecontent)
|
if (!filecontent)
|
||||||
{
|
{
|
||||||
return pushall();
|
if (confirm("No remote index found. Init remote now?"))
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// compare and fetch modified and serialize all that
|
|
||||||
}
|
|
||||||
|
|
||||||
}).catch(err =>
|
|
||||||
{
|
|
||||||
if (err == "error: authent")
|
|
||||||
{
|
{
|
||||||
settings.password = prompt("Password: ", settings.password);
|
pushall().then(resolve);
|
||||||
savesettings();
|
|
||||||
init();
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
showtemporaryinfo(err);
|
resolve();
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var localindex = JSON.parse(localStorage.getItem("index"));
|
||||||
|
var remoteindex = JSON.parse(filecontent);
|
||||||
|
|
||||||
|
var list = [];
|
||||||
|
Object.keys(remoteindex).forEach(guid =>
|
||||||
|
{
|
||||||
|
if (!localindex[guid] || localindex[guid].lastchanged < remoteindex[guid].lastchanged)
|
||||||
|
{
|
||||||
|
list.push(fetchandserialize(guid));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Promise.all(list).then( () =>
|
||||||
|
{
|
||||||
|
localStorage.setItem("index", JSON.stringify(remoteindex));
|
||||||
|
resolve();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(err =>
|
||||||
|
{
|
||||||
|
if (err == "error: authent")
|
||||||
|
{
|
||||||
|
settings.password = prompt("Password: ", settings.password);
|
||||||
|
savesettings();
|
||||||
|
init();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
showtemporaryinfo(err);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -1347,6 +1425,7 @@ function init()
|
||||||
|
|
||||||
window.onbeforeunload = checksaved;
|
window.onbeforeunload = checksaved;
|
||||||
window.onclick = focuseditor;
|
window.onclick = focuseditor;
|
||||||
|
title.value = "";
|
||||||
|
|
||||||
initsnippets();
|
initsnippets();
|
||||||
|
|
||||||
|
@ -1369,53 +1448,74 @@ function init()
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function encryptdata(params)
|
||||||
|
{
|
||||||
|
if (params.data)
|
||||||
|
{
|
||||||
|
return encryptstring(params.data)
|
||||||
|
.then(encrypted =>
|
||||||
|
{
|
||||||
|
params.data = encrypted;
|
||||||
|
return Promise.resolve(params);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return Promise.resolve(params);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function queryremote(params)
|
function queryremote(params)
|
||||||
{
|
{
|
||||||
return new Promise( (apply, failed) => {
|
return encryptdata(params)
|
||||||
|
.then(encparams =>
|
||||||
params.password = settings.password;
|
{
|
||||||
|
return new Promise ( (resolve, reject) =>
|
||||||
var paramlist = [];
|
|
||||||
for (var i in params)
|
|
||||||
{
|
{
|
||||||
paramlist.push(i + "=" + encodeURIComponent(params[i]));
|
encparams.password = settings.password;
|
||||||
}
|
|
||||||
|
|
||||||
var xhr = new XMLHttpRequest();
|
var paramlist = [];
|
||||||
xhr.open("POST", "handler.php");
|
for (var i in encparams)
|
||||||
xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
|
|
||||||
|
|
||||||
xhr.onerror = function()
|
|
||||||
{
|
|
||||||
failed("XMLHttpRequest error");
|
|
||||||
}
|
|
||||||
|
|
||||||
xhr.onload = function()
|
|
||||||
{
|
|
||||||
if (xhr.status !== 200)
|
|
||||||
{
|
{
|
||||||
failed("Http status " + xhr.status);
|
paramlist.push(i + "=" + encodeURIComponent(encparams[i]));
|
||||||
}
|
}
|
||||||
else
|
|
||||||
|
var xhr = new XMLHttpRequest();
|
||||||
|
xhr.open("POST", "handler.php");
|
||||||
|
xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
|
||||||
|
|
||||||
|
xhr.onerror = function()
|
||||||
{
|
{
|
||||||
decryptstring(xhr.responseText)
|
reject("XMLHttpRequest error");
|
||||||
.then(decrypted =>
|
}
|
||||||
|
|
||||||
|
xhr.onload = function()
|
||||||
|
{
|
||||||
|
if (xhr.status !== 200)
|
||||||
{
|
{
|
||||||
if (decrypted.startsWith("error: "))
|
reject("Http status " + xhr.status);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
decryptstring(xhr.responseText)
|
||||||
|
.then(decrypted =>
|
||||||
{
|
{
|
||||||
failed(decrypted);
|
if (decrypted.startsWith("error: "))
|
||||||
}
|
{
|
||||||
else
|
reject(decrypted);
|
||||||
{
|
}
|
||||||
apply(decrypted);
|
else
|
||||||
}
|
{
|
||||||
});
|
resolve(decrypted);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
var paramstring = paramlist.join("&");
|
var paramstring = paramlist.join("&");
|
||||||
console.log("http request length: " + formatsize(paramstring.length));
|
console.log("http '" + encparams.action + "' request length: " + formatsize(paramstring.length));
|
||||||
xhr.send(paramstring);
|
xhr.send(paramstring);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1800,12 +1900,12 @@ function postpone()
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function serialize()
|
function flush()
|
||||||
{
|
{
|
||||||
clearTimeout(workerid);
|
clearTimeout(workerid);
|
||||||
workerid = null;
|
workerid = null;
|
||||||
|
|
||||||
var guid = getguid(window.title.value);
|
var guid = getguid(title.value);
|
||||||
if (guid)
|
if (guid)
|
||||||
{
|
{
|
||||||
var item = metadata[guid];
|
var item = metadata[guid];
|
||||||
|
@ -1814,8 +1914,8 @@ function serialize()
|
||||||
item.header = indexheader(md.value);
|
item.header = indexheader(md.value);
|
||||||
item.lastchanged = Date.now();
|
item.lastchanged = Date.now();
|
||||||
|
|
||||||
serializeindex()
|
serializeindex();
|
||||||
localStorage.setItem(guid, md.value);
|
serialize(guid, md.value);
|
||||||
}
|
}
|
||||||
else if (title.value == "settings.json")
|
else if (title.value == "settings.json")
|
||||||
{
|
{
|
||||||
|
@ -1827,96 +1927,10 @@ function serialize()
|
||||||
{
|
{
|
||||||
localStorage.setItem("pgpkeys", md.value);
|
localStorage.setItem("pgpkeys", md.value);
|
||||||
}
|
}
|
||||||
console.log("data serialized");
|
|
||||||
|
unsavedmark.hidden = Object.keys(pending).length == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*function save()
|
|
||||||
{
|
|
||||||
return new Promise(function(resolve, reject)
|
|
||||||
{
|
|
||||||
clearTimeout(workerid);
|
|
||||||
|
|
||||||
if (title.value == "settings.json")
|
|
||||||
{
|
|
||||||
settings = JSON.parse(md.value);
|
|
||||||
savesettings();
|
|
||||||
loadsettings();
|
|
||||||
setsaved();
|
|
||||||
resolve();
|
|
||||||
}
|
|
||||||
else if (title.value == "pgpkeys")
|
|
||||||
{
|
|
||||||
localStorage.setItem("pgpkeys", md.value);
|
|
||||||
setsaved();
|
|
||||||
resolve();
|
|
||||||
}
|
|
||||||
else if (pending)
|
|
||||||
{
|
|
||||||
console.log("pending query: save cancelled");
|
|
||||||
reject();
|
|
||||||
}
|
|
||||||
else if (saved)
|
|
||||||
{
|
|
||||||
console.log("nothing to save");
|
|
||||||
reject();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var content = md.value;
|
|
||||||
if ((content == "" && backup != "") || content == "null" || content == "undefined")
|
|
||||||
{
|
|
||||||
showtemporaryinfo("Invalid content '" + content + "', file '" + title.value + "' not saved");
|
|
||||||
reject();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
serialize();
|
|
||||||
|
|
||||||
if (settings.sync)
|
|
||||||
{
|
|
||||||
var datatosend = JSON.stringify(localdata);
|
|
||||||
return encryptstring(datatosend)
|
|
||||||
.then(encrypted =>
|
|
||||||
{
|
|
||||||
console.log("sending data to php server...");
|
|
||||||
pending = true;
|
|
||||||
return queryremote({action: "push", data: encrypted})
|
|
||||||
})
|
|
||||||
.then(() =>
|
|
||||||
{
|
|
||||||
console.log("...data saved on server");
|
|
||||||
setsaved();
|
|
||||||
})
|
|
||||||
.catch(remotecallfailed)
|
|
||||||
.finally(() =>
|
|
||||||
{
|
|
||||||
pending = false;
|
|
||||||
if (content != md.value)
|
|
||||||
{
|
|
||||||
console.log("but content changed: will save again");
|
|
||||||
return datachanged();
|
|
||||||
}
|
|
||||||
else if (!saved)
|
|
||||||
{
|
|
||||||
console.log("save failed. Data unsaved on server. Will retry.");
|
|
||||||
return datachanged();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
resolve();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
setsaved();
|
|
||||||
resolve();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}*/
|
|
||||||
|
|
||||||
function escapeHtml(unsafe) {
|
function escapeHtml(unsafe) {
|
||||||
return unsafe
|
return unsafe
|
||||||
.replace(/&/g, "&")
|
.replace(/&/g, "&")
|
||||||
|
@ -2174,10 +2188,14 @@ function applycolors(currentonly)
|
||||||
|
|
||||||
function editorinput()
|
function editorinput()
|
||||||
{
|
{
|
||||||
|
unsavedmark.hidden = false;
|
||||||
|
|
||||||
// criteria to improve. Or redraw only after?
|
// criteria to improve. Or redraw only after?
|
||||||
var multiline = md.value.substring(md.selectionStart, md.selectionEnd).includes("\n");
|
var multiline = md.value.substring(md.selectionStart, md.selectionEnd).includes("\n");
|
||||||
applycolors(!multiline && event.data && (event.inputType == "insertText" || event.inputType == "deleteContentBackward" || event.inputType == "deleteContentForward"));
|
applycolors(!multiline && event.data && (event.inputType == "insertText" || event.inputType == "deleteContentBackward" || event.inputType == "deleteContentForward"));
|
||||||
postpone().then(serialize);
|
|
||||||
|
// todo: fix if current note change during postponing, the wrong one will be saved. Or prevent binding another note?
|
||||||
|
postpone().then(flush);
|
||||||
resize();
|
resize();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2191,13 +2209,13 @@ function timestamp()
|
||||||
|
|
||||||
function quicknewnote()
|
function quicknewnote()
|
||||||
{
|
{
|
||||||
serialize();
|
flush();
|
||||||
loadnote(timestamp());
|
loadnote(timestamp());
|
||||||
}
|
}
|
||||||
|
|
||||||
function startnewnote()
|
function startnewnote()
|
||||||
{
|
{
|
||||||
serialize();
|
flush();
|
||||||
var title = prompt("Note title: ", timestamp());
|
var title = prompt("Note title: ", timestamp());
|
||||||
if (title)
|
if (title)
|
||||||
{
|
{
|
||||||
|
@ -2385,7 +2403,7 @@ function restoredeleted()
|
||||||
|
|
||||||
function serializeindex()
|
function serializeindex()
|
||||||
{
|
{
|
||||||
localStorage.setItem("index", JSON.stringify(metadata));
|
serialize("index", JSON.stringify(metadata));
|
||||||
}
|
}
|
||||||
|
|
||||||
function deletenote(title)
|
function deletenote(title)
|
||||||
|
@ -2614,8 +2632,8 @@ function renameinternallinks(from, to)
|
||||||
var newcontent = content.replaceAll("[[" + from + "]]", "[[" + to + "]]");
|
var newcontent = content.replaceAll("[[" + from + "]]", "[[" + to + "]]");
|
||||||
if (content != newcontent)
|
if (content != newcontent)
|
||||||
{
|
{
|
||||||
localStorage.setItem(guid, newcontent);
|
serialize(guid, newcontent);
|
||||||
if (item.title == window.title.value)
|
if (item.title == title.value)
|
||||||
{
|
{
|
||||||
seteditorcontent(newcontent);
|
seteditorcontent(newcontent);
|
||||||
}
|
}
|
||||||
|
@ -2639,7 +2657,7 @@ function ontitlechange()
|
||||||
previoustitle = title.value;
|
previoustitle = title.value;
|
||||||
metadata[guid].title = title.value;
|
metadata[guid].title = title.value;
|
||||||
setwindowtitle();
|
setwindowtitle();
|
||||||
serialize();
|
flush();
|
||||||
|
|
||||||
if (!settings.titlebydefault)
|
if (!settings.titlebydefault)
|
||||||
{
|
{
|
||||||
|
@ -2821,6 +2839,12 @@ function togglepreviewwithsubs()
|
||||||
|
|
||||||
function bind(title, content, pos)
|
function bind(title, content, pos)
|
||||||
{
|
{
|
||||||
|
if (workerid)
|
||||||
|
{
|
||||||
|
showtemporaryinfo("Cannot open '" + title + "' because current note not yet serialized");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
previoustitle = title;
|
previoustitle = title;
|
||||||
backup = content;
|
backup = content;
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue