User:ערן/veReplace.js

/* 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': 'Repalce all',

've-SearchAndReplaceDialog-replace': 'Repalce',

've-SearchAndReplaceDialog-matchcase': 'Match case',

've-SearchAndReplaceDialog-replace-complete': 'Found and replaced $1 occurrences',

've-ReplaceTool-ToolbarButton': 'Replace'

});

}

/* end of translations */

/*!

* VisualEditor replace gadget

*

* @copyright 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 );

});