mediawiki-extensions-WikiEd.../modules/jquery.wikiEditor.dialogs.config.js
Timo Tijhof 7769baa56e Replace deprecated jQuery bind() and size()
> JQMIGRATE: jQuery.fn.bind() is deprecated
> JQMIGRATE: jQuery.fn.size() is deprecated; use the .length property

Note that bind() is not removed in v3, merely deprecated.
Even after the jQuery Migrate phase, it will continue to work.

size() was removed in v3 and works only during the Migrate phase.

https://api.jquery.com/size/
https://api.jquery.com/bind/
https://jquery.com/upgrade-guide/3.0/

Bug: T124742
Change-Id: I6bbd8f829ecf987228c6a5abd32c84e4e088a9bd
2016-11-21 18:23:50 -08:00

1280 lines
48 KiB
JavaScript

/**
* 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 loadingMsg,
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)
loadingMsg = mw.msg( 'wikieditor-toolbar-tool-link-int-target-status-loading' );
$( '#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' )
.append( $( '<img>' ).attr( {
src: $.wikiEditor.imgPath + 'dialogs/' + 'loading-small.gif',
alt: loadingMsg,
title: loadingMsg
} ) )
)
.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 === '' ) {
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
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 () {
// Clear any saved selection state
var context = $( this ).data( 'context' );
context.fn.restoreCursorAndScrollTop();
$( 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' );
// Restore and immediately save selection state, needed for inserting stuff later
context.fn.restoreCursorAndScrollTop();
context.fn.saveCursorAndScrollTop();
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 () {
// Clear any saved selection state
var context = $( this ).data( 'context' );
context.fn.restoreCursorAndScrollTop();
$( this ).dialog( 'close' );
}
},
open: function () {
// Pre-fill the text fields based on the current selection
var selection, matches, text,
context = $( this ).data( 'context' );
// Restore and immediately save selection state, needed for inserting stuff later
context.fn.restoreCursorAndScrollTop();
context.fn.saveCursorAndScrollTop();
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 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 );
if ( typeof jQuery.fn.tablesorter === 'function' ) {
$( '#wikieditor-toolbar-table-preview, #wikieditor-toolbar-table-preview2' )
.filter( '.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 ) {
alert( mw.msg( 'wikieditor-toolbar-tool-table-invalidnumber' ) );
return;
}
if ( rows + header === 0 || cols === 0 ) {
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.
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.
firefox: [ [ '>=', 2 ] ],
opera: false,
safari: [ [ '>=', 3 ] ],
chrome: [ [ '>=', 3 ] ]
},
// Right-to-left languages
rtl: {
msie: [ [ '>=', 11 ] ], // Works on 11 but dialog positioning is cruddy.
firefox: [ [ '>=', 2 ] ],
opera: false,
safari: [ [ '>=', 3 ] ],
chrome: [ [ '>=', 3 ] ]
}
},
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' ) {
actualReplacement;
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 ).unbind( 'keypress.srdialog' );
$( this ).closest( '.ui-dialog' ).data( 'dialogaction', false );
}
}
}
} };
}
};
}( jQuery, mediaWiki, OO ) );