Compare commits

..

No commits in common. "d767f80874cd6468e2fd186091d270333c0c2f2f" and "5f049cf587d824bd836e78bce22795538f462c6d" have entirely different histories.

1 changed files with 160 additions and 184 deletions

344
main.js
View File

@ -31,7 +31,6 @@ 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 = [
{ {
@ -59,7 +58,7 @@ var commands = [
}, },
{ {
hint: "Force save", hint: "Force save",
action: flush, action: serialize,
shortcut: "ctrl+s" shortcut: "ctrl+s"
}, },
{ {
@ -285,7 +284,7 @@ function seteditorcontent(content)
{ {
md.value = content; md.value = content;
applycolors(); applycolors();
flush(); serialize();
resize(); resize();
} }
@ -352,46 +351,6 @@ 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:");
@ -415,7 +374,7 @@ function createsubnote()
var range = currentrange(); var range = currentrange();
var content = getrangecontent(range); var content = getrangecontent(range);
guid = createnote(title); guid = createnote(title);
serialize(guid, content); localStorage.setItem(guid, content);
seteditorcontent(md.value.substring(0, range.start) seteditorcontent(md.value.substring(0, range.start)
+ "[[" + title + "]]" + "[[" + title + "]]"
@ -486,7 +445,6 @@ 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(),
@ -499,7 +457,7 @@ function showinfo()
function savesettings() function savesettings()
{ {
localStorage.setItem("settings", JSON.stringify(settings)); window.localStorage.setItem("settings", JSON.stringify(settings));
} }
function descendants(note) function descendants(note)
@ -1086,9 +1044,9 @@ function inserttodo(text)
} }
else else
{ {
serialize(guid, content); localStorage.setItem(guid, content);
metadata[guid].lastchanged = Date.now(); metadata[guid].lastchanged = Date.now();
flush(); serialize();
} }
} }
@ -1160,7 +1118,7 @@ function createnote(title)
}; };
metadata[guid] = item; metadata[guid] = item;
serializeindex() serializeindex()
serialize(guid, content); localStorage.setItem(guid, content);
return guid; return guid;
} }
@ -1244,10 +1202,10 @@ function loadsettings()
function checksaved() function checksaved()
{ {
if (!unsavedmark.hidden) /*if (!saved)
{ {
return "not saved"; return "not saved";
} }*/
} }
function initsnippets() function initsnippets()
@ -1304,8 +1262,7 @@ function migratelegacystorage()
var legacy = localStorage.getItem("data"); var legacy = localStorage.getItem("data");
if (legacy) if (legacy)
{ {
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."); alert("Old data model detected. Click ok to migrate, or close app.")
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) =>
@ -1334,18 +1291,6 @@ 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)
@ -1366,49 +1311,26 @@ function fetch()
{ {
if (!filecontent) if (!filecontent)
{ {
if (confirm("No remote index found. Init remote now?")) return pushall();
}
else
{
// compare and fetch modified and serialize all that
}
}).catch(err =>
{
if (err == "error: authent")
{ {
pushall().then(resolve); settings.password = prompt("Password: ", settings.password);
savesettings();
init();
} }
else else
{ {
resolve(); showtemporaryinfo(err);
} }
} });
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
@ -1425,7 +1347,6 @@ function init()
window.onbeforeunload = checksaved; window.onbeforeunload = checksaved;
window.onclick = focuseditor; window.onclick = focuseditor;
title.value = "";
initsnippets(); initsnippets();
@ -1448,74 +1369,53 @@ 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 encryptdata(params) return new Promise( (apply, failed) => {
.then(encparams =>
{ params.password = settings.password;
return new Promise ( (resolve, reject) =>
var paramlist = [];
for (var i in params)
{ {
encparams.password = settings.password; paramlist.push(i + "=" + encodeURIComponent(params[i]));
}
var paramlist = []; var xhr = new XMLHttpRequest();
for (var i in encparams) xhr.open("POST", "handler.php");
xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
xhr.onerror = function()
{
failed("XMLHttpRequest error");
}
xhr.onload = function()
{
if (xhr.status !== 200)
{ {
paramlist.push(i + "=" + encodeURIComponent(encparams[i])); failed("Http status " + xhr.status);
} }
else
var xhr = new XMLHttpRequest();
xhr.open("POST", "handler.php");
xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
xhr.onerror = function()
{ {
reject("XMLHttpRequest error"); decryptstring(xhr.responseText)
} .then(decrypted =>
xhr.onload = function()
{
if (xhr.status !== 200)
{ {
reject("Http status " + xhr.status); if (decrypted.startsWith("error: "))
}
else
{
decryptstring(xhr.responseText)
.then(decrypted =>
{ {
if (decrypted.startsWith("error: ")) failed(decrypted);
{ }
reject(decrypted); else
} {
else apply(decrypted);
{ }
resolve(decrypted); });
}
});
}
} }
}
var paramstring = paramlist.join("&"); var paramstring = paramlist.join("&");
console.log("http '" + encparams.action + "' request length: " + formatsize(paramstring.length)); console.log("http request length: " + formatsize(paramstring.length));
xhr.send(paramstring); xhr.send(paramstring);
});
}); });
} }
@ -1900,12 +1800,12 @@ function postpone()
}); });
} }
function flush() function serialize()
{ {
clearTimeout(workerid); clearTimeout(workerid);
workerid = null; workerid = null;
var guid = getguid(title.value); var guid = getguid(window.title.value);
if (guid) if (guid)
{ {
var item = metadata[guid]; var item = metadata[guid];
@ -1914,8 +1814,8 @@ function flush()
item.header = indexheader(md.value); item.header = indexheader(md.value);
item.lastchanged = Date.now(); item.lastchanged = Date.now();
serializeindex(); serializeindex()
serialize(guid, md.value); localStorage.setItem(guid, md.value);
} }
else if (title.value == "settings.json") else if (title.value == "settings.json")
{ {
@ -1927,10 +1827,96 @@ function flush()
{ {
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, "&amp;") .replace(/&/g, "&amp;")
@ -2188,14 +2174,10 @@ 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();
} }
@ -2209,13 +2191,13 @@ function timestamp()
function quicknewnote() function quicknewnote()
{ {
flush(); serialize();
loadnote(timestamp()); loadnote(timestamp());
} }
function startnewnote() function startnewnote()
{ {
flush(); serialize();
var title = prompt("Note title: ", timestamp()); var title = prompt("Note title: ", timestamp());
if (title) if (title)
{ {
@ -2403,7 +2385,7 @@ function restoredeleted()
function serializeindex() function serializeindex()
{ {
serialize("index", JSON.stringify(metadata)); localStorage.setItem("index", JSON.stringify(metadata));
} }
function deletenote(title) function deletenote(title)
@ -2632,8 +2614,8 @@ function renameinternallinks(from, to)
var newcontent = content.replaceAll("[[" + from + "]]", "[[" + to + "]]"); var newcontent = content.replaceAll("[[" + from + "]]", "[[" + to + "]]");
if (content != newcontent) if (content != newcontent)
{ {
serialize(guid, newcontent); localStorage.setItem(guid, newcontent);
if (item.title == title.value) if (item.title == window.title.value)
{ {
seteditorcontent(newcontent); seteditorcontent(newcontent);
} }
@ -2657,7 +2639,7 @@ function ontitlechange()
previoustitle = title.value; previoustitle = title.value;
metadata[guid].title = title.value; metadata[guid].title = title.value;
setwindowtitle(); setwindowtitle();
flush(); serialize();
if (!settings.titlebydefault) if (!settings.titlebydefault)
{ {
@ -2839,12 +2821,6 @@ 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;