]> scripts.mit.edu Git - autoinstallsdev/mediawiki.git/blobdiff - extensions/WikiEditor/modules/jquery.wikiEditor.dialogs.config.js
MediaWiki 1.30.2
[autoinstallsdev/mediawiki.git] / extensions / WikiEditor / modules / jquery.wikiEditor.dialogs.config.js
diff --git a/extensions/WikiEditor/modules/jquery.wikiEditor.dialogs.config.js b/extensions/WikiEditor/modules/jquery.wikiEditor.dialogs.config.js
new file mode 100644 (file)
index 0000000..ccbcead
--- /dev/null
@@ -0,0 +1,1258 @@
+/**
+ * Configuration of Dialog module for wikiEditor
+ */
+( function ( $, mw, OO ) {
+
+       var hasOwn = Object.prototype.hasOwnProperty;
+
+       $.wikiEditor.modules.dialogs.config = {
+
+               replaceIcons: function ( $textarea ) {
+                       $textarea
+                               .wikiEditor( 'removeFromToolbar', { section: 'main', group: 'insert', tool: 'xlink' } )
+                               .wikiEditor( 'removeFromToolbar', { section: 'main', group: 'insert', tool: 'ilink' } )
+                               .wikiEditor( 'removeFromToolbar', { section: 'main', group: 'insert', tool: 'file' } )
+                               .wikiEditor( 'removeFromToolbar', { section: 'main', group: 'insert', tool: 'reference' } )
+                               .wikiEditor( 'removeFromToolbar', { section: 'advanced', group: 'insert', tool: 'table' } )
+                               .wikiEditor( 'addToToolbar', {
+                                       section: 'main',
+                                       group: 'insert',
+                                       tools: {
+                                               link: {
+                                                       labelMsg: 'wikieditor-toolbar-tool-link',
+                                                       type: 'button',
+                                                       icon: 'insert-link.png',
+                                                       offset: [ 2, -1654 ],
+                                                       action: {
+                                                               type: 'dialog',
+                                                               module: 'insert-link'
+                                                       }
+                                               },
+                                               file: {
+                                                       labelMsg: 'wikieditor-toolbar-tool-file',
+                                                       type: 'button',
+                                                       icon: 'insert-file.png',
+                                                       offset: [ 2, -1438 ],
+                                                       action: {
+                                                               type: 'dialog',
+                                                               module: 'insert-file'
+                                                       }
+                                               },
+                                               reference: {
+                                                       labelMsg: 'wikieditor-toolbar-tool-reference',
+                                                       filters: [ 'body.ns-subject' ],
+                                                       type: 'button',
+                                                       icon: 'insert-reference.png',
+                                                       offset: [ 2, -1798 ],
+                                                       action: {
+                                                               type: 'dialog',
+                                                               module: 'insert-reference'
+                                                       }
+                                               }
+                                       }
+                               } )
+                               .wikiEditor( 'addToToolbar', {
+                                       section: 'advanced',
+                                       group: 'insert',
+                                       tools: {
+                                               table: {
+                                                       labelMsg: 'wikieditor-toolbar-tool-table',
+                                                       type: 'button',
+                                                       icon: 'insert-table.png',
+                                                       offset: [ 2, -1942 ],
+                                                       action: {
+                                                               type: 'dialog',
+                                                               module: 'insert-table'
+                                                       }
+                                               }
+                                       }
+                               } )
+                               .wikiEditor( 'addToToolbar', {
+                                       section: 'advanced',
+                                       groups: {
+                                               search: {
+                                                       tools: {
+                                                               replace: {
+                                                                       labelMsg: 'wikieditor-toolbar-tool-replace',
+                                                                       type: 'button',
+                                                                       icon: 'search-replace.png',
+                                                                       offset: [ -70, -214 ],
+                                                                       action: {
+                                                                               type: 'dialog',
+                                                                               module: 'search-and-replace'
+                                                                       }
+                                                               }
+                                                       }
+                                               }
+                                       }
+                               } );
+               },
+
+               getDefaultConfig: function () {
+                       return { dialogs: {
+                               'insert-link': {
+                                       titleMsg: 'wikieditor-toolbar-tool-link-title',
+                                       id: 'wikieditor-toolbar-link-dialog',
+                                       htmlTemplate: 'dialogInsertLink.html',
+
+                                       init: function () {
+                                               var api = new mw.Api();
+
+                                               function isExternalLink( s ) {
+                                                       // The following things are considered to be external links:
+                                                       // * Starts with a URL protocol
+                                                       // * Starts with www.
+                                                       // All of these are potentially valid titles, and the latter two categories match about 6300
+                                                       // titles in enwiki's ns0. Out of 6.9M titles, that's 0.09%
+                                                       /* eslint-disable no-caller */
+                                                       if ( typeof arguments.callee.regex === 'undefined' ) {
+                                                               // Cache the regex
+                                                               arguments.callee.regex =
+                                                                       new RegExp( '^(' + mw.config.get( 'wgUrlProtocols' ) + '|www\\.)', 'i' );
+                                                       }
+                                                       return s.match( arguments.callee.regex );
+                                                       /* eslint-enable no-caller */
+                                               }
+
+                                               // Updates the status indicator above the target link
+                                               function updateWidget( status ) {
+                                                       $( '#wikieditor-toolbar-link-int-target-status' ).children().hide();
+                                                       $( '#wikieditor-toolbar-link-int-target' ).parent()
+                                                               .removeClass(
+                                                                       'status-invalid status-external status-notexists status-exists status-loading'
+                                                               );
+                                                       if ( status ) {
+                                                               $( '#wikieditor-toolbar-link-int-target-status-' + status ).show();
+                                                               $( '#wikieditor-toolbar-link-int-target' ).parent().addClass( 'status-' + status );
+                                                       }
+                                                       if ( status === 'invalid' ) {
+                                                               $( '.ui-dialog:visible .ui-dialog-buttonpane button:first' )
+                                                                       .prop( 'disabled', true )
+                                                                       .addClass( 'disabled' );
+                                                       } else {
+                                                               $( '.ui-dialog:visible .ui-dialog-buttonpane button:first' )
+                                                                       .prop( 'disabled', false )
+                                                                       .removeClass( 'disabled' );
+                                                       }
+                                               }
+
+                                               // Updates the UI to show if the page title being inputted by the user exists or not
+                                               // accepts parameter internal for bypassing external link detection
+                                               function updateExistence( internal ) {
+                                                       // Abort previous request
+                                                       var request = $( '#wikieditor-toolbar-link-int-target-status' ).data( 'request' ),
+                                                               target = $( '#wikieditor-toolbar-link-int-target' ).val(),
+                                                               cache = $( '#wikieditor-toolbar-link-int-target-status' ).data( 'existencecache' );
+                                                       // ensure the internal parameter is a boolean
+                                                       if ( internal !== true ) {
+                                                               internal = false;
+                                                       }
+                                                       if ( request ) {
+                                                               request.abort();
+                                                       }
+                                                       if ( hasOwn.call( cache, target ) ) {
+                                                               updateWidget( cache[ target ] );
+                                                               return;
+                                                       }
+                                                       if ( target.replace( /^\s+$/, '' ) === '' ) {
+                                                               // Hide the widget when the textbox is empty
+                                                               updateWidget( false );
+                                                               return;
+                                                       }
+                                                       // If the forced internal parameter was not true, check if the target is an external link
+                                                       if ( !internal && isExternalLink( target ) ) {
+                                                               updateWidget( 'external' );
+                                                               return;
+                                                       }
+                                                       if ( target.indexOf( '|' ) !== -1 ) {
+                                                               // Title contains | , which means it's invalid
+                                                               // but confuses the API. Show invalid and bypass API
+                                                               updateWidget( 'invalid' );
+                                                               return;
+                                                       }
+                                                       // Show loading spinner while waiting for the API to respond
+                                                       updateWidget( 'loading' );
+                                                       // Call the API to check page status, saving the request object so it can be aborted if
+                                                       // necessary.
+                                                       // This used to request a page that would show whether or not the target exists, but we can
+                                                       // also check whether it has the disambiguation property and still get existence information.
+                                                       // If the Disambiguator extension is not installed then such a property won't be set.
+                                                       $( '#wikieditor-toolbar-link-int-target-status' ).data(
+                                                               'request',
+                                                               api.get( {
+                                                                       formatversion: 2,
+                                                                       action: 'query',
+                                                                       prop: 'pageprops',
+                                                                       titles: target,
+                                                                       ppprop: 'disambiguation'
+                                                               } ).done( function ( data ) {
+                                                                       var status, page;
+                                                                       if ( !data.query || !data.query.pages ) {
+                                                                               // This happens in some weird cases like interwiki links
+                                                                               status = false;
+                                                                       } else {
+                                                                               page = data.query.pages[ 0 ];
+                                                                               status = 'exists';
+                                                                               if ( page.missing ) {
+                                                                                       status = 'notexists';
+                                                                               } else if ( page.invalid ) {
+                                                                                       status = 'invalid';
+                                                                               } else if ( page.pageprops ) {
+                                                                                       status = 'disambig';
+                                                                               }
+                                                                       }
+                                                                       // Cache the status of the link target if the force internal
+                                                                       // parameter was not passed
+                                                                       if ( !internal ) {
+                                                                               cache[ target ] = status;
+                                                                       }
+                                                                       updateWidget( status );
+                                                               } )
+                                                       );
+                                               }
+                                               $( '#wikieditor-toolbar-link-type-int, #wikieditor-toolbar-link-type-ext' ).click( function () {
+                                                       var request;
+                                                       if ( $( '#wikieditor-toolbar-link-type-ext' ).prop( 'checked' ) ) {
+                                                               // Abort previous request
+                                                               request = $( '#wikieditor-toolbar-link-int-target-status' ).data( 'request' );
+                                                               if ( request ) {
+                                                                       request.abort();
+                                                               }
+                                                               updateWidget( 'external' );
+                                                       }
+                                                       if ( $( '#wikieditor-toolbar-link-type-int' ).prop( 'checked' ) ) {
+                                                               updateExistence( true );
+                                                       }
+                                               } );
+                                               // Set labels of tabs based on rel values
+                                               $( this ).find( '[rel]' ).each( function () {
+                                                       $( this ).text( mw.msg( $( this ).attr( 'rel' ) ) );
+                                               } );
+                                               // Set tabindexes on form fields
+                                               $.wikiEditor.modules.dialogs.fn.setTabindexes( $( this ).find( 'input' ).not( '[tabindex]' ) );
+                                               // Setup the tooltips in the textboxes
+                                               $( '#wikieditor-toolbar-link-int-target' )
+                                                       .data( 'tooltip', mw.msg( 'wikieditor-toolbar-tool-link-int-target-tooltip' ) );
+                                               $( '#wikieditor-toolbar-link-int-text' )
+                                                       .data( 'tooltip', mw.msg( 'wikieditor-toolbar-tool-link-int-text-tooltip' ) );
+                                               $( '#wikieditor-toolbar-link-int-target, #wikieditor-toolbar-link-int-text' )
+                                                       .each( function () {
+                                                               if ( $( this ).val() === '' ) {
+                                                                       $( this )
+                                                                               .addClass( 'wikieditor-toolbar-dialog-hint' )
+                                                                               .val( $( this ).data( 'tooltip' ) )
+                                                                               .data( 'tooltip-mode', true );
+                                                               }
+                                                       } )
+                                                       .on( 'focus', function () {
+                                                               if ( $( this ).val() === $( this ).data( 'tooltip' ) ) {
+                                                                       $( this )
+                                                                               .val( '' )
+                                                                               .removeClass( 'wikieditor-toolbar-dialog-hint' )
+                                                                               .data( 'tooltip-mode', false );
+                                                               }
+                                                       } )
+                                                       .on( 'change', function () {
+                                                               if ( $( this ).val() !== $( this ).data( 'tooltip' ) ) {
+                                                                       $( this )
+                                                                               .removeClass( 'wikieditor-toolbar-dialog-hint' )
+                                                                               .data( 'tooltip-mode', false );
+                                                               }
+                                                       } )
+                                                       .on( 'blur', function () {
+                                                               if ( $( this ).val() === '' ) {
+                                                                       $( this )
+                                                                               .addClass( 'wikieditor-toolbar-dialog-hint' )
+                                                                               .val( $( this ).data( 'tooltip' ) )
+                                                                               .data( 'tooltip-mode', true );
+                                                               }
+                                                       } );
+
+                                               // Automatically copy the value of the internal link page title field to the link text field unless the
+                                               // user has changed the link text field - this is a convenience thing since most link texts are going to
+                                               // be the the same as the page title - Also change the internal/external radio button accordingly
+                                               $( '#wikieditor-toolbar-link-int-target' ).on( 'change keydown paste cut', function () {
+                                                       // $( this ).val() is the old value, before the keypress - Defer this until $( this ).val() has
+                                                       // been updated
+                                                       setTimeout( function () {
+                                                               if ( isExternalLink( $( '#wikieditor-toolbar-link-int-target' ).val() ) ) {
+                                                                       $( '#wikieditor-toolbar-link-type-ext' ).prop( 'checked', true );
+                                                                       updateWidget( 'external' );
+                                                               } else {
+                                                                       $( '#wikieditor-toolbar-link-type-int' ).prop( 'checked', true );
+                                                                       updateExistence();
+                                                               }
+                                                               if ( $( '#wikieditor-toolbar-link-int-text' ).data( 'untouched' ) ) {
+                                                                       // eslint-disable-next-line eqeqeq
+                                                                       if ( $( '#wikieditor-toolbar-link-int-target' ).val() ==
+                                                                               $( '#wikieditor-toolbar-link-int-target' ).data( 'tooltip' )
+                                                                       ) {
+                                                                               $( '#wikieditor-toolbar-link-int-text' )
+                                                                                       .addClass( 'wikieditor-toolbar-dialog-hint' )
+                                                                                       .val( $( '#wikieditor-toolbar-link-int-text' ).data( 'tooltip' ) )
+                                                                                       .change();
+                                                                       } else {
+                                                                               $( '#wikieditor-toolbar-link-int-text' )
+                                                                                       .val( $( '#wikieditor-toolbar-link-int-target' ).val() )
+                                                                                       .change();
+                                                                       }
+                                                               }
+                                                       }, 0 );
+                                               } );
+                                               $( '#wikieditor-toolbar-link-int-text' ).on( 'change keydown paste cut', function () {
+                                                       var oldVal = $( this ).val(),
+                                                               that = this;
+                                                       setTimeout( function () {
+                                                               if ( $( that ).val() !== oldVal ) {
+                                                                       $( that ).data( 'untouched', false );
+                                                               }
+                                                       }, 0 );
+                                               } );
+                                               // Add images to the page existence widget, which will be shown mutually exclusively to communicate if
+                                               // the page exists, does not exist or the title is invalid (like if it contains a | character)
+                                               $( '#wikieditor-toolbar-link-int-target-status' )
+                                                       .append( $( '<div>' )
+                                                               .attr( 'id', 'wikieditor-toolbar-link-int-target-status-exists' )
+                                                               .text( mw.msg( 'wikieditor-toolbar-tool-link-int-target-status-exists' ) )
+                                                       )
+                                                       .append( $( '<div>' )
+                                                               .attr( 'id', 'wikieditor-toolbar-link-int-target-status-notexists' )
+                                                               .text( mw.msg( 'wikieditor-toolbar-tool-link-int-target-status-notexists' ) )
+                                                       )
+                                                       .append( $( '<div>' )
+                                                               .attr( 'id', 'wikieditor-toolbar-link-int-target-status-invalid' )
+                                                               .text( mw.msg( 'wikieditor-toolbar-tool-link-int-target-status-invalid' ) )
+                                                       )
+                                                       .append( $( '<div>' )
+                                                               .attr( 'id', 'wikieditor-toolbar-link-int-target-status-external' )
+                                                               .text( mw.msg( 'wikieditor-toolbar-tool-link-int-target-status-external' ) )
+                                                       )
+                                                       .append( $( '<div>' )
+                                                               .attr( 'id', 'wikieditor-toolbar-link-int-target-status-loading' )
+                                                               .attr( 'title', mw.msg( 'wikieditor-toolbar-tool-link-int-target-status-loading' ) )
+                                                       )
+                                                       .append( $( '<div>' )
+                                                               .attr( 'id', 'wikieditor-toolbar-link-int-target-status-disambig' )
+                                                               .text( mw.msg( 'wikieditor-toolbar-tool-link-int-target-status-disambig' ) )
+                                                       )
+                                                       .data( 'existencecache', {} )
+                                                       .children().hide();
+
+                                               $( '#wikieditor-toolbar-link-int-target' )
+                                                       .on( 'keyup paste cut', function () {
+                                                               var timerID;
+                                                               // Cancel the running timer if applicable
+                                                               if ( typeof $( this ).data( 'timerID' ) !== 'undefined' ) {
+                                                                       clearTimeout( $( this ).data( 'timerID' ) );
+                                                               }
+                                                               // Delay fetch for a while
+                                                               // FIXME: Make 120 configurable elsewhere
+                                                               timerID = setTimeout( updateExistence, 120 );
+                                                               $( this ).data( 'timerID', timerID );
+                                                       } )
+                                                       .on( 'change', function () {
+                                                               // Cancel the running timer if applicable
+                                                               if ( typeof $( this ).data( 'timerID' ) !== 'undefined' ) {
+                                                                       clearTimeout( $( this ).data( 'timerID' ) );
+                                                               }
+                                                               // Fetch right now
+                                                               updateExistence();
+                                                       } );
+
+                                               // Title suggestions
+                                               $( '#wikieditor-toolbar-link-int-target' ).data( 'suggcache', {} ).suggestions( {
+                                                       fetch: function () {
+                                                               var cache, request,
+                                                                       that = this,
+                                                                       title = $( this ).val();
+
+                                                               if ( isExternalLink( title ) || title.indexOf( '|' ) !== -1 || title === '' ) {
+                                                                       $( this ).suggestions( 'suggestions', [] );
+                                                                       return;
+                                                               }
+
+                                                               cache = $( this ).data( 'suggcache' );
+                                                               if ( hasOwn.call( cache, title ) ) {
+                                                                       $( this ).suggestions( 'suggestions', cache[ title ] );
+                                                                       return;
+                                                               }
+
+                                                               request = api.get( {
+                                                                       formatversion: 2,
+                                                                       action: 'opensearch',
+                                                                       search: title,
+                                                                       namespace: 0,
+                                                                       suggest: ''
+                                                               } )
+                                                               .done( function ( data ) {
+                                                                       cache[ title ] = data[ 1 ];
+                                                                       $( that ).suggestions( 'suggestions', data[ 1 ] );
+                                                               } );
+                                                               $( this ).data( 'request', request );
+                                                       },
+                                                       cancel: function () {
+                                                               var request = $( this ).data( 'request' );
+                                                               if ( request ) {
+                                                                       request.abort();
+                                                               }
+                                                       }
+                                               } );
+                                       },
+                                       dialog: {
+                                               width: 500,
+                                               dialogClass: 'wikiEditor-toolbar-dialog',
+                                               buttons: {
+                                                       'wikieditor-toolbar-tool-link-insert': function () {
+                                                               var match, buttons, escTarget, escText,
+                                                                       that = this,
+                                                                       insertText = '',
+                                                                       whitespace = $( '#wikieditor-toolbar-link-dialog' ).data( 'whitespace' ),
+                                                                       target = $( '#wikieditor-toolbar-link-int-target' ).val(),
+                                                                       text = $( '#wikieditor-toolbar-link-int-text' ).val();
+
+                                                               function escapeInternalText( s ) {
+                                                                       return s.replace( /(\]{2,})/g, '<nowiki>$1</nowiki>' );
+                                                               }
+                                                               function escapeExternalTarget( s ) {
+                                                                       return s.replace( / /g, '%20' )
+                                                                               .replace( /\[/g, '%5B' )
+                                                                               .replace( /\]/g, '%5D' );
+                                                               }
+                                                               function escapeExternalText( s ) {
+                                                                       return s.replace( /(\]+)/g, '<nowiki>$1</nowiki>' );
+                                                               }
+                                                               // check if the tooltips were passed as target or text
+                                                               if ( $( '#wikieditor-toolbar-link-int-target' ).data( 'tooltip-mode' ) ) {
+                                                                       target = '';
+                                                               }
+                                                               if ( $( '#wikieditor-toolbar-link-int-text' ).data( 'tooltip-mode' ) ) {
+                                                                       text = '';
+                                                               }
+                                                               if ( target === '' ) {
+                                                                       // eslint-disable-next-line no-alert
+                                                                       alert( mw.msg( 'wikieditor-toolbar-tool-link-empty' ) );
+                                                                       return;
+                                                               }
+                                                               if ( $.trim( text ) === '' ) {
+                                                                       // [[Foo| ]] creates an invisible link
+                                                                       // Instead, generate [[Foo|]]
+                                                                       text = '';
+                                                               }
+                                                               if ( $( '#wikieditor-toolbar-link-type-int' ).is( ':checked' ) ) {
+                                                                       // FIXME: Exactly how fragile is this?
+                                                                       if ( $( '#wikieditor-toolbar-link-int-target-status-invalid' ).is( ':visible' ) ) {
+                                                                               // Refuse to add links to invalid titles
+                                                                               // eslint-disable-next-line no-alert
+                                                                               alert( mw.msg( 'wikieditor-toolbar-tool-link-int-invalid' ) );
+                                                                               return;
+                                                                       }
+
+                                                                       if ( target === text || !text.length ) {
+                                                                               insertText = '[[' + target + ']]';
+                                                                       } else {
+                                                                               insertText = '[[' + target + '|' + escapeInternalText( text ) + ']]';
+                                                                       }
+                                                               } else {
+                                                                       target = $.trim( target );
+                                                                       // Prepend http:// if there is no protocol
+                                                                       if ( !target.match( /^[a-z]+:\/\/./ ) ) {
+                                                                               target = 'http://' + target;
+                                                                       }
+
+                                                                       // Detect if this is really an internal link in disguise
+                                                                       match = target.match( $( this ).data( 'articlePathRegex' ) );
+                                                                       if ( match && !$( this ).data( 'ignoreLooksInternal' ) ) {
+                                                                               buttons = {};
+                                                                               buttons[ mw.msg( 'wikieditor-toolbar-tool-link-lookslikeinternal-int' ) ] =
+                                                                                       function () {
+                                                                                               $( '#wikieditor-toolbar-link-int-target' ).val( match[ 1 ] ).change();
+                                                                                               $( this ).dialog( 'close' );
+                                                                                       };
+                                                                               buttons[ mw.msg( 'wikieditor-toolbar-tool-link-lookslikeinternal-ext' ) ] =
+                                                                                       function () {
+                                                                                               $( that ).data( 'ignoreLooksInternal', true );
+                                                                                               $( that ).closest( '.ui-dialog' ).find( 'button:first' ).click();
+                                                                                               $( that ).data( 'ignoreLooksInternal', false );
+                                                                                               $( this ).dialog( 'close' );
+                                                                                       };
+                                                                               $.wikiEditor.modules.dialogs.quickDialog(
+                                                                                       mw.msg( 'wikieditor-toolbar-tool-link-lookslikeinternal', match[ 1 ] ),
+                                                                                       { buttons: buttons }
+                                                                               );
+                                                                               return;
+                                                                       }
+
+                                                                       escTarget = escapeExternalTarget( target );
+                                                                       escText = escapeExternalText( text );
+
+                                                                       if ( escTarget === escText ) {
+                                                                               insertText = escTarget;
+                                                                       } else if ( text === '' ) {
+                                                                               insertText = '[' + escTarget + ']';
+                                                                       } else {
+                                                                               insertText = '[' + escTarget + ' ' + escText + ']';
+                                                                       }
+                                                               }
+                                                               // Preserve whitespace in selection when replacing
+                                                               if ( whitespace ) {
+                                                                       insertText = whitespace[ 0 ] + insertText + whitespace[ 1 ];
+                                                               }
+                                                               $( this ).dialog( 'close' );
+                                                               $.wikiEditor.modules.toolbar.fn.doAction( $( this ).data( 'context' ), {
+                                                                       type: 'replace',
+                                                                       options: {
+                                                                               pre: insertText
+                                                                       }
+                                                               }, $( this ) );
+
+                                                               // Blank form
+                                                               $( '#wikieditor-toolbar-link-int-target, #wikieditor-toolbar-link-int-text' ).val( '' );
+                                                               $( '#wikieditor-toolbar-link-type-int, #wikieditor-toolbar-link-type-ext' )
+                                                                       .prop( 'checked', false );
+                                                       },
+                                                       'wikieditor-toolbar-tool-link-cancel': function () {
+                                                               $( this ).dialog( 'close' );
+                                                       }
+                                               },
+                                               open: function () {
+                                                       var target, text, type, matches, context, selection,
+                                                               // Obtain the server name without the protocol. wgServer may be protocol-relative
+                                                               serverName = mw.config.get( 'wgServer' ).replace( /^(https?:)?\/\//, '' );
+                                                       // Cache the articlepath regex
+                                                       $( this ).data( 'articlePathRegex', new RegExp(
+                                                               '^https?://' + mw.RegExp.escape( serverName + mw.config.get( 'wgArticlePath' ) )
+                                                                       .replace( /\\\$1/g, '(.*)' ) + '$'
+                                                       ) );
+                                                       // Pre-fill the text fields based on the current selection
+                                                       context = $( this ).data( 'context' );
+                                                       selection = context.$textarea.textSelection( 'getSelection' );
+                                                       $( '#wikieditor-toolbar-link-int-target' ).focus();
+                                                       // Trigger the change event, so the link status indicator is up to date
+                                                       $( '#wikieditor-toolbar-link-int-target' ).change();
+                                                       $( '#wikieditor-toolbar-link-dialog' ).data( 'whitespace', [ '', '' ] );
+                                                       if ( selection !== '' ) {
+                                                               if ( ( matches = selection.match( /^(\s*)\[\[([^\]\|]+)(\|([^\]\|]*))?\]\](\s*)$/ ) ) ) {
+                                                                       // [[foo|bar]] or [[foo]]
+                                                                       target = matches[ 2 ];
+                                                                       text = ( matches[ 4 ] ? matches[ 4 ] : matches[ 2 ] );
+                                                                       type = 'int';
+                                                                       // Preserve whitespace when replacing
+                                                                       $( '#wikieditor-toolbar-link-dialog' ).data( 'whitespace', [ matches[ 1 ], matches[ 5 ] ] );
+                                                               } else if ( ( matches = selection.match( /^(\s*)\[([^\] ]+)( ([^\]]+))?\](\s*)$/ ) ) ) {
+                                                                       // [http://www.example.com foo] or [http://www.example.com]
+                                                                       target = matches[ 2 ];
+                                                                       text = ( matches[ 4 ] || '' );
+                                                                       type = 'ext';
+                                                                       // Preserve whitespace when replacing
+                                                                       $( '#wikieditor-toolbar-link-dialog' ).data( 'whitespace', [ matches[ 1 ], matches[ 5 ] ] );
+                                                               } else {
+                                                                       // Trim any leading and trailing whitespace from the selection,
+                                                                       // but preserve it when replacing
+                                                                       target = text = $.trim( selection );
+                                                                       if ( target.length < selection.length ) {
+                                                                               $( '#wikieditor-toolbar-link-dialog' ).data( 'whitespace', [
+                                                                                       selection.substr( 0, selection.indexOf( target.charAt( 0 ) ) ),
+                                                                                       selection.substr(
+                                                                                               selection.lastIndexOf( target.charAt( target.length - 1 ) ) + 1
+                                                                                       ) ]
+                                                                               );
+                                                                       }
+                                                               }
+
+                                                               // Change the value by calling val() doesn't trigger the change event, so let's do that
+                                                               // ourselves
+                                                               if ( typeof text !== 'undefined' ) {
+                                                                       $( '#wikieditor-toolbar-link-int-text' ).val( text ).change();
+                                                               }
+                                                               if ( typeof target !== 'undefined' ) {
+                                                                       $( '#wikieditor-toolbar-link-int-target' ).val( target ).change();
+                                                               }
+                                                               if ( typeof type !== 'undefined' ) {
+                                                                       $( '#wikieditor-toolbar-link-' + type ).prop( 'checked', true );
+                                                               }
+                                                       }
+                                                       $( '#wikieditor-toolbar-link-int-text' ).data( 'untouched',
+                                                               $( '#wikieditor-toolbar-link-int-text' ).val() ===
+                                                                               $( '#wikieditor-toolbar-link-int-target' ).val() ||
+                                                                       $( '#wikieditor-toolbar-link-int-text' ).hasClass( 'wikieditor-toolbar-dialog-hint' )
+                                                       );
+                                                       $( '#wikieditor-toolbar-link-int-target' ).suggestions();
+
+                                                       // don't overwrite user's text
+                                                       if ( selection !== '' ) {
+                                                               $( '#wikieditor-toolbar-link-int-text' ).data( 'untouched', false );
+                                                       }
+
+                                                       $( '#wikieditor-toolbar-link-int-text, #wikiedit-toolbar-link-int-target' )
+                                                               .each( function () {
+                                                                       if ( $( this ).val() === '' ) {
+                                                                               $( this ).parent().find( 'label' ).show();
+                                                                       }
+                                                               } );
+
+                                                       if ( !$( this ).data( 'dialogkeypressset' ) ) {
+                                                               $( this ).data( 'dialogkeypressset', true );
+                                                               // Execute the action associated with the first button
+                                                               // when the user presses Enter
+                                                               $( this ).closest( '.ui-dialog' ).keypress( function ( e ) {
+                                                                       var button;
+                                                                       if ( ( e.keyCode || e.which ) === 13 ) {
+                                                                               button = $( this ).data( 'dialogaction' ) || $( this ).find( 'button:first' );
+                                                                               button.click();
+                                                                               e.preventDefault();
+                                                                       }
+                                                               } );
+
+                                                               // Make tabbing to a button and pressing
+                                                               // Enter do what people expect
+                                                               $( this ).closest( '.ui-dialog' ).find( 'button' ).focus( function () {
+                                                                       $( this ).closest( '.ui-dialog' ).data( 'dialogaction', this );
+                                                               } );
+                                                       }
+                                               }
+                                       }
+                               },
+                               'insert-reference': {
+                                       titleMsg: 'wikieditor-toolbar-tool-reference-title',
+                                       id: 'wikieditor-toolbar-reference-dialog',
+                                       htmlTemplate: 'dialogInsertReference.html',
+                                       init: function () {
+                                               // Insert translated strings into labels
+                                               $( this ).find( '[rel]' ).each( function () {
+                                                       $( this ).text( mw.msg( $( this ).attr( 'rel' ) ) );
+                                               } );
+
+                                       },
+                                       dialog: {
+                                               dialogClass: 'wikiEditor-toolbar-dialog',
+                                               width: 590,
+                                               buttons: {
+                                                       'wikieditor-toolbar-tool-reference-insert': function () {
+                                                               var insertText = $( '#wikieditor-toolbar-reference-text' ).val(),
+                                                                       whitespace = $( '#wikieditor-toolbar-reference-dialog' ).data( 'whitespace' ),
+                                                                       attributes = $( '#wikieditor-toolbar-reference-dialog' ).data( 'attributes' );
+                                                               // Close the dialog
+                                                               $( this ).dialog( 'close' );
+                                                               $.wikiEditor.modules.toolbar.fn.doAction(
+                                                                       $( this ).data( 'context' ),
+                                                                       {
+                                                                               type: 'replace',
+                                                                               options: {
+                                                                                       pre: whitespace[ 0 ] + '<ref' + attributes + '>',
+                                                                                       peri: insertText,
+                                                                                       post: '</ref>' + whitespace[ 1 ]
+                                                                               }
+                                                                       },
+                                                                       $( this )
+                                                               );
+                                                               // Restore form state
+                                                               $( '#wikieditor-toolbar-reference-text' ).val( '' );
+                                                       },
+                                                       'wikieditor-toolbar-tool-reference-cancel': function () {
+                                                               $( this ).dialog( 'close' );
+                                                       }
+                                               },
+                                               open: function () {
+                                                       // Pre-fill the text fields based on the current selection
+                                                       var matches, text,
+                                                               context = $( this ).data( 'context' ),
+                                                               selection = context.$textarea.textSelection( 'getSelection' );
+                                                       // set focus
+                                                       $( '#wikieditor-toolbar-reference-text' ).focus();
+                                                       $( '#wikieditor-toolbar-reference-dialog' )
+                                                               .data( 'whitespace', [ '', '' ] )
+                                                               .data( 'attributes', '' );
+                                                       if ( selection !== '' ) {
+                                                               if ( ( matches = selection.match( /^(\s*)<ref([^\>]*)>([^<]*)<\/ref\>(\s*)$/ ) ) ) {
+                                                                       text = matches[ 3 ];
+                                                                       // Preserve whitespace when replacing
+                                                                       $( '#wikieditor-toolbar-reference-dialog' )
+                                                                               .data( 'whitespace', [ matches[ 1 ], matches[ 4 ] ] );
+                                                                       $( '#wikieditor-toolbar-reference-dialog' ).data( 'attributes', matches[ 2 ] );
+                                                               } else {
+                                                                       text = selection;
+                                                               }
+                                                               $( '#wikieditor-toolbar-reference-text' ).val( text );
+                                                       }
+                                                       if ( !( $( this ).data( 'dialogkeypressset' ) ) ) {
+                                                               $( this ).data( 'dialogkeypressset', true );
+                                                               // Execute the action associated with the first button
+                                                               // when the user presses Enter
+                                                               $( this ).closest( '.ui-dialog' ).keypress( function ( e ) {
+                                                                       var button;
+                                                                       if ( ( e.keyCode || e.which ) === 13 ) {
+                                                                               button = $( this ).data( 'dialogaction' ) || $( this ).find( 'button:first' );
+                                                                               button.click();
+                                                                               e.preventDefault();
+                                                                       }
+                                                               } );
+                                                               // Make tabbing to a button and pressing
+                                                               // Enter do what people expect
+                                                               $( this ).closest( '.ui-dialog' ).find( 'button' ).focus( function () {
+                                                                       $( this ).closest( '.ui-dialog' ).data( 'dialogaction', this );
+                                                               } );
+                                                       }
+                                               }
+                                       }
+                               },
+                               'insert-file': {
+                                       titleMsg: 'wikieditor-toolbar-tool-file-title',
+                                       id: 'wikieditor-toolbar-file-dialog',
+                                       htmlTemplate: 'dialogInsertFile.html',
+                                       init: function () {
+                                               var magicWordsI18N = mw.config.get( 'wgWikiEditorMagicWords' ),
+                                                       defaultMsg = mw.msg( 'wikieditor-toolbar-file-default' );
+                                               $( this )
+                                                       .find( '[data-i18n-magic]' )
+                                                               .text( function () {
+                                                                       return magicWordsI18N[ $( this ).attr( 'data-i18n-magic' ) ];
+                                                               } )
+                                                               .removeAttr( 'data-i18n-magic' )
+                                                               .end()
+                                                       .find( '#wikieditor-toolbar-file-size' )
+                                                               .attr( 'placeholder', defaultMsg )
+                                                               // The message may be long in some languages
+                                                               .attr( 'size', defaultMsg.length )
+                                                               .end()
+                                                       .find( '[rel]' )
+                                                               .text( function () {
+                                                                       return mw.msg( $( this ).attr( 'rel' ) );
+                                                               } )
+                                                               .removeAttr( 'rel' )
+                                                               .end();
+                                       },
+                                       dialog: {
+                                               resizable: false,
+                                               dialogClass: 'wikiEditor-toolbar-dialog',
+                                               width: 590,
+                                               buttons: {
+                                                       'wikieditor-toolbar-tool-file-insert': function () {
+                                                               var fileName, caption, fileFloat, fileFormat, fileSize, fileTitle,
+                                                                       options, fileUse,
+                                                                       hasPxRgx = /.+px$/,
+                                                                       magicWordsI18N = mw.config.get( 'wgWikiEditorMagicWords' );
+                                                               fileName = $( '#wikieditor-toolbar-file-target' ).val();
+                                                               caption = $( '#wikieditor-toolbar-file-caption' ).val();
+                                                               fileFloat = $( '#wikieditor-toolbar-file-float' ).val();
+                                                               fileFormat = $( '#wikieditor-toolbar-file-format' ).val();
+                                                               fileSize = $( '#wikieditor-toolbar-file-size' ).val();
+                                                               // Append px to end to size if not already contains it
+                                                               if ( fileSize !== '' && !hasPxRgx.test( fileSize ) ) {
+                                                                       fileSize += 'px';
+                                                               }
+                                                               if ( fileName !== '' ) {
+                                                                       fileTitle = new mw.Title( fileName );
+                                                                       // Append file namespace prefix to filename if not already contains it
+                                                                       if ( fileTitle.getNamespaceId() !== 6 ) {
+                                                                               fileTitle = new mw.Title( fileName, 6 );
+                                                                       }
+                                                                       fileName = fileTitle.toText();
+                                                               }
+                                                               options = [ fileSize, fileFormat, fileFloat ];
+                                                               // Filter empty values
+                                                               options = $.grep( options, function ( val ) {
+                                                                       return val.length && val !== 'default';
+                                                               } );
+                                                               if ( caption.length ) {
+                                                                       options.push( caption );
+                                                               }
+                                                               fileUse = options.length === 0 ? fileName : ( fileName + '|' + options.join( '|' ) );
+                                                               $( this ).dialog( 'close' );
+                                                               $.wikiEditor.modules.toolbar.fn.doAction(
+                                                                       $( this ).data( 'context' ),
+                                                                       {
+                                                                               type: 'replace',
+                                                                               options: {
+                                                                                       pre: '[[',
+                                                                                       peri: fileUse,
+                                                                                       post: ']]',
+                                                                                       ownline: true
+                                                                               }
+                                                                       },
+                                                                       $( this )
+                                                               );
+
+                                                               // Restore form state
+                                                               $( [ '#wikieditor-toolbar-file-target',
+                                                                       '#wikieditor-toolbar-file-caption',
+                                                                       '#wikieditor-toolbar-file-size' ].join( ',' )
+                                                               ).val( '' );
+                                                               $( '#wikieditor-toolbar-file-float' ).val( 'default' );
+                                                               $( '#wikieditor-toolbar-file-format' ).val( magicWordsI18N.img_thumbnail );
+                                                       },
+                                                       'wikieditor-toolbar-tool-file-cancel': function () {
+                                                               $( this ).dialog( 'close' );
+                                                       },
+                                                       'wikieditor-toolbar-tool-file-upload': function () {
+                                                               var windowManager = new OO.ui.WindowManager(),
+                                                                       uploadDialog = new mw.Upload.Dialog( {
+                                                                               bookletClass: mw.ForeignStructuredUpload.BookletLayout
+                                                                       } );
+
+                                                               $( this ).dialog( 'close' );
+                                                               $( 'body' ).append( windowManager.$element );
+                                                               windowManager.addWindows( [ uploadDialog ] );
+                                                               windowManager.openWindow( uploadDialog );
+
+                                                               uploadDialog.uploadBooklet.on( 'fileSaved', function ( imageInfo ) {
+                                                                       uploadDialog.close();
+                                                                       windowManager.$element.remove();
+
+                                                                       $.wikiEditor.modules.dialogs.api.openDialog( this, 'insert-file' );
+                                                                       $( '#wikieditor-toolbar-file-target' ).val( imageInfo.canonicaltitle );
+                                                               } );
+                                                       }
+                                               },
+                                               open: function () {
+                                                       $( '#wikieditor-toolbar-file-target' ).focus();
+                                                       if ( !( $( this ).data( 'dialogkeypressset' ) ) ) {
+                                                               $( this ).data( 'dialogkeypressset', true );
+                                                               // Execute the action associated with the first button
+                                                               // when the user presses Enter
+                                                               $( this ).closest( '.ui-dialog' ).keypress( function ( e ) {
+                                                                       var button;
+                                                                       if ( e.which === 13 ) {
+                                                                               button = $( this ).data( 'dialogaction' ) ||
+                                                                                       $( this ).find( 'button:first' );
+                                                                               button.click();
+                                                                               e.preventDefault();
+                                                                       }
+                                                               } );
+
+                                                               // Make tabbing to a button and pressing
+                                                               // Enter do what people expect
+                                                               $( this ).closest( '.ui-dialog' ).find( 'button' ).focus( function () {
+                                                                       $( this ).closest( '.ui-dialog' ).data( 'dialogaction', this );
+                                                               } );
+                                                       }
+                                               }
+                                       }
+                               },
+                               'insert-table': {
+                                       titleMsg: 'wikieditor-toolbar-tool-table-title',
+                                       id: 'wikieditor-toolbar-table-dialog',
+                                       htmlTemplate: 'dialogInsertTable.html',
+                                       init: function () {
+                                               $( this ).find( '[rel]' ).each( function () {
+                                                       $( this ).text( mw.msg( $( this ).attr( 'rel' ) ) );
+                                               } );
+                                               // Set tabindexes on form fields
+                                               $.wikiEditor.modules.dialogs.fn.setTabindexes( $( this ).find( 'input' ).not( '[tabindex]' ) );
+
+                                               $( '#wikieditor-toolbar-table-dimensions-rows' ).val( 3 );
+                                               $( '#wikieditor-toolbar-table-dimensions-columns' ).val( 3 );
+                                               $( '#wikieditor-toolbar-table-wikitable' ).click( function () {
+                                                       $( '.wikieditor-toolbar-table-preview' ).toggleClass( 'wikitable' );
+                                               } );
+
+                                               // Hack for sortable preview: dynamically adding
+                                               // sortable class doesn't work, so we use a clone
+                                               $( '#wikieditor-toolbar-table-preview' )
+                                                       .clone()
+                                                       .attr( 'id', 'wikieditor-toolbar-table-preview2' )
+                                                       .addClass( 'sortable' )
+                                                       .insertAfter( $( '#wikieditor-toolbar-table-preview' ) )
+                                                       .hide();
+
+                                               mw.loader.using( 'jquery.tablesorter', function () {
+                                                       $( '#wikieditor-toolbar-table-preview2' ).tablesorter();
+                                               } );
+
+                                               $( '#wikieditor-toolbar-table-sortable' ).click( function () {
+                                                       // Swap the currently shown one clone with the other one
+                                                       $( '#wikieditor-toolbar-table-preview' )
+                                                               .hide()
+                                                               .attr( 'id', 'wikieditor-toolbar-table-preview3' );
+                                                       $( '#wikieditor-toolbar-table-preview2' )
+                                                               .attr( 'id', 'wikieditor-toolbar-table-preview' )
+                                                               .show();
+                                                       $( '#wikieditor-toolbar-table-preview3' ).attr( 'id', 'wikieditor-toolbar-table-preview2' );
+                                               } );
+
+                                               $( '#wikieditor-toolbar-table-dimensions-header' ).click( function () {
+                                                       // Instead of show/hiding, switch the HTML around
+                                                       // We do this because the sortable tables script styles the first row,
+                                                       // visible or not
+                                                       var $sortable,
+                                                               headerHTML = $( '.wikieditor-toolbar-table-preview-header' ).html(),
+                                                               hiddenHTML = $( '.wikieditor-toolbar-table-preview-hidden' ).html();
+                                                       $( '.wikieditor-toolbar-table-preview-header' ).html( hiddenHTML );
+                                                       $( '.wikieditor-toolbar-table-preview-hidden' ).html( headerHTML );
+                                                       $sortable = $( '#wikieditor-toolbar-table-preview, #wikieditor-toolbar-table-preview2' )
+                                                               .filter( '.sortable' );
+                                                       mw.loader.using( 'jquery.tablesorter', function () {
+                                                               $sortable.tablesorter();
+                                                       } );
+                                               } );
+                                       },
+                                       dialog: {
+                                               resizable: false,
+                                               dialogClass: 'wikiEditor-toolbar-dialog',
+                                               width: 590,
+                                               buttons: {
+                                                       'wikieditor-toolbar-tool-table-insert': function () {
+                                                               var headerText, normalText, table, r, c,
+                                                                       isHeader, delim, classes, classStr,
+                                                                       rowsVal = $( '#wikieditor-toolbar-table-dimensions-rows' ).val(),
+                                                                       colsVal = $( '#wikieditor-toolbar-table-dimensions-columns' ).val(),
+                                                                       rows = parseInt( rowsVal, 10 ),
+                                                                       cols = parseInt( colsVal, 10 ),
+                                                                       header = $( '#wikieditor-toolbar-table-dimensions-header' ).prop( 'checked' ) ? 1 : 0;
+                                                               if ( isNaN( rows ) || isNaN( cols ) || String( rows ) !== rowsVal || String( cols ) !== colsVal || rowsVal < 0 || colsVal < 0 ) {
+                                                                       // eslint-disable-next-line no-alert
+                                                                       alert( mw.msg( 'wikieditor-toolbar-tool-table-invalidnumber' ) );
+                                                                       return;
+                                                               }
+                                                               if ( rows + header === 0 || cols === 0 ) {
+                                                                       // eslint-disable-next-line no-alert
+                                                                       alert( mw.msg( 'wikieditor-toolbar-tool-table-zero' ) );
+                                                                       return;
+                                                               }
+                                                               if ( ( rows * cols ) > 1000 ) {
+                                                                       // 1000 is in the English message. The parameter replacement is kept for BC.
+                                                                       // eslint-disable-next-line no-alert
+                                                                       alert( mw.msg( 'wikieditor-toolbar-tool-table-toomany', 1000 ) );
+                                                                       return;
+                                                               }
+                                                               headerText = mw.msg( 'wikieditor-toolbar-tool-table-example-header' );
+                                                               normalText = mw.msg( 'wikieditor-toolbar-tool-table-example' );
+                                                               table = '';
+                                                               for ( r = 0; r < rows + header; r++ ) {
+                                                                       table += '|-\n';
+                                                                       for ( c = 0; c < cols; c++ ) {
+                                                                               isHeader = ( header && r === 0 );
+                                                                               delim = isHeader ? '!' : '|';
+                                                                               if ( c > 0 ) {
+                                                                                       delim += delim;
+                                                                               }
+                                                                               table += delim + ' ' + ( isHeader ? headerText : normalText ) + ' ';
+                                                                       }
+                                                                       // Replace trailing space by newline
+                                                                       // table[table.length - 1] is read-only
+                                                                       table = table.substr( 0, table.length - 1 ) + '\n';
+                                                               }
+                                                               classes = [];
+                                                               if ( $( '#wikieditor-toolbar-table-wikitable' ).is( ':checked' ) ) {
+                                                                       classes.push( 'wikitable' );
+                                                               }
+                                                               if ( $( '#wikieditor-toolbar-table-sortable' ).is( ':checked' ) ) {
+                                                                       classes.push( 'sortable' );
+                                                               }
+                                                               classStr = classes.length > 0 ? ' class="' + classes.join( ' ' ) + '"' : '';
+                                                               $( this ).dialog( 'close' );
+                                                               $.wikiEditor.modules.toolbar.fn.doAction(
+                                                                       $( this ).data( 'context' ),
+                                                                       {
+                                                                               type: 'replace',
+                                                                               options: {
+                                                                                       pre: '{|' + classStr + '\n',
+                                                                                       peri: table,
+                                                                                       post: '|}',
+                                                                                       ownline: true
+                                                                               }
+                                                                       },
+                                                                       $( this )
+                                                               );
+
+                                                               // Restore form state
+                                                               $( '#wikieditor-toolbar-table-dimensions-rows' ).val( 3 );
+                                                               $( '#wikieditor-toolbar-table-dimensions-columns' ).val( 3 );
+                                                               // Simulate clicks instead of setting values, so the according
+                                                               // actions are performed
+                                                               if ( !$( '#wikieditor-toolbar-table-dimensions-header' ).is( ':checked' ) ) {
+                                                                       $( '#wikieditor-toolbar-table-dimensions-header' ).click();
+                                                               }
+                                                               if ( !$( '#wikieditor-toolbar-table-wikitable' ).is( ':checked' ) ) {
+                                                                       $( '#wikieditor-toolbar-table-wikitable' ).click();
+                                                               }
+                                                               if ( $( '#wikieditor-toolbar-table-sortable' ).is( ':checked' ) ) {
+                                                                       $( '#wikieditor-toolbar-table-sortable' ).click();
+                                                               }
+                                                       },
+                                                       'wikieditor-toolbar-tool-table-cancel': function () {
+                                                               $( this ).dialog( 'close' );
+                                                       }
+                                               },
+                                               open: function () {
+                                                       $( '#wikieditor-toolbar-table-dimensions-rows' ).focus();
+                                                       if ( !( $( this ).data( 'dialogkeypressset' ) ) ) {
+                                                               $( this ).data( 'dialogkeypressset', true );
+                                                               // Execute the action associated with the first button
+                                                               // when the user presses Enter
+                                                               $( this ).closest( '.ui-dialog' ).keypress( function ( e ) {
+                                                                       var button;
+                                                                       if ( ( e.keyCode || e.which ) === 13 ) {
+                                                                               button = $( this ).data( 'dialogaction' ) || $( this ).find( 'button:first' );
+                                                                               button.click();
+                                                                               e.preventDefault();
+                                                                       }
+                                                               } );
+
+                                                               // Make tabbing to a button and pressing
+                                                               // Enter do what people expect
+                                                               $( this ).closest( '.ui-dialog' ).find( 'button' ).focus( function () {
+                                                                       $( this ).closest( '.ui-dialog' ).data( 'dialogaction', this );
+                                                               } );
+                                                       }
+                                               }
+                                       }
+                               },
+                               'search-and-replace': {
+                                       browsers: {
+                                               // Left-to-right languages
+                                               ltr: {
+                                                       msie: [ [ '>=', 11 ] ] // Known to work on 11.
+                                               },
+                                               // Right-to-left languages
+                                               rtl: {
+                                                       msie: [ [ '>=', 11 ] ] // Works on 11 but dialog positioning is cruddy.
+                                               }
+                                       },
+                                       titleMsg: 'wikieditor-toolbar-tool-replace-title',
+                                       id: 'wikieditor-toolbar-replace-dialog',
+                                       htmlTemplate: 'dialogReplace.html',
+                                       init: function () {
+                                               $( this ).find( '[rel]' ).each( function () {
+                                                       $( this ).text( mw.msg( $( this ).attr( 'rel' ) ) );
+                                               } );
+                                               // Set tabindexes on form fields
+                                               $.wikiEditor.modules.dialogs.fn.setTabindexes( $( this ).find( 'input' ).not( '[tabindex]' ) );
+
+                                               // TODO: Find a cleaner way to share this function
+                                               $( this ).data( 'replaceCallback', function ( mode ) {
+                                                       var offset, textRemainder, regex, index, i,
+                                                               searchStr, replaceStr, flags, matchCase, isRegex,
+                                                               $textarea, text, match,
+                                                               matchedText, replace, newEnd,
+                                                               actualReplacement,
+                                                               start, end;
+
+                                                       $( '#wikieditor-toolbar-replace-nomatch, #wikieditor-toolbar-replace-success, #wikieditor-toolbar-replace-emptysearch, #wikieditor-toolbar-replace-invalidregex' ).hide();
+
+                                                       // Search string cannot be empty
+                                                       searchStr = $( '#wikieditor-toolbar-replace-search' ).val();
+                                                       if ( searchStr === '' ) {
+                                                               $( '#wikieditor-toolbar-replace-emptysearch' ).show();
+                                                               return;
+                                                       }
+
+                                                       // Replace string can be empty
+                                                       replaceStr = $( '#wikieditor-toolbar-replace-replace' ).val();
+
+                                                       // Prepare the regular expression flags
+                                                       flags = 'm';
+                                                       matchCase = $( '#wikieditor-toolbar-replace-case' ).is( ':checked' );
+                                                       if ( !matchCase ) {
+                                                               flags += 'i';
+                                                       }
+                                                       isRegex = $( '#wikieditor-toolbar-replace-regex' ).is( ':checked' );
+                                                       if ( !isRegex ) {
+                                                               searchStr = mw.RegExp.escape( searchStr );
+                                                       }
+                                                       if ( mode === 'replaceAll' ) {
+                                                               flags += 'g';
+                                                       }
+
+                                                       try {
+                                                               regex = new RegExp( searchStr, flags );
+                                                       } catch ( e ) {
+                                                               $( '#wikieditor-toolbar-replace-invalidregex' )
+                                                                       .text( mw.msg( 'wikieditor-toolbar-tool-replace-invalidregex',
+                                                                               e.message ) )
+                                                                       .show();
+                                                               return;
+                                                       }
+
+                                                       $textarea = $( this ).data( 'context' ).$textarea;
+                                                       text = $textarea.textSelection( 'getContents' );
+                                                       match = false;
+                                                       if ( mode !== 'replaceAll' ) {
+                                                               if ( mode === 'replace' ) {
+                                                                       offset = $( this ).data( 'matchIndex' );
+                                                               } else {
+                                                                       offset = $( this ).data( 'offset' );
+                                                               }
+                                                               textRemainder = text.substr( offset );
+                                                               match = textRemainder.match( regex );
+                                                       }
+                                                       if ( !match ) {
+                                                               // Search hit BOTTOM, continuing at TOP
+                                                               // TODO: Add a "Wrap around" option.
+                                                               offset = 0;
+                                                               textRemainder = text;
+                                                               match = textRemainder.match( regex );
+                                                       }
+
+                                                       if ( !match ) {
+                                                               $( '#wikieditor-toolbar-replace-nomatch' ).show();
+                                                       } else if ( mode === 'replaceAll' ) {
+                                                               // Instead of using repetitive .match() calls, we use one .match() call with /g
+                                                               // and indexOf() followed by substr() to find the offsets. This is actually
+                                                               // faster because our indexOf+substr loop is faster than a match loop, and the
+                                                               // /g match is so ridiculously fast that it's negligible.
+                                                               // FIXME: Repetitively calling encapsulateSelection() is probably the best strategy
+                                                               // in Firefox/Webkit, but in IE replacing the entire content once is better.
+                                                               for ( i = 0; i < match.length; i++ ) {
+                                                                       index = textRemainder.indexOf( match[ i ] );
+                                                                       if ( index === -1 ) {
+                                                                               // This shouldn't happen
+                                                                               break;
+                                                                       }
+                                                                       matchedText = textRemainder.substr( index, match[ i ].length );
+                                                                       textRemainder = textRemainder.substr( index + match[ i ].length );
+
+                                                                       start = index + offset;
+                                                                       end = start + match[ i ].length;
+                                                                       // Make regex placeholder substitution ($1) work
+                                                                       replace = isRegex ? matchedText.replace( regex, replaceStr ) : replaceStr;
+                                                                       newEnd = start + replace.length;
+                                                                       $textarea
+                                                                               .textSelection( 'setSelection', { start: start, end: end } )
+                                                                               .textSelection( 'encapsulateSelection', {
+                                                                                       peri: replace,
+                                                                                       replace: true } )
+                                                                               .textSelection( 'setSelection', { start: start, end: newEnd } );
+                                                                       offset = newEnd;
+                                                               }
+                                                               $( '#wikieditor-toolbar-replace-success' )
+                                                                       .text( mw.msg( 'wikieditor-toolbar-tool-replace-success', match.length ) )
+                                                                       .show();
+                                                               $( this ).data( 'offset', 0 );
+                                                       } else {
+
+                                                               if ( mode === 'replace' ) {
+
+                                                                       if ( isRegex ) {
+                                                                               // If backreferences (like $1) are used, the actual actual replacement string will be different
+                                                                               actualReplacement = match[ 0 ].replace( regex, replaceStr );
+                                                                       } else {
+                                                                               actualReplacement = replaceStr;
+                                                                       }
+
+                                                                       if ( match ) {
+                                                                               // Do the replacement
+                                                                               $textarea.textSelection( 'encapsulateSelection', {
+                                                                                       peri: actualReplacement,
+                                                                                       replace: true } );
+                                                                               // Reload the text after replacement
+                                                                               text = $textarea.textSelection( 'getContents' );
+                                                                       }
+
+                                                                       // Find the next instance
+                                                                       offset = offset + match[ 0 ].length + actualReplacement.length;
+                                                                       textRemainder = text.substr( offset );
+                                                                       match = textRemainder.match( regex );
+
+                                                                       if ( match ) {
+                                                                               start = offset + match.index;
+                                                                               end = start + match[ 0 ].length;
+                                                                       } else {
+                                                                               // If no new string was found, try searching from the beginning.
+                                                                               // TODO: Add a "Wrap around" option.
+                                                                               textRemainder = text;
+                                                                               match = textRemainder.match( regex );
+                                                                               if ( match ) {
+                                                                                       start = match.index;
+                                                                                       end = start + match[ 0 ].length;
+                                                                               } else {
+                                                                                       // Give up
+                                                                                       start = 0;
+                                                                                       end = 0;
+                                                                               }
+                                                                       }
+                                                               } else {
+                                                                       start = offset + match.index;
+                                                                       end = start + match[ 0 ].length;
+                                                               }
+
+                                                               $( this ).data( 'matchIndex', start );
+
+                                                               $textarea.textSelection( 'setSelection', {
+                                                                       start: start,
+                                                                       end: end } );
+                                                               $textarea.textSelection( 'scrollToCaretPosition' );
+                                                               $( this ).data( 'offset', end );
+                                                               $textarea[ 0 ].focus();
+                                                       }
+                                               } );
+                                       },
+                                       dialog: {
+                                               width: 500,
+                                               dialogClass: 'wikiEditor-toolbar-dialog',
+                                               modal: false,
+                                               buttons: {
+                                                       'wikieditor-toolbar-tool-replace-button-findnext': function ( e ) {
+                                                               $( this ).closest( '.ui-dialog' ).data( 'dialogaction', e.target );
+                                                               $( this ).data( 'replaceCallback' ).call( this, 'find' );
+                                                       },
+                                                       'wikieditor-toolbar-tool-replace-button-replace': function ( e ) {
+                                                               $( this ).closest( '.ui-dialog' ).data( 'dialogaction', e.target );
+                                                               $( this ).data( 'replaceCallback' ).call( this, 'replace' );
+                                                       },
+                                                       'wikieditor-toolbar-tool-replace-button-replaceall': function ( e ) {
+                                                               $( this ).closest( '.ui-dialog' ).data( 'dialogaction', e.target );
+                                                               $( this ).data( 'replaceCallback' ).call( this, 'replaceAll' );
+                                                       },
+                                                       'wikieditor-toolbar-tool-replace-close': function () {
+                                                               $( this ).dialog( 'close' );
+                                                       }
+                                               },
+                                               open: function () {
+                                                       var dialog, context, textbox,
+                                                               that = this;
+                                                       $( this ).data( 'offset', 0 );
+                                                       $( this ).data( 'matchIndex', 0 );
+
+                                                       $( '#wikieditor-toolbar-replace-search' ).focus();
+                                                       $( '#wikieditor-toolbar-replace-nomatch, #wikieditor-toolbar-replace-success, #wikieditor-toolbar-replace-emptysearch, #wikieditor-toolbar-replace-invalidregex' ).hide();
+                                                       if ( !( $( this ).data( 'onetimeonlystuff' ) ) ) {
+                                                               $( this ).data( 'onetimeonlystuff', true );
+                                                               // Execute the action associated with the first button
+                                                               // when the user presses Enter
+                                                               $( this ).closest( '.ui-dialog' ).keypress( function ( e ) {
+                                                                       var button;
+                                                                       if ( ( e.keyCode || e.which ) === 13 ) {
+                                                                               button = $( this ).data( 'dialogaction' ) || $( this ).find( 'button:first' );
+                                                                               button.click();
+                                                                               e.preventDefault();
+                                                                       }
+                                                               } );
+                                                               // Make tabbing to a button and pressing
+                                                               // Enter do what people expect
+                                                               $( this ).closest( '.ui-dialog' ).find( 'button' ).focus( function () {
+                                                                       $( this ).closest( '.ui-dialog' ).data( 'dialogaction', this );
+                                                               } );
+                                                       }
+                                                       dialog = $( this ).closest( '.ui-dialog' );
+                                                       that = this;
+                                                       context = $( this ).data( 'context' );
+                                                       textbox = context.$textarea;
+
+                                                       $( textbox )
+                                                               .on( 'keypress.srdialog', function ( e ) {
+                                                                       var button;
+                                                                       if ( e.which === 13 ) {
+                                                                               // Enter
+                                                                               button = dialog.data( 'dialogaction' ) || dialog.find( 'button:first' );
+                                                                               button.click();
+                                                                               e.preventDefault();
+                                                                       } else if ( e.which === 27 ) {
+                                                                               // Escape
+                                                                               $( that ).dialog( 'close' );
+                                                                       }
+                                                               } );
+                                               },
+                                               close: function () {
+                                                       var context = $( this ).data( 'context' ),
+                                                               textbox = context.$textarea;
+                                                       $( textbox ).off( 'keypress.srdialog' );
+                                                       $( this ).closest( '.ui-dialog' ).data( 'dialogaction', false );
+                                               }
+                                       }
+                               }
+                       } };
+               }
+
+       };
+
+}( jQuery, mediaWiki, OO ) );