qown-zettelkasten/zettel.qml

255 lines
7.9 KiB
QML

import QtQml 2.0
import QOwnNotesTypes 1.0
import com.qownnotes.noteapi 1.0
QtObject {
property bool anchorInFilename;
property string anchorMarker;
property bool wikiLinks;
property string template;
property variant settingsVariables: [
{
"identifier": "anchorInFilename",
"name": "Display Zettel anchors in note filenames",
"description": "By default, the anchor of a Zettel will appear in its body, prefixed with the anchor marker selected below.\nIf enabled, this will instead put the anchor in the filename itself and disregard any anchors in Zettel bodies.",
"type": "boolean",
"default": "false",
},
{
"identifier": "anchorMarker",
"name": "Zettelkasten anchor marker",
"description": "The in-text marker denoting a Zettel anchor is following.\nMake sure to use something which is not ambiguous, and does not appear in any other way than to actually denote a Zettel anchor.\nMultiple characters are fine (as in the default '§§')\nIf 'In-Title Anchors' is selected above this option has no effect.",
"type": "string",
"default": "§§",
},
{
"identifier": "wikiLinks",
"name": "Default to [[WikiLinks]]",
"description": "By default, links will take the form of standard markdown links.\nThis will make automatically created links between Zettel take the form of wiki-links instead.\nThe 'go to link' function understands both types, regardless of this choice.",
"type": "boolean",
"default": "false",
},
{
"identifier": "template",
"name": "Zettel Template",
"description": "What gets created when letting the script create a new Zettel.\nUse {headline} and {anchor} as placeholders to be put there on Zettel creation.",
"type": "text",
"default": "{headline}\n===============\n{anchor}\n\n\n\n\n\n\n## Related\n\n\n\n## Reference\n\n",
},
]
function init() {
script.registerCustomAction("zettelCreate", "Create Zettelkasten Zettel", "Zettel", "address-book-new");
script.registerCustomAction("manualLink", "Insert Zettelkasten Link", "InsertLink", "insert-link");
script.registerCustomAction("jumpToId", "Follow Zettelkasten Link", "Link", "go-next");
}
function customActionInvoked(identifier) {
switch(identifier) {
case "zettelCreate":
onZettelCreateClicked();
break;
case "jumpToId":
onJumpToZettelClicked();
break;
case "manualLink":
onInsertLinkClicked();
break;
}
}
// ----------------------------
// ----- BUTTON FUNCTIONS -----
// ----------------------------
function onZettelCreateClicked() {
let anchor = exactDate();
let selected = script.noteTextEditSelectedText().trim();
// Link the from the old Zettel to the new one if anything was selected
if(selected !== "") {
createLinkInPlace(anchor, selected);
}
// Create a Zettel
createZettel(anchor, selected);
}
function onInsertLinkClicked() {
let selected = script.noteTextEditSelectedText();
let clipboard = script.clipboard();
let anchor = null
if (verifyAnchor(clipboard)) {
anchor = verifyAnchor(clipboard);
} else {
let otherNote = zettelSelectorDialog(true);
anchor = extractAnchorFromString(otherNote.noteText);
}
if (anchor == null) {
return;
}
createLinkInPlace(anchor, selected)
}
function onJumpToZettelClicked() {
let selected = verifyAnchor(script.noteTextEditSelectedText(), true) ? verifyAnchor(script.noteTextEditSelectedText(), true) : verifyAnchor(script.noteTextEditCurrentWord(true), true);
if (selected) {
setNoteToAnchorOrElse(selected, function(anchor) {script.informationMessageBox("Zettel " + anchor + " does not exist.")});
} else {
script.setCurrentNote(zettelSelectorDialog(true));
}
}
// ----------------------------
// --- Autocomplete Linking ---
// ----------------------------
function autocompletionHook() {
let anchor = verifyAnchor(script.noteTextEditCurrentWord(true), true);
if(anchor == false) {
return [];
}
setNoteToAnchorOrElse(anchor);
return [];
}
// ----------------------------
// ---- Internal Functions ----
// ----------------------------
function zettelSelectorDialog(isEditableTextBox) {
let backingIDs = [];
let zettelArray = [];
fetchZettelIDs().forEach(function (noteId){
let note = script.fetchNoteById(noteId);
zettelArray.push(note.name);
backingIDs.push(note.id);
});
if (zettelArray.length == 0) {
script.informationMessageBox("No valid Zettel found.");
}
let selected = script.inputDialogGetItem("Zettel", "Select a Zettel", zettelArray, 0, isEditableTextBox);
return script.fetchNoteById(backingIDs[zettelArray.indexOf(selected)]);
}
function extractAnchorFromString(text) {
let regex = new RegExp(anchorMarker + '\\d{14}\\s')
let markerpos = text.search(regex) + 2;
if (markerpos == -1) {
return;
}
return text.substring(markerpos, markerpos+14);
}
function stripAnchor(link) {
return link.replace(/\D*/g, "");
}
function verifyAnchor(anchor, shouldStrip) {
if (shouldStrip) anchor = stripAnchor(anchor);
if (anchor.match(/^\d{14}$/) != null) {
return anchor;
} else if (anchor.match(new RegExp(anchorMarker + '^\\d{14}$')) != null) {
return anchor.substring(2);
} else {
return false;
}
}
// Returns List of Zettel ids with the specified anchor.
// If no anchor provided returns all Zettel found in the current directory.
function fetchZettelIDs(anchor) {
let anchor = anchor ? anchor : "";
let noteIds = anchorInFilename ? _getAllZettelFromTitle(anchor) : _getAllZettelFromAnchor(anchor);
return noteIds;
}
// Looks for the combination of anchorMarker and anchor (optionally) in all note texts.
function _getAllZettelFromAnchor(anchor) {
return script.fetchNoteIdsByNoteTextPart(anchorMarker + anchor);
}
// Looks for the anchor (optionally) in all note texts.
function _getAllZettelFromTitle(anchor) {
let allnotes = script.fetchNoteIdsByNoteTextPart("");
let noteIds = [];
if (anchor != "") {
allnotes.forEach(function(noteId) {
if (script.fetchNoteById(noteId).name.startsWith(anchor)) noteIds.push(noteId);
});
return noteIds;
}
allnotes.forEach(function(noteId) {
if (script.fetchNoteById(noteId).name.search(/^\d{14}/) != -1) noteIds.push(noteId);
});
return noteIds;
}
function getNoteByAnchor(anchor) {
let noteId = fetchZettelIDs(anchor);
if (noteId.length == 0) {
return;
}
return script.fetchNoteById(noteId[0]);
}
function setNoteToAnchorOrElse(anchor, orElse) {
let zettel = getNoteByAnchor(anchor);
if (zettel == null) {
if(orElse != null) orElse(anchor);
return;
}
script.setCurrentNote(zettel);
}
function createLinkInPlace(anchor, selected) {
wikiLinks ? script.noteTextEditWrite("[[" + anchor + "]] " + selected) : script.noteTextEditWrite("["+selected+"](" + anchor + ")");
}
function createZettel(anchor, headline) {
// Create Headline - empty if nothing selected
if (headline == "") {
headline = "Zettel"
}
let text = template;
if (anchorInFilename) {
text = text.replace(/\{headline\}/, anchor.toString() + " " + headline).replace(/\{anchor\}/, "");
} else {
text = text.replace(/\{headline\}/, headline).replace(/\{anchor\}/, anchorMarker + anchor.toString());
}
script.createNote(text);
return script.currentNote();
}
function exactDate() {
let date = new Date();
return (""
+ date.getUTCFullYear().toString()
+ padToLen2((date.getUTCMonth()+1).toString())
+ padToLen2( date.getUTCDate().toString())
+ padToLen2( date.getUTCHours().toString())
+ padToLen2( date.getUTCMinutes().toString())
+ padToLen2( date.getUTCSeconds().toString())
);
}
function padToLen2(text) {
return ("00" + text).slice(-2)
}
}