import NoteContextAwareWidget from "../note_context_aware_widget.js";
import noteAutocompleteService from "../../services/note_autocomplete.js";
import server from "../../services/server.js";
import contextMenuService from "../../menus/context_menu.js";
import attributeParser from "../../services/attribute_parser.js";
import libraryLoader from "../../services/library_loader.js";
import froca from "../../services/froca.js";
import attributeRenderer from "../../services/attribute_renderer.js";
import noteCreateService from "../../services/note_create.js";
import attributeService from "../../services/attributes.js";
import linkService from "../../services/link.js";
const HELP_TEXT = `
To add label, just type e.g. #rock
or if you want to add also value then e.g. #year = 2020
]+href="(#[A-Za-z0-9_/]*)"[^>]*>[^<]*<\/a>/g, "$1")
.replace(/ /g, " "); // otherwise .text() below outputs non-breaking space in unicode
return $("").html(str).text();
}
async initEditor() {
await libraryLoader.requireLibrary(libraryLoader.CKEDITOR);
this.$widget.show();
this.$editor.on("click", e => this.handleEditorClick(e));
/** @property {BalloonEditor} */
this.textEditor = await BalloonEditor.create(this.$editor[0], editorConfig);
this.textEditor.model.document.on('change:data', () => this.dataChanged());
this.textEditor.editing.view.document.on('enter', (event, data) => {
// disable entering new line - see https://github.com/ckeditor/ckeditor5/issues/9422
data.preventDefault();
event.stop();
}, {priority: 'high'});
// disable spellcheck for attribute editor
this.textEditor.editing.view.change(writer => writer.setAttribute('spellcheck', 'false', this.textEditor.editing.view.document.getRoot()));
//await import(/* webpackIgnore: true */'../../libraries/ckeditor/inspector');
//CKEditorInspector.attach(this.textEditor);
}
dataChanged() {
this.lastUpdatedNoteId = this.noteId;
if (this.lastSavedContent === this.textEditor.getData()) {
this.$saveAttributesButton.fadeOut();
}
else {
this.$saveAttributesButton.fadeIn();
}
if (this.$errors.is(":visible")) {
// using .hide() instead of .slideUp() since this will also hide the error after confirming
// mention for relation name which suits up. When using.slideUp() error will appear and the slideUp which is weird
this.$errors.hide();
}
}
async handleEditorClick(e) {
const pos = this.textEditor.model.document.selection.getFirstPosition();
if (pos && pos.textNode && pos.textNode.data) {
const clickIndex = this.getClickIndex(pos);
let parsedAttrs;
try {
parsedAttrs = attributeParser.lexAndParse(this.getPreprocessedData(), true);
}
catch (e) {
// the input is incorrect because the user messed up with it and now needs to fix it manually
return null;
}
let matchedAttr = null;
for (const attr of parsedAttrs) {
if (clickIndex > attr.startIndex && clickIndex <= attr.endIndex) {
matchedAttr = attr;
break;
}
}
setTimeout(() => {
if (matchedAttr) {
this.$editor.tooltip('hide');
this.attributeDetailWidget.showAttributeDetail({
allAttributes: parsedAttrs,
attribute: matchedAttr,
isOwned: true,
x: e.pageX,
y: e.pageY
});
}
else {
this.showHelpTooltip();
}
}, 100);
}
else {
this.showHelpTooltip();
}
}
showHelpTooltip() {
this.attributeDetailWidget.hide();
this.$editor.tooltip({
trigger: 'focus',
html: true,
title: HELP_TEXT,
placement: 'bottom',
offset: "0,30"
});
this.$editor.tooltip('show');
}
getClickIndex(pos) {
let clickIndex = pos.offset - pos.textNode.startOffset;
let curNode = pos.textNode;
while (curNode.previousSibling) {
curNode = curNode.previousSibling;
if (curNode.name === 'reference') {
clickIndex += curNode._attrs.get('notePath').length + 1;
} else {
clickIndex += curNode.data.length;
}
}
return clickIndex;
}
async loadReferenceLinkTitle($el, href) {
const {noteId} = linkService.parseNavigationStateFromUrl(href);
const note = await froca.getNote(noteId, true);
const title = note ? note.title : '[missing]';
$el.text(title);
}
async refreshWithNote(note) {
await this.renderOwnedAttributes(note.getOwnedAttributes(), true);
}
async renderOwnedAttributes(ownedAttributes, saved) {
// attrs are not resorted if position changes after the initial load
ownedAttributes.sort((a, b) => a.position - b.position);
let htmlAttrs = (await attributeRenderer.renderAttributes(ownedAttributes, true)).html();
if (htmlAttrs.length > 0) {
htmlAttrs += " ";
}
this.textEditor.setData(htmlAttrs);
if (saved) {
this.lastSavedContent = this.textEditor.getData();
this.$saveAttributesButton.fadeOut(0);
}
}
async createNoteForReferenceLink(title) {
const {note} = await noteCreateService.createNoteWithTypePrompt(this.notePath, {
activate: false,
title: title
});
return note.getBestNotePathString();
}
async updateAttributeList(attributes) {
await this.renderOwnedAttributes(attributes, false);
}
focus() {
this.$editor.trigger('focus');
this.textEditor.model.change( writer => {
const positionAt = writer.createPositionAt(this.textEditor.model.document.getRoot(), 'end');
writer.setSelection(positionAt);
} );
}
entitiesReloadedEvent({loadResults}) {
if (loadResults.getAttributeRows(this.componentId).find(attr => attributeService.isAffecting(attr, this.note))) {
this.refresh();
}
}
}