2019-10-02 03:11:11 +08:00
|
|
|
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";
|
2019-10-06 17:21:12 +08:00
|
|
|
import protectedSessionHolder from "./protected_session_holder.js";
|
|
|
|
import protectedSessionService from "./protected_session.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
|
|
|
};
|
2019-10-02 03:11:11 +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
|
|
|
|
2019-11-10 00:39:48 +08:00
|
|
|
this.$zoomInButton.on('click', () => this.setZoom(this.zoomLevel - 1));
|
|
|
|
this.$zoomOutButton.on('click', () => this.setZoom(this.zoomLevel + 1));
|
2019-10-05 15:33:31 +08:00
|
|
|
|
2019-11-10 00:39:48 +08:00
|
|
|
this.$expandChildrenButton.on('click', async () => {
|
2019-10-05 17:22:42 +08:00
|
|
|
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) {
|
2019-10-05 18:01:00 +08:00
|
|
|
if (!(zoomLevel in ZOOMS)) {
|
2019-10-06 02:27:30 +08:00
|
|
|
zoomLevel = this.getDefaultZoomLevel();
|
2019-10-05 18:01:00 +08:00
|
|
|
}
|
|
|
|
|
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);
|
2019-10-02 03:11:11 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
async render() {
|
2019-10-05 04:21:14 +08:00
|
|
|
this.$content.empty();
|
2019-10-02 03:11:11 +08:00
|
|
|
|
2019-10-06 02:27:30 +08:00
|
|
|
if (this.isAutoBook()) {
|
2019-11-10 00:39:48 +08:00
|
|
|
const $addTextLink = $('<a href="javascript:">here</a>').on('click', () => {
|
2019-10-06 02:27:30 +08:00
|
|
|
this.ctx.renderComponent(true);
|
|
|
|
});
|
|
|
|
|
|
|
|
this.$content.append($('<div class="note-book-auto-message"></div>')
|
2019-10-06 18:33:47 +08:00
|
|
|
.append(`This note doesn't have any content so we display its children. Click `)
|
2019-10-06 02:27:30 +08:00
|
|
|
.append($addTextLink)
|
|
|
|
.append(' if you want to add some text.'))
|
|
|
|
}
|
|
|
|
|
|
|
|
const zoomLevel = parseInt(await this.ctx.note.getLabelValue('bookZoomLevel')) || this.getDefaultZoomLevel();
|
2019-10-05 18:01:00 +08:00
|
|
|
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()) {
|
2019-10-06 17:21:12 +08:00
|
|
|
const type = this.getRenderingType(childNote);
|
|
|
|
|
2019-11-09 22:31:47 +08:00
|
|
|
const childNotePath = this.ctx.notePath + '/' + childNote.noteId;
|
|
|
|
|
2019-10-05 15:33:31 +08:00
|
|
|
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-06 17:21:12 +08:00
|
|
|
.addClass("type-" + type)
|
2019-12-29 04:10:02 +08:00
|
|
|
.append($('<h5 class="note-book-title">').append(await linkService.createNoteLink(childNotePath, {showTooltip: false})))
|
2019-10-05 16:55:29 +08:00
|
|
|
.append($('<div class="note-book-content">')
|
|
|
|
.css("max-height", ZOOMS[this.zoomLevel].height)
|
2019-10-06 17:21:12 +08:00
|
|
|
.append(await this.getNoteContent(type, 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);
|
2019-10-02 03:11:11 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-10-06 17:21:12 +08:00
|
|
|
async getNoteContent(type, note) {
|
|
|
|
if (type === 'text') {
|
2019-10-02 03:11:11 +08:00
|
|
|
const fullNote = await server.get('notes/' + note.noteId);
|
|
|
|
|
|
|
|
const $content = $("<div>").html(fullNote.content);
|
|
|
|
|
2019-10-06 02:27:30 +08:00
|
|
|
if (utils.isHtmlEmpty(fullNote.content)) {
|
2019-10-02 03:11:11 +08:00
|
|
|
return "";
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
return $content;
|
|
|
|
}
|
|
|
|
}
|
2019-10-06 17:21:12 +08:00
|
|
|
else if (type === 'code') {
|
2019-10-03 01:40:22 +08:00
|
|
|
const fullNote = await server.get('notes/' + note.noteId);
|
|
|
|
|
|
|
|
if (fullNote.content.trim() === "") {
|
|
|
|
return "";
|
|
|
|
}
|
|
|
|
|
|
|
|
return $("<pre>").text(fullNote.content);
|
|
|
|
}
|
2019-10-06 17:21:12 +08:00
|
|
|
else if (type === 'image') {
|
2019-10-03 01:40:22 +08:00
|
|
|
return $("<img>").attr("src", `api/images/${note.noteId}/${note.title}`);
|
|
|
|
}
|
2019-10-06 17:21:12 +08:00
|
|
|
else if (type === 'file') {
|
2019-10-05 04:21:14 +08:00
|
|
|
function getFileUrl() {
|
2019-11-23 03:35:17 +08:00
|
|
|
return utils.getUrlForDownload("api/notes/" + note.noteId + "/download");
|
2019-10-05 04:21:14 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
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>');
|
|
|
|
|
2019-11-10 00:39:48 +08:00
|
|
|
$downloadButton.on('click', () => utils.download(getFileUrl()));
|
|
|
|
$openButton.on('click', () => {
|
2019-10-05 04:21:14 +08:00
|
|
|
if (utils.isElectron()) {
|
|
|
|
const open = require("open");
|
|
|
|
|
2019-10-17 01:42:42 +08:00
|
|
|
open(getFileUrl(), {url: true});
|
2019-10-05 04:21:14 +08:00
|
|
|
}
|
|
|
|
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(' ')
|
|
|
|
.append($openButton);
|
|
|
|
}
|
2019-10-06 17:21:12 +08:00
|
|
|
else if (type === 'render') {
|
2019-10-05 16:55:29 +08:00
|
|
|
const $el = $('<div>');
|
|
|
|
|
|
|
|
await renderService.render(note, $el, this.ctx);
|
|
|
|
|
|
|
|
return $el;
|
|
|
|
}
|
2019-10-06 17:21:12 +08:00
|
|
|
else if (type === 'protected-session') {
|
2019-11-02 19:17:00 +08:00
|
|
|
const $button = $(`<button class="btn btn-sm"><span class="bx bx-log-in"></span> Enter protected session</button>`)
|
2019-11-10 00:39:48 +08:00
|
|
|
.on('click', protectedSessionService.enterProtectedSession);
|
2019-10-06 17:21:12 +08:00
|
|
|
|
|
|
|
return $("<div>")
|
|
|
|
.append("<div>This note is protected and to access it you need to enter password.</div>")
|
|
|
|
.append("<br/>")
|
|
|
|
.append($button);
|
|
|
|
}
|
2019-10-02 03:11:11 +08:00
|
|
|
else {
|
|
|
|
return "<em>Content of this note cannot be displayed in the book format</em>";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-10-06 02:27:30 +08:00
|
|
|
/** @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() {
|
2019-10-14 18:06:10 +08:00
|
|
|
if (this.isAutoBook()) {
|
|
|
|
const w = this.$component.width();
|
|
|
|
|
|
|
|
if (w <= 600) {
|
|
|
|
return 1;
|
|
|
|
} else if (w <= 900) {
|
|
|
|
return 2;
|
|
|
|
} else if (w <= 1300) {
|
|
|
|
return 3;
|
|
|
|
} else {
|
|
|
|
return 4;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
return 1;
|
|
|
|
}
|
2019-10-06 02:27:30 +08:00
|
|
|
}
|
|
|
|
|
2019-10-06 17:21:12 +08:00
|
|
|
getRenderingType(childNote) {
|
|
|
|
let type = childNote.type;
|
|
|
|
|
|
|
|
if (childNote.isProtected) {
|
|
|
|
if (protectedSessionHolder.isProtectedSessionAvailable()) {
|
|
|
|
protectedSessionHolder.touchProtectedSession();
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
type = 'protected-session';
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return type;
|
|
|
|
}
|
|
|
|
|
2019-11-08 06:25:58 +08:00
|
|
|
getContent() {
|
|
|
|
// for auto-book cases when renaming title there should be content
|
|
|
|
return "";
|
|
|
|
}
|
2019-10-02 03:11:11 +08:00
|
|
|
|
|
|
|
show() {
|
|
|
|
this.$component.show();
|
|
|
|
}
|
|
|
|
|
|
|
|
focus() {}
|
|
|
|
|
|
|
|
onNoteChange() {}
|
|
|
|
|
|
|
|
cleanup() {
|
2019-10-05 04:21:14 +08:00
|
|
|
this.$content.empty();
|
2019-10-02 03:11:11 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
scrollToTop() {
|
|
|
|
this.$component.scrollTop(0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
export default NoteDetailBook;
|