mirror of
https://gerrit.wikimedia.org/r/mediawiki/extensions/Math
synced 2024-12-22 12:22:39 +00:00
17ac038ae4
Add the skin-invert class to latex symbols (insert chemical formula and math formula dialogues) so that they appear legible in night mode. This is likely the only way we can accomplish this until mathml is fully rolled out, at which I believe this will no longer be necessary Bug: T366737 Change-Id: Ia0dfa4ab684f5205d109da7f9aefad927be70eb0
346 lines
10 KiB
JavaScript
346 lines
10 KiB
JavaScript
/*!
|
|
* VisualEditor user interface MWLatexDialog class.
|
|
*
|
|
* @copyright See AUTHORS.txt
|
|
* @license MIT
|
|
*/
|
|
|
|
/**
|
|
* Abstract dialog for inserting and editing different formulas
|
|
* provided by the Math extension.
|
|
*
|
|
* @abstract
|
|
* @class
|
|
* @extends ve.ui.MWExtensionPreviewDialog
|
|
*
|
|
* @constructor
|
|
* @param {Object} [config] Configuration options
|
|
*/
|
|
ve.ui.MWLatexDialog = function VeUiMWLatexDialog( config ) {
|
|
// Parent constructor
|
|
ve.ui.MWLatexDialog.super.call( this, config );
|
|
};
|
|
|
|
/* Inheritance */
|
|
|
|
OO.inheritClass( ve.ui.MWLatexDialog, ve.ui.MWExtensionPreviewDialog );
|
|
|
|
/* Static properties */
|
|
|
|
ve.ui.MWLatexDialog.static.size = 'larger';
|
|
|
|
ve.ui.MWLatexDialog.static.dir = 'ltr';
|
|
|
|
ve.ui.MWLatexDialog.static.symbolsModule = null;
|
|
|
|
/* Methods */
|
|
|
|
/**
|
|
* @inheritdoc
|
|
*/
|
|
ve.ui.MWLatexDialog.prototype.initialize = function () {
|
|
// Parent method
|
|
ve.ui.MWLatexDialog.super.prototype.initialize.call( this );
|
|
|
|
// Layout for the formula inserter (formula tab panel) and options form (options tab panel)
|
|
this.indexLayout = new OO.ui.IndexLayout();
|
|
|
|
const formulaTabPanel = new OO.ui.TabPanelLayout( 'formula', {
|
|
label: ve.msg( 'math-visualeditor-mwlatexdialog-card-formula' ),
|
|
padded: true,
|
|
classes: [ 'latex-dialog-formula-panel' ]
|
|
} );
|
|
const optionsTabPanel = new OO.ui.TabPanelLayout( 'options', {
|
|
label: ve.msg( 'math-visualeditor-mwlatexdialog-card-options' ),
|
|
padded: true,
|
|
classes: [ 'latex-dialog-options-panel' ]
|
|
} );
|
|
|
|
this.indexLayout.addTabPanels( [
|
|
formulaTabPanel,
|
|
optionsTabPanel
|
|
] );
|
|
|
|
// Layout for symbol picker (menu) and input and preview (content)
|
|
this.menuLayout = new OO.ui.MenuLayout( {
|
|
menuPosition: 'bottom',
|
|
classes: [ 've-ui-mwLatexDialog-menuLayout' ]
|
|
} );
|
|
|
|
this.previewElement.$element.addClass(
|
|
've-ui-mwLatexDialog-preview'
|
|
);
|
|
|
|
this.input = new ve.ui.MWAceEditorWidget( {
|
|
rows: 1, // This will be recalculated later in onWindowManagerResize
|
|
autocomplete: 'live',
|
|
autocompleteWordList: this.constructor.static.autocompleteWordList
|
|
} ).setLanguage( 'latex' );
|
|
|
|
this.input.togglePrintMargin( false );
|
|
|
|
this.displaySelect = new OO.ui.ButtonSelectWidget( {
|
|
items: [
|
|
new OO.ui.ButtonOptionWidget( {
|
|
data: 'default',
|
|
icon: 'mathematicsDisplayDefault',
|
|
label: ve.msg( 'math-visualeditor-mwlatexinspector-display-default' )
|
|
} ),
|
|
new OO.ui.ButtonOptionWidget( {
|
|
data: 'inline',
|
|
icon: 'mathematicsDisplayInline',
|
|
label: ve.msg( 'math-visualeditor-mwlatexinspector-display-inline' )
|
|
} ),
|
|
new OO.ui.ButtonOptionWidget( {
|
|
data: 'block',
|
|
icon: 'mathematicsDisplayBlock',
|
|
label: ve.msg( 'math-visualeditor-mwlatexinspector-display-block' )
|
|
} )
|
|
]
|
|
} );
|
|
|
|
this.idInput = new OO.ui.TextInputWidget();
|
|
this.qidInput = new mw.widgets.MathWbEntitySelector();
|
|
|
|
const inputField = new OO.ui.FieldLayout( this.input, {
|
|
align: 'top',
|
|
classes: [ 'latex-dialog-formula-field' ],
|
|
label: ve.msg( 'math-visualeditor-mwlatexdialog-card-formula' )
|
|
} );
|
|
const displayField = new OO.ui.FieldLayout( this.displaySelect, {
|
|
align: 'top',
|
|
classes: [ 'latex-dialog-display-field' ],
|
|
label: ve.msg( 'math-visualeditor-mwlatexinspector-display' )
|
|
} );
|
|
const idField = new OO.ui.FieldLayout( this.idInput, {
|
|
align: 'top',
|
|
classes: [ 'latex-dialog-id-field' ],
|
|
label: ve.msg( 'math-visualeditor-mwlatexinspector-id' )
|
|
} );
|
|
const qidField = new OO.ui.FieldLayout( this.qidInput, {
|
|
align: 'top',
|
|
classes: [ 'latex-dialog-qid-field' ],
|
|
label: ve.msg( 'math-visualeditor-mwlatexinspector-qid' )
|
|
} );
|
|
|
|
const formulaPanel = new OO.ui.PanelLayout( {
|
|
scrollable: true,
|
|
padded: true
|
|
} );
|
|
|
|
// Layout for the symbol picker
|
|
this.bookletLayout = new ve.ui.SymbolListBookletLayout( {
|
|
classes: [ 've-ui-mwLatexDialog-symbols' ]
|
|
} );
|
|
this.pages = [];
|
|
this.symbolsPromise = mw.loader.using( this.constructor.static.symbolsModule ).done( ( require ) => {
|
|
// eslint-disable-next-line security/detect-non-literal-require
|
|
const symbols = require( this.constructor.static.symbolsModule );
|
|
const symbolData = {};
|
|
for ( const category in symbols ) {
|
|
const symbolList = symbols[ category ].filter( ( symbol ) => {
|
|
if ( symbol.notWorking || symbol.duplicate ) {
|
|
return false;
|
|
}
|
|
const tex = symbol.tex || symbol.insert;
|
|
const classes = [ 've-ui-mwLatexDialog-symbol' ];
|
|
classes.push(
|
|
've-ui-mwLatexSymbol-' + tex.replace( /[^\w]/g, ( c ) => '_' + c.charCodeAt( 0 ) + '_' )
|
|
);
|
|
if ( symbol.width ) {
|
|
// The following classes are used here:
|
|
// * ve-ui-mwLatexDialog-symbol-wide
|
|
// * ve-ui-mwLatexDialog-symbol-wider
|
|
// * ve-ui-mwLatexDialog-symbol-widest
|
|
classes.push( 've-ui-mwLatexDialog-symbol-' + symbol.width );
|
|
}
|
|
if ( symbol.contain ) {
|
|
classes.push( 've-ui-mwLatexDialog-symbol-contain' );
|
|
}
|
|
if ( symbol.largeLayout ) {
|
|
classes.push( 've-ui-mwLatexDialog-symbol-largeLayout' );
|
|
}
|
|
|
|
// T366737 - make sure the symbols appear in night mode
|
|
classes.push( 'skin-invert' );
|
|
|
|
symbol.label = '';
|
|
symbol.classes = classes;
|
|
|
|
return true;
|
|
} );
|
|
symbolData[ category ] = {
|
|
// eslint-disable-next-line mediawiki/msg-doc
|
|
label: ve.msg( category ),
|
|
symbols: symbolList
|
|
};
|
|
}
|
|
this.bookletLayout.setSymbolData( symbolData );
|
|
this.bookletLayout.connect( this, {
|
|
choose: 'onSymbolChoose'
|
|
} );
|
|
|
|
// Append everything
|
|
formulaPanel.$element.append(
|
|
this.previewElement.$element,
|
|
inputField.$element
|
|
);
|
|
this.menuLayout.setMenuPanel( this.bookletLayout );
|
|
this.menuLayout.setContentPanel( formulaPanel );
|
|
|
|
formulaTabPanel.$element.append(
|
|
this.menuLayout.$element
|
|
);
|
|
optionsTabPanel.$element.append(
|
|
displayField.$element,
|
|
idField.$element,
|
|
qidField.$element
|
|
);
|
|
|
|
this.$body
|
|
.addClass( 've-ui-mwLatexDialog-content' )
|
|
.append( this.indexLayout.$element );
|
|
} );
|
|
|
|
};
|
|
|
|
/**
|
|
* @inheritdoc
|
|
*/
|
|
ve.ui.MWLatexDialog.prototype.getSetupProcess = function ( data ) {
|
|
return ve.ui.MWLatexDialog.super.prototype.getSetupProcess.call( this, data )
|
|
.next( () => {
|
|
const attributes = this.selectedNode && this.selectedNode.getAttribute( 'mw' ).attrs,
|
|
display = attributes && attributes.display || 'default',
|
|
id = attributes && attributes.id || '',
|
|
qid = attributes && attributes.qid || '',
|
|
isReadOnly = this.isReadOnly();
|
|
|
|
// Populate form
|
|
// TODO: This widget is not readable when disabled
|
|
this.displaySelect.selectItemByData( display ).setDisabled( isReadOnly );
|
|
this.idInput.setValue( id ).setReadOnly( isReadOnly );
|
|
this.qidInput.setValue( qid ).setReadOnly( isReadOnly );
|
|
|
|
// Add event handlers
|
|
this.input.on( 'change', this.onChangeHandler );
|
|
this.displaySelect.on( 'choose', this.onChangeHandler );
|
|
this.idInput.on( 'change', this.onChangeHandler );
|
|
this.qidInput.on( 'change', this.onChangeHandler );
|
|
} );
|
|
};
|
|
|
|
/**
|
|
* @inheritdoc
|
|
*/
|
|
ve.ui.MWLatexDialog.prototype.getReadyProcess = function ( data ) {
|
|
mw.hook( 've.ui.MwLatexDialogReadyProcess' ).fire();
|
|
return ve.ui.MWLatexDialog.super.prototype.getReadyProcess.call( this, data )
|
|
.next( () => this.symbolsPromise )
|
|
.next( () => {
|
|
// Resize the input once the dialog has been appended
|
|
this.input.adjustSize( true ).focus().moveCursorToEnd();
|
|
this.getManager().connect( this, { resize: 'onWindowManagerResize' } );
|
|
this.onWindowManagerResize();
|
|
} );
|
|
};
|
|
|
|
/**
|
|
* @inheritdoc
|
|
*/
|
|
ve.ui.MWLatexDialog.prototype.getTeardownProcess = function ( data ) {
|
|
return ve.ui.MWLatexDialog.super.prototype.getTeardownProcess.call( this, data )
|
|
.first( () => {
|
|
this.input.off( 'change', this.onChangeHandler );
|
|
this.displaySelect.off( 'choose', this.onChangeHandler );
|
|
this.idInput.off( 'change', this.onChangeHandler );
|
|
this.qidInput.off( 'change', this.onChangeHandler );
|
|
this.getManager().disconnect( this );
|
|
this.indexLayout.setTabPanel( 'formula' );
|
|
this.indexLayout.resetScroll();
|
|
this.menuLayout.resetScroll();
|
|
this.bookletLayout.resetScroll();
|
|
} );
|
|
};
|
|
|
|
/**
|
|
* @inheritdoc
|
|
*/
|
|
ve.ui.MWLatexDialog.prototype.updateMwData = function ( mwData ) {
|
|
// Parent method
|
|
ve.ui.MWLatexDialog.super.prototype.updateMwData.call( this, mwData );
|
|
|
|
// Get data from dialog
|
|
const display = this.displaySelect.findSelectedItem().getData();
|
|
const id = this.idInput.getValue();
|
|
const qid = this.qidInput.getValue();
|
|
|
|
// Update attributes
|
|
mwData.attrs.display = display !== 'default' ? display : undefined;
|
|
mwData.attrs.id = id || undefined;
|
|
mwData.attrs.qid = qid || undefined;
|
|
};
|
|
|
|
/**
|
|
* @inheritdoc
|
|
*/
|
|
ve.ui.MWLatexDialog.prototype.getBodyHeight = function () {
|
|
return 600;
|
|
};
|
|
|
|
/**
|
|
* Handle the window resize event
|
|
*/
|
|
ve.ui.MWLatexDialog.prototype.onWindowManagerResize = function () {
|
|
this.input.loadingPromise.always( () => {
|
|
// Toggle short mode as necessary
|
|
// NB a change of mode triggers a transition...
|
|
this.menuLayout.$element.toggleClass(
|
|
've-ui-mwLatexDialog-menuLayout-short', this.menuLayout.$element.height() < 450
|
|
);
|
|
|
|
// ...So wait for the possible menuLayout transition to finish
|
|
setTimeout( () => {
|
|
// Give the input the right number of rows to fit the space
|
|
const availableSpace = this.menuLayout.$content.height() - this.input.$element.position().top;
|
|
// TODO: Compute this line height from the skin
|
|
const singleLineHeight = 21;
|
|
const border = 1;
|
|
const padding = 3;
|
|
const borderAndPadding = 2 * ( border + padding );
|
|
const maxInputHeight = availableSpace - borderAndPadding;
|
|
const minRows = Math.floor( maxInputHeight / singleLineHeight );
|
|
this.input.loadingPromise.done( () => {
|
|
this.input.setMinRows( minRows );
|
|
} ).fail( () => {
|
|
this.input.$input.attr( 'rows', minRows );
|
|
} );
|
|
}, OO.ui.theme.getDialogTransitionDuration() );
|
|
} );
|
|
};
|
|
|
|
/**
|
|
* Handle a symbol being chosen from the list
|
|
*
|
|
* @param {Object} symbol
|
|
*/
|
|
ve.ui.MWLatexDialog.prototype.onSymbolChoose = function ( symbol ) {
|
|
if ( this.isReadOnly() ) {
|
|
return;
|
|
}
|
|
|
|
const encapsulate = symbol.encapsulate;
|
|
|
|
if ( encapsulate ) {
|
|
const range = this.input.getRange();
|
|
if ( range.from === range.to ) {
|
|
this.input.insertContent( encapsulate.placeholder );
|
|
this.input.selectRange( range.from, range.from + encapsulate.placeholder.length );
|
|
}
|
|
this.input.encapsulateContent( encapsulate.pre, encapsulate.post );
|
|
} else {
|
|
const insert = symbol.insert;
|
|
this.input.insertContent( insert );
|
|
}
|
|
};
|