refactor local serialization
This commit is contained in:
parent
d633cf719d
commit
de6268d86f
280
main.js
280
main.js
|
@ -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();
|
||||
|
|
Loading…
Reference in New Issue