mirror of
https://gerrit.wikimedia.org/r/mediawiki/extensions/VisualEditor
synced 2024-09-26 19:56:49 +00:00
Autolink typed ISBN/RFC/PMIDs
Depends on Ibdad2fa98fca08eeaa96bf33a08dd7723c1edb8c in Parsoid. Depends on I3dcd289ed7b565b9162ee671038eeb45449e1215 in ve-core. Bug: T109498 Change-Id: I5650410d7fca30c90baddd4f0c3f6d80e6b39042
This commit is contained in:
parent
3adfa82ace
commit
ceb0e1f867
|
@ -10,14 +10,17 @@ QUnit.module( 've.ui.MWLinkAction' );
|
||||||
/* Tests */
|
/* Tests */
|
||||||
|
|
||||||
function runMWAutolinkTest( assert, html, method, range, expectedRange, expectedData, expectedOriginalData, msg ) {
|
function runMWAutolinkTest( assert, html, method, range, expectedRange, expectedData, expectedOriginalData, msg ) {
|
||||||
var status,
|
var status, actualData,
|
||||||
expectFail = /^Don't/.test( msg ),
|
expectFail = /^Don't/.test( msg ),
|
||||||
surface = ve.test.utils.createModelOnlySurfaceFromHtml( html || ve.dm.example.html ),
|
surface = ve.test.utils.createModelOnlySurfaceFromHtml( html || ve.dm.example.html ),
|
||||||
linkAction = new ve.ui.MWLinkAction( surface ),
|
linkAction = new ve.ui.MWLinkAction( surface ),
|
||||||
data = ve.copy( surface.getModel().getDocument().getFullData() ),
|
data = ve.copy( surface.getModel().getDocument().getFullData() ),
|
||||||
originalData = ve.copy( data );
|
originalData = ve.copy( data ),
|
||||||
|
makeLinkAnnotation = function ( linktext ) {
|
||||||
|
return linkAction.getLinkAnnotation( linktext ).element;
|
||||||
|
};
|
||||||
|
|
||||||
expectedData( data );
|
expectedData( data, makeLinkAnnotation );
|
||||||
if ( expectedOriginalData ) {
|
if ( expectedOriginalData ) {
|
||||||
expectedOriginalData( originalData );
|
expectedOriginalData( originalData );
|
||||||
}
|
}
|
||||||
|
@ -25,7 +28,9 @@ function runMWAutolinkTest( assert, html, method, range, expectedRange, expected
|
||||||
status = linkAction[ method ]();
|
status = linkAction[ method ]();
|
||||||
assert.equal( status, !expectFail, msg + ': action return value' );
|
assert.equal( status, !expectFail, msg + ': action return value' );
|
||||||
|
|
||||||
assert.equalLinearData( surface.getModel().getDocument().getFullData(), data, msg + ': data models match' );
|
actualData = surface.getModel().getDocument().getFullData();
|
||||||
|
ve.dm.example.postprocessAnnotations( actualData, surface.getModel().getDocument().getStore() );
|
||||||
|
assert.equalLinearData( actualData, data, msg + ': data models match' );
|
||||||
assert.equalRange( surface.getModel().getSelection().getRange(), expectedRange, msg + ': ranges match' );
|
assert.equalRange( surface.getModel().getSelection().getRange(), expectedRange, msg + ': ranges match' );
|
||||||
|
|
||||||
if ( status ) {
|
if ( status ) {
|
||||||
|
@ -40,17 +45,90 @@ QUnit.test( 'MW autolink', function ( assert ) {
|
||||||
var i,
|
var i,
|
||||||
cases = [
|
cases = [
|
||||||
{
|
{
|
||||||
|
msg: 'Strip trailing punctuation (but not matched parens)',
|
||||||
html: '<p>https://en.wikipedia.org/wiki/Red_(disambiguation) xyz</p>',
|
html: '<p>https://en.wikipedia.org/wiki/Red_(disambiguation) xyz</p>',
|
||||||
range: new ve.Range( 1, 52 ),
|
range: new ve.Range( 1, 52 ),
|
||||||
method: 'autolinkUrl',
|
method: 'autolinkUrl',
|
||||||
expectedRange: new ve.Range( 52, 52 ),
|
expectedRange: new ve.Range( 52, 52 ),
|
||||||
expectedData: function ( data ) {
|
expectedData: function ( data, makeAnnotation ) {
|
||||||
var i;
|
var i,
|
||||||
|
a = makeAnnotation( 'https://en.wikipedia.org/wiki/Red_(disambiguation)' );
|
||||||
for ( i = 1; i < 51; i++ ) {
|
for ( i = 1; i < 51; i++ ) {
|
||||||
data[ i ] = [ data[ i ], [ 0 ] ];
|
data[ i ] = [ data[ i ], [ a ] ];
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
msg: 'Strip trailing punctuation (but not matched parens)'
|
},
|
||||||
|
{
|
||||||
|
msg: 'Autolink valid RFC',
|
||||||
|
html: '<p>RFC 1234 xyz</p>',
|
||||||
|
range: new ve.Range( 1, 10 ),
|
||||||
|
method: 'autolinkMagicLink',
|
||||||
|
expectedRange: new ve.Range( 10, 10 ),
|
||||||
|
expectedData: function ( data, makeAnnotation ) {
|
||||||
|
var i,
|
||||||
|
a = makeAnnotation( '//tools.ietf.org/html/rfc1234' );
|
||||||
|
for ( i = 1; i < 9; i++ ) {
|
||||||
|
data[ i ] = [ data[ i ], [ a ] ];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
msg: 'Don\'t autolink invalid RFC',
|
||||||
|
html: '<p>RFC 123x xyz</p>',
|
||||||
|
range: new ve.Range( 1, 10 ),
|
||||||
|
method: 'autolinkMagicLink',
|
||||||
|
expectedRange: new ve.Range( 1, 10 ),
|
||||||
|
expectedData: function ( /*data, makeAnnotation*/ ) {
|
||||||
|
/* no change, no link */
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
msg: 'Autolink valid PMID',
|
||||||
|
html: '<p>PMID 1234 xyz</p>',
|
||||||
|
range: new ve.Range( 1, 11 ),
|
||||||
|
method: 'autolinkMagicLink',
|
||||||
|
expectedRange: new ve.Range( 11, 11 ),
|
||||||
|
expectedData: function ( data, makeAnnotation ) {
|
||||||
|
var i,
|
||||||
|
a = makeAnnotation( '//www.ncbi.nlm.nih.gov/pubmed/1234?dopt=Abstract' );
|
||||||
|
for ( i = 1; i < 10; i++ ) {
|
||||||
|
data[ i ] = [ data[ i ], [ a ] ];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
msg: 'Don\'t autolink invalid PMID',
|
||||||
|
html: '<p>PMID 123x xyz</p>',
|
||||||
|
range: new ve.Range( 1, 11 ),
|
||||||
|
method: 'autolinkMagicLink',
|
||||||
|
expectedRange: new ve.Range( 1, 11 ),
|
||||||
|
expectedData: function ( /*data, makeAnnotation*/ ) {
|
||||||
|
/* no change, no link */
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
msg: 'Autolink valid ISBN',
|
||||||
|
html: '<p>ISBN 978-0596517748 xyz</p>',
|
||||||
|
range: new ve.Range( 1, 21 ),
|
||||||
|
method: 'autolinkMagicLink',
|
||||||
|
expectedRange: new ve.Range( 21, 21 ),
|
||||||
|
expectedData: function ( data, makeAnnotation ) {
|
||||||
|
var i,
|
||||||
|
a = makeAnnotation( './Special:BookSources/9780596517748' );
|
||||||
|
for ( i = 1; i < 20; i++ ) {
|
||||||
|
data[ i ] = [ data[ i ], [ a ] ];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
msg: 'Don\'t autolink invalid ISBN',
|
||||||
|
html: '<p>ISBN 978-059651774 xyz</p>',
|
||||||
|
range: new ve.Range( 1, 20 ),
|
||||||
|
method: 'autolinkMagicLink',
|
||||||
|
expectedRange: new ve.Range( 1, 20 ),
|
||||||
|
expectedData: function ( /*data, makeAnnotation*/ ) {
|
||||||
|
/* no change, no link */
|
||||||
|
}
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
|
@ -31,7 +31,7 @@ OO.inheritClass( ve.ui.MWLinkAction, ve.ui.LinkAction );
|
||||||
* @static
|
* @static
|
||||||
* @property
|
* @property
|
||||||
*/
|
*/
|
||||||
ve.ui.MWLinkAction.static.methods = ve.ui.MWLinkAction.super.static.methods.concat( [ 'open' ] );
|
ve.ui.MWLinkAction.static.methods = ve.ui.MWLinkAction.super.static.methods.concat( [ 'open', 'autolinkMagicLink' ] );
|
||||||
|
|
||||||
/* Methods */
|
/* Methods */
|
||||||
|
|
||||||
|
@ -53,14 +53,27 @@ ve.ui.MWLinkAction.prototype.getTrailingPunctuation = function ( candidate ) {
|
||||||
* @inheritdoc
|
* @inheritdoc
|
||||||
* @return {ve.dm.MWExternalLinkAnnotation} The annotation to use.
|
* @return {ve.dm.MWExternalLinkAnnotation} The annotation to use.
|
||||||
*/
|
*/
|
||||||
ve.ui.MWLinkAction.prototype.getLinkAnnotation = function ( href ) {
|
ve.ui.MWLinkAction.prototype.getLinkAnnotation = function ( linktext ) {
|
||||||
var title,
|
var title, targetData, m,
|
||||||
|
href = linktext;
|
||||||
|
// The link has been validated in #autolinkMagicLink and/or
|
||||||
|
// #autolinkUrl, so we can use a quick and dirty regexp here to pull
|
||||||
|
// apart the magic link.
|
||||||
|
m = /^(RFC|PMID|ISBN)\s+(\S.*)$/.exec( linktext );
|
||||||
|
if ( m && m[ 1 ] === 'RFC' ) {
|
||||||
|
href = '//tools.ietf.org/html/rfc' + m[ 2 ];
|
||||||
|
} else if ( m && m[ 1 ] === 'PMID' ) {
|
||||||
|
href = '//www.ncbi.nlm.nih.gov/pubmed/' + m[ 2 ] + '?dopt=Abstract';
|
||||||
|
} else if ( m && m[ 1 ] === 'ISBN' ) {
|
||||||
|
title = mw.Title.newFromText( 'Special:BookSources/' + m[ 2 ].replace( /[^0-9Xx]/g, '' ) );
|
||||||
|
} else {
|
||||||
targetData = ve.dm.MWInternalLinkAnnotation.static.getTargetDataFromHref(
|
targetData = ve.dm.MWInternalLinkAnnotation.static.getTargetDataFromHref(
|
||||||
href,
|
href,
|
||||||
this.surface.getModel().getDocument().getHtmlDocument()
|
this.surface.getModel().getDocument().getHtmlDocument()
|
||||||
);
|
);
|
||||||
if ( targetData.isInternal ) {
|
if ( targetData.isInternal ) {
|
||||||
title = mw.Title.newFromText( targetData.title );
|
title = mw.Title.newFromText( targetData.title );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return title ?
|
return title ?
|
||||||
ve.dm.MWInternalLinkAnnotation.static.newFromTitle( title ) :
|
ve.dm.MWInternalLinkAnnotation.static.newFromTitle( title ) :
|
||||||
|
@ -70,6 +83,27 @@ ve.ui.MWLinkAction.prototype.getLinkAnnotation = function ( href ) {
|
||||||
} );
|
} );
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Autolink the selected RFC/PMID/ISBN, which may have trailing whitespace.
|
||||||
|
*
|
||||||
|
* @see ve.ui.LinkAction#autolinkUrl
|
||||||
|
* @method
|
||||||
|
* @return {boolean}
|
||||||
|
* True if the selection is a valid RFC/PMID/ISBN and the autolink action
|
||||||
|
* was executed; otherwise false.
|
||||||
|
*/
|
||||||
|
ve.ui.MWLinkAction.prototype.autolinkMagicLink = function () {
|
||||||
|
return this.autolink( function ( linktext ) {
|
||||||
|
if ( /^(RFC|PMID) [0-9]+$/.test( linktext ) ) {
|
||||||
|
return true; // Valid RFC/PMID
|
||||||
|
}
|
||||||
|
if ( /^ISBN (97[89][- ]?)?([0-9][- ]?){9}[0-9Xx]$/.test( linktext ) ) {
|
||||||
|
return true; // Valid ISBN
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
} );
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Open either the 'link' or 'linkNode' window, depending on what is selected.
|
* Open either the 'link' or 'linkNode' window, depending on what is selected.
|
||||||
*
|
*
|
||||||
|
@ -90,3 +124,16 @@ ve.ui.MWLinkAction.prototype.open = function () {
|
||||||
/* Registration */
|
/* Registration */
|
||||||
|
|
||||||
ve.ui.actionFactory.register( ve.ui.MWLinkAction );
|
ve.ui.actionFactory.register( ve.ui.MWLinkAction );
|
||||||
|
|
||||||
|
ve.ui.commandRegistry.register(
|
||||||
|
new ve.ui.Command(
|
||||||
|
'autolinkMagicLink', ve.ui.MWLinkAction.static.name, 'autolinkMagicLink',
|
||||||
|
{ supportedSelections: [ 'linear' ] }
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
ve.ui.sequenceRegistry.register(
|
||||||
|
// This regexp doesn't have to be precise; we'll validate the magic
|
||||||
|
// link in #autolinkMagicLink above.
|
||||||
|
new ve.ui.Sequence( 'autolinkMagicLink', 'autolinkMagicLink', /\b(RFC|PMID|ISBN)\s+[0-9]([- 0-9]*[0-9Xx])?(\s|\n\n)$/, 0, true )
|
||||||
|
);
|
||||||
|
|
Loading…
Reference in a new issue