MediaWiki:Gadget-veReplace.js

/* Gadget to add a Replace text button to Visual editor's Insert menu */

/* Translate the following to your language: */ mw.loader.using('ext.visualEditor.core').then(function {

if (!mw.messages.exists( 've-SearchAndReplaceDialog-title' )) { mw.messages.set({		've-SearchAndReplaceDialog-title': 'Search and replace',		've-SearchAndReplaceDialog-from-label': 'From:',		've-SearchAndReplaceDialog-to-label': 'To:',		've-SearchAndReplaceDialog-from-placeholder': 'From text',		've-SearchAndReplaceDialog-to-placeholder': 'To text',		've-SearchAndReplaceDialog-replaceAll': 'Replace all',		've-SearchAndReplaceDialog-replace': 'Replace text',		've-SearchAndReplaceDialog-matchcase': 'Match case',		've-SearchAndReplaceDialog-replace-complete': 'Found and replaced $1 occurrences',		've-ReplaceTool-ToolbarButton': 'Replace text'	}); } /* end of translations */

/*! * VisualEditor replace gadget * * @copyright Wikipedia [en:User:ערן|Eranroz] and Ravid ziv * @license The MIT License (MIT) */ function extractText{ var nodes = []; var model = ve.init.target.getSurface.getModel; function getTextNodes( obj ) { var i;		for ( i = 0; i < obj.children.length; i++ ) { if ( obj.children[i].type == 'text'){ nodes.push(obj.children[i]); }

if ( obj.children[i].children ) { getTextNodes( obj.children[i] ); }		}	}	getTextNodes(ve.init.target.getSurface.getModel.getDocument.getDocumentNode); return nodes; }

function searchAndReplace( fromText, toText, replaceAll, matchCase ) { var textNodes = extractText; var model = ve.init.target.getSurface.getModel; var firstIndex = 0; var numReplacements = 0; for (var nodeI = 0; nodeI < textNodes.length; nodeI++) { var node = textNodes[nodeI]; var nodeRange = node.getRange; var nodeText = model.getLinearFragment(nodeRange).getText;

var fromIndex = matchCase? nodeText.toUpperCase.indexOf( fromText.toUpperCase, firstIndex ) : nodeText.indexOf( fromText, firstIndex ); if ( fromIndex == -1 ) { firstIndex = 0; continue; }		var start = nodeRange.from+fromIndex; var end = start+fromText.length; if (!replaceAll && model.selection.start > start) { continue;//skip replacements before selection }		var removeRange = new ve.Range( start, end ); var transaction = ve.dm.Transaction.newFromReplacement(			ve.init.target.getSurface.getView.getDocument.model, 			removeRange, 			toText		); var newSelection = new ve.Range(0,0); if (!replaceAll) { newSelection = new ve.Range( start, start+toText.length ); }		ve.init.target.getSurface.getView.changeModel(transaction, newSelection); numReplacements++; if (!replaceAll) { break; }		firstIndex = fromIndex + toText.length; nodeI = nodeI -1; }	if (numReplacements==0 || replaceAll) { mw.notify( mw.msg( 've-SearchAndReplaceDialog-replace-complete', numReplacements ) ); } }

ve.ui.SearchAndReplaceDialog = function( manager, config ) { // Parent constructor ve.ui.SearchAndReplaceDialog.super.call( this, manager, config );

}; /* Inheritance */

OO.inheritClass( ve.ui.SearchAndReplaceDialog, ve.ui.FragmentDialog );

ve.ui.SearchAndReplaceDialog.prototype.getActionProcess = function ( action ) { var fromVal = this.fromInput.getValue, toVal = this.toInput.getValue, matchCase = this.matchCaseCheckbox.getValue;

if ( action === 'replace' ) { return new OO.ui.Process( function {			searchAndReplace( fromVal, toVal, false, matchCase );		}, this ); } else if ( action === 'replace-all' ) { return new OO.ui.Process( function {			searchAndReplace( fromVal, toVal, true, matchCase );			this.close;		}, this ); }	return ve.ui.MWMediaDialog.super.prototype.getActionProcess.call( this, action ); }

ve.ui.SearchAndReplaceDialog.prototype.getBodyHeight = function { return 200; };

/* Static Properties */ ve.ui.SearchAndReplaceDialog.static.name = 'search'; ve.ui.SearchAndReplaceDialog.static.title = mw.msg( 've-SearchAndReplaceDialog-title' ); ve.ui.SearchAndReplaceDialog.static.size = 'medium';

ve.ui.SearchAndReplaceDialog.static.actions = [ {		'action': 'replace', 'label': mw.msg( 've-SearchAndReplaceDialog-replace' ), 'flags': [ 'constructive' ], 'modes': 'insert' },	{		'label': OO.ui.deferMsg( 'visualeditor-dialog-action-cancel' ), 'flags': 'safe', 'modes': [ 'edit', 'insert', 'select' ] },	{		'action': 'replace-all', 'label': mw.msg( 've-SearchAndReplaceDialog-replaceAll' ), 'flags': [ 'constructive' ], 'modes': 'insert' } ];

ve.ui.SearchAndReplaceDialog.prototype.initialize = function { ve.ui.SearchAndReplaceDialog.super.prototype.initialize.call( this ); this.panel = new OO.ui.PanelLayout( { '$': this.$, 'scrollable': true, 'padded': true } ); this.inputsFieldset = new OO.ui.FieldsetLayout( {		'$': this.$	} ); // input from this.fromInput = new OO.ui.TextInputWidget(		{ '$': this.$, 'multiline': false, 'placeholder': mw.msg( 've-SearchAndReplaceDialog-from-placeholder' ) }	); //input to	this.toInput = new OO.ui.TextInputWidget(		{ '$': this.$, 'multiline': false, 'placeholder': mw.msg( 've-SearchAndReplaceDialog-to-placeholder' ) }	); this.fromField = new OO.ui.FieldLayout( this.fromInput, {		'$': this.$,		'label': mw.msg( 've-SearchAndReplaceDialog-from-label' )	} ); this.toField = new OO.ui.FieldLayout( this.toInput, {		'$': this.$,		'label': mw.msg( 've-SearchAndReplaceDialog-to-label' )	} );

this.matchCaseCheckbox = new OO.ui.CheckboxInputWidget( {		'$': this.$	} ); var matchCaseField = new OO.ui.FieldLayout( this.matchCaseCheckbox, {		'$': this.$,		'align': 'inline',		'label': mw.msg( 've-SearchAndReplaceDialog-matchcase' )	} );

this.inputsFieldset.$element.append(		this.fromField.$element,		this.toField.$element,		matchCaseField.$element	); this.panel.$element.append(	this.inputsFieldset.$element ); this.$body.append( this.panel.$element );

}

ve.ui.windowFactory.register( ve.ui.SearchAndReplaceDialog );

//-- replace tool -- function ReplaceTool( toolGroup, config ) { OO.ui.Tool.call( this, toolGroup, config ); } OO.inheritClass( ReplaceTool, OO.ui.Tool ); ReplaceTool.static.name = 'ReplaceTool'; ReplaceTool.static.title = mw.msg('ve-ReplaceTool-ToolbarButton'); ReplaceTool.prototype.onSelect = function { this.toolbar.getSurface.execute( 'window', 'open', 'search', null ); }; ReplaceTool.prototype.onUpdateState = function { this.setActive( false ); }; ve.ui.toolFactory.register( ReplaceTool ); });