soundcite-1.1.2/js/ckeditor5_plugins/soundcite/src/soundciteediting.js
js/ckeditor5_plugins/soundcite/src/soundciteediting.js
import { Plugin } from "ckeditor5/src/core";
import { toWidget, toWidgetEditable } from "ckeditor5/src/widget";
import { Widget } from "ckeditor5/src/widget";
import InsertSoundciteCommand from "./insertsoundcitecommand";
// cSpell:ignore Soundcite insertsoundcitecommand
/**
* CKEditor 5 plugins do not work directly with the DOM. They are defined as
* plugin-specific data models that are then converted to markup that
* is inserted in the DOM.
*
* CKEditor 5 internally interacts with Soundcite as this model:
* <Soundcite url="" start="" end="" plays="" text="">
* </Soundcite>
*
* Which is converted for the browser/user as this markup
* <span class="coundcite" data-url="" data-start="" data-end="" data-plays="">
* // Link Text
* </span>
*
* This file has the logic for defining the Soundcite model, and for how it is
* converted to standard DOM markup.
*/
export default class SoundciteEditing extends Plugin {
static get requires() {
return [Widget];
}
init() {
this._defineSchema();
this._defineConverters();
this.editor.commands.add(
"insertSoundcite",
new InsertSoundciteCommand(this.editor)
);
}
/*
* This registers the structure that will be seen by CKEditor 5 as
* <Soundcite>
* </Soundcite>
*
* The logic in _defineConverters() will determine how this is converted to
* markup.
*/
_defineSchema() {
// Schemas are registered via the central `editor` object.
const schema = this.editor.model.schema;
schema.register("Soundcite", {
// Behaves like a self-contained object (e.g. an image).
isObject: true,
// Allow in places where other blocks are allowed (e.g. directly in the root).
isInline: true,
allowWhere: "$text",
allowAttributes: [
"data-url",
"data-start",
"data-end",
"data-plays",
"text",
],
});
schema.addChildCheck((context, childDefinition) => {
// Disallow Soundcite inside Soundcite.
if (
context.endsWith("Soundcite") &&
childDefinition.name === "Soundcite"
) {
return false;
}
});
}
/**
* Converters determine how CKEditor 5 models are converted into markup and
* vice-versa.
*/
_defineConverters() {
// Converters are registered via the central editor object.
const { conversion } = this.editor;
// Upcast Converters: determine how existing HTML is interpreted by the
// editor. These trigger when an editor instance loads.
//
// If <span class="soundcite"> is present in the existing markup
// processed by CKEditor, then CKEditor recognizes and loads it as a
// <Soundcite> model.
conversion.for("upcast").elementToElement({
model: (viewElement, { writer: modelWriter }) => {
return modelWriter.createElement("Soundcite", {
"data-url": viewElement.getAttribute("data-url"),
"data-start": viewElement.getAttribute("data-start"),
"data-end": viewElement.getAttribute("data-end"),
"data-plays": viewElement.getAttribute("data-plays"),
text: viewElement.getChild(0).data,
});
},
view: {
name: "span",
classes: "soundcite",
},
});
conversion.for("downcast").elementToElement({
model: "Soundcite",
view: (modelElement, { writer: viewWriter }) => {
const span = viewWriter.createContainerElement("span", {
class: "soundcite",
"data-url": modelElement.getAttribute("data-url"),
"data-start": modelElement.getAttribute("data-start"),
"data-end": modelElement.getAttribute("data-end"),
"data-plays": modelElement.getAttribute("data-plays"),
});
const text = viewWriter.createText(modelElement.getAttribute("text"));
viewWriter.insert(viewWriter.createPositionAt(span, 0), text);
return toWidget(span, viewWriter, { label: "Soundcite widget" });
},
});
}
}
