@wiris/mathtype-ckeditor5 8.10.0 → 8.11.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/plugin.js CHANGED
@@ -1,8 +1,8 @@
1
1
  // CKEditor imports
2
- import { Plugin } from 'ckeditor5/src/core.js';
3
- import { ButtonView } from 'ckeditor5/src/ui.js';
4
- import { ClickObserver, HtmlDataProcessor, XmlDataProcessor, UpcastWriter } from 'ckeditor5/src/engine.js';
5
- import { Widget, toWidget, viewToModelPositionOutsideModelElement } from 'ckeditor5/src/widget.js';
2
+ import { Plugin } from "ckeditor5/src/core.js";
3
+ import { ButtonView } from "ckeditor5/src/ui.js";
4
+ import { ClickObserver, HtmlDataProcessor, XmlDataProcessor, UpcastWriter } from "ckeditor5/src/engine.js";
5
+ import { Widget, toWidget, viewToModelPositionOutsideModelElement } from "ckeditor5/src/widget.js";
6
6
 
7
7
  // MathType API imports
8
8
  import IntegrationModel from "@wiris/mathtype-html-integration-devkit/src/integrationmodel.js";
@@ -201,7 +201,7 @@ export default class MathType extends Plugin {
201
201
 
202
202
  schema.register("mathml", {
203
203
  inheritAllFrom: "$inlineObject",
204
- allowAttributes: ["formula"],
204
+ allowAttributes: ["formula", "htmlContent"],
205
205
  });
206
206
  }
207
207
 
@@ -304,6 +304,59 @@ export default class MathType extends Plugin {
304
304
  }
305
305
  });
306
306
 
