diff --git a/bin/release.sh b/bin/release.sh index 5759da04d..0967443ea 100755 --- a/bin/release.sh +++ b/bin/release.sh @@ -24,9 +24,9 @@ jq '.version = "'$VERSION'"' package.json|sponge package.json git add package.json -echo 'module.exports = { buildDate:"'`date --iso-8601=seconds`'", buildRevision: "'`git log -1 --format="%H"`'" };' > services/build.js +echo 'module.exports = { buildDate:"'`date --iso-8601=seconds`'", buildRevision: "'`git log -1 --format="%H"`'" };' > src/services/build.js -git add services/build.js +git add src/services/build.js TAG=v$VERSION diff --git a/db/migrations/0087__add_type_mime_to_note_revision.sql b/db/migrations/0087__add_type_mime_to_note_revision.sql new file mode 100644 index 000000000..f4b6629e3 --- /dev/null +++ b/db/migrations/0087__add_type_mime_to_note_revision.sql @@ -0,0 +1,5 @@ +ALTER TABLE note_revisions ADD type TEXT DEFAULT '' NOT NULL; +ALTER TABLE note_revisions ADD mime TEXT DEFAULT '' NOT NULL; + +UPDATE note_revisions SET type = (SELECT type FROM notes WHERE notes.noteId = note_revisions.noteId); +UPDATE note_revisions SET mime = (SELECT mime FROM notes WHERE notes.noteId = note_revisions.noteId); \ No newline at end of file diff --git a/src/entities/note.js b/src/entities/note.js index c11833667..9f599c5f1 100644 --- a/src/entities/note.js +++ b/src/entities/note.js @@ -12,7 +12,8 @@ class Note extends Entity { constructor(row) { super(row); - if (this.isProtected) { + // check if there's noteId, otherwise this is a new entity which wasn't encrypted yet + if (this.isProtected && this.noteId) { protected_session.decryptNote(this); } @@ -21,6 +22,14 @@ class Note extends Entity { } } + setContent(content) { + this.content = content; + + if (this.isJson()) { + this.jsonContent = JSON.parse(this.content); + } + } + isJson() { return this.mime === "application/json"; } diff --git a/src/public/javascripts/dialogs/note_revisions.js b/src/public/javascripts/dialogs/note_revisions.js index 331a0dff3..42543565f 100644 --- a/src/public/javascripts/dialogs/note_revisions.js +++ b/src/public/javascripts/dialogs/note_revisions.js @@ -54,7 +54,13 @@ $list.on('change', () => { const revisionItem = revisionItems.find(r => r.noteRevisionId === optVal); $title.html(revisionItem.title); - $content.html(revisionItem.content); + + if (revisionItem.type === 'text') { + $content.html(revisionItem.content); + } + else if (revisionItem.type === 'code') { + $content.html($("
").text(revisionItem.content));
+ }
});
$(document).on('click', "a[action='note-revision']", event => {
diff --git a/src/public/javascripts/entities/branch.js b/src/public/javascripts/entities/branch.js
index e6f2d66a2..2a2268e5d 100644
--- a/src/public/javascripts/entities/branch.js
+++ b/src/public/javascripts/entities/branch.js
@@ -14,6 +14,10 @@ class Branch {
return await this.treeCache.getNote(this.noteId);
}
+ isTopLevel() {
+ return this.parentNoteId === 'root';
+ }
+
get toString() {
return `Branch(branchId=${this.branchId})`;
}
diff --git a/src/public/javascripts/entities/note_short.js b/src/public/javascripts/entities/note_short.js
index 8d10a7c9b..6a831111b 100644
--- a/src/public/javascripts/entities/note_short.js
+++ b/src/public/javascripts/entities/note_short.js
@@ -44,6 +44,14 @@ class NoteShort {
get toString() {
return `Note(noteId=${this.noteId}, title=${this.title})`;
}
+
+ get dto() {
+ const dto = Object.assign({}, this);
+ delete dto.treeCache;
+ delete dto.hideInAutocomplete;
+
+ return dto;
+ }
}
export default NoteShort;
\ No newline at end of file
diff --git a/src/public/javascripts/services/autocomplete.js b/src/public/javascripts/services/autocomplete.js
index 7054c14d7..7243a53f9 100644
--- a/src/public/javascripts/services/autocomplete.js
+++ b/src/public/javascripts/services/autocomplete.js
@@ -1,5 +1,6 @@
import treeCache from "./tree_cache.js";
import treeUtils from "./tree_utils.js";
+import protectedSessionHolder from './protected_session_holder.js';
async function getAutocompleteItems(parentNoteId, notePath, titlePath) {
if (!parentNoteId) {
@@ -21,9 +22,6 @@ async function getAutocompleteItems(parentNoteId, notePath, titlePath) {
titlePath = '';
}
- // https://github.com/zadam/trilium/issues/46
- // unfortunately not easy to implement because we don't have an easy access to note's isProtected property
-
const autocompleteItems = [];
for (const childNote of childNotes) {
@@ -34,10 +32,12 @@ async function getAutocompleteItems(parentNoteId, notePath, titlePath) {
const childNotePath = (notePath ? (notePath + '/') : '') + childNote.noteId;
const childTitlePath = (titlePath ? (titlePath + ' / ') : '') + await treeUtils.getNoteTitle(childNote.noteId, parentNoteId);
- autocompleteItems.push({
- value: childTitlePath + ' (' + childNotePath + ')',
- label: childTitlePath
- });
+ if (!childNote.isProtected || protectedSessionHolder.isProtectedSessionAvailable()) {
+ autocompleteItems.push({
+ value: childTitlePath + ' (' + childNotePath + ')',
+ label: childTitlePath
+ });
+ }
const childItems = await getAutocompleteItems(childNote.noteId, childNotePath, childTitlePath);
diff --git a/src/public/javascripts/services/note_detail.js b/src/public/javascripts/services/note_detail.js
index 33d45cf4b..cbf648f74 100644
--- a/src/public/javascripts/services/note_detail.js
+++ b/src/public/javascripts/services/note_detail.js
@@ -1,4 +1,5 @@
import treeService from './tree.js';
+import treeUtils from './tree_utils.js';
import noteTypeService from './note_type.js';
import protectedSessionService from './protected_session.js';
import protectedSessionHolder from './protected_session_holder.js';
@@ -24,6 +25,7 @@ const $noteDetailWrapper = $("#note-detail-wrapper");
const $noteIdDisplay = $("#note-id-display");
const $labelList = $("#label-list");
const $labelListInner = $("#label-list-inner");
+const $childrenOverview = $("#children-overview");
let currentNote = null;
@@ -73,52 +75,44 @@ function noteChanged() {
async function reload() {
// no saving here
- await loadNoteToEditor(getCurrentNoteId());
+ await loadNoteDetail(getCurrentNoteId());
}
async function switchToNote(noteId) {
if (getCurrentNoteId() !== noteId) {
await saveNoteIfChanged();
- await loadNoteToEditor(noteId);
+ await loadNoteDetail(noteId);
}
}
-async function saveNoteIfChanged() {
- if (!isNoteChanged) {
- return;
- }
-
+async function saveNote() {
const note = getCurrentNote();
- updateNoteFromInputs(note);
-
- await saveNoteToServer(note);
-
- if (note.isProtected) {
- protectedSessionHolder.touchProtectedSession();
- }
-}
-
-function updateNoteFromInputs(note) {
note.title = $noteTitle.val();
note.content = getComponent(note.type).getContent();
treeService.setNoteTitle(note.noteId, note.title);
-}
-async function saveNoteToServer(note) {
- const dto = Object.assign({}, note);
- delete dto.treeCache;
- delete dto.hideInAutocomplete;
-
- await server.put('notes/' + dto.noteId, dto);
+ await server.put('notes/' + note.noteId, note.dto);
isNoteChanged = false;
+ if (note.isProtected) {
+ protectedSessionHolder.touchProtectedSession();
+ }
+
infoService.showMessage("Saved!");
}
+async function saveNoteIfChanged() {
+ if (!isNoteChanged) {
+ return;
+ }
+
+ await saveNote();
+}
+
function setNoteBackgroundIfProtected(note) {
const isProtected = !!note.isProtected;
@@ -145,7 +139,7 @@ async function handleProtectedSession() {
protectedSessionService.ensureDialogIsClosed();
}
-async function loadNoteToEditor(noteId) {
+async function loadNoteDetail(noteId) {
currentNote = await loadNote(noteId);
if (isNewNoteCreated) {
@@ -183,6 +177,26 @@ async function loadNoteToEditor(noteId) {
$noteDetailWrapper.scrollTop(0);
await loadLabelList();
+
+ await showChildrenOverview();
+}
+
+async function showChildrenOverview() {
+ const note = getCurrentNote();
+
+ $childrenOverview.empty();
+
+ const notePath = treeService.getCurrentNotePath();
+
+ for (const childBranch of await note.getChildBranches()) {
+ const link = $('', {
+ href: 'javascript:',
+ text: await treeUtils.getNoteTitle(childBranch.noteId, childBranch.parentNoteId)
+ }).attr('action', 'note').attr('note-path', notePath + '/' + childBranch.noteId);
+
+ const childEl = $('').html(link);
+ $childrenOverview.append(childEl);
+ }
}
async function loadLabelList() {
@@ -245,8 +259,6 @@ setInterval(saveNoteIfChanged, 5000);
export default {
reload,
switchToNote,
- updateNoteFromInputs,
- saveNoteToServer,
setNoteBackgroundIfProtected,
loadNote,
getCurrentNote,
@@ -255,6 +267,7 @@ export default {
newNoteCreated,
focus,
loadLabelList,
+ saveNote,
saveNoteIfChanged,
noteChanged
};
\ No newline at end of file
diff --git a/src/public/javascripts/services/note_detail_code.js b/src/public/javascripts/services/note_detail_code.js
index 00b52194d..5a9baf2b3 100644
--- a/src/public/javascripts/services/note_detail_code.js
+++ b/src/public/javascripts/services/note_detail_code.js
@@ -16,6 +16,10 @@ async function show() {
CodeMirror.keyMap.default["Shift-Tab"] = "indentLess";
CodeMirror.keyMap.default["Tab"] = "indentMore";
+ // these conflict with backward/forward navigation shortcuts
+ delete CodeMirror.keyMap.default["Alt-Left"];
+ delete CodeMirror.keyMap.default["Alt-Right"];
+
CodeMirror.modeURL = 'libraries/codemirror/mode/%N/%N.js';
codeEditor = CodeMirror($noteDetailCode[0], {
diff --git a/src/public/javascripts/services/note_type.js b/src/public/javascripts/services/note_type.js
index 83c5dfe92..34922c2c9 100644
--- a/src/public/javascripts/services/note_type.js
+++ b/src/public/javascripts/services/note_type.js
@@ -1,5 +1,5 @@
import treeService from './tree.js';
-import noteDetail from './note_detail.js';
+import noteDetailService from './note_detail.js';
import server from './server.js';
import infoService from "./info.js";
@@ -84,13 +84,13 @@ function NoteTypeModel() {
};
async function save() {
- const note = noteDetail.getCurrentNote();
+ const note = noteDetailService.getCurrentNote();
await server.put('notes/' + note.noteId
+ '/type/' + encodeURIComponent(self.type())
+ '/mime/' + encodeURIComponent(self.mime()));
- await noteDetail.reload();
+ await noteDetailService.reload();
// for the note icon to be updated in the tree
await treeService.reload();
diff --git a/src/public/javascripts/services/protected_session.js b/src/public/javascripts/services/protected_session.js
index 36306bc2c..399742250 100644
--- a/src/public/javascripts/services/protected_session.js
+++ b/src/public/javascripts/services/protected_session.js
@@ -1,5 +1,5 @@
import treeService from './tree.js';
-import noteDetail from './note_detail.js';
+import noteDetailService from './note_detail.js';
import utils from './utils.js';
import server from './server.js';
import protectedSessionHolder from './protected_session_holder.js';
@@ -57,7 +57,7 @@ async function setupProtectedSession() {
$dialog.dialog("close");
- noteDetail.reload();
+ noteDetailService.reload();
treeService.reload();
if (protectedSessionDeferred !== null) {
@@ -90,33 +90,27 @@ async function enterProtectedSession(password) {
async function protectNoteAndSendToServer() {
await ensureProtectedSession(true, true);
- const note = noteDetail.getCurrentNote();
-
- noteDetail.updateNoteFromInputs(note);
-
+ const note = noteDetailService.getCurrentNote();
note.isProtected = true;
- await noteDetail.saveNoteToServer(note);
+ await noteDetailService.saveNote(note);
treeService.setProtected(note.noteId, note.isProtected);
- noteDetail.setNoteBackgroundIfProtected(note);
+ noteDetailService.setNoteBackgroundIfProtected(note);
}
async function unprotectNoteAndSendToServer() {
await ensureProtectedSession(true, true);
- const note = noteDetail.getCurrentNote();
-
- noteDetail.updateNoteFromInputs(note);
-
+ const note = noteDetailService.getCurrentNote();
note.isProtected = false;
- await noteDetail.saveNoteToServer(note);
+ await noteDetailService.saveNote(note);
treeService.setProtected(note.noteId, note.isProtected);
- noteDetail.setNoteBackgroundIfProtected(note);
+ noteDetailService.setNoteBackgroundIfProtected(note);
}
async function protectBranch(noteId, protect) {
@@ -127,7 +121,7 @@ async function protectBranch(noteId, protect) {
infoService.showMessage("Request to un/protect sub tree has finished successfully");
treeService.reload();
- noteDetail.reload();
+ noteDetailService.reload();
}
$passwordForm.submit(() => {
diff --git a/src/public/javascripts/services/tree.js b/src/public/javascripts/services/tree.js
index 751a8d3fb..9a47262ee 100644
--- a/src/public/javascripts/services/tree.js
+++ b/src/public/javascripts/services/tree.js
@@ -293,7 +293,7 @@ function initFancyTree(branch) {
keyboard: false, // we takover keyboard handling in the hotkeys plugin
extensions: ["hotkeys", "filter", "dnd", "clones"],
source: branch,
- scrollParent: $("#tree"),
+ scrollParent: $tree,
click: (event, data) => {
const targetType = data.targetType;
const node = data.node;
diff --git a/src/public/libraries/jquery.js b/src/public/libraries/jquery.js
index d2d8ca479..9b5206bcc 100644
--- a/src/public/libraries/jquery.js
+++ b/src/public/libraries/jquery.js
@@ -1,5 +1,5 @@
/*!
- * jQuery JavaScript Library v3.2.1
+ * jQuery JavaScript Library v3.3.1
* https://jquery.com/
*
* Includes Sizzle.js
@@ -9,7 +9,7 @@
* Released under the MIT license
* https://jquery.org/license
*
- * Date: 2017-03-20T18:59Z
+ * Date: 2018-01-20T17:24Z
*/
( function( global, factory ) {
@@ -71,16 +71,57 @@ var ObjectFunctionString = fnToString.call( Object );
var support = {};
+var isFunction = function isFunction( obj ) {
+ // Support: Chrome <=57, Firefox <=52
+ // In some browsers, typeof returns "function" for HTML