import renderService from "./render.js"; import protectedSessionService from "./protected_session.js"; import protectedSessionHolder from "./protected_session_holder.js"; import libraryLoader from "./library_loader.js"; import openService from "./open.js"; import froca from "./froca.js"; import utils from "./utils.js"; import linkService from "./link.js"; import treeService from "./tree.js"; import FNote from "../entities/fnote.js"; import FAttachment from "../entities/fattachment.js"; import imageContextMenuService from "../menus/image_context_menu.js"; import { applySingleBlockSyntaxHighlight, applySyntaxHighlight } from "./syntax_highlight.js"; import mime_types from "./mime_types.js"; import { loadElkIfNeeded } from "./mermaid.js"; let idCounter = 1; /** * @param {FNote|FAttachment} entity * @param {object} options * @return {Promise<{type: string, $renderedContent: jQuery}>} */ async function getRenderedContent(entity, options = {}) { options = Object.assign({ tooltip: false }, options); const type = getRenderingType(entity); // attachment supports only image and file/pdf/audio/video const $renderedContent = $('
");
$codeBlock.text(blob.content);
$renderedContent.append($("").append($codeBlock));
await applySingleBlockSyntaxHighlight($codeBlock, mime_types.normalizeMimeTypeForCKEditor(note.mime));
}
function renderImage(entity, $renderedContent, options = {}) {
const encodedTitle = encodeURIComponent(entity.title);
let url;
if (entity instanceof FNote) {
url = `api/images/${entity.noteId}/${encodedTitle}?${Math.random()}`;
} else if (entity instanceof FAttachment) {
url = `api/attachments/${entity.attachmentId}/image/${encodedTitle}?${entity.utcDateModified}">`;
}
$renderedContent // styles needed for the zoom to work well
.css('display', 'flex')
.css('align-items', 'center')
.css('justify-content', 'center');
const $img = $("
")
.attr("src", url)
.attr("id", "attachment-image-" + idCounter++)
.css("max-width", "100%");
$renderedContent.append($img);
if (options.imageHasZoom) {
libraryLoader.requireLibrary(libraryLoader.WHEEL_ZOOM).then(() => {
WZoom.create(`#${$img.attr("id")}`, {
maxScale: 50,
speed: 1.3,
zoomOnClick: false
});
});
}
imageContextMenuService.setupContextMenu($img);
}
function renderFile(entity, type, $renderedContent) {
let entityType, entityId;
if (entity instanceof FNote) {
entityType = 'notes';
entityId = entity.noteId;
} else if (entity instanceof FAttachment) {
entityType = 'attachments';
entityId = entity.attachmentId;
} else {
throw new Error(`Can't recognize entity type of '${entity}'`);
}
const $content = $('');
if (type === 'pdf') {
const $pdfPreview = $('');
$pdfPreview.attr("src", openService.getUrlForDownload(`api/${entityType}/${entityId}/open`));
$content.append($pdfPreview);
} else if (type === 'audio') {
const $audioPreview = $('')
.attr("src", openService.getUrlForDownload(`api/${entityType}/${entityId}/open-partial`))
.attr("type", entity.mime)
.css("width", "100%");
$content.append($audioPreview);
} else if (type === 'video') {
const $videoPreview = $('')
.attr("src", openService.getUrlForDownload(`api/${entityType}/${entityId}/open-partial`))
.attr("type", entity.mime)
.css("width", "100%");
$content.append($videoPreview);
}
if (entityType === 'notes') {
// TODO: we should make this available also for attachments, but there's a problem with "Open externally" support
// in attachment list
const $downloadButton = $('');
const $openButton = $('');
$downloadButton.on('click', () => openService.downloadFileNote(entity.noteId));
$openButton.on('click', () => openService.openNoteExternally(entity.noteId, entity.mime));
// open doesn't work for protected notes since it works through a browser which isn't in protected session
$openButton.toggle(!entity.isProtected);
$content.append(
$('')
.append($downloadButton)
.append($openButton)
);
}
$renderedContent.append($content);
}
async function renderMermaid(note, $renderedContent) {
await libraryLoader.requireLibrary(libraryLoader.MERMAID);
const blob = await note.getBlob();
const content = blob.content || "";
$renderedContent
.css("display", "flex")
.css("justify-content", "space-around");
const documentStyle = window.getComputedStyle(document.documentElement);
const mermaidTheme = documentStyle.getPropertyValue('--mermaid-theme');
mermaid.mermaidAPI.initialize({startOnLoad: false, theme: mermaidTheme.trim(), securityLevel: 'antiscript'});
try {
await loadElkIfNeeded(content);
const {svg} = await mermaid.mermaidAPI.render("in-mermaid-graph-" + idCounter++, content);
$renderedContent.append($(svg));
} catch (e) {
const $error = $("The diagram could not displayed.
");
$renderedContent.append($error);
}
}
/**
* @param {jQuery} $renderedContent
* @param {FNote} note
* @returns {Promise}
*/
async function renderChildrenList($renderedContent, note) {
$renderedContent.css("padding", "10px");
$renderedContent.addClass("text-with-ellipsis");
let childNoteIds = note.getChildNoteIds();
if (childNoteIds.length > 10) {
childNoteIds = childNoteIds.slice(0, 10);
}
// just load the first 10 child notes
const childNotes = await froca.getNotes(childNoteIds);
for (const childNote of childNotes) {
$renderedContent.append(await linkService.createLink(`${note.noteId}/${childNote.noteId}`, {
showTooltip: false,
showNoteIcon: true
}));
$renderedContent.append("
");
}
}
function getRenderingType(entity) {
let type = entity.type || entity.role;
const mime = entity.mime;
if (type === 'file' && mime === 'application/pdf') {
type = 'pdf';
} else if (type === 'file' && mime.startsWith('audio/')) {
type = 'audio';
} else if (type === 'file' && mime.startsWith('video/')) {
type = 'video';
}
if (entity.isProtected) {
if (protectedSessionHolder.isProtectedSessionAvailable()) {
protectedSessionHolder.touchProtectedSession();
}
else {
type = 'protectedSession';
}
}
return type;
}
export default {
getRenderedContent
};