307
+ // Data view -> Model
308
+ editor.data.upcastDispatcher.on("element:img", (evt, data, conversionApi) => {
309
+ const { consumable, writer } = conversionApi;
310
+ const { viewItem } = data;
311
+
312
+ // Only upcast when is wiris formula
313
+ if (viewItem.getClassNames().next().value !== "Wirisformula") {
314
+ return;
315
+ }
316
+
317
+ const mathAttributes = [...viewItem.getAttributes()].map(([key, value]) => ` ${key}="${value}"`).join("");
318
+ let htmlContent = Util.htmlSanitize(`<img${mathAttributes}>`);
319
+
320
+ const modelNode = writer.createElement("mathml", { htmlContent });
321
+
322
+ // Find allowed parent for element that we are going to insert.
323
+ // If current parent does not allow to insert element but one of the ancestors does
324
+ // then split nodes to allowed parent.
325
+ const splitResult = conversionApi.splitToAllowedParent(modelNode, data.modelCursor);
326
+
327
+ // When there is no split result it means that we can't insert element to model tree, so let's skip it.
328
+ if (!splitResult) {
329
+ return;
330
+ }
331
+
332
+ // Insert element on allowed position.
333
+ conversionApi.writer.insert(modelNode, splitResult.position);
334
+
335
+ // Consume appropriate value from consumable values list.
336
+ consumable.consume(viewItem, { name: true });
337
+
338
+ const parts = conversionApi.getSplitParts(modelNode);
339
+
340
+ // Set conversion result range.
341
+ data.modelRange = writer.createRange(
342
+ conversionApi.writer.createPositionBefore(modelNode),
343
+ conversionApi.writer.createPositionAfter(parts[parts.length - 1]),
344
+ );
345
+
346
+ // Now we need to check where the `modelCursor` should be.
347
+ if (splitResult.cursorParent) {
348
+ // If we split parent to insert our element then we want to continue conversion in the new part of the split parent.
349
+ //
350
+ // before: <allowed><notAllowed>foo[]</notAllowed></allowed>
351
+ // after: <allowed><notAllowed>foo</notAllowed><converted></converted><notAllowed>[]</notAllowed></allowed>
352
+
353
+ data.modelCursor = conversionApi.writer.createPositionAt(splitResult.cursorParent, 0);
354
+ } else {
355
+ // Otherwise just continue after inserted element.
356
+ data.modelCursor = data.modelRange.end;
357
+ }
358
+ });
359
+
307
360
  /**
308
361
  * Whether the given view <math> element has a LaTeX annotation element.
309
362
  * @param {*} math
@@ -337,13 +390,31 @@ export default class MathType extends Plugin {
337
390
  function createViewImage(modelItem, { writer: viewWriter }) {
338
391
  const htmlDataProcessor = new HtmlDataProcessor(viewWriter.document);
339
392
 
340
- const mathString = modelItem.getAttribute("formula").replaceAll('ref="<"', 'ref="&lt;"');
341
- const imgHtml = Parser.initParse(mathString, integration.getLanguage());
342
- const imgElement = htmlDataProcessor.toView(imgHtml).getChild(0);
393
+ const formula = modelItem.getAttribute("formula");
394
+ const htmlContent = modelItem.getAttribute("htmlContent");
395
+
396
+ if(!formula && !htmlContent){
397
+ return null;
398
+ }
399
+
400
+ let imgElement = null;
401
+
402
+ if (htmlContent) {
403
+ imgElement = htmlDataProcessor.toView(htmlContent).getChild(0);
404
+ } else if(formula) {
405
+ const mathString = formula.replaceAll('ref="<"', 'ref="&lt;"');
406
+
407
+ const imgHtml = Parser.initParse(mathString, integration.getLanguage());
408
+ imgElement = htmlDataProcessor.toView(imgHtml).getChild(0);
409
+
410
+ // Add HTML element (<img>) to model
411
+ viewWriter.setAttribute("htmlContent", imgHtml, modelItem);
412
+ }
343
413
 
344
414
  /* Although we use the HtmlDataProcessor to obtain the attributes,
345
- we must create a new EmptyElement which is independent of the
346
- DataProcessor being used by this editor instance */
415
+ * we must create a new EmptyElement which is independent of the
416
+ * DataProcessor being used by this editor instance
417
+ */
347
418
  if (imgElement) {
348
419
  return viewWriter.createEmptyElement("img", imgElement.getAttributes(), {
349
420
  renderUnsafeAttributes: ["src"],
@@ -391,7 +462,8 @@ export default class MathType extends Plugin {
391
462
  function createDataString(modelItem, { writer: viewWriter }) {
392
463
  const htmlDataProcessor = new HtmlDataProcessor(viewWriter.document);
393
464
 
394
- let mathString = Parser.endParseSaveMode(modelItem.getAttribute("formula"));
465
+ // Load img element
466
+ let mathString = modelItem.getAttribute("htmlContent");
395
467
 
396
468
  const sourceMathElement = htmlDataProcessor.toView(mathString).getChild(0);
397
469
 
@@ -414,15 +486,17 @@ export default class MathType extends Plugin {
414
486
  "get",
415
487
  (e) => {
416
488
  let output = e.return;
417
- // This line cleans all the semantics stuff, including the handwritten data points and returns the MathML IF there is any.
418
- // For text or latex formulas, it returns the original output.
419
- e.return = MathML.removeSemantics(output, "application/json");
489
+ const parsedResult = Parser.endParse(output);
490
+
491
+ // Cleans all the semantics tag for safexml
492
+ // including the handwritten data points
493
+ e.return = MathML.removeSafeXMLSemantics(parsedResult);
420
494
  },
421
495
  { priority: "low" },
422
496
  );
423
497
 
424
498
  /**
425
- * Hack to transform <math> with LaTeX into $$LaTeX$$ in editor.setData().
499
+ * Hack to transform <math> with LaTeX into $$LaTeX$$ and formula images in editor.setData().
426
500
  */
427
501
  editor.data.on(
428
502
  "set",
@@ -430,24 +504,29 @@ export default class MathType extends Plugin {
430
504
  // Retrieve the data to be set on the CKEditor.
431
505
  let modifiedData = args[0];
432
506
  // Regex to find all mathml formulas.
433
- const regexp = /<math(.*?)<\/math>/gm;
434
-
435
- // Get all MathML formulas and store them in an array.
436
- // Using the conditional operator on data.main because the data parameter has different types depending on:
437
- // editor.data.set can be used directly or by the source editing plugin.
438
- // With the source editor plugin, data is an object with the key `main` which contains the source code string.
439
- // When using the editor.data.set method, the data is a string with the content to be set to the editor.
440
- let formulas = Object.values(modifiedData)[0]
441
- ? [...Object.values(modifiedData)[0].matchAll(regexp)]
442
- : [...modifiedData.matchAll(regexp)];
443
-
444
- // Loop to find LaTeX formulas and replace the MathML for the LaTeX notation.
507
+ const regexp = /(<img\b[^>]*>)|(<math(.*?)<\/math>)/gm;
508
+ const formulas = [];
509
+ let formula;
510
+
511
+ // Both data.set from the source plugin and console command are taken into account as the data received is MathML or an image containing the MathML.
512
+ while ((formula = regexp.exec(modifiedData)) !== null) {
513
+ formulas.push(formula[0]);
514
+ }
515
+
516
+ // Loop to find LaTeX and formula images and replace the MathML for the both.
445
517
  formulas.forEach((formula) => {
446
- let mathml = formula[0];
447
- if (mathml.includes('encoding="LaTeX"')) {
518
+ if (formula.includes('encoding="LaTeX"')) {
448
519
  // LaTeX found.
449
- let latex = "$$$" + Latex.getLatexFromMathML(mathml) + "$$$"; // We add $$$ instead of $$ because the replace function ignores one $.
450
- modifiedData = modifiedData.replace(mathml, latex);
520
+ let latex = "$$$" + Latex.getLatexFromMathML(formula) + "$$$"; // We add $$$ instead of $$ because the replace function ignores one $.
521
+ modifiedData = modifiedData.replace(formula, latex);
522
+ } else if (formula.includes("<img")) {
523
+ // If we found a formula image, we should find MathML data, and then substitute the entire image.
524
+ const regexp = /«math\b[^»]*»(.*?)«\/math»/g;
525
+ const safexml = formula.match(regexp);
526
+ if(safexml !== null) {
527
+ let decodeXML = MathML.safeXmlDecode(safexml[0]);
528
+ modifiedData = modifiedData.replace(formula, decodeXML);
529
+ }
451
530
  }
452
531
  });
453
532