mirror of
https://gerrit.wikimedia.org/r/mediawiki/extensions/VisualEditor
synced 2024-12-30 15:03:01 +00:00
cb657e0bdf
Change-Id: I9dbc12335016a326f00bf5b9a960551f828c685d
146 lines
5.3 KiB
JavaScript
146 lines
5.3 KiB
JavaScript
/*!
|
|
* VisualEditor UserInterface MWWikitextAction class.
|
|
*
|
|
* @copyright See AUTHORS.txt
|
|
*/
|
|
|
|
/**
|
|
* Content action.
|
|
*
|
|
* @class
|
|
* @extends ve.ui.Action
|
|
*
|
|
* @constructor
|
|
* @param {ve.ui.Surface} surface Surface to act on
|
|
*/
|
|
ve.ui.MWWikitextAction = function VeUiMWWikitextAction() {
|
|
// Parent constructor
|
|
ve.ui.MWWikitextAction.super.apply( this, arguments );
|
|
};
|
|
|
|
/* Inheritance */
|
|
|
|
OO.inheritClass( ve.ui.MWWikitextAction, ve.ui.Action );
|
|
|
|
/* Static Properties */
|
|
|
|
ve.ui.MWWikitextAction.static.name = 'mwWikitext';
|
|
|
|
ve.ui.MWWikitextAction.static.methods = [ 'toggleWrapSelection', 'wrapSelection', 'wrapLine' ];
|
|
|
|
/* Methods */
|
|
|
|
/**
|
|
* Wrap an selection inline
|
|
*
|
|
* @param {string} before Text to go before selection
|
|
* @param {string} after Text to go after selection
|
|
* @param {Function|string} placeholder Placeholder text to insert at an empty selection
|
|
* @param {Function} [expandOffsetsCallback] Function that returns a tuple of offsets to expand to selection to in order to get relevant text for unwrapping
|
|
* @param {Function} [unwrapOffsetsCallback] Function that returns a tuple of offsets to unwrap from the selected text,
|
|
* e.g. "''Foo'''" -> [2,3] to unwrap 2 from the left and 3 from the right
|
|
* @return {boolean} Action was executed
|
|
*/
|
|
ve.ui.MWWikitextAction.prototype.toggleWrapSelection = function ( before, after, placeholder, expandOffsetsCallback, unwrapOffsetsCallback ) {
|
|
const originalFragment = this.surface.getModel().getFragment( null, false, true /* excludeInsertions */ );
|
|
|
|
let fragment = originalFragment;
|
|
let textBefore, textAfter;
|
|
if ( expandOffsetsCallback ) {
|
|
const contextRange = fragment.expandLinearSelection( 'siblings' ).getSelection().getCoveringRange();
|
|
const data = fragment.getDocument().data;
|
|
const range = fragment.getSelection().getCoveringRange();
|
|
textBefore = data.getText( true, new ve.Range( contextRange.start, range.start ) );
|
|
textAfter = data.getText( true, new ve.Range( range.end, contextRange.end ) );
|
|
const expandOffsets = expandOffsetsCallback( textBefore, textAfter );
|
|
if ( expandOffsets ) {
|
|
fragment = originalFragment.adjustLinearSelection( expandOffsets[ 0 ], expandOffsets[ 1 ] );
|
|
}
|
|
}
|
|
|
|
if ( unwrapOffsetsCallback ) {
|
|
const unwrapOffsets = unwrapOffsetsCallback( fragment.getText(), textBefore, textAfter );
|
|
if ( unwrapOffsets ) {
|
|
fragment.unwrapText( unwrapOffsets[ 0 ], unwrapOffsets[ 1 ] );
|
|
} else {
|
|
fragment.wrapText( before, after, placeholder, true );
|
|
}
|
|
originalFragment.select();
|
|
return true;
|
|
}
|
|
|
|
fragment.wrapText( before, after, placeholder ).select();
|
|
originalFragment.select();
|
|
return true;
|
|
};
|
|
|
|
/**
|
|
* Wrap an selection inline
|
|
*
|
|
* @param {string} before Text to go before selection
|
|
* @param {string} after Text to go after selection
|
|
* @param {Function|string} placeholder Placeholder text to insert at an empty selection
|
|
* @return {boolean} Action was executed
|
|
*/
|
|
ve.ui.MWWikitextAction.prototype.wrapSelection = function ( before, after, placeholder ) {
|
|
const fragment = this.surface.getModel().getFragment( null, false, true /* excludeInsertions */ );
|
|
fragment.wrapText( before, after, placeholder ).select();
|
|
return true;
|
|
};
|
|
|
|
/**
|
|
* Wrap an selection as a block element on its own line
|
|
*
|
|
* If the selection is collapsed, it expands to take the whole line, otherwise it splits
|
|
* the paragraph to make sure it is one line
|
|
*
|
|
* @param {string} before Text to go before each line
|
|
* @param {string} after Text to go after each line
|
|
* @param {Function|string} placeholder Placeholder text to insert at an empty selection
|
|
* @param {Function} [unwrapOffsetsCallback] Function that returns a tuple of offsets to unwrap from the selected text,
|
|
* e.g. '== Foo ===' -> [2,3] to unwrap 2 from the left and 3 from the right
|
|
* @return {boolean} Action was executed
|
|
*/
|
|
ve.ui.MWWikitextAction.prototype.wrapLine = function ( before, after, placeholder, unwrapOffsetsCallback ) {
|
|
let originalFragment = this.surface.getModel().getFragment( null, false, true /* excludeInsertions */ );
|
|
const selectedNodes = originalFragment.getLeafNodes();
|
|
|
|
let unwrapped = false;
|
|
for ( let i = selectedNodes.length - 1; i >= 0; i-- ) {
|
|
if ( selectedNodes.length > 1 && selectedNodes[ i ].nodeRange.isCollapsed() ) {
|
|
continue;
|
|
}
|
|
const fragment = this.surface.getModel().getLinearFragment( selectedNodes[ i ].nodeRange, true );
|
|
const unwrapOffsets = unwrapOffsetsCallback && unwrapOffsetsCallback( fragment.getText() );
|
|
|
|
if ( selectedNodes.length === 1 && originalFragment.getSelection().isCollapsed() ) {
|
|
originalFragment = fragment;
|
|
}
|
|
|
|
if ( unwrapOffsets ) {
|
|
fragment.unwrapText( unwrapOffsets[ 0 ], unwrapOffsets[ 1 ] );
|
|
unwrapped = true;
|
|
}
|
|
|
|
const wrappedFragment = fragment.wrapText( before, after, placeholder );
|
|
if ( !unwrapped && wrappedFragment !== fragment ) {
|
|
if ( !ve.dm.LinearData.static.isElementData(
|
|
wrappedFragment.collapseToStart().adjustLinearSelection( -1, 0 ).getData()[ 0 ]
|
|
) ) {
|
|
wrappedFragment.collapseToStart().insertContent( [ { type: '/paragraph' }, { type: 'paragraph' } ] );
|
|
}
|
|
if ( !ve.dm.LinearData.static.isElementData(
|
|
wrappedFragment.collapseToEnd().adjustLinearSelection( 0, 1 ).getData()[ 0 ]
|
|
) ) {
|
|
wrappedFragment.collapseToEnd().insertContent( [ { type: '/paragraph' }, { type: 'paragraph' } ] );
|
|
}
|
|
}
|
|
}
|
|
originalFragment.select();
|
|
return true;
|
|
};
|
|
|
|
/* Registration */
|
|
|
|
ve.ui.actionFactory.register( ve.ui.MWWikitextAction );
|