1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141
|
/*!
* VisualEditor ContentEditable MWGalleryImageNode class.
*
* @copyright See AUTHORS.txt
* @license The MIT License (MIT); see LICENSE.txt
*/
/**
* ContentEditable MediaWiki gallery image node.
*
* @class
* @extends ve.ce.BranchNode
*
* @constructor
* @param {ve.dm.MWGalleryImageNode} model Model to observe
* @param {Object} [config] Configuration options
*/
ve.ce.MWGalleryImageNode = function VeCeMWGalleryImageNode( model ) {
// Parent constructor
ve.ce.MWGalleryImageNode.super.apply( this, arguments );
// DOM hierarchy for MWGalleryImageNode:
// <li> this.$element (gallerybox)
// <div> thumbDiv
// <span> innerDiv
// <a> a
// <img> img
// <a> filenameA (galleryfilename)
// <figcaption> ve.ce.MWGalleryImageCaptionNode
const defaults = mw.config.get( 'wgVisualEditorConfig' ).galleryOptions;
const attributes = model.getAttributes();
const galleryMwAttrs = model.parent.getAttribute( 'mw' ).attrs;
// Putting all this setup in the constructor works because MWGalleryImageNodes are never updated,
// only created from scratch
// These dimensions are different depending on the gallery mode.
// (This only vaguely approximates the actual rendering.)
const mode = galleryMwAttrs.mode || defaults.mode;
let innerDivWidth, innerDivHeight, innerDivMargin, outerDivWidth;
if ( mode === 'traditional' || mode === 'nolines' || mode === 'slideshow' ) {
const imagePadding = ( mode === 'traditional' ? 30 : 0 );
innerDivWidth = parseInt( galleryMwAttrs.widths || defaults.imageWidth ) + imagePadding;
innerDivHeight = parseInt( galleryMwAttrs.heights || defaults.imageHeight ) + imagePadding;
if ( mode === 'traditional' ) {
const imageHeight = parseInt( attributes.height );
innerDivMargin = ( ( innerDivHeight - imageHeight ) / 2 ) + 'px auto';
} else {
innerDivMargin = 0;
}
outerDivWidth = innerDivWidth + 8;
} else {
innerDivWidth = parseInt( attributes.width );
innerDivHeight = parseInt( galleryMwAttrs.heights || defaults.imageHeight );
innerDivMargin = 0;
outerDivWidth = innerDivWidth + 4;
}
const resourceTitle = mw.Title.newFromText( mw.libs.ve.normalizeParsoidResourceName( attributes.resource ) );
this.$element
.addClass( 'gallerybox' )
.css( 'width', outerDivWidth + 'px' );
const $thumbDiv = $( '<div>' )
.addClass( 'thumb' )
.css( 'width', innerDivWidth + 'px' )
.css( 'height', innerDivHeight + 'px' );
const $innerDiv = $( '<span>' )
.css( 'margin', innerDivMargin );
const $a = $( '<a>' ).addClass( 'mw-file-description' );
let $img;
if ( model.getAttribute( 'isError' ) ) {
$img = $( '<span>' )
.addClass( 'mw-file-element mw-broken-media' )
.text( model.getAttribute( 'errorText' ) );
} else {
$img = $( '<img>' )
.addClass( 'mw-file-element' )
.attr( 'resource', attributes.resource )
.attr( 'alt', attributes.altText )
.attr( 'src', attributes.src )
.attr( 'height', attributes.height )
.attr( 'width', attributes.width );
}
this.$filenameA = $( '<a>' )
.attr( 'href', '#' ) // Just to make it look like a link
.text( resourceTitle ? resourceTitle.getMainText() : attributes.resource )
.toggleClass( 'oo-ui-element-hidden', galleryMwAttrs.showfilename !== 'yes' );
this.$element.prepend(
$thumbDiv.append(
$innerDiv.append(
$a.append(
$img
)
)
),
this.$filenameA
);
this.model.parent.connect( this, { attributeChange: 'onGalleryAttributeChange' } );
};
/* Inheritance */
OO.inheritClass( ve.ce.MWGalleryImageNode, ve.ce.BranchNode );
/* Static Properties */
ve.ce.MWGalleryImageNode.static.name = 'mwGalleryImage';
ve.ce.MWGalleryImageNode.static.tagName = 'li';
/* Methods */
ve.ce.MWGalleryImageNode.prototype.onGalleryAttributeChange = function ( key, from, to ) {
if ( key !== 'mw' ) {
return;
}
this.$filenameA.toggleClass( 'oo-ui-element-hidden', to.attrs.showfilename !== 'yes' );
};
ve.ce.MWGalleryImageNode.prototype.getDomPosition = function () {
// We need to override this because this.$element can have children other than renderings of child
// CE nodes (specifically, the image, this.$thumbDiv), which throws the calculations out of whack.
// Luckily, MWGalleryImageNode is very simple and can contain at most one other node: its caption,
// which is always inserted at the end.
const domNode = this.$element.last()[ 0 ];
return {
node: domNode,
offset: domNode.childNodes.length
};
};
/* Registration */
ve.ce.nodeFactory.register( ve.ce.MWGalleryImageNode );
|