From b7ccab38ff275f2c6cf67f343f3fc5487e3ea1a7 Mon Sep 17 00:00:00 2001 From: Sauli Anto Date: Thu, 3 Oct 2019 03:47:26 +0300 Subject: [PATCH] Add preview fix --- src/mathui.js | 16 ++++- src/ui/mainformview.js | 15 ++--- src/ui/mathview.js | 37 +--------- src/utils.js | 149 ++++++++++++++++++++++++++++++++--------- theme/mathform.css | 11 ++- 5 files changed, 144 insertions(+), 84 deletions(-) diff --git a/src/mathui.js b/src/mathui.js index aa6ad87d5..09fabf3d2 100644 --- a/src/mathui.js +++ b/src/mathui.js @@ -64,7 +64,7 @@ export default class MathUI extends Plugin { ...this.editor.config.get( 'math' ) }; - const formView = new MainFormView( editor.locale, mathConfig.engine ); + const formView = new MainFormView( editor.locale, mathConfig.engine, mathConfig.enablePreview ); formView.mathInputView.bind( 'value' ).to( mathCommand, 'value' ); formView.displayButtonView.bind( 'isOn' ).to( mathCommand, 'display' ); @@ -106,6 +106,13 @@ export default class MathUI extends Plugin { this._form.mathInputView.select(); } + // Show preview element + const elId = 'math-preview'; + let prewviewEl = document.getElementById( elId );// eslint-disable-line + if ( prewviewEl ) { + prewviewEl.style.display = 'block'; + } + this._form.equation = mathCommand.value || ''; this._form.displayButtonView.isOn = mathCommand.display || false; } @@ -141,6 +148,13 @@ 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 + if ( prewviewEl ) { + prewviewEl.style.display = 'none'; + } + this.editor.editing.view.focus(); } } diff --git a/src/ui/mainformview.js b/src/ui/mainformview.js index f433fcf5f..c75886de2 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 ) { + constructor( locale, engine, previewEnabled ) { super( locale ); const t = locale.t; @@ -33,10 +33,6 @@ export default class MainFormView extends View { // Equation input this.mathInputView = this._createMathInput(); - // Fixme: - // Preview isn't available in katex, because .ck-reset_all * css rule breaks it - this.previewEnabled = engine !== 'katex' || true; - // Display button this.displayButtonView = this._createDisplayButton(); @@ -47,11 +43,12 @@ export default class MainFormView extends View { // Cancel button this.cancelButtonView = this._createButton( t( 'Cancel' ), cancelIcon, 'ck-button-cancel', 'cancel' ); - // Preview label - this.previewLabel = new LabelView( locale ); + this.previewEnabled = previewEnabled; let children = []; if ( this.previewEnabled ) { + // Preview label + this.previewLabel = new LabelView( locale ); this.previewLabel.text = t( 'Equation preview' ); // Math element @@ -65,11 +62,9 @@ export default class MainFormView extends View { this.mathView ]; } else { - this.previewLabel.text = t( 'Equation preview isn\'t available' ); children = [ this.mathInputView, - this.displayButtonView, - this.previewLabel + this.displayButtonView ]; } diff --git a/src/ui/mathview.js b/src/ui/mathview.js index afc75c122..a7baf4726 100644 --- a/src/ui/mathview.js +++ b/src/ui/mathview.js @@ -16,7 +16,7 @@ export default class MathView extends View { } ); this.setTemplate( { - tag: 'iframe', + tag: 'div', attributes: { class: [ 'ck', @@ -29,40 +29,7 @@ export default class MathView extends View { updateMath() { const el = this.element; if ( el ) { - // Fixme - // eslint-disable-next-line - setTimeout( () => { - let docEl = ( el.contentWindow || el.contentDocument ); - if ( docEl.document ) { - docEl = docEl.document; - } - - const headEl = docEl.head; - - // Remove old styles - while ( headEl.hasChildNodes() ) { - headEl.removeChild( headEl.firstChild ); - } - - // Add all MathJax styles - const styles = document.head.getElementsByTagName( 'style' ); // eslint-disable-line - for ( const style of styles ) { - const id = style.getAttribute( 'id' ); - if ( id && id.startsWith( 'MJX' ) ) { - headEl.appendChild( style.cloneNode( true ) ); - } - } - - const links = document.head.getElementsByTagName( 'link' ); // eslint-disable-line - for ( const link of links ) { - headEl.appendChild( link.cloneNode( true ) ); - } - - const bodyEl = docEl.body; - bodyEl.setAttribute( 'style', 'margin-left: 0; margin-right: 0; user-select: none;' ); - - renderEquation( this.value, bodyEl, this.engine, this.display ); - }, 100 ); + renderEquation( this.value, el, this.engine, this.display, true ); } } diff --git a/src/utils.js b/src/utils.js index 865671c87..84372a696 100644 --- a/src/utils.js +++ b/src/utils.js @@ -1,7 +1,8 @@ export const defaultConfig = { engine: 'mathjax', outputType: 'script', - forceOutputType: false + forceOutputType: false, + enablePreview: true }; export function getSelectedMathModelWidget( selection ) { @@ -14,47 +15,30 @@ export function getSelectedMathModelWidget( selection ) { return null; } -export function renderEquation( equation, element, engine = 'katex', display = false ) { +export function renderEquation( equation, element, engine = 'katex', display = false, previewHack = false ) { if ( engine === 'mathjax' && typeof MathJax !== 'undefined' ) { if ( isMathJaxVersion3( MathJax.version ) ) { - const options = MathJax.getMetricsFor( element, display ); - let promiseFunction = undefined; - if ( typeof MathJax.tex2chtmlPromise !== 'undefined' ) { - promiseFunction = MathJax.tex2chtmlPromise; - } else if ( typeof MathJax.tex2svgPromise !== 'undefined' ) { - promiseFunction = MathJax.tex2svgPromise; - } - - if ( typeof promiseFunction !== 'undefined' ) { - promiseFunction( equation, options ).then( node => { - if ( element.firstChild ) { - element.firstChild.replaceWith( node ); - } else { - element.appendChild( node ); + selectRenderMode( element, previewHack, el => { + renderMathJax3( equation, el, display, () => { + if ( previewHack ) { + moveAndScaleElement( element, el ); } - MathJax.startup.document.clear(); - MathJax.startup.document.updateDocument(); } ); - } + } ); } else { - // Fixme: MathJax typesetting cause occasionally math processing error without asynchronous call - // eslint-disable-next-line - setTimeout( () => { - if ( display ) { - element.innerHTML = '\\[' + equation + '\\]'; - } else { - element.innerHTML = '\\(' + equation + '\\)'; - } - MathJax.Hub.Queue( [ 'Typeset', MathJax.Hub, element ] ); // eslint-disable-line + selectRenderMode( element, previewHack, el => { + renderMathJax2( equation, el, display ); } ); } } else if ( engine === 'katex' && typeof katex !== 'undefined' ) { - katex.render( equation, element, { - throwOnError: false, - displayMode: display + selectRenderMode( element, previewHack, el => { + katex.render( equation, el, { + throwOnError: false, + displayMode: display + } ); } ); } else if ( typeof engine === 'function' ) { - engine( equation, element, display ); + engine( equation, element, display, previewHack ); } else { element.innerHTML = equation; // eslint-disable-next-line @@ -62,6 +46,107 @@ export function renderEquation( equation, element, engine = 'katex', display = f } } +function selectRenderMode( element, preview, cb ) { + if ( preview ) { + createPreviewElement( element, prewviewEl => { + cb( prewviewEl ); + } ); + } else { + cb( element ); + } +} + +function renderMathJax3( equation, element, display, after ) { + const options = MathJax.getMetricsFor( element, display ); + let promiseFunction = undefined; + if ( typeof MathJax.tex2chtmlPromise !== 'undefined' ) { + promiseFunction = MathJax.tex2chtmlPromise; + } else if ( typeof MathJax.tex2svgPromise !== 'undefined' ) { + promiseFunction = MathJax.tex2svgPromise; + } + + if ( typeof promiseFunction !== 'undefined' ) { + promiseFunction( equation, options ).then( node => { + if ( element.firstChild ) { + element.firstChild.replaceWith( node ); + } else { + element.appendChild( node ); + } + MathJax.startup.document.clear(); + MathJax.startup.document.updateDocument(); + after(); + } ); + } +} + +function renderMathJax2( equation, element, display ) { + // Fixme: MathJax typesetting cause occasionally math processing error without asynchronous call + // eslint-disable-next-line + setTimeout( () => { + if ( display ) { + element.innerHTML = '\\[' + equation + '\\]'; + } else { + element.innerHTML = '\\(' + equation + '\\)'; + } + MathJax.Hub.Queue( [ 'Typeset', MathJax.Hub, element ] ); // eslint-disable-line + } ); +} + +function createPreviewElement( element, render ) { + const prewviewEl = getPreviewElement( element ); + render( prewviewEl ); + moveAndScaleElement( element, prewviewEl ); +} + +export function getPreviewElement( element ) { + const elId = 'math-preview'; + let prewviewEl = document.getElementById( elId ); // eslint-disable-line + if ( !prewviewEl ) { + prewviewEl = document.createElement( 'div' ); // eslint-disable-line + prewviewEl.setAttribute( 'id', elId ); + document.body.appendChild( prewviewEl ); // eslint-disable-line + + let ticking = false; + + const renderTransformation = () => { + if ( !ticking ) { + // eslint-disable-next-line + window.requestAnimationFrame( () => { + moveElement( element, prewviewEl ); + ticking = false; + } ); + + ticking = true; + } + }; + + // Create scroll listener for following + window.addEventListener( 'resize', renderTransformation ); // eslint-disable-line + window.addEventListener( 'scroll', renderTransformation ); // eslint-disable-line + } + return prewviewEl; +} + +function moveAndScaleElement( parent, element ) { + moveElement( parent, element ); + + // Scale parent element same as preview + const domRect = element.getBoundingClientRect(); + // element.style.width = domRect.width + 'px'; + parent.style.height = domRect.height + 'px'; +} + +function moveElement( parent, element ) { + const domRect = parent.getBoundingClientRect(); + const left = window.scrollX + domRect.left; // eslint-disable-line + const top = window.scrollY + domRect.top; // eslint-disable-line + element.style.position = 'absolute'; + element.style.left = left + 'px'; + element.style.top = top + 'px'; + element.style.zIndex = 'var(--ck-z-modal)'; + element.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'; diff --git a/theme/mathform.css b/theme/mathform.css index b5cff9a7f..38da47323 100644 --- a/theme/mathform.css +++ b/theme/mathform.css @@ -6,12 +6,6 @@ flex-direction: row; flex-wrap: nowrap; - & .ck.ck-math-preview { - /* Todo: calculate content size */ - width: 234px; - height: 10em; - } - @mixin ck-media-phone { flex-wrap: wrap; @@ -32,3 +26,8 @@ } } } + +// Increase toolbar default z-index by one because math preview should be below of it +.ck.ck-toolbar-container { + z-index: calc(var(--ck-z-modal) + 2); +}