trilium/src/public/javascripts/services/note_detail_book.js

250 lines
8 KiB
JavaScript
Raw Normal View History

import server from "./server.js";
import linkService from "./link.js";
2019-10-05 04:21:14 +08:00
import utils from "./utils.js";
2019-10-05 15:33:31 +08:00
import treeCache from "./tree_cache.js";
2019-10-05 16:55:29 +08:00
import renderService from "./render.js";
import zoom from "./zoom.js";
2019-10-05 04:21:14 +08:00
const MIN_ZOOM_LEVEL = 1;
const MAX_ZOOM_LEVEL = 6;
const ZOOMS = {
2019-10-05 16:55:29 +08:00
1: {
width: "100%",
height: "100%"
},
2: {
width: "49%",
height: "350px"
},
3: {
width: "32%",
height: "250px"
},
4: {
width: "24%",
height: "200px"
},
5: {
width: "19%",
height: "175px"
},
6: {
width: "16%",
height: "150px"
}
2019-10-05 04:21:14 +08:00
};
class NoteDetailBook {
/**
* @param {TabContext} ctx
*/
constructor(ctx) {
this.ctx = ctx;
this.$component = ctx.$tabContent.find('.note-detail-book');
2019-10-05 04:21:14 +08:00
this.$content = this.$component.find('.note-detail-book-content');
2019-10-05 17:22:42 +08:00
this.$zoomInButton = this.$component.find('.book-zoom-in-button');
this.$zoomOutButton = this.$component.find('.book-zoom-out-button');
this.$expandChildrenButton = this.$component.find('.expand-children-button');
2019-10-05 04:21:14 +08:00
this.$zoomInButton.click(() => this.setZoom(this.zoomLevel - 1));
this.$zoomOutButton.click(() => this.setZoom(this.zoomLevel + 1));
2019-10-05 15:33:31 +08:00
2019-10-05 17:22:42 +08:00
this.$expandChildrenButton.click(async () => {
for (let i = 1; i < 30; i++) { // protection against infinite cycle
const $unexpandedLinks = this.$content.find('.note-book-open-children-button:visible');
if ($unexpandedLinks.length === 0) {
break;
}
for (const link of $unexpandedLinks) {
const $card = $(link).closest(".note-book-card");
await this.expandCard($card);
}
}
});
2019-10-05 15:33:31 +08:00
this.$content.on('click', '.note-book-open-children-button', async ev => {
const $card = $(ev.target).closest('.note-book-card');
2019-10-05 17:22:42 +08:00
await this.expandCard($card);
2019-10-05 15:33:31 +08:00
});
this.$content.on('click', '.note-book-hide-children-button', async ev => {
const $card = $(ev.target).closest('.note-book-card');
$card.find('.note-book-open-children-button').show();
$card.find('.note-book-hide-children-button').hide();
$card.find('.note-book-children-content').empty();
});
2019-10-05 04:21:14 +08:00
}
2019-10-05 17:22:42 +08:00
async expandCard($card) {
const noteId = $card.attr('data-note-id');
const note = await treeCache.getNote(noteId);
$card.find('.note-book-open-children-button').hide();
$card.find('.note-book-hide-children-button').show();
await this.renderIntoElement(note, $card.find('.note-book-children-content'));
}
2019-10-05 04:21:14 +08:00
setZoom(zoomLevel) {
if (!(zoomLevel in ZOOMS)) {
zoomLevel = this.getDefaultZoomLevel();
}
2019-10-05 04:21:14 +08:00
this.zoomLevel = zoomLevel;
this.$zoomInButton.prop("disabled", zoomLevel === MIN_ZOOM_LEVEL);
this.$zoomOutButton.prop("disabled", zoomLevel === MAX_ZOOM_LEVEL);
2019-10-05 16:55:29 +08:00
this.$content.find('.note-book-card').css("flex-basis", ZOOMS[zoomLevel].width);
this.$content.find('.note-book-content').css("max-height", ZOOMS[zoomLevel].height);
}
async render() {
2019-10-05 04:21:14 +08:00
this.$content.empty();
if (this.isAutoBook()) {
const $addTextLink = $('<a href="javascript:">here</a>').click(() => {
this.ctx.renderComponent(true);
});
this.$content.append($('<div class="note-book-auto-message"></div>')
.append(`This note doesn't have any content so we display it's children. Click `)
.append($addTextLink)
.append(' if you want to add some text.'))
}
const zoomLevel = parseInt(await this.ctx.note.getLabelValue('bookZoomLevel')) || this.getDefaultZoomLevel();
this.setZoom(zoomLevel);
2019-10-05 15:33:31 +08:00
await this.renderIntoElement(this.ctx.note, this.$content);
}
async renderIntoElement(note, $container) {
for (const childNote of await note.getChildNotes()) {
const $card = $('<div class="note-book-card">')
.attr('data-note-id', childNote.noteId)
2019-10-05 16:55:29 +08:00
.css("flex-basis", ZOOMS[this.zoomLevel].width)
2019-10-05 15:33:31 +08:00
.addClass("type-" + childNote.type)
.append($('<h5 class="note-book-title">').append(await linkService.createNoteLink(childNote.noteId, null, false)))
2019-10-05 16:55:29 +08:00
.append($('<div class="note-book-content">')
.css("max-height", ZOOMS[this.zoomLevel].height)
.append(await this.getNoteContent(childNote)));
2019-10-05 15:33:31 +08:00
const childCount = childNote.getChildNoteIds().length;
if (childCount > 0) {
const label = `${childCount} child${childCount > 1 ? 'ren' : ''}`;
$card.append($('<div class="note-book-children">')
2019-10-05 16:55:29 +08:00
.append($(`<a class="note-book-open-children-button" href="javascript:">+ Show ${label}</a>`))
.append($(`<a class="note-book-hide-children-button" href="javascript:">- Hide ${label}</a>`).hide())
2019-10-05 15:33:31 +08:00
.append($('<div class="note-book-children-content">'))
);
}
$container.append($card);
}
}
async getNoteContent(note) {
if (note.type === 'text') {
const fullNote = await server.get('notes/' + note.noteId);
const $content = $("<div>").html(fullNote.content);
if (utils.isHtmlEmpty(fullNote.content)) {
return "";
}
else {
return $content;
}
}
else if (note.type === 'code') {
const fullNote = await server.get('notes/' + note.noteId);
if (fullNote.content.trim() === "") {
return "";
}
return $("<pre>").text(fullNote.content);
}
else if (note.type === 'image') {
return $("<img>").attr("src", `api/images/${note.noteId}/${note.title}`);
}
2019-10-05 04:21:14 +08:00
else if (note.type === 'file') {
function getFileUrl() {
// electron needs absolute URL so we extract current host, port, protocol
return utils.getHost() + "/api/notes/" + note.noteId + "/download";
}
const $downloadButton = $('<button class="file-download btn btn-primary" type="button">Download</button>');
const $openButton = $('<button class="file-open btn btn-primary" type="button">Open</button>');
$downloadButton.click(() => utils.download(getFileUrl()));
$openButton.click(() => {
if (utils.isElectron()) {
const open = require("open");
open(getFileUrl());
}
else {
window.location.href = getFileUrl();
}
});
// open doesn't work for protected notes since it works through browser which isn't in protected session
$openButton.toggle(!note.isProtected);
return $('<div>')
.append($downloadButton)
.append(' &nbsp; ')
.append($openButton);
}
2019-10-05 16:55:29 +08:00
else if (note.type === 'render') {
const $el = $('<div>');
await renderService.render(note, $el, this.ctx);
return $el;
}
else {
return "<em>Content of this note cannot be displayed in the book format</em>";
}
}
/** @return {boolean} true if this is "auto book" activated (empty text note) and not explicit book note */
isAutoBook() {
return this.ctx.note.type !== 'book';
}
getDefaultZoomLevel() {
return this.isAutoBook() ? 3 : 1;
}
getContent() {}
show() {
this.$component.show();
}
focus() {}
onNoteChange() {}
cleanup() {
2019-10-05 04:21:14 +08:00
this.$content.empty();
}
scrollToTop() {
this.$component.scrollTop(0);
}
}
export default NoteDetailBook;