diff --git a/devblog/2021-03-01-RichTextDataProcessor-Configuration-API.md b/devblog/2021-03-01-RichTextDataProcessor-Configuration-API.md deleted file mode 100644 index 6815de98d5..0000000000 --- a/devblog/2021-03-01-RichTextDataProcessor-Configuration-API.md +++ /dev/null @@ -1,245 +0,0 @@ -RichText Data Processor: Configuration API -================================================================================ - -We are just at designing a central aspect for enabling CKEditor 5 for CoreMedia -Studio: Adding a plugin for data processing CoreMedia RichText 1.0 from view -to data and vice versa. - -The first sketch exists for mapping elements from view to data with a similar -design as it was for CKEditor 4. As for example CoreMedia RichText 1.0 does -not support elements `
` with a well-known class: - -```javascript -{ - h1: (element) => { - element.name = "p"; - element.attributes["class"] = "p--heading-1"; - } -} -``` - -CKEditor 4: Misbehavior of CoreMedia Data Processing --------------------------------------------------------------------------------- - -In CKEditor 4 we ignored modeling the reverse-mapping. Thus, even within -CKEditor HTML a previously entered `
`. - -This caused no obvious problem at first glance, as actions to insert headings -were custom actions always adding `
` when a heading -has been selected during formatting. - -The problem only became obvious on paste from external sources: CKEditor HTML -contained `
` element. - -CKEditor 5: Sketching Requirements --------------------------------------------------------------------------------- - -### Req. 1: Use CKEditor Toolbars and Actions - -The central requirement is, that CKEditor toolbars and actions, which come by -default, are supported. Thus, if the editor has an action to set the current -line to heading level 1, no customization should be required to the action: It -should just simply add `
` comes with a reverse mapping, so that -`
` becomes `
`. - -### Req. 5: Configurable Extension Point - -This may be an optional requirement, at least not priority one: It may be nice, -if it would be possible to access the configuration as well from mapping rules. -This way, we can access for example the strictness setting. - -### Req. 6: Easy Bijective Mapping Overview - -In traditional CKEditor 4 implementation, we would have two mappings kept -separate. One for the `toData` direction (HTML to RichText) and one for the -`toView` direction (RichText to HTML). - -This is easily error-prone, as when mapping one way you should not forget -about mentioning the reverse mapping as well. - -The idea is to have both mappings close together, so that you can easily spot -_a missing branch_. - -### Req. 7: One Way Mapping - -It should be possible proving one way mapping. This most likely is especially -about adjusting elements, which cause invalid CoreMedia RichText 1.0. - -### Req. 8: Similarity to CKEditor 4 Data Processing - -At the time of writing, this is already fulfilled, but it may break, if we -decide to refactor the approach. To be able to see, if the mapping in CKEditor 5 -is similar to CKEditor 4 mapping, the mapping configuration should be similar. - -### Req. 9: Fast - -Any solution we find needs to be fast on repeating calls. It is expected, that -for example every 5 seconds the data need to be stored back to the server, and -thus transformed. An editor must not experience noticeable difference while -typing in a 10-character text from a 20,000-character text (roughly guessed -number). - -Configuration Sketch --------------------------------------------------------------------------------- - -### Default Mapping - -By default, the mapping may look as follows: - -```javascript -{ - elements: { - h1: { - toData: (element) => { - element.name = "p"; - element.attributes["class"] = "p--heading-1"; - }, - toView: { - p: (element) => { - if (element.attributes["class"] === "p--heading-1") { - element.name = "h1"; - delete element.attributes["class"]; - } - } - } - }, - // Similar mapping as above. Challenge: Two rules to handle p elements. - h2: { - toData: (element) => { - element.name = "p"; - element.attributes["class"] = "p--heading-2"; - }, - toView: { - p: (element) => { - if (element.attributes["class"] === "p--heading-2") { - element.name = "h2"; - delete element.attributes["class"]; - } - }, - }, - }, - // The One-Way Direction (Here just removing an invalid element we want to ignore). - "invalid:element": () => false, - // Especially for configuration access, the editor instance is handed over - // as optional argument: - "$$": (element, editor) => { - // now do processing according to given strictness for example. - }, - } -} -``` - -This is still similar to the CKEditor 4 configuration. It also has the toView -mapping close to the toData mapping. The idea is, that on initialization phase -we dynamically split the toData and toView mapping into two, to have a fast -lookup table during processing. In the end, we will have something like this: - -toData: - -```javascript -{ - elements: { - h1: (element) => { - element.name = "p"; - element.attributes["class"] = "p--heading-1"; - }, - h2: (element) => { - element.name = "p"; - element.attributes["class"] = "p--heading-2"; - }, - "invalid:element": () => false, - "$$": (element, editor) => { - // ... - }, - } -} -``` - -toView: - -```javascript -{ - elements: { - p: [ - (element) => { - if (element.attributes["class"] === "p--heading-1") { - element.name = "h1"; - delete element.attributes["class"]; - } - }, - (element) => { - if (element.attributes["class"] === "p--heading-2") { - element.name = "h2"; - delete element.attributes["class"]; - } - }, - ] - } -} -``` - -Now, we can just check for each element name in the map. For the toView part -we need an array, as both rules may apply to `
`.
-
-### Configuration and Extension
-
-Here is an example, how we may add the font-color mapping to a span. It takes
-into account, that we may already have a mapping for spans in the default
-configuration. If we don't, we will override the behavior, which may be
-another desired use-case.
-
-```javascript
-ClassicEditor.create(document.querySelector( '.editor' ), {
- plugins: [
- // ...
- CoreMediaRichText
- ],
- "coremedia:richtext": {
- strictness: Strictness.STRICT,
- rules: {
- // editor: Just showing, how to pass the editor if we require for example
- // configuration access.
- span: (element, super, editor) => {
- // Unsure, if super is a reserved word. If yes, just choose another name.
- super.apply(this, arguments);
- // Very simplistic approach. Just for demonstration purpose.
- if (element.attributes["style"]?.contains("color:white")) {
- element.attributes["class"]=`${element.attributes["class"]}`;
- }
- };
- },
- },
-});
-```
-
-Having this, we may also change our mind on how to handle the parsed `toView`
-mapping. Instead, upon parsing, we just may override the existing mapping
-with a new one, taking the old mapping into account.
diff --git a/devblog/2021-03-02-Extensible-Configuration-API.md b/devblog/2021-03-02-Extensible-Configuration-API.md
deleted file mode 100644
index 885498361d..0000000000
--- a/devblog/2021-03-02-Extensible-Configuration-API.md
+++ /dev/null
@@ -1,143 +0,0 @@
-Extensible Configuration API
-================================================================================
-
-In the [previous post](2021-03-01-RichTextDataProcessor-Configuration-API.md)
-we sketched a possible scenario how to provide a configurable filtering API.
-One challenge was, that we need to be able to customize the filtering within
-the CKEditor configuration section. Customization may include replacing,
-extending or even (for whatever reason) disabling an existing filter.
-
-Yet, we want especially at CKEditor configuration level have an easy to write
-configuration section. One of the challenges are the parameters to pass. When
-we are _between_ the configuration hierarchy, we don't want to have to write
-bloated parameter lists, where 80% are just forwarded without ever using them.
-We need some _pick, what you want_ approach.
-
-The idea is, to pass an object to the filter functions. Which seems to be
-heavy-weight at first glance, provides much flexibility … and can easily be
-extended for future scenarios (like adding additional parameters).
-
-Example Hierarchy Processing
---------------------------------------------------------------------------------
-
-Here is a rough example, which you can try at
-[TS Playground][example-1]:
-
-```typescript
-type EditorConfig = { [key:string]: string };
-// Used for named parameters.
-interface MapFnParameters {
- el: HTMLElement,
- parent?: MapFn,
- cfg?: EditorConfig,
-}
-type MapFn = (args: MapFnParameters) => void;
-type Config = { [key:string]: MapFn };
-
-const element: HTMLElement = document.createElement("h1");
-element.classList.add("initial");
-
-// Some additional configuration options in CKEditor Configuration.
-const editorConfig: EditorConfig = {
- "class": "config",
-};
-
-const defaultConfig: Config = {
- h1: (args: MapFnParameters) => {
- args.el.classList.add("default");
- args.cfg && args.cfg["class"] && args.el.classList.add(args.cfg["class"]);
- },
-};
-
-const customConfig: Config = {
- h1: (args: MapFnParameters) => {
- args.parent && args.parent(args);
- args.el.classList.add("custom");
- },
-};
-
-const mapFn: MapFn | undefined = customConfig[element.localName];
-
-mapFn && mapFn({
- el: element,
- parent: defaultConfig[element.localName],
- cfg: editorConfig
-});
-
-console.log({className: element.className});
-```
-
-Example List Processing
---------------------------------------------------------------------------------
-
-An additional requirement from
-[previous post](2021-03-01-RichTextDataProcessor-Configuration-API.md) is the
-ability to merge lists of processing instructions. This is especially relevant
-for the `toView` section, where we may have a bunch of rules for processing
-a paragraph, which represents a heading at certain levels.
-
-Extending the example above, now having two custom configs to be merged, it may
-look like this [see TS PlayGround][example-2]:
-
-```typescript
-function mergeConfigs(...config: Config[]): Config {
- const result: Config = {};
- config.forEach((c) => {
- Object.keys(c).forEach((k) => {
- if (result.hasOwnProperty(k)) {
- const previousFn: MapFn = result[k];
- const thisFn: MapFn = c[k];
- result[k] = (args: MapFnParameters) => {
- previousFn(args);
- thisFn(args);
- };
- } else {
- result[k] = c[k];
- }
- });
- });
- return result;
-}
-
-const customConfig1: Config = {
- h1: (args: MapFnParameters) => {
- args.parent && args.parent(args);
- args.el.classList.add("custom1");
- },
-};
-const customConfig2: Config = {
- h1: (args: MapFnParameters) => {
- args.parent && args.parent(args);
- args.el.classList.add("custom2");
- },
-};
-const customConfig: Config = mergeConfigs(customConfig1, customConfig2);
-```
-
-Where is the Difference?
---------------------------------------------------------------------------------
-
-The two approaches (hierarchy and list based) are similar. This raises the
-question, if there is a common base, or if one could be used to represent the
-other.
-
-The main difference is, that for the hierarchical approach we want to be able
-to completely override a default behavior. This may be a bugfix, or just
-(for the headings example) another class we want to assign instead of
-`p--heading-1`.
-
-To use the list approach also for hierarchical approach, we would require some
-explicit order to define, a priority. If a priority is higher, this processing
-will come last, and thus, enable overriding the original behavior.
-
-But, this comes at cost:
-
-* You need to carefully maintain and document the order attributes, so that
- everyone knows what a higher or lower priority is.
-
-* The continuous processing costs may increase without need, as the super-call
- would always be executed, even if a later instructions completely overrides
- the behavior.
-
-[example-1]: