File: ve.ce.MWGalleryNode.js

package info (click to toggle)
mediawiki 1%3A1.43.3%2Bdfsg-1
  • links: PTS, VCS
  • area: main
  • in suites: trixie
  • size: 417,464 kB
  • sloc: php: 1,062,949; javascript: 664,290; sql: 9,714; python: 5,458; xml: 3,489; sh: 1,131; makefile: 64
file content (149 lines) | stat: -rw-r--r-- 4,301 bytes parent folder | download | duplicates (2)
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 );