mirror of
https://github.com/zadam/trilium.git
synced 2025-06-06 18:08:33 +02:00
refactoring - moving stuff to separate files
This commit is contained in:
parent
7c623d9a0b
commit
aad90f016b
@ -152,11 +152,21 @@
|
|||||||
<link href="stat/style.css" rel="stylesheet">
|
<link href="stat/style.css" rel="stylesheet">
|
||||||
|
|
||||||
<script src="stat/js/init.js"></script>
|
<script src="stat/js/init.js"></script>
|
||||||
|
|
||||||
<script src="stat/js/tree.js"></script>
|
<script src="stat/js/tree.js"></script>
|
||||||
|
<script src="stat/js/treeutils.js"></script>
|
||||||
|
<script src="stat/js/draganddrop.js"></script>
|
||||||
|
<script src="stat/js/contextmenu.js"></script>
|
||||||
|
|
||||||
<script src="stat/js/note.js"></script>
|
<script src="stat/js/note.js"></script>
|
||||||
<script src="stat/js/notecase2html.js"></script>
|
<script src="stat/js/notecase2html.js"></script>
|
||||||
<script src="stat/js/html2notecase.js"></script>
|
<script src="stat/js/html2notecase.js"></script>
|
||||||
<script src="stat/js/encryption.js"></script>
|
<script src="stat/js/encryption.js"></script>
|
||||||
|
|
||||||
|
<script src="stat/js/recentnotes.js"></script>
|
||||||
|
<script src="stat/js/addlink.js"></script>
|
||||||
|
<script src="stat/js/jumptonote.js"></script>
|
||||||
|
|
||||||
<script src="stat/js/utils.js"></script>
|
<script src="stat/js/utils.js"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
96
static/js/addlink.js
Normal file
96
static/js/addlink.js
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
$(document).bind('keydown', 'alt+l', function() {
|
||||||
|
$("#noteAutocomplete").val('');
|
||||||
|
$("#linkTitle").val('');
|
||||||
|
|
||||||
|
const noteDetail = $('#noteDetail');
|
||||||
|
noteDetail.summernote('editor.saveRange');
|
||||||
|
|
||||||
|
$("#insertLinkDialog").dialog({
|
||||||
|
modal: true,
|
||||||
|
width: 500
|
||||||
|
});
|
||||||
|
|
||||||
|
function setDefaultLinkTitle(noteId) {
|
||||||
|
const note = getNodeByKey(noteId);
|
||||||
|
if (!note) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let noteTitle = note.title;
|
||||||
|
|
||||||
|
if (noteTitle.endsWith(" (clone)")) {
|
||||||
|
noteTitle = noteTitle.substr(0, noteTitle.length - 8);
|
||||||
|
}
|
||||||
|
|
||||||
|
$("#linkTitle").val(noteTitle);
|
||||||
|
}
|
||||||
|
|
||||||
|
$("#noteAutocomplete").autocomplete({
|
||||||
|
source: getAutocompleteItems(globalAllNoteIds),
|
||||||
|
minLength: 0,
|
||||||
|
change: function () {
|
||||||
|
const val = $("#noteAutocomplete").val();
|
||||||
|
const noteId = getNodeIdFromLabel(val);
|
||||||
|
|
||||||
|
if (noteId) {
|
||||||
|
setDefaultLinkTitle(noteId);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// this is called when user goes through autocomplete list with keyboard
|
||||||
|
// at this point the item isn't selected yet so we use supplied ui.item to see where the cursor is
|
||||||
|
focus: function (event, ui) {
|
||||||
|
const noteId = getNodeIdFromLabel(ui.item.value);
|
||||||
|
|
||||||
|
setDefaultLinkTitle(noteId);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
$("#insertLinkForm").submit(function() {
|
||||||
|
let val = $("#noteAutocomplete").val();
|
||||||
|
|
||||||
|
const noteId = getNodeIdFromLabel(val);
|
||||||
|
|
||||||
|
if (noteId) {
|
||||||
|
const linkTitle = $("#linkTitle").val();
|
||||||
|
const noteDetail = $('#noteDetail');
|
||||||
|
|
||||||
|
$("#insertLinkDialog").dialog("close");
|
||||||
|
|
||||||
|
noteDetail.summernote('editor.restoreRange');
|
||||||
|
|
||||||
|
noteDetail.summernote('createLink', {
|
||||||
|
text: linkTitle,
|
||||||
|
url: 'app#' + noteId,
|
||||||
|
isNewWindow: true
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
|
||||||
|
// when click on link popup, in case of internal link, just go the the referenced note instead of default behavior
|
||||||
|
// of opening the link in new window/tab
|
||||||
|
$(document).on('click', 'div.popover-content a', function(e) {
|
||||||
|
const targetUrl = $(e.target).attr("href");
|
||||||
|
|
||||||
|
const noteIdMatch = /app#([A-Za-z0-9]{22})/.exec(targetUrl);
|
||||||
|
|
||||||
|
if (noteIdMatch !== null) {
|
||||||
|
const noteId = noteIdMatch[1];
|
||||||
|
|
||||||
|
getNodeByKey(noteId).setActive();
|
||||||
|
|
||||||
|
e.preventDefault();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
function getNodeIdFromLabel(label) {
|
||||||
|
const noteIdMatch = / \(([A-Za-z0-9]{22})\)/.exec(label);
|
||||||
|
|
||||||
|
if (noteIdMatch !== null) {
|
||||||
|
return noteIdMatch[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
63
static/js/contextmenu.js
Normal file
63
static/js/contextmenu.js
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
const contextMenuSetup = {
|
||||||
|
delegate: "span.fancytree-title",
|
||||||
|
autoFocus: true,
|
||||||
|
menu: [
|
||||||
|
{title: "Insert note here", cmd: "insertNoteHere", uiIcon: "ui-icon-pencil"},
|
||||||
|
{title: "Insert child note", cmd: "insertChildNote", uiIcon: "ui-icon-pencil"},
|
||||||
|
{title: "Delete", cmd: "delete", uiIcon: "ui-icon-trash"},
|
||||||
|
{title: "----"},
|
||||||
|
{title: "Cut", cmd: "cut", uiIcon: "ui-icon-scissors"},
|
||||||
|
{title: "Copy / clone", cmd: "copy", uiIcon: "ui-icon-copy"},
|
||||||
|
{title: "Paste after", cmd: "pasteAfter", uiIcon: "ui-icon-clipboard"},
|
||||||
|
{title: "Paste into", cmd: "pasteInto", uiIcon: "ui-icon-clipboard"}
|
||||||
|
],
|
||||||
|
beforeOpen: function (event, ui) {
|
||||||
|
const node = $.ui.fancytree.getNode(ui.target);
|
||||||
|
// Modify menu entries depending on node status
|
||||||
|
globalTree.contextmenu("enableEntry", "pasteAfter", globalClipboardNoteId !== null);
|
||||||
|
globalTree.contextmenu("enableEntry", "pasteInto", globalClipboardNoteId !== null);
|
||||||
|
|
||||||
|
// Activate node on right-click
|
||||||
|
node.setActive();
|
||||||
|
// Disable tree keyboard handling
|
||||||
|
ui.menu.prevKeyboard = node.tree.options.keyboard;
|
||||||
|
node.tree.options.keyboard = false;
|
||||||
|
},
|
||||||
|
close: function (event, ui) {},
|
||||||
|
select: function (event, ui) {
|
||||||
|
const node = $.ui.fancytree.getNode(ui.target);
|
||||||
|
|
||||||
|
if (ui.cmd === "insertNoteHere") {
|
||||||
|
const parentKey = getParentKey(node);
|
||||||
|
const encryption = getParentEncryption(node);
|
||||||
|
|
||||||
|
createNote(node, parentKey, 'after', encryption);
|
||||||
|
}
|
||||||
|
else if (ui.cmd === "insertChildNote") {
|
||||||
|
createNote(node, node.key, 'into');
|
||||||
|
}
|
||||||
|
else if (ui.cmd === "cut") {
|
||||||
|
globalClipboardNoteId = node.key;
|
||||||
|
}
|
||||||
|
else if (ui.cmd === "pasteAfter") {
|
||||||
|
const subjectNode = getNodeByKey(globalClipboardNoteId);
|
||||||
|
|
||||||
|
moveAfterNode(subjectNode, node);
|
||||||
|
|
||||||
|
globalClipboardNoteId = null;
|
||||||
|
}
|
||||||
|
else if (ui.cmd === "pasteInto") {
|
||||||
|
const subjectNode = getNodeByKey(globalClipboardNoteId);
|
||||||
|
|
||||||
|
moveToNode(subjectNode, node);
|
||||||
|
|
||||||
|
globalClipboardNoteId = null;
|
||||||
|
}
|
||||||
|
else if (ui.cmd === "delete") {
|
||||||
|
deleteNode(node);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
console.log("Unknown command: " + ui.cmd);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
62
static/js/draganddrop.js
Normal file
62
static/js/draganddrop.js
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
const dragAndDropSetup = {
|
||||||
|
autoExpandMS: 600,
|
||||||
|
draggable: { // modify default jQuery draggable options
|
||||||
|
zIndex: 1000,
|
||||||
|
scroll: false,
|
||||||
|
containment: "parent",
|
||||||
|
revert: "invalid"
|
||||||
|
},
|
||||||
|
preventRecursiveMoves: true, // Prevent dropping nodes on own descendants
|
||||||
|
preventVoidMoves: true, // Prevent dropping nodes 'before self', etc.
|
||||||
|
|
||||||
|
dragStart: function (node, data) {
|
||||||
|
// This function MUST be defined to enable dragging for the tree.
|
||||||
|
// Return false to cancel dragging of node.
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
dragEnter: function (node, data) {
|
||||||
|
/* data.otherNode may be null for non-fancytree droppables.
|
||||||
|
* Return false to disallow dropping on node. In this case
|
||||||
|
* dragOver and dragLeave are not called.
|
||||||
|
* Return 'over', 'before, or 'after' to force a hitMode.
|
||||||
|
* Return ['before', 'after'] to restrict available hitModes.
|
||||||
|
* Any other return value will calc the hitMode from the cursor position.
|
||||||
|
*/
|
||||||
|
// Prevent dropping a parent below another parent (only sort
|
||||||
|
// nodes under the same parent):
|
||||||
|
// if(node.parent !== data.otherNode.parent){
|
||||||
|
// return false;
|
||||||
|
// }
|
||||||
|
// Don't allow dropping *over* a node (would create a child). Just
|
||||||
|
// allow changing the order:
|
||||||
|
// return ["before", "after"];
|
||||||
|
// Accept everything:
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
dragExpand: function (node, data) {
|
||||||
|
// return false to prevent auto-expanding data.node on hover
|
||||||
|
},
|
||||||
|
dragOver: function (node, data) {
|
||||||
|
},
|
||||||
|
dragLeave: function (node, data) {
|
||||||
|
},
|
||||||
|
dragStop: function (node, data) {
|
||||||
|
},
|
||||||
|
dragDrop: function (node, data) {
|
||||||
|
// This function MUST be defined to enable dropping of items on the tree.
|
||||||
|
// data.hitMode is 'before', 'after', or 'over'.
|
||||||
|
|
||||||
|
if (data.hitMode === "before") {
|
||||||
|
moveBeforeNode(data.otherNode, node);
|
||||||
|
}
|
||||||
|
else if (data.hitMode === "after") {
|
||||||
|
moveAfterNode(data.otherNode, node);
|
||||||
|
}
|
||||||
|
else if (data.hitMode === "over") {
|
||||||
|
moveToNode(data.otherNode, node);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
throw new Exception("Unknown hitMode=" + data.hitMode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
@ -14,7 +14,7 @@ function handleEncryption(requireEncryption, modal, callback) {
|
|||||||
open: function() {
|
open: function() {
|
||||||
if (!modal) {
|
if (!modal) {
|
||||||
// dialog steals focus for itself, which is not what we want for non-modal (viewing)
|
// dialog steals focus for itself, which is not what we want for non-modal (viewing)
|
||||||
getNodeByKey(globalNote.detail.note_id).setFocus();
|
getNodeByKey(globalCurrentNote.detail.note_id).setFocus();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -127,8 +127,8 @@ setInterval(function() {
|
|||||||
if (globalLastEncryptionOperationDate !== null && new Date().getTime() - globalLastEncryptionOperationDate.getTime() > globalEncryptionKeyTimeToLive) {
|
if (globalLastEncryptionOperationDate !== null && new Date().getTime() - globalLastEncryptionOperationDate.getTime() > globalEncryptionKeyTimeToLive) {
|
||||||
globalEncryptionKey = null;
|
globalEncryptionKey = null;
|
||||||
|
|
||||||
if (globalNote.detail.encryption > 0) {
|
if (globalCurrentNote.detail.encryption > 0) {
|
||||||
loadNote(globalNote.detail.note_id);
|
loadNote(globalCurrentNote.detail.note_id);
|
||||||
|
|
||||||
for (const noteId of globalAllNoteIds) {
|
for (const noteId of globalAllNoteIds) {
|
||||||
const note = getNodeByKey(noteId);
|
const note = getNodeByKey(noteId);
|
||||||
@ -189,7 +189,7 @@ function encryptNote(note) {
|
|||||||
|
|
||||||
function encryptNoteAndSendToServer() {
|
function encryptNoteAndSendToServer() {
|
||||||
handleEncryption(true, true, () => {
|
handleEncryption(true, true, () => {
|
||||||
const note = globalNote;
|
const note = globalCurrentNote;
|
||||||
|
|
||||||
updateNoteFromInputs(note);
|
updateNoteFromInputs(note);
|
||||||
|
|
||||||
@ -203,7 +203,7 @@ function encryptNoteAndSendToServer() {
|
|||||||
|
|
||||||
function decryptNoteAndSendToServer() {
|
function decryptNoteAndSendToServer() {
|
||||||
handleEncryption(true, true, () => {
|
handleEncryption(true, true, () => {
|
||||||
const note = globalNote;
|
const note = globalCurrentNote;
|
||||||
|
|
||||||
updateNoteFromInputs(note);
|
updateNoteFromInputs(note);
|
||||||
|
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
$(function() {
|
$(function() {
|
||||||
$(window).resize(function() {
|
$(window).resize(function() {
|
||||||
|
// dynamically setting height of tree and note content to match window's height
|
||||||
const fancyTree = $('ul.fancytree-container');
|
const fancyTree = $('ul.fancytree-container');
|
||||||
|
|
||||||
if (fancyTree.length) {
|
if (fancyTree.length) {
|
||||||
@ -15,6 +16,7 @@ $(function() {
|
|||||||
$(window).resize();
|
$(window).resize();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// hot keys are active also inside inputs and content editables
|
||||||
jQuery.hotkeys.options.filterInputAcceptingElements = true;
|
jQuery.hotkeys.options.filterInputAcceptingElements = true;
|
||||||
jQuery.hotkeys.options.filterContentEditable = true;
|
jQuery.hotkeys.options.filterContentEditable = true;
|
||||||
|
|
||||||
@ -27,176 +29,11 @@ $(document).bind('keydown', 'alt+h', function() {
|
|||||||
$("#noteDetailWrapper").css("width", hidden ? "750px" : "100%");
|
$("#noteDetailWrapper").css("width", hidden ? "750px" : "100%");
|
||||||
});
|
});
|
||||||
|
|
||||||
$(document).bind('keydown', 'alt+q', function() {
|
|
||||||
$("#recentNotesDialog").dialog({
|
|
||||||
modal: true,
|
|
||||||
width: 500
|
|
||||||
});
|
|
||||||
|
|
||||||
let recentNotesSelectBox = $('#recentNotesSelectBox');
|
|
||||||
|
|
||||||
recentNotesSelectBox.find('option').remove();
|
|
||||||
|
|
||||||
// remove the current note
|
|
||||||
let recNotes = globalRecentNotes.filter(note => note !== globalNote.detail.note_id);
|
|
||||||
|
|
||||||
$.each(recNotes, function(key, valueNoteId) {
|
|
||||||
let noteTitle = getFullName(valueNoteId);
|
|
||||||
|
|
||||||
if (!noteTitle) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let option = $("<option></option>")
|
|
||||||
.attr("value", valueNoteId)
|
|
||||||
.text(noteTitle);
|
|
||||||
|
|
||||||
// select the first one (most recent one) by default
|
|
||||||
if (key === 0) {
|
|
||||||
option.attr("selected", "selected");
|
|
||||||
}
|
|
||||||
|
|
||||||
recentNotesSelectBox.append(option);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
function setActiveNoteBasedOnRecentNotes() {
|
|
||||||
let noteId = $("#recentNotesSelectBox option:selected").val();
|
|
||||||
|
|
||||||
getNodeByKey(noteId).setActive();
|
|
||||||
|
|
||||||
$("#recentNotesDialog").dialog('close');
|
|
||||||
}
|
|
||||||
|
|
||||||
$('#recentNotesSelectBox').keydown(function(e) {
|
|
||||||
let key = e.which;
|
|
||||||
|
|
||||||
if (key === 13)// the enter key code
|
|
||||||
{
|
|
||||||
setActiveNoteBasedOnRecentNotes();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
$('#recentNotesSelectBox').dblclick(function(e) {
|
|
||||||
setActiveNoteBasedOnRecentNotes();
|
|
||||||
});
|
|
||||||
|
|
||||||
// when click on link popup, in case of internal link, just go the the referenced note instead of default behavior
|
|
||||||
// of opening the link in new window/tab
|
|
||||||
$(document).on('click', 'div.popover-content a', function(e) {
|
|
||||||
const targetUrl = $(e.target).attr("href");
|
|
||||||
|
|
||||||
const noteIdMatch = /app#([A-Za-z0-9]{22})/.exec(targetUrl);
|
|
||||||
|
|
||||||
if (noteIdMatch !== null) {
|
|
||||||
const noteId = noteIdMatch[1];
|
|
||||||
|
|
||||||
getNodeByKey(noteId).setActive();
|
|
||||||
|
|
||||||
e.preventDefault();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
function getNodeIdFromLabel(label) {
|
|
||||||
const noteIdMatch = / \(([A-Za-z0-9]{22})\)/.exec(label);
|
|
||||||
|
|
||||||
if (noteIdMatch !== null) {
|
|
||||||
return noteIdMatch[1];
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
function getAutocompleteItems() {
|
|
||||||
const autocompleteItems = [];
|
|
||||||
|
|
||||||
for (const noteId of globalAllNoteIds) {
|
|
||||||
const fullName = getFullName(noteId);
|
|
||||||
|
|
||||||
autocompleteItems.push({
|
|
||||||
value: fullName + " (" + noteId + ")",
|
|
||||||
label: fullName
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return autocompleteItems;
|
|
||||||
}
|
|
||||||
|
|
||||||
$(document).bind('keydown', 'alt+l', function() {
|
|
||||||
$("#noteAutocomplete").val('');
|
|
||||||
$("#linkTitle").val('');
|
|
||||||
|
|
||||||
const noteDetail = $('#noteDetail');
|
|
||||||
noteDetail.summernote('editor.saveRange');
|
|
||||||
|
|
||||||
$("#insertLinkDialog").dialog({
|
|
||||||
modal: true,
|
|
||||||
width: 500
|
|
||||||
});
|
|
||||||
|
|
||||||
function setDefaultLinkTitle(noteId) {
|
|
||||||
const note = getNodeByKey(noteId);
|
|
||||||
if (!note) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let noteTitle = note.title;
|
|
||||||
|
|
||||||
if (noteTitle.endsWith(" (clone)")) {
|
|
||||||
noteTitle = noteTitle.substr(0, noteTitle.length - 8);
|
|
||||||
}
|
|
||||||
|
|
||||||
$("#linkTitle").val(noteTitle);
|
|
||||||
}
|
|
||||||
|
|
||||||
$("#noteAutocomplete").autocomplete({
|
|
||||||
source: getAutocompleteItems(),
|
|
||||||
minLength: 0,
|
|
||||||
change: function () {
|
|
||||||
const val = $("#noteAutocomplete").val();
|
|
||||||
const noteId = getNodeIdFromLabel(val);
|
|
||||||
|
|
||||||
if (noteId) {
|
|
||||||
setDefaultLinkTitle(noteId);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
// this is called when user goes through autocomplete list with keyboard
|
|
||||||
// at this point the item isn't selected yet so we use supplied ui.item to see where the cursor is
|
|
||||||
focus: function (event, ui) {
|
|
||||||
const noteId = getNodeIdFromLabel(ui.item.value);
|
|
||||||
|
|
||||||
setDefaultLinkTitle(noteId);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
$("#insertLinkForm").submit(function() {
|
|
||||||
let val = $("#noteAutocomplete").val();
|
|
||||||
|
|
||||||
const noteId = getNodeIdFromLabel(val);
|
|
||||||
|
|
||||||
if (noteId) {
|
|
||||||
const linkTitle = $("#linkTitle").val();
|
|
||||||
const noteDetail = $('#noteDetail');
|
|
||||||
|
|
||||||
$("#insertLinkDialog").dialog("close");
|
|
||||||
|
|
||||||
noteDetail.summernote('editor.restoreRange');
|
|
||||||
|
|
||||||
noteDetail.summernote('createLink', {
|
|
||||||
text: linkTitle,
|
|
||||||
url: 'app#' + noteId,
|
|
||||||
isNewWindow: true
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
});
|
|
||||||
|
|
||||||
$(document).bind('keydown', 'alt+s', function() {
|
$(document).bind('keydown', 'alt+s', function() {
|
||||||
$("input[name=search]").focus();
|
$("input[name=search]").focus();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// hide (toggle) everything except for the note content for distraction free writing
|
||||||
$(document).bind('keydown', 'alt+t', function() {
|
$(document).bind('keydown', 'alt+t', function() {
|
||||||
const date = new Date();
|
const date = new Date();
|
||||||
|
|
||||||
@ -206,34 +43,8 @@ $(document).bind('keydown', 'alt+t', function() {
|
|||||||
$('#noteDetail').summernote('insertText', dateString);
|
$('#noteDetail').summernote('insertText', dateString);
|
||||||
});
|
});
|
||||||
|
|
||||||
$(document).bind('keydown', 'alt+j', function() {
|
|
||||||
$("#jumpToNoteAutocomplete").val('');
|
|
||||||
|
|
||||||
$("#jumpToNoteDialog").dialog({
|
|
||||||
modal: true,
|
|
||||||
width: 500
|
|
||||||
});
|
|
||||||
|
|
||||||
$("#jumpToNoteAutocomplete").autocomplete({
|
|
||||||
source: getAutocompleteItems(),
|
|
||||||
minLength: 0
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
$("#jumpToNoteForm").submit(function() {
|
|
||||||
const val = $("#jumpToNoteAutocomplete").val();
|
|
||||||
const noteId = getNodeIdFromLabel(val);
|
|
||||||
|
|
||||||
if (noteId) {
|
|
||||||
getNodeByKey(noteId).setActive();
|
|
||||||
|
|
||||||
$("#jumpToNoteDialog").dialog('close');
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
});
|
|
||||||
|
|
||||||
$(window).on('beforeunload', function(){
|
$(window).on('beforeunload', function(){
|
||||||
// asynchronously send the request and don't wait for result
|
// this makes sure that when user e.g. reloads the page or navigates away from the page, the note's content is saved
|
||||||
|
// this sends the request asynchronously and doesn't wait for result
|
||||||
saveNoteIfChanged();
|
saveNoteIfChanged();
|
||||||
});
|
});
|
26
static/js/jumptonote.js
Normal file
26
static/js/jumptonote.js
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
$(document).bind('keydown', 'alt+j', function() {
|
||||||
|
$("#jumpToNoteAutocomplete").val('');
|
||||||
|
|
||||||
|
$("#jumpToNoteDialog").dialog({
|
||||||
|
modal: true,
|
||||||
|
width: 500
|
||||||
|
});
|
||||||
|
|
||||||
|
$("#jumpToNoteAutocomplete").autocomplete({
|
||||||
|
source: getAutocompleteItems(globalAllNoteIds),
|
||||||
|
minLength: 0
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
$("#jumpToNoteForm").submit(function() {
|
||||||
|
const val = $("#jumpToNoteAutocomplete").val();
|
||||||
|
const noteId = getNodeIdFromLabel(val);
|
||||||
|
|
||||||
|
if (noteId) {
|
||||||
|
getNodeByKey(noteId).setActive();
|
||||||
|
|
||||||
|
$("#jumpToNoteDialog").dialog('close');
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
});
|
@ -21,6 +21,43 @@ function noteChanged() {
|
|||||||
isNoteChanged = true;
|
isNoteChanged = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function saveNoteIfChanged(callback) {
|
||||||
|
if (!isNoteChanged) {
|
||||||
|
if (callback) {
|
||||||
|
callback();
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const note = globalCurrentNote;
|
||||||
|
|
||||||
|
updateNoteFromInputs(note);
|
||||||
|
|
||||||
|
encryptNoteIfNecessary(note);
|
||||||
|
|
||||||
|
saveNoteToServer(note, callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
setInterval(saveNoteIfChanged, 5000);
|
||||||
|
|
||||||
|
$(document).ready(function() {
|
||||||
|
$("#noteTitle").on('input', function() {
|
||||||
|
noteChanged();
|
||||||
|
});
|
||||||
|
|
||||||
|
$('#noteDetail').summernote({
|
||||||
|
airMode: true,
|
||||||
|
height: 300,
|
||||||
|
callbacks: {
|
||||||
|
onChange: noteChanged
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// so that tab jumps from note title (which has tabindex 1)
|
||||||
|
$(".note-editable").attr("tabindex", 2);
|
||||||
|
});
|
||||||
|
|
||||||
function updateNoteFromInputs(note) {
|
function updateNoteFromInputs(note) {
|
||||||
let contents = $('#noteDetail').summernote('code');
|
let contents = $('#noteDetail').summernote('code');
|
||||||
|
|
||||||
@ -54,44 +91,7 @@ function saveNoteToServer(note, callback) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function saveNoteIfChanged(callback) {
|
let globalCurrentNote;
|
||||||
if (!isNoteChanged) {
|
|
||||||
if (callback) {
|
|
||||||
callback();
|
|
||||||
}
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const note = globalNote;
|
|
||||||
|
|
||||||
updateNoteFromInputs(note);
|
|
||||||
|
|
||||||
encryptNoteIfNecessary(note);
|
|
||||||
|
|
||||||
saveNoteToServer(note, callback);
|
|
||||||
}
|
|
||||||
|
|
||||||
setInterval(saveNoteIfChanged, 5000);
|
|
||||||
|
|
||||||
$(document).ready(function() {
|
|
||||||
$("#noteTitle").on('input', function() {
|
|
||||||
noteChanged();
|
|
||||||
});
|
|
||||||
|
|
||||||
$('#noteDetail').summernote({
|
|
||||||
airMode: true,
|
|
||||||
height: 300,
|
|
||||||
callbacks: {
|
|
||||||
onChange: noteChanged
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// so that tab jumps from note title (which has tabindex 1)
|
|
||||||
$(".note-editable").attr("tabindex", 2);
|
|
||||||
});
|
|
||||||
|
|
||||||
let globalNote;
|
|
||||||
|
|
||||||
function createNewTopLevelNote() {
|
function createNewTopLevelNote() {
|
||||||
let rootNode = globalTree.fancytree("getRootNode");
|
let rootNode = globalTree.fancytree("getRootNode");
|
||||||
@ -149,8 +149,6 @@ function createNote(node, parentKey, target, encryption) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
globalRecentNotes = [];
|
|
||||||
|
|
||||||
function setNoteBackgroundIfEncrypted(note) {
|
function setNoteBackgroundIfEncrypted(note) {
|
||||||
if (note.detail.encryption > 0) {
|
if (note.detail.encryption > 0) {
|
||||||
$(".note-editable").addClass("encrypted");
|
$(".note-editable").addClass("encrypted");
|
||||||
@ -169,7 +167,7 @@ function setNoteBackgroundIfEncrypted(note) {
|
|||||||
|
|
||||||
function loadNote(noteId) {
|
function loadNote(noteId) {
|
||||||
$.get(baseUrl + 'notes/' + noteId).then(function(note) {
|
$.get(baseUrl + 'notes/' + noteId).then(function(note) {
|
||||||
globalNote = note;
|
globalCurrentNote = note;
|
||||||
|
|
||||||
if (newNoteCreated) {
|
if (newNoteCreated) {
|
||||||
newNoteCreated = false;
|
newNoteCreated = false;
|
||||||
@ -213,18 +211,3 @@ function loadNote(noteId) {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function addRecentNote(noteTreeId, noteContentId) {
|
|
||||||
const origDate = new Date();
|
|
||||||
|
|
||||||
setTimeout(function() {
|
|
||||||
// we include the note into recent list only if the user stayed on the note at least 5 seconds
|
|
||||||
if (noteTreeId === globalNote.detail.note_id || noteContentId === globalNote.detail.note_id) {
|
|
||||||
// if it's already there, remove the note
|
|
||||||
globalRecentNotes = globalRecentNotes.filter(note => note !== noteTreeId);
|
|
||||||
|
|
||||||
globalRecentNotes.unshift(noteTreeId);
|
|
||||||
}
|
|
||||||
}, 1500);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
69
static/js/recentnotes.js
Normal file
69
static/js/recentnotes.js
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
let globalRecentNotes = [];
|
||||||
|
|
||||||
|
function addRecentNote(noteTreeId, noteContentId) {
|
||||||
|
const origDate = new Date();
|
||||||
|
|
||||||
|
setTimeout(function() {
|
||||||
|
// we include the note into recent list only if the user stayed on the note at least 5 seconds
|
||||||
|
if (noteTreeId === globalCurrentNote.detail.note_id || noteContentId === globalCurrentNote.detail.note_id) {
|
||||||
|
// if it's already there, remove the note
|
||||||
|
globalRecentNotes = globalRecentNotes.filter(note => note !== noteTreeId);
|
||||||
|
|
||||||
|
globalRecentNotes.unshift(noteTreeId);
|
||||||
|
}
|
||||||
|
}, 1500);
|
||||||
|
}
|
||||||
|
|
||||||
|
$(document).bind('keydown', 'alt+q', function() {
|
||||||
|
$("#recentNotesDialog").dialog({
|
||||||
|
modal: true,
|
||||||
|
width: 500
|
||||||
|
});
|
||||||
|
|
||||||
|
let recentNotesSelectBox = $('#recentNotesSelectBox');
|
||||||
|
|
||||||
|
recentNotesSelectBox.find('option').remove();
|
||||||
|
|
||||||
|
// remove the current note
|
||||||
|
let recNotes = globalRecentNotes.filter(note => note !== globalCurrentNote.detail.note_id);
|
||||||
|
|
||||||
|
$.each(recNotes, function(key, valueNoteId) {
|
||||||
|
let noteTitle = getFullName(valueNoteId);
|
||||||
|
|
||||||
|
if (!noteTitle) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let option = $("<option></option>")
|
||||||
|
.attr("value", valueNoteId)
|
||||||
|
.text(noteTitle);
|
||||||
|
|
||||||
|
// select the first one (most recent one) by default
|
||||||
|
if (key === 0) {
|
||||||
|
option.attr("selected", "selected");
|
||||||
|
}
|
||||||
|
|
||||||
|
recentNotesSelectBox.append(option);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
function setActiveNoteBasedOnRecentNotes() {
|
||||||
|
let noteId = $("#recentNotesSelectBox option:selected").val();
|
||||||
|
|
||||||
|
getNodeByKey(noteId).setActive();
|
||||||
|
|
||||||
|
$("#recentNotesDialog").dialog('close');
|
||||||
|
}
|
||||||
|
|
||||||
|
$('#recentNotesSelectBox').keydown(function(e) {
|
||||||
|
let key = e.which;
|
||||||
|
|
||||||
|
if (key === 13)// the enter key code
|
||||||
|
{
|
||||||
|
setActiveNoteBasedOnRecentNotes();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
$('#recentNotesSelectBox').dblclick(function(e) {
|
||||||
|
setActiveNoteBasedOnRecentNotes();
|
||||||
|
});
|
@ -25,7 +25,7 @@ function moveToNode(node, toNode) {
|
|||||||
url: baseUrl + 'notes/' + node.key + '/moveTo/' + toNode.key,
|
url: baseUrl + 'notes/' + node.key + '/moveTo/' + toNode.key,
|
||||||
type: 'PUT',
|
type: 'PUT',
|
||||||
contentType: "application/json",
|
contentType: "application/json",
|
||||||
success: function (result) {
|
success: function () {
|
||||||
node.moveTo(toNode);
|
node.moveTo(toNode);
|
||||||
|
|
||||||
toNode.setExpanded(true);
|
toNode.setExpanded(true);
|
||||||
@ -66,12 +66,22 @@ function deleteNode(node) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function getParentKey(node) {
|
function moveNodeUp(node) {
|
||||||
return (node.getParent() === null || node.getParent().key === "root_1") ? "root" : node.getParent().key;
|
if (node.getParent() !== null) {
|
||||||
}
|
$.ajax({
|
||||||
|
url: baseUrl + 'notes/' + node.key + '/moveAfter/' + node.getParent().key,
|
||||||
|
type: 'PUT',
|
||||||
|
contentType: "application/json",
|
||||||
|
success: function () {
|
||||||
|
if (node.getParent() !== null && node.getParent().getChildren().length <= 1) {
|
||||||
|
node.getParent().folder = false;
|
||||||
|
node.getParent().renderTitle();
|
||||||
|
}
|
||||||
|
|
||||||
function getParentEncryption(node) {
|
node.moveTo(node.getParent(), 'after');
|
||||||
return node.getParent() === null ? 0 : node.getParent().data.encryption;
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const keybindings = {
|
const keybindings = {
|
||||||
@ -101,21 +111,7 @@ const keybindings = {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"shift+left": function(node) {
|
"shift+left": function(node) {
|
||||||
if (node.getParent() !== null) {
|
moveNodeUp(node);
|
||||||
$.ajax({
|
|
||||||
url: baseUrl + 'notes/' + node.key + '/moveAfter/' + node.getParent().key,
|
|
||||||
type: 'PUT',
|
|
||||||
contentType: "application/json",
|
|
||||||
success: function() {
|
|
||||||
if (node.getParent() !== null && node.getParent().getChildren().length <= 1) {
|
|
||||||
node.getParent().folder = false;
|
|
||||||
node.getParent().renderTitle();
|
|
||||||
}
|
|
||||||
|
|
||||||
node.moveTo(node.getParent(), 'after');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
"shift+right": function(node) {
|
"shift+right": function(node) {
|
||||||
let toNode = node.getPrevSibling();
|
let toNode = node.getPrevSibling();
|
||||||
@ -132,30 +128,47 @@ const keybindings = {
|
|||||||
|
|
||||||
let globalAllNoteIds = [];
|
let globalAllNoteIds = [];
|
||||||
|
|
||||||
let globalTree;
|
const globalTree = $("#tree");
|
||||||
|
|
||||||
function getNodeByKey(noteId) {
|
|
||||||
return globalTree.fancytree('getNodeByKey', noteId);
|
|
||||||
}
|
|
||||||
|
|
||||||
function getFullName(noteId) {
|
|
||||||
let note = getNodeByKey(noteId);
|
|
||||||
const path = [];
|
|
||||||
|
|
||||||
while (note) {
|
|
||||||
path.push(note.title);
|
|
||||||
|
|
||||||
note = note.getParent();
|
|
||||||
}
|
|
||||||
|
|
||||||
// remove "root" element
|
|
||||||
path.pop();
|
|
||||||
|
|
||||||
return path.reverse().join(" > ");
|
|
||||||
}
|
|
||||||
|
|
||||||
let globalClipboardNoteId = null;
|
let globalClipboardNoteId = null;
|
||||||
|
|
||||||
|
function prepareNoteTree(notes) {
|
||||||
|
for (const note of notes) {
|
||||||
|
globalAllNoteIds.push(note.note_id);
|
||||||
|
|
||||||
|
if (note.encryption > 0) {
|
||||||
|
note.title = "[encrypted]";
|
||||||
|
|
||||||
|
note.extraClasses = "encrypted";
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
note.title = note.note_title;
|
||||||
|
|
||||||
|
if (note.is_clone) {
|
||||||
|
note.title += " (clone)";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
note.key = note.note_id;
|
||||||
|
note.expanded = note.is_expanded;
|
||||||
|
|
||||||
|
if (note.children && note.children.length > 0) {
|
||||||
|
prepareNoteTree(note.children);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function setExpandedToServer(note_id, is_expanded) {
|
||||||
|
expanded_num = is_expanded ? 1 : 0;
|
||||||
|
|
||||||
|
$.ajax({
|
||||||
|
url: baseUrl + 'notes/' + note_id + '/expanded/' + expanded_num,
|
||||||
|
type: 'PUT',
|
||||||
|
contentType: "application/json",
|
||||||
|
success: function(result) {}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
$(function(){
|
$(function(){
|
||||||
$.get(baseUrl + 'tree').then(resp => {
|
$.get(baseUrl + 'tree').then(resp => {
|
||||||
const notes = resp.notes;
|
const notes = resp.notes;
|
||||||
@ -165,46 +178,8 @@ $(function(){
|
|||||||
startNoteId = document.location.hash.substr(1); // strip initial #
|
startNoteId = document.location.hash.substr(1); // strip initial #
|
||||||
}
|
}
|
||||||
|
|
||||||
function copyTitle(notes) {
|
prepareNoteTree(notes);
|
||||||
for (const note of notes) {
|
|
||||||
globalAllNoteIds.push(note.note_id);
|
|
||||||
|
|
||||||
if (note.encryption > 0) {
|
|
||||||
note.title = "[encrypted]";
|
|
||||||
|
|
||||||
note.extraClasses = "encrypted";
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
note.title = note.note_title;
|
|
||||||
|
|
||||||
if (note.is_clone) {
|
|
||||||
note.title += " (clone)";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
note.key = note.note_id;
|
|
||||||
note.expanded = note.is_expanded;
|
|
||||||
|
|
||||||
if (note.children && note.children.length > 0) {
|
|
||||||
copyTitle(note.children);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
copyTitle(notes);
|
|
||||||
|
|
||||||
function setExpanded(note_id, is_expanded) {
|
|
||||||
expanded_num = is_expanded ? 1 : 0;
|
|
||||||
|
|
||||||
$.ajax({
|
|
||||||
url: baseUrl + 'notes/' + note_id + '/expanded/' + expanded_num,
|
|
||||||
type: 'PUT',
|
|
||||||
contentType: "application/json",
|
|
||||||
success: function(result) {}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
globalTree = $("#tree");
|
|
||||||
globalTree.fancytree({
|
globalTree.fancytree({
|
||||||
autoScroll: true,
|
autoScroll: true,
|
||||||
extensions: ["hotkeys", "filter", "dnd"],
|
extensions: ["hotkeys", "filter", "dnd"],
|
||||||
@ -215,10 +190,10 @@ $(function(){
|
|||||||
saveNoteIfChanged(() => loadNote(node.note_id));
|
saveNoteIfChanged(() => loadNote(node.note_id));
|
||||||
},
|
},
|
||||||
expand: function(event, data) {
|
expand: function(event, data) {
|
||||||
setExpanded(data.node.key, true);
|
setExpandedToServer(data.node.key, true);
|
||||||
},
|
},
|
||||||
collapse: function(event, data) {
|
collapse: function(event, data) {
|
||||||
setExpanded(data.node.key, false);
|
setExpandedToServer(data.node.key, false);
|
||||||
},
|
},
|
||||||
init: function(event, data) {
|
init: function(event, data) {
|
||||||
if (startNoteId) {
|
if (startNoteId) {
|
||||||
@ -242,133 +217,10 @@ $(function(){
|
|||||||
nodata: true, // Display a 'no data' status node if result is empty
|
nodata: true, // Display a 'no data' status node if result is empty
|
||||||
mode: "hide" // Grayout unmatched nodes (pass "hide" to remove unmatched node instead)
|
mode: "hide" // Grayout unmatched nodes (pass "hide" to remove unmatched node instead)
|
||||||
},
|
},
|
||||||
dnd: {
|
dnd: dragAndDropSetup
|
||||||
autoExpandMS: 600,
|
|
||||||
draggable: { // modify default jQuery draggable options
|
|
||||||
zIndex: 1000,
|
|
||||||
scroll: false,
|
|
||||||
containment: "parent",
|
|
||||||
revert: "invalid"
|
|
||||||
},
|
|
||||||
preventRecursiveMoves: true, // Prevent dropping nodes on own descendants
|
|
||||||
preventVoidMoves: true, // Prevent dropping nodes 'before self', etc.
|
|
||||||
|
|
||||||
dragStart: function(node, data) {
|
|
||||||
// This function MUST be defined to enable dragging for the tree.
|
|
||||||
// Return false to cancel dragging of node.
|
|
||||||
return true;
|
|
||||||
},
|
|
||||||
dragEnter: function(node, data) {
|
|
||||||
/* data.otherNode may be null for non-fancytree droppables.
|
|
||||||
* Return false to disallow dropping on node. In this case
|
|
||||||
* dragOver and dragLeave are not called.
|
|
||||||
* Return 'over', 'before, or 'after' to force a hitMode.
|
|
||||||
* Return ['before', 'after'] to restrict available hitModes.
|
|
||||||
* Any other return value will calc the hitMode from the cursor position.
|
|
||||||
*/
|
|
||||||
// Prevent dropping a parent below another parent (only sort
|
|
||||||
// nodes under the same parent):
|
|
||||||
// if(node.parent !== data.otherNode.parent){
|
|
||||||
// return false;
|
|
||||||
// }
|
|
||||||
// Don't allow dropping *over* a node (would create a child). Just
|
|
||||||
// allow changing the order:
|
|
||||||
// return ["before", "after"];
|
|
||||||
// Accept everything:
|
|
||||||
return true;
|
|
||||||
},
|
|
||||||
dragExpand: function(node, data) {
|
|
||||||
// return false to prevent auto-expanding data.node on hover
|
|
||||||
},
|
|
||||||
dragOver: function(node, data) {
|
|
||||||
},
|
|
||||||
dragLeave: function(node, data) {
|
|
||||||
},
|
|
||||||
dragStop: function(node, data) {
|
|
||||||
},
|
|
||||||
dragDrop: function(node, data) {
|
|
||||||
// This function MUST be defined to enable dropping of items on the tree.
|
|
||||||
// data.hitMode is 'before', 'after', or 'over'.
|
|
||||||
|
|
||||||
if (data.hitMode === "before") {
|
|
||||||
moveBeforeNode(data.otherNode, node);
|
|
||||||
}
|
|
||||||
else if (data.hitMode === "after") {
|
|
||||||
moveAfterNode(data.otherNode, node);
|
|
||||||
}
|
|
||||||
else if (data.hitMode === "over") {
|
|
||||||
moveToNode(data.otherNode, node);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
throw new Exception("Unknown hitMode=" + data.hitMode);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
globalTree.contextmenu({
|
globalTree.contextmenu(contextMenuSetup);
|
||||||
delegate: "span.fancytree-title",
|
|
||||||
autoFocus: true,
|
|
||||||
menu: [
|
|
||||||
{title: "Insert note here", cmd: "insertNoteHere", uiIcon: "ui-icon-pencil"},
|
|
||||||
{title: "Insert child note", cmd: "insertChildNote", uiIcon: "ui-icon-pencil"},
|
|
||||||
{title: "Delete", cmd: "delete", uiIcon: "ui-icon-trash"},
|
|
||||||
{title: "----"},
|
|
||||||
{title: "Cut", cmd: "cut", uiIcon: "ui-icon-scissors"},
|
|
||||||
{title: "Copy / clone", cmd: "copy", uiIcon: "ui-icon-copy"},
|
|
||||||
{title: "Paste after", cmd: "pasteAfter", uiIcon: "ui-icon-clipboard"},
|
|
||||||
{title: "Paste into", cmd: "pasteInto", uiIcon: "ui-icon-clipboard"}
|
|
||||||
],
|
|
||||||
beforeOpen: function (event, ui) {
|
|
||||||
const node = $.ui.fancytree.getNode(ui.target);
|
|
||||||
// Modify menu entries depending on node status
|
|
||||||
globalTree.contextmenu("enableEntry", "pasteAfter", globalClipboardNoteId !== null);
|
|
||||||
globalTree.contextmenu("enableEntry", "pasteInto", globalClipboardNoteId !== null);
|
|
||||||
|
|
||||||
// Activate node on right-click
|
|
||||||
node.setActive();
|
|
||||||
// Disable tree keyboard handling
|
|
||||||
ui.menu.prevKeyboard = node.tree.options.keyboard;
|
|
||||||
node.tree.options.keyboard = false;
|
|
||||||
},
|
|
||||||
close: function (event, ui) {},
|
|
||||||
select: function (event, ui) {
|
|
||||||
const node = $.ui.fancytree.getNode(ui.target);
|
|
||||||
|
|
||||||
if (ui.cmd === "insertNoteHere") {
|
|
||||||
const parentKey = getParentKey(node);
|
|
||||||
const encryption = getParentEncryption(node);
|
|
||||||
|
|
||||||
createNote(node, parentKey, 'after', encryption);
|
|
||||||
}
|
|
||||||
else if (ui.cmd === "insertChildNote") {
|
|
||||||
createNote(node, node.key, 'into');
|
|
||||||
}
|
|
||||||
else if (ui.cmd === "cut") {
|
|
||||||
globalClipboardNoteId = node.key;
|
|
||||||
}
|
|
||||||
else if (ui.cmd === "pasteAfter") {
|
|
||||||
const subjectNode = getNodeByKey(globalClipboardNoteId);
|
|
||||||
|
|
||||||
moveAfterNode(subjectNode, node);
|
|
||||||
|
|
||||||
globalClipboardNoteId = null;
|
|
||||||
}
|
|
||||||
else if (ui.cmd === "pasteInto") {
|
|
||||||
const subjectNode = getNodeByKey(globalClipboardNoteId);
|
|
||||||
|
|
||||||
moveToNode(subjectNode, node);
|
|
||||||
|
|
||||||
globalClipboardNoteId = null;
|
|
||||||
}
|
|
||||||
else if (ui.cmd === "delete") {
|
|
||||||
deleteNode(node);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
console.log("Unknown command: " + ui.cmd);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
27
static/js/treeutils.js
Normal file
27
static/js/treeutils.js
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
function getParentKey(node) {
|
||||||
|
return (node.getParent() === null || node.getParent().key === "root_1") ? "root" : node.getParent().key;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getParentEncryption(node) {
|
||||||
|
return node.getParent() === null ? 0 : node.getParent().data.encryption;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getNodeByKey(noteId) {
|
||||||
|
return globalTree.fancytree('getNodeByKey', noteId);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getFullName(noteId) {
|
||||||
|
let note = getNodeByKey(noteId);
|
||||||
|
const path = [];
|
||||||
|
|
||||||
|
while (note) {
|
||||||
|
path.push(note.title);
|
||||||
|
|
||||||
|
note = note.getParent();
|
||||||
|
}
|
||||||
|
|
||||||
|
// remove "root" element
|
||||||
|
path.pop();
|
||||||
|
|
||||||
|
return path.reverse().join(" > ");
|
||||||
|
}
|
@ -14,6 +14,21 @@ function error(str) {
|
|||||||
error.fadeOut(10000);
|
error.fadeOut(10000);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getAutocompleteItems(notes) {
|
||||||
|
const autocompleteItems = [];
|
||||||
|
|
||||||
|
for (const noteId of notes) {
|
||||||
|
const fullName = getFullName(noteId);
|
||||||
|
|
||||||
|
autocompleteItems.push({
|
||||||
|
value: fullName + " (" + noteId + ")",
|
||||||
|
label: fullName
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return autocompleteItems;
|
||||||
|
}
|
||||||
|
|
||||||
function uint8ToBase64(u8Arr) {
|
function uint8ToBase64(u8Arr) {
|
||||||
const CHUNK_SIZE = 0x8000; //arbitrary number
|
const CHUNK_SIZE = 0x8000; //arbitrary number
|
||||||
const length = u8Arr.length;
|
const length = u8Arr.length;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user