2017-01-11 21:50:35 +00:00
|
|
|
/*!
|
|
|
|
* VisualEditor user interface MWLatexDialog class.
|
|
|
|
*
|
|
|
|
* @copyright 2015 VisualEditor Team and others; see AUTHORS.txt
|
2018-04-13 14:04:06 +00:00
|
|
|
* @license MIT
|
2017-01-11 21:50:35 +00:00
|
|
|
*/
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 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.symbols = null;
|
|
|
|
|
|
|
|
ve.ui.MWLatexDialog.static.symbolsModule = null;
|
|
|
|
|
|
|
|
/* static methods */
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Set the symbols property
|
|
|
|
*
|
|
|
|
* @param {Object} symbols The symbols and their group names
|
|
|
|
*/
|
|
|
|
ve.ui.MWLatexDialog.static.setSymbols = function ( symbols ) {
|
|
|
|
this.symbols = symbols;
|
|
|
|
};
|
|
|
|
|
|
|
|
/* Methods */
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @inheritdoc
|
|
|
|
*/
|
|
|
|
ve.ui.MWLatexDialog.prototype.initialize = function () {
|
|
|
|
var formulaPanel, inputField, displayField, idField, category,
|
2017-05-17 09:31:57 +00:00
|
|
|
formulaTabPanel, optionsTabPanel,
|
2017-01-11 21:50:35 +00:00
|
|
|
dialog = this;
|
|
|
|
|
|
|
|
// Parent method
|
|
|
|
ve.ui.MWLatexDialog.super.prototype.initialize.call( this );
|
|
|
|
|
2017-05-17 09:31:57 +00:00
|
|
|
// Layout for the formula inserter (formula tab panel) and options form (options tab panel)
|
2018-11-12 16:15:00 +00:00
|
|
|
this.indexLayout = new OO.ui.IndexLayout();
|
2017-01-11 21:50:35 +00:00
|
|
|
|
2017-05-17 09:31:57 +00:00
|
|
|
formulaTabPanel = new OO.ui.TabPanelLayout( 'formula', {
|
2017-01-11 21:50:35 +00:00
|
|
|
label: ve.msg( 'math-visualeditor-mwlatexdialog-card-formula' ),
|
|
|
|
padded: true
|
|
|
|
} );
|
2017-05-17 09:31:57 +00:00
|
|
|
optionsTabPanel = new OO.ui.TabPanelLayout( 'options', {
|
2017-01-11 21:50:35 +00:00
|
|
|
label: ve.msg( 'math-visualeditor-mwlatexdialog-card-options' ),
|
|
|
|
padded: true
|
|
|
|
} );
|
|
|
|
|
2017-05-17 09:31:57 +00:00
|
|
|
this.indexLayout.addTabPanels( [
|
|
|
|
formulaTabPanel,
|
|
|
|
optionsTabPanel
|
2017-01-11 21:50:35 +00:00
|
|
|
] );
|
|
|
|
|
|
|
|
// 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',
|
2019-01-23 22:36:14 +00:00
|
|
|
icon: 'mathematicsDisplayDefault',
|
2017-01-11 21:50:35 +00:00
|
|
|
label: ve.msg( 'math-visualeditor-mwlatexinspector-display-default' )
|
|
|
|
} ),
|
|
|
|
new OO.ui.ButtonOptionWidget( {
|
|
|
|
data: 'inline',
|
2019-01-23 22:36:14 +00:00
|
|
|
icon: 'mathematicsDisplayInline',
|
2017-01-11 21:50:35 +00:00
|
|
|
label: ve.msg( 'math-visualeditor-mwlatexinspector-display-inline' )
|
|
|
|
} ),
|
|
|
|
new OO.ui.ButtonOptionWidget( {
|
|
|
|
data: 'block',
|
2019-01-23 22:36:14 +00:00
|
|
|
icon: 'mathematicsDisplayBlock',
|
2017-01-11 21:50:35 +00:00
|
|
|
label: ve.msg( 'math-visualeditor-mwlatexinspector-display-block' )
|
|
|
|
} )
|
|
|
|
]
|
|
|
|
} );
|
|
|
|
|
|
|
|
this.idInput = new OO.ui.TextInputWidget();
|
|
|
|
|
|
|
|
inputField = new OO.ui.FieldLayout( this.input, {
|
|
|
|
align: 'top',
|
|
|
|
label: ve.msg( 'math-visualeditor-mwlatexdialog-card-formula' )
|
|
|
|
} );
|
|
|
|
displayField = new OO.ui.FieldLayout( this.displaySelect, {
|
|
|
|
align: 'top',
|
|
|
|
label: ve.msg( 'math-visualeditor-mwlatexinspector-display' )
|
|
|
|
} );
|
|
|
|
idField = new OO.ui.FieldLayout( this.idInput, {
|
|
|
|
align: 'top',
|
|
|
|
label: ve.msg( 'math-visualeditor-mwlatexinspector-id' )
|
|
|
|
} );
|
|
|
|
|
|
|
|
formulaPanel = new OO.ui.PanelLayout( {
|
2018-11-12 16:15:00 +00:00
|
|
|
scrollable: true,
|
2017-01-11 21:50:35 +00:00
|
|
|
padded: true
|
|
|
|
} );
|
|
|
|
|
|
|
|
// Layout for the symbol picker
|
|
|
|
this.bookletLayout = new OO.ui.BookletLayout( {
|
|
|
|
classes: [ 've-ui-mwLatexDialog-symbols' ],
|
|
|
|
menuPosition: 'before',
|
|
|
|
outlined: true,
|
|
|
|
continuous: true
|
|
|
|
} );
|
|
|
|
this.pages = [];
|
|
|
|
this.symbolsPromise = mw.loader.using( this.constructor.static.symbolsModule ).done( function () {
|
|
|
|
var symbols = dialog.constructor.static.symbols;
|
|
|
|
for ( category in symbols ) {
|
|
|
|
dialog.pages.push(
|
|
|
|
new ve.ui.MWLatexPage( ve.msg( category ), {
|
|
|
|
label: ve.msg( category ),
|
|
|
|
symbols: symbols[ category ]
|
|
|
|
} )
|
|
|
|
);
|
|
|
|
}
|
|
|
|
dialog.bookletLayout.addPages( dialog.pages );
|
|
|
|
dialog.bookletLayout.$element.on(
|
|
|
|
'click',
|
|
|
|
'.ve-ui-mwLatexPage-symbol',
|
|
|
|
dialog.onListClick.bind( dialog )
|
|
|
|
);
|
|
|
|
|
|
|
|
// Append everything
|
2018-11-12 16:15:00 +00:00
|
|
|
formulaPanel.$element.append(
|
|
|
|
dialog.previewElement.$element,
|
|
|
|
inputField.$element
|
2017-01-11 21:50:35 +00:00
|
|
|
);
|
2018-11-12 16:15:00 +00:00
|
|
|
dialog.menuLayout.setMenuPanel( dialog.bookletLayout );
|
|
|
|
dialog.menuLayout.setContentPanel( formulaPanel );
|
2017-01-11 21:50:35 +00:00
|
|
|
|
2017-05-17 09:31:57 +00:00
|
|
|
formulaTabPanel.$element.append(
|
2017-01-11 21:50:35 +00:00
|
|
|
dialog.menuLayout.$element
|
|
|
|
);
|
2017-05-17 09:31:57 +00:00
|
|
|
optionsTabPanel.$element.append(
|
2017-01-11 21:50:35 +00:00
|
|
|
displayField.$element,
|
|
|
|
idField.$element
|
|
|
|
);
|
|
|
|
|
|
|
|
dialog.$body
|
|
|
|
.addClass( 've-ui-mwLatexDialog-content' )
|
|
|
|
.append( dialog.indexLayout.$element );
|
|
|
|
} );
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @inheritdoc
|
|
|
|
*/
|
|
|
|
ve.ui.MWLatexDialog.prototype.getSetupProcess = function ( data ) {
|
|
|
|
return ve.ui.MWLatexDialog.super.prototype.getSetupProcess.call( this, data )
|
|
|
|
.next( function () {
|
|
|
|
var attributes = this.selectedNode && this.selectedNode.getAttribute( 'mw' ).attrs,
|
|
|
|
display = attributes && attributes.display || 'default',
|
2019-02-19 14:58:41 +00:00
|
|
|
id = attributes && attributes.id || '',
|
|
|
|
isReadOnly = this.isReadOnly();
|
2017-01-11 21:50:35 +00:00
|
|
|
|
|
|
|
// Populate form
|
2019-02-19 14:58:41 +00:00
|
|
|
// TODO: This widget is not readable when disabled
|
|
|
|
this.displaySelect.selectItemByData( display ).setDisabled( isReadOnly );
|
2019-04-16 11:04:58 +00:00
|
|
|
this.idInput.setValue( id ).setReadOnly( isReadOnly );
|
2017-01-11 21:50:35 +00:00
|
|
|
|
|
|
|
// Add event handlers
|
|
|
|
this.input.on( 'change', this.onChangeHandler );
|
|
|
|
this.displaySelect.on( 'choose', this.onChangeHandler );
|
|
|
|
this.idInput.on( 'change', this.onChangeHandler );
|
|
|
|
}, this );
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @inheritdoc
|
|
|
|
*/
|
|
|
|
ve.ui.MWLatexDialog.prototype.getReadyProcess = function ( data ) {
|
|
|
|
return ve.ui.MWLatexDialog.super.prototype.getReadyProcess.call( this, data )
|
|
|
|
.next( function () {
|
|
|
|
return this.symbolsPromise;
|
|
|
|
}, this )
|
|
|
|
.next( function () {
|
|
|
|
// Resize the input once the dialog has been appended
|
|
|
|
this.input.adjustSize( true ).focus().moveCursorToEnd();
|
|
|
|
this.getManager().connect( this, { resize: 'onWindowManagerResize' } );
|
|
|
|
this.onWindowManagerResize();
|
|
|
|
}, this );
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @inheritdoc
|
|
|
|
*/
|
|
|
|
ve.ui.MWLatexDialog.prototype.getTeardownProcess = function ( data ) {
|
|
|
|
return ve.ui.MWLatexDialog.super.prototype.getTeardownProcess.call( this, data )
|
|
|
|
.first( function () {
|
|
|
|
this.input.off( 'change', this.onChangeHandler );
|
|
|
|
this.displaySelect.off( 'choose', this.onChangeHandler );
|
|
|
|
this.idInput.off( 'change', this.onChangeHandler );
|
|
|
|
this.getManager().disconnect( this );
|
2018-11-12 16:15:00 +00:00
|
|
|
this.indexLayout.setTabPanel( 'formula' );
|
|
|
|
this.indexLayout.resetScroll();
|
|
|
|
this.menuLayout.resetScroll();
|
|
|
|
this.bookletLayout.resetScroll();
|
2017-01-11 21:50:35 +00:00
|
|
|
}, this );
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @inheritdoc
|
|
|
|
*/
|
|
|
|
ve.ui.MWLatexDialog.prototype.updateMwData = function ( mwData ) {
|
|
|
|
var display, id;
|
|
|
|
|
|
|
|
// Parent method
|
|
|
|
ve.ui.MWLatexDialog.super.prototype.updateMwData.call( this, mwData );
|
|
|
|
|
|
|
|
// Get data from dialog
|
2018-01-17 13:49:06 +00:00
|
|
|
display = this.displaySelect.findSelectedItem().getData();
|
2017-01-11 21:50:35 +00:00
|
|
|
id = this.idInput.getValue();
|
|
|
|
|
|
|
|
// Update attributes
|
|
|
|
mwData.attrs.display = display !== 'default' ? display : undefined;
|
|
|
|
mwData.attrs.id = id || undefined;
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @inheritdoc
|
|
|
|
*/
|
|
|
|
ve.ui.MWLatexDialog.prototype.getBodyHeight = function () {
|
|
|
|
return 600;
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Handle the window resize event
|
|
|
|
*/
|
|
|
|
ve.ui.MWLatexDialog.prototype.onWindowManagerResize = function () {
|
|
|
|
var dialog = this;
|
2018-01-11 14:07:29 +00:00
|
|
|
this.input.loadingPromise.always( function () {
|
2017-01-11 21:50:35 +00:00
|
|
|
var availableSpace, maxInputHeight, singleLineHeight, minRows,
|
|
|
|
border = 1,
|
|
|
|
padding = 3,
|
|
|
|
borderAndPadding = 2 * ( border + padding );
|
|
|
|
// Toggle short mode as necessary
|
|
|
|
// NB a change of mode triggers a transition...
|
|
|
|
dialog.menuLayout.$element.toggleClass(
|
|
|
|
've-ui-mwLatexDialog-menuLayout-short', dialog.menuLayout.$element.height() < 450
|
|
|
|
);
|
|
|
|
|
|
|
|
// ...So wait for the possible menuLayout transition to finish
|
|
|
|
setTimeout( function () {
|
|
|
|
// Give the input the right number of rows to fit the space
|
|
|
|
availableSpace = dialog.menuLayout.$content.height() - dialog.input.$element.position().top;
|
2018-06-07 23:11:59 +00:00
|
|
|
// TODO: Compute this line height from the skin
|
|
|
|
singleLineHeight = 21;
|
2017-01-11 21:50:35 +00:00
|
|
|
maxInputHeight = availableSpace - borderAndPadding;
|
|
|
|
minRows = Math.floor( maxInputHeight / singleLineHeight );
|
2018-01-11 14:07:29 +00:00
|
|
|
dialog.input.loadingPromise.done( function () {
|
|
|
|
dialog.input.setMinRows( minRows );
|
|
|
|
} ).fail( function () {
|
|
|
|
dialog.input.$input.attr( 'rows', minRows );
|
|
|
|
} );
|
2017-01-11 21:50:35 +00:00
|
|
|
}, OO.ui.theme.getDialogTransitionDuration() );
|
|
|
|
} );
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Handle the click event on the list
|
|
|
|
*
|
|
|
|
* @param {jQuery.Event} e Mouse click event
|
|
|
|
*/
|
|
|
|
ve.ui.MWLatexDialog.prototype.onListClick = function ( e ) {
|
|
|
|
var symbol = $( e.target ).data( 'symbol' ),
|
|
|
|
encapsulate = symbol.encapsulate,
|
|
|
|
insert = symbol.insert,
|
|
|
|
range = this.input.getRange();
|
|
|
|
|
2019-02-19 14:58:41 +00:00
|
|
|
if ( this.isReadOnly() ) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2017-01-11 21:50:35 +00:00
|
|
|
if ( encapsulate ) {
|
|
|
|
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 {
|
|
|
|
this.input.insertContent( insert );
|
|
|
|
}
|
|
|
|
};
|