mirror of
https://gerrit.wikimedia.org/r/mediawiki/extensions/VisualEditor
synced 2024-09-25 11:16:51 +00:00
Image node refactor
ve.ce.ImageNode.js * Moved in generic stuff from MWImageNode * Added drag end handler (empty, will be used soon) ve.ce.MWImageNode.js * Changed to inherit ImageNode * Moved generic stuff out ve.dm.ImageNode.js * Added attribute extraction/preservation for src, width and height ve.dm.MWImageNode.js * Changed to inherit ImageNode * Re-using ImageNode's attribute handling to extract/preserve attributes on both the image and wrapper level Change-Id: Ied4e1ece24e6804220eac35330790f7084df55de
This commit is contained in:
parent
8f3e6f152f
commit
9510440640
|
@ -17,7 +17,16 @@ ve.ce.ImageNode = function VeCeImageNode( model ) {
|
|||
// Parent constructor
|
||||
ve.ce.LeafNode.call( this, model, $( '<img>' ) );
|
||||
|
||||
// DOM Changes
|
||||
// Events
|
||||
this.model.addListenerMethod( this, 'update', 'onUpdate' );
|
||||
this.$.on( {
|
||||
'click': ve.bind( this.onClick, this ),
|
||||
'dragstart': ve.bind( this.onDragStart, this ),
|
||||
'dragend': ve.bind( this.onDragEnd, this )
|
||||
} );
|
||||
|
||||
// Initialization
|
||||
ve.setDomAttributes( this.$[0], this.model.getAttributes(), ['src', 'width', 'height'] );
|
||||
this.$.addClass( 've-ce-imageNode' );
|
||||
};
|
||||
|
||||
|
@ -29,6 +38,51 @@ ve.inheritClass( ve.ce.ImageNode, ve.ce.LeafNode );
|
|||
|
||||
ve.ce.ImageNode.static.name = 'image';
|
||||
|
||||
/* Methods */
|
||||
|
||||
/**
|
||||
* Handle the mouse click.
|
||||
*
|
||||
* @method
|
||||
* @param {jQuery.Event} e Click event
|
||||
*/
|
||||
ve.ce.ImageNode.prototype.onClick = function ( e ) {
|
||||
var range,
|
||||
surfaceModel = this.getRoot().getSurface().getModel(),
|
||||
selection = surfaceModel.getSelection();
|
||||
|
||||
range = new ve.Range(
|
||||
this.model.getOffset(),
|
||||
this.model.getOffset() + this.model.getOuterLength()
|
||||
);
|
||||
|
||||
if ( e.shiftKey ) {
|
||||
range = ve.Range.newCoveringRange( [ selection, range ], selection.from > range.from );
|
||||
}
|
||||
|
||||
this.getRoot().getSurface().getModel().change( null, range );
|
||||
};
|
||||
|
||||
/**
|
||||
* Handle the dragstart.
|
||||
*
|
||||
* @method
|
||||
* @param {jQuery.Event} e Dragstart event
|
||||
*/
|
||||
ve.ce.ImageNode.prototype.onDragStart = function () {
|
||||
return false;
|
||||
};
|
||||
|
||||
/**
|
||||
* Handle the dragend.
|
||||
*
|
||||
* @method
|
||||
* @param {jQuery.Event} e Dragstart event
|
||||
*/
|
||||
ve.ce.ImageNode.prototype.onDragEnd = function () {
|
||||
return false;
|
||||
};
|
||||
|
||||
/* Registration */
|
||||
|
||||
ve.ce.nodeFactory.register( ve.ce.ImageNode );
|
||||
|
|
|
@ -9,38 +9,27 @@
|
|||
* ContentEditable MediaWiki image node.
|
||||
*
|
||||
* @class
|
||||
* @extends ve.ce.LeafNode
|
||||
* @extends ve.ce.ImageNode
|
||||
* @constructor
|
||||
* @param {ve.dm.MWImageNode} model Model to observe
|
||||
*/
|
||||
ve.ce.MWImageNode = function VeCeMWImageNode( model ) {
|
||||
// Parent constructor
|
||||
ve.ce.LeafNode.call( this, model, $( '<a>' ) );
|
||||
|
||||
// DOM Changes
|
||||
this.$.addClass( 've-ce-MWImageNode' );
|
||||
this.$.attr( 'contenteditable', false );
|
||||
this.$img = $( '<img>' ).appendTo( this.$ );
|
||||
this.$img.attr( {
|
||||
'width': this.model.getAttribute( 'width' ),
|
||||
'height': this.model.getAttribute( 'height' ),
|
||||
'src': this.model.getAttribute( 'src' )
|
||||
} );
|
||||
|
||||
// Events
|
||||
this.model.addListenerMethod( this, 'update', 'onUpdate' );
|
||||
this.$.on( {
|
||||
'click': ve.bind( this.onClick, this ),
|
||||
'dragstart': ve.bind( this.onDragstart, this )
|
||||
} );
|
||||
ve.ce.ImageNode.call( this, model );
|
||||
|
||||
// Initialization
|
||||
this.$.addClass( 've-ce-MWImageNode' );
|
||||
this.$image = this.$;
|
||||
this.$ = $( '<' + ( model.getAttribute( 'isLinked' ) ? 'a' : 'span' ) + '>' );
|
||||
|
||||
// Initialization
|
||||
this.$.attr( 'contenteditable', false ).append( this.$image );
|
||||
this.onUpdate();
|
||||
};
|
||||
|
||||
/* Inheritance */
|
||||
|
||||
ve.inheritClass( ve.ce.MWImageNode, ve.ce.LeafNode );
|
||||
ve.inheritClass( ve.ce.MWImageNode, ve.ce.ImageNode );
|
||||
|
||||
/* Static Properties */
|
||||
|
||||
|
@ -52,40 +41,6 @@ ve.ce.MWImageNode.prototype.onUpdate = function () {
|
|||
// ...
|
||||
};
|
||||
|
||||
/**
|
||||
* Handle the mouse click.
|
||||
*
|
||||
* @method
|
||||
* @param {jQuery.Event} e Click event
|
||||
*/
|
||||
ve.ce.MWImageNode.prototype.onClick = function ( e ) {
|
||||
var range,
|
||||
surfaceModel = this.getRoot().getSurface().getModel(),
|
||||
selection = surfaceModel.getSelection();
|
||||
|
||||
range = new ve.Range(
|
||||
this.model.getOffset(),
|
||||
this.model.getOffset() + this.model.getOuterLength()
|
||||
);
|
||||
|
||||
if ( e.shiftKey ) {
|
||||
range = ve.Range.newCoveringRange( [ selection, range ], selection.from > range.from );
|
||||
}
|
||||
|
||||
this.getRoot().getSurface().getModel().change( null, range );
|
||||
};
|
||||
|
||||
/**
|
||||
* Handle the dragstart.
|
||||
*
|
||||
* @method
|
||||
* @param {jQuery.Event} e Dragstart event
|
||||
*/
|
||||
ve.ce.MWImageNode.prototype.onDragstart = function ( e ) {
|
||||
e.preventDefault();
|
||||
return false;
|
||||
};
|
||||
|
||||
/* Registration */
|
||||
|
||||
ve.ce.nodeFactory.register( ve.ce.MWImageNode );
|
||||
|
|
|
@ -31,12 +31,25 @@ ve.dm.ImageNode.static.isContent = true;
|
|||
|
||||
ve.dm.ImageNode.static.matchTagNames = [ 'img' ];
|
||||
|
||||
ve.dm.ImageNode.static.toDataElement = function () {
|
||||
return { 'type': 'image' };
|
||||
ve.dm.ImageNode.static.toDataElement = function ( domElements ) {
|
||||
var $node = $( domElements[0] ),
|
||||
width = $node.attr( 'width' ),
|
||||
height = $node.attr( 'height' );
|
||||
|
||||
return {
|
||||
'type': 'image',
|
||||
'attributes': {
|
||||
'src': $node.attr( 'src' ),
|
||||
'width': width !== undefined && width !== '' ? Number( width ) : null,
|
||||
'height': height !== undefined && height !== '' ? Number( height ) : null
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
ve.dm.ImageNode.static.toDomElements = function ( dataElement, doc ) {
|
||||
return [ doc.createElement( 'img' ) ];
|
||||
var domElement = doc.createElement( 'img' );
|
||||
ve.setDomAttributes( domElement, dataElement.attributes, [ 'src', 'width', 'height' ] );
|
||||
return [ domElement ];
|
||||
};
|
||||
|
||||
/* Registration */
|
||||
|
|
|
@ -9,53 +9,64 @@
|
|||
* DataModel MediaWiki image node.
|
||||
*
|
||||
* @class
|
||||
* @extends ve.dm.LeafNode
|
||||
* @extends ve.dm.ImageNode
|
||||
* @constructor
|
||||
* @param {number} [length] Length of content data in document
|
||||
* @param {Object} [element] Reference to element in linear model
|
||||
*/
|
||||
ve.dm.MWImageNode = function VeDmMWImageNode( length, element ) {
|
||||
ve.dm.LeafNode.call( this, 0, element );
|
||||
ve.dm.ImageNode.call( this, 0, element );
|
||||
};
|
||||
|
||||
/* Inheritance */
|
||||
|
||||
ve.inheritClass( ve.dm.MWImageNode, ve.dm.LeafNode );
|
||||
ve.inheritClass( ve.dm.MWImageNode, ve.dm.ImageNode );
|
||||
|
||||
/* Static Properties */
|
||||
|
||||
ve.dm.MWImageNode.static.name = 'MWimage';
|
||||
|
||||
ve.dm.MWImageNode.static.isContent = true;
|
||||
ve.dm.MWImageNode.static.matchTagNames = null;
|
||||
|
||||
ve.dm.MWImageNode.static.matchRdfaTypes = [ 'mw:Image' ];
|
||||
|
||||
ve.dm.MWImageNode.static.storeHtmlAttributes = false;
|
||||
|
||||
ve.dm.MWImageNode.static.toDataElement = function ( domElements ) {
|
||||
var $node = $( domElements[0].childNodes[0] ),
|
||||
width = $node.attr( 'width' ),
|
||||
height = $node.attr( 'height' ),
|
||||
html = $( '<div>', domElements[0].ownerDocument ).append( $( domElements ).clone() ).html();
|
||||
var i, j, childNode, children = Array.prototype.slice.call( domElements[0].children, 0 ),
|
||||
parentResult = ve.dm.ImageNode.static.toDataElement.apply(
|
||||
this, [ children ].concat( Array.prototype.slice.call( arguments, 1 ) )
|
||||
),
|
||||
dataElement = ve.copyObject( parentResult );
|
||||
|
||||
return {
|
||||
'type': this.name,
|
||||
// Preserve the child nodes' attributes in html/0-i/foo
|
||||
for ( i = 0; i < domElements[0].childNodes.length; i++ ) {
|
||||
childNode = domElements[0].childNodes[i];
|
||||
for ( j = 0; j < childNode.attributes.length; j++ ) {
|
||||
dataElement.attributes['html/0-' + i + '/' + childNode.attributes[j].name] =
|
||||
childNode.attributes[j].value;
|
||||
}
|
||||
}
|
||||
|
||||
return ve.extendObject( true, dataElement, {
|
||||
'type': 'MWimage',
|
||||
'attributes': {
|
||||
'src': $node.attr( 'src' ),
|
||||
'width': width !== '' ? Number( width ) : null,
|
||||
'height': height !== '' ? Number( height ) : null,
|
||||
// TODO: don't store html, just enough attributes to rebuild
|
||||
'html': html
|
||||
},
|
||||
};
|
||||
'isLinked': domElements[0].nodeName.toLowerCase() === 'a'
|
||||
}
|
||||
} );
|
||||
};
|
||||
|
||||
ve.dm.MWImageNode.static.toDomElements = function ( dataElement, doc ) {
|
||||
//TODO: rebuild html from attributes
|
||||
var wrapper = doc.createElement( 'div' );
|
||||
wrapper.innerHTML = dataElement.attributes.html;
|
||||
// Convert wrapper.children to an array
|
||||
return Array.prototype.slice.call( wrapper.childNodes, 0 );
|
||||
var k, wrapper = doc.createElement( dataElement.attributes.isLinked ? 'a' : 'span' ),
|
||||
imageDomElement = ve.dm.ImageNode.static.toDomElements.apply( this, arguments )[0];
|
||||
|
||||
wrapper.appendChild( imageDomElement );
|
||||
// Restore attributes from html/0-0/*
|
||||
for ( k in dataElement.attributes ) {
|
||||
if ( k.indexOf( 'html/0-0/' ) === 0 ) {
|
||||
imageDomElement.setAttribute( k.substr( 9 ), dataElement.attributes[k] );
|
||||
}
|
||||
}
|
||||
|
||||
return [ wrapper ];
|
||||
};
|
||||
|
||||
/* Registration */
|
||||
|
|
|
@ -277,7 +277,12 @@ QUnit.test( 'newFromRemoval', 15, function ( assert ) {
|
|||
'type': 'replace',
|
||||
'remove': [
|
||||
'h',
|
||||
{ 'type': 'image', 'attributes': { 'html/0/src': ve.dm.example.imgSrc } },
|
||||
{ 'type': 'image', 'attributes': {
|
||||
'html/0/src': ve.dm.example.imgSrc,
|
||||
'src': ve.dm.example.imgSrc,
|
||||
'width': null,
|
||||
'height': null
|
||||
} },
|
||||
{ 'type': '/image' },
|
||||
'i'
|
||||
],
|
||||
|
|
|
@ -253,7 +253,12 @@ ve.dm.example.data = [
|
|||
// 38 - Plain "h"
|
||||
'h',
|
||||
// 39 - Beginning of inline image
|
||||
{ 'type': 'image', 'attributes': { 'html/0/src': ve.dm.example.imgSrc } },
|
||||
{ 'type': 'image', 'attributes': {
|
||||
'html/0/src': ve.dm.example.imgSrc,
|
||||
'src': ve.dm.example.imgSrc,
|
||||
'width': null,
|
||||
'height': null
|
||||
} },
|
||||
// 40 - End of inline image
|
||||
{ 'type': '/image' },
|
||||
// 41 - Plain "i"
|
||||
|
@ -736,7 +741,12 @@ ve.dm.example.domToDataCases = {
|
|||
'html': '<body><img src="' + ve.dm.example.imgSrc + '"></body>',
|
||||
'data': [
|
||||
{ 'type': 'paragraph', 'internal': { 'generated': 'wrapper' } },
|
||||
{ 'type': 'image', 'attributes' : { 'html/0/src' : ve.dm.example.imgSrc } },
|
||||
{ 'type': 'image', 'attributes' : {
|
||||
'html/0/src' : ve.dm.example.imgSrc,
|
||||
'width': null,
|
||||
'height': null,
|
||||
'src': ve.dm.example.imgSrc
|
||||
} },
|
||||
{ 'type' : '/image' },
|
||||
{ 'type': '/paragraph' }
|
||||
]
|
||||
|
@ -748,10 +758,17 @@ ve.dm.example.domToDataCases = {
|
|||
{
|
||||
'type': 'MWimage',
|
||||
'attributes': {
|
||||
'html/0-0/alt': 'Wiki.png',
|
||||
'html/0-0/height': '',
|
||||
'html/0-0/src': '/index.php?title=Special:FilePath/Wiki.png&width=500',
|
||||
'html/0-0/width': '500',
|
||||
'html/0/data-parsoid': '{"tsr":[158,216],"src":"[[Image:Wiki.png|500px|thumb|center|Example wiki file]]","optNames":{"width":"$1px"},"dsr":[158,216,null,null]}',
|
||||
'html/0/href': './File:Wiki.png',
|
||||
'html/0/rel': 'mw:Image',
|
||||
'src': '/index.php?title=Special:FilePath/Wiki.png&width=500',
|
||||
'width': 500,
|
||||
'height': null,
|
||||
'html': ve.dm.example.MWImageHtml
|
||||
'isLinked': true
|
||||
}
|
||||
},
|
||||
{ 'type': '/MWimage' },
|
||||
|
@ -842,7 +859,12 @@ ve.dm.example.domToDataCases = {
|
|||
'html': '<body><img src="' + ve.dm.example.imgSrc + '">12</body>',
|
||||
'data': [
|
||||
{ 'type': 'paragraph', 'internal': { 'generated': 'wrapper' } },
|
||||
{ 'type': 'image', 'attributes': { 'html/0/src': ve.dm.example.imgSrc } },
|
||||
{ 'type': 'image', 'attributes': {
|
||||
'html/0/src': ve.dm.example.imgSrc,
|
||||
'src': ve.dm.example.imgSrc,
|
||||
'width': null,
|
||||
'height': null
|
||||
} },
|
||||
{ 'type': '/image' },
|
||||
'1',
|
||||
'2',
|
||||
|
|
Loading…
Reference in a new issue