mirror of
https://github.com/zadam/trilium.git
synced 2025-11-08 06:15:48 +01:00
using ES6 modules for whole frontend SPA app
This commit is contained in:
@@ -1,137 +1,141 @@
|
||||
"use strict";
|
||||
|
||||
const addLink = (function() {
|
||||
const $dialog = $("#add-link-dialog");
|
||||
const $form = $("#add-link-form");
|
||||
const $autoComplete = $("#note-autocomplete");
|
||||
const $linkTitle = $("#link-title");
|
||||
const $clonePrefix = $("#clone-prefix");
|
||||
const $linkTitleFormGroup = $("#add-link-title-form-group");
|
||||
const $prefixFormGroup = $("#add-link-prefix-form-group");
|
||||
const $linkTypes = $("input[name='add-link-type']");
|
||||
const $linkTypeHtml = $linkTypes.filter('input[value="html"]');
|
||||
import treeService from '../note_tree.js';
|
||||
import cloning from '../cloning.js';
|
||||
import link from '../link.js';
|
||||
import noteEditor from '../note_editor.js';
|
||||
import treeUtils from '../tree_utils.js';
|
||||
|
||||
function setLinkType(linkType) {
|
||||
$linkTypes.each(function () {
|
||||
$(this).prop('checked', $(this).val() === linkType);
|
||||
});
|
||||
const $dialog = $("#add-link-dialog");
|
||||
const $form = $("#add-link-form");
|
||||
const $autoComplete = $("#note-autocomplete");
|
||||
const $linkTitle = $("#link-title");
|
||||
const $clonePrefix = $("#clone-prefix");
|
||||
const $linkTitleFormGroup = $("#add-link-title-form-group");
|
||||
const $prefixFormGroup = $("#add-link-prefix-form-group");
|
||||
const $linkTypes = $("input[name='add-link-type']");
|
||||
const $linkTypeHtml = $linkTypes.filter('input[value="html"]');
|
||||
|
||||
linkTypeChanged();
|
||||
function setLinkType(linkType) {
|
||||
$linkTypes.each(function () {
|
||||
$(this).prop('checked', $(this).val() === linkType);
|
||||
});
|
||||
|
||||
linkTypeChanged();
|
||||
}
|
||||
|
||||
async function showDialog() {
|
||||
glob.activeDialog = $dialog;
|
||||
|
||||
if (noteEditor.getCurrentNoteType() === 'text') {
|
||||
$linkTypeHtml.prop('disabled', false);
|
||||
|
||||
setLinkType('html');
|
||||
}
|
||||
else {
|
||||
$linkTypeHtml.prop('disabled', true);
|
||||
|
||||
setLinkType('selected-to-current');
|
||||
}
|
||||
|
||||
async function showDialog() {
|
||||
glob.activeDialog = $dialog;
|
||||
$dialog.dialog({
|
||||
modal: true,
|
||||
width: 700
|
||||
});
|
||||
|
||||
if (noteEditor.getCurrentNoteType() === 'text') {
|
||||
$linkTypeHtml.prop('disabled', false);
|
||||
$autoComplete.val('').focus();
|
||||
$clonePrefix.val('');
|
||||
$linkTitle.val('');
|
||||
|
||||
setLinkType('html');
|
||||
}
|
||||
else {
|
||||
$linkTypeHtml.prop('disabled', true);
|
||||
function setDefaultLinkTitle(noteId) {
|
||||
const noteTitle = treeService.getNoteTitle(noteId);
|
||||
|
||||
setLinkType('selected-to-current');
|
||||
}
|
||||
$linkTitle.val(noteTitle);
|
||||
}
|
||||
|
||||
$dialog.dialog({
|
||||
modal: true,
|
||||
width: 700
|
||||
});
|
||||
$autoComplete.autocomplete({
|
||||
source: await treeService.getAutocompleteItems(),
|
||||
minLength: 0,
|
||||
change: () => {
|
||||
const val = $autoComplete.val();
|
||||
const notePath = link.getNodePathFromLabel(val);
|
||||
if (!notePath) {
|
||||
return;
|
||||
}
|
||||
|
||||
$autoComplete.val('').focus();
|
||||
$clonePrefix.val('');
|
||||
$linkTitle.val('');
|
||||
|
||||
function setDefaultLinkTitle(noteId) {
|
||||
const noteTitle = treeService.getNoteTitle(noteId);
|
||||
|
||||
$linkTitle.val(noteTitle);
|
||||
}
|
||||
|
||||
$autoComplete.autocomplete({
|
||||
source: await treeService.getAutocompleteItems(),
|
||||
minLength: 0,
|
||||
change: () => {
|
||||
const val = $autoComplete.val();
|
||||
const notePath = link.getNodePathFromLabel(val);
|
||||
if (!notePath) {
|
||||
return;
|
||||
}
|
||||
|
||||
const noteId = treeUtils.getNoteIdFromNotePath(notePath);
|
||||
|
||||
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: (event, ui) => {
|
||||
const notePath = link.getNodePathFromLabel(ui.item.value);
|
||||
const noteId = treeUtils.getNoteIdFromNotePath(notePath);
|
||||
const noteId = treeUtils.getNoteIdFromNotePath(notePath);
|
||||
|
||||
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: (event, ui) => {
|
||||
const notePath = link.getNodePathFromLabel(ui.item.value);
|
||||
const noteId = treeUtils.getNoteIdFromNotePath(notePath);
|
||||
|
||||
$form.submit(() => {
|
||||
const value = $autoComplete.val();
|
||||
|
||||
const notePath = link.getNodePathFromLabel(value);
|
||||
const noteId = treeUtils.getNoteIdFromNotePath(notePath);
|
||||
|
||||
if (notePath) {
|
||||
const linkType = $("input[name='add-link-type']:checked").val();
|
||||
|
||||
if (linkType === 'html') {
|
||||
const linkTitle = $linkTitle.val();
|
||||
|
||||
$dialog.dialog("close");
|
||||
|
||||
link.addLinkToEditor(linkTitle, '#' + notePath);
|
||||
}
|
||||
else if (linkType === 'selected-to-current') {
|
||||
const prefix = $clonePrefix.val();
|
||||
|
||||
cloning.cloneNoteTo(noteId, noteEditor.getCurrentNoteId(), prefix);
|
||||
|
||||
$dialog.dialog("close");
|
||||
}
|
||||
else if (linkType === 'current-to-selected') {
|
||||
const prefix = $clonePrefix.val();
|
||||
|
||||
cloning.cloneNoteTo(noteEditor.getCurrentNoteId(), noteId, prefix);
|
||||
|
||||
$dialog.dialog("close");
|
||||
}
|
||||
setDefaultLinkTitle(noteId);
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
function linkTypeChanged() {
|
||||
const value = $linkTypes.filter(":checked").val();
|
||||
$form.submit(() => {
|
||||
const value = $autoComplete.val();
|
||||
|
||||
if (value === 'html') {
|
||||
$linkTitleFormGroup.show();
|
||||
$prefixFormGroup.hide();
|
||||
const notePath = link.getNodePathFromLabel(value);
|
||||
const noteId = treeUtils.getNoteIdFromNotePath(notePath);
|
||||
|
||||
if (notePath) {
|
||||
const linkType = $("input[name='add-link-type']:checked").val();
|
||||
|
||||
if (linkType === 'html') {
|
||||
const linkTitle = $linkTitle.val();
|
||||
|
||||
$dialog.dialog("close");
|
||||
|
||||
link.addLinkToEditor(linkTitle, '#' + notePath);
|
||||
}
|
||||
else {
|
||||
$linkTitleFormGroup.hide();
|
||||
$prefixFormGroup.show();
|
||||
else if (linkType === 'selected-to-current') {
|
||||
const prefix = $clonePrefix.val();
|
||||
|
||||
cloning.cloneNoteTo(noteId, noteEditor.getCurrentNoteId(), prefix);
|
||||
|
||||
$dialog.dialog("close");
|
||||
}
|
||||
else if (linkType === 'current-to-selected') {
|
||||
const prefix = $clonePrefix.val();
|
||||
|
||||
cloning.cloneNoteTo(noteEditor.getCurrentNoteId(), noteId, prefix);
|
||||
|
||||
$dialog.dialog("close");
|
||||
}
|
||||
}
|
||||
|
||||
$linkTypes.change(linkTypeChanged);
|
||||
return false;
|
||||
});
|
||||
|
||||
$(document).bind('keydown', 'ctrl+l', e => {
|
||||
showDialog();
|
||||
function linkTypeChanged() {
|
||||
const value = $linkTypes.filter(":checked").val();
|
||||
|
||||
e.preventDefault();
|
||||
});
|
||||
if (value === 'html') {
|
||||
$linkTitleFormGroup.show();
|
||||
$prefixFormGroup.hide();
|
||||
}
|
||||
else {
|
||||
$linkTitleFormGroup.hide();
|
||||
$prefixFormGroup.show();
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
showDialog
|
||||
};
|
||||
})();
|
||||
$linkTypes.change(linkTypeChanged);
|
||||
|
||||
$(document).bind('keydown', 'ctrl+l', e => {
|
||||
showDialog();
|
||||
|
||||
e.preventDefault();
|
||||
});
|
||||
|
||||
export default {
|
||||
showDialog
|
||||
};
|
||||
@@ -1,46 +1,46 @@
|
||||
"use strict";
|
||||
|
||||
const editTreePrefix = (function() {
|
||||
const $dialog = $("#edit-tree-prefix-dialog");
|
||||
const $form = $("#edit-tree-prefix-form");
|
||||
const $treePrefixInput = $("#tree-prefix-input");
|
||||
const $noteTitle = $('#tree-prefix-note-title');
|
||||
import treeService from '../note_tree.js';
|
||||
|
||||
let branchId;
|
||||
const $dialog = $("#edit-tree-prefix-dialog");
|
||||
const $form = $("#edit-tree-prefix-form");
|
||||
const $treePrefixInput = $("#tree-prefix-input");
|
||||
const $noteTitle = $('#tree-prefix-note-title');
|
||||
|
||||
async function showDialog() {
|
||||
glob.activeDialog = $dialog;
|
||||
let branchId;
|
||||
|
||||
await $dialog.dialog({
|
||||
modal: true,
|
||||
width: 500
|
||||
});
|
||||
async function showDialog() {
|
||||
glob.activeDialog = $dialog;
|
||||
|
||||
const currentNode = treeService.getCurrentNode();
|
||||
|
||||
branchId = currentNode.data.branchId;
|
||||
const nt = treeService.getBranch(branchId);
|
||||
|
||||
$treePrefixInput.val(nt.prefix).focus();
|
||||
|
||||
const noteTitle = treeService.getNoteTitle(currentNode.data.noteId);
|
||||
|
||||
$noteTitle.html(noteTitle);
|
||||
}
|
||||
|
||||
$form.submit(() => {
|
||||
const prefix = $treePrefixInput.val();
|
||||
|
||||
server.put('tree/' + branchId + '/set-prefix', {
|
||||
prefix: prefix
|
||||
}).then(() => treeService.setPrefix(branchId, prefix));
|
||||
|
||||
$dialog.dialog("close");
|
||||
|
||||
return false;
|
||||
await $dialog.dialog({
|
||||
modal: true,
|
||||
width: 500
|
||||
});
|
||||
|
||||
return {
|
||||
showDialog
|
||||
};
|
||||
})();
|
||||
const currentNode = treeService.getCurrentNode();
|
||||
|
||||
branchId = currentNode.data.branchId;
|
||||
const nt = treeService.getBranch(branchId);
|
||||
|
||||
$treePrefixInput.val(nt.prefix).focus();
|
||||
|
||||
const noteTitle = treeService.getNoteTitle(currentNode.data.noteId);
|
||||
|
||||
$noteTitle.html(noteTitle);
|
||||
}
|
||||
|
||||
$form.submit(() => {
|
||||
const prefix = $treePrefixInput.val();
|
||||
|
||||
server.put('tree/' + branchId + '/set-prefix', {
|
||||
prefix: prefix
|
||||
}).then(() => treeService.setPrefix(branchId, prefix));
|
||||
|
||||
$dialog.dialog("close");
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
export default {
|
||||
showDialog
|
||||
};
|
||||
@@ -1,38 +1,39 @@
|
||||
"use strict";
|
||||
|
||||
const eventLog = (function() {
|
||||
const $dialog = $("#event-log-dialog");
|
||||
const $list = $("#event-log-list");
|
||||
import link from '../link.js';
|
||||
import utils from '../utils.js';
|
||||
|
||||
async function showDialog() {
|
||||
glob.activeDialog = $dialog;
|
||||
const $dialog = $("#event-log-dialog");
|
||||
const $list = $("#event-log-list");
|
||||
|
||||
$dialog.dialog({
|
||||
modal: true,
|
||||
width: 800,
|
||||
height: 700
|
||||
});
|
||||
async function showDialog() {
|
||||
glob.activeDialog = $dialog;
|
||||
|
||||
const result = await server.get('event-log');
|
||||
$dialog.dialog({
|
||||
modal: true,
|
||||
width: 800,
|
||||
height: 700
|
||||
});
|
||||
|
||||
$list.html('');
|
||||
const result = await server.get('event-log');
|
||||
|
||||
for (const event of result) {
|
||||
const dateTime = utils.formatDateTime(utils.parseDate(event.dateAdded));
|
||||
$list.html('');
|
||||
|
||||
if (event.noteId) {
|
||||
const noteLink = link.createNoteLink(event.noteId).prop('outerHTML');
|
||||
for (const event of result) {
|
||||
const dateTime = utils.formatDateTime(utils.parseDate(event.dateAdded));
|
||||
|
||||
event.comment = event.comment.replace('<note>', noteLink);
|
||||
}
|
||||
if (event.noteId) {
|
||||
const noteLink = link.createNoteLink(event.noteId).prop('outerHTML');
|
||||
|
||||
const eventEl = $('<li>').html(dateTime + " - " + event.comment);
|
||||
|
||||
$list.append(eventEl);
|
||||
event.comment = event.comment.replace('<note>', noteLink);
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
showDialog
|
||||
};
|
||||
})();
|
||||
const eventEl = $('<li>').html(dateTime + " - " + event.comment);
|
||||
|
||||
$list.append(eventEl);
|
||||
}
|
||||
}
|
||||
|
||||
export default {
|
||||
showDialog
|
||||
};
|
||||
|
||||
@@ -1,59 +1,61 @@
|
||||
"use strict";
|
||||
|
||||
const jumpToNote = (function() {
|
||||
const $showDialogButton = $("#jump-to-note-button");
|
||||
const $dialog = $("#jump-to-note-dialog");
|
||||
const $autoComplete = $("#jump-to-note-autocomplete");
|
||||
const $form = $("#jump-to-note-form");
|
||||
import treeService from '../note_tree.js';
|
||||
import link from '../link.js';
|
||||
import utils from '../utils.js';
|
||||
|
||||
async function showDialog() {
|
||||
glob.activeDialog = $dialog;
|
||||
const $showDialogButton = $("#jump-to-note-button");
|
||||
const $dialog = $("#jump-to-note-dialog");
|
||||
const $autoComplete = $("#jump-to-note-autocomplete");
|
||||
const $form = $("#jump-to-note-form");
|
||||
|
||||
$autoComplete.val('');
|
||||
async function showDialog() {
|
||||
glob.activeDialog = $dialog;
|
||||
|
||||
$dialog.dialog({
|
||||
modal: true,
|
||||
width: 800
|
||||
});
|
||||
$autoComplete.val('');
|
||||
|
||||
await $autoComplete.autocomplete({
|
||||
source: await utils.stopWatch("building autocomplete", treeService.getAutocompleteItems),
|
||||
minLength: 0
|
||||
});
|
||||
}
|
||||
|
||||
function getSelectedNotePath() {
|
||||
const val = $autoComplete.val();
|
||||
return link.getNodePathFromLabel(val);
|
||||
}
|
||||
|
||||
function goToNote() {
|
||||
const notePath = getSelectedNotePath();
|
||||
|
||||
if (notePath) {
|
||||
treeService.activateNode(notePath);
|
||||
|
||||
$dialog.dialog('close');
|
||||
}
|
||||
}
|
||||
|
||||
$(document).bind('keydown', 'ctrl+j', e => {
|
||||
showDialog();
|
||||
|
||||
e.preventDefault();
|
||||
$dialog.dialog({
|
||||
modal: true,
|
||||
width: 800
|
||||
});
|
||||
|
||||
$form.submit(() => {
|
||||
const action = $dialog.find("button:focus").val();
|
||||
|
||||
goToNote();
|
||||
|
||||
return false;
|
||||
await $autoComplete.autocomplete({
|
||||
source: await utils.stopWatch("building autocomplete", treeService.getAutocompleteItems),
|
||||
minLength: 0
|
||||
});
|
||||
}
|
||||
|
||||
$showDialogButton.click(showDialog);
|
||||
function getSelectedNotePath() {
|
||||
const val = $autoComplete.val();
|
||||
return link.getNodePathFromLabel(val);
|
||||
}
|
||||
|
||||
return {
|
||||
showDialog
|
||||
};
|
||||
})();
|
||||
function goToNote() {
|
||||
const notePath = getSelectedNotePath();
|
||||
|
||||
if (notePath) {
|
||||
treeService.activateNode(notePath);
|
||||
|
||||
$dialog.dialog('close');
|
||||
}
|
||||
}
|
||||
|
||||
$(document).bind('keydown', 'ctrl+j', e => {
|
||||
showDialog();
|
||||
|
||||
e.preventDefault();
|
||||
});
|
||||
|
||||
$form.submit(() => {
|
||||
const action = $dialog.find("button:focus").val();
|
||||
|
||||
goToNote();
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
$showDialogButton.click(showDialog);
|
||||
|
||||
export default {
|
||||
showDialog
|
||||
};
|
||||
@@ -1,227 +1,228 @@
|
||||
"use strict";
|
||||
|
||||
const labelsDialog = (function() {
|
||||
const $showDialogButton = $(".show-labels-button");
|
||||
const $dialog = $("#labels-dialog");
|
||||
const $saveLabelsButton = $("#save-labels-button");
|
||||
const $labelsBody = $('#labels-table tbody');
|
||||
import noteEditor from '../note_editor.js';
|
||||
import utils from '../utils.js';
|
||||
|
||||
const labelsModel = new LabelsModel();
|
||||
let labelNames = [];
|
||||
const $showDialogButton = $(".show-labels-button");
|
||||
const $dialog = $("#labels-dialog");
|
||||
const $saveLabelsButton = $("#save-labels-button");
|
||||
const $labelsBody = $('#labels-table tbody');
|
||||
|
||||
function LabelsModel() {
|
||||
const self = this;
|
||||
const labelsModel = new LabelsModel();
|
||||
let labelNames = [];
|
||||
|
||||
this.labels = ko.observableArray();
|
||||
function LabelsModel() {
|
||||
const self = this;
|
||||
|
||||
this.loadLabels = async function() {
|
||||
const noteId = noteEditor.getCurrentNoteId();
|
||||
this.labels = ko.observableArray();
|
||||
|
||||
const labels = await server.get('notes/' + noteId + '/labels');
|
||||
this.loadLabels = async function() {
|
||||
const noteId = noteEditor.getCurrentNoteId();
|
||||
|
||||
self.labels(labels.map(ko.observable));
|
||||
const labels = await server.get('notes/' + noteId + '/labels');
|
||||
|
||||
self.labels(labels.map(ko.observable));
|
||||
|
||||
addLastEmptyRow();
|
||||
|
||||
labelNames = await server.get('labels/names');
|
||||
|
||||
// label might not be rendered immediatelly so could not focus
|
||||
setTimeout(() => $(".label-name:last").focus(), 100);
|
||||
|
||||
$labelsBody.sortable({
|
||||
handle: '.handle',
|
||||
containment: $labelsBody,
|
||||
update: function() {
|
||||
let position = 0;
|
||||
|
||||
// we need to update positions by searching in the DOM, because order of the
|
||||
// labels in the viewmodel (self.labels()) stays the same
|
||||
$labelsBody.find('input[name="position"]').each(function() {
|
||||
const attr = self.getTargetLabel(this);
|
||||
|
||||
attr().position = position++;
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
this.deleteLabel = function(data, event) {
|
||||
const attr = self.getTargetLabel(event.target);
|
||||
const attrData = attr();
|
||||
|
||||
if (attrData) {
|
||||
attrData.isDeleted = 1;
|
||||
|
||||
attr(attrData);
|
||||
|
||||
addLastEmptyRow();
|
||||
|
||||
labelNames = await server.get('labels/names');
|
||||
|
||||
// label might not be rendered immediatelly so could not focus
|
||||
setTimeout(() => $(".label-name:last").focus(), 100);
|
||||
|
||||
$labelsBody.sortable({
|
||||
handle: '.handle',
|
||||
containment: $labelsBody,
|
||||
update: function() {
|
||||
let position = 0;
|
||||
|
||||
// we need to update positions by searching in the DOM, because order of the
|
||||
// labels in the viewmodel (self.labels()) stays the same
|
||||
$labelsBody.find('input[name="position"]').each(function() {
|
||||
const attr = self.getTargetLabel(this);
|
||||
|
||||
attr().position = position++;
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
this.deleteLabel = function(data, event) {
|
||||
const attr = self.getTargetLabel(event.target);
|
||||
const attrData = attr();
|
||||
|
||||
if (attrData) {
|
||||
attrData.isDeleted = 1;
|
||||
|
||||
attr(attrData);
|
||||
|
||||
addLastEmptyRow();
|
||||
}
|
||||
};
|
||||
|
||||
function isValid() {
|
||||
for (let attrs = self.labels(), i = 0; i < attrs.length; i++) {
|
||||
if (self.isEmptyName(i)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
this.save = async function() {
|
||||
// we need to defocus from input (in case of enter-triggered save) because value is updated
|
||||
// on blur event (because of conflict with jQuery UI Autocomplete). Without this, input would
|
||||
// stay in focus, blur wouldn't be triggered and change wouldn't be updated in the viewmodel.
|
||||
$saveLabelsButton.focus();
|
||||
|
||||
if (!isValid()) {
|
||||
alert("Please fix all validation errors and try saving again.");
|
||||
return;
|
||||
}
|
||||
|
||||
const noteId = noteEditor.getCurrentNoteId();
|
||||
|
||||
const labelsToSave = self.labels()
|
||||
.map(attr => attr())
|
||||
.filter(attr => attr.labelId !== "" || attr.name !== "");
|
||||
|
||||
const labels = await server.put('notes/' + noteId + '/labels', labelsToSave);
|
||||
|
||||
self.labels(labels.map(ko.observable));
|
||||
|
||||
addLastEmptyRow();
|
||||
|
||||
utils.showMessage("Labels have been saved.");
|
||||
|
||||
noteEditor.loadLabelList();
|
||||
};
|
||||
|
||||
function addLastEmptyRow() {
|
||||
const attrs = self.labels().filter(attr => attr().isDeleted === 0);
|
||||
const last = attrs.length === 0 ? null : attrs[attrs.length - 1]();
|
||||
|
||||
if (!last || last.name.trim() !== "" || last.value !== "") {
|
||||
self.labels.push(ko.observable({
|
||||
labelId: '',
|
||||
name: '',
|
||||
value: '',
|
||||
isDeleted: 0,
|
||||
position: 0
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
this.labelChanged = function (data, event) {
|
||||
addLastEmptyRow();
|
||||
|
||||
const attr = self.getTargetLabel(event.target);
|
||||
|
||||
attr.valueHasMutated();
|
||||
};
|
||||
|
||||
this.isNotUnique = function(index) {
|
||||
const cur = self.labels()[index]();
|
||||
|
||||
if (cur.name.trim() === "") {
|
||||
function isValid() {
|
||||
for (let attrs = self.labels(), i = 0; i < attrs.length; i++) {
|
||||
if (self.isEmptyName(i)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
for (let attrs = self.labels(), i = 0; i < attrs.length; i++) {
|
||||
const attr = attrs[i]();
|
||||
return true;
|
||||
}
|
||||
|
||||
if (index !== i && cur.name === attr.name) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
this.save = async function() {
|
||||
// we need to defocus from input (in case of enter-triggered save) because value is updated
|
||||
// on blur event (because of conflict with jQuery UI Autocomplete). Without this, input would
|
||||
// stay in focus, blur wouldn't be triggered and change wouldn't be updated in the viewmodel.
|
||||
$saveLabelsButton.focus();
|
||||
|
||||
return false;
|
||||
};
|
||||
if (!isValid()) {
|
||||
alert("Please fix all validation errors and try saving again.");
|
||||
return;
|
||||
}
|
||||
|
||||
this.isEmptyName = function(index) {
|
||||
const cur = self.labels()[index]();
|
||||
const noteId = noteEditor.getCurrentNoteId();
|
||||
|
||||
return cur.name.trim() === "" && (cur.labelId !== "" || cur.value !== "");
|
||||
};
|
||||
const labelsToSave = self.labels()
|
||||
.map(attr => attr())
|
||||
.filter(attr => attr.labelId !== "" || attr.name !== "");
|
||||
|
||||
this.getTargetLabel = function(target) {
|
||||
const context = ko.contextFor(target);
|
||||
const index = context.$index();
|
||||
const labels = await server.put('notes/' + noteId + '/labels', labelsToSave);
|
||||
|
||||
return self.labels()[index];
|
||||
self.labels(labels.map(ko.observable));
|
||||
|
||||
addLastEmptyRow();
|
||||
|
||||
utils.showMessage("Labels have been saved.");
|
||||
|
||||
noteEditor.loadLabelList();
|
||||
};
|
||||
|
||||
function addLastEmptyRow() {
|
||||
const attrs = self.labels().filter(attr => attr().isDeleted === 0);
|
||||
const last = attrs.length === 0 ? null : attrs[attrs.length - 1]();
|
||||
|
||||
if (!last || last.name.trim() !== "" || last.value !== "") {
|
||||
self.labels.push(ko.observable({
|
||||
labelId: '',
|
||||
name: '',
|
||||
value: '',
|
||||
isDeleted: 0,
|
||||
position: 0
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
async function showDialog() {
|
||||
glob.activeDialog = $dialog;
|
||||
this.labelChanged = function (data, event) {
|
||||
addLastEmptyRow();
|
||||
|
||||
await labelsModel.loadLabels();
|
||||
const attr = self.getTargetLabel(event.target);
|
||||
|
||||
$dialog.dialog({
|
||||
modal: true,
|
||||
width: 800,
|
||||
height: 500
|
||||
attr.valueHasMutated();
|
||||
};
|
||||
|
||||
this.isNotUnique = function(index) {
|
||||
const cur = self.labels()[index]();
|
||||
|
||||
if (cur.name.trim() === "") {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (let attrs = self.labels(), i = 0; i < attrs.length; i++) {
|
||||
const attr = attrs[i]();
|
||||
|
||||
if (index !== i && cur.name === attr.name) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
this.isEmptyName = function(index) {
|
||||
const cur = self.labels()[index]();
|
||||
|
||||
return cur.name.trim() === "" && (cur.labelId !== "" || cur.value !== "");
|
||||
};
|
||||
|
||||
this.getTargetLabel = function(target) {
|
||||
const context = ko.contextFor(target);
|
||||
const index = context.$index();
|
||||
|
||||
return self.labels()[index];
|
||||
}
|
||||
}
|
||||
|
||||
async function showDialog() {
|
||||
glob.activeDialog = $dialog;
|
||||
|
||||
await labelsModel.loadLabels();
|
||||
|
||||
$dialog.dialog({
|
||||
modal: true,
|
||||
width: 800,
|
||||
height: 500
|
||||
});
|
||||
}
|
||||
|
||||
$(document).bind('keydown', 'alt+a', e => {
|
||||
showDialog();
|
||||
|
||||
e.preventDefault();
|
||||
});
|
||||
|
||||
ko.applyBindings(labelsModel, document.getElementById('labels-dialog'));
|
||||
|
||||
$(document).on('focus', '.label-name', function (e) {
|
||||
if (!$(this).hasClass("ui-autocomplete-input")) {
|
||||
$(this).autocomplete({
|
||||
// shouldn't be required and autocomplete should just accept array of strings, but that fails
|
||||
// because we have overriden filter() function in init.js
|
||||
source: labelNames.map(attr => {
|
||||
return {
|
||||
label: attr,
|
||||
value: attr
|
||||
}
|
||||
}),
|
||||
minLength: 0
|
||||
});
|
||||
}
|
||||
|
||||
$(document).bind('keydown', 'alt+a', e => {
|
||||
showDialog();
|
||||
$(this).autocomplete("search", $(this).val());
|
||||
});
|
||||
|
||||
e.preventDefault();
|
||||
});
|
||||
$(document).on('focus', '.label-value', async function (e) {
|
||||
if (!$(this).hasClass("ui-autocomplete-input")) {
|
||||
const labelName = $(this).parent().parent().find('.label-name').val();
|
||||
|
||||
ko.applyBindings(labelsModel, document.getElementById('labels-dialog'));
|
||||
|
||||
$(document).on('focus', '.label-name', function (e) {
|
||||
if (!$(this).hasClass("ui-autocomplete-input")) {
|
||||
$(this).autocomplete({
|
||||
// shouldn't be required and autocomplete should just accept array of strings, but that fails
|
||||
// because we have overriden filter() function in init.js
|
||||
source: labelNames.map(attr => {
|
||||
return {
|
||||
label: attr,
|
||||
value: attr
|
||||
}
|
||||
}),
|
||||
minLength: 0
|
||||
});
|
||||
if (labelName.trim() === "") {
|
||||
return;
|
||||
}
|
||||
|
||||
$(this).autocomplete("search", $(this).val());
|
||||
});
|
||||
const labelValues = await server.get('labels/values/' + encodeURIComponent(labelName));
|
||||
|
||||
$(document).on('focus', '.label-value', async function (e) {
|
||||
if (!$(this).hasClass("ui-autocomplete-input")) {
|
||||
const labelName = $(this).parent().parent().find('.label-name').val();
|
||||
|
||||
if (labelName.trim() === "") {
|
||||
return;
|
||||
}
|
||||
|
||||
const labelValues = await server.get('labels/values/' + encodeURIComponent(labelName));
|
||||
|
||||
if (labelValues.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
$(this).autocomplete({
|
||||
// shouldn't be required and autocomplete should just accept array of strings, but that fails
|
||||
// because we have overriden filter() function in init.js
|
||||
source: labelValues.map(attr => {
|
||||
return {
|
||||
label: attr,
|
||||
value: attr
|
||||
}
|
||||
}),
|
||||
minLength: 0
|
||||
});
|
||||
if (labelValues.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
$(this).autocomplete("search", $(this).val());
|
||||
});
|
||||
$(this).autocomplete({
|
||||
// shouldn't be required and autocomplete should just accept array of strings, but that fails
|
||||
// because we have overriden filter() function in init.js
|
||||
source: labelValues.map(attr => {
|
||||
return {
|
||||
label: attr,
|
||||
value: attr
|
||||
}
|
||||
}),
|
||||
minLength: 0
|
||||
});
|
||||
}
|
||||
|
||||
$showDialogButton.click(showDialog);
|
||||
$(this).autocomplete("search", $(this).val());
|
||||
});
|
||||
|
||||
return {
|
||||
showDialog
|
||||
};
|
||||
})();
|
||||
$showDialogButton.click(showDialog);
|
||||
|
||||
export default {
|
||||
showDialog
|
||||
};
|
||||
@@ -1,81 +1,82 @@
|
||||
"use strict";
|
||||
|
||||
const noteHistory = (function() {
|
||||
const $showDialogButton = $("#show-history-button");
|
||||
const $dialog = $("#note-history-dialog");
|
||||
const $list = $("#note-history-list");
|
||||
const $content = $("#note-history-content");
|
||||
const $title = $("#note-history-title");
|
||||
import noteEditor from '../note_editor.js';
|
||||
import utils from '../utils.js';
|
||||
|
||||
let historyItems = [];
|
||||
const $showDialogButton = $("#show-history-button");
|
||||
const $dialog = $("#note-history-dialog");
|
||||
const $list = $("#note-history-list");
|
||||
const $content = $("#note-history-content");
|
||||
const $title = $("#note-history-title");
|
||||
|
||||
async function showCurrentNoteHistory() {
|
||||
await showNoteHistoryDialog(noteEditor.getCurrentNoteId());
|
||||
let historyItems = [];
|
||||
|
||||
async function showCurrentNoteHistory() {
|
||||
await showNoteHistoryDialog(noteEditor.getCurrentNoteId());
|
||||
}
|
||||
|
||||
async function showNoteHistoryDialog(noteId, noteRevisionId) {
|
||||
glob.activeDialog = $dialog;
|
||||
|
||||
$dialog.dialog({
|
||||
modal: true,
|
||||
width: 800,
|
||||
height: 700
|
||||
});
|
||||
|
||||
$list.empty();
|
||||
$content.empty();
|
||||
|
||||
historyItems = await server.get('notes-history/' + noteId);
|
||||
|
||||
for (const item of historyItems) {
|
||||
const dateModified = utils.parseDate(item.dateModifiedFrom);
|
||||
|
||||
$list.append($('<option>', {
|
||||
value: item.noteRevisionId,
|
||||
text: utils.formatDateTime(dateModified)
|
||||
}));
|
||||
}
|
||||
|
||||
async function showNoteHistoryDialog(noteId, noteRevisionId) {
|
||||
glob.activeDialog = $dialog;
|
||||
|
||||
$dialog.dialog({
|
||||
modal: true,
|
||||
width: 800,
|
||||
height: 700
|
||||
});
|
||||
|
||||
$list.empty();
|
||||
$content.empty();
|
||||
|
||||
historyItems = await server.get('notes-history/' + noteId);
|
||||
|
||||
for (const item of historyItems) {
|
||||
const dateModified = utils.parseDate(item.dateModifiedFrom);
|
||||
|
||||
$list.append($('<option>', {
|
||||
value: item.noteRevisionId,
|
||||
text: utils.formatDateTime(dateModified)
|
||||
}));
|
||||
if (historyItems.length > 0) {
|
||||
if (!noteRevisionId) {
|
||||
noteRevisionId = $list.find("option:first").val();
|
||||
}
|
||||
|
||||
if (historyItems.length > 0) {
|
||||
if (!noteRevisionId) {
|
||||
noteRevisionId = $list.find("option:first").val();
|
||||
}
|
||||
|
||||
$list.val(noteRevisionId).trigger('change');
|
||||
}
|
||||
else {
|
||||
$title.text("No history for this note yet...");
|
||||
}
|
||||
$list.val(noteRevisionId).trigger('change');
|
||||
}
|
||||
else {
|
||||
$title.text("No history for this note yet...");
|
||||
}
|
||||
}
|
||||
|
||||
$(document).bind('keydown', 'alt+h', e => {
|
||||
showCurrentNoteHistory();
|
||||
$(document).bind('keydown', 'alt+h', e => {
|
||||
showCurrentNoteHistory();
|
||||
|
||||
e.preventDefault();
|
||||
});
|
||||
e.preventDefault();
|
||||
});
|
||||
|
||||
$list.on('change', () => {
|
||||
const optVal = $list.find(":selected").val();
|
||||
$list.on('change', () => {
|
||||
const optVal = $list.find(":selected").val();
|
||||
|
||||
const historyItem = historyItems.find(r => r.noteRevisionId === optVal);
|
||||
const historyItem = historyItems.find(r => r.noteRevisionId === optVal);
|
||||
|
||||
$title.html(historyItem.title);
|
||||
$content.html(historyItem.content);
|
||||
});
|
||||
$title.html(historyItem.title);
|
||||
$content.html(historyItem.content);
|
||||
});
|
||||
|
||||
$(document).on('click', "a[action='note-history']", event => {
|
||||
const linkEl = $(event.target);
|
||||
const noteId = linkEl.attr('note-path');
|
||||
const noteRevisionId = linkEl.attr('note-history-id');
|
||||
$(document).on('click', "a[action='note-history']", event => {
|
||||
const linkEl = $(event.target);
|
||||
const noteId = linkEl.attr('note-path');
|
||||
const noteRevisionId = linkEl.attr('note-history-id');
|
||||
|
||||
showNoteHistoryDialog(noteId, noteRevisionId);
|
||||
showNoteHistoryDialog(noteId, noteRevisionId);
|
||||
|
||||
return false;
|
||||
});
|
||||
return false;
|
||||
});
|
||||
|
||||
$showDialogButton.click(showCurrentNoteHistory);
|
||||
$showDialogButton.click(showCurrentNoteHistory);
|
||||
|
||||
return {
|
||||
showCurrentNoteHistory
|
||||
};
|
||||
})();
|
||||
export default {
|
||||
showCurrentNoteHistory
|
||||
};
|
||||
@@ -1,60 +1,60 @@
|
||||
"use strict";
|
||||
|
||||
const noteSource = (function() {
|
||||
const $showDialogButton = $("#show-source-button");
|
||||
const $dialog = $("#note-source-dialog");
|
||||
const $noteSource = $("#note-source");
|
||||
import noteEditor from '../note_editor.js';
|
||||
|
||||
function showDialog() {
|
||||
glob.activeDialog = $dialog;
|
||||
const $showDialogButton = $("#show-source-button");
|
||||
const $dialog = $("#note-source-dialog");
|
||||
const $noteSource = $("#note-source");
|
||||
|
||||
$dialog.dialog({
|
||||
modal: true,
|
||||
width: 800,
|
||||
height: 500
|
||||
});
|
||||
function showDialog() {
|
||||
glob.activeDialog = $dialog;
|
||||
|
||||
const noteText = noteEditor.getCurrentNote().detail.content;
|
||||
|
||||
$noteSource.text(formatHtml(noteText));
|
||||
}
|
||||
|
||||
function formatHtml(str) {
|
||||
const div = document.createElement('div');
|
||||
div.innerHTML = str.trim();
|
||||
|
||||
return formatNode(div, 0).innerHTML.trim();
|
||||
}
|
||||
|
||||
function formatNode(node, level) {
|
||||
const indentBefore = new Array(level++ + 1).join(' ');
|
||||
const indentAfter = new Array(level - 1).join(' ');
|
||||
let textNode;
|
||||
|
||||
for (let i = 0; i < node.children.length; i++) {
|
||||
textNode = document.createTextNode('\n' + indentBefore);
|
||||
node.insertBefore(textNode, node.children[i]);
|
||||
|
||||
formatNode(node.children[i], level);
|
||||
|
||||
if (node.lastElementChild === node.children[i]) {
|
||||
textNode = document.createTextNode('\n' + indentAfter);
|
||||
node.appendChild(textNode);
|
||||
}
|
||||
}
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
$(document).bind('keydown', 'ctrl+u', e => {
|
||||
showDialog();
|
||||
|
||||
e.preventDefault();
|
||||
$dialog.dialog({
|
||||
modal: true,
|
||||
width: 800,
|
||||
height: 500
|
||||
});
|
||||
|
||||
$showDialogButton.click(showDialog);
|
||||
const noteText = noteEditor.getCurrentNote().detail.content;
|
||||
|
||||
return {
|
||||
showDialog
|
||||
};
|
||||
})();
|
||||
$noteSource.text(formatHtml(noteText));
|
||||
}
|
||||
|
||||
function formatHtml(str) {
|
||||
const div = document.createElement('div');
|
||||
div.innerHTML = str.trim();
|
||||
|
||||
return formatNode(div, 0).innerHTML.trim();
|
||||
}
|
||||
|
||||
function formatNode(node, level) {
|
||||
const indentBefore = new Array(level++ + 1).join(' ');
|
||||
const indentAfter = new Array(level - 1).join(' ');
|
||||
let textNode;
|
||||
|
||||
for (let i = 0; i < node.children.length; i++) {
|
||||
textNode = document.createTextNode('\n' + indentBefore);
|
||||
node.insertBefore(textNode, node.children[i]);
|
||||
|
||||
formatNode(node.children[i], level);
|
||||
|
||||
if (node.lastElementChild === node.children[i]) {
|
||||
textNode = document.createTextNode('\n' + indentAfter);
|
||||
node.appendChild(textNode);
|
||||
}
|
||||
}
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
$(document).bind('keydown', 'ctrl+u', e => {
|
||||
showDialog();
|
||||
|
||||
e.preventDefault();
|
||||
});
|
||||
|
||||
$showDialogButton.click(showDialog);
|
||||
|
||||
export default {
|
||||
showDialog
|
||||
};
|
||||
@@ -1,92 +1,93 @@
|
||||
"use strict";
|
||||
|
||||
const recentChanges = (function() {
|
||||
const $showDialogButton = $("#recent-changes-button");
|
||||
const $dialog = $("#recent-changes-dialog");
|
||||
import link from '../link.js';
|
||||
import utils from '../utils.js';
|
||||
|
||||
async function showDialog() {
|
||||
glob.activeDialog = $dialog;
|
||||
const $showDialogButton = $("#recent-changes-button");
|
||||
const $dialog = $("#recent-changes-dialog");
|
||||
|
||||
$dialog.dialog({
|
||||
modal: true,
|
||||
width: 800,
|
||||
height: 700
|
||||
});
|
||||
async function showDialog() {
|
||||
glob.activeDialog = $dialog;
|
||||
|
||||
const result = await server.get('recent-changes/');
|
||||
$dialog.dialog({
|
||||
modal: true,
|
||||
width: 800,
|
||||
height: 700
|
||||
});
|
||||
|
||||
$dialog.html('');
|
||||
const result = await server.get('recent-changes/');
|
||||
|
||||
const groupedByDate = groupByDate(result);
|
||||
$dialog.html('');
|
||||
|
||||
for (const [dateDay, dayChanges] of groupedByDate) {
|
||||
const changesListEl = $('<ul>');
|
||||
const groupedByDate = groupByDate(result);
|
||||
|
||||
const dayEl = $('<div>').append($('<b>').html(utils.formatDate(dateDay))).append(changesListEl);
|
||||
for (const [dateDay, dayChanges] of groupedByDate) {
|
||||
const changesListEl = $('<ul>');
|
||||
|
||||
for (const change of dayChanges) {
|
||||
const formattedTime = utils.formatTime(utils.parseDate(change.dateModifiedTo));
|
||||
const dayEl = $('<div>').append($('<b>').html(utils.formatDate(dateDay))).append(changesListEl);
|
||||
|
||||
const revLink = $("<a>", {
|
||||
href: 'javascript:',
|
||||
text: 'rev'
|
||||
}).attr('action', 'note-history')
|
||||
.attr('note-path', change.noteId)
|
||||
.attr('note-history-id', change.noteRevisionId);
|
||||
for (const change of dayChanges) {
|
||||
const formattedTime = utils.formatTime(utils.parseDate(change.dateModifiedTo));
|
||||
|
||||
let noteLink;
|
||||
const revLink = $("<a>", {
|
||||
href: 'javascript:',
|
||||
text: 'rev'
|
||||
}).attr('action', 'note-history')
|
||||
.attr('note-path', change.noteId)
|
||||
.attr('note-history-id', change.noteRevisionId);
|
||||
|
||||
if (change.current_isDeleted) {
|
||||
noteLink = change.current_title;
|
||||
}
|
||||
else {
|
||||
noteLink = link.createNoteLink(change.noteId, change.title);
|
||||
}
|
||||
let noteLink;
|
||||
|
||||
changesListEl.append($('<li>')
|
||||
.append(formattedTime + ' - ')
|
||||
.append(noteLink)
|
||||
.append(' (').append(revLink).append(')'));
|
||||
}
|
||||
|
||||
$dialog.append(dayEl);
|
||||
}
|
||||
}
|
||||
|
||||
function groupByDate(result) {
|
||||
const groupedByDate = new Map();
|
||||
const dayCache = {};
|
||||
|
||||
for (const row of result) {
|
||||
let dateDay = utils.parseDate(row.dateModifiedTo);
|
||||
dateDay.setHours(0);
|
||||
dateDay.setMinutes(0);
|
||||
dateDay.setSeconds(0);
|
||||
dateDay.setMilliseconds(0);
|
||||
|
||||
// this stupidity is to make sure that we always use the same day object because Map uses only
|
||||
// reference equality
|
||||
if (dayCache[dateDay]) {
|
||||
dateDay = dayCache[dateDay];
|
||||
if (change.current_isDeleted) {
|
||||
noteLink = change.current_title;
|
||||
}
|
||||
else {
|
||||
dayCache[dateDay] = dateDay;
|
||||
noteLink = link.createNoteLink(change.noteId, change.title);
|
||||
}
|
||||
|
||||
if (!groupedByDate.has(dateDay)) {
|
||||
groupedByDate.set(dateDay, []);
|
||||
}
|
||||
|
||||
groupedByDate.get(dateDay).push(row);
|
||||
changesListEl.append($('<li>')
|
||||
.append(formattedTime + ' - ')
|
||||
.append(noteLink)
|
||||
.append(' (').append(revLink).append(')'));
|
||||
}
|
||||
return groupedByDate;
|
||||
|
||||
$dialog.append(dayEl);
|
||||
}
|
||||
}
|
||||
|
||||
$(document).bind('keydown', 'alt+r', showDialog);
|
||||
function groupByDate(result) {
|
||||
const groupedByDate = new Map();
|
||||
const dayCache = {};
|
||||
|
||||
$showDialogButton.click(showDialog);
|
||||
for (const row of result) {
|
||||
let dateDay = utils.parseDate(row.dateModifiedTo);
|
||||
dateDay.setHours(0);
|
||||
dateDay.setMinutes(0);
|
||||
dateDay.setSeconds(0);
|
||||
dateDay.setMilliseconds(0);
|
||||
|
||||
return {
|
||||
showDialog
|
||||
};
|
||||
})();
|
||||
// this stupidity is to make sure that we always use the same day object because Map uses only
|
||||
// reference equality
|
||||
if (dayCache[dateDay]) {
|
||||
dateDay = dayCache[dateDay];
|
||||
}
|
||||
else {
|
||||
dayCache[dateDay] = dateDay;
|
||||
}
|
||||
|
||||
if (!groupedByDate.has(dateDay)) {
|
||||
groupedByDate.set(dateDay, []);
|
||||
}
|
||||
|
||||
groupedByDate.get(dateDay).push(row);
|
||||
}
|
||||
return groupedByDate;
|
||||
}
|
||||
|
||||
$(document).bind('keydown', 'alt+r', showDialog);
|
||||
|
||||
$showDialogButton.click(showDialog);
|
||||
|
||||
export default {
|
||||
showDialog
|
||||
};
|
||||
@@ -1,105 +1,107 @@
|
||||
"use strict";
|
||||
|
||||
const recentNotes = (function() {
|
||||
const $showDialogButton = $("#recent-notes-button");
|
||||
const $dialog = $("#recent-notes-dialog");
|
||||
const $searchInput = $('#recent-notes-search-input');
|
||||
import treeService from '../note_tree.js';
|
||||
import server from '../server.js';
|
||||
import messaging from '../messaging.js';
|
||||
|
||||
// list of recent note paths
|
||||
let list = [];
|
||||
const $showDialogButton = $("#recent-notes-button");
|
||||
const $dialog = $("#recent-notes-dialog");
|
||||
const $searchInput = $('#recent-notes-search-input');
|
||||
|
||||
async function reload() {
|
||||
const result = await server.get('recent-notes');
|
||||
// list of recent note paths
|
||||
let list = [];
|
||||
|
||||
list = result.map(r => r.notePath);
|
||||
}
|
||||
async function reload() {
|
||||
const result = await server.get('recent-notes');
|
||||
|
||||
function addRecentNote(branchId, notePath) {
|
||||
setTimeout(async () => {
|
||||
// we include the note into recent list only if the user stayed on the note at least 5 seconds
|
||||
if (notePath && notePath === treeService.getCurrentNotePath()) {
|
||||
const result = await server.put('recent-notes/' + branchId + '/' + encodeURIComponent(notePath));
|
||||
list = result.map(r => r.notePath);
|
||||
}
|
||||
|
||||
list = result.map(r => r.notePath);
|
||||
}
|
||||
}, 1500);
|
||||
}
|
||||
function addRecentNote(branchId, notePath) {
|
||||
setTimeout(async () => {
|
||||
// we include the note into recent list only if the user stayed on the note at least 5 seconds
|
||||
if (notePath && notePath === treeService.getCurrentNotePath()) {
|
||||
const result = await server.put('recent-notes/' + branchId + '/' + encodeURIComponent(notePath));
|
||||
|
||||
function showDialog() {
|
||||
glob.activeDialog = $dialog;
|
||||
list = result.map(r => r.notePath);
|
||||
}
|
||||
}, 1500);
|
||||
}
|
||||
|
||||
$dialog.dialog({
|
||||
modal: true,
|
||||
width: 800,
|
||||
height: 100,
|
||||
position: { my: "center top+100", at: "top", of: window }
|
||||
});
|
||||
function showDialog() {
|
||||
glob.activeDialog = $dialog;
|
||||
|
||||
$searchInput.val('');
|
||||
|
||||
// remove the current note
|
||||
const recNotes = list.filter(note => note !== treeService.getCurrentNotePath());
|
||||
|
||||
$searchInput.autocomplete({
|
||||
source: recNotes.map(notePath => {
|
||||
let noteTitle;
|
||||
|
||||
try {
|
||||
noteTitle = treeService.getNotePathTitle(notePath);
|
||||
}
|
||||
catch (e) {
|
||||
noteTitle = "[error - can't find note title]";
|
||||
|
||||
messaging.logError("Could not find title for notePath=" + notePath + ", stack=" + e.stack);
|
||||
}
|
||||
|
||||
return {
|
||||
label: noteTitle,
|
||||
value: notePath
|
||||
}
|
||||
}),
|
||||
minLength: 0,
|
||||
autoFocus: true,
|
||||
select: function (event, ui) {
|
||||
treeService.activateNode(ui.item.value);
|
||||
|
||||
$searchInput.autocomplete('destroy');
|
||||
$dialog.dialog('close');
|
||||
},
|
||||
focus: function (event, ui) {
|
||||
event.preventDefault();
|
||||
},
|
||||
close: function (event, ui) {
|
||||
if (event.keyCode === 27) { // escape closes dialog
|
||||
$searchInput.autocomplete('destroy');
|
||||
$dialog.dialog('close');
|
||||
}
|
||||
else {
|
||||
// keep autocomplete open
|
||||
// we're kind of abusing autocomplete to work in a way which it's not designed for
|
||||
$searchInput.autocomplete("search", "");
|
||||
}
|
||||
},
|
||||
create: () => $searchInput.autocomplete("search", ""),
|
||||
classes: {
|
||||
"ui-autocomplete": "recent-notes-autocomplete"
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
reload();
|
||||
|
||||
$(document).bind('keydown', 'ctrl+e', e => {
|
||||
showDialog();
|
||||
|
||||
e.preventDefault();
|
||||
$dialog.dialog({
|
||||
modal: true,
|
||||
width: 800,
|
||||
height: 100,
|
||||
position: { my: "center top+100", at: "top", of: window }
|
||||
});
|
||||
|
||||
$showDialogButton.click(showDialog);
|
||||
$searchInput.val('');
|
||||
|
||||
return {
|
||||
showDialog,
|
||||
addRecentNote,
|
||||
reload
|
||||
};
|
||||
})();
|
||||
// remove the current note
|
||||
const recNotes = list.filter(note => note !== treeService.getCurrentNotePath());
|
||||
|
||||
$searchInput.autocomplete({
|
||||
source: recNotes.map(notePath => {
|
||||
let noteTitle;
|
||||
|
||||
try {
|
||||
noteTitle = treeService.getNotePathTitle(notePath);
|
||||
}
|
||||
catch (e) {
|
||||
noteTitle = "[error - can't find note title]";
|
||||
|
||||
messaging.logError("Could not find title for notePath=" + notePath + ", stack=" + e.stack);
|
||||
}
|
||||
|
||||
return {
|
||||
label: noteTitle,
|
||||
value: notePath
|
||||
}
|
||||
}),
|
||||
minLength: 0,
|
||||
autoFocus: true,
|
||||
select: function (event, ui) {
|
||||
treeService.activateNode(ui.item.value);
|
||||
|
||||
$searchInput.autocomplete('destroy');
|
||||
$dialog.dialog('close');
|
||||
},
|
||||
focus: function (event, ui) {
|
||||
event.preventDefault();
|
||||
},
|
||||
close: function (event, ui) {
|
||||
if (event.keyCode === 27) { // escape closes dialog
|
||||
$searchInput.autocomplete('destroy');
|
||||
$dialog.dialog('close');
|
||||
}
|
||||
else {
|
||||
// keep autocomplete open
|
||||
// we're kind of abusing autocomplete to work in a way which it's not designed for
|
||||
$searchInput.autocomplete("search", "");
|
||||
}
|
||||
},
|
||||
create: () => $searchInput.autocomplete("search", ""),
|
||||
classes: {
|
||||
"ui-autocomplete": "recent-notes-autocomplete"
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
reload();
|
||||
|
||||
$(document).bind('keydown', 'ctrl+e', e => {
|
||||
showDialog();
|
||||
|
||||
e.preventDefault();
|
||||
});
|
||||
|
||||
$showDialogButton.click(showDialog);
|
||||
|
||||
export default {
|
||||
showDialog,
|
||||
addRecentNote,
|
||||
reload
|
||||
};
|
||||
@@ -1,54 +1,56 @@
|
||||
"use strict";
|
||||
|
||||
const settings = (function() {
|
||||
const $showDialogButton = $("#settings-button");
|
||||
const $dialog = $("#settings-dialog");
|
||||
const $tabs = $("#settings-tabs");
|
||||
import protected_session from '../protected_session.js';
|
||||
import utils from '../utils.js';
|
||||
import server from '../server.js';
|
||||
|
||||
const settingModules = [];
|
||||
const $showDialogButton = $("#settings-button");
|
||||
const $dialog = $("#settings-dialog");
|
||||
const $tabs = $("#settings-tabs");
|
||||
|
||||
function addModule(module) {
|
||||
settingModules.push(module);
|
||||
}
|
||||
const settingModules = [];
|
||||
|
||||
async function showDialog() {
|
||||
glob.activeDialog = $dialog;
|
||||
function addModule(module) {
|
||||
settingModules.push(module);
|
||||
}
|
||||
|
||||
const settings = await server.get('settings');
|
||||
async function showDialog() {
|
||||
glob.activeDialog = $dialog;
|
||||
|
||||
$dialog.dialog({
|
||||
modal: true,
|
||||
width: 900
|
||||
});
|
||||
const settings = await server.get('settings');
|
||||
|
||||
$tabs.tabs();
|
||||
$dialog.dialog({
|
||||
modal: true,
|
||||
width: 900
|
||||
});
|
||||
|
||||
for (const module of settingModules) {
|
||||
if (module.settingsLoaded) {
|
||||
module.settingsLoaded(settings);
|
||||
}
|
||||
$tabs.tabs();
|
||||
|
||||
for (const module of settingModules) {
|
||||
if (module.settingsLoaded) {
|
||||
module.settingsLoaded(settings);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function saveSettings(settingName, settingValue) {
|
||||
await server.post('settings', {
|
||||
name: settingName,
|
||||
value: settingValue
|
||||
});
|
||||
async function saveSettings(settingName, settingValue) {
|
||||
await server.post('settings', {
|
||||
name: settingName,
|
||||
value: settingValue
|
||||
});
|
||||
|
||||
utils.showMessage("Settings change have been saved.");
|
||||
}
|
||||
utils.showMessage("Settings change have been saved.");
|
||||
}
|
||||
|
||||
$showDialogButton.click(showDialog);
|
||||
$showDialogButton.click(showDialog);
|
||||
|
||||
return {
|
||||
showDialog,
|
||||
saveSettings,
|
||||
addModule
|
||||
};
|
||||
})();
|
||||
export default {
|
||||
showDialog,
|
||||
saveSettings,
|
||||
addModule
|
||||
};
|
||||
|
||||
settings.addModule((function() {
|
||||
addModule((function() {
|
||||
const $form = $("#change-password-form");
|
||||
const $oldPassword = $("#old-password");
|
||||
const $newPassword1 = $("#new-password1");
|
||||
@@ -94,7 +96,7 @@ settings.addModule((function() {
|
||||
};
|
||||
})());
|
||||
|
||||
settings.addModule((function() {
|
||||
addModule((function() {
|
||||
const $form = $("#protected-session-timeout-form");
|
||||
const $protectedSessionTimeout = $("#protected-session-timeout-in-seconds");
|
||||
const settingName = 'protected_session_timeout';
|
||||
@@ -118,7 +120,7 @@ settings.addModule((function() {
|
||||
};
|
||||
})());
|
||||
|
||||
settings.addModule((function () {
|
||||
addModule((function () {
|
||||
const $form = $("#history-snapshot-time-interval-form");
|
||||
const $timeInterval = $("#history-snapshot-time-interval-in-seconds");
|
||||
const settingName = 'history_snapshot_time_interval';
|
||||
@@ -138,7 +140,7 @@ settings.addModule((function () {
|
||||
};
|
||||
})());
|
||||
|
||||
settings.addModule((async function () {
|
||||
addModule((async function () {
|
||||
const $appVersion = $("#app-version");
|
||||
const $dbVersion = $("#db-version");
|
||||
const $buildDate = $("#build-date");
|
||||
@@ -155,7 +157,7 @@ settings.addModule((async function () {
|
||||
return {};
|
||||
})());
|
||||
|
||||
settings.addModule((async function () {
|
||||
addModule((async function () {
|
||||
const $forceFullSyncButton = $("#force-full-sync-button");
|
||||
const $fillSyncRowsButton = $("#fill-sync-rows-button");
|
||||
const $anonymizeButton = $("#anonymize-button");
|
||||
|
||||
@@ -1,106 +1,106 @@
|
||||
"use strict";
|
||||
|
||||
const sqlConsole = (function() {
|
||||
const $dialog = $("#sql-console-dialog");
|
||||
const $query = $('#sql-console-query');
|
||||
const $executeButton = $('#sql-console-execute');
|
||||
const $resultHead = $('#sql-console-results thead');
|
||||
const $resultBody = $('#sql-console-results tbody');
|
||||
import utils from '../utils.js';
|
||||
|
||||
let codeEditor;
|
||||
const $dialog = $("#sql-console-dialog");
|
||||
const $query = $('#sql-console-query');
|
||||
const $executeButton = $('#sql-console-execute');
|
||||
const $resultHead = $('#sql-console-results thead');
|
||||
const $resultBody = $('#sql-console-results tbody');
|
||||
|
||||
function showDialog() {
|
||||
glob.activeDialog = $dialog;
|
||||
let codeEditor;
|
||||
|
||||
$dialog.dialog({
|
||||
modal: true,
|
||||
width: $(window).width(),
|
||||
height: $(window).height(),
|
||||
open: function() {
|
||||
initEditor();
|
||||
}
|
||||
});
|
||||
}
|
||||
function showDialog() {
|
||||
glob.activeDialog = $dialog;
|
||||
|
||||
async function initEditor() {
|
||||
if (!codeEditor) {
|
||||
await utils.requireLibrary(utils.CODE_MIRROR);
|
||||
|
||||
CodeMirror.keyMap.default["Shift-Tab"] = "indentLess";
|
||||
CodeMirror.keyMap.default["Tab"] = "indentMore";
|
||||
|
||||
// removing Escape binding so that Escape will propagate to the dialog (which will close on escape)
|
||||
delete CodeMirror.keyMap.basic["Esc"];
|
||||
|
||||
CodeMirror.modeURL = 'libraries/codemirror/mode/%N/%N.js';
|
||||
|
||||
codeEditor = CodeMirror($query[0], {
|
||||
value: "",
|
||||
viewportMargin: Infinity,
|
||||
indentUnit: 4,
|
||||
highlightSelectionMatches: {showToken: /\w/, annotateScrollbar: false}
|
||||
});
|
||||
|
||||
codeEditor.setOption("mode", "text/x-sqlite");
|
||||
CodeMirror.autoLoadMode(codeEditor, "sql");
|
||||
$dialog.dialog({
|
||||
modal: true,
|
||||
width: $(window).width(),
|
||||
height: $(window).height(),
|
||||
open: function() {
|
||||
initEditor();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
codeEditor.focus();
|
||||
}
|
||||
async function initEditor() {
|
||||
if (!codeEditor) {
|
||||
await utils.requireLibrary(utils.CODE_MIRROR);
|
||||
|
||||
async function execute(e) {
|
||||
// stop from propagating upwards (dangerous especially with ctrl+enter executable javascript notes)
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
CodeMirror.keyMap.default["Shift-Tab"] = "indentLess";
|
||||
CodeMirror.keyMap.default["Tab"] = "indentMore";
|
||||
|
||||
const sqlQuery = codeEditor.getValue();
|
||||
// removing Escape binding so that Escape will propagate to the dialog (which will close on escape)
|
||||
delete CodeMirror.keyMap.basic["Esc"];
|
||||
|
||||
const result = await server.post("sql/execute", {
|
||||
query: sqlQuery
|
||||
CodeMirror.modeURL = 'libraries/codemirror/mode/%N/%N.js';
|
||||
|
||||
codeEditor = CodeMirror($query[0], {
|
||||
value: "",
|
||||
viewportMargin: Infinity,
|
||||
indentUnit: 4,
|
||||
highlightSelectionMatches: {showToken: /\w/, annotateScrollbar: false}
|
||||
});
|
||||
|
||||
if (!result.success) {
|
||||
utils.showError(result.error);
|
||||
return;
|
||||
}
|
||||
else {
|
||||
utils.showMessage("Query was executed successfully.");
|
||||
}
|
||||
|
||||
const rows = result.rows;
|
||||
|
||||
$resultHead.empty();
|
||||
$resultBody.empty();
|
||||
|
||||
if (rows.length > 0) {
|
||||
const result = rows[0];
|
||||
const rowEl = $("<tr>");
|
||||
|
||||
for (const key in result) {
|
||||
rowEl.append($("<th>").html(key));
|
||||
}
|
||||
|
||||
$resultHead.append(rowEl);
|
||||
}
|
||||
|
||||
for (const result of rows) {
|
||||
const rowEl = $("<tr>");
|
||||
|
||||
for (const key in result) {
|
||||
rowEl.append($("<td>").html(result[key]));
|
||||
}
|
||||
|
||||
$resultBody.append(rowEl);
|
||||
}
|
||||
codeEditor.setOption("mode", "text/x-sqlite");
|
||||
CodeMirror.autoLoadMode(codeEditor, "sql");
|
||||
}
|
||||
|
||||
$(document).bind('keydown', 'alt+o', showDialog);
|
||||
codeEditor.focus();
|
||||
}
|
||||
|
||||
$query.bind('keydown', 'ctrl+return', execute);
|
||||
async function execute(e) {
|
||||
// stop from propagating upwards (dangerous especially with ctrl+enter executable javascript notes)
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
|
||||
$executeButton.click(execute);
|
||||
const sqlQuery = codeEditor.getValue();
|
||||
|
||||
return {
|
||||
showDialog
|
||||
};
|
||||
})();
|
||||
const result = await server.post("sql/execute", {
|
||||
query: sqlQuery
|
||||
});
|
||||
|
||||
if (!result.success) {
|
||||
utils.showError(result.error);
|
||||
return;
|
||||
}
|
||||
else {
|
||||
utils.showMessage("Query was executed successfully.");
|
||||
}
|
||||
|
||||
const rows = result.rows;
|
||||
|
||||
$resultHead.empty();
|
||||
$resultBody.empty();
|
||||
|
||||
if (rows.length > 0) {
|
||||
const result = rows[0];
|
||||
const rowEl = $("<tr>");
|
||||
|
||||
for (const key in result) {
|
||||
rowEl.append($("<th>").html(key));
|
||||
}
|
||||
|
||||
$resultHead.append(rowEl);
|
||||
}
|
||||
|
||||
for (const result of rows) {
|
||||
const rowEl = $("<tr>");
|
||||
|
||||
for (const key in result) {
|
||||
rowEl.append($("<td>").html(result[key]));
|
||||
}
|
||||
|
||||
$resultBody.append(rowEl);
|
||||
}
|
||||
}
|
||||
|
||||
$(document).bind('keydown', 'alt+o', showDialog);
|
||||
|
||||
$query.bind('keydown', 'ctrl+return', execute);
|
||||
|
||||
$executeButton.click(execute);
|
||||
|
||||
export default {
|
||||
showDialog
|
||||
};
|
||||
Reference in New Issue
Block a user