mirror of
https://github.com/zadam/trilium.git
synced 2025-03-01 14:22:32 +01:00
250 lines
7.9 KiB
JavaScript
250 lines
7.9 KiB
JavaScript
import TabAwareWidget from "./tab_aware_widget.js";
|
|
import libraryLoader from "../services/library_loader.js";
|
|
import noteAutocompleteService from "../services/note_autocomplete.js";
|
|
import treeCache from "../services/tree_cache.js";
|
|
import server from "../services/server.js";
|
|
import utils from "../services/utils.js";
|
|
import ws from "../services/ws.js";
|
|
import SpacedUpdate from "../services/spaced_update.js";
|
|
import attributesParser from "../services/attribute_parser.js";
|
|
|
|
const mentionSetup = {
|
|
feeds: [
|
|
{
|
|
marker: '@',
|
|
feed: queryText => {
|
|
return new Promise((res, rej) => {
|
|
noteAutocompleteService.autocompleteSource(queryText, rows => {
|
|
res(rows.map(row => {
|
|
return {
|
|
id: '@' + row.notePathTitle,
|
|
name: row.notePathTitle,
|
|
link: '#' + row.notePath,
|
|
notePath: row.notePath,
|
|
highlightedNotePathTitle: row.highlightedNotePathTitle
|
|
}
|
|
}));
|
|
});
|
|
});
|
|
},
|
|
itemRenderer: item => {
|
|
const itemElement = document.createElement('span');
|
|
|
|
itemElement.classList.add('mentions-item');
|
|
itemElement.innerHTML = `${item.highlightedNotePathTitle} `;
|
|
|
|
return itemElement;
|
|
},
|
|
minimumCharacters: 0
|
|
},
|
|
{
|
|
marker: '#',
|
|
feed: async queryText => {
|
|
const names = await server.get(`attributes/names/?type=label&query=${encodeURIComponent(queryText)}`);
|
|
|
|
return names.map(name => {
|
|
return {
|
|
id: '#' + name,
|
|
name: name
|
|
}
|
|
});
|
|
},
|
|
minimumCharacters: 0,
|
|
attributeMention: true
|
|
},
|
|
{
|
|
marker: '~',
|
|
feed: async queryText => {
|
|
const names = await server.get(`attributes/names/?type=relation&query=${encodeURIComponent(queryText)}`);
|
|
|
|
return names.map(name => {
|
|
return {
|
|
id: '~' + name,
|
|
name: name
|
|
}
|
|
});
|
|
},
|
|
minimumCharacters: 0,
|
|
attributeMention: true
|
|
}
|
|
]
|
|
};
|
|
|
|
const TPL = `
|
|
<div class="note-attributes">
|
|
<div class="note-attributes-editor"></div>
|
|
</div>
|
|
`;
|
|
|
|
export default class NoteAttributesWidget extends TabAwareWidget {
|
|
constructor() {
|
|
super();
|
|
|
|
this.spacedUpdate = new SpacedUpdate(async () => {
|
|
const content = this.textEditor.getData();
|
|
|
|
this.parse(content);
|
|
});
|
|
}
|
|
|
|
doRender() {
|
|
this.$widget = $(TPL);
|
|
this.$editor = this.$widget.find('.note-attributes-editor');
|
|
this.initialized = this.initEditor();
|
|
|
|
this.$editor.keypress(async e => {
|
|
const keycode = (e.keyCode ? e.keyCode : e.which);
|
|
if (keycode === 13) {
|
|
const attributes = attributesParser.lexAndParse(this.textEditor.getData());
|
|
|
|
await server.put(`notes/${this.noteId}/attributes2`, attributes, this.componentId);
|
|
|
|
console.log("Saved!");
|
|
}
|
|
})
|
|
|
|
return this.$widget;
|
|
}
|
|
|
|
async initEditor() {
|
|
await libraryLoader.requireLibrary(libraryLoader.CKEDITOR);
|
|
|
|
// CKEditor since version 12 needs the element to be visible before initialization. At the same time
|
|
// we want to avoid flicker - i.e. show editor only once everything is ready. That's why we have separate
|
|
// display of $widget in both branches.
|
|
this.$widget.show();
|
|
|
|
this.$editor.on("click", () => {
|
|
const pos = this.textEditor.model.document.selection.getFirstPosition();
|
|
console.log(pos.textNode && pos.textNode.data, pos.parent.textNode && pos.parent.textNode.data, pos.offset);
|
|
});
|
|
|
|
this.textEditor = await BalloonEditor.create(this.$editor[0], {
|
|
removePlugins: [
|
|
'Enter',
|
|
'ShiftEnter',
|
|
'Heading',
|
|
'Link',
|
|
'Autoformat',
|
|
'Bold',
|
|
'Italic',
|
|
'Underline',
|
|
'Strikethrough',
|
|
'Code',
|
|
'Superscript',
|
|
'Subscript',
|
|
'BlockQuote',
|
|
'Image',
|
|
'ImageCaption',
|
|
'ImageStyle',
|
|
'ImageToolbar',
|
|
'ImageUpload',
|
|
'ImageResize',
|
|
'List',
|
|
'TodoList',
|
|
'PasteFromOffice',
|
|
'Table',
|
|
'TableToolbar',
|
|
'TableProperties',
|
|
'TableCellProperties',
|
|
'Indent',
|
|
'IndentBlock',
|
|
'BlockToolbar',
|
|
'ParagraphButtonUI',
|
|
'HeadingButtonsUI',
|
|
'UploadimagePlugin',
|
|
'InternalLinkPlugin',
|
|
'MarkdownImportPlugin',
|
|
'CuttonotePlugin',
|
|
'TextTransformation',
|
|
'Font',
|
|
'FontColor',
|
|
'FontBackgroundColor',
|
|
'CodeBlock',
|
|
'SelectAll',
|
|
'IncludeNote',
|
|
'CutToNote'
|
|
],
|
|
toolbar: {
|
|
items: []
|
|
},
|
|
placeholder: "Type the labels and relations here ...",
|
|
mention: mentionSetup
|
|
});
|
|
|
|
this.textEditor.model.document.on('change:data', () => this.spacedUpdate.scheduleUpdate());
|
|
|
|
await import(/* webpackIgnore: true */'../../libraries/ckeditor/inspector.js');
|
|
CKEditorInspector.attach(this.textEditor);
|
|
}
|
|
|
|
async loadReferenceLinkTitle(noteId, $el) {
|
|
const note = await treeCache.getNote(noteId, true);
|
|
|
|
let title;
|
|
|
|
if (!note) {
|
|
title = '[missing]';
|
|
}
|
|
else if (!note.isDeleted) {
|
|
title = note.title;
|
|
}
|
|
else {
|
|
title = note.isErased ? '[erased]' : `${note.title} (deleted)`;
|
|
}
|
|
|
|
$el.text(title);
|
|
}
|
|
|
|
async refreshWithNote(note) {
|
|
const ownedAttributes = note.getOwnedAttributes();
|
|
const $attributesContainer = $("<div>");
|
|
|
|
await this.renderAttributes(ownedAttributes, $attributesContainer);
|
|
console.log($attributesContainer.html());
|
|
this.textEditor.setData($attributesContainer.html());
|
|
}
|
|
|
|
createNoteLink(noteId) {
|
|
return $("<a>", {
|
|
href: '#' + noteId,
|
|
class: 'reference-link',
|
|
'data-note-path': noteId
|
|
});
|
|
}
|
|
|
|
async renderAttributes(attributes, $container) {
|
|
for (const attribute of attributes) {
|
|
if (attribute.type === 'label') {
|
|
$container.append(utils.formatLabel(attribute) + " ");
|
|
} else if (attribute.type === 'relation') {
|
|
if (attribute.isAutoLink) {
|
|
continue;
|
|
}
|
|
|
|
if (attribute.value) {
|
|
$container.append('~' + attribute.name + "=");
|
|
$container.append(this.createNoteLink(attribute.value));
|
|
$container.append(" ");
|
|
} else {
|
|
ws.logError(`Relation ${attribute.attributeId} has empty target`);
|
|
}
|
|
} else {
|
|
ws.logError("Unknown attr type: " + attribute.type);
|
|
}
|
|
}
|
|
}
|
|
|
|
parse(content) {
|
|
if (content.startsWith('<p>')) {
|
|
content = content.substr(3);
|
|
}
|
|
|
|
if (content.endsWith('</p>')) {
|
|
content = content.substr(0, content - 4);
|
|
}
|
|
|
|
console.log(content);
|
|
}
|
|
}
|