Switch to OOUI TitleInputWidget for the link-inserter
In order to get a better look-up experience (images, description, redirects, and disambigutation pages listed last), swich from a custom title-autocomplete input (that used jquery.suggestions) to OOUI's standard one (which is used in a bunch of other places in MediaWiki). This means a fair bit of code can be deleted from jquery.wikiEditor.dialogs.config.js, and some of it moved to the new OOUI classes. This patch aims to be the minimum required, and so leaves a few things for subsequent patches (such as converting the other parts of the insert-link dialog to OOUI). Bug: T289214 Change-Id: I9fb7e66bf925eb9a8260d6245d2a7db54a7a2fec
|
@ -42,6 +42,9 @@
|
|||
"group": "ext.wikiEditor",
|
||||
"packageFiles": [
|
||||
"ext.wikiEditor.js",
|
||||
"insertlink/TitleInputField.js",
|
||||
"insertlink/TitleInputWidget.js",
|
||||
"insertlink/TitleOptionWidget.js",
|
||||
"jquery.wikiEditor.js",
|
||||
"jquery.wikiEditor.toolbar.js",
|
||||
"jquery.wikiEditor.toolbar.config.js",
|
||||
|
@ -54,6 +57,7 @@
|
|||
}
|
||||
],
|
||||
"styles": [
|
||||
"insertlink/TitleInputField.less",
|
||||
"jquery.wikiEditor.less",
|
||||
"jquery.wikiEditor.toolbar.less",
|
||||
"jquery.wikiEditor.dialogs.less",
|
||||
|
@ -271,7 +275,6 @@
|
|||
"dependencies": [
|
||||
"jquery.cookie",
|
||||
"jquery.throttle-debounce",
|
||||
"jquery.suggestions",
|
||||
"jquery.textSelection",
|
||||
"jquery.ui",
|
||||
"mediawiki.api",
|
||||
|
@ -281,6 +284,7 @@
|
|||
"mediawiki.Title",
|
||||
"mediawiki.user",
|
||||
"mediawiki.util",
|
||||
"mediawiki.widgets",
|
||||
"oojs-ui.styles.icons-content",
|
||||
"oojs-ui.styles.icons-editing-advanced",
|
||||
"oojs-ui.styles.icons-editing-citation",
|
||||
|
|
|
@ -33,12 +33,12 @@
|
|||
"wikieditor-toolbar-tool-link-ext-text": "Link text:",
|
||||
"wikieditor-toolbar-tool-link-insert": "Insert link",
|
||||
"wikieditor-toolbar-tool-link-cancel": "Cancel",
|
||||
"wikieditor-toolbar-tool-link-int-target-status-exists": "Page exists",
|
||||
"wikieditor-toolbar-tool-link-int-target-status-notexists": "Page does not exist",
|
||||
"wikieditor-toolbar-tool-link-int-target-status-invalid": "Invalid title",
|
||||
"wikieditor-toolbar-tool-link-int-target-status-exists": "This page exists on {{SITENAME}}",
|
||||
"wikieditor-toolbar-tool-link-int-target-status-notexists": "This page does not exist on {{SITENAME}}",
|
||||
"wikieditor-toolbar-tool-link-int-target-status-invalid": "The title you specified is invalid",
|
||||
"wikieditor-toolbar-tool-link-int-target-status-external": "External link",
|
||||
"wikieditor-toolbar-tool-link-int-target-status-loading": "Checking page existence...",
|
||||
"wikieditor-toolbar-tool-link-int-target-status-disambig": "Disambiguation page",
|
||||
"wikieditor-toolbar-tool-link-int-target-status-disambig": "This page is not a content page, it lists topics with similar names",
|
||||
"wikieditor-toolbar-tool-link-int-invalid": "The title you specified is invalid.",
|
||||
"wikieditor-toolbar-tool-link-lookslikeinternal": "The URL you specified looks like it was intended as a link to another wiki page.\nDo you want to make it an internal link?",
|
||||
"wikieditor-toolbar-tool-link-lookslikeinternal-int": "Internal link",
|
||||
|
|
Before Width: | Height: | Size: 724 B |
Before Width: | Height: | Size: 273 B |
Before Width: | Height: | Size: 229 B |
Before Width: | Height: | Size: 227 B |
Before Width: | Height: | Size: 487 B |
Before Width: | Height: | Size: 430 B |
Before Width: | Height: | Size: 4 KiB |
Before Width: | Height: | Size: 4.5 KiB |
105
modules/insertlink/TitleInputField.js
Normal file
|
@ -0,0 +1,105 @@
|
|||
var TitleInputWidget = require( './TitleInputWidget.js' );
|
||||
var TitleOptionWidget = require( './TitleOptionWidget.js' );
|
||||
|
||||
/**
|
||||
* A FieldLayout containing a custom TitleInputwidget and message-display system.
|
||||
*
|
||||
* @class
|
||||
* @extends OO.ui.FieldLayout
|
||||
* @mixes OO.EventEmitter
|
||||
* @constructor
|
||||
*/
|
||||
function TitleInputField() {
|
||||
var input = new TitleInputWidget();
|
||||
input.connect( this, {
|
||||
change: this.onChange,
|
||||
select: this.onSelect
|
||||
} );
|
||||
|
||||
// The URL mode is set by the user via radio buttons.
|
||||
this.urlModes = {
|
||||
internal: 'internal',
|
||||
external: 'external'
|
||||
};
|
||||
this.urlMode = this.urlModes.internal;
|
||||
|
||||
var config = {
|
||||
align: 'top',
|
||||
label: mw.msg( 'wikieditor-toolbar-tool-link-int-target' ),
|
||||
classes: [ 'ext-WikiEditor-InsertLink-TitleInputField' ]
|
||||
};
|
||||
TitleInputField.super.call( this, input, config );
|
||||
|
||||
OO.EventEmitter.call( this );
|
||||
}
|
||||
|
||||
OO.inheritClass( TitleInputField, OO.ui.FieldLayout );
|
||||
OO.mixinClass( TitleInputField, OO.EventEmitter );
|
||||
|
||||
/**
|
||||
* @public
|
||||
* @param {string} urlMode One of the `TitleInputField.urlModes.*` values.
|
||||
*/
|
||||
TitleInputField.prototype.setUrlMode = function ( urlMode ) {
|
||||
this.urlMode = urlMode === this.urlModes.external ?
|
||||
this.urlModes.external :
|
||||
this.urlModes.internal;
|
||||
};
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
TitleInputField.prototype.makeMessage = function ( kind, text ) {
|
||||
this.message = new OO.ui.MessageWidget( {
|
||||
type: kind,
|
||||
inline: true,
|
||||
label: text
|
||||
} );
|
||||
return this.message.$element;
|
||||
};
|
||||
|
||||
/**
|
||||
* Set the displayed field message, replacing any that is already set.
|
||||
*
|
||||
* @param {string} icon
|
||||
* @param {string} message
|
||||
* @param {string} type
|
||||
*/
|
||||
TitleInputField.prototype.setMessage = function ( icon, message, type ) {
|
||||
this.setNotices( [ message ] );
|
||||
// Note that setNotices() must be called before this.message is available.
|
||||
this.message.setType( type || 'notice' );
|
||||
this.message.setIcon( icon );
|
||||
};
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param {string} value
|
||||
*/
|
||||
TitleInputField.prototype.onChange = function ( value ) {
|
||||
if ( this.urlMode === this.urlModes.internal && value !== '' && !mw.Title.newFromText( value ) ) {
|
||||
this.setMessage( 'error', mw.message( 'wikieditor-toolbar-tool-link-int-target-status-invalid' ).parse(), 'error' );
|
||||
this.emit( 'invalid' );
|
||||
} else {
|
||||
// Remove message; it'll be re-added if required (after selection or blur).
|
||||
this.setNotices( [] );
|
||||
this.emit( 'change', value );
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {TitleOptionWidget} item
|
||||
*/
|
||||
TitleInputField.prototype.onSelect = function ( item ) {
|
||||
if ( item.isExternal() ) {
|
||||
this.setMessage( 'linkExternal', mw.message( 'wikieditor-toolbar-tool-link-int-target-status-external' ).parse() );
|
||||
} else if ( item.isDisambiguation() ) {
|
||||
this.setMessage( 'articleDisambiguation', mw.message( 'wikieditor-toolbar-tool-link-int-target-status-disambig' ).parse() );
|
||||
} else if ( !item.isMissing() ) {
|
||||
this.setMessage( 'article', mw.message( 'wikieditor-toolbar-tool-link-int-target-status-exists' ).parse() );
|
||||
} else if ( item.isMissing() ) {
|
||||
this.setMessage( 'articleNotFound', mw.message( 'wikieditor-toolbar-tool-link-int-target-status-notexists' ).parse() );
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = TitleInputField;
|
10
modules/insertlink/TitleInputField.less
Normal file
|
@ -0,0 +1,10 @@
|
|||
@import 'mediawiki.ui/variables.less';
|
||||
|
||||
.ext-WikiEditor-InsertLink-TitleInputField {
|
||||
margin-bottom: 8px;
|
||||
|
||||
.oo-ui-messageWidget {
|
||||
color: @color-base--subtle;
|
||||
font-weight: normal;
|
||||
}
|
||||
}
|
146
modules/insertlink/TitleInputWidget.js
Normal file
|
@ -0,0 +1,146 @@
|
|||
var InsertLinkTitleOptionWidget = require( './TitleOptionWidget.js' );
|
||||
|
||||
/**
|
||||
* A custom TitleInputWidget that adds support for external links
|
||||
* (any string that starts with a protocol or `www.` and doesn't exist as a page).
|
||||
*
|
||||
* @class
|
||||
* @extends mw.widgets.TitleInputWidget
|
||||
* @constructor
|
||||
*/
|
||||
function TitleInputWidget() {
|
||||
TitleInputWidget.parent.call( this, {
|
||||
showImages: true,
|
||||
showDescriptions: true,
|
||||
showDisambigsLast: true,
|
||||
placeholder: mw.msg( 'wikieditor-toolbar-tool-link-int-target-tooltip' ),
|
||||
$overlay: this.getOverlay(),
|
||||
validateTitle: false,
|
||||
showInterwikis: true,
|
||||
required: true,
|
||||
addQueryInput: true
|
||||
} );
|
||||
}
|
||||
|
||||
OO.inheritClass( TitleInputWidget, mw.widgets.TitleInputWidget );
|
||||
|
||||
/**
|
||||
* Regular expression for determining what might be an external link.
|
||||
*
|
||||
* @static
|
||||
* @property {RegExp}
|
||||
*/
|
||||
TitleInputWidget.static.urlRegex = new RegExp( '^(' + mw.config.get( 'wgUrlProtocols' ) + '|www\\.)', 'i' );
|
||||
|
||||
/**
|
||||
* When leaving the input without selecting a menu item,
|
||||
* automatically select a matching item if there is one.
|
||||
* Even though we specify addQueryInput=true in the config, the entered string
|
||||
* is not always available to be selected. See T291056.
|
||||
*/
|
||||
TitleInputWidget.prototype.onLookupInputBlur = function () {
|
||||
TitleInputWidget.parent.prototype.onLookupInputBlur.apply( this );
|
||||
var that = this;
|
||||
this.getLookupMenuItems().done( function ( items ) {
|
||||
// The matching item is not always the first,
|
||||
// because disambiguation pages are moved to the end.
|
||||
for ( var i = 0; i < items.length; i++ ) {
|
||||
var item = items[ i ];
|
||||
var queryVal = that.getQueryValue();
|
||||
// Check for exact match, or a match with uppercase first character.
|
||||
if ( item.getData() === queryVal ||
|
||||
item.getData() === queryVal.charAt( 0 ).toUpperCase() + queryVal.slice( 1 )
|
||||
) {
|
||||
// If a matching title is is found, fire an event and stop looking.
|
||||
that.emit( 'select', item );
|
||||
break;
|
||||
}
|
||||
}
|
||||
} );
|
||||
};
|
||||
|
||||
/**
|
||||
* Get menu option widget data from the title and page data,
|
||||
* adding an `external` property.
|
||||
*
|
||||
* @param {string} title Page title
|
||||
* @param {Object} data Page data
|
||||
* @return {Object} Data for option widget
|
||||
*/
|
||||
TitleInputWidget.prototype.getOptionWidgetData = function ( title, data ) {
|
||||
var widgetData = TitleInputWidget.parent.prototype.getOptionWidgetData.call( this, title, data );
|
||||
widgetData.external = data.originalData.external;
|
||||
return widgetData;
|
||||
};
|
||||
|
||||
/**
|
||||
* Create a InsertLinkTitleOptionWidget.
|
||||
*
|
||||
* @param {Object} data Data for option widget
|
||||
* @return {OO.ui.MenuOptionWidget} The option widget
|
||||
*/
|
||||
TitleInputWidget.prototype.createOptionWidget = function ( data ) {
|
||||
return new InsertLinkTitleOptionWidget( data );
|
||||
};
|
||||
|
||||
/**
|
||||
* Get pages' data from the API response, adding an `external` property for
|
||||
* pages that do not exist and which look like external URLs.
|
||||
*
|
||||
* @param {Object} response
|
||||
* @return {Object}
|
||||
*/
|
||||
TitleInputWidget.prototype.getLookupCacheDataFromResponse = function ( response ) {
|
||||
var res = TitleInputWidget.parent.prototype.getLookupCacheDataFromResponse( response );
|
||||
// Guard against zero responses.
|
||||
if ( res.pages === undefined ) {
|
||||
return res;
|
||||
}
|
||||
for ( var pageId in res.pages ) {
|
||||
if ( Object.prototype.hasOwnProperty.call( res.pages, pageId ) ) {
|
||||
var page = res.pages[ pageId ];
|
||||
page.external = page.missing !== undefined && this.isExternalLink( page.title );
|
||||
}
|
||||
}
|
||||
return res;
|
||||
};
|
||||
|
||||
/**
|
||||
* Handle menu item 'choose' event, updating the text input value to the value of the clicked item.
|
||||
*
|
||||
* @param {OO.ui.MenuOptionWidget} item Selected item
|
||||
*/
|
||||
TitleInputWidget.prototype.onLookupMenuChoose = function ( item ) {
|
||||
TitleInputWidget.parent.prototype.onLookupMenuChoose.call( this, item );
|
||||
this.emit( 'select', item );
|
||||
};
|
||||
|
||||
/**
|
||||
* Get a custom overlay for the dropdown menu, so it's not contained within the jQuery UI dialog.
|
||||
*
|
||||
* @private
|
||||
* @return {jQuery}
|
||||
*/
|
||||
TitleInputWidget.prototype.getOverlay = function () {
|
||||
// Overlay z-index must be greater than the jQuery UI dialog's of 1002.
|
||||
var $overlay = OO.ui.getDefaultOverlay()
|
||||
.clone()
|
||||
.css( 'z-index', '1010' );
|
||||
$( document.body ).append( $overlay );
|
||||
return $overlay;
|
||||
};
|
||||
|
||||
/**
|
||||
* Determine if a given string is likely to be an external URL.
|
||||
* External URLs start with either a valid protocol (from $wgUrlProtocols) or `www.`.
|
||||
*
|
||||
* @public
|
||||
* @param {string} urlString The possible URL.
|
||||
* @return {boolean}
|
||||
*/
|
||||
TitleInputWidget.prototype.isExternalLink = function ( urlString ) {
|
||||
var matches = urlString.match( this.constructor.static.urlRegex );
|
||||
return matches !== null && matches.length > 0;
|
||||
};
|
||||
|
||||
module.exports = TitleInputWidget;
|
53
modules/insertlink/TitleOptionWidget.js
Normal file
|
@ -0,0 +1,53 @@
|
|||
/**
|
||||
* A custom TitleOptionWidget that knows about external links.
|
||||
*
|
||||
* @class
|
||||
* @extends mw.widgets.TitleOptionWidget
|
||||
* @constructor
|
||||
* @param {Object} config Configuration options.
|
||||
* @cfg {boolean} [external] Page title is an external link.
|
||||
*/
|
||||
function InsertLinkTitleOptionWidget( config ) {
|
||||
this.external = config.external || false;
|
||||
if ( this.external ) {
|
||||
config.icon = 'linkExternal';
|
||||
config.description = mw.msg( 'wikieditor-toolbar-tool-link-int-target-status-external' );
|
||||
// Lowercase the first character; it was uppercased by the API.
|
||||
config.url = config.data.substr( 0, 1 ).toLowerCase() + config.data.substr( 1 );
|
||||
config.data = config.url;
|
||||
// Prepend http:// if there is no protocol (i.e. if it starts with "www.").
|
||||
// @TODO This is repeated when the link is inserted (in jquery.wikiEditor.dialogs.config.js).
|
||||
if ( !config.url.match( /^[a-z]+:\/\/./ ) ) {
|
||||
config.url = 'http://' + config.url;
|
||||
}
|
||||
config.missing = false;
|
||||
}
|
||||
this.disambiguation = config.disambiguation || false;
|
||||
this.missing = config.missing || false;
|
||||
InsertLinkTitleOptionWidget.super.call( this, config );
|
||||
}
|
||||
|
||||
OO.inheritClass( InsertLinkTitleOptionWidget, mw.widgets.TitleOptionWidget );
|
||||
|
||||
/**
|
||||
* @return {boolean}
|
||||
*/
|
||||
InsertLinkTitleOptionWidget.prototype.isExternal = function () {
|
||||
return this.external;
|
||||
};
|
||||
|
||||
/**
|
||||
* @return {boolean}
|
||||
*/
|
||||
InsertLinkTitleOptionWidget.prototype.isMissing = function () {
|
||||
return this.missing;
|
||||
};
|
||||
|
||||
/**
|
||||
* @return {boolean}
|
||||
*/
|
||||
InsertLinkTitleOptionWidget.prototype.isDisambiguation = function () {
|
||||
return this.disambiguation;
|
||||
};
|
||||
|
||||
module.exports = InsertLinkTitleOptionWidget;
|
|
@ -3,8 +3,9 @@
|
|||
*/
|
||||
( function () {
|
||||
|
||||
var hasOwn = Object.prototype.hasOwnProperty,
|
||||
toolbarModule = require( './jquery.wikiEditor.toolbar.js' ),
|
||||
var toolbarModule = require( './jquery.wikiEditor.toolbar.js' ),
|
||||
InsertLinkTitleInputField = require( './insertlink/TitleInputField.js' ),
|
||||
insertLinkTitleInputField = new InsertLinkTitleInputField(),
|
||||
configData = require( './data.json' );
|
||||
|
||||
function triggerButtonClick( element ) {
|
||||
|
@ -103,173 +104,50 @@
|
|||
htmlTemplate: 'dialogInsertLink.html',
|
||||
|
||||
init: function () {
|
||||
var api = new mw.Api();
|
||||
$( '.wikieditor-toolbar-link-target' ).replaceWith( insertLinkTitleInputField.$element );
|
||||
|
||||
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, reason ) {
|
||||
$( '#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();
|
||||
// Status classes listed above
|
||||
// eslint-disable-next-line mediawiki/class-doc
|
||||
$( '#wikieditor-toolbar-link-int-target' ).parent().addClass( 'status-' + status );
|
||||
}
|
||||
if ( status === 'invalid' ) {
|
||||
// eslint-disable-next-line no-jquery/no-sizzle
|
||||
$( '.ui-dialog:visible .ui-dialog-buttonpane button' ).first()
|
||||
.prop( 'disabled', true )
|
||||
.addClass( 'disabled' );
|
||||
if ( reason ) {
|
||||
$( '#wikieditor-toolbar-link-int-target-status-invalid' ).html( reason );
|
||||
} else {
|
||||
$( '#wikieditor-toolbar-link-int-target-status-invalid' )
|
||||
.text( mw.msg( 'wikieditor-toolbar-tool-link-int-target-status-invalid' ) );
|
||||
}
|
||||
|
||||
} else {
|
||||
// eslint-disable-next-line no-jquery/no-sizzle
|
||||
$( '.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' ),
|
||||
reasoncache = $( '#wikieditor-toolbar-link-int-target-status' ).data( 'reasoncache' );
|
||||
// ensure the internal parameter is a boolean
|
||||
if ( internal !== true ) {
|
||||
internal = false;
|
||||
}
|
||||
if ( request ) {
|
||||
request.abort();
|
||||
}
|
||||
if ( hasOwn.call( cache, target ) ) {
|
||||
updateWidget( cache[ target ], reasoncache[ 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;
|
||||
}
|
||||
// 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',
|
||||
errorformat: 'html',
|
||||
errorlang: mw.config.get( 'wgUserLanguage' )
|
||||
} ).done( function ( data ) {
|
||||
var reason = null;
|
||||
var status;
|
||||
if ( !data.query || !data.query.pages ) {
|
||||
// This happens in some weird cases like interwiki links
|
||||
status = false;
|
||||
} else {
|
||||
var page = data.query.pages[ 0 ];
|
||||
status = 'exists';
|
||||
if ( page.missing ) {
|
||||
status = 'notexists';
|
||||
} else if ( page.invalid ) {
|
||||
status = 'invalid';
|
||||
reason = page.invalidreason && page.invalidreason.html;
|
||||
} 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;
|
||||
reasoncache[ target ] = reason;
|
||||
}
|
||||
updateWidget( status, reason );
|
||||
} )
|
||||
);
|
||||
}
|
||||
$( '#wikieditor-toolbar-link-type-int, #wikieditor-toolbar-link-type-ext' ).on( 'click', function () {
|
||||
if ( $( '#wikieditor-toolbar-link-type-ext' ).prop( 'checked' ) ) {
|
||||
// Abort previous request
|
||||
var 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 () {
|
||||
// eslint-disable-next-line mediawiki/msg-doc
|
||||
$( this ).text( mw.msg( $( this ).attr( 'rel' ) ) );
|
||||
} );
|
||||
$( '#wikieditor-toolbar-link-int-target' ).attr( 'placeholder',
|
||||
mw.msg( 'wikieditor-toolbar-tool-link-int-target-tooltip' ) );
|
||||
$( '#wikieditor-toolbar-link-int-text' ).attr( 'placeholder',
|
||||
mw.msg( 'wikieditor-toolbar-tool-link-int-text-tooltip' ) );
|
||||
// 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 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() ) ) {
|
||||
insertLinkTitleInputField.connect( this, {
|
||||
change: function ( val ) {
|
||||
if ( insertLinkTitleInputField.getField().isExternalLink( 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' ) ) {
|
||||
$( '#wikieditor-toolbar-link-int-text' )
|
||||
.val( $( '#wikieditor-toolbar-link-int-target' ).val() )
|
||||
.val( val )
|
||||
.trigger( 'change' );
|
||||
}
|
||||
}, 0 );
|
||||
// eslint-disable-next-line no-jquery/no-sizzle
|
||||
$( '.ui-dialog:visible .ui-dialog-buttonpane button' )
|
||||
.first()
|
||||
.prop( 'disabled', false )
|
||||
.removeClass( 'disabled' );
|
||||
},
|
||||
invalid: function () {
|
||||
// eslint-disable-next-line no-jquery/no-sizzle
|
||||
$( '.ui-dialog:visible .ui-dialog-buttonpane button' )
|
||||
.first()
|
||||
.prop( 'disabled', true )
|
||||
.addClass( 'disabled' );
|
||||
}
|
||||
} );
|
||||
// Tell the title input field when the internal/external radio changes.
|
||||
$( 'input[name="wikieditor-toolbar-link-type"]' ).on( 'change', function ( event ) {
|
||||
var urlMode = event.target.id === 'wikieditor-toolbar-link-type-ext' ?
|
||||
insertLinkTitleInputField.urlModes.external :
|
||||
insertLinkTitleInputField.urlModes.internal;
|
||||
insertLinkTitleInputField.setUrlMode( urlMode );
|
||||
} );
|
||||
$( '#wikieditor-toolbar-link-int-text' ).on( 'change keydown paste cut', function () {
|
||||
var oldVal = $( this ).val(),
|
||||
|
@ -280,75 +158,6 @@
|
|||
}
|
||||
}, 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' )
|
||||
)
|
||||
.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', {} )
|
||||
.data( 'reasoncache', {} )
|
||||
.children().hide();
|
||||
|
||||
$( '#wikieditor-toolbar-link-int-target' )
|
||||
.on( 'keyup paste cut', $.debounce( 500, updateExistence ) )
|
||||
.on( 'change', updateExistence ); // update right now
|
||||
|
||||
// Title suggestions
|
||||
$( '#wikieditor-toolbar-link-int-target' ).data( 'suggcache', {} ).suggestions( {
|
||||
fetch: function () {
|
||||
var that = this,
|
||||
title = $( this ).val();
|
||||
|
||||
if ( isExternalLink( title ) || title.indexOf( '|' ) !== -1 || title === '' ) {
|
||||
$( this ).suggestions( 'suggestions', [] );
|
||||
return;
|
||||
}
|
||||
|
||||
var cache = $( this ).data( 'suggcache' );
|
||||
if ( hasOwn.call( cache, title ) ) {
|
||||
$( this ).suggestions( 'suggestions', cache[ title ] );
|
||||
return;
|
||||
}
|
||||
|
||||
var request = api.get( {
|
||||
formatversion: 2,
|
||||
action: 'opensearch',
|
||||
search: title,
|
||||
namespace: 0
|
||||
} ).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,
|
||||
|
@ -369,7 +178,7 @@
|
|||
return s.replace( /(\]+)/g, '<nowiki>$1</nowiki>' );
|
||||
}
|
||||
|
||||
var target = $( '#wikieditor-toolbar-link-int-target' ).val();
|
||||
var target = insertLinkTitleInputField.getField().getValue();
|
||||
if ( target === '' ) {
|
||||
// eslint-disable-next-line no-alert
|
||||
alert( mw.msg( 'wikieditor-toolbar-tool-link-empty' ) );
|
||||
|
@ -454,7 +263,8 @@
|
|||
}, $( this ) );
|
||||
|
||||
// Blank form
|
||||
$( '#wikieditor-toolbar-link-int-target, #wikieditor-toolbar-link-int-text' ).val( '' );
|
||||
insertLinkTitleInputField.getField().setValue( '' );
|
||||
$( '#wikieditor-toolbar-link-int-text' ).val( '' );
|
||||
$( '#wikieditor-toolbar-link-type-int, #wikieditor-toolbar-link-type-ext' )
|
||||
.prop( 'checked', false );
|
||||
},
|
||||
|
@ -473,9 +283,10 @@
|
|||
// Pre-fill the text fields based on the current selection
|
||||
var context = $( this ).data( 'context' );
|
||||
var selection = context.$textarea.textSelection( 'getSelection' );
|
||||
$( '#wikieditor-toolbar-link-int-target' ).trigger( 'focus' );
|
||||
|
||||
insertLinkTitleInputField.getField().focus();
|
||||
// Trigger the change event, so the link status indicator is up to date
|
||||
$( '#wikieditor-toolbar-link-int-target' ).trigger( 'change' );
|
||||
insertLinkTitleInputField.getField().$input.trigger( 'change' );
|
||||
$( '#wikieditor-toolbar-link-dialog' ).data( 'whitespace', [ '', '' ] );
|
||||
if ( selection !== '' ) {
|
||||
var matches, target, text, type;
|
||||
|
@ -513,7 +324,7 @@
|
|||
$( '#wikieditor-toolbar-link-int-text' ).val( text ).trigger( 'change' );
|
||||
}
|
||||
if ( typeof target !== 'undefined' ) {
|
||||
$( '#wikieditor-toolbar-link-int-target' ).val( target ).trigger( 'change' );
|
||||
insertLinkTitleInputField.getField().setValue( target );
|
||||
}
|
||||
if ( typeof type !== 'undefined' ) {
|
||||
$( '#wikieditor-toolbar-link-' + type ).prop( 'checked', true );
|
||||
|
@ -521,9 +332,8 @@
|
|||
}
|
||||
$( '#wikieditor-toolbar-link-int-text' ).data( 'untouched',
|
||||
$( '#wikieditor-toolbar-link-int-text' ).val() ===
|
||||
$( '#wikieditor-toolbar-link-int-target' ).val()
|
||||
insertLinkTitleInputField.getField().getValue()
|
||||
);
|
||||
$( '#wikieditor-toolbar-link-int-target' ).suggestions();
|
||||
|
||||
// don't overwrite user's text
|
||||
if ( selection !== '' ) {
|
||||
|
|
|
@ -75,63 +75,10 @@
|
|||
}
|
||||
|
||||
/* Insert Link Dialog */
|
||||
#wikieditor-toolbar-link-int-target-status {
|
||||
float: right;
|
||||
line-height: 1.7em;
|
||||
}
|
||||
|
||||
#wikieditor-toolbar-link-int-target,
|
||||
#wikieditor-toolbar-link-int-text {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
#wikieditor-toolbar-tool-link-int-target-label {
|
||||
float: left;
|
||||
line-height: 1.7em;
|
||||
}
|
||||
|
||||
#wikieditor-toolbar-link-int-target-status-exists,
|
||||
#wikieditor-toolbar-link-int-target-status-notexists,
|
||||
#wikieditor-toolbar-link-int-target-status-invalid,
|
||||
#wikieditor-toolbar-link-int-target-status-external,
|
||||
#wikieditor-toolbar-link-int-target-status-loading,
|
||||
#wikieditor-toolbar-link-int-target-status-disambig {
|
||||
padding-left: 30px;
|
||||
background-position: 0 50%;
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
|
||||
#wikieditor-toolbar-link-int-target-status-exists {
|
||||
.background-image('images/dialogs/insert-link-exists.png');
|
||||
background-position: left;
|
||||
}
|
||||
|
||||
#wikieditor-toolbar-link-int-target-status-notexists {
|
||||
.background-image('images/dialogs/insert-link-notexists.png');
|
||||
background-position: left;
|
||||
}
|
||||
|
||||
#wikieditor-toolbar-link-int-target-status-invalid {
|
||||
.background-image('images/dialogs/insert-link-invalid.png');
|
||||
background-position: left;
|
||||
}
|
||||
|
||||
#wikieditor-toolbar-link-int-target-status-external {
|
||||
.background-image('images/dialogs/insert-link-external.png');
|
||||
background-position: left;
|
||||
}
|
||||
|
||||
#wikieditor-toolbar-link-int-target-status-loading {
|
||||
.background-image('images/dialogs/loading-small.gif');
|
||||
background-position: right;
|
||||
height: 18px;
|
||||
}
|
||||
|
||||
#wikieditor-toolbar-link-int-target-status-disambig {
|
||||
.background-image('images/dialogs/insert-disambiguation.png');
|
||||
background-position: left;
|
||||
}
|
||||
|
||||
/* File dialog */
|
||||
#wikieditor-toolbar-file-target,
|
||||
#wikieditor-toolbar-file-caption,
|
||||
|
|
|
@ -1,9 +1,5 @@
|
|||
<fieldset>
|
||||
<div class="wikieditor-toolbar-field-wrapper">
|
||||
<label for="wikieditor-toolbar-link-int-target" rel="wikieditor-toolbar-tool-link-int-target" id="wikieditor-toolbar-tool-link-int-target-label"></label>
|
||||
<div id="wikieditor-toolbar-link-int-target-status"></div>
|
||||
<input type="text" id="wikieditor-toolbar-link-int-target"/>
|
||||
</div>
|
||||
<div class="wikieditor-toolbar-link-target"></div>
|
||||
<div class="wikieditor-toolbar-field-wrapper">
|
||||
<label for="wikieditor-toolbar-link-int-text" rel="wikieditor-toolbar-tool-link-int-text"></label>
|
||||
<input type="text" id="wikieditor-toolbar-link-int-text"/>
|
||||
|
|