mirror of
https://gerrit.wikimedia.org/r/mediawiki/extensions/VisualEditor
synced 2024-09-24 10:48:42 +00:00
Merge "Specialized inspector for ISBN magic links"
This commit is contained in:
commit
8502d3ea05
|
@ -440,6 +440,7 @@ class VisualEditorHooks {
|
||||||
'enableTocWidget' => $veConfig->get( 'VisualEditorEnableTocWidget' ),
|
'enableTocWidget' => $veConfig->get( 'VisualEditorEnableTocWidget' ),
|
||||||
'svgMaxSize' => $coreConfig->get( 'SVGMaxSize' ),
|
'svgMaxSize' => $coreConfig->get( 'SVGMaxSize' ),
|
||||||
'namespacesWithSubpages' => $coreConfig->get( 'NamespacesWithSubpages' ),
|
'namespacesWithSubpages' => $coreConfig->get( 'NamespacesWithSubpages' ),
|
||||||
|
'specialBooksources' => urldecode( SpecialPage::getTitleFor( 'Booksources' )->getPrefixedURL() ),
|
||||||
'restbaseUrl' => $coreConfig->get( 'VisualEditorRestbaseURL' ),
|
'restbaseUrl' => $coreConfig->get( 'VisualEditorRestbaseURL' ),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -1191,9 +1191,11 @@
|
||||||
},
|
},
|
||||||
"ext.visualEditor.mwlink": {
|
"ext.visualEditor.mwlink": {
|
||||||
"scripts": [
|
"scripts": [
|
||||||
|
"modules/ve-mw/dm/nodes/ve.dm.MWMagicLinkNode.js",
|
||||||
"modules/ve-mw/dm/nodes/ve.dm.MWNumberedExternalLinkNode.js",
|
"modules/ve-mw/dm/nodes/ve.dm.MWNumberedExternalLinkNode.js",
|
||||||
"modules/ve-mw/dm/annotations/ve.dm.MWExternalLinkAnnotation.js",
|
"modules/ve-mw/dm/annotations/ve.dm.MWExternalLinkAnnotation.js",
|
||||||
"modules/ve-mw/dm/annotations/ve.dm.MWInternalLinkAnnotation.js",
|
"modules/ve-mw/dm/annotations/ve.dm.MWInternalLinkAnnotation.js",
|
||||||
|
"modules/ve-mw/ce/nodes/ve.ce.MWMagicLinkNode.js",
|
||||||
"modules/ve-mw/ce/nodes/ve.ce.MWNumberedExternalLinkNode.js",
|
"modules/ve-mw/ce/nodes/ve.ce.MWNumberedExternalLinkNode.js",
|
||||||
"modules/ve-mw/ce/annotations/ve.ce.MWExternalLinkAnnotation.js",
|
"modules/ve-mw/ce/annotations/ve.ce.MWExternalLinkAnnotation.js",
|
||||||
"modules/ve-mw/ce/annotations/ve.ce.MWInternalLinkAnnotation.js",
|
"modules/ve-mw/ce/annotations/ve.ce.MWInternalLinkAnnotation.js",
|
||||||
|
@ -1201,8 +1203,10 @@
|
||||||
"modules/ve-mw/ui/widgets/ve.ui.MWExternalLinkAnnotationWidget.js",
|
"modules/ve-mw/ui/widgets/ve.ui.MWExternalLinkAnnotationWidget.js",
|
||||||
"modules/ve-mw/ui/inspectors/ve.ui.MWLinkAnnotationInspector.js",
|
"modules/ve-mw/ui/inspectors/ve.ui.MWLinkAnnotationInspector.js",
|
||||||
"modules/ve-mw/ui/inspectors/ve.ui.MWLinkNodeInspector.js",
|
"modules/ve-mw/ui/inspectors/ve.ui.MWLinkNodeInspector.js",
|
||||||
|
"modules/ve-mw/ui/inspectors/ve.ui.MWMagicLinkNodeInspector.js",
|
||||||
"modules/ve-mw/ui/tools/ve.ui.MWLinkInspectorTool.js",
|
"modules/ve-mw/ui/tools/ve.ui.MWLinkInspectorTool.js",
|
||||||
"modules/ve-mw/ui/contextitems/ve.ui.MWInternalLinkContextItem.js",
|
"modules/ve-mw/ui/contextitems/ve.ui.MWInternalLinkContextItem.js",
|
||||||
|
"modules/ve-mw/ui/contextitems/ve.ui.MWMagicLinkNodeContextItem.js",
|
||||||
"modules/ve-mw/ui/contextitems/ve.ui.MWNumberedExternalLinkNodeContextItem.js"
|
"modules/ve-mw/ui/contextitems/ve.ui.MWNumberedExternalLinkNodeContextItem.js"
|
||||||
],
|
],
|
||||||
"styles": [
|
"styles": [
|
||||||
|
@ -1214,11 +1218,21 @@
|
||||||
],
|
],
|
||||||
"messages": [
|
"messages": [
|
||||||
"visualeditor-annotationbutton-linknode-tooltip",
|
"visualeditor-annotationbutton-linknode-tooltip",
|
||||||
|
"visualeditor-annotationbutton-magiclinknode-tooltip-isbn",
|
||||||
|
"visualeditor-annotationbutton-magiclinknode-tooltip-pmid",
|
||||||
|
"visualeditor-annotationbutton-magiclinknode-tooltip-rfc",
|
||||||
"visualeditor-linkinspector-button-link-external",
|
"visualeditor-linkinspector-button-link-external",
|
||||||
"visualeditor-linkinspector-button-link-internal",
|
"visualeditor-linkinspector-button-link-internal",
|
||||||
|
"visualeditor-linkinspector-convert-link-isbn",
|
||||||
|
"visualeditor-linkinspector-convert-link-pmid",
|
||||||
|
"visualeditor-linkinspector-convert-link-rfc",
|
||||||
"visualeditor-linkinspector-illegal-title",
|
"visualeditor-linkinspector-illegal-title",
|
||||||
"visualeditor-linknodeinspector-add-label",
|
"visualeditor-linknodeinspector-add-label",
|
||||||
"visualeditor-linknodeinspector-title"
|
"visualeditor-linknodeinspector-title",
|
||||||
|
"visualeditor-magiclinknodeinspector-convert-link",
|
||||||
|
"visualeditor-magiclinknodeinspector-title-isbn",
|
||||||
|
"visualeditor-magiclinknodeinspector-title-pmid",
|
||||||
|
"visualeditor-magiclinknodeinspector-title-rfc"
|
||||||
],
|
],
|
||||||
"targets": [
|
"targets": [
|
||||||
"desktop",
|
"desktop",
|
||||||
|
|
81
modules/ve-mw/ce/nodes/ve.ce.MWMagicLinkNode.js
Normal file
81
modules/ve-mw/ce/nodes/ve.ce.MWMagicLinkNode.js
Normal file
|
@ -0,0 +1,81 @@
|
||||||
|
/*!
|
||||||
|
* VisualEditor ContentEditable MWMagicLinkNode class.
|
||||||
|
*
|
||||||
|
* @copyright 2011-2015 VisualEditor Team and others; see AUTHORS.txt
|
||||||
|
* @license The MIT License (MIT); see LICENSE.txt
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ContentEditable MediaWiki magic link node.
|
||||||
|
*
|
||||||
|
* @class
|
||||||
|
* @extends ve.ce.LeafNode
|
||||||
|
* @mixins ve.ce.FocusableNode
|
||||||
|
* @constructor
|
||||||
|
* @param {ve.dm.MWMagicLinkNode} model Model to observe
|
||||||
|
* @param {Object} [config] Configuration options
|
||||||
|
*/
|
||||||
|
ve.ce.MWMagicLinkNode = function VeCeMWMagicLinkNode( model, config ) {
|
||||||
|
// Parent constructor
|
||||||
|
ve.ce.LeafNode.call( this, model, config );
|
||||||
|
|
||||||
|
// Mixin constructors
|
||||||
|
ve.ce.FocusableNode.call( this );
|
||||||
|
|
||||||
|
// DOM changes
|
||||||
|
this.$element
|
||||||
|
.addClass( 've-ce-mwMagicLinkNode' )
|
||||||
|
// Need CE=false to prevent selection issues
|
||||||
|
.prop( 'contentEditable', 'false' );
|
||||||
|
|
||||||
|
// Add link
|
||||||
|
this.$link = $( '<a>' )
|
||||||
|
.appendTo( this.$element );
|
||||||
|
|
||||||
|
// Events
|
||||||
|
this.model.connect( this, { update: 'onUpdate' } );
|
||||||
|
|
||||||
|
// Initialization
|
||||||
|
this.onUpdate();
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Inheritance */
|
||||||
|
|
||||||
|
OO.inheritClass( ve.ce.MWMagicLinkNode, ve.ce.LeafNode );
|
||||||
|
|
||||||
|
OO.mixinClass( ve.ce.MWMagicLinkNode, ve.ce.FocusableNode );
|
||||||
|
|
||||||
|
/* Static Properties */
|
||||||
|
|
||||||
|
ve.ce.MWMagicLinkNode.static.name = 'link/mwMagic';
|
||||||
|
|
||||||
|
ve.ce.MWMagicLinkNode.static.tagName = 'span';
|
||||||
|
|
||||||
|
ve.ce.MWMagicLinkNode.static.primaryCommandName = 'link';
|
||||||
|
|
||||||
|
/* Static Methods */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritdoc
|
||||||
|
*/
|
||||||
|
ve.ce.MWMagicLinkNode.static.getDescription = function ( model ) {
|
||||||
|
return model.getAttribute( 'content' );
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Methods */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle model update events.
|
||||||
|
*
|
||||||
|
* @method
|
||||||
|
*/
|
||||||
|
ve.ce.MWMagicLinkNode.prototype.onUpdate = function () {
|
||||||
|
this.$link
|
||||||
|
.attr( 'href', this.model.getHref() )
|
||||||
|
.attr( 'rel', this.model.getRel() )
|
||||||
|
.text( this.model.getAttribute( 'content' ) );
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Registration */
|
||||||
|
|
||||||
|
ve.ce.nodeFactory.register( ve.ce.MWMagicLinkNode );
|
|
@ -137,7 +137,8 @@ ve.dm.MWInternalLinkAnnotation.static.getHref = function ( dataElement ) {
|
||||||
href = dataElement.attributes.hrefPrefix + href;
|
href = dataElement.attributes.hrefPrefix + href;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
href = encodeURIComponent( title );
|
// Don't escape slashes in the title; they represent subpages.
|
||||||
|
href = title.split( '/' ).map( encodeURIComponent ).join( '/' );
|
||||||
}
|
}
|
||||||
return href;
|
return href;
|
||||||
};
|
};
|
||||||
|
|
415
modules/ve-mw/dm/nodes/ve.dm.MWMagicLinkNode.js
Normal file
415
modules/ve-mw/dm/nodes/ve.dm.MWMagicLinkNode.js
Normal file
|
@ -0,0 +1,415 @@
|
||||||
|
/*!
|
||||||
|
* VisualEditor DataModel MWMagicLinkNode class.
|
||||||
|
*
|
||||||
|
* @copyright 2011-2015 VisualEditor Team and others; see AUTHORS.txt
|
||||||
|
* @license The MIT License (MIT); see LICENSE.txt
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DataModel MediaWiki magic link node.
|
||||||
|
*
|
||||||
|
* @class
|
||||||
|
* @extends ve.dm.LeafNode
|
||||||
|
* @mixins ve.dm.FocusableNode
|
||||||
|
*
|
||||||
|
* @constructor
|
||||||
|
* @param {Object} [element] Reference to element in linear model
|
||||||
|
*/
|
||||||
|
ve.dm.MWMagicLinkNode = function VeDmMWMagicLinkNode() {
|
||||||
|
// Parent constructor
|
||||||
|
ve.dm.LeafNode.apply( this, arguments );
|
||||||
|
|
||||||
|
// Mixin constructors
|
||||||
|
ve.dm.FocusableNode.call( this );
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Inheritance */
|
||||||
|
|
||||||
|
OO.inheritClass( ve.dm.MWMagicLinkNode, ve.dm.LeafNode );
|
||||||
|
|
||||||
|
OO.mixinClass( ve.dm.MWMagicLinkNode, ve.dm.FocusableNode );
|
||||||
|
|
||||||
|
/* Static Properties */
|
||||||
|
|
||||||
|
ve.dm.MWMagicLinkNode.static.name = 'link/mwMagic';
|
||||||
|
|
||||||
|
ve.dm.MWMagicLinkNode.static.isContent = true;
|
||||||
|
|
||||||
|
ve.dm.MWMagicLinkNode.static.matchTagNames = [ 'a' ];
|
||||||
|
|
||||||
|
ve.dm.MWMagicLinkNode.static.matchRdfaTypes = [ 'mw:WikiLink', 'mw:ExtLink' ];
|
||||||
|
|
||||||
|
ve.dm.MWMagicLinkNode.static.blacklistedAnnotationTypes = [ 'link' ];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine whether the given `element` is a magic link.
|
||||||
|
*
|
||||||
|
* @return {boolean} True if the element is a magic link
|
||||||
|
*/
|
||||||
|
ve.dm.MWMagicLinkNode.static.matchFunction = function ( element ) {
|
||||||
|
var i,
|
||||||
|
children = element.childNodes,
|
||||||
|
href = element.getAttribute( 'href' );
|
||||||
|
// All children must be text nodes, or a <span> representing an entity.
|
||||||
|
for ( i = 0; i < children.length; i++ ) {
|
||||||
|
if ( children[ i ].nodeType === Node.TEXT_NODE ) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// <span typeof='mw:Entity'>...</span> (for )
|
||||||
|
if ( children[ i ].nodeType === Node.ELEMENT_NODE &&
|
||||||
|
children[ i ].tagName === 'SPAN' &&
|
||||||
|
children[ i ].getAttribute( 'typeof' ) === 'mw:Entity' ) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check that text content matches one of the magic link types and that
|
||||||
|
// the href matches that expected for the magic link type.
|
||||||
|
return ve.dm.MWMagicLinkNode.static.validateHref( element.textContent, href );
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test that a proposed content string is valid for a magic link.
|
||||||
|
* If `optType` is given, additionally verify that the content string is
|
||||||
|
* valid for the particular type of magic link (RFC/ISBN/PMID).
|
||||||
|
*
|
||||||
|
* @param {string} content
|
||||||
|
* The content string to test.
|
||||||
|
* @param {string} [optType]
|
||||||
|
* The desired type of magic link, one of "RFC", "ISBN", or "PMID".
|
||||||
|
* If not supplied, returns true if the content is valid for any one
|
||||||
|
* of these.
|
||||||
|
* @return {boolean}
|
||||||
|
* True if the content string is valid for a magic link of the appropriate
|
||||||
|
* type (or any type).
|
||||||
|
*/
|
||||||
|
ve.dm.MWMagicLinkNode.static.validateContent = function ( content, optType ) {
|
||||||
|
var type = ve.dm.MWMagicLinkType.static.fromContent( content );
|
||||||
|
if ( type === null || ( optType !== undefined && type.type !== optType ) ) {
|
||||||
|
// Not a valid magic link, or a magic link of the wrong type.
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test that a proposed content string and href is valid for a magic link.
|
||||||
|
*
|
||||||
|
* @param {string} content
|
||||||
|
* The content string to test.
|
||||||
|
* @param {string} href
|
||||||
|
* The URL target of the magic link.
|
||||||
|
* @return {boolean}
|
||||||
|
* True if the content string and href are valid for a magic link.
|
||||||
|
*/
|
||||||
|
ve.dm.MWMagicLinkNode.static.validateHref = function ( content, href ) {
|
||||||
|
var type = ve.dm.MWMagicLinkType.static.fromContent( content );
|
||||||
|
return type && type.matchHref( href );
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return a link annotation appropriate for converting a magic link
|
||||||
|
* with the given content into a simple link, or `null` if the given
|
||||||
|
* content is not a valid magic link.
|
||||||
|
*
|
||||||
|
* @return {ve.dm.MWExternalLinkAnnotation|ve.dm.MWInternalLinkAnnotation|null}
|
||||||
|
*/
|
||||||
|
ve.dm.MWMagicLinkNode.static.annotationFromContent = function ( content ) {
|
||||||
|
var type = ve.dm.MWMagicLinkType.static.fromContent( content );
|
||||||
|
return type !== null ? type.getAnnotation() : null;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritdoc
|
||||||
|
*/
|
||||||
|
ve.dm.MWMagicLinkNode.static.toDataElement = function ( domElements ) {
|
||||||
|
var textContent = domElements[ 0 ].textContent,
|
||||||
|
htmlContent = domElements[ 0 ].innerHTML;
|
||||||
|
return {
|
||||||
|
type: this.name,
|
||||||
|
attributes: {
|
||||||
|
content: textContent,
|
||||||
|
// These next two attributes allow lossless round-tripping
|
||||||
|
// if the original wikitext contained html entities like
|
||||||
|
//
|
||||||
|
origText: textContent,
|
||||||
|
origHtml: htmlContent
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritdoc
|
||||||
|
*/
|
||||||
|
ve.dm.MWMagicLinkNode.static.toDomElements = function ( dataElement, doc ) {
|
||||||
|
var content = dataElement.attributes.content,
|
||||||
|
type = ve.dm.MWMagicLinkType.static.fromContent( content ),
|
||||||
|
href = type.getHref(),
|
||||||
|
domElement = doc.createElement( 'a' );
|
||||||
|
domElement.setAttribute( 'href', href );
|
||||||
|
domElement.setAttribute( 'rel', type.rel );
|
||||||
|
if ( content === dataElement.attributes.origText ) {
|
||||||
|
// Preserve <span typeof="mw:Entity"> elements from the original.
|
||||||
|
domElement.innerHTML = dataElement.attributes.origHtml;
|
||||||
|
} else {
|
||||||
|
domElement.textContent = content;
|
||||||
|
}
|
||||||
|
return [ domElement ];
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Methods */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the link target appropriate for this magic link node.
|
||||||
|
*
|
||||||
|
* @return {string} Link href
|
||||||
|
*/
|
||||||
|
ve.dm.MWMagicLinkNode.prototype.getHref = function () {
|
||||||
|
var content = this.element.attributes.content,
|
||||||
|
type = ve.dm.MWMagicLinkType.static.fromContent( content );
|
||||||
|
return type.getHref();
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the rel attribute appropriate for this magic link node.
|
||||||
|
*
|
||||||
|
* @return {string} Either "mw:ExtLink" or "mw:WikiLink"
|
||||||
|
*/
|
||||||
|
ve.dm.MWMagicLinkNode.prototype.getRel = function () {
|
||||||
|
var content = this.element.attributes.content,
|
||||||
|
type = ve.dm.MWMagicLinkType.static.fromContent( content );
|
||||||
|
return type.rel;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the type of this magic link node: one of "RFC", "PMID", or "ISBN".
|
||||||
|
*
|
||||||
|
* @return {string} Magic link type
|
||||||
|
*/
|
||||||
|
ve.dm.MWMagicLinkNode.prototype.getMagicType = function () {
|
||||||
|
var content = this.element.attributes.content,
|
||||||
|
type = ve.dm.MWMagicLinkType.static.fromContent( content );
|
||||||
|
return type.type;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the numeric code associated with this magic link node.
|
||||||
|
*
|
||||||
|
* @return {string}
|
||||||
|
*/
|
||||||
|
ve.dm.MWMagicLinkNode.prototype.getCode = function () {
|
||||||
|
var content = this.element.attributes.content,
|
||||||
|
type = ve.dm.MWMagicLinkType.static.fromContent( content );
|
||||||
|
return type.num;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Encapsulation of a particular magic link type.
|
||||||
|
*
|
||||||
|
* @class
|
||||||
|
* @private
|
||||||
|
*
|
||||||
|
* @constructor
|
||||||
|
* @param {string} type
|
||||||
|
* The type of magic link; one of `'ISBN'`, `'PMID'`, or `'RFC'`.
|
||||||
|
* @param {string} rel
|
||||||
|
* The value of the link's "rel" attribute.
|
||||||
|
* Either `'mw:ExtLink'` or `'mw:WikiLink'`.
|
||||||
|
* @param {string} content
|
||||||
|
* The content of the magic link.
|
||||||
|
*/
|
||||||
|
ve.dm.MWMagicLinkType = function VeDmMWMagicLinkType( type, rel, content ) {
|
||||||
|
this.type = type;
|
||||||
|
this.rel = rel;
|
||||||
|
this.content = content;
|
||||||
|
// Make the code available as a property; this is also used for
|
||||||
|
// validity checking.
|
||||||
|
this.code = this.getCode();
|
||||||
|
};
|
||||||
|
|
||||||
|
OO.initClass( ve.dm.MWMagicLinkType );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritdoc ve.dm.MWMagicLinkNode#annotationFromContent
|
||||||
|
*/
|
||||||
|
ve.dm.MWMagicLinkType.prototype.getAnnotation = function () {
|
||||||
|
return new ve.dm.MWExternalLinkAnnotation( {
|
||||||
|
type: 'link/mwExternal',
|
||||||
|
attributes: { href: this.getHref() }
|
||||||
|
} );
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritdoc ve.dm.MWMagicLinkNode#getCode
|
||||||
|
* @protected
|
||||||
|
*/
|
||||||
|
ve.dm.MWMagicLinkType.prototype.getCode = function () {
|
||||||
|
var m = /^([A-Z]+)[\t \u00A0\u1680\u2000-\u200A\u202F\u205F\u3000]+(\d+)$/.exec( this.content );
|
||||||
|
if ( !m || m[ 1 ] !== this.type ) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return m[ 2 ];
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @method getHref
|
||||||
|
* @inheritdoc ve.dm.MWMagicLinkNode#getHref
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return true if the given href is appropriate for this magic link.
|
||||||
|
*
|
||||||
|
* @param {string} href
|
||||||
|
* @return {boolean}
|
||||||
|
*/
|
||||||
|
ve.dm.MWMagicLinkType.prototype.matchHref = function ( href ) {
|
||||||
|
return href.replace( /^https?:/i, '' ) === this.getHref();
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the subclass of {@link ve.dm.MWMagicLinkType}
|
||||||
|
* appropriate for the given content, or `null` if the content
|
||||||
|
* is not appropriate for a magic link.
|
||||||
|
*
|
||||||
|
* @param {string} content
|
||||||
|
* @return {ve.dm.MWMagicLinkType|null}
|
||||||
|
*/
|
||||||
|
ve.dm.MWMagicLinkType.static.fromContent = function ( content ) {
|
||||||
|
var m = /^(ISBN|PMID|RFC)/.exec( content ),
|
||||||
|
typeStr = m && m[ 1 ],
|
||||||
|
type = null;
|
||||||
|
if ( typeStr === 'ISBN' ) {
|
||||||
|
type = new ve.dm.MWMagicLinkIsbnType( content );
|
||||||
|
} else if ( typeStr === 'PMID' ) {
|
||||||
|
type = new ve.dm.MWMagicLinkPmidType( content );
|
||||||
|
} else if ( typeStr === 'RFC' ) {
|
||||||
|
type = new ve.dm.MWMagicLinkRfcType( content );
|
||||||
|
}
|
||||||
|
// validate parsed number
|
||||||
|
return type && type.code !== null ? type : null;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An ISBN magic link.
|
||||||
|
*
|
||||||
|
* @class
|
||||||
|
* @extends ve.dm.MWMagicLinkType
|
||||||
|
* @private
|
||||||
|
*
|
||||||
|
* @constructor
|
||||||
|
* @param {string} content
|
||||||
|
*/
|
||||||
|
ve.dm.MWMagicLinkIsbnType = function VeDmMWMagicLinkIsbnType( content ) {
|
||||||
|
// Parent constructor
|
||||||
|
ve.dm.MWMagicLinkIsbnType.super.call( this, 'ISBN', 'mw:WikiLink', content );
|
||||||
|
};
|
||||||
|
|
||||||
|
OO.inheritClass( ve.dm.MWMagicLinkIsbnType, ve.dm.MWMagicLinkType );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritdoc
|
||||||
|
*/
|
||||||
|
ve.dm.MWMagicLinkIsbnType.prototype.getAnnotation = function () {
|
||||||
|
var conf = mw.config.get( 'wgVisualEditorConfig' ),
|
||||||
|
title = mw.Title.newFromText( conf.specialBooksources + '/' + this.code );
|
||||||
|
return ve.dm.MWInternalLinkAnnotation.static.newFromTitle( title );
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritdoc
|
||||||
|
*/
|
||||||
|
ve.dm.MWMagicLinkIsbnType.prototype.getCode = function () {
|
||||||
|
var spaceOrDash, isbnCode,
|
||||||
|
content = this.content;
|
||||||
|
|
||||||
|
if ( !/^ISBN[^-0-9][\s\S]+[0-9Xx]$/.test( content ) ) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove unicode whitespace and dashes
|
||||||
|
spaceOrDash = /[-\t \u00A0\u1680\u2000-\u200A\u202F\u205F\u3000]+/g;
|
||||||
|
isbnCode = content.replace( spaceOrDash, '' ).replace( /^ISBN/, '' );
|
||||||
|
|
||||||
|
// Verify format of ISBN
|
||||||
|
if ( !/^(97[89])?\d{9}[0-9Xx]$/.test( isbnCode ) ) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return isbnCode.toUpperCase();
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritdoc
|
||||||
|
*/
|
||||||
|
ve.dm.MWMagicLinkIsbnType.prototype.getHref = function () {
|
||||||
|
var conf = mw.config.get( 'wgVisualEditorConfig' );
|
||||||
|
return './' + conf.specialBooksources + '/' + this.code;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritdoc
|
||||||
|
*/
|
||||||
|
ve.dm.MWMagicLinkIsbnType.prototype.matchHref = function ( href ) {
|
||||||
|
var conf, m, normalized;
|
||||||
|
|
||||||
|
conf = mw.config.get( 'wgVisualEditorConfig' );
|
||||||
|
m = /^(?:[.]+\/)*([^\/]+)\/(\d+[Xx]?)$/.exec( href );
|
||||||
|
if ( !m ) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// conf.specialBooksources has localized name for Special:Booksources
|
||||||
|
normalized = ve.safeDecodeURIComponent( m[ 1 ] ).replace( ' ', '_' );
|
||||||
|
if ( normalized !== 'Special:BookSources' && normalized !== conf.specialBooksources ) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if ( m[ 2 ] !== this.code ) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A PMID magic link.
|
||||||
|
*
|
||||||
|
* @class
|
||||||
|
* @extends ve.dm.MWMagicLinkType
|
||||||
|
* @private
|
||||||
|
*
|
||||||
|
* @constructor
|
||||||
|
* @param {string} content
|
||||||
|
*/
|
||||||
|
ve.dm.MWMagicLinkPmidType = function VeDmMWMagicLinkPmidType( content ) {
|
||||||
|
// Parent constructor
|
||||||
|
ve.dm.MWMagicLinkPmidType.super.call( this, 'PMID', 'mw:ExtLink', content );
|
||||||
|
};
|
||||||
|
|
||||||
|
OO.inheritClass( ve.dm.MWMagicLinkPmidType, ve.dm.MWMagicLinkType );
|
||||||
|
|
||||||
|
ve.dm.MWMagicLinkPmidType.prototype.getHref = function () {
|
||||||
|
return '//www.ncbi.nlm.nih.gov/pubmed/' + this.code + '?dopt=Abstract';
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An RFC magic link.
|
||||||
|
*
|
||||||
|
* @class
|
||||||
|
* @extends ve.dm.MWMagicLinkType
|
||||||
|
* @private
|
||||||
|
*
|
||||||
|
* @constructor
|
||||||
|
* @param {string} content
|
||||||
|
*/
|
||||||
|
ve.dm.MWMagicLinkRfcType = function VeDmMWMagicLinkRfcType( content ) {
|
||||||
|
// Parent constructor
|
||||||
|
ve.dm.MWMagicLinkRfcType.super.call( this, 'RFC', 'mw:ExtLink', content );
|
||||||
|
};
|
||||||
|
|
||||||
|
OO.inheritClass( ve.dm.MWMagicLinkRfcType, ve.dm.MWMagicLinkType );
|
||||||
|
|
||||||
|
ve.dm.MWMagicLinkRfcType.prototype.getHref = function () {
|
||||||
|
return '//tools.ietf.org/html/rfc' + this.code;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Registration */
|
||||||
|
|
||||||
|
ve.dm.modelRegistry.register( ve.dm.MWMagicLinkNode );
|
|
@ -49,6 +49,9 @@
|
||||||
"tooltip-ca-ve-edit": "Edit this page",
|
"tooltip-ca-ve-edit": "Edit this page",
|
||||||
"visualeditor-advancedsettings-tool": "Advanced settings",
|
"visualeditor-advancedsettings-tool": "Advanced settings",
|
||||||
"visualeditor-annotationbutton-linknode-tooltip": "Simple link",
|
"visualeditor-annotationbutton-linknode-tooltip": "Simple link",
|
||||||
|
"visualeditor-annotationbutton-magiclinknode-tooltip-isbn": "ISBN link",
|
||||||
|
"visualeditor-annotationbutton-magiclinknode-tooltip-pmid": "PMID link",
|
||||||
|
"visualeditor-annotationbutton-magiclinknode-tooltip-rfc": "RFC link",
|
||||||
"visualeditor-backbutton-tooltip": "Go back",
|
"visualeditor-backbutton-tooltip": "Go back",
|
||||||
"visualeditor-beta-appendix": "beta",
|
"visualeditor-beta-appendix": "beta",
|
||||||
"visualeditor-beta-label": "beta",
|
"visualeditor-beta-label": "beta",
|
||||||
|
@ -245,6 +248,9 @@
|
||||||
"visualeditor-languages-tool": "Languages",
|
"visualeditor-languages-tool": "Languages",
|
||||||
"visualeditor-linkinspector-button-link-external": "External link",
|
"visualeditor-linkinspector-button-link-external": "External link",
|
||||||
"visualeditor-linkinspector-button-link-internal": "Search pages",
|
"visualeditor-linkinspector-button-link-internal": "Search pages",
|
||||||
|
"visualeditor-linkinspector-convert-link-isbn": "Convert to ISBN link",
|
||||||
|
"visualeditor-linkinspector-convert-link-pmid": "Convert to PMID link",
|
||||||
|
"visualeditor-linkinspector-convert-link-rfc": "Convert to RFC link",
|
||||||
"visualeditor-linkinspector-illegal-title": "Invalid page title",
|
"visualeditor-linkinspector-illegal-title": "Invalid page title",
|
||||||
"visualeditor-linknodeinspector-add-label": "Add label",
|
"visualeditor-linknodeinspector-add-label": "Add label",
|
||||||
"visualeditor-linknodeinspector-title": "Simple link",
|
"visualeditor-linknodeinspector-title": "Simple link",
|
||||||
|
@ -253,6 +259,10 @@
|
||||||
"visualeditor-loadwarning": "Error loading data from server: $1. Would you like to retry?",
|
"visualeditor-loadwarning": "Error loading data from server: $1. Would you like to retry?",
|
||||||
"visualeditor-loadwarning-token": "Error loading edit token from server: $1. Would you like to retry?",
|
"visualeditor-loadwarning-token": "Error loading edit token from server: $1. Would you like to retry?",
|
||||||
"visualeditor-mainnamespacepagelink": "Project:Main namespace",
|
"visualeditor-mainnamespacepagelink": "Project:Main namespace",
|
||||||
|
"visualeditor-magiclinknodeinspector-convert-link": "Convert to simple link",
|
||||||
|
"visualeditor-magiclinknodeinspector-title-isbn": "ISBN link",
|
||||||
|
"visualeditor-magiclinknodeinspector-title-pmid": "PMID link",
|
||||||
|
"visualeditor-magiclinknodeinspector-title-rfc": "RFC link",
|
||||||
"visualeditor-media-input-placeholder": "Search for media",
|
"visualeditor-media-input-placeholder": "Search for media",
|
||||||
"visualeditor-meta-tool": "Options",
|
"visualeditor-meta-tool": "Options",
|
||||||
"visualeditor-mweditmodesource-title": "Switch to source editing?",
|
"visualeditor-mweditmodesource-title": "Switch to source editing?",
|
||||||
|
|
|
@ -58,6 +58,9 @@
|
||||||
"tooltip-ca-ve-edit": "Tooltip of the dedicated VisualEditor \"Edit\" tab.\n{{Identical|Edit this page}}",
|
"tooltip-ca-ve-edit": "Tooltip of the dedicated VisualEditor \"Edit\" tab.\n{{Identical|Edit this page}}",
|
||||||
"visualeditor-advancedsettings-tool": "Tool for opening the advanced settings section of the meta dialog.\n{{Identical|Advanced settings}}",
|
"visualeditor-advancedsettings-tool": "Tool for opening the advanced settings section of the meta dialog.\n{{Identical|Advanced settings}}",
|
||||||
"visualeditor-annotationbutton-linknode-tooltip": "Tooltip text for link button for auto-numbered, labelless, external links.\n\nSee also:\n* {{msg-mw|Visualeditor-linknodeinspector-title}}\n{{Related|Visualeditor-annotationbutton}}",
|
"visualeditor-annotationbutton-linknode-tooltip": "Tooltip text for link button for auto-numbered, labelless, external links.\n\nSee also:\n* {{msg-mw|Visualeditor-linknodeinspector-title}}\n{{Related|Visualeditor-annotationbutton}}",
|
||||||
|
"visualeditor-annotationbutton-magiclinknode-tooltip-isbn": "Tooltip text for link button for ISBN magic links.\n\nSee also:\n* {{msg-mw|Visualeditor-magiclinknodeinspector-title-isbn}}\n{{Related|Visualeditor-annotationbutton}}",
|
||||||
|
"visualeditor-annotationbutton-magiclinknode-tooltip-pmid": "Tooltip text for link button for PMID magic links.\n\nSee also:\n* {{msg-mw|Visualeditor-magiclinknodeinspector-title-pmid}}\n{{Related|Visualeditor-annotationbutton}}",
|
||||||
|
"visualeditor-annotationbutton-magiclinknode-tooltip-rfc": "Tooltip text for link button for RFC magic links.\n\nSee also:\n* {{msg-mw|Visualeditor-magiclinknodeinspector-title-rfc}}\n{{Related|Visualeditor-annotationbutton}}",
|
||||||
"visualeditor-backbutton-tooltip": "Tooltip text for back button taking user to reading mode and closing the editor.\n{{Identical|Go back}}",
|
"visualeditor-backbutton-tooltip": "Tooltip text for back button taking user to reading mode and closing the editor.\n{{Identical|Go back}}",
|
||||||
"visualeditor-beta-appendix": "Used in {{msg-mw|Guidedtour-tour-firsteditve-edit-page-description}}.\n{{Identical|Beta}}",
|
"visualeditor-beta-appendix": "Used in {{msg-mw|Guidedtour-tour-firsteditve-edit-page-description}}.\n{{Identical|Beta}}",
|
||||||
"visualeditor-beta-label": "Text of tool in the toolbar that highlights that VisualEditor is still in beta.\n{{Identical|Beta}}",
|
"visualeditor-beta-label": "Text of tool in the toolbar that highlights that VisualEditor is still in beta.\n{{Identical|Beta}}",
|
||||||
|
@ -254,6 +257,9 @@
|
||||||
"visualeditor-languages-tool": "Tool for opening the languages links section of the meta dialog.\n{{Identical|Language}}",
|
"visualeditor-languages-tool": "Tool for opening the languages links section of the meta dialog.\n{{Identical|Language}}",
|
||||||
"visualeditor-linkinspector-button-link-external": "Button label for entering an external link.\n{{Identical|External link}}",
|
"visualeditor-linkinspector-button-link-external": "Button label for entering an external link.\n{{Identical|External link}}",
|
||||||
"visualeditor-linkinspector-button-link-internal": "Button label for entering an internal link.",
|
"visualeditor-linkinspector-button-link-internal": "Button label for entering an internal link.",
|
||||||
|
"visualeditor-linkinspector-convert-link-isbn": "Button label for converting a simple link to a ISBN magic link.",
|
||||||
|
"visualeditor-linkinspector-convert-link-pmid": "Button label for converting a simple link to a PMID magic link.",
|
||||||
|
"visualeditor-linkinspector-convert-link-rfc": "Button label for converting a simple link to a RFC magic link.",
|
||||||
"visualeditor-linkinspector-illegal-title": "Warning that the entered text is not a valid page title.",
|
"visualeditor-linkinspector-illegal-title": "Warning that the entered text is not a valid page title.",
|
||||||
"visualeditor-linknodeinspector-add-label": "Label of button that converts an auto-numbered, external, labelless link into a labeled external link",
|
"visualeditor-linknodeinspector-add-label": "Label of button that converts an auto-numbered, external, labelless link into a labeled external link",
|
||||||
"visualeditor-linknodeinspector-title": "Title of inspector for editing auto-numbered, external, labelless links.\n\nSee also:\n* {{msg-mw|Visualeditor-annotationbutton-linknode-tooltip}}",
|
"visualeditor-linknodeinspector-title": "Title of inspector for editing auto-numbered, external, labelless links.\n\nSee also:\n* {{msg-mw|Visualeditor-annotationbutton-linknode-tooltip}}",
|
||||||
|
@ -262,6 +268,10 @@
|
||||||
"visualeditor-loadwarning": "Text (JavaScript confirm()) shown when the editor fails to load properly.\n\nParameters:\n* $1 - the error message from the server, in English. e.g. \"parsoidserver-http-err\"",
|
"visualeditor-loadwarning": "Text (JavaScript confirm()) shown when the editor fails to load properly.\n\nParameters:\n* $1 - the error message from the server, in English. e.g. \"parsoidserver-http-err\"",
|
||||||
"visualeditor-loadwarning-token": "Text (JavaScript confirm()) shown when the editor fails to load properly.\n\nParameters:\n* $1 - the error message from the server.",
|
"visualeditor-loadwarning-token": "Text (JavaScript confirm()) shown when the editor fails to load properly.\n\nParameters:\n* $1 - the error message from the server.",
|
||||||
"visualeditor-mainnamespacepagelink": "Name of a page describing the main namespace (NS0) in this project.\n{{doc-important|Do not translate \"Project\"; it is automatically converted to the wiki's project namespace.}}",
|
"visualeditor-mainnamespacepagelink": "Name of a page describing the main namespace (NS0) in this project.\n{{doc-important|Do not translate \"Project\"; it is automatically converted to the wiki's project namespace.}}",
|
||||||
|
"visualeditor-magiclinknodeinspector-convert-link": "Label of button that converts a magic link into a normal labeled link",
|
||||||
|
"visualeditor-magiclinknodeinspector-title-isbn": "Title of inspector for editing ISBN magic links.\n\nSee also:\n* {{msg-mw|Visualeditor-annotationbutton-magiclinknode-tooltip-isbn}}",
|
||||||
|
"visualeditor-magiclinknodeinspector-title-pmid": "Title of inspector for editing PMID magic links.\n\nSee also:\n* {{msg-mw|Visualeditor-annotationbutton-magiclinknode-tooltip-pmid}}",
|
||||||
|
"visualeditor-magiclinknodeinspector-title-rfc": "Title of inspector for editing RFC magic links.\n\nSee also:\n* {{msg-mw|Visualeditor-annotationbutton-magiclinknode-tooltip-rfc}}",
|
||||||
"visualeditor-media-input-placeholder": "Place holder text for media search input",
|
"visualeditor-media-input-placeholder": "Place holder text for media search input",
|
||||||
"visualeditor-meta-tool": "Text of tool in the toolbar the lets users set categories, language links and other page settings.\n{{Identical|Options}}",
|
"visualeditor-meta-tool": "Text of tool in the toolbar the lets users set categories, language links and other page settings.\n{{Identical|Options}}",
|
||||||
"visualeditor-mweditmodesource-title": "Title of dialog to confirm switching to source mode.",
|
"visualeditor-mweditmodesource-title": "Title of dialog to confirm switching to source mode.",
|
||||||
|
|
|
@ -114,7 +114,8 @@ QUnit.test( 'MW autolink', function ( assert ) {
|
||||||
expectedRange: new ve.Range( 21, 21 ),
|
expectedRange: new ve.Range( 21, 21 ),
|
||||||
expectedData: function ( data, makeAnnotation ) {
|
expectedData: function ( data, makeAnnotation ) {
|
||||||
var i,
|
var i,
|
||||||
a = makeAnnotation( './Special:BookSources/9780596517748' );
|
conf = mw.config.get( 'wgVisualEditorConfig' ),
|
||||||
|
a = makeAnnotation( './' + conf.specialBooksources + '/9780596517748' );
|
||||||
for ( i = 1; i < 20; i++ ) {
|
for ( i = 1; i < 20; i++ ) {
|
||||||
data[ i ] = [ data[ i ], [ a ] ];
|
data[ i ] = [ data[ i ], [ a ] ];
|
||||||
}
|
}
|
||||||
|
|
|
@ -166,22 +166,19 @@ QUnit.test( 'convert', function ( assert ) {
|
||||||
pasteString: 'RFC 1234',
|
pasteString: 'RFC 1234',
|
||||||
pasteType: 'text/plain',
|
pasteType: 'text/plain',
|
||||||
parsoidResponse: '<body data-parsoid=\'{"dsr":[0,8,0,0]}\' lang="en" class="mw-content-ltr sitedir-ltr ltr mw-body mw-body-content mediawiki" dir="ltr"><p data-parsoid=\'{"dsr":[0,8,0,0]}\'><a href="//tools.ietf.org/html/rfc1234" rel="mw:ExtLink" data-parsoid=\'{"stx":"magiclink","dsr":[0,8,0,0]}\'>RFC 1234</a></p></body>',
|
parsoidResponse: '<body data-parsoid=\'{"dsr":[0,8,0,0]}\' lang="en" class="mw-content-ltr sitedir-ltr ltr mw-body mw-body-content mediawiki" dir="ltr"><p data-parsoid=\'{"dsr":[0,8,0,0]}\'><a href="//tools.ietf.org/html/rfc1234" rel="mw:ExtLink" data-parsoid=\'{"stx":"magiclink","dsr":[0,8,0,0]}\'>RFC 1234</a></p></body>',
|
||||||
annotations: [ {
|
annotations: [],
|
||||||
type: 'link/mwExternal',
|
|
||||||
attributes: {
|
|
||||||
href: '//tools.ietf.org/html/rfc1234',
|
|
||||||
rel: 'mw:ExtLink'
|
|
||||||
}
|
|
||||||
} ],
|
|
||||||
expectedData: [
|
expectedData: [
|
||||||
[ 'R', [ 0 ] ],
|
{
|
||||||
[ 'F', [ 0 ] ],
|
type: 'link/mwMagic',
|
||||||
[ 'C', [ 0 ] ],
|
attributes: {
|
||||||
[ ' ', [ 0 ] ],
|
content: 'RFC 1234',
|
||||||
[ '1', [ 0 ] ],
|
origText: 'RFC 1234',
|
||||||
[ '2', [ 0 ] ],
|
origHtml: 'RFC 1234'
|
||||||
[ '3', [ 0 ] ],
|
}
|
||||||
[ '4', [ 0 ] ],
|
},
|
||||||
|
{
|
||||||
|
type: '/link/mwMagic'
|
||||||
|
},
|
||||||
{ type: 'internalList' },
|
{ type: 'internalList' },
|
||||||
{ type: '/internalList' }
|
{ type: '/internalList' }
|
||||||
]
|
]
|
||||||
|
@ -191,23 +188,19 @@ QUnit.test( 'convert', function ( assert ) {
|
||||||
pasteString: 'PMID 1234',
|
pasteString: 'PMID 1234',
|
||||||
pasteType: 'text/plain',
|
pasteType: 'text/plain',
|
||||||
parsoidResponse: '<body data-parsoid=\'{"dsr":[0,9,0,0]}\' lang="en" class="mw-content-ltr sitedir-ltr ltr mw-body mw-body-content mediawiki" dir="ltr"><p data-parsoid=\'{"dsr":[0,9,0,0]}\'><a href="//www.ncbi.nlm.nih.gov/pubmed/1234?dopt=Abstract" rel="mw:ExtLink" data-parsoid=\'{"stx":"magiclink","dsr":[0,9,0,0]}\'>PMID 1234</a></p></body>',
|
parsoidResponse: '<body data-parsoid=\'{"dsr":[0,9,0,0]}\' lang="en" class="mw-content-ltr sitedir-ltr ltr mw-body mw-body-content mediawiki" dir="ltr"><p data-parsoid=\'{"dsr":[0,9,0,0]}\'><a href="//www.ncbi.nlm.nih.gov/pubmed/1234?dopt=Abstract" rel="mw:ExtLink" data-parsoid=\'{"stx":"magiclink","dsr":[0,9,0,0]}\'>PMID 1234</a></p></body>',
|
||||||
annotations: [ {
|
annotations: [],
|
||||||
type: 'link/mwExternal',
|
|
||||||
attributes: {
|
|
||||||
href: '//www.ncbi.nlm.nih.gov/pubmed/1234?dopt=Abstract',
|
|
||||||
rel: 'mw:ExtLink'
|
|
||||||
}
|
|
||||||
} ],
|
|
||||||
expectedData: [
|
expectedData: [
|
||||||
[ 'P', [ 0 ] ],
|
{
|
||||||
[ 'M', [ 0 ] ],
|
type: 'link/mwMagic',
|
||||||
[ 'I', [ 0 ] ],
|
attributes: {
|
||||||
[ 'D', [ 0 ] ],
|
content: 'PMID 1234',
|
||||||
[ ' ', [ 0 ] ],
|
origText: 'PMID 1234',
|
||||||
[ '1', [ 0 ] ],
|
origHtml: 'PMID 1234'
|
||||||
[ '2', [ 0 ] ],
|
}
|
||||||
[ '3', [ 0 ] ],
|
},
|
||||||
[ '4', [ 0 ] ],
|
{
|
||||||
|
type: '/link/mwMagic'
|
||||||
|
},
|
||||||
{ type: 'internalList' },
|
{ type: 'internalList' },
|
||||||
{ type: '/internalList' }
|
{ type: '/internalList' }
|
||||||
]
|
]
|
||||||
|
@ -217,29 +210,19 @@ QUnit.test( 'convert', function ( assert ) {
|
||||||
pasteString: 'ISBN 123456789X',
|
pasteString: 'ISBN 123456789X',
|
||||||
pasteType: 'text/plain',
|
pasteType: 'text/plain',
|
||||||
parsoidResponse: '<body data-parsoid=\'{"dsr":[0,15,0,0]}\' lang="en" class="mw-content-ltr sitedir-ltr ltr mw-body mw-body-content mediawiki" dir="ltr"><p data-parsoid=\'{"dsr":[0,15,0,0]}\'><a href="./Special:BookSources/123456789X" rel="mw:ExtLink" data-parsoid=\'{"stx":"magiclink","dsr":[0,15,0,0]}\'>ISBN 123456789X</a></p></body>',
|
parsoidResponse: '<body data-parsoid=\'{"dsr":[0,15,0,0]}\' lang="en" class="mw-content-ltr sitedir-ltr ltr mw-body mw-body-content mediawiki" dir="ltr"><p data-parsoid=\'{"dsr":[0,15,0,0]}\'><a href="./Special:BookSources/123456789X" rel="mw:ExtLink" data-parsoid=\'{"stx":"magiclink","dsr":[0,15,0,0]}\'>ISBN 123456789X</a></p></body>',
|
||||||
annotations: [ {
|
annotations: [],
|
||||||
type: 'link/mwExternal',
|
|
||||||
attributes: {
|
|
||||||
href: './Special:BookSources/123456789X',
|
|
||||||
rel: 'mw:ExtLink'
|
|
||||||
}
|
|
||||||
} ],
|
|
||||||
expectedData: [
|
expectedData: [
|
||||||
[ 'I', [ 0 ] ],
|
{
|
||||||
[ 'S', [ 0 ] ],
|
type: 'link/mwMagic',
|
||||||
[ 'B', [ 0 ] ],
|
attributes: {
|
||||||
[ 'N', [ 0 ] ],
|
content: 'ISBN 123456789X',
|
||||||
[ ' ', [ 0 ] ],
|
origText: 'ISBN 123456789X',
|
||||||
[ '1', [ 0 ] ],
|
origHtml: 'ISBN 123456789X'
|
||||||
[ '2', [ 0 ] ],
|
}
|
||||||
[ '3', [ 0 ] ],
|
},
|
||||||
[ '4', [ 0 ] ],
|
{
|
||||||
[ '5', [ 0 ] ],
|
type: '/link/mwMagic'
|
||||||
[ '6', [ 0 ] ],
|
},
|
||||||
[ '7', [ 0 ] ],
|
|
||||||
[ '8', [ 0 ] ],
|
|
||||||
[ '9', [ 0 ] ],
|
|
||||||
[ 'X', [ 0 ] ],
|
|
||||||
{ type: 'internalList' },
|
{ type: 'internalList' },
|
||||||
{ type: '/internalList' }
|
{ type: '/internalList' }
|
||||||
]
|
]
|
||||||
|
|
|
@ -62,33 +62,27 @@ ve.ui.MWLinkAction.prototype.getTrailingPunctuation = function ( candidate ) {
|
||||||
* @return {ve.dm.MWExternalLinkAnnotation} The annotation to use.
|
* @return {ve.dm.MWExternalLinkAnnotation} The annotation to use.
|
||||||
*/
|
*/
|
||||||
ve.ui.MWLinkAction.prototype.getLinkAnnotation = function ( linktext ) {
|
ve.ui.MWLinkAction.prototype.getLinkAnnotation = function ( linktext ) {
|
||||||
var title, targetData, m,
|
var title, targetData,
|
||||||
href = linktext;
|
href = linktext;
|
||||||
// The link has been validated in #autolinkMagicLink and/or
|
|
||||||
// #autolinkUrl, so we can use a quick and dirty regexp here to pull
|
// Is this a "magic link"?
|
||||||
// apart the magic link.
|
if ( ve.dm.MWMagicLinkNode.static.validateContent( linktext ) ) {
|
||||||
m = /^(RFC|PMID|ISBN)\s+(\S.*)$/.exec( linktext );
|
return ve.dm.MWMagicLinkNode.static.annotationFromContent( 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(
|
|
||||||
href,
|
|
||||||
this.surface.getModel().getDocument().getHtmlDocument()
|
|
||||||
);
|
|
||||||
if ( targetData.isInternal ) {
|
|
||||||
title = mw.Title.newFromText( targetData.title );
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return title ?
|
// Is this an internal link?
|
||||||
ve.dm.MWInternalLinkAnnotation.static.newFromTitle( title ) :
|
targetData = ve.dm.MWInternalLinkAnnotation.static.getTargetDataFromHref(
|
||||||
new ve.dm.MWExternalLinkAnnotation( {
|
href,
|
||||||
type: 'link/mwExternal',
|
this.surface.getModel().getDocument().getHtmlDocument()
|
||||||
attributes: { href: href }
|
);
|
||||||
} );
|
if ( targetData.isInternal ) {
|
||||||
|
title = mw.Title.newFromText( targetData.title );
|
||||||
|
return ve.dm.MWInternalLinkAnnotation.static.newFromTitle( title );
|
||||||
|
}
|
||||||
|
// It's an external link.
|
||||||
|
return new ve.dm.MWExternalLinkAnnotation( {
|
||||||
|
type: 'link/mwExternal',
|
||||||
|
attributes: { href: href }
|
||||||
|
} );
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -103,13 +97,7 @@ ve.ui.MWLinkAction.prototype.getLinkAnnotation = function ( linktext ) {
|
||||||
*/
|
*/
|
||||||
ve.ui.MWLinkAction.prototype.autolinkMagicLink = function () {
|
ve.ui.MWLinkAction.prototype.autolinkMagicLink = function () {
|
||||||
return this.autolink( function ( linktext ) {
|
return this.autolink( function ( linktext ) {
|
||||||
if ( /^(RFC|PMID) [0-9]+$/.test( linktext ) ) {
|
return ve.dm.MWMagicLinkNode.static.validateContent( linktext );
|
||||||
return true; // Valid RFC/PMID
|
|
||||||
}
|
|
||||||
if ( /^ISBN (97[89][- ]?)?([0-9][- ]?){9}[0-9Xx]$/.test( linktext ) ) {
|
|
||||||
return true; // Valid ISBN
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
} );
|
} );
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -121,10 +109,13 @@ ve.ui.MWLinkAction.prototype.autolinkMagicLink = function () {
|
||||||
*/
|
*/
|
||||||
ve.ui.MWLinkAction.prototype.open = function () {
|
ve.ui.MWLinkAction.prototype.open = function () {
|
||||||
var fragment = this.surface.getModel().getFragment(),
|
var fragment = this.surface.getModel().getFragment(),
|
||||||
|
selectedNode = fragment.getSelectedNode(),
|
||||||
windowName = 'link';
|
windowName = 'link';
|
||||||
|
|
||||||
if ( fragment.getSelectedNode() instanceof ve.dm.MWNumberedExternalLinkNode ) {
|
if ( selectedNode instanceof ve.dm.MWNumberedExternalLinkNode ) {
|
||||||
windowName = 'linkNode';
|
windowName = 'linkNode';
|
||||||
|
} else if ( selectedNode instanceof ve.dm.MWMagicLinkNode ) {
|
||||||
|
windowName = 'linkMagicNode';
|
||||||
}
|
}
|
||||||
this.surface.execute( 'window', 'open', windowName );
|
this.surface.execute( 'window', 'open', windowName );
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -0,0 +1,55 @@
|
||||||
|
/*!
|
||||||
|
* VisualEditor MWMagicLinkNodeContextItem class.
|
||||||
|
*
|
||||||
|
* @copyright 2011-2015 VisualEditor Team and others; see http://ve.mit-license.org
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Context item for a MWMagicLinkNode.
|
||||||
|
*
|
||||||
|
* @class
|
||||||
|
* @extends ve.ui.LinkContextItem
|
||||||
|
*
|
||||||
|
* @constructor
|
||||||
|
* @param {ve.ui.Context} context Context item is in
|
||||||
|
* @param {ve.dm.MWMagicLinkNode} model Model item is related to
|
||||||
|
* @param {Object} config Configuration options
|
||||||
|
*/
|
||||||
|
ve.ui.MWMagicLinkNodeContextItem = function VeUiMWMagicLinkNodeContextItem() {
|
||||||
|
// Parent constructor
|
||||||
|
ve.ui.MWMagicLinkNodeContextItem.super.apply( this, arguments );
|
||||||
|
|
||||||
|
// Initialization
|
||||||
|
this.$element.addClass( 've-ui-mwMagicLinkNodeContextItem' );
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Inheritance */
|
||||||
|
|
||||||
|
OO.inheritClass( ve.ui.MWMagicLinkNodeContextItem, ve.ui.LinkContextItem );
|
||||||
|
|
||||||
|
/* Static Properties */
|
||||||
|
|
||||||
|
ve.ui.MWMagicLinkNodeContextItem.static.name = 'link/mwMagic';
|
||||||
|
|
||||||
|
ve.ui.MWMagicLinkNodeContextItem.static.label = null; // see #setup()
|
||||||
|
|
||||||
|
ve.ui.MWMagicLinkNodeContextItem.static.modelClasses = [ ve.dm.MWMagicLinkNode ];
|
||||||
|
|
||||||
|
/* Methods */
|
||||||
|
|
||||||
|
ve.ui.MWMagicLinkNodeContextItem.prototype.setup = function () {
|
||||||
|
// Set up label
|
||||||
|
var msg = 'visualeditor-magiclinknodeinspector-title-' +
|
||||||
|
this.model.getMagicType().toLowerCase();
|
||||||
|
this.setLabel( OO.ui.deferMsg( msg ) );
|
||||||
|
// Invoke superclass method.
|
||||||
|
return ve.ui.MWMagicLinkNodeContextItem.super.prototype.setup.call( this );
|
||||||
|
};
|
||||||
|
|
||||||
|
ve.ui.MWMagicLinkNodeContextItem.prototype.getDescription = function () {
|
||||||
|
return this.model.getAttribute( 'content' );
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Registration */
|
||||||
|
|
||||||
|
ve.ui.contextItemFactory.register( ve.ui.MWMagicLinkNodeContextItem );
|
|
@ -59,7 +59,7 @@ ve.ui.MWWikitextStringTransferHandler.static.matchFunction = function ( item ) {
|
||||||
|
|
||||||
// Detect autolink opportunities for magic words.
|
// Detect autolink opportunities for magic words.
|
||||||
// (The link should be the only contents of paste to match this heuristic)
|
// (The link should be the only contents of paste to match this heuristic)
|
||||||
if ( /^\s*(RFC|ISBN|PMID)[-\s0-9]+[Xx]?\s*$/.test( text ) ) {
|
if ( ve.dm.MWMagicLinkNode.static.validateContent( text.trim() ) ) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -32,6 +32,14 @@ ve.ui.MWLinkAnnotationInspector.static.modelClasses = [
|
||||||
ve.dm.MWInternalLinkAnnotation
|
ve.dm.MWInternalLinkAnnotation
|
||||||
];
|
];
|
||||||
|
|
||||||
|
ve.ui.MWLinkAnnotationInspector.static.actions = ve.ui.MWLinkAnnotationInspector.static.actions.concat( [
|
||||||
|
{
|
||||||
|
action: 'convert',
|
||||||
|
label: null, // see #updateActions
|
||||||
|
modes: [ 'edit', 'insert' ]
|
||||||
|
}
|
||||||
|
] );
|
||||||
|
|
||||||
/* Methods */
|
/* Methods */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -129,6 +137,38 @@ ve.ui.MWLinkAnnotationInspector.prototype.onExternalLinkChange = function () {
|
||||||
this.updateActions();
|
this.updateActions();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritdoc
|
||||||
|
*/
|
||||||
|
ve.ui.MWLinkAnnotationInspector.prototype.updateActions = function () {
|
||||||
|
var content, annotation, href, type,
|
||||||
|
msg = null;
|
||||||
|
|
||||||
|
ve.ui.MWLinkAnnotationInspector.super.prototype.updateActions.call( this );
|
||||||
|
|
||||||
|
// show/hide convert action
|
||||||
|
content = this.fragment ? this.fragment.getText() : '';
|
||||||
|
annotation = this.annotationInput.getAnnotation();
|
||||||
|
href = annotation && annotation.getHref();
|
||||||
|
if ( href && ve.dm.MWMagicLinkNode.static.validateHref( content, href ) ) {
|
||||||
|
type = ve.dm.MWMagicLinkType.static.fromContent( content ).type;
|
||||||
|
msg = 'visualeditor-linkinspector-convert-link-' + type.toLowerCase();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Once we toggle the visibility of the ActionWidget, we can't filter
|
||||||
|
// it with `get` any more. So we have to use `forEach`:
|
||||||
|
this.actions.forEach( null, function ( action ) {
|
||||||
|
if ( action.getAction() === 'convert' ) {
|
||||||
|
if ( msg ) {
|
||||||
|
action.setLabel( OO.ui.deferMsg( msg ) );
|
||||||
|
action.toggle( true );
|
||||||
|
} else {
|
||||||
|
action.toggle( false );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} );
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @inheritdoc
|
* @inheritdoc
|
||||||
*/
|
*/
|
||||||
|
@ -159,12 +199,47 @@ ve.ui.MWLinkAnnotationInspector.prototype.getReadyProcess = function ( data ) {
|
||||||
}, this );
|
}, this );
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritdoc
|
||||||
|
*/
|
||||||
|
ve.ui.MWLinkAnnotationInspector.prototype.getActionProcess = function ( action ) {
|
||||||
|
if ( action === 'convert' ) {
|
||||||
|
return new OO.ui.Process( function () {
|
||||||
|
this.close( { action: 'done', convert: true } );
|
||||||
|
}, this );
|
||||||
|
}
|
||||||
|
return ve.ui.MWLinkAnnotationInspector.super.prototype.getActionProcess.call( this, action );
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @inheritdoc
|
* @inheritdoc
|
||||||
*/
|
*/
|
||||||
ve.ui.MWLinkAnnotationInspector.prototype.getTeardownProcess = function ( data ) {
|
ve.ui.MWLinkAnnotationInspector.prototype.getTeardownProcess = function ( data ) {
|
||||||
|
var fragment;
|
||||||
return ve.ui.MWLinkAnnotationInspector.super.prototype.getTeardownProcess.call( this, data )
|
return ve.ui.MWLinkAnnotationInspector.super.prototype.getTeardownProcess.call( this, data )
|
||||||
|
.first( function () {
|
||||||
|
// Save the original fragment for later.
|
||||||
|
fragment = this.getFragment();
|
||||||
|
}, this )
|
||||||
.next( function () {
|
.next( function () {
|
||||||
|
var selection = fragment && fragment.getSelection();
|
||||||
|
|
||||||
|
// Handle conversion to magic link.
|
||||||
|
if ( data.convert && selection instanceof ve.dm.LinearSelection ) {
|
||||||
|
fragment.insertContent( [
|
||||||
|
{
|
||||||
|
type: 'link/mwMagic',
|
||||||
|
attributes: {
|
||||||
|
content: fragment.getText()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: '/link/mwMagic'
|
||||||
|
}
|
||||||
|
], true );
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clear dialog state.
|
||||||
this.allowProtocolInInternal = false;
|
this.allowProtocolInInternal = false;
|
||||||
// Make sure both inputs are cleared
|
// Make sure both inputs are cleared
|
||||||
this.internalAnnotationInput.setAnnotation( null );
|
this.internalAnnotationInput.setAnnotation( null );
|
||||||
|
|
182
modules/ve-mw/ui/inspectors/ve.ui.MWMagicLinkNodeInspector.js
Normal file
182
modules/ve-mw/ui/inspectors/ve.ui.MWMagicLinkNodeInspector.js
Normal file
|
@ -0,0 +1,182 @@
|
||||||
|
/*!
|
||||||
|
* VisualEditor UserInterface MWMagicLinkNodeInspector class.
|
||||||
|
*
|
||||||
|
* @copyright 2011-2015 VisualEditor Team and others; see AUTHORS.txt
|
||||||
|
* @license The MIT License (MIT); see LICENSE.txt
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Inspector for editing MediaWiki magic links (RFC/ISBN/PMID).
|
||||||
|
*
|
||||||
|
* @class
|
||||||
|
* @extends ve.ui.NodeInspector
|
||||||
|
*
|
||||||
|
* @constructor
|
||||||
|
* @param {Object} [config] Configuration options
|
||||||
|
*/
|
||||||
|
ve.ui.MWMagicLinkNodeInspector = function VeUiMWMagicLinkNodeInspector( config ) {
|
||||||
|
// Parent constructor
|
||||||
|
ve.ui.NodeInspector.call( this, config );
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Inheritance */
|
||||||
|
|
||||||
|
OO.inheritClass( ve.ui.MWMagicLinkNodeInspector, ve.ui.NodeInspector );
|
||||||
|
|
||||||
|
/* Static properties */
|
||||||
|
|
||||||
|
ve.ui.MWMagicLinkNodeInspector.static.name = 'linkMagicNode';
|
||||||
|
|
||||||
|
ve.ui.MWMagicLinkNodeInspector.static.icon = 'link';
|
||||||
|
|
||||||
|
ve.ui.MWMagicLinkNodeInspector.static.title = null; // see #getSetupProcess
|
||||||
|
|
||||||
|
ve.ui.MWMagicLinkNodeInspector.static.modelClasses = [ ve.dm.MWMagicLinkNode ];
|
||||||
|
|
||||||
|
ve.ui.MWMagicLinkNodeInspector.static.actions = ve.ui.MWMagicLinkNodeInspector.super.static.actions.concat( [
|
||||||
|
{
|
||||||
|
action: 'convert',
|
||||||
|
label: OO.ui.deferMsg( 'visualeditor-magiclinknodeinspector-convert-link' ),
|
||||||
|
modes: [ 'edit' ]
|
||||||
|
}
|
||||||
|
] );
|
||||||
|
|
||||||
|
/* Methods */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritdoc
|
||||||
|
*/
|
||||||
|
ve.ui.MWMagicLinkNodeInspector.prototype.initialize = function () {
|
||||||
|
// Parent method
|
||||||
|
ve.ui.MWMagicLinkNodeInspector.super.prototype.initialize.call( this );
|
||||||
|
|
||||||
|
// Properties
|
||||||
|
this.targetInput = new OO.ui.TextInputWidget( {
|
||||||
|
validate: this.validate.bind( this )
|
||||||
|
} );
|
||||||
|
this.targetInput.on( 'change', this.onChange.bind( this ) );
|
||||||
|
|
||||||
|
// Initialization
|
||||||
|
this.form.$element.append( this.targetInput.$element );
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return true if the given string is a valid magic link of the
|
||||||
|
* appropriate type.
|
||||||
|
*
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
ve.ui.MWMagicLinkNodeInspector.prototype.validate = function ( str ) {
|
||||||
|
var node = this.getFragment().getSelectedNode();
|
||||||
|
return node.constructor.static.validateContent( str, node.getMagicType() );
|
||||||
|
};
|
||||||
|
|
||||||
|
ve.ui.MWMagicLinkNodeInspector.prototype.onChange = function ( value ) {
|
||||||
|
// Disable the unsafe action buttons if the input isn't valid
|
||||||
|
var isValid = this.validate( value );
|
||||||
|
this.actions.forEach( null, function ( action ) {
|
||||||
|
if ( !action.hasFlag( 'safe' ) ) {
|
||||||
|
action.setDisabled( !isValid );
|
||||||
|
}
|
||||||
|
} );
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritdoc
|
||||||
|
*/
|
||||||
|
ve.ui.MWMagicLinkNodeInspector.prototype.getActionProcess = function ( action ) {
|
||||||
|
if ( ( action === 'done' || action === 'convert' ) &&
|
||||||
|
!this.validate( this.targetInput.getValue() ) ) {
|
||||||
|
// Don't close dialog: input isn't valid.
|
||||||
|
return new OO.ui.Process( 0 );
|
||||||
|
}
|
||||||
|
if ( action === 'convert' ) {
|
||||||
|
return new OO.ui.Process( function () {
|
||||||
|
this.close( { action: action } );
|
||||||
|
}, this );
|
||||||
|
}
|
||||||
|
return ve.ui.MWMagicLinkNodeInspector.super.prototype.getActionProcess.call( this, action );
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritdoc
|
||||||
|
*/
|
||||||
|
ve.ui.MWMagicLinkNodeInspector.prototype.getSetupProcess = function ( data ) {
|
||||||
|
// Set the title based on the node type
|
||||||
|
var fragment = data.fragment,
|
||||||
|
node = fragment instanceof ve.dm.SurfaceFragment ?
|
||||||
|
fragment.getSelectedNode() : null,
|
||||||
|
type = node instanceof ve.dm.MWMagicLinkNode ?
|
||||||
|
node.getMagicType() : null,
|
||||||
|
msg = type ?
|
||||||
|
'visualeditor-magiclinknodeinspector-title-' + type.toLowerCase() :
|
||||||
|
null;
|
||||||
|
|
||||||
|
data = $.extend( {
|
||||||
|
title: msg ? OO.ui.deferMsg( msg ) : null
|
||||||
|
}, data );
|
||||||
|
return ve.ui.MWMagicLinkNodeInspector.super.prototype.getSetupProcess.call( this, data )
|
||||||
|
.next( function () {
|
||||||
|
// Initialization
|
||||||
|
this.targetInput.setValue(
|
||||||
|
this.selectedNode ? this.selectedNode.getAttribute( 'content' ) : ''
|
||||||
|
);
|
||||||
|
}, this );
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritdoc
|
||||||
|
*/
|
||||||
|
ve.ui.MWMagicLinkNodeInspector.prototype.getReadyProcess = function ( data ) {
|
||||||
|
return ve.ui.MWMagicLinkNodeInspector.super.prototype.getReadyProcess.call( this, data )
|
||||||
|
.next( function () {
|
||||||
|
this.targetInput.focus().select();
|
||||||
|
}, this );
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritdoc
|
||||||
|
*/
|
||||||
|
ve.ui.MWMagicLinkNodeInspector.prototype.getTeardownProcess = function ( data ) {
|
||||||
|
data = data || {};
|
||||||
|
return ve.ui.MWMagicLinkNodeInspector.super.prototype.getTeardownProcess.call( this, data )
|
||||||
|
.first( function () {
|
||||||
|
var content, annotation, annotations,
|
||||||
|
surfaceModel = this.getFragment().getSurface(),
|
||||||
|
doc = surfaceModel.getDocument(),
|
||||||
|
nodeRange = this.selectedNode.getOuterRange(),
|
||||||
|
value = this.targetInput.getValue(),
|
||||||
|
done = data.action === 'done',
|
||||||
|
convert = data.action === 'convert',
|
||||||
|
remove = data.action === 'remove' || ( done && !value );
|
||||||
|
|
||||||
|
if ( remove ) {
|
||||||
|
surfaceModel.change(
|
||||||
|
ve.dm.Transaction.newFromRemoval( doc, nodeRange )
|
||||||
|
);
|
||||||
|
} else if ( convert ) {
|
||||||
|
annotation = ve.dm.MWMagicLinkNode.static.annotationFromContent(
|
||||||
|
value
|
||||||
|
);
|
||||||
|
if ( annotation ) {
|
||||||
|
annotations = doc.data.getAnnotationsFromOffset( nodeRange.start ).clone();
|
||||||
|
annotations.push( annotation );
|
||||||
|
content = value.split( '' );
|
||||||
|
ve.dm.Document.static.addAnnotationsToData( content, annotations );
|
||||||
|
surfaceModel.change(
|
||||||
|
ve.dm.Transaction.newFromReplacement( doc, nodeRange, content )
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else if ( done && this.validate( value ) ) {
|
||||||
|
surfaceModel.change(
|
||||||
|
ve.dm.Transaction.newFromAttributeChanges(
|
||||||
|
doc, nodeRange.start, { content: value }
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}, this );
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Registration */
|
||||||
|
|
||||||
|
ve.ui.windowFactory.register( ve.ui.MWMagicLinkNodeInspector );
|
|
@ -24,15 +24,27 @@ ve.ui.MWLinkInspectorTool = function VeUiMwLinkInspectorTool() {
|
||||||
|
|
||||||
OO.inheritClass( ve.ui.MWLinkInspectorTool, ve.ui.LinkInspectorTool );
|
OO.inheritClass( ve.ui.MWLinkInspectorTool, ve.ui.LinkInspectorTool );
|
||||||
|
|
||||||
// FIXME should eventually vary title based on link type
|
|
||||||
// Use message visualeditor-annotationbutton-linknode-tooltip
|
|
||||||
|
|
||||||
ve.ui.MWLinkInspectorTool.static.modelClasses =
|
ve.ui.MWLinkInspectorTool.static.modelClasses =
|
||||||
ve.ui.MWLinkInspectorTool.super.static.modelClasses.concat( [
|
ve.ui.MWLinkInspectorTool.super.static.modelClasses.concat( [
|
||||||
ve.dm.MWNumberedExternalLinkNode
|
ve.dm.MWNumberedExternalLinkNode,
|
||||||
|
ve.dm.MWMagicLinkNode
|
||||||
] );
|
] );
|
||||||
|
|
||||||
ve.ui.MWLinkInspectorTool.static.associatedWindows = [ 'link', 'linkNode' ];
|
ve.ui.MWLinkInspectorTool.static.associatedWindows = [ 'link', 'linkNode', 'linkMagicNode' ];
|
||||||
|
|
||||||
|
ve.ui.MWLinkInspectorTool.prototype.onUpdateState = function ( fragment ) {
|
||||||
|
// Vary title based on link type.
|
||||||
|
var node = fragment instanceof ve.dm.SurfaceFragment ?
|
||||||
|
fragment.getSelectedNode() : null,
|
||||||
|
type = node instanceof ve.dm.MWMagicLinkNode ?
|
||||||
|
'magiclinknode-tooltip-' + node.getMagicType().toLowerCase() :
|
||||||
|
node instanceof ve.dm.MWNumberedExternalLinkNode ?
|
||||||
|
'linknode-tooltip' : null,
|
||||||
|
title = type ?
|
||||||
|
OO.ui.deferMsg( 'visualeditor-annotationbutton-' + type ) :
|
||||||
|
ve.ui.MWLinkInspectorTool.static.title;
|
||||||
|
this.setTitle( title );
|
||||||
|
};
|
||||||
|
|
||||||
ve.ui.toolFactory.register( ve.ui.MWLinkInspectorTool );
|
ve.ui.toolFactory.register( ve.ui.MWLinkInspectorTool );
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue