mirror of
https://github.com/zadam/trilium.git
synced 2025-03-01 14:22:32 +01:00
converted remaining type widgets
This commit is contained in:
parent
11a61325f9
commit
eacefeb08b
@ -21,12 +21,12 @@ const typeWidgetClasses = {
|
|||||||
'text': "./type_widgets/text.js",
|
'text': "./type_widgets/text.js",
|
||||||
'code': "./type_widgets/code.js",
|
'code': "./type_widgets/code.js",
|
||||||
'file': "./type_widgets/file.js",
|
'file': "./type_widgets/file.js",
|
||||||
'image': "./type_widgets/note_detail_image.js",
|
'image': "./type_widgets/image.js",
|
||||||
'search': "./type_widgets/note_detail_search.js",
|
'search': "./type_widgets/search.js",
|
||||||
'render': "./type_widgets/note_detail_render.js",
|
'render': "./type_widgets/render.js",
|
||||||
'relation-map': "./type_widgets/note_detail_relation_map.js",
|
'relation-map': "./type_widgets/relation_map.js",
|
||||||
'protected-session': "./type_widgets/note_detail_protected_session.js",
|
'protected-session': "./type_widgets/protected_session.js",
|
||||||
'book': "./type_widgets/note_detail_book.js"
|
'book': "./type_widgets/book.js"
|
||||||
};
|
};
|
||||||
|
|
||||||
export default class NoteDetailWidget extends TabAwareWidget {
|
export default class NoteDetailWidget extends TabAwareWidget {
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import linkService from "../../services/link.js";
|
import linkService from "../../services/link.js";
|
||||||
import treeCache from "../../services/tree_cache.js";
|
import treeCache from "../../services/tree_cache.js";
|
||||||
import noteContentRenderer from "../../services/note_content_renderer.js";
|
import noteContentRenderer from "../../services/note_content_renderer.js";
|
||||||
|
import TypeWidget from "./type_widget.js";
|
||||||
|
|
||||||
const MIN_ZOOM_LEVEL = 1;
|
const MIN_ZOOM_LEVEL = 1;
|
||||||
const MAX_ZOOM_LEVEL = 6;
|
const MAX_ZOOM_LEVEL = 6;
|
||||||
@ -32,18 +33,104 @@ const ZOOMS = {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
class NoteDetailBook {
|
const TPL = `
|
||||||
/**
|
<div class="note-detail-book note-detail-printable">
|
||||||
* @param {TabContext} ctx
|
<style>
|
||||||
*/
|
.note-detail-book {
|
||||||
constructor(ctx) {
|
height: 100%;
|
||||||
this.ctx = ctx;
|
padding: 10px;
|
||||||
this.$component = ctx.$tabContent.find('.note-detail-book');
|
}
|
||||||
this.$content = this.$component.find('.note-detail-book-content');
|
|
||||||
this.$zoomInButton = this.$component.find('.book-zoom-in-button');
|
.note-detail-book-content {
|
||||||
this.$zoomOutButton = this.$component.find('.book-zoom-out-button');
|
display: flex;
|
||||||
this.$expandChildrenButton = this.$component.find('.expand-children-button');
|
flex-wrap: wrap;
|
||||||
this.$help = this.$component.find('.note-detail-book-help');
|
overflow: auto;
|
||||||
|
height: 100%;
|
||||||
|
align-content: start;
|
||||||
|
}
|
||||||
|
|
||||||
|
.note-book-card {
|
||||||
|
border-radius: 10px;
|
||||||
|
background-color: var(--accented-background-color);
|
||||||
|
padding: 15px;
|
||||||
|
padding-bottom: 5px;
|
||||||
|
margin: 5px;
|
||||||
|
margin-left: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.note-book-card .note-book-card {
|
||||||
|
border: 1px solid var(--main-border-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.note-book-content {
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.note-book-card.type-image .note-book-content, .note-book-card.type-file .note-book-content, .note-book-card.type-protected-session .note-book-content {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.note-book-card.type-image .note-book-content img {
|
||||||
|
max-width: 100%;
|
||||||
|
max-height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.note-book-title {
|
||||||
|
flex-grow: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.note-book-content {
|
||||||
|
flex-grow: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.note-book-auto-message {
|
||||||
|
background-color: var(--accented-background-color);
|
||||||
|
text-align: center;
|
||||||
|
width: 100%;
|
||||||
|
border-radius: 10px;
|
||||||
|
padding: 5px;
|
||||||
|
margin-top: 5px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<div class="btn-group floating-button" style="right: 20px; top: 20px;">
|
||||||
|
<button type="button"
|
||||||
|
class="expand-children-button btn icon-button bx bx-move-vertical"
|
||||||
|
title="Expand all children"></button>
|
||||||
|
|
||||||
|
<button type="button"
|
||||||
|
class="book-zoom-in-button btn icon-button bx bx-zoom-in"
|
||||||
|
title="Zoom In"></button>
|
||||||
|
|
||||||
|
<button type="button"
|
||||||
|
class="book-zoom-out-button btn icon-button bx bx-zoom-out"
|
||||||
|
title="Zoom Out"></button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="note-detail-book-help alert alert-warning">
|
||||||
|
This note of type Book doesn't have any child notes so there's nothing to display. See <a href="https://github.com/zadam/trilium/wiki/Book-note">wiki</a> for details.
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="note-detail-book-content"></div>
|
||||||
|
</div>`;
|
||||||
|
|
||||||
|
export default class BookTypeWidget extends TypeWidget {
|
||||||
|
static getType() { return "book"; }
|
||||||
|
|
||||||
|
doRender() {
|
||||||
|
this.$widget = $(TPL);
|
||||||
|
this.$content = this.$widget.find('.note-detail-book-content');
|
||||||
|
this.$zoomInButton = this.$widget.find('.book-zoom-in-button');
|
||||||
|
this.$zoomOutButton = this.$widget.find('.book-zoom-out-button');
|
||||||
|
this.$expandChildrenButton = this.$widget.find('.expand-children-button');
|
||||||
|
this.$help = this.$widget.find('.note-detail-book-help');
|
||||||
|
|
||||||
this.$zoomInButton.on('click', () => this.setZoom(this.zoomLevel - 1));
|
this.$zoomInButton.on('click', () => this.setZoom(this.zoomLevel - 1));
|
||||||
this.$zoomOutButton.on('click', () => this.setZoom(this.zoomLevel + 1));
|
this.$zoomOutButton.on('click', () => this.setZoom(this.zoomLevel + 1));
|
||||||
@ -78,6 +165,8 @@ class NoteDetailBook {
|
|||||||
|
|
||||||
$card.find('.note-book-children-content').empty();
|
$card.find('.note-book-children-content').empty();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
return this.$widget;
|
||||||
}
|
}
|
||||||
|
|
||||||
async expandCard($card) {
|
async expandCard($card) {
|
||||||
@ -104,13 +193,13 @@ class NoteDetailBook {
|
|||||||
this.$content.find('.note-book-content').css("max-height", ZOOMS[zoomLevel].height);
|
this.$content.find('.note-book-content').css("max-height", ZOOMS[zoomLevel].height);
|
||||||
}
|
}
|
||||||
|
|
||||||
async render() {
|
async doRefresh() {
|
||||||
this.$content.empty();
|
this.$content.empty();
|
||||||
this.$help.hide();
|
this.$help.hide();
|
||||||
|
|
||||||
if (this.isAutoBook()) {
|
if (this.isAutoBook()) {
|
||||||
const $addTextLink = $('<a href="javascript:">here</a>').on('click', () => {
|
const $addTextLink = $('<a href="javascript:">here</a>').on('click', () => {
|
||||||
this.ctx.renderComponent(true);
|
// FIXME
|
||||||
});
|
});
|
||||||
|
|
||||||
this.$content.append($('<div class="note-book-auto-message"></div>')
|
this.$content.append($('<div class="note-book-auto-message"></div>')
|
||||||
@ -119,17 +208,17 @@ class NoteDetailBook {
|
|||||||
.append(' if you want to add some text.'))
|
.append(' if you want to add some text.'))
|
||||||
}
|
}
|
||||||
|
|
||||||
const zoomLevel = parseInt(await this.ctx.note.getLabelValue('bookZoomLevel')) || this.getDefaultZoomLevel();
|
const zoomLevel = parseInt(await this.tabContext.note.getLabelValue('bookZoomLevel')) || this.getDefaultZoomLevel();
|
||||||
this.setZoom(zoomLevel);
|
this.setZoom(zoomLevel);
|
||||||
|
|
||||||
await this.renderIntoElement(this.ctx.note, this.$content);
|
await this.renderIntoElement(this.tabContext.note, this.$content);
|
||||||
}
|
}
|
||||||
|
|
||||||
async renderIntoElement(note, $container) {
|
async renderIntoElement(note, $container) {
|
||||||
const childNotes = await note.getChildNotes();
|
const childNotes = await note.getChildNotes();
|
||||||
|
|
||||||
for (const childNote of childNotes) {
|
for (const childNote of childNotes) {
|
||||||
const childNotePath = this.ctx.notePath + '/' + childNote.noteId;
|
const childNotePath = this.tabContext.notePath + '/' + childNote.noteId;
|
||||||
|
|
||||||
const {type, renderedContent} = await noteContentRenderer.getRenderedContent(childNote);
|
const {type, renderedContent} = await noteContentRenderer.getRenderedContent(childNote);
|
||||||
|
|
||||||
@ -164,12 +253,12 @@ class NoteDetailBook {
|
|||||||
|
|
||||||
/** @return {boolean} true if this is "auto book" activated (empty text note) and not explicit book note */
|
/** @return {boolean} true if this is "auto book" activated (empty text note) and not explicit book note */
|
||||||
isAutoBook() {
|
isAutoBook() {
|
||||||
return this.ctx.note.type !== 'book';
|
return this.tabContext.note.type !== 'book';
|
||||||
}
|
}
|
||||||
|
|
||||||
getDefaultZoomLevel() {
|
getDefaultZoomLevel() {
|
||||||
if (this.isAutoBook()) {
|
if (this.isAutoBook()) {
|
||||||
const w = this.$component.width();
|
const w = this.$widget.width();
|
||||||
|
|
||||||
if (w <= 600) {
|
if (w <= 600) {
|
||||||
return 1;
|
return 1;
|
||||||
@ -192,7 +281,7 @@ class NoteDetailBook {
|
|||||||
}
|
}
|
||||||
|
|
||||||
show() {
|
show() {
|
||||||
this.$component.show();
|
this.$widget.show();
|
||||||
}
|
}
|
||||||
|
|
||||||
focus() {}
|
focus() {}
|
||||||
@ -204,8 +293,6 @@ class NoteDetailBook {
|
|||||||
}
|
}
|
||||||
|
|
||||||
scrollToTop() {
|
scrollToTop() {
|
||||||
this.$component.scrollTop(0);
|
this.$widget.scrollTop(0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default NoteDetailBook;
|
|
@ -22,7 +22,7 @@ const TPL = `
|
|||||||
<div class="note-detail-code-editor"></div>
|
<div class="note-detail-code-editor"></div>
|
||||||
</div>`;
|
</div>`;
|
||||||
|
|
||||||
class CodeTypeWidget extends TypeWidget {
|
export default class CodeTypeWidget extends TypeWidget {
|
||||||
static getType() { return "code"; }
|
static getType() { return "code"; }
|
||||||
|
|
||||||
doRender() {
|
doRender() {
|
||||||
@ -139,5 +139,3 @@ class CodeTypeWidget extends TypeWidget {
|
|||||||
this.$widget.scrollTop(0);
|
this.$widget.scrollTop(0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default CodeTypeWidget;
|
|
@ -12,7 +12,7 @@ const TPL = `
|
|||||||
</div>
|
</div>
|
||||||
</div>`;
|
</div>`;
|
||||||
|
|
||||||
class NoteDetailEmpty extends TypeWidget {
|
export default class EmptyTypeWidget extends TypeWidget {
|
||||||
static getType() { return "empty"; }
|
static getType() { return "empty"; }
|
||||||
|
|
||||||
doRender() {
|
doRender() {
|
||||||
@ -57,5 +57,3 @@ class NoteDetailEmpty extends TypeWidget {
|
|||||||
|
|
||||||
scrollToTop() {}
|
scrollToTop() {}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default NoteDetailEmpty;
|
|
@ -55,7 +55,7 @@ const TPL = `
|
|||||||
<input type="file" class="file-upload-new-revision-input" style="display: none">
|
<input type="file" class="file-upload-new-revision-input" style="display: none">
|
||||||
</div>`;
|
</div>`;
|
||||||
|
|
||||||
class FileTypeWidget extends TypeWidget {
|
export default class FileTypeWidget extends TypeWidget {
|
||||||
static getType() { return "file"; }
|
static getType() { return "file"; }
|
||||||
|
|
||||||
doRender() {
|
doRender() {
|
||||||
@ -156,5 +156,3 @@ class FileTypeWidget extends TypeWidget {
|
|||||||
|
|
||||||
scrollToTop() {}
|
scrollToTop() {}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default FileTypeWidget;
|
|
168
src/public/javascripts/widgets/type_widgets/image.js
Normal file
168
src/public/javascripts/widgets/type_widgets/image.js
Normal file
@ -0,0 +1,168 @@
|
|||||||
|
import utils from "../../services/utils.js";
|
||||||
|
import toastService from "../../services/toast.js";
|
||||||
|
import server from "../../services/server.js";
|
||||||
|
import noteDetailService from "../../services/note_detail.js";
|
||||||
|
import TypeWidget from "./type_widget.js";
|
||||||
|
|
||||||
|
const TPL = `
|
||||||
|
<div class="note-detail-image note-detail-printable">
|
||||||
|
<style>
|
||||||
|
.note-detail-image {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.note-detail-image-view {
|
||||||
|
max-width: 100%;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<div style="display: flex; justify-content: space-evenly; margin: 10px;">
|
||||||
|
<button class="image-download btn btn-sm btn-primary" type="button">Download</button>
|
||||||
|
|
||||||
|
<button class="image-copy-to-clipboard btn btn-sm btn-primary" type="button">Copy to clipboard</button>
|
||||||
|
|
||||||
|
<button class="image-upload-new-revision btn btn-sm btn-primary" type="button">Upload new revision</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="note-detail-image-wrapper">
|
||||||
|
<img class="note-detail-image-view" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div style="display: flex; justify-content: space-evenly; margin: 10px;">
|
||||||
|
<span>
|
||||||
|
<strong>Original file name:</strong>
|
||||||
|
<span class="image-filename"></span>
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<span>
|
||||||
|
<strong>File type:</strong>
|
||||||
|
<span class="image-filetype"></span>
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<span>
|
||||||
|
<strong>File size:</strong>
|
||||||
|
<span class="image-filesize"></span>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<input type="file" class="image-upload-new-revision-input" style="display: none">
|
||||||
|
</div>`;
|
||||||
|
|
||||||
|
class NoteDetailImage extends TypeWidget {
|
||||||
|
static getType() { return "image"; }
|
||||||
|
|
||||||
|
doRender() {
|
||||||
|
this.$widget = $(TPL);
|
||||||
|
this.$imageWrapper = this.$widget.find('.note-detail-image-wrapper');
|
||||||
|
this.$imageView = this.$widget.find('.note-detail-image-view');
|
||||||
|
this.$copyToClipboardButton = this.$widget.find(".image-copy-to-clipboard");
|
||||||
|
this.$uploadNewRevisionButton = this.$widget.find(".image-upload-new-revision");
|
||||||
|
this.$uploadNewRevisionInput = this.$widget.find(".image-upload-new-revision-input");
|
||||||
|
this.$fileName = this.$widget.find(".image-filename");
|
||||||
|
this.$fileType = this.$widget.find(".image-filetype");
|
||||||
|
this.$fileSize = this.$widget.find(".image-filesize");
|
||||||
|
|
||||||
|
this.$imageDownloadButton = this.$widget.find(".image-download");
|
||||||
|
this.$imageDownloadButton.on('click', () => utils.download(this.getFileUrl()));
|
||||||
|
|
||||||
|
this.$copyToClipboardButton.on('click',() => {
|
||||||
|
this.$imageWrapper.attr('contenteditable','true');
|
||||||
|
|
||||||
|
try {
|
||||||
|
this.selectImage(this.$imageWrapper.get(0));
|
||||||
|
|
||||||
|
const success = document.execCommand('copy');
|
||||||
|
|
||||||
|
if (success) {
|
||||||
|
toastService.showMessage("Image copied to the clipboard");
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
toastService.showAndLogError("Could not copy the image to clipboard.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
window.getSelection().removeAllRanges();
|
||||||
|
this.$imageWrapper.removeAttr('contenteditable');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
this.$uploadNewRevisionButton.on("click", () => {
|
||||||
|
this.$uploadNewRevisionInput.trigger("click");
|
||||||
|
});
|
||||||
|
|
||||||
|
this.$uploadNewRevisionInput.on('change', async () => {
|
||||||
|
const fileToUpload = this.$uploadNewRevisionInput[0].files[0]; // copy to allow reset below
|
||||||
|
this.$uploadNewRevisionInput.val('');
|
||||||
|
|
||||||
|
const formData = new FormData();
|
||||||
|
formData.append('upload', fileToUpload);
|
||||||
|
|
||||||
|
const result = await $.ajax({
|
||||||
|
url: baseApiUrl + 'images/' + this.tabContext.note.noteId,
|
||||||
|
headers: server.getHeaders(),
|
||||||
|
data: formData,
|
||||||
|
type: 'PUT',
|
||||||
|
timeout: 60 * 60 * 1000,
|
||||||
|
contentType: false, // NEEDED, DON'T REMOVE THIS
|
||||||
|
processData: false, // NEEDED, DON'T REMOVE THIS
|
||||||
|
});
|
||||||
|
|
||||||
|
if (result.uploaded) {
|
||||||
|
toastService.showMessage("New image revision has been uploaded.");
|
||||||
|
|
||||||
|
await utils.clearBrowserCache();
|
||||||
|
|
||||||
|
await noteDetailService.reload();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
toastService.showError("Upload of a new image revision failed: " + result.message);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return this.$widget;
|
||||||
|
}
|
||||||
|
|
||||||
|
async doRefresh() {
|
||||||
|
const note = this.tabContext.note;
|
||||||
|
const attributes = await server.get('notes/' + note.noteId + '/attributes');
|
||||||
|
const attributeMap = utils.toObject(attributes, l => [l.name, l.value]);
|
||||||
|
|
||||||
|
this.$widget.show();
|
||||||
|
|
||||||
|
this.$fileName.text(attributeMap.originalFileName || "?");
|
||||||
|
this.$fileSize.text(note.contentLength + " bytes");
|
||||||
|
this.$fileType.text(note.mime);
|
||||||
|
|
||||||
|
const imageHash = note.utcDateModified.replace(" ", "_");
|
||||||
|
|
||||||
|
this.$imageView.prop("src", `api/images/${note.noteId}/${note.title}?${imageHash}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
selectImage(element) {
|
||||||
|
const selection = window.getSelection();
|
||||||
|
const range = document.createRange();
|
||||||
|
range.selectNodeContents(element);
|
||||||
|
selection.removeAllRanges();
|
||||||
|
selection.addRange(range);
|
||||||
|
}
|
||||||
|
|
||||||
|
getFileUrl() {
|
||||||
|
return utils.getUrlForDownload(`api/notes/${this.tabContext.note.noteId}/download`);
|
||||||
|
}
|
||||||
|
|
||||||
|
show() {}
|
||||||
|
|
||||||
|
getContent() {}
|
||||||
|
|
||||||
|
focus() {}
|
||||||
|
|
||||||
|
onNoteChange() {}
|
||||||
|
|
||||||
|
cleanup() {}
|
||||||
|
|
||||||
|
scrollToTop() {
|
||||||
|
this.$widget.scrollTop(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default NoteDetailImage
|
@ -1,122 +0,0 @@
|
|||||||
import utils from "../../services/utils.js";
|
|
||||||
import toastService from "../../services/toast.js";
|
|
||||||
import server from "../../services/server.js";
|
|
||||||
import noteDetailService from "../../services/note_detail.js";
|
|
||||||
|
|
||||||
class NoteDetailImage {
|
|
||||||
/**
|
|
||||||
* @param {TabContext} ctx
|
|
||||||
*/
|
|
||||||
constructor(ctx) {
|
|
||||||
this.ctx = ctx;
|
|
||||||
this.$component = ctx.$tabContent.find('.note-detail-image');
|
|
||||||
this.$imageWrapper = ctx.$tabContent.find('.note-detail-image-wrapper');
|
|
||||||
this.$imageView = ctx.$tabContent.find('.note-detail-image-view');
|
|
||||||
this.$copyToClipboardButton = ctx.$tabContent.find(".image-copy-to-clipboard");
|
|
||||||
this.$uploadNewRevisionButton = ctx.$tabContent.find(".image-upload-new-revision");
|
|
||||||
this.$uploadNewRevisionInput = ctx.$tabContent.find(".image-upload-new-revision-input");
|
|
||||||
this.$fileName = ctx.$tabContent.find(".image-filename");
|
|
||||||
this.$fileType = ctx.$tabContent.find(".image-filetype");
|
|
||||||
this.$fileSize = ctx.$tabContent.find(".image-filesize");
|
|
||||||
|
|
||||||
this.$imageDownloadButton = ctx.$tabContent.find(".image-download");
|
|
||||||
this.$imageDownloadButton.on('click', () => utils.download(this.getFileUrl()));
|
|
||||||
|
|
||||||
this.$copyToClipboardButton.on('click',() => {
|
|
||||||
this.$imageWrapper.attr('contenteditable','true');
|
|
||||||
|
|
||||||
try {
|
|
||||||
this.selectImage(this.$imageWrapper.get(0));
|
|
||||||
|
|
||||||
const success = document.execCommand('copy');
|
|
||||||
|
|
||||||
if (success) {
|
|
||||||
toastService.showMessage("Image copied to the clipboard");
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
toastService.showAndLogError("Could not copy the image to clipboard.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
finally {
|
|
||||||
window.getSelection().removeAllRanges();
|
|
||||||
this.$imageWrapper.removeAttr('contenteditable');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
this.$uploadNewRevisionButton.on("click", () => {
|
|
||||||
this.$uploadNewRevisionInput.trigger("click");
|
|
||||||
});
|
|
||||||
|
|
||||||
this.$uploadNewRevisionInput.on('change', async () => {
|
|
||||||
const fileToUpload = this.$uploadNewRevisionInput[0].files[0]; // copy to allow reset below
|
|
||||||
this.$uploadNewRevisionInput.val('');
|
|
||||||
|
|
||||||
const formData = new FormData();
|
|
||||||
formData.append('upload', fileToUpload);
|
|
||||||
|
|
||||||
const result = await $.ajax({
|
|
||||||
url: baseApiUrl + 'images/' + this.ctx.note.noteId,
|
|
||||||
headers: server.getHeaders(),
|
|
||||||
data: formData,
|
|
||||||
type: 'PUT',
|
|
||||||
timeout: 60 * 60 * 1000,
|
|
||||||
contentType: false, // NEEDED, DON'T REMOVE THIS
|
|
||||||
processData: false, // NEEDED, DON'T REMOVE THIS
|
|
||||||
});
|
|
||||||
|
|
||||||
if (result.uploaded) {
|
|
||||||
toastService.showMessage("New image revision has been uploaded.");
|
|
||||||
|
|
||||||
await utils.clearBrowserCache();
|
|
||||||
|
|
||||||
await noteDetailService.reload();
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
toastService.showError("Upload of a new image revision failed: " + result.message);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
async render() {
|
|
||||||
const attributes = await server.get('notes/' + this.ctx.note.noteId + '/attributes');
|
|
||||||
const attributeMap = utils.toObject(attributes, l => [l.name, l.value]);
|
|
||||||
|
|
||||||
this.$component.show();
|
|
||||||
|
|
||||||
this.$fileName.text(attributeMap.originalFileName || "?");
|
|
||||||
this.$fileSize.text(this.ctx.note.contentLength + " bytes");
|
|
||||||
this.$fileType.text(this.ctx.note.mime);
|
|
||||||
|
|
||||||
const imageHash = this.ctx.note.utcDateModified.replace(" ", "_");
|
|
||||||
|
|
||||||
this.$imageView.prop("src", `api/images/${this.ctx.note.noteId}/${this.ctx.note.title}?${imageHash}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
selectImage(element) {
|
|
||||||
const selection = window.getSelection();
|
|
||||||
const range = document.createRange();
|
|
||||||
range.selectNodeContents(element);
|
|
||||||
selection.removeAllRanges();
|
|
||||||
selection.addRange(range);
|
|
||||||
}
|
|
||||||
|
|
||||||
getFileUrl() {
|
|
||||||
return utils.getUrlForDownload(`api/notes/${this.ctx.note.noteId}/download`);
|
|
||||||
}
|
|
||||||
|
|
||||||
show() {}
|
|
||||||
|
|
||||||
getContent() {}
|
|
||||||
|
|
||||||
focus() {}
|
|
||||||
|
|
||||||
onNoteChange() {}
|
|
||||||
|
|
||||||
cleanup() {}
|
|
||||||
|
|
||||||
scrollToTop() {
|
|
||||||
this.$component.scrollTop(0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default NoteDetailImage
|
|
@ -1,42 +0,0 @@
|
|||||||
import protectedSessionService from '../../services/protected_session.js';
|
|
||||||
|
|
||||||
class NoteDetailProtectedSession {
|
|
||||||
/**
|
|
||||||
* @param {TabContext} ctx
|
|
||||||
*/
|
|
||||||
constructor(ctx) {
|
|
||||||
this.ctx = ctx;
|
|
||||||
this.$component = ctx.$tabContent.find(".protected-session-password-component");
|
|
||||||
this.$passwordForm = ctx.$tabContent.find(".protected-session-password-form");
|
|
||||||
this.$passwordInput = ctx.$tabContent.find(".protected-session-password");
|
|
||||||
|
|
||||||
this.$passwordForm.on('submit', () => {
|
|
||||||
const password = this.$passwordInput.val();
|
|
||||||
this.$passwordInput.val("");
|
|
||||||
|
|
||||||
protectedSessionService.setupProtectedSession(password);
|
|
||||||
|
|
||||||
return false;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
|
||||||
this.$component.show();
|
|
||||||
}
|
|
||||||
|
|
||||||
show() {}
|
|
||||||
|
|
||||||
getContent() {}
|
|
||||||
|
|
||||||
focus() {}
|
|
||||||
|
|
||||||
onNoteChange() {}
|
|
||||||
|
|
||||||
cleanup() {}
|
|
||||||
|
|
||||||
scrollToTop() {
|
|
||||||
this.$component.scrollTop(0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default NoteDetailProtectedSession;
|
|
@ -1,45 +0,0 @@
|
|||||||
import renderService from "../../services/render.js";
|
|
||||||
|
|
||||||
class NoteDetailRender {
|
|
||||||
/**
|
|
||||||
* @param {TabContext} ctx
|
|
||||||
*/
|
|
||||||
constructor(ctx) {
|
|
||||||
this.ctx = ctx;
|
|
||||||
this.$component = ctx.$tabContent.find('.note-detail-render');
|
|
||||||
this.$noteDetailRenderHelp = ctx.$tabContent.find('.note-detail-render-help');
|
|
||||||
this.$noteDetailRenderContent = ctx.$tabContent.find('.note-detail-render-content');
|
|
||||||
this.$renderButton = ctx.$tabContent.find('.render-button');
|
|
||||||
|
|
||||||
this.$renderButton.on('click', () => this.render()); // long form!
|
|
||||||
}
|
|
||||||
|
|
||||||
async render() {
|
|
||||||
this.$component.show();
|
|
||||||
this.$noteDetailRenderHelp.hide();
|
|
||||||
|
|
||||||
const renderNotesFound = await renderService.render(this.ctx.note, this.$noteDetailRenderContent, this.ctx);
|
|
||||||
|
|
||||||
if (!renderNotesFound) {
|
|
||||||
this.$noteDetailRenderHelp.show();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
getContent() {}
|
|
||||||
|
|
||||||
show() {}
|
|
||||||
|
|
||||||
focus() {}
|
|
||||||
|
|
||||||
onNoteChange() {}
|
|
||||||
|
|
||||||
cleanup() {
|
|
||||||
this.$noteDetailRenderContent.empty();
|
|
||||||
}
|
|
||||||
|
|
||||||
scrollToTop() {
|
|
||||||
this.$component.scrollTop(0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default NoteDetailRender;
|
|
@ -1,58 +0,0 @@
|
|||||||
import noteDetailService from "../../services/note_detail.js";
|
|
||||||
import searchNotesService from "../../services/search_notes.js";
|
|
||||||
|
|
||||||
class NoteDetailSearch {
|
|
||||||
/**
|
|
||||||
* @param {TabContext} ctx
|
|
||||||
*/
|
|
||||||
constructor(ctx) {
|
|
||||||
this.ctx = ctx;
|
|
||||||
this.$searchString = ctx.$tabContent.find(".search-string");
|
|
||||||
this.$component = ctx.$tabContent.find('.note-detail-search');
|
|
||||||
this.$help = ctx.$tabContent.find(".note-detail-search-help");
|
|
||||||
this.$refreshButton = ctx.$tabContent.find('.note-detail-search-refresh-results-button');
|
|
||||||
|
|
||||||
this.$refreshButton.on('click', async () => {
|
|
||||||
// FIXME
|
|
||||||
await noteDetailService.saveNotesIfChanged();
|
|
||||||
|
|
||||||
await searchNotesService.refreshSearch();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
|
||||||
this.$help.html(searchNotesService.getHelpText());
|
|
||||||
|
|
||||||
this.$component.show();
|
|
||||||
|
|
||||||
try {
|
|
||||||
const json = JSON.parse(this.ctx.note.content);
|
|
||||||
|
|
||||||
this.$searchString.val(json.searchString);
|
|
||||||
}
|
|
||||||
catch (e) {
|
|
||||||
console.log(e);
|
|
||||||
this.$searchString.val('');
|
|
||||||
}
|
|
||||||
|
|
||||||
this.$searchString.on('input', () => this.ctx.noteChanged());
|
|
||||||
}
|
|
||||||
|
|
||||||
getContent() {
|
|
||||||
return JSON.stringify({
|
|
||||||
searchString: this.$searchString.val()
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
focus() {}
|
|
||||||
|
|
||||||
show() {}
|
|
||||||
|
|
||||||
onNoteChange() {}
|
|
||||||
|
|
||||||
cleanup() {}
|
|
||||||
|
|
||||||
scrollToTop() {}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default NoteDetailSearch;
|
|
@ -0,0 +1,60 @@
|
|||||||
|
import protectedSessionService from '../../services/protected_session.js';
|
||||||
|
import TypeWidget from "./type_widget.js";
|
||||||
|
|
||||||
|
const TPL = `
|
||||||
|
<div class="protected-session-password-component note-detail-printable">
|
||||||
|
<style>
|
||||||
|
.protected-session-password-component {
|
||||||
|
width: 300px;
|
||||||
|
margin: 30px auto auto;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<form class="protected-session-password-form">
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="protected-session-password-in-detail">Showing protected note requires entering your password:</label>
|
||||||
|
<input class="protected-session-password-in-detail form-control protected-session-password" type="password">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button class="btn btn-primary">Start protected session <kbd>enter</kbd></button>
|
||||||
|
</form>
|
||||||
|
</div>`;
|
||||||
|
|
||||||
|
export default class ProtectedSessionTypeWidget extends TypeWidget {
|
||||||
|
static getType() { return "protected-session"; }
|
||||||
|
|
||||||
|
doRender() {
|
||||||
|
this.$widget = $(TPL);
|
||||||
|
this.$passwordForm = this.$widget.find(".protected-session-password-form");
|
||||||
|
this.$passwordInput = this.$widget.find(".protected-session-password");
|
||||||
|
|
||||||
|
this.$passwordForm.on('submit', () => {
|
||||||
|
const password = this.$passwordInput.val();
|
||||||
|
this.$passwordInput.val("");
|
||||||
|
|
||||||
|
protectedSessionService.setupProtectedSession(password);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
|
||||||
|
return this.$widget;
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
this.$widget.show();
|
||||||
|
}
|
||||||
|
|
||||||
|
show() {}
|
||||||
|
|
||||||
|
getContent() {}
|
||||||
|
|
||||||
|
focus() {}
|
||||||
|
|
||||||
|
onNoteChange() {}
|
||||||
|
|
||||||
|
cleanup() {}
|
||||||
|
|
||||||
|
scrollToTop() {
|
||||||
|
this.$widget.scrollTop(0);
|
||||||
|
}
|
||||||
|
}
|
@ -6,6 +6,7 @@ import treeService from "../../services/tree.js";
|
|||||||
import contextMenuWidget from "../../services/context_menu.js";
|
import contextMenuWidget from "../../services/context_menu.js";
|
||||||
import toastService from "../../services/toast.js";
|
import toastService from "../../services/toast.js";
|
||||||
import attributeAutocompleteService from "../../services/attribute_autocomplete.js";
|
import attributeAutocompleteService from "../../services/attribute_autocomplete.js";
|
||||||
|
import TypeWidget from "./type_widget.js";
|
||||||
|
|
||||||
const uniDirectionalOverlays = [
|
const uniDirectionalOverlays = [
|
||||||
[ "Arrow", {
|
[ "Arrow", {
|
||||||
@ -61,20 +62,47 @@ const linkOverlays = [
|
|||||||
} ]
|
} ]
|
||||||
];
|
];
|
||||||
|
|
||||||
|
const TPL = `
|
||||||
|
<div class="note-detail-relation-map note-detail-printable">
|
||||||
|
<button class="relation-map-create-child-note btn btn-sm floating-button" type="button"
|
||||||
|
title="Create new child note and add it into this relation map">
|
||||||
|
<span class="bx bx-folder-plus"></span>
|
||||||
|
|
||||||
|
Create child note
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<button type="button"
|
||||||
|
class="relation-map-reset-pan-zoom btn icon-button floating-button bx bx-crop"
|
||||||
|
title="Reset pan & zoom to initial coordinates and magnification"
|
||||||
|
style="right: 70px;"></button>
|
||||||
|
|
||||||
|
<div class="btn-group floating-button" style="right: 10px;">
|
||||||
|
<button type="button"
|
||||||
|
class="relation-map-zoom-in btn icon-button bx bx-zoom-in"
|
||||||
|
title="Zoom In"></button>
|
||||||
|
|
||||||
|
<button type="button"
|
||||||
|
class="relation-map-zoom-out btn icon-button bx bx-zoom-out"
|
||||||
|
title="Zoom Out"></button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="relation-map-wrapper">
|
||||||
|
<div class="relation-map-container"></div>
|
||||||
|
</div>
|
||||||
|
</div>`;
|
||||||
|
|
||||||
let containerCounter = 1;
|
let containerCounter = 1;
|
||||||
|
|
||||||
class NoteDetailRelationMap {
|
export default class RelationMapTypeWidget extends TypeWidget {
|
||||||
/**
|
static getType() { return "relation-map"; }
|
||||||
* @param {TabContext} ctx
|
|
||||||
*/
|
doRender() {
|
||||||
constructor(ctx) {
|
this.$widget = $(TPL);
|
||||||
this.ctx = ctx;
|
this.$relationMapContainer = this.$widget.find(".relation-map-container");
|
||||||
this.$component = ctx.$tabContent.find(".note-detail-relation-map");
|
this.$createChildNote = this.$widget.find(".relation-map-create-child-note");
|
||||||
this.$relationMapContainer = ctx.$tabContent.find(".relation-map-container");
|
this.$zoomInButton = this.$widget.find(".relation-map-zoom-in");
|
||||||
this.$createChildNote = ctx.$tabContent.find(".relation-map-create-child-note");
|
this.$zoomOutButton = this.$widget.find(".relation-map-zoom-out");
|
||||||
this.$zoomInButton = ctx.$tabContent.find(".relation-map-zoom-in");
|
this.$resetPanZoomButton = this.$widget.find(".relation-map-reset-pan-zoom");
|
||||||
this.$zoomOutButton = ctx.$tabContent.find(".relation-map-zoom-out");
|
|
||||||
this.$resetPanZoomButton = ctx.$tabContent.find(".relation-map-reset-pan-zoom");
|
|
||||||
|
|
||||||
this.mapData = null;
|
this.mapData = null;
|
||||||
this.jsPlumbInstance = null;
|
this.jsPlumbInstance = null;
|
||||||
@ -82,7 +110,7 @@ class NoteDetailRelationMap {
|
|||||||
this.relations = null;
|
this.relations = null;
|
||||||
this.pzInstance = null;
|
this.pzInstance = null;
|
||||||
|
|
||||||
this.$relationMapWrapper = ctx.$tabContent.find('.relation-map-wrapper');
|
this.$relationMapWrapper = this.$widget.find('.relation-map-wrapper');
|
||||||
this.$relationMapWrapper.on('click', event => {
|
this.$relationMapWrapper.on('click', event => {
|
||||||
if (this.clipboard) {
|
if (this.clipboard) {
|
||||||
let {x, y} = this.getMousePosition(event);
|
let {x, y} = this.getMousePosition(event);
|
||||||
@ -129,7 +157,7 @@ class NoteDetailRelationMap {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const {note} = await server.post(`notes/${this.ctx.note.noteId}/children?target=into`, {
|
const {note} = await server.post(`notes/${this.tabContext.note.noteId}/children?target=into`, {
|
||||||
title,
|
title,
|
||||||
content: '',
|
content: '',
|
||||||
type: 'text'
|
type: 'text'
|
||||||
@ -150,8 +178,10 @@ class NoteDetailRelationMap {
|
|||||||
this.pzInstance.moveTo(0, 0);
|
this.pzInstance.moveTo(0, 0);
|
||||||
});
|
});
|
||||||
|
|
||||||
this.$component.on("drop", ev => this.dropNoteOntoRelationMapHandler(ev));
|
this.$widget.on("drop", ev => this.dropNoteOntoRelationMapHandler(ev));
|
||||||
this.$component.on("dragover", ev => ev.preventDefault());
|
this.$widget.on("dragover", ev => ev.preventDefault());
|
||||||
|
|
||||||
|
return this.$widget;
|
||||||
}
|
}
|
||||||
|
|
||||||
async tabContextMenuHandler(event, cmd) {
|
async tabContextMenuHandler(event, cmd) {
|
||||||
@ -217,9 +247,9 @@ class NoteDetailRelationMap {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if (this.ctx.note.content) {
|
if (this.tabContext.note.content) {
|
||||||
try {
|
try {
|
||||||
this.mapData = JSON.parse(this.ctx.note.content);
|
this.mapData = JSON.parse(this.tabContext.note.content);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.log("Could not parse content: ", e);
|
console.log("Could not parse content: ", e);
|
||||||
}
|
}
|
||||||
@ -234,14 +264,14 @@ class NoteDetailRelationMap {
|
|||||||
return id.substr(13);
|
return id.substr(13);
|
||||||
}
|
}
|
||||||
|
|
||||||
async render() {
|
async doRefresh() {
|
||||||
this.$component.show();
|
this.$widget.show();
|
||||||
|
|
||||||
await libraryLoader.requireLibrary(libraryLoader.RELATION_MAP);
|
await libraryLoader.requireLibrary(libraryLoader.RELATION_MAP);
|
||||||
|
|
||||||
jsPlumb.ready(() => {
|
jsPlumb.ready(() => {
|
||||||
// lazy loading above can take time and tab might have been already switched to another note
|
// lazy loading above can take time and tab might have been already switched to another note
|
||||||
if (this.ctx.note && this.ctx.note.type === 'relation-map') {
|
if (this.tabContext.note && this.tabContext.note.type === 'relation-map') {
|
||||||
this.loadMapData();
|
this.loadMapData();
|
||||||
|
|
||||||
this.initJsPlumbInstance();
|
this.initJsPlumbInstance();
|
||||||
@ -490,7 +520,7 @@ class NoteDetailRelationMap {
|
|||||||
}
|
}
|
||||||
|
|
||||||
saveData() {
|
saveData() {
|
||||||
this.ctx.noteChanged();
|
this.tabContext.noteChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
async createNoteBox(noteId, title, x, y) {
|
async createNoteBox(noteId, title, x, y) {
|
||||||
@ -624,5 +654,3 @@ class NoteDetailRelationMap {
|
|||||||
|
|
||||||
scrollToTop() {}
|
scrollToTop() {}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default NoteDetailRelationMap;
|
|
55
src/public/javascripts/widgets/type_widgets/render.js
Normal file
55
src/public/javascripts/widgets/type_widgets/render.js
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
import renderService from "../../services/render.js";
|
||||||
|
import TypeWidget from "./type_widget.js";
|
||||||
|
|
||||||
|
const TPL = `
|
||||||
|
<div class="note-detail-render note-detail-printable">
|
||||||
|
<div class="note-detail-render-help alert alert-warning">
|
||||||
|
<p><strong>This help note is shown because this note of type Render HTML doesn't have required relation to function properly.</strong></p>
|
||||||
|
|
||||||
|
<p>Render HTML note type is used for <a href="https://github.com/zadam/trilium/wiki/Scripts">scripting</a>. In short, you have a HTML code note (optionally with some JavaScript) and this note will render it. To make it work, you need to define a relation (in <a class="show-attributes-button">Attributes dialog</a>) called "renderNote" pointing to the HTML note to render. Once that's defined you can click on the "play" button to render.</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="note-detail-render-content"></div>
|
||||||
|
</div>`;
|
||||||
|
|
||||||
|
export default class RenderTypeWidget extends TypeWidget {
|
||||||
|
static getType() { return "render"; }
|
||||||
|
|
||||||
|
doRender() {
|
||||||
|
this.$widget = $(TPL);
|
||||||
|
this.$noteDetailRenderHelp = this.$widget.find('.note-detail-render-help');
|
||||||
|
this.$noteDetailRenderContent = this.$widget.find('.note-detail-render-content');
|
||||||
|
this.$renderButton = this.$widget.find('.render-button');
|
||||||
|
|
||||||
|
this.$renderButton.on('click', () => this.render()); // long form!
|
||||||
|
|
||||||
|
return this.$widget;
|
||||||
|
}
|
||||||
|
|
||||||
|
async doRefresh() {
|
||||||
|
this.$widget.show();
|
||||||
|
this.$noteDetailRenderHelp.hide();
|
||||||
|
|
||||||
|
const renderNotesFound = await renderService.render(this.ctx.note, this.$noteDetailRenderContent, this.ctx);
|
||||||
|
|
||||||
|
if (!renderNotesFound) {
|
||||||
|
this.$noteDetailRenderHelp.show();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getContent() {}
|
||||||
|
|
||||||
|
show() {}
|
||||||
|
|
||||||
|
focus() {}
|
||||||
|
|
||||||
|
onNoteChange() {}
|
||||||
|
|
||||||
|
cleanup() {
|
||||||
|
this.$noteDetailRenderContent.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
scrollToTop() {
|
||||||
|
this.$widget.scrollTop(0);
|
||||||
|
}
|
||||||
|
}
|
75
src/public/javascripts/widgets/type_widgets/search.js
Normal file
75
src/public/javascripts/widgets/type_widgets/search.js
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
import noteDetailService from "../../services/note_detail.js";
|
||||||
|
import searchNotesService from "../../services/search_notes.js";
|
||||||
|
import TypeWidget from "./type_widget.js";
|
||||||
|
|
||||||
|
const TPL = `
|
||||||
|
<div class="note-detail-search note-detail-printable">
|
||||||
|
<div style="display: flex; align-items: center; margin-right: 20px; margin-top: 15px;">
|
||||||
|
<strong>Search string: </strong>
|
||||||
|
<textarea rows="4" style="width: auto !important; flex-grow: 4" class="search-string form-control"></textarea>
|
||||||
|
|
||||||
|
<span>
|
||||||
|
|
||||||
|
<button type="button" class="btn btn-primary note-detail-search-refresh-results-button">Refresh search results</button>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<br />
|
||||||
|
|
||||||
|
<div class="note-detail-search-help"></div>
|
||||||
|
</div>`;
|
||||||
|
|
||||||
|
export default class SearchTypeWidget extends TypeWidget {
|
||||||
|
static getType() { return "search"; }
|
||||||
|
|
||||||
|
doRender() {
|
||||||
|
this.$widget = $(TPL);
|
||||||
|
this.$searchString = this.$widget.find(".search-string");
|
||||||
|
this.$component = this.$widget.find('.note-detail-search');
|
||||||
|
this.$help = this.$widget.find(".note-detail-search-help");
|
||||||
|
this.$refreshButton = this.$widget.find('.note-detail-search-refresh-results-button');
|
||||||
|
|
||||||
|
this.$refreshButton.on('click', async () => {
|
||||||
|
// FIXME
|
||||||
|
await noteDetailService.saveNotesIfChanged();
|
||||||
|
|
||||||
|
await searchNotesService.refreshSearch();
|
||||||
|
});
|
||||||
|
|
||||||
|
return this.$widget;
|
||||||
|
}
|
||||||
|
|
||||||
|
doRefresh() {
|
||||||
|
this.$help.html(searchNotesService.getHelpText());
|
||||||
|
|
||||||
|
this.$component.show();
|
||||||
|
|
||||||
|
try {
|
||||||
|
const json = JSON.parse(this.ctx.note.content);
|
||||||
|
|
||||||
|
this.$searchString.val(json.searchString);
|
||||||
|
}
|
||||||
|
catch (e) {
|
||||||
|
console.log(e);
|
||||||
|
this.$searchString.val('');
|
||||||
|
}
|
||||||
|
|
||||||
|
this.$searchString.on('input', () => this.ctx.noteChanged());
|
||||||
|
}
|
||||||
|
|
||||||
|
getContent() {
|
||||||
|
return JSON.stringify({
|
||||||
|
searchString: this.$searchString.val()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
focus() {}
|
||||||
|
|
||||||
|
show() {}
|
||||||
|
|
||||||
|
onNoteChange() {}
|
||||||
|
|
||||||
|
cleanup() {}
|
||||||
|
|
||||||
|
scrollToTop() {}
|
||||||
|
}
|
@ -4,9 +4,6 @@ import noteAutocompleteService from '../../services/note_autocomplete.js';
|
|||||||
import mimeTypesService from '../../services/mime_types.js';
|
import mimeTypesService from '../../services/mime_types.js';
|
||||||
import TypeWidget from "./type_widget.js";
|
import TypeWidget from "./type_widget.js";
|
||||||
import utils from "../../services/utils.js";
|
import utils from "../../services/utils.js";
|
||||||
import linkService from "../../services/link.js";
|
|
||||||
import appContext from "../../services/app_context.js";
|
|
||||||
import noteDetailService from "../../services/note_detail.js";
|
|
||||||
|
|
||||||
const ENABLE_INSPECTOR = false;
|
const ENABLE_INSPECTOR = false;
|
||||||
|
|
||||||
@ -77,7 +74,7 @@ const TPL = `
|
|||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
class TextTypeWidget extends TypeWidget {
|
export default class TextTypeWidget extends TypeWidget {
|
||||||
static getType() { return "text"; }
|
static getType() { return "text"; }
|
||||||
|
|
||||||
doRender() {
|
doRender() {
|
||||||
@ -218,5 +215,3 @@ class TextTypeWidget extends TypeWidget {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default TextTypeWidget;
|
|
@ -398,14 +398,6 @@ button.icon-button {
|
|||||||
padding: 0;
|
padding: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.note-detail-image {
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.note-detail-image-view {
|
|
||||||
max-width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
pre:not(.CodeMirror-line) {
|
pre:not(.CodeMirror-line) {
|
||||||
color: var(--main-text-color) !important;
|
color: var(--main-text-color) !important;
|
||||||
white-space: pre-wrap;
|
white-space: pre-wrap;
|
||||||
@ -625,12 +617,6 @@ div[data-notify="container"] {
|
|||||||
cursor: pointer !important;
|
cursor: pointer !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
#protected-session-password-component {
|
|
||||||
max-width: 450px;
|
|
||||||
margin: auto;
|
|
||||||
padding-top: 50px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ck-editor__is-empty.ck-content.ck-editor__editable::before {
|
.ck-editor__is-empty.ck-content.ck-editor__editable::before {
|
||||||
content: 'You can start writing note here ...';
|
content: 'You can start writing note here ...';
|
||||||
position: absolute;
|
position: absolute;
|
||||||
@ -711,10 +697,7 @@ a.external:not(.no-arrow):after, a[href^="http://"]:not(.no-arrow):after, a[href
|
|||||||
display: inline-block;
|
display: inline-block;
|
||||||
}
|
}
|
||||||
|
|
||||||
.protected-session-password-component {
|
|
||||||
width: 300px;
|
|
||||||
margin: 30px auto auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.note-detail-empty {
|
.note-detail-empty {
|
||||||
margin: 50px;
|
margin: 50px;
|
||||||
@ -729,68 +712,6 @@ a.external:not(.no-arrow):after, a[href^="http://"]:not(.no-arrow):after, a[href
|
|||||||
z-index: 100;
|
z-index: 100;
|
||||||
}
|
}
|
||||||
|
|
||||||
.note-detail-book {
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.note-detail-book-content {
|
|
||||||
display: flex;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
overflow: auto;
|
|
||||||
height: 100%;
|
|
||||||
align-content: start;
|
|
||||||
}
|
|
||||||
|
|
||||||
.note-book-card {
|
|
||||||
border-radius: 10px;
|
|
||||||
background-color: var(--accented-background-color);
|
|
||||||
padding: 15px;
|
|
||||||
padding-bottom: 5px;
|
|
||||||
margin: 5px;
|
|
||||||
margin-left: 0;
|
|
||||||
overflow: hidden;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
flex-shrink: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.note-book-card .note-book-card {
|
|
||||||
border: 1px solid var(--main-border-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
.note-book-content {
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
.note-book-card.type-image .note-book-content, .note-book-card.type-file .note-book-content, .note-book-card.type-protected-session .note-book-content {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.note-book-card.type-image .note-book-content img {
|
|
||||||
max-width: 100%;
|
|
||||||
max-height: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.note-book-title {
|
|
||||||
flex-grow: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.note-book-content {
|
|
||||||
flex-grow: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.note-book-auto-message {
|
|
||||||
background-color: var(--accented-background-color);
|
|
||||||
text-align: center;
|
|
||||||
width: 100%;
|
|
||||||
border-radius: 10px;
|
|
||||||
padding: 5px;
|
|
||||||
margin-top: 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
#toast-container {
|
#toast-container {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
@ -1,21 +0,0 @@
|
|||||||
<div class="note-detail-book note-detail-printable">
|
|
||||||
<div class="btn-group floating-button" style="right: 20px; top: 20px;">
|
|
||||||
<button type="button"
|
|
||||||
class="expand-children-button btn icon-button bx bx-move-vertical"
|
|
||||||
title="Expand all children"></button>
|
|
||||||
|
|
||||||
<button type="button"
|
|
||||||
class="book-zoom-in-button btn icon-button bx bx-zoom-in"
|
|
||||||
title="Zoom In"></button>
|
|
||||||
|
|
||||||
<button type="button"
|
|
||||||
class="book-zoom-out-button btn icon-button bx bx-zoom-out"
|
|
||||||
title="Zoom Out"></button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="note-detail-book-help alert alert-warning">
|
|
||||||
This note of type Book doesn't have any child notes so there's nothing to display. See <a href="https://github.com/zadam/trilium/wiki/Book-note">wiki</a> for details.
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="note-detail-book-content"></div>
|
|
||||||
</div>
|
|
@ -1,32 +0,0 @@
|
|||||||
<div class="note-detail-image note-detail-printable">
|
|
||||||
<div style="display: flex; justify-content: space-evenly; margin: 10px;">
|
|
||||||
<button class="image-download btn btn-sm btn-primary" type="button">Download</button>
|
|
||||||
|
|
||||||
<button class="image-copy-to-clipboard btn btn-sm btn-primary" type="button">Copy to clipboard</button>
|
|
||||||
|
|
||||||
<button class="image-upload-new-revision btn btn-sm btn-primary" type="button">Upload new revision</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="note-detail-image-wrapper">
|
|
||||||
<img class="note-detail-image-view" />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div style="display: flex; justify-content: space-evenly; margin: 10px;">
|
|
||||||
<span>
|
|
||||||
<strong>Original file name:</strong>
|
|
||||||
<span class="image-filename"></span>
|
|
||||||
</span>
|
|
||||||
|
|
||||||
<span>
|
|
||||||
<strong>File type:</strong>
|
|
||||||
<span class="image-filetype"></span>
|
|
||||||
</span>
|
|
||||||
|
|
||||||
<span>
|
|
||||||
<strong>File size:</strong>
|
|
||||||
<span class="image-filesize"></span>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<input type="file" class="image-upload-new-revision-input" style="display: none">
|
|
||||||
</div>
|
|
@ -1,10 +0,0 @@
|
|||||||
<div class="protected-session-password-component note-detail-printable">
|
|
||||||
<form class="protected-session-password-form">
|
|
||||||
<div class="form-group">
|
|
||||||
<label for="protected-session-password-in-detail">Showing protected note requires entering your password:</label>
|
|
||||||
<input class="protected-session-password-in-detail form-control protected-session-password" type="password">
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<button class="btn btn-primary">Start protected session <kbd>enter</kbd></button>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
@ -1,27 +0,0 @@
|
|||||||
<div class="note-detail-relation-map note-detail-printable">
|
|
||||||
<button class="relation-map-create-child-note btn btn-sm floating-button" type="button"
|
|
||||||
title="Create new child note and add it into this relation map">
|
|
||||||
<span class="bx bx-folder-plus"></span>
|
|
||||||
|
|
||||||
Create child note
|
|
||||||
</button>
|
|
||||||
|
|
||||||
<button type="button"
|
|
||||||
class="relation-map-reset-pan-zoom btn icon-button floating-button bx bx-crop"
|
|
||||||
title="Reset pan & zoom to initial coordinates and magnification"
|
|
||||||
style="right: 70px;"></button>
|
|
||||||
|
|
||||||
<div class="btn-group floating-button" style="right: 10px;">
|
|
||||||
<button type="button"
|
|
||||||
class="relation-map-zoom-in btn icon-button bx bx-zoom-in"
|
|
||||||
title="Zoom In"></button>
|
|
||||||
|
|
||||||
<button type="button"
|
|
||||||
class="relation-map-zoom-out btn icon-button bx bx-zoom-out"
|
|
||||||
title="Zoom Out"></button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="relation-map-wrapper">
|
|
||||||
<div class="relation-map-container"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
@ -1,9 +0,0 @@
|
|||||||
<div class="note-detail-render note-detail-printable">
|
|
||||||
<div class="note-detail-render-help alert alert-warning">
|
|
||||||
<p><strong>This help note is shown because this note of type Render HTML doesn't have required relation to function properly.</strong></p>
|
|
||||||
|
|
||||||
<p>Render HTML note type is used for <a href="https://github.com/zadam/trilium/wiki/Scripts">scripting</a>. In short, you have a HTML code note (optionally with some JavaScript) and this note will render it. To make it work, you need to define a relation (in <a class="show-attributes-button">Attributes dialog</a>) called "renderNote" pointing to the HTML note to render. Once that's defined you can click on the "play" button to render.</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="note-detail-render-content"></div>
|
|
||||||
</div>
|
|
@ -1,15 +0,0 @@
|
|||||||
<div class="note-detail-search note-detail-printable">
|
|
||||||
<div style="display: flex; align-items: center; margin-right: 20px; margin-top: 15px;">
|
|
||||||
<strong>Search string: </strong>
|
|
||||||
<textarea rows="4" style="width: auto !important; flex-grow: 4" class="search-string form-control"></textarea>
|
|
||||||
|
|
||||||
<span>
|
|
||||||
|
|
||||||
<button type="button" class="btn btn-primary note-detail-search-refresh-results-button">Refresh search results</button>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<br />
|
|
||||||
|
|
||||||
<div class="note-detail-search-help"></div>
|
|
||||||
</div>
|
|
Loading…
x
Reference in New Issue
Block a user