diff --git a/VisualEditor.php b/VisualEditor.php index 82a55d91cf..f930a5f42b 100644 --- a/VisualEditor.php +++ b/VisualEditor.php @@ -333,6 +333,7 @@ $wgResourceModules += array( 've-mw/dm/nodes/ve.dm.MWInlineImageNode.js', 've-mw/dm/nodes/ve.dm.MWBlockImageNode.js', 've-mw/dm/nodes/ve.dm.MWImageCaptionNode.js', + 've-mw/dm/nodes/ve.dm.MWNumberedExternalLinkNode.js', 've-mw/dm/nodes/ve.dm.MWTransclusionNode.js', 've-mw/dm/nodes/ve.dm.MWReferenceListNode.js', 've-mw/dm/nodes/ve.dm.MWReferenceNode.js', @@ -409,6 +410,7 @@ $wgResourceModules += array( 've-mw/ce/nodes/ve.ce.MWInlineImageNode.js', 've-mw/ce/nodes/ve.ce.MWBlockImageNode.js', 've-mw/ce/nodes/ve.ce.MWImageCaptionNode.js', + 've-mw/ce/nodes/ve.ce.MWNumberedExternalLinkNode.js', 've-mw/ce/nodes/ve.ce.MWTransclusionNode.js', 've-mw/ce/nodes/ve.ce.MWReferenceListNode.js', 've-mw/ce/nodes/ve.ce.MWReferenceNode.js', diff --git a/modules/ve-mw/ce/nodes/ve.ce.MWNumberedExternalLinkNode.js b/modules/ve-mw/ce/nodes/ve.ce.MWNumberedExternalLinkNode.js new file mode 100644 index 0000000000..20cbcd9f01 --- /dev/null +++ b/modules/ve-mw/ce/nodes/ve.ce.MWNumberedExternalLinkNode.js @@ -0,0 +1,69 @@ +/*! + * VisualEditor ContentEditable MWNumberedExternalLinkNode class. + * + * @copyright 2011-2013 VisualEditor Team and others; see AUTHORS.txt + * @license The MIT License (MIT); see LICENSE.txt + */ + +/** + * ContentEditable MediaWiki numbered external link node. + * + * @class + * @extends ve.ce.LeafNode + * @constructor + * @param {ve.dm.MWNumberedExternalLinkNode} model Model to observe + * @param {Object} [config] Configuration options + */ +ve.ce.MWNumberedExternalLinkNode = function VeCeMWNumberedExternalLinkNode( model, config ) { + // Parent constructor + ve.ce.LeafNode.call( this, model, config ); + + // Mixin constructors + ve.ce.ProtectedNode.call( this ); + ve.ce.FocusableNode.call( this ); + + // DOM changes + this.$.addClass( 've-ce-mwNumberedExternalLinkNode' ); + // Need CE=false to prevent selection issues + this.$.prop( 'contentEditable', 'false' ); + + // Add link + this.$link = this.$$( '' ).appendTo( this.$ ); + + // Events + this.model.connect( this, { 'update': 'onUpdate' } ); + + // Initialization + this.onUpdate(); +}; + +/* Inheritance */ + +OO.inheritClass( ve.ce.MWNumberedExternalLinkNode, ve.ce.LeafNode ); + +OO.mixinClass( ve.ce.MWNumberedExternalLinkNode, ve.ce.ProtectedNode ); + +OO.mixinClass( ve.ce.MWNumberedExternalLinkNode, ve.ce.FocusableNode ); + +/* Static Properties */ + +ve.ce.MWNumberedExternalLinkNode.static.name = 'link/mwNumberedExternal'; + +ve.ce.MWNumberedExternalLinkNode.static.tagName = 'span'; + +/* Methods */ + +/** + * Handle model update events. + * + * If the source changed since last update the image's src attribute will be updated accordingly. + * + * @method + */ +ve.ce.MWNumberedExternalLinkNode.prototype.onUpdate = function () { + this.$link.attr( 'href', this.model.getAttribute( 'href' ) ); +}; + +/* Registration */ + +ve.ce.nodeFactory.register( ve.ce.MWNumberedExternalLinkNode ); diff --git a/modules/ve-mw/ce/styles/ve.ce.Node.css b/modules/ve-mw/ce/styles/ve.ce.Node.css index 4c6e8cfaeb..d907fe952c 100644 --- a/modules/ve-mw/ce/styles/ve.ce.Node.css +++ b/modules/ve-mw/ce/styles/ve.ce.Node.css @@ -68,3 +68,14 @@ .ve-ce-mwHieroNode { display: table; } + +/* ve.ce.MWNumberedExternalLinkNode */ + +.ve-ce-surface { + counter-reset: ve-ce-mwNumberedExternalLinkNode; +} + +.ve-ce-mwNumberedExternalLinkNode a:empty:after { + content: "[" counter(ve-ce-mwNumberedExternalLinkNode) "]"; + counter-increment: ve-ce-mwNumberedExternalLinkNode; +} \ No newline at end of file diff --git a/modules/ve-mw/dm/annotations/ve.dm.MWExternalLinkAnnotation.js b/modules/ve-mw/dm/annotations/ve.dm.MWExternalLinkAnnotation.js index 83b146015c..9fe0d979cb 100644 --- a/modules/ve-mw/dm/annotations/ve.dm.MWExternalLinkAnnotation.js +++ b/modules/ve-mw/dm/annotations/ve.dm.MWExternalLinkAnnotation.js @@ -33,10 +33,7 @@ OO.inheritClass( ve.dm.MWExternalLinkAnnotation, ve.dm.LinkAnnotation ); ve.dm.MWExternalLinkAnnotation.static.name = 'link/mwExternal'; -ve.dm.MWExternalLinkAnnotation.static.matchRdfaTypes = [ - 'mw:ExtLink', - 'mw:ExtLink/Numbered' -]; +ve.dm.MWExternalLinkAnnotation.static.matchRdfaTypes = [ 'mw:ExtLink' ]; ve.dm.MWExternalLinkAnnotation.static.toDataElement = function ( domElements ) { var parentResult = ve.dm.LinkAnnotation.static.toDataElement.apply( this, arguments ); diff --git a/modules/ve-mw/dm/nodes/ve.dm.MWNumberedExternalLinkNode.js b/modules/ve-mw/dm/nodes/ve.dm.MWNumberedExternalLinkNode.js new file mode 100644 index 0000000000..cc28582a9d --- /dev/null +++ b/modules/ve-mw/dm/nodes/ve.dm.MWNumberedExternalLinkNode.js @@ -0,0 +1,59 @@ +/*! + * VisualEditor DataModel MWNumberedExternalLinkNode class. + * + * @copyright 2011-2013 VisualEditor Team and others; see AUTHORS.txt + * @license The MIT License (MIT); see LICENSE.txt + */ + +/** + * DataModel MediaWiki numbered external link node. + * + * @class + * @extends ve.dm.LeafNode + * @constructor + * @param {number} [length] Length of content data in document + * @param {Object} [element] Reference to element in linear model + */ +ve.dm.MWNumberedExternalLinkNode = function VeDmMWNumberedExternalLinkNode( length, element ) { + // Parent constructor + ve.dm.LeafNode.call( this, 0, element ); +}; + +/* Inheritance */ + +OO.inheritClass( ve.dm.MWNumberedExternalLinkNode, ve.dm.LeafNode ); + +/* Static Properties */ + +ve.dm.MWNumberedExternalLinkNode.static.name = 'link/mwNumberedExternal'; + +ve.dm.MWNumberedExternalLinkNode.static.isContent = true; + +ve.dm.MWNumberedExternalLinkNode.static.matchTagNames = [ 'a' ]; + +ve.dm.MWNumberedExternalLinkNode.static.matchRdfaTypes = [ 'mw:ExtLink' ]; + +ve.dm.MWNumberedExternalLinkNode.static.matchFunction = function ( element ) { + // Must be empty + return element.childNodes.length === 0; +}; + +ve.dm.MWNumberedExternalLinkNode.static.toDataElement = function ( domElements ) { + return { + 'type': 'link/mwNumberedExternal', + 'attributes': { + 'href': domElements[0].getAttribute( 'href' ) + } + }; +}; + +ve.dm.MWNumberedExternalLinkNode.static.toDomElements = function ( dataElement, doc ) { + var domElement = doc.createElement( 'a' ); + domElement.setAttribute( 'href', dataElement.attributes.href ); + domElement.setAttribute( 'rel', 'mw:ExtLink' ); + return [ domElement ]; +}; + +/* Registration */ + +ve.dm.modelRegistry.register( ve.dm.MWNumberedExternalLinkNode ); diff --git a/modules/ve-mw/test/dm/ve.dm.mwExample.js b/modules/ve-mw/test/dm/ve.dm.mwExample.js index 30d2c1dd00..683141f27b 100644 --- a/modules/ve-mw/test/dm/ve.dm.mwExample.js +++ b/modules/ve-mw/test/dm/ve.dm.mwExample.js @@ -1370,52 +1370,25 @@ ve.dm.mwExample.domToDataCases = { { 'type': '/internalList' } ] }, - 'numbered external link': { - 'html': '

[1]

', + 'numbered external link (empty mw:Extlink)': { + 'html': '

FooBar

', 'data': [ { 'type': 'paragraph' }, - [ - '[', - [ { - 'type': 'link/mwExternal', - 'attributes': { - 'href': 'http://www.mediawiki.org/', - 'rel': 'mw:ExtLink/Numbered' - }, - 'htmlAttributes': [ { 'values': { - 'href': 'http://www.mediawiki.org/', - 'rel': 'mw:ExtLink/Numbered' - } } ] + 'F', 'o', 'o', + { + 'type': 'link/mwNumberedExternal', + 'attributes': { + 'href': 'http://www.example.com' + }, + 'htmlAttributes': [ { + 'values': { + 'href': 'http://www.example.com', + 'rel': 'mw:ExtLink' + } } ] - ], - [ - '1', - [ { - 'type': 'link/mwExternal', - 'attributes': { - 'href': 'http://www.mediawiki.org/', - 'rel': 'mw:ExtLink/Numbered' - }, - 'htmlAttributes': [ { 'values': { - 'href': 'http://www.mediawiki.org/', - 'rel': 'mw:ExtLink/Numbered' - } } ] - } ] - ], - [ - ']', - [ { - 'type': 'link/mwExternal', - 'attributes': { - 'href': 'http://www.mediawiki.org/', - 'rel': 'mw:ExtLink/Numbered' - }, - 'htmlAttributes': [ { 'values': { - 'href': 'http://www.mediawiki.org/', - 'rel': 'mw:ExtLink/Numbered' - } } ] - } ] - ], + }, + { 'type': '/link/mwNumberedExternal' }, + 'B', 'a', 'r', { 'type': '/paragraph' }, { 'type': 'internalList' }, { 'type': '/internalList' }