diff --git a/src/mathui.js b/src/mathui.js index 90ec46437..4dfe0b895 100644 --- a/src/mathui.js +++ b/src/mathui.js @@ -2,6 +2,7 @@ import Plugin from '@ckeditor/ckeditor5-core/src/plugin'; import ClickObserver from '@ckeditor/ckeditor5-engine/src/view/observer/clickobserver'; import ContextualBalloon from '@ckeditor/ckeditor5-ui/src/panel/balloon/contextualballoon'; import clickOutsideHandler from '@ckeditor/ckeditor5-ui/src/bindings/clickoutsidehandler'; +import uid from '@ckeditor/ckeditor5-utils/src/uid'; import ButtonView from '@ckeditor/ckeditor5-ui/src/button/buttonview'; import MainFormView from './ui/mainformview'; @@ -27,6 +28,8 @@ export default class MathUI extends Plugin { const editor = this.editor; editor.editing.view.addObserver( ClickObserver ); + this._previewUid = `math-preview-${ uid() }`; + this._form = this._createFormView(); this._balloon = editor.plugins.get( ContextualBalloon ); @@ -61,7 +64,7 @@ export default class MathUI extends Plugin { const mathConfig = Object.assign( defaultConfig, this.editor.config.get( 'math' ) ); - const formView = new MainFormView( editor.locale, mathConfig.engine, mathConfig.enablePreview ); + const formView = new MainFormView( editor.locale, mathConfig.engine, mathConfig.enablePreview, this._previewUid ); formView.mathInputView.bind( 'value' ).to( mathCommand, 'value' ); formView.displayButtonView.bind( 'isOn' ).to( mathCommand, 'display' ); @@ -104,8 +107,7 @@ export default class MathUI extends Plugin { } // Show preview element - const elId = 'math-preview'; - let prewviewEl = document.getElementById( elId ); // eslint-disable-line + let prewviewEl = document.getElementById( this._previewUid ); // eslint-disable-line if ( prewviewEl && this._form.previewEnabled ) { // Force refresh preview this._form.mathView.updateMath(); @@ -147,8 +149,7 @@ export default class MathUI extends Plugin { this._balloon.remove( this._form ); // Hide preview element - const elId = 'math-preview'; - let prewviewEl = document.getElementById( elId );// eslint-disable-line + let prewviewEl = document.getElementById( this._previewUid );// eslint-disable-line if ( prewviewEl ) { prewviewEl.style.display = 'none'; } diff --git a/src/ui/mainformview.js b/src/ui/mainformview.js index 510067567..feb457a70 100644 --- a/src/ui/mainformview.js +++ b/src/ui/mainformview.js @@ -22,7 +22,7 @@ import MathView from './mathview'; import '../../theme/mathform.css'; export default class MainFormView extends View { - constructor( locale, engine, previewEnabled ) { + constructor( locale, engine, previewEnabled, previewUid ) { super( locale ); const t = locale.t; @@ -52,7 +52,7 @@ export default class MainFormView extends View { this.previewLabel.text = t( 'Equation preview' ); // Math element - this.mathView = new MathView( engine, locale ); + this.mathView = new MathView( engine, locale, previewUid ); this.mathView.bind( 'display' ).to( this.displayButtonView, 'isOn' ); children = [ diff --git a/src/ui/mathview.js b/src/ui/mathview.js index 063b05004..81e78b03b 100644 --- a/src/ui/mathview.js +++ b/src/ui/mathview.js @@ -3,10 +3,11 @@ import View from '@ckeditor/ckeditor5-ui/src/view'; import { renderEquation } from '../utils'; export default class MathView extends View { - constructor( engine, locale ) { + constructor( engine, locale, previewUid ) { super( locale ); this.engine = engine; + this.previewUid = previewUid; this.set( 'value', '' ); this.set( 'display', false ); @@ -29,7 +30,7 @@ export default class MathView extends View { } updateMath() { - renderEquation( this.value, this.element, this.engine, this.display, true ); + renderEquation( this.value, this.element, this.engine, this.display, true, this.previewUid ); } render() { diff --git a/src/utils.js b/src/utils.js index 51f504d78..f748d1c8c 100644 --- a/src/utils.js +++ b/src/utils.js @@ -15,10 +15,37 @@ export function getSelectedMathModelWidget( selection ) { return null; } -export function renderEquation( equation, element, engine = 'katex', display = false, preview = false ) { +// Simple MathJax 3 version check +export function isMathJaxVersion3( version ) { + return version && typeof version === 'string' && version.split( '.' ).length === 3 && version.split( '.' )[ 0 ] === '3'; +} + +// Check if equation has delimiters +export function hasDelimiters( text ) { + return text.match( /^(\\\[.*?\\\]|\\\(.*?\\\))$/ ); +} + +// Extract delimiters and figure display mode for the model +export function extractDelimiters( equation ) { + equation = equation.trim(); + + // Remove delimiters (e.g. \( \) or \[ \]) + const hasInlineDelimiters = equation.includes( '\\(' ) && equation.includes( '\\)' ); + const hasDisplayDelimiters = equation.includes( '\\[' ) && equation.includes( '\\]' ); + if ( hasInlineDelimiters || hasDisplayDelimiters ) { + equation = equation.substring( 2, equation.length - 2 ).trim(); + } + + return { + equation, + display: hasDisplayDelimiters + }; +} + +export function renderEquation( equation, element, engine = 'katex', display = false, preview = false, previewUid ) { if ( engine === 'mathjax' && typeof MathJax !== 'undefined' ) { if ( isMathJaxVersion3( MathJax.version ) ) { - selectRenderMode( element, preview, el => { + selectRenderMode( element, preview, previewUid, el => { renderMathJax3( equation, el, display, () => { if ( preview ) { el.style.display = 'block'; @@ -27,7 +54,7 @@ export function renderEquation( equation, element, engine = 'katex', display = f } ); } ); } else { - selectRenderMode( element, preview, el => { + selectRenderMode( element, preview, previewUid, el => { // Fixme: MathJax typesetting cause occasionally math processing error without asynchronous call // eslint-disable-next-line setTimeout( () => { @@ -45,7 +72,7 @@ export function renderEquation( equation, element, engine = 'katex', display = f } ); } } else if ( engine === 'katex' && typeof katex !== 'undefined' ) { - selectRenderMode( element, preview, el => { + selectRenderMode( element, preview, previewUid, el => { katex.render( equation, el, { throwOnError: false, displayMode: display @@ -64,9 +91,9 @@ export function renderEquation( equation, element, engine = 'katex', display = f } } -function selectRenderMode( element, preview, cb ) { +function selectRenderMode( element, preview, previewUid, cb ) { if ( preview ) { - createPreviewElement( element, prewviewEl => { + createPreviewElement( element, previewUid, prewviewEl => { cb( prewviewEl ); } ); } else { @@ -102,17 +129,17 @@ function renderMathJax2( equation, element, display ) { MathJax.Hub.Queue( [ 'Typeset', MathJax.Hub, element ] ); // eslint-disable-line } -function createPreviewElement( element, render ) { - const prewviewEl = getPreviewElement( element ); +function createPreviewElement( element, previewUid, render ) { + const prewviewEl = getPreviewElement( element, previewUid ); render( prewviewEl ); } -export function getPreviewElement( element ) { - const elId = 'math-preview'; - let prewviewEl = document.getElementById( elId ); // eslint-disable-line +function getPreviewElement( element, previewUid ) { + let prewviewEl = document.getElementById( previewUid ); // eslint-disable-line + // Create if not found if ( !prewviewEl ) { prewviewEl = document.createElement( 'div' ); // eslint-disable-line - prewviewEl.setAttribute( 'id', elId ); + prewviewEl.setAttribute( 'id', previewUid ); document.body.appendChild( prewviewEl ); // eslint-disable-line let ticking = false; @@ -156,30 +183,3 @@ function moveElement( parent, child ) { child.style.zIndex = 'var(--ck-z-modal)'; child.style.pointerEvents = 'none'; } - -// Simple MathJax 3 version check -export function isMathJaxVersion3( version ) { - return version && typeof version === 'string' && version.split( '.' ).length === 3 && version.split( '.' )[ 0 ] === '3'; -} - -// Check if equation has delimiters -export function hasDelimiters( text ) { - return text.match( /^(\\\[.*?\\\]|\\\(.*?\\\))$/ ); -} - -// Extract delimiters and figure display mode for the model -export function extractDelimiters( equation ) { - equation = equation.trim(); - - // Remove delimiters (e.g. \( \) or \[ \]) - const hasInlineDelimiters = equation.includes( '\\(' ) && equation.includes( '\\)' ); - const hasDisplayDelimiters = equation.includes( '\\[' ) && equation.includes( '\\]' ); - if ( hasInlineDelimiters || hasDisplayDelimiters ) { - equation = equation.substring( 2, equation.length - 2 ).trim(); - } - - return { - equation, - display: hasDisplayDelimiters - }; -}