mirror of
https://github.com/TriliumNext/Notes.git
synced 2025-10-26 01:21:34 +08:00
Remove JavaScript files
This commit is contained in:
parent
9765abc0bd
commit
2825393538
@ -1,49 +0,0 @@
|
|||||||
import { Plugin } from 'ckeditor5/src/core';
|
|
||||||
import { global, logWarning } from 'ckeditor5/src/utils';
|
|
||||||
import blockAutoformatEditing from '@ckeditor/ckeditor5-autoformat/src/blockautoformatediting';
|
|
||||||
import Math from './math';
|
|
||||||
|
|
||||||
export default class AutoformatMath extends Plugin {
|
|
||||||
static get requires() {
|
|
||||||
return [ Math, 'Autoformat' ];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @inheritDoc
|
|
||||||
*/
|
|
||||||
init() {
|
|
||||||
const editor = this.editor;
|
|
||||||
|
|
||||||
if ( !editor.plugins.has( 'Math' ) ) {
|
|
||||||
logWarning( 'autoformat-math-feature-missing', editor );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
afterInit() {
|
|
||||||
const editor = this.editor;
|
|
||||||
const command = editor.commands.get( 'math' );
|
|
||||||
|
|
||||||
if ( command ) {
|
|
||||||
const callback = () => {
|
|
||||||
if ( !command.isEnabled ) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
command.display = true;
|
|
||||||
|
|
||||||
// Wait until selection is removed.
|
|
||||||
global.window.setTimeout(
|
|
||||||
() => editor.plugins.get( 'MathUI' )._showUI(),
|
|
||||||
50
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
blockAutoformatEditing( editor, this, /^\$\$$/, callback );
|
|
||||||
blockAutoformatEditing( editor, this, /^\\\[$/, callback );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static get pluginName() {
|
|
||||||
return 'AutoformatMath';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
120
src/automath.js
120
src/automath.js
@ -1,120 +0,0 @@
|
|||||||
import { Clipboard } from 'ckeditor5/src/clipboard';
|
|
||||||
import { Plugin } from 'ckeditor5/src/core';
|
|
||||||
import { LivePosition, LiveRange } from 'ckeditor5/src/engine';
|
|
||||||
import { Undo } from 'ckeditor5/src/undo';
|
|
||||||
import { global } from 'ckeditor5/src/utils';
|
|
||||||
import { extractDelimiters, hasDelimiters, delimitersCounts } from './utils';
|
|
||||||
|
|
||||||
export default class AutoMath extends Plugin {
|
|
||||||
static get requires() {
|
|
||||||
return [ Clipboard, Undo ];
|
|
||||||
}
|
|
||||||
|
|
||||||
static get pluginName() {
|
|
||||||
return 'AutoMath';
|
|
||||||
}
|
|
||||||
|
|
||||||
constructor( editor ) {
|
|
||||||
super( editor );
|
|
||||||
|
|
||||||
this._timeoutId = null;
|
|
||||||
|
|
||||||
this._positionToInsert = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
init() {
|
|
||||||
const editor = this.editor;
|
|
||||||
const modelDocument = editor.model.document;
|
|
||||||
|
|
||||||
this.listenTo( editor.plugins.get( Clipboard ), 'inputTransformation', () => {
|
|
||||||
const firstRange = modelDocument.selection.getFirstRange();
|
|
||||||
|
|
||||||
const leftLivePosition = LivePosition.fromPosition( firstRange.start );
|
|
||||||
leftLivePosition.stickiness = 'toPrevious';
|
|
||||||
|
|
||||||
const rightLivePosition = LivePosition.fromPosition( firstRange.end );
|
|
||||||
rightLivePosition.stickiness = 'toNext';
|
|
||||||
|
|
||||||
modelDocument.once( 'change:data', () => {
|
|
||||||
this._mathBetweenPositions( leftLivePosition, rightLivePosition );
|
|
||||||
|
|
||||||
leftLivePosition.detach();
|
|
||||||
rightLivePosition.detach();
|
|
||||||
}, { priority: 'high' } );
|
|
||||||
} );
|
|
||||||
|
|
||||||
editor.commands.get( 'undo' ).on( 'execute', () => {
|
|
||||||
if ( this._timeoutId ) {
|
|
||||||
global.window.clearTimeout( this._timeoutId );
|
|
||||||
this._positionToInsert.detach();
|
|
||||||
|
|
||||||
this._timeoutId = null;
|
|
||||||
this._positionToInsert = null;
|
|
||||||
}
|
|
||||||
}, { priority: 'high' } );
|
|
||||||
}
|
|
||||||
|
|
||||||
_mathBetweenPositions( leftPosition, rightPosition ) {
|
|
||||||
const editor = this.editor;
|
|
||||||
|
|
||||||
const mathConfig = this.editor.config.get( 'math' );
|
|
||||||
|
|
||||||
const equationRange = new LiveRange( leftPosition, rightPosition );
|
|
||||||
const walker = equationRange.getWalker( { ignoreElementEnd: true } );
|
|
||||||
|
|
||||||
let text = '';
|
|
||||||
|
|
||||||
// Get equation text
|
|
||||||
for ( const node of walker ) {
|
|
||||||
if ( node.item.is( '$textProxy' ) ) {
|
|
||||||
text += node.item.data;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
text = text.trim();
|
|
||||||
|
|
||||||
// Skip if don't have delimiters
|
|
||||||
if ( !hasDelimiters( text ) || delimitersCounts( text ) !== 2 ) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const mathCommand = editor.commands.get( 'math' );
|
|
||||||
|
|
||||||
// Do not anything if math element cannot be inserted at the current position
|
|
||||||
if ( !mathCommand.isEnabled ) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this._positionToInsert = LivePosition.fromPosition( leftPosition );
|
|
||||||
|
|
||||||
// With timeout user can undo conversation if want use plain text
|
|
||||||
this._timeoutId = global.window.setTimeout( () => {
|
|
||||||
editor.model.change( writer => {
|
|
||||||
this._timeoutId = null;
|
|
||||||
|
|
||||||
writer.remove( equationRange );
|
|
||||||
|
|
||||||
let insertPosition;
|
|
||||||
|
|
||||||
// Check if position where the math element should be inserted is still valid.
|
|
||||||
if ( this._positionToInsert.root.rootName !== '$graveyard' ) {
|
|
||||||
insertPosition = this._positionToInsert;
|
|
||||||
}
|
|
||||||
|
|
||||||
editor.model.change( innerWriter => {
|
|
||||||
const params = Object.assign( extractDelimiters( text ), {
|
|
||||||
type: mathConfig.outputType
|
|
||||||
} );
|
|
||||||
const mathElement = innerWriter.createElement( params.display ? 'mathtex-display' : 'mathtex-inline', params );
|
|
||||||
|
|
||||||
editor.model.insertContent( mathElement, insertPosition );
|
|
||||||
|
|
||||||
innerWriter.setSelection( mathElement, 'on' );
|
|
||||||
} );
|
|
||||||
|
|
||||||
this._positionToInsert.detach();
|
|
||||||
this._positionToInsert = null;
|
|
||||||
} );
|
|
||||||
}, 100 );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,6 +0,0 @@
|
|||||||
/**
|
|
||||||
* @module math
|
|
||||||
*/
|
|
||||||
|
|
||||||
export { default as Math } from './math';
|
|
||||||
export { default as AutoformatMath } from './autoformatmath';
|
|
||||||
16
src/math.js
16
src/math.js
@ -1,16 +0,0 @@
|
|||||||
import { Plugin } from 'ckeditor5/src/core';
|
|
||||||
import { Widget } from 'ckeditor5/src/widget';
|
|
||||||
|
|
||||||
import MathUI from './mathui';
|
|
||||||
import MathEditing from './mathediting';
|
|
||||||
import AutoMath from './automath';
|
|
||||||
|
|
||||||
export default class Math extends Plugin {
|
|
||||||
static get requires() {
|
|
||||||
return [ MathEditing, MathUI, AutoMath, Widget ];
|
|
||||||
}
|
|
||||||
|
|
||||||
static get pluginName() {
|
|
||||||
return 'Math';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,41 +0,0 @@
|
|||||||
import { Command } from 'ckeditor5/src/core';
|
|
||||||
import { getSelectedMathModelWidget } from './utils';
|
|
||||||
|
|
||||||
export default class MathCommand extends Command {
|
|
||||||
execute( equation, display, outputType, forceOutputType ) {
|
|
||||||
const model = this.editor.model;
|
|
||||||
const selection = model.document.selection;
|
|
||||||
const selectedElement = selection.getSelectedElement();
|
|
||||||
|
|
||||||
model.change( writer => {
|
|
||||||
let mathtex;
|
|
||||||
if ( selectedElement && ( selectedElement.is( 'element', 'mathtex-inline' ) ||
|
|
||||||
selectedElement.is( 'element', 'mathtex-display' ) ) ) {
|
|
||||||
// Update selected element
|
|
||||||
const typeAttr = selectedElement.getAttribute( 'type' );
|
|
||||||
|
|
||||||
// Use already set type if found and is not forced
|
|
||||||
const type = forceOutputType ? outputType : typeAttr || outputType;
|
|
||||||
|
|
||||||
mathtex = writer.createElement( display ? 'mathtex-display' : 'mathtex-inline', { equation, type, display } );
|
|
||||||
} else {
|
|
||||||
// Create new model element
|
|
||||||
mathtex = writer.createElement( display ? 'mathtex-display' : 'mathtex-inline', { equation, type: outputType, display } );
|
|
||||||
}
|
|
||||||
model.insertContent( mathtex );
|
|
||||||
} );
|
|
||||||
}
|
|
||||||
|
|
||||||
refresh() {
|
|
||||||
const model = this.editor.model;
|
|
||||||
const selection = model.document.selection;
|
|
||||||
const selectedElement = selection.getSelectedElement();
|
|
||||||
|
|
||||||
this.isEnabled = selectedElement === null || ( selectedElement.is( 'element', 'mathtex-inline' ) ||
|
|
||||||
selectedElement.is( 'element', 'mathtex-display' ) );
|
|
||||||
|
|
||||||
const selectedEquation = getSelectedMathModelWidget( selection );
|
|
||||||
this.value = selectedEquation ? selectedEquation.getAttribute( 'equation' ) : null;
|
|
||||||
this.display = selectedEquation ? selectedEquation.getAttribute( 'display' ) : null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,214 +0,0 @@
|
|||||||
import MathCommand from './mathcommand';
|
|
||||||
import { Plugin } from 'ckeditor5/src/core';
|
|
||||||
import { toWidget, Widget, viewToModelPositionOutsideModelElement } from 'ckeditor5/src/widget';
|
|
||||||
import { renderEquation, extractDelimiters } from './utils';
|
|
||||||
|
|
||||||
export default class MathEditing extends Plugin {
|
|
||||||
static get requires() {
|
|
||||||
return [ Widget ];
|
|
||||||
}
|
|
||||||
|
|
||||||
static get pluginName() {
|
|
||||||
return 'MathEditing';
|
|
||||||
}
|
|
||||||
|
|
||||||
constructor( editor ) {
|
|
||||||
super( editor );
|
|
||||||
editor.config.define( 'math', {
|
|
||||||
engine: 'mathjax',
|
|
||||||
outputType: 'script',
|
|
||||||
className: 'math-tex',
|
|
||||||
forceOutputType: false,
|
|
||||||
enablePreview: true,
|
|
||||||
previewClassName: [],
|
|
||||||
popupClassName: [],
|
|
||||||
katexRenderOptions: {}
|
|
||||||
} );
|
|
||||||
}
|
|
||||||
|
|
||||||
init() {
|
|
||||||
const editor = this.editor;
|
|
||||||
editor.commands.add( 'math', new MathCommand( editor ) );
|
|
||||||
|
|
||||||
this._defineSchema();
|
|
||||||
this._defineConverters();
|
|
||||||
|
|
||||||
editor.editing.mapper.on(
|
|
||||||
'viewToModelPosition',
|
|
||||||
viewToModelPositionOutsideModelElement( editor.model, viewElement => viewElement.hasClass( 'math' ) )
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
_defineSchema() {
|
|
||||||
const schema = this.editor.model.schema;
|
|
||||||
schema.register( 'mathtex-inline', {
|
|
||||||
allowWhere: '$text',
|
|
||||||
isInline: true,
|
|
||||||
isObject: true,
|
|
||||||
allowAttributes: [ 'equation', 'type', 'display' ]
|
|
||||||
} );
|
|
||||||
|
|
||||||
schema.register( 'mathtex-display', {
|
|
||||||
allowWhere: '$block',
|
|
||||||
isInline: false,
|
|
||||||
isObject: true,
|
|
||||||
allowAttributes: [ 'equation', 'type', 'display' ]
|
|
||||||
} );
|
|
||||||
}
|
|
||||||
|
|
||||||
_defineConverters() {
|
|
||||||
const conversion = this.editor.conversion;
|
|
||||||
const mathConfig = this.editor.config.get( 'math' );
|
|
||||||
|
|
||||||
// View -> Model
|
|
||||||
conversion.for( 'upcast' )
|
|
||||||
// MathJax inline way (e.g. <script type="math/tex">\sqrt{\frac{a}{b}}</script>)
|
|
||||||
.elementToElement( {
|
|
||||||
view: {
|
|
||||||
name: 'script',
|
|
||||||
attributes: {
|
|
||||||
type: 'math/tex'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
model: ( viewElement, { writer } ) => {
|
|
||||||
const equation = viewElement.getChild( 0 ).data.trim();
|
|
||||||
return writer.createElement( 'mathtex-inline', {
|
|
||||||
equation,
|
|
||||||
type: mathConfig.forceOutputType ? mathConfig.outputType : 'script',
|
|
||||||
display: false
|
|
||||||
} );
|
|
||||||
}
|
|
||||||
} )
|
|
||||||
// MathJax display way (e.g. <script type="math/tex; mode=display">\sqrt{\frac{a}{b}}</script>)
|
|
||||||
.elementToElement( {
|
|
||||||
view: {
|
|
||||||
name: 'script',
|
|
||||||
attributes: {
|
|
||||||
type: 'math/tex; mode=display'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
model: ( viewElement, { writer } ) => {
|
|
||||||
const equation = viewElement.getChild( 0 ).data.trim();
|
|
||||||
return writer.createElement( 'mathtex-display', {
|
|
||||||
equation,
|
|
||||||
type: mathConfig.forceOutputType ? mathConfig.outputType : 'script',
|
|
||||||
display: true
|
|
||||||
} );
|
|
||||||
}
|
|
||||||
} )
|
|
||||||
// CKEditor 4 way (e.g. <span class="math-tex">\( \sqrt{\frac{a}{b}} \)</span>)
|
|
||||||
.elementToElement( {
|
|
||||||
view: {
|
|
||||||
name: 'span',
|
|
||||||
classes: [ mathConfig.className ]
|
|
||||||
},
|
|
||||||
model: ( viewElement, { writer } ) => {
|
|
||||||
const equation = viewElement.getChild( 0 ).data.trim();
|
|
||||||
|
|
||||||
const params = Object.assign( extractDelimiters( equation ), {
|
|
||||||
type: mathConfig.forceOutputType ? mathConfig.outputType : 'span'
|
|
||||||
} );
|
|
||||||
|
|
||||||
return writer.createElement( params.display ? 'mathtex-display' : 'mathtex-inline', params );
|
|
||||||
}
|
|
||||||
} )
|
|
||||||
// KaTeX from Quill: https://github.com/quilljs/quill/blob/develop/formats/formula.js
|
|
||||||
.elementToElement( {
|
|
||||||
view: {
|
|
||||||
name: 'span',
|
|
||||||
classes: [ 'ql-formula' ]
|
|
||||||
},
|
|
||||||
model: ( viewElement, { writer } ) => {
|
|
||||||
const equation = viewElement.getAttribute( 'data-value' ).trim();
|
|
||||||
return writer.createElement( 'mathtex-inline', {
|
|
||||||
equation,
|
|
||||||
type: mathConfig.forceOutputType ? mathConfig.outputType : 'script',
|
|
||||||
display: false
|
|
||||||
} );
|
|
||||||
}
|
|
||||||
} );
|
|
||||||
|
|
||||||
// Model -> View (element)
|
|
||||||
conversion.for( 'editingDowncast' )
|
|
||||||
.elementToElement( {
|
|
||||||
model: 'mathtex-inline',
|
|
||||||
view: ( modelItem, { writer } ) => {
|
|
||||||
const widgetElement = createMathtexEditingView( modelItem, writer );
|
|
||||||
return toWidget( widgetElement, writer, 'span' );
|
|
||||||
}
|
|
||||||
} ).elementToElement( {
|
|
||||||
model: 'mathtex-display',
|
|
||||||
view: ( modelItem, { writer } ) => {
|
|
||||||
const widgetElement = createMathtexEditingView( modelItem, writer );
|
|
||||||
return toWidget( widgetElement, writer, 'div' );
|
|
||||||
}
|
|
||||||
} );
|
|
||||||
|
|
||||||
// Model -> Data
|
|
||||||
conversion.for( 'dataDowncast' )
|
|
||||||
.elementToElement( {
|
|
||||||
model: 'mathtex-inline',
|
|
||||||
view: createMathtexView
|
|
||||||
} )
|
|
||||||
.elementToElement( {
|
|
||||||
model: 'mathtex-display',
|
|
||||||
view: createMathtexView
|
|
||||||
} );
|
|
||||||
|
|
||||||
// Create view for editor
|
|
||||||
function createMathtexEditingView( modelItem, writer ) {
|
|
||||||
const equation = modelItem.getAttribute( 'equation' );
|
|
||||||
const display = modelItem.getAttribute( 'display' );
|
|
||||||
|
|
||||||
const styles = 'user-select: none; ' + ( display ? '' : 'display: inline-block;' );
|
|
||||||
const classes = 'ck-math-tex ' + ( display ? 'ck-math-tex-display' : 'ck-math-tex-inline' );
|
|
||||||
|
|
||||||
const mathtexView = writer.createContainerElement( display ? 'div' : 'span', {
|
|
||||||
style: styles,
|
|
||||||
class: classes
|
|
||||||
} );
|
|
||||||
|
|
||||||
const uiElement = writer.createUIElement( 'div', null, function( domDocument ) {
|
|
||||||
const domElement = this.toDomElement( domDocument );
|
|
||||||
|
|
||||||
renderEquation( equation, domElement, mathConfig.engine, mathConfig.lazyLoad, display, false, mathConfig.previewClassName,
|
|
||||||
null, mathConfig.katexRenderOptions );
|
|
||||||
|
|
||||||
return domElement;
|
|
||||||
} );
|
|
||||||
|
|
||||||
writer.insert( writer.createPositionAt( mathtexView, 0 ), uiElement );
|
|
||||||
|
|
||||||
return mathtexView;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create view for data
|
|
||||||
function createMathtexView( modelItem, { writer } ) {
|
|
||||||
const equation = modelItem.getAttribute( 'equation' );
|
|
||||||
const type = modelItem.getAttribute( 'type' );
|
|
||||||
const display = modelItem.getAttribute( 'display' );
|
|
||||||
|
|
||||||
if ( type === 'span' ) {
|
|
||||||
const mathtexView = writer.createContainerElement( 'span', {
|
|
||||||
class: mathConfig.className
|
|
||||||
} );
|
|
||||||
|
|
||||||
if ( display ) {
|
|
||||||
writer.insert( writer.createPositionAt( mathtexView, 0 ), writer.createText( '\\[' + equation + '\\]' ) );
|
|
||||||
} else {
|
|
||||||
writer.insert( writer.createPositionAt( mathtexView, 0 ), writer.createText( '\\(' + equation + '\\)' ) );
|
|
||||||
}
|
|
||||||
|
|
||||||
return mathtexView;
|
|
||||||
} else {
|
|
||||||
const mathtexView = writer.createContainerElement( 'script', {
|
|
||||||
type: display ? 'math/tex; mode=display' : 'math/tex'
|
|
||||||
} );
|
|
||||||
|
|
||||||
writer.insert( writer.createPositionAt( mathtexView, 0 ), writer.createText( equation ) );
|
|
||||||
|
|
||||||
return mathtexView;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
246
src/mathui.js
246
src/mathui.js
@ -1,246 +0,0 @@
|
|||||||
import MathEditing from './mathediting';
|
|
||||||
import MainFormView from './ui/mainformview';
|
|
||||||
import mathIcon from '../theme/icons/math.svg';
|
|
||||||
import { Plugin } from 'ckeditor5/src/core';
|
|
||||||
import { ClickObserver } from 'ckeditor5/src/engine';
|
|
||||||
import { ButtonView, ContextualBalloon, clickOutsideHandler } from 'ckeditor5/src/ui';
|
|
||||||
import { global, uid } from 'ckeditor5/src/utils';
|
|
||||||
import { getBalloonPositionData } from './utils';
|
|
||||||
|
|
||||||
const mathKeystroke = 'Ctrl+M';
|
|
||||||
|
|
||||||
export default class MathUI extends Plugin {
|
|
||||||
static get requires() {
|
|
||||||
return [ ContextualBalloon, MathEditing ];
|
|
||||||
}
|
|
||||||
|
|
||||||
static get pluginName() {
|
|
||||||
return 'MathUI';
|
|
||||||
}
|
|
||||||
|
|
||||||
init() {
|
|
||||||
const editor = this.editor;
|
|
||||||
editor.editing.view.addObserver( ClickObserver );
|
|
||||||
|
|
||||||
this._previewUid = `math-preview-${ uid() }`;
|
|
||||||
|
|
||||||
this.formView = this._createFormView();
|
|
||||||
|
|
||||||
this._balloon = editor.plugins.get( ContextualBalloon );
|
|
||||||
|
|
||||||
this._createToolbarMathButton();
|
|
||||||
|
|
||||||
this._enableUserBalloonInteractions();
|
|
||||||
}
|
|
||||||
|
|
||||||
destroy() {
|
|
||||||
super.destroy();
|
|
||||||
|
|
||||||
this.formView.destroy();
|
|
||||||
|
|
||||||
// Destroy preview element
|
|
||||||
const previewEl = global.document.getElementById( this._previewUid );
|
|
||||||
if ( previewEl ) {
|
|
||||||
previewEl.parentNode.removeChild( previewEl );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_showUI() {
|
|
||||||
const editor = this.editor;
|
|
||||||
const mathCommand = editor.commands.get( 'math' );
|
|
||||||
|
|
||||||
if ( !mathCommand.isEnabled ) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this._addFormView();
|
|
||||||
|
|
||||||
this._balloon.showStack( 'main' );
|
|
||||||
}
|
|
||||||
|
|
||||||
_createFormView() {
|
|
||||||
const editor = this.editor;
|
|
||||||
const mathCommand = editor.commands.get( 'math' );
|
|
||||||
|
|
||||||
const mathConfig = editor.config.get( 'math' );
|
|
||||||
|
|
||||||
const formView = new MainFormView(
|
|
||||||
editor.locale,
|
|
||||||
mathConfig.engine,
|
|
||||||
mathConfig.lazyLoad,
|
|
||||||
mathConfig.enablePreview,
|
|
||||||
this._previewUid,
|
|
||||||
mathConfig.previewClassName,
|
|
||||||
mathConfig.popupClassName,
|
|
||||||
mathConfig.katexRenderOptions
|
|
||||||
);
|
|
||||||
|
|
||||||
formView.mathInputView.bind( 'value' ).to( mathCommand, 'value' );
|
|
||||||
formView.displayButtonView.bind( 'isOn' ).to( mathCommand, 'display' );
|
|
||||||
|
|
||||||
// Form elements should be read-only when corresponding commands are disabled.
|
|
||||||
formView.mathInputView.bind( 'isReadOnly' ).to( mathCommand, 'isEnabled', value => !value );
|
|
||||||
formView.saveButtonView.bind( 'isEnabled' ).to( mathCommand );
|
|
||||||
formView.displayButtonView.bind( 'isEnabled' ).to( mathCommand );
|
|
||||||
|
|
||||||
// Listen to submit button click
|
|
||||||
this.listenTo( formView, 'submit', () => {
|
|
||||||
editor.execute( 'math', formView.equation, formView.displayButtonView.isOn, mathConfig.outputType, mathConfig.forceOutputType );
|
|
||||||
this._closeFormView();
|
|
||||||
} );
|
|
||||||
|
|
||||||
// Listen to cancel button click
|
|
||||||
this.listenTo( formView, 'cancel', () => {
|
|
||||||
this._closeFormView();
|
|
||||||
} );
|
|
||||||
|
|
||||||
// Close plugin ui, if esc is pressed (while ui is focused)
|
|
||||||
formView.keystrokes.set( 'esc', ( data, cancel ) => {
|
|
||||||
this._closeFormView();
|
|
||||||
cancel();
|
|
||||||
} );
|
|
||||||
|
|
||||||
return formView;
|
|
||||||
}
|
|
||||||
|
|
||||||
_addFormView() {
|
|
||||||
if ( this._isFormInPanel ) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const editor = this.editor;
|
|
||||||
const mathCommand = editor.commands.get( 'math' );
|
|
||||||
|
|
||||||
this._balloon.add( {
|
|
||||||
view: this.formView,
|
|
||||||
position: getBalloonPositionData( editor )
|
|
||||||
} );
|
|
||||||
|
|
||||||
if ( this._balloon.visibleView === this.formView ) {
|
|
||||||
this.formView.mathInputView.fieldView.element.select();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Show preview element
|
|
||||||
const previewEl = global.document.getElementById( this._previewUid );
|
|
||||||
if ( previewEl && this.formView.previewEnabled ) {
|
|
||||||
// Force refresh preview
|
|
||||||
this.formView.mathView.updateMath();
|
|
||||||
}
|
|
||||||
|
|
||||||
this.formView.equation = mathCommand.value || '';
|
|
||||||
this.formView.displayButtonView.isOn = mathCommand.display || false;
|
|
||||||
}
|
|
||||||
|
|
||||||
_hideUI() {
|
|
||||||
if ( !this._isFormInPanel ) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const editor = this.editor;
|
|
||||||
|
|
||||||
this.stopListening( editor.ui, 'update' );
|
|
||||||
this.stopListening( this._balloon, 'change:visibleView' );
|
|
||||||
|
|
||||||
editor.editing.view.focus();
|
|
||||||
|
|
||||||
// Remove form first because it's on top of the stack.
|
|
||||||
this._removeFormView();
|
|
||||||
}
|
|
||||||
|
|
||||||
_closeFormView() {
|
|
||||||
const mathCommand = this.editor.commands.get( 'math' );
|
|
||||||
if ( mathCommand.value !== undefined ) {
|
|
||||||
this._removeFormView();
|
|
||||||
} else {
|
|
||||||
this._hideUI();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_removeFormView() {
|
|
||||||
if ( this._isFormInPanel ) {
|
|
||||||
this.formView.saveButtonView.focus();
|
|
||||||
|
|
||||||
this._balloon.remove( this.formView );
|
|
||||||
|
|
||||||
// Hide preview element
|
|
||||||
const previewEl = global.document.getElementById( this._previewUid );
|
|
||||||
if ( previewEl ) {
|
|
||||||
previewEl.style.visibility = 'hidden';
|
|
||||||
}
|
|
||||||
|
|
||||||
this.editor.editing.view.focus();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_createToolbarMathButton() {
|
|
||||||
const editor = this.editor;
|
|
||||||
const mathCommand = editor.commands.get( 'math' );
|
|
||||||
const t = editor.t;
|
|
||||||
|
|
||||||
// Handle the `Ctrl+M` keystroke and show the panel.
|
|
||||||
editor.keystrokes.set( mathKeystroke, ( keyEvtData, cancel ) => {
|
|
||||||
// Prevent focusing the search bar in FF and opening new tab in Edge. #153, #154.
|
|
||||||
cancel();
|
|
||||||
|
|
||||||
if ( mathCommand.isEnabled ) {
|
|
||||||
this._showUI();
|
|
||||||
}
|
|
||||||
} );
|
|
||||||
|
|
||||||
this.editor.ui.componentFactory.add( 'math', locale => {
|
|
||||||
const button = new ButtonView( locale );
|
|
||||||
|
|
||||||
button.isEnabled = true;
|
|
||||||
button.label = t( 'Insert math' );
|
|
||||||
button.icon = mathIcon;
|
|
||||||
button.keystroke = mathKeystroke;
|
|
||||||
button.tooltip = true;
|
|
||||||
button.isToggleable = true;
|
|
||||||
|
|
||||||
button.bind( 'isEnabled' ).to( mathCommand, 'isEnabled' );
|
|
||||||
|
|
||||||
this.listenTo( button, 'execute', () => this._showUI() );
|
|
||||||
|
|
||||||
return button;
|
|
||||||
} );
|
|
||||||
}
|
|
||||||
|
|
||||||
_enableUserBalloonInteractions() {
|
|
||||||
const editor = this.editor;
|
|
||||||
const viewDocument = this.editor.editing.view.document;
|
|
||||||
this.listenTo( viewDocument, 'click', () => {
|
|
||||||
const mathCommand = editor.commands.get( 'math' );
|
|
||||||
if ( mathCommand.value ) {
|
|
||||||
if ( mathCommand.isEnabled ) {
|
|
||||||
this._showUI();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} );
|
|
||||||
|
|
||||||
// Close the panel on the Esc key press when the editable has focus and the balloon is visible.
|
|
||||||
editor.keystrokes.set( 'Esc', ( data, cancel ) => {
|
|
||||||
if ( this._isUIVisible ) {
|
|
||||||
this._hideUI();
|
|
||||||
cancel();
|
|
||||||
}
|
|
||||||
} );
|
|
||||||
|
|
||||||
// Close on click outside of balloon panel element.
|
|
||||||
clickOutsideHandler( {
|
|
||||||
emitter: this.formView,
|
|
||||||
activator: () => this._isFormInPanel,
|
|
||||||
contextElements: [ this._balloon.view.element ],
|
|
||||||
callback: () => this._hideUI()
|
|
||||||
} );
|
|
||||||
}
|
|
||||||
|
|
||||||
get _isUIVisible() {
|
|
||||||
const visibleView = this._balloon.visibleView;
|
|
||||||
|
|
||||||
return visibleView == this.formView;
|
|
||||||
}
|
|
||||||
|
|
||||||
get _isFormInPanel() {
|
|
||||||
return this._balloon.hasView( this.formView );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,234 +0,0 @@
|
|||||||
import { icons } from 'ckeditor5/src/core';
|
|
||||||
import {
|
|
||||||
ButtonView, createLabeledInputText, FocusCycler, LabelView, LabeledFieldView,
|
|
||||||
submitHandler, SwitchButtonView, View, ViewCollection
|
|
||||||
} from 'ckeditor5/src/ui';
|
|
||||||
import { FocusTracker, KeystrokeHandler } from 'ckeditor5/src/utils';
|
|
||||||
import { extractDelimiters, hasDelimiters } from '../utils';
|
|
||||||
import MathView from './mathview';
|
|
||||||
import '../../theme/mathform.css';
|
|
||||||
|
|
||||||
const { check: checkIcon, cancel: cancelIcon } = icons;
|
|
||||||
|
|
||||||
export default class MainFormView extends View {
|
|
||||||
constructor( locale, engine, lazyLoad, previewEnabled, previewUid, previewClassName, popupClassName, katexRenderOptions ) {
|
|
||||||
super( locale );
|
|
||||||
|
|
||||||
const t = locale.t;
|
|
||||||
|
|
||||||
// Create key event & focus trackers
|
|
||||||
this._createKeyAndFocusTrackers();
|
|
||||||
|
|
||||||
// Submit button
|
|
||||||
this.saveButtonView = this._createButton( t( 'Save' ), checkIcon, 'ck-button-save', null );
|
|
||||||
this.saveButtonView.type = 'submit';
|
|
||||||
|
|
||||||
// Equation input
|
|
||||||
this.mathInputView = this._createMathInput();
|
|
||||||
|
|
||||||
// Display button
|
|
||||||
this.displayButtonView = this._createDisplayButton();
|
|
||||||
|
|
||||||
// Cancel button
|
|
||||||
this.cancelButtonView = this._createButton( t( 'Cancel' ), cancelIcon, 'ck-button-cancel', 'cancel' );
|
|
||||||
|
|
||||||
this.previewEnabled = previewEnabled;
|
|
||||||
|
|
||||||
let children = [];
|
|
||||||
if ( this.previewEnabled ) {
|
|
||||||
// Preview label
|
|
||||||
this.previewLabel = new LabelView( locale );
|
|
||||||
this.previewLabel.text = t( 'Equation preview' );
|
|
||||||
|
|
||||||
// Math element
|
|
||||||
this.mathView = new MathView( engine, lazyLoad, locale, previewUid, previewClassName, katexRenderOptions );
|
|
||||||
this.mathView.bind( 'display' ).to( this.displayButtonView, 'isOn' );
|
|
||||||
|
|
||||||
children = [
|
|
||||||
this.mathInputView,
|
|
||||||
this.displayButtonView,
|
|
||||||
this.previewLabel,
|
|
||||||
this.mathView
|
|
||||||
];
|
|
||||||
} else {
|
|
||||||
children = [
|
|
||||||
this.mathInputView,
|
|
||||||
this.displayButtonView
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add UI elements to template
|
|
||||||
this.setTemplate( {
|
|
||||||
tag: 'form',
|
|
||||||
attributes: {
|
|
||||||
class: [
|
|
||||||
'ck',
|
|
||||||
'ck-math-form',
|
|
||||||
...popupClassName
|
|
||||||
],
|
|
||||||
tabindex: '-1',
|
|
||||||
spellcheck: 'false'
|
|
||||||
},
|
|
||||||
children: [
|
|
||||||
{
|
|
||||||
tag: 'div',
|
|
||||||
attributes: {
|
|
||||||
class: [
|
|
||||||
'ck-math-view'
|
|
||||||
]
|
|
||||||
},
|
|
||||||
children
|
|
||||||
},
|
|
||||||
this.saveButtonView,
|
|
||||||
this.cancelButtonView
|
|
||||||
]
|
|
||||||
} );
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
|
||||||
super.render();
|
|
||||||
|
|
||||||
// Prevent default form submit event & trigger custom 'submit'
|
|
||||||
submitHandler( {
|
|
||||||
view: this
|
|
||||||
} );
|
|
||||||
|
|
||||||
// Register form elements to focusable elements
|
|
||||||
const childViews = [
|
|
||||||
this.mathInputView,
|
|
||||||
this.displayButtonView,
|
|
||||||
this.saveButtonView,
|
|
||||||
this.cancelButtonView
|
|
||||||
];
|
|
||||||
|
|
||||||
childViews.forEach( v => {
|
|
||||||
this._focusables.add( v );
|
|
||||||
this.focusTracker.add( v.element );
|
|
||||||
} );
|
|
||||||
|
|
||||||
// Listen to keypresses inside form element
|
|
||||||
this.keystrokes.listenTo( this.element );
|
|
||||||
}
|
|
||||||
|
|
||||||
focus() {
|
|
||||||
this._focusCycler.focusFirst();
|
|
||||||
}
|
|
||||||
|
|
||||||
get equation() {
|
|
||||||
return this.mathInputView.fieldView.element.value;
|
|
||||||
}
|
|
||||||
|
|
||||||
set equation( equation ) {
|
|
||||||
this.mathInputView.fieldView.element.value = equation;
|
|
||||||
if ( this.previewEnabled ) {
|
|
||||||
this.mathView.value = equation;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_createKeyAndFocusTrackers() {
|
|
||||||
this.focusTracker = new FocusTracker();
|
|
||||||
this.keystrokes = new KeystrokeHandler();
|
|
||||||
this._focusables = new ViewCollection();
|
|
||||||
|
|
||||||
this._focusCycler = new FocusCycler( {
|
|
||||||
focusables: this._focusables,
|
|
||||||
focusTracker: this.focusTracker,
|
|
||||||
keystrokeHandler: this.keystrokes,
|
|
||||||
actions: {
|
|
||||||
focusPrevious: 'shift + tab',
|
|
||||||
focusNext: 'tab'
|
|
||||||
}
|
|
||||||
} );
|
|
||||||
}
|
|
||||||
|
|
||||||
_createMathInput() {
|
|
||||||
const t = this.locale.t;
|
|
||||||
|
|
||||||
// Create equation input
|
|
||||||
const mathInput = new LabeledFieldView( this.locale, createLabeledInputText );
|
|
||||||
const fieldView = mathInput.fieldView;
|
|
||||||
mathInput.infoText = t( 'Insert equation in TeX format.' );
|
|
||||||
|
|
||||||
const onInput = () => {
|
|
||||||
if ( fieldView.element != null ) {
|
|
||||||
let equationInput = fieldView.element.value.trim();
|
|
||||||
|
|
||||||
// If input has delimiters
|
|
||||||
if ( hasDelimiters( equationInput ) ) {
|
|
||||||
// Get equation without delimiters
|
|
||||||
const params = extractDelimiters( equationInput );
|
|
||||||
|
|
||||||
// Remove delimiters from input field
|
|
||||||
fieldView.element.value = params.equation;
|
|
||||||
|
|
||||||
equationInput = params.equation;
|
|
||||||
|
|
||||||
// update display button and preview
|
|
||||||
this.displayButtonView.isOn = params.display;
|
|
||||||
}
|
|
||||||
if ( this.previewEnabled ) {
|
|
||||||
// Update preview view
|
|
||||||
this.mathView.value = equationInput;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.saveButtonView.isEnabled = !!equationInput;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
fieldView.on( 'render', onInput );
|
|
||||||
fieldView.on( 'input', onInput );
|
|
||||||
|
|
||||||
return mathInput;
|
|
||||||
}
|
|
||||||
|
|
||||||
_createButton( label, icon, className, eventName ) {
|
|
||||||
const button = new ButtonView( this.locale );
|
|
||||||
|
|
||||||
button.set( {
|
|
||||||
label,
|
|
||||||
icon,
|
|
||||||
tooltip: true
|
|
||||||
} );
|
|
||||||
|
|
||||||
button.extendTemplate( {
|
|
||||||
attributes: {
|
|
||||||
class: className
|
|
||||||
}
|
|
||||||
} );
|
|
||||||
|
|
||||||
if ( eventName ) {
|
|
||||||
button.delegate( 'execute' ).to( this, eventName );
|
|
||||||
}
|
|
||||||
|
|
||||||
return button;
|
|
||||||
}
|
|
||||||
|
|
||||||
_createDisplayButton() {
|
|
||||||
const t = this.locale.t;
|
|
||||||
|
|
||||||
const switchButton = new SwitchButtonView( this.locale );
|
|
||||||
|
|
||||||
switchButton.set( {
|
|
||||||
label: t( 'Display mode' ),
|
|
||||||
withText: true
|
|
||||||
} );
|
|
||||||
|
|
||||||
switchButton.extendTemplate( {
|
|
||||||
attributes: {
|
|
||||||
class: 'ck-button-display-toggle'
|
|
||||||
}
|
|
||||||
} );
|
|
||||||
|
|
||||||
switchButton.on( 'execute', () => {
|
|
||||||
// Toggle state
|
|
||||||
switchButton.isOn = !switchButton.isOn;
|
|
||||||
|
|
||||||
if ( this.previewEnabled ) {
|
|
||||||
// Update preview view
|
|
||||||
this.mathView.display = switchButton.isOn;
|
|
||||||
}
|
|
||||||
} );
|
|
||||||
|
|
||||||
return switchButton;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,43 +0,0 @@
|
|||||||
import { View } from 'ckeditor5/src/ui';
|
|
||||||
import { renderEquation } from '../utils';
|
|
||||||
|
|
||||||
export default class MathView extends View {
|
|
||||||
constructor( engine, lazyLoad, locale, previewUid, previewClassName, katexRenderOptions ) {
|
|
||||||
super( locale );
|
|
||||||
|
|
||||||
this.engine = engine;
|
|
||||||
this.lazyLoad = lazyLoad;
|
|
||||||
this.previewUid = previewUid;
|
|
||||||
this.katexRenderOptions = katexRenderOptions;
|
|
||||||
this.previewClassName = previewClassName;
|
|
||||||
|
|
||||||
this.set( 'value', '' );
|
|
||||||
this.set( 'display', false );
|
|
||||||
|
|
||||||
this.on( 'change', () => {
|
|
||||||
if ( this.isRendered ) {
|
|
||||||
this.updateMath();
|
|
||||||
}
|
|
||||||
} );
|
|
||||||
|
|
||||||
this.setTemplate( {
|
|
||||||
tag: 'div',
|
|
||||||
attributes: {
|
|
||||||
class: [
|
|
||||||
'ck',
|
|
||||||
'ck-math-preview'
|
|
||||||
]
|
|
||||||
}
|
|
||||||
} );
|
|
||||||
}
|
|
||||||
|
|
||||||
updateMath() {
|
|
||||||
renderEquation( this.value, this.element, this.engine, this.lazyLoad, this.display, true, this.previewUid, this.previewClassName,
|
|
||||||
this.katexRenderOptions );
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
|
||||||
super.render();
|
|
||||||
this.updateMath();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
233
src/utils.js
233
src/utils.js
@ -1,233 +0,0 @@
|
|||||||
import { BalloonPanelView } from 'ckeditor5/src/ui';
|
|
||||||
import { global } from 'ckeditor5/src/utils';
|
|
||||||
|
|
||||||
export function getSelectedMathModelWidget( selection ) {
|
|
||||||
const selectedElement = selection.getSelectedElement();
|
|
||||||
|
|
||||||
if ( selectedElement && ( selectedElement.is( 'element', 'mathtex-inline' ) || selectedElement.is( 'element', 'mathtex-display' ) ) ) {
|
|
||||||
return selectedElement;
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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( /^(\\\[.*?\\\]|\\\(.*?\\\))$/ );
|
|
||||||
}
|
|
||||||
|
|
||||||
// Find delimiters count
|
|
||||||
export function delimitersCounts( text ) {
|
|
||||||
return text.match( /(\\\[|\\\]|\\\(|\\\))/g ).length;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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 async function renderEquation(
|
|
||||||
equation, element, engine = 'katex', lazyLoad, display = false, preview = false, previewUid, previewClassName = [],
|
|
||||||
katexRenderOptions = {}
|
|
||||||
) {
|
|
||||||
if ( engine === 'mathjax' && typeof MathJax !== 'undefined' ) {
|
|
||||||
if ( isMathJaxVersion3( MathJax.version ) ) {
|
|
||||||
selectRenderMode( element, preview, previewUid, previewClassName, el => {
|
|
||||||
renderMathJax3( equation, el, display, () => {
|
|
||||||
if ( preview ) {
|
|
||||||
moveAndScaleElement( element, el );
|
|
||||||
el.style.visibility = 'visible';
|
|
||||||
}
|
|
||||||
} );
|
|
||||||
} );
|
|
||||||
} else {
|
|
||||||
selectRenderMode( element, preview, previewUid, previewClassName, el => {
|
|
||||||
// Fixme: MathJax typesetting cause occasionally math processing error without asynchronous call
|
|
||||||
global.window.setTimeout( () => {
|
|
||||||
renderMathJax2( equation, el, display );
|
|
||||||
|
|
||||||
// Move and scale after rendering
|
|
||||||
if ( preview ) {
|
|
||||||
// eslint-disable-next-line
|
|
||||||
MathJax.Hub.Queue( () => {
|
|
||||||
moveAndScaleElement( element, el );
|
|
||||||
el.style.visibility = 'visible';
|
|
||||||
} );
|
|
||||||
}
|
|
||||||
} );
|
|
||||||
} );
|
|
||||||
}
|
|
||||||
} else if ( engine === 'katex' && typeof katex !== 'undefined' ) {
|
|
||||||
selectRenderMode( element, preview, previewUid, previewClassName, el => {
|
|
||||||
katex.render( equation, el, {
|
|
||||||
throwOnError: false,
|
|
||||||
displayMode: display,
|
|
||||||
...katexRenderOptions
|
|
||||||
} );
|
|
||||||
if ( preview ) {
|
|
||||||
moveAndScaleElement( element, el );
|
|
||||||
el.style.visibility = 'visible';
|
|
||||||
}
|
|
||||||
} );
|
|
||||||
} else if ( typeof engine === 'function' ) {
|
|
||||||
engine( equation, element, display );
|
|
||||||
} else {
|
|
||||||
if ( typeof lazyLoad !== 'undefined' ) {
|
|
||||||
try {
|
|
||||||
if ( !global.window.CKEDITOR_MATH_LAZY_LOAD ) {
|
|
||||||
global.window.CKEDITOR_MATH_LAZY_LOAD = lazyLoad();
|
|
||||||
}
|
|
||||||
element.innerHTML = equation;
|
|
||||||
await global.window.CKEDITOR_MATH_LAZY_LOAD;
|
|
||||||
renderEquation( equation, element, engine, undefined, display, preview, previewUid, previewClassName, katexRenderOptions );
|
|
||||||
}
|
|
||||||
catch ( err ) {
|
|
||||||
element.innerHTML = equation;
|
|
||||||
console.error( `math-tex-typesetting-lazy-load-failed: Lazy load failed: ${ err }` );
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
element.innerHTML = equation;
|
|
||||||
console.warn( `math-tex-typesetting-missing: Missing the mathematical typesetting engine (${ engine }) for tex.` );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getBalloonPositionData( editor ) {
|
|
||||||
const view = editor.editing.view;
|
|
||||||
const defaultPositions = BalloonPanelView.defaultPositions;
|
|
||||||
|
|
||||||
const selectedElement = view.document.selection.getSelectedElement();
|
|
||||||
if ( selectedElement ) {
|
|
||||||
return {
|
|
||||||
target: view.domConverter.viewToDom( selectedElement ),
|
|
||||||
positions: [
|
|
||||||
defaultPositions.southArrowNorth,
|
|
||||||
defaultPositions.southArrowNorthWest,
|
|
||||||
defaultPositions.southArrowNorthEast
|
|
||||||
]
|
|
||||||
};
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
const viewDocument = view.document;
|
|
||||||
return {
|
|
||||||
target: view.domConverter.viewRangeToDom( viewDocument.selection.getFirstRange() ),
|
|
||||||
positions: [
|
|
||||||
defaultPositions.southArrowNorth,
|
|
||||||
defaultPositions.southArrowNorthWest,
|
|
||||||
defaultPositions.southArrowNorthEast
|
|
||||||
]
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function selectRenderMode( element, preview, previewUid, previewClassName, cb ) {
|
|
||||||
if ( preview ) {
|
|
||||||
createPreviewElement( element, previewUid, previewClassName, previewEl => {
|
|
||||||
cb( previewEl );
|
|
||||||
} );
|
|
||||||
} else {
|
|
||||||
cb( element );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function renderMathJax3( equation, element, display, cb ) {
|
|
||||||
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, { display } ).then( node => {
|
|
||||||
if ( element.firstChild ) {
|
|
||||||
element.removeChild( element.firstChild );
|
|
||||||
}
|
|
||||||
element.appendChild( node );
|
|
||||||
cb();
|
|
||||||
} );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function renderMathJax2( equation, element, display ) {
|
|
||||||
if ( display ) {
|
|
||||||
element.innerHTML = '\\[' + equation + '\\]';
|
|
||||||
} else {
|
|
||||||
element.innerHTML = '\\(' + equation + '\\)';
|
|
||||||
}
|
|
||||||
// eslint-disable-next-line
|
|
||||||
MathJax.Hub.Queue( [ 'Typeset', MathJax.Hub, element ] );
|
|
||||||
}
|
|
||||||
|
|
||||||
function createPreviewElement( element, previewUid, previewClassName, render ) {
|
|
||||||
const previewEl = getPreviewElement( element, previewUid, previewClassName );
|
|
||||||
render( previewEl );
|
|
||||||
}
|
|
||||||
|
|
||||||
function getPreviewElement( element, previewUid, previewClassName ) {
|
|
||||||
let previewEl = global.document.getElementById( previewUid );
|
|
||||||
// Create if not found
|
|
||||||
if ( !previewEl ) {
|
|
||||||
previewEl = global.document.createElement( 'div' );
|
|
||||||
previewEl.setAttribute( 'id', previewUid );
|
|
||||||
previewEl.classList.add( ...previewClassName );
|
|
||||||
previewEl.style.visibility = 'hidden';
|
|
||||||
global.document.body.appendChild( previewEl );
|
|
||||||
|
|
||||||
let ticking = false;
|
|
||||||
|
|
||||||
const renderTransformation = () => {
|
|
||||||
if ( !ticking ) {
|
|
||||||
global.window.requestAnimationFrame( () => {
|
|
||||||
moveElement( element, previewEl );
|
|
||||||
ticking = false;
|
|
||||||
} );
|
|
||||||
|
|
||||||
ticking = true;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Create scroll listener for following
|
|
||||||
global.window.addEventListener( 'resize', renderTransformation );
|
|
||||||
global.window.addEventListener( 'scroll', renderTransformation );
|
|
||||||
}
|
|
||||||
return previewEl;
|
|
||||||
}
|
|
||||||
|
|
||||||
function moveAndScaleElement( parent, child ) {
|
|
||||||
// Move to right place
|
|
||||||
moveElement( parent, child );
|
|
||||||
|
|
||||||
// Scale parent element same as preview
|
|
||||||
const domRect = child.getBoundingClientRect();
|
|
||||||
parent.style.width = domRect.width + 'px';
|
|
||||||
parent.style.height = domRect.height + 'px';
|
|
||||||
}
|
|
||||||
|
|
||||||
function moveElement( parent, child ) {
|
|
||||||
const domRect = parent.getBoundingClientRect();
|
|
||||||
const left = global.window.scrollX + domRect.left;
|
|
||||||
const top = global.window.scrollY + domRect.top;
|
|
||||||
child.style.position = 'absolute';
|
|
||||||
child.style.left = left + 'px';
|
|
||||||
child.style.top = top + 'px';
|
|
||||||
child.style.zIndex = 'var(--ck-z-panel)';
|
|
||||||
child.style.pointerEvents = 'none';
|
|
||||||
}
|
|
||||||
Loading…
x
Reference in New Issue
Block a user