refactor local serialization

This commit is contained in:
quenousimporte 2024-02-04 22:43:24 +01:00
parent d633cf719d
commit de6268d86f
1 changed files with 120 additions and 182 deletions

280
main.js
View File

@ -19,37 +19,18 @@ var defaultsettings =
};
//builtin
var markerslist = ["* ", "- ", " * ", " - ", ">> ", "> ", "=> ", "— ", "[ ] ", " ", "• ", "- [ ]", "[x] ", "- [x]"];
var codelanguages = ["xml", "js", "sql"];
var tagmark = "+";
const markerslist = ["* ", "- ", " * ", " - ", ">> ", "> ", "=> ", "— ", "[ ] ", " ", "• ", "- [ ]", "[x] ", "- [x]"];
const codelanguages = ["xml", "js", "sql"];
const tagmark = "+";
// globals
var previoustitle = "";
var metadata = null;
var fileindex = 0;
var workerid = null;
var backup = "";
var saved = true;
var lastsaved = "";
var pending = false;
var settings = null;
var backup = "";var settings = null;
var tags = null;
var stat =
{
ses:
{
q: 0,
t: timestamp(),
d: 0
},
cur:
{
q: 0,
t: timestamp(),
d: 0
}
}
var commands = [
{
hint: "Close menu"
@ -57,9 +38,7 @@ var commands = [
{
shortcut: "ctrl+shift+P",
hint: "Command palette",
allowunsaved: true,
action: commandpalette,
excludepalette: true
},
{
shortcut: "ctrl+p",
@ -78,14 +57,12 @@ var commands = [
},
{
hint: "Force save",
action: save,
shortcut: "ctrl+s",
allowunsaved: true
action: serialize,
shortcut: "ctrl+s"
},
{
hint: "Share note",
action: share,
allowunsaved: true
action: share
},
{
hint: "Share note (html)",
@ -99,20 +76,17 @@ var commands = [
{
shortcut: "ctrl+i",
hint: "Toggle title",
action: toggletitle,
allowunsaved: true
action: toggletitle
},
{
shortcut: "ctrl+m",
hint: "Toggle preview",
action: togglepreview,
allowunsaved: true
action: togglepreview
},
{
shortcut: "ctrl+shift+M",
hint: "Toggle preview with merged subnotes",
action: togglepreviewwithsubs,
allowunsaved: true
action: togglepreviewwithsubs
},
{
shortcut: "ctrl+d",
@ -125,8 +99,7 @@ var commands = [
},
{
hint: "Insert markdown header",
action: insertheader,
allowunsaved: true
action: insertheader
},
{
hint: "Show help",
@ -166,8 +139,7 @@ var commands = [
{
hint: "Note outline",
action: showoutline,
shortcut: "ctrl+o",
allowunsaved: true
shortcut: "ctrl+o"
},
{
hint: "Show connected notes",
@ -177,13 +149,11 @@ var commands = [
{
hint: "Show stats",
action: showinfo,
shortcut: "ctrl+w",
allowunsaved: true
shortcut: "ctrl+w"
},
{
hint: "Toggle spell check",
action: togglespellcheck,
allowunsaved: true,
shortcut: "F7"
},
{
@ -255,8 +225,7 @@ var commands = [
},
{
hint: "Sort text",
action: sortselection,
allowunsaved: true
action: sortselection
},
{
hint: "Show backlinks",
@ -264,8 +233,7 @@ var commands = [
},
{
hint: "Remove completed tasks",
action: purgetodo,
allowunsaved: true
action: purgetodo
}];
var snippets = [
@ -316,21 +284,19 @@ function genguid()
return crypto.randomUUID();
}
function purgetodo() {
function purgetodo()
{
if (currentistodo() && confirm("Remove completed tasks?"))
{
seteditorcontent(currentnote.content.replace(/\nx .*/g, ""));
}
}
function seteditorcontent(content, silent)
function seteditorcontent(content)
{
md.value = content;
applycolors();
if (!silent)
{
datachanged();
}
serialize();
resize();
}
@ -453,7 +419,7 @@ function includesub()
if (confirm("Delete '" + title + "'?"))
{
deletenote(subnote);
deletenote(title);
}
}
}
@ -490,19 +456,12 @@ function showinfo()
var tags = gettags(currentnote);
showtemporaryinfo(
[
"saved: " + saved + (lastsaved? " (" + lastsaved + ")": ""),
"sync: " + (settings.sync ? "en" : "dis") + "abled",
"title: " + currentnote.title,
"line count: " + md.value.split("\n").length,
"word count: " + getwords(),
"cursor position: " + md.selectionStart + " (" + pospercent() + "%)",
(tags ? "tags: " + tags : ""),
"current note start: " + stat.cur.t,
"current note queries: " + stat.cur.q,
"current note data sent: " + formatsize(stat.cur.d),
"session start: " + stat.ses.t,
"session queries: " + stat.ses.q,
"session data sent: " + formatsize(stat.ses.d),
"notes count: " + localdata.length,
"spell check: " + (md.spellcheck ? "en" : "dis") + "abled"
].join("\n"));
@ -877,11 +836,7 @@ function restoresettings()
function editsettings()
{
bind(
{
title: "settings.json",
content: JSON.stringify(settings, null, " ")
});
bind("settings.json", JSON.stringify(settings, null, " "));
}
function editsetting(name)
@ -932,11 +887,7 @@ function decryptnote()
function editpgpkeys()
{
bind(
{
title: "pgpkeys",
content: localStorage.getItem("pgpkeys") || ""
});
bind("pgpkeys", localStorage.getItem("pgpkeys") || "");
}
function showtemporaryinfo(info)
@ -1101,14 +1052,24 @@ function headerandtext(content)
function inserttodo(text)
{
var todo = getorcreate("todo", "");
var split = headerandtext(todo);
todo.content = split.header + text + "\n" + split.text;
if (todo == currentnote)
var guid = getguid("todo");
if (!guid)
{
seteditorcontent(todo.content, true);
guid = createnote("todo");
}
var content = localStorage.getItem(guid);
var split = headerandtext(content);
content = split.header + text + "\n" + split.text;
if (title.value == "todo")
{
seteditorcontent(content);
}
else
{
localStorage.setItem(guid, content);
metadata[guid].lastchanged = Date.now();
serialize();
}
return datachanged();
}
function promptinserttodo()
@ -1228,7 +1189,8 @@ function loadstorage()
msg.innerText = "Clipping...";
notepage.appendChild(msg);
inserttodo("@clip " + clip).then(window.close);
inserttodo("@clip " + clip)
window.close();
}
// when multiple tabs or split?
@ -1249,8 +1211,7 @@ function loadstorage()
if (window.title.value)
{
var guid = getguid(window.title.value);
bind(guid);
bind(window.title.value);
if (line)
{
gotoline(line);
@ -1354,14 +1315,14 @@ function migratelegacystorage()
var legacy = localStorage.getItem("data");
if (legacy)
{
// todo: use title as key and guid as property. or not.
var legacy = JSON.parse(legacy);
var index = {};
legacy.forEach(note =>
legacy.reverse().forEach(note =>
{
var guid = genguid();
localStorage.setItem(guid, note.content);
note.header = indexheader(note.content);
note.lastchanged = Date.now();
delete note.content;
index[guid] = note;
});
@ -1448,9 +1409,6 @@ function queryremote(params)
{
return new Promise( (apply, failed) => {
stat.cur.q++;
stat.ses.q++;
params.password = settings.password;
var paramlist = [];
@ -1459,9 +1417,6 @@ function queryremote(params)
paramlist.push(i + "=" + encodeURIComponent(params[i]));
}
stat.cur.d += paramlist.join("&").length;
stat.ses.d += paramlist.join("&").length;
var xhr = new XMLHttpRequest();
xhr.open("POST", "handler.php");
xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
@ -1661,11 +1616,7 @@ function showgrepresult(needle, grepresult)
grepcontent.push("No result.");
}
bind(
{
title: "Search result",
content: grepcontent.join("\n")
});
bind("Search result", grepcontent.join("\n"));
if (preview.hidden)
{
@ -1877,12 +1828,6 @@ function resize()
}
}
function putontop()
{
console.warn("todo: put on top");
return;
}
function postpone()
{
return new Promise(function(resolve)
@ -1892,31 +1837,37 @@ function postpone()
});
}
function setsaved()
{
unsavedmark.hidden = true;
saved = true;
lastsaved = timestamp();
}
function serialize()
{
// serialize all gui stuff to local storage
clearTimeout(workerid);
workerid = null;
var guid = getguid(window.title.value);
if (guid)
{
var item = metadata[guid];
item.title = title.value;
item.pos = md.selectionStart;
item.header = indexheader(md.value);
// is it the right place?
putontop();
item.lastchanged = Date.now();
localStorage.setItem("index", JSON.stringify(metadata));
localStorage.setItem(guid, md.value);
}
else if (title.value == "settings.json")
{
settings = JSON.parse(md.value);
savesettings();
loadsettings();
}
else if (title.value == "pgpkeys")
{
localStorage.setItem("pgpkeys", md.value);
}
console.log("data serialized");
}
function save()
/*function save()
{
return new Promise(function(resolve, reject)
{
@ -2001,7 +1952,7 @@ function save()
}
}
});
}
}*/
function escapeHtml(unsafe) {
return unsafe
@ -2263,19 +2214,10 @@ function editorinput()
// criteria to improve. Or redraw only after?
var multiline = md.value.substring(md.selectionStart, md.selectionEnd).includes("\n");
applycolors(!multiline && event.data && (event.inputType == "insertText" || event.inputType == "deleteContentBackward" || event.inputType == "deleteContentForward"));
datachanged();
postpone().then(serialize);
resize();
}
function datachanged()
{
saved = false;
unsavedmark.hidden = !settings.sync;
return postpone()
.then(save);
}
function timestamp()
{
var utc = new Date();
@ -2286,17 +2228,17 @@ function timestamp()
function quicknewnote()
{
serialize();
loadnote(timestamp());
datachanged();
}
function startnewnote()
{
serialize();
var title = prompt("Note title: ", timestamp());
if (title)
{
loadnote(title);
datachanged();
}
}
@ -2334,11 +2276,7 @@ function showhelp()
help.push("##Sources");
help.push("https://github.com/quenousimporte/notes");
bind(
{
title: "Help",
content: help.join("\n")
});
bind("Help", help.join("\n"));
if (preview.hidden)
{
@ -2362,6 +2300,7 @@ function toggletitle()
function selectnote()
{
// todo: sort by lastchanged
return searchinlist(
Object.values(metadata).map(item =>
{
@ -2491,6 +2430,7 @@ function renamereferences(newname)
function restoredeleted()
{
// todo: parse local storage starting with "deleted" and reindex with a new guid
var trash = window.localStorage.getItem("trash");
if (trash)
{
@ -2518,24 +2458,22 @@ function restoredeleted()
}
}
function deletenote(note)
function deletenote(title)
{
var trash = JSON.parse(window.localStorage.getItem("trash") || "[]");
note.deletiondate = timestamp();
trash.push(note);
window.localStorage.setItem("trash", JSON.stringify(trash));
localdata = localdata.filter(n => n != note);
renamereferences(note.title + " (deleted)");
datachanged();
var guid = getguid(title);
delete metadata[guid];
renameinternallinks(title, title + " (deleted)");
var content = localStorage.getItem(guid);
localStorage.removeItem(guid);
localStorage.setItem("deleted_" + title, content);
localStorage.setItem("index", JSON.stringify(metadata));
}
function deletecurrentnote()
{
if (confirm('delete "' + currentnote.title + '"?'))
if (confirm('delete "' + title.value + '"?'))
{
deletenote(currentnote);
deletenote(title.value);
loadlast();
}
}
@ -2578,11 +2516,7 @@ function shortcutmatches(event, shortcut)
function executecommand(command)
{
if (!command.allowunsaved && !saved)
{
showtemporaryinfo("Cannot perform '" + command.hint + "' because current note is not saved.");
}
else if (command.remoteonly && !settings.sync)
if (command.remoteonly && !settings.sync)
{
showtemporaryinfo(command.hint + " is not available in local mode.");
}
@ -2748,32 +2682,42 @@ function setwindowtitle()
document.title = title.value;
}
function renameinternallinks(from, to)
{
foreachmetadata( (guid, item) =>
{
var content = localStorage.getItem(guid);
var newcontent = content.replaceAll("[[" + from + "]]", "[[" + to + "]]");
if (content != newcontent)
{
localStorage.setItem(guid, newcontent);
}
});
}
function ontitlechange()
{
if (localdata.find(n => n.title == title.value))
var guid = getguid(title.value);
if (guid)
{
showtemporaryinfo(title.value + " alreday exists");
title.value = currentnote.title;
return;
title.value = previoustitle;
}
// rename internal references
localdata
.filter(note => note != currentnote)
.forEach(note =>
else
{
note.content = note.content.replaceAll("[[" + currentnote.title + "]]", "[[" + title.value + "]]");
});
renameinternallinks(previoustitle, title.value);
currentnote.title = title.value;
datachanged();
guid = getguid(previoustitle);
previoustitle = title.value;
metadata[guid].title = title.value;
setwindowtitle();
serialize();
if (!settings.titlebydefault)
{
toggletitle();
}
}
}
function simplifystring(str)
@ -2961,18 +2905,16 @@ function togglepreviewwithsubs()
function bind(title, content, pos)
{
var changed = title.value != title;
previoustitle = title;
backup = content;
window.title.value = title;
setwindowtitle();
seteditorcontent(content || "", true);
md.value = content || "";
applycolors();
if (changed)
{
md.style.height = "0px";
}
resize();
setpos(pos || 0);
@ -3027,10 +2969,6 @@ function loadnote(title)
bind(item.title, content, item.pos);
stat.cur.q = 0;
stat.cur.d = 0;
stat.cur.t = timestamp();
if (!preview.hidden || (preview.hidden && (gettags(md.value).indexOf("preview") !== -1)))
{
togglepreview();