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 142 143 144 145 146 147 148 149
|
/*!
* VisualEditor ContentEditable MWGalleryNode class.
*
* @copyright See AUTHORS.txt
* @license The MIT License (MIT); see LICENSE.txt
*/
/**
* ContentEditable MediaWiki gallery node.
*
* @class
* @extends ve.ce.BranchNode
* @mixes ve.ce.FocusableNode
*
* @constructor
* @param {ve.dm.MWGalleryNode} model Model to observe
* @param {Object} [config] Configuration options
*/
ve.ce.MWGalleryNode = function VeCeMWGalleryNode() {
// Parent constructor
ve.ce.MWGalleryNode.super.apply( this, arguments );
// DOM hierarchy for MWGalleryNode:
// <ul> this.$element (gallery mw-gallery-{mode})
// <li> ve.ce.MWGalleryCaptionNode (gallerycaption)
// <li> ve.ce.MWGalleryImageNode (gallerybox)
// <li> ve.ce.MWGalleryImageNode (gallerybox)
// ⋮
// Mixin constructors
ve.ce.FocusableNode.call( this, this.getFocusableElement() );
this.onUpdateDebounced = ve.debounce( this.onUpdate.bind( this ) );
// Events
this.model.connect( this, {
update: 'onUpdateDebounced',
// Update $focusable when number of images changes
splice: 'onUpdateDebounced',
attributeChange: 'onAttributeChange'
} );
// Initialization
this.$element.addClass( 'gallery' );
this.onUpdate();
};
/* Inheritance */
OO.inheritClass( ve.ce.MWGalleryNode, ve.ce.BranchNode );
OO.mixinClass( ve.ce.MWGalleryNode, ve.ce.FocusableNode );
/* Static Properties */
ve.ce.MWGalleryNode.static.name = 'mwGallery';
ve.ce.MWGalleryNode.static.tagName = 'ul';
ve.ce.MWGalleryNode.static.iconWhenInvisible = 'imageGallery';
ve.ce.MWGalleryNode.static.primaryCommandName = 'gallery';
/* Methods */
/**
* Handle model update events.
*/
ve.ce.MWGalleryNode.prototype.onUpdate = function () {
const mwAttrs = this.model.getAttribute( 'mw' ).attrs;
const defaults = mw.config.get( 'wgVisualEditorConfig' ).galleryOptions;
const mode = mwAttrs.mode || defaults.mode;
// `.attr( …, undefined )` does nothing - it's required to use `null` to remove an attribute.
// (This also clears the 'max-width', set below, if it's not needed.)
this.$element.attr( 'style', mwAttrs.style || null );
if ( mwAttrs.perrow && ( mode === 'traditional' || mode === 'nolines' ) ) {
// Magic 30 and 8 matches the code in ve.ce.MWGalleryImageNode
const imageWidth = parseInt( mwAttrs.widths || defaults.imageWidth );
const imagePadding = ( mode === 'traditional' ? 30 : 0 );
this.$element.css( 'max-width', mwAttrs.perrow * ( imageWidth + imagePadding + 8 ) );
}
// Update $focusable/$bounding, similar to ve.ce.GeneratedContentNode
if ( this.live ) {
this.emit( 'teardown' );
}
this.$focusable = this.getFocusableElement();
this.$bounding = this.$focusable;
if ( this.live ) {
this.emit( 'setup' );
}
this.updateInvisibleIcon();
};
/**
* Handle attribute changes to keep the live HTML element updated.
*
* @param {string} key Attribute name
* @param {any} from Old value
* @param {any} to New value
*/
ve.ce.MWGalleryNode.prototype.onAttributeChange = function ( key, from, to ) {
const defaults = mw.config.get( 'wgVisualEditorConfig' ).galleryOptions;
if ( key !== 'mw' ) {
return;
}
if ( from.attrs.class !== to.attrs.class ) {
// We can't overwrite the whole 'class' HTML attribute, because it also contains a class
// generated from the 'mode' MW attribute, and VE internal classes like 've-ce-focusableNode'
// eslint-disable-next-line mediawiki/class-doc
this.$element
.removeClass( from.attrs.class )
.addClass( to.attrs.class );
}
if ( from.attrs.mode !== to.attrs.mode ) {
// The following classes are used here:
// * mw-gallery-traditional
// * mw-gallery-nolines
// * mw-gallery-packed
// * mw-gallery-packed-overlay
// * mw-gallery-packed-hover
// * mw-gallery-slideshow
this.$element
.removeClass( 'mw-gallery-' + ( from.attrs.mode || defaults.mode ) )
.addClass( 'mw-gallery-' + ( to.attrs.mode || defaults.mode ) );
}
};
/**
* Get the focusable element
*
* As seen in ve.ce.GeneratedContentNode.
* TODO: Consider making this a core ve.ce.FocsableNode feature.
*
* @return {jQuery} Focusable element
*/
ve.ce.MWGalleryNode.prototype.getFocusableElement = function () {
return this.$element.find( '.gallerybox .thumb' );
};
/* Registration */
ve.ce.nodeFactory.register( ve.ce.MWGalleryNode );
|