2018-09-26 12:49:10 +00:00
|
|
|
/*!
|
2013-06-11 19:16:04 +00:00
|
|
|
* VisualEditor user interface MWTransclusionDialog class.
|
|
|
|
*
|
2023-12-01 16:06:11 +00:00
|
|
|
* @copyright See AUTHORS.txt
|
2013-06-11 19:16:04 +00:00
|
|
|
* @license The MIT License (MIT); see LICENSE.txt
|
|
|
|
*/
|
|
|
|
|
|
|
|
/**
|
2021-06-03 16:25:15 +00:00
|
|
|
* Dialog for inserting and editing MediaWiki transclusions, i.e. a sequence of one or more template
|
|
|
|
* invocations that strictly belong to each other (e.g. because they are unbalanced), possibly
|
|
|
|
* mixed with raw wikitext snippets.
|
|
|
|
*
|
|
|
|
* Note the base class {@see ve.ui.MWTemplateDialog} alone does not allow to manage more than a
|
|
|
|
* single template invocation. Most of the code for this feature set is exclusive to this subclass.
|
2013-06-11 19:16:04 +00:00
|
|
|
*
|
|
|
|
* @class
|
2014-04-10 19:07:40 +00:00
|
|
|
* @extends ve.ui.MWTemplateDialog
|
2013-06-11 19:16:04 +00:00
|
|
|
*
|
|
|
|
* @constructor
|
2013-09-25 10:21:09 +00:00
|
|
|
* @param {Object} [config] Configuration options
|
2013-06-11 19:16:04 +00:00
|
|
|
*/
|
2014-08-21 00:50:54 +00:00
|
|
|
ve.ui.MWTransclusionDialog = function VeUiMWTransclusionDialog( config ) {
|
2013-06-11 19:16:04 +00:00
|
|
|
// Parent constructor
|
2014-08-21 00:50:54 +00:00
|
|
|
ve.ui.MWTransclusionDialog.super.call( this, config );
|
2013-07-03 01:30:10 +00:00
|
|
|
|
2013-06-11 19:16:04 +00:00
|
|
|
// Properties
|
2021-06-04 15:41:41 +00:00
|
|
|
this.isSidebarExpanded = null;
|
2021-05-03 10:40:02 +00:00
|
|
|
|
2022-02-25 14:59:34 +00:00
|
|
|
this.hotkeyTriggers = {};
|
2021-09-27 11:16:33 +00:00
|
|
|
this.$element.on( 'keydown', this.onKeyDown.bind( this ) );
|
2013-06-11 19:16:04 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
/* Inheritance */
|
|
|
|
|
2014-04-10 19:07:40 +00:00
|
|
|
OO.inheritClass( ve.ui.MWTransclusionDialog, ve.ui.MWTemplateDialog );
|
2013-07-03 01:30:10 +00:00
|
|
|
|
2013-06-11 19:16:04 +00:00
|
|
|
/* Static Properties */
|
|
|
|
|
2014-03-04 22:56:14 +00:00
|
|
|
ve.ui.MWTransclusionDialog.static.name = 'transclusion';
|
|
|
|
|
2022-06-21 12:22:47 +00:00
|
|
|
ve.ui.MWTransclusionDialog.static.size = 'larger';
|
|
|
|
|
2024-05-29 16:03:23 +00:00
|
|
|
ve.ui.MWTransclusionDialog.static.actions = [
|
|
|
|
...ve.ui.MWTemplateDialog.static.actions,
|
2014-07-14 21:32:49 +00:00
|
|
|
{
|
2014-08-22 20:50:48 +00:00
|
|
|
action: 'mode',
|
2024-12-02 15:24:50 +00:00
|
|
|
// label is set by updateModeActionState
|
|
|
|
modes: [ 'edit', 'insert' ]
|
2021-05-07 10:40:36 +00:00
|
|
|
},
|
|
|
|
{
|
|
|
|
action: 'back',
|
|
|
|
label: OO.ui.deferMsg( 'visualeditor-dialog-action-goback' ),
|
|
|
|
modes: [ 'edit', 'insert' ],
|
|
|
|
flags: [ 'safe', 'back' ]
|
2014-07-14 21:32:49 +00:00
|
|
|
}
|
2024-05-29 16:03:23 +00:00
|
|
|
];
|
2014-07-14 21:32:49 +00:00
|
|
|
|
2021-07-23 15:07:39 +00:00
|
|
|
ve.ui.MWTransclusionDialog.static.smallScreenMaxWidth = 540;
|
|
|
|
|
2022-05-12 10:43:50 +00:00
|
|
|
/* Static Methods */
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @return {boolean}
|
|
|
|
*/
|
|
|
|
ve.ui.MWTransclusionDialog.static.isSmallScreen = function () {
|
|
|
|
return $( window ).width() <= ve.ui.MWTransclusionDialog.static.smallScreenMaxWidth;
|
|
|
|
};
|
|
|
|
|
2013-06-11 19:16:04 +00:00
|
|
|
/* Methods */
|
|
|
|
|
2014-03-04 22:56:14 +00:00
|
|
|
/**
|
|
|
|
* Handle outline controls move events.
|
|
|
|
*
|
2021-06-18 13:51:25 +00:00
|
|
|
* @private
|
2014-03-04 22:56:14 +00:00
|
|
|
* @param {number} places Number of places to move the selected item
|
|
|
|
*/
|
|
|
|
ve.ui.MWTransclusionDialog.prototype.onOutlineControlsMove = function ( places ) {
|
2024-05-21 14:22:56 +00:00
|
|
|
const part = this.transclusionModel.getPartFromId( this.bookletLayout.getSelectedTopLevelPartId() );
|
2021-10-08 07:37:01 +00:00
|
|
|
if ( !part ) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2024-05-21 14:22:56 +00:00
|
|
|
const newPlace = this.transclusionModel.getParts().indexOf( part ) + places;
|
2022-02-08 13:30:54 +00:00
|
|
|
if ( newPlace < 0 || newPlace >= this.transclusionModel.getParts().length ) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2021-10-08 07:37:01 +00:00
|
|
|
// Move part to new location, and if dialog is loaded switch to new part page
|
2024-05-21 14:22:56 +00:00
|
|
|
const promise = this.transclusionModel.addPart( part, newPlace );
|
2021-10-08 07:37:01 +00:00
|
|
|
if ( this.loaded && !this.preventReselection ) {
|
2022-07-22 13:10:46 +00:00
|
|
|
// FIXME: Should be handled internally {@see ve.ui.MWTwoPaneTransclusionDialogLayout}
|
2022-06-28 13:31:54 +00:00
|
|
|
promise.done( this.bookletLayout.focusPart.bind( this.bookletLayout, part.getId() ) );
|
2014-03-04 22:56:14 +00:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Handle outline controls remove events.
|
2021-06-18 13:51:25 +00:00
|
|
|
*
|
|
|
|
* @private
|
2014-03-04 22:56:14 +00:00
|
|
|
*/
|
|
|
|
ve.ui.MWTransclusionDialog.prototype.onOutlineControlsRemove = function () {
|
2024-05-21 14:22:56 +00:00
|
|
|
const controls = this.bookletLayout.getOutlineControls();
|
2022-03-01 11:25:22 +00:00
|
|
|
// T301914: Safe-guard for when a keyboard shortcut triggers this, instead of the actual button
|
|
|
|
if ( !controls.isVisible() ||
|
|
|
|
!controls.removeButton.isVisible() ||
|
|
|
|
controls.removeButton.isDisabled()
|
|
|
|
) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2024-05-21 14:22:56 +00:00
|
|
|
const partId = this.bookletLayout.getSelectedTopLevelPartId(),
|
2022-07-22 13:10:46 +00:00
|
|
|
part = this.transclusionModel.getPartFromId( partId );
|
2022-07-26 11:21:39 +00:00
|
|
|
if ( part ) {
|
2021-10-08 07:37:01 +00:00
|
|
|
this.transclusionModel.removePart( part );
|
2014-03-04 22:56:14 +00:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
2021-09-27 11:16:33 +00:00
|
|
|
* Create a new template part at the end of the transclusion.
|
2021-06-18 13:51:25 +00:00
|
|
|
*
|
|
|
|
* @private
|
2014-03-04 22:56:14 +00:00
|
|
|
*/
|
2021-09-27 11:16:33 +00:00
|
|
|
ve.ui.MWTransclusionDialog.prototype.addTemplatePlaceholder = function () {
|
2014-04-24 00:22:45 +00:00
|
|
|
this.addPart( new ve.dm.MWTemplatePlaceholderModel( this.transclusionModel ) );
|
2014-03-04 22:56:14 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
2022-07-25 14:43:28 +00:00
|
|
|
* Handle add wikitext button click or hotkey events.
|
2021-06-18 13:51:25 +00:00
|
|
|
*
|
|
|
|
* @private
|
2014-03-04 22:56:14 +00:00
|
|
|
*/
|
2022-06-28 14:10:12 +00:00
|
|
|
ve.ui.MWTransclusionDialog.prototype.addWikitext = function () {
|
2021-06-04 10:16:58 +00:00
|
|
|
this.addPart( new ve.dm.MWTransclusionContentModel( this.transclusionModel ) );
|
2014-03-04 22:56:14 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
2022-06-21 12:22:47 +00:00
|
|
|
* Handle add parameter hotkey events.
|
2021-06-18 13:51:25 +00:00
|
|
|
*
|
|
|
|
* @private
|
2022-07-25 14:43:28 +00:00
|
|
|
* @param {jQuery.Event} e Key down event
|
2014-03-04 22:56:14 +00:00
|
|
|
*/
|
2022-07-25 14:43:28 +00:00
|
|
|
ve.ui.MWTransclusionDialog.prototype.addParameter = function ( e ) {
|
|
|
|
// Check if the focus was in e.g. a parameter list or filter input when the hotkey was pressed
|
2024-05-21 14:22:56 +00:00
|
|
|
let partId = this.bookletLayout.sidebar.findPartIdContainingElement( e.target ),
|
2022-07-25 14:43:28 +00:00
|
|
|
part = this.transclusionModel.getPartFromId( partId );
|
|
|
|
|
|
|
|
if ( !( part instanceof ve.dm.MWTemplateModel ) ) {
|
|
|
|
// Otherwise add to the template that's currently selected via its title or parameter
|
|
|
|
partId = this.bookletLayout.getTopLevelPartIdForSelection();
|
2022-07-22 13:53:10 +00:00
|
|
|
part = this.transclusionModel.getPartFromId( partId );
|
2022-07-25 14:43:28 +00:00
|
|
|
}
|
|
|
|
|
2023-06-06 15:22:30 +00:00
|
|
|
if ( this.transclusionModel.isSingleTemplate() ) {
|
|
|
|
part = this.transclusionModel.getParts()[ 0 ];
|
|
|
|
}
|
|
|
|
|
2021-06-18 14:09:52 +00:00
|
|
|
if ( !( part instanceof ve.dm.MWTemplateModel ) ) {
|
|
|
|
return;
|
2014-03-04 22:56:14 +00:00
|
|
|
}
|
2021-06-18 14:09:52 +00:00
|
|
|
|
|
|
|
// TODO: Use a distinct class for placeholder model rather than
|
|
|
|
// these magical "empty" constants.
|
2024-05-21 14:22:56 +00:00
|
|
|
const placeholderParameter = new ve.dm.MWParameterModel( part );
|
2021-10-14 11:43:35 +00:00
|
|
|
part.addParameter( placeholderParameter );
|
2022-06-28 13:31:54 +00:00
|
|
|
this.bookletLayout.focusPart( placeholderParameter.getId() );
|
2021-10-14 11:59:46 +00:00
|
|
|
|
2022-06-10 15:15:18 +00:00
|
|
|
this.autoExpandSidebar();
|
2014-03-04 22:56:14 +00:00
|
|
|
};
|
|
|
|
|
2013-06-11 19:16:04 +00:00
|
|
|
/**
|
2014-04-10 19:07:40 +00:00
|
|
|
* @inheritdoc
|
2013-06-11 19:16:04 +00:00
|
|
|
*/
|
2013-12-02 20:10:55 +00:00
|
|
|
ve.ui.MWTransclusionDialog.prototype.onReplacePart = function ( removed, added ) {
|
2014-04-10 19:07:40 +00:00
|
|
|
ve.ui.MWTransclusionDialog.super.prototype.onReplacePart.call( this, removed, added );
|
2024-05-21 14:22:56 +00:00
|
|
|
const parts = this.transclusionModel.getParts();
|
2014-07-14 21:32:49 +00:00
|
|
|
|
2021-08-23 11:25:15 +00:00
|
|
|
if ( parts.length === 0 ) {
|
2015-03-19 22:54:48 +00:00
|
|
|
this.addPart( new ve.dm.MWTemplatePlaceholderModel( this.transclusionModel ) );
|
2021-10-08 15:15:05 +00:00
|
|
|
} else if ( parts.length > 1 ) {
|
|
|
|
this.$element.removeClass( 've-ui-mwTransclusionDialog-single-transclusion' );
|
2014-10-23 18:37:50 +00:00
|
|
|
}
|
|
|
|
|
2021-12-06 13:11:14 +00:00
|
|
|
// multipart message
|
2022-06-21 12:22:47 +00:00
|
|
|
this.bookletLayout.stackLayout.$element.prepend( this.multipartMessage.$element );
|
|
|
|
this.multipartMessage.toggle( parts.length > 1 );
|
2021-08-23 11:25:15 +00:00
|
|
|
|
2021-10-13 15:28:34 +00:00
|
|
|
this.autoExpandSidebar();
|
2021-09-20 14:50:36 +00:00
|
|
|
this.updateModeActionState();
|
2021-05-07 10:40:36 +00:00
|
|
|
this.updateActionSet();
|
2013-06-11 19:16:04 +00:00
|
|
|
};
|
|
|
|
|
2021-09-30 11:07:53 +00:00
|
|
|
/**
|
|
|
|
* @private
|
|
|
|
*/
|
|
|
|
ve.ui.MWTransclusionDialog.prototype.setupHotkeyTriggers = function () {
|
2022-01-21 14:04:54 +00:00
|
|
|
// Lower-case modifier and key names as specified in {@see ve.ui.Trigger}
|
2024-05-21 14:22:56 +00:00
|
|
|
const isMac = ve.getSystemPlatform() === 'mac',
|
2022-02-25 14:59:34 +00:00
|
|
|
meta = isMac ? 'meta+' : 'ctrl+';
|
2024-05-21 14:22:56 +00:00
|
|
|
const hotkeys = {
|
2022-01-21 14:04:54 +00:00
|
|
|
addTemplate: meta + 'd',
|
|
|
|
addWikitext: meta + 'shift+y',
|
|
|
|
addParameter: meta + 'shift+d',
|
|
|
|
moveUp: meta + 'shift+up',
|
|
|
|
moveDown: meta + 'shift+down',
|
2022-02-08 13:46:01 +00:00
|
|
|
remove: meta + 'delete',
|
|
|
|
removeBackspace: meta + 'backspace'
|
2022-01-21 14:04:54 +00:00
|
|
|
};
|
2021-09-30 11:07:53 +00:00
|
|
|
|
2024-05-21 14:22:56 +00:00
|
|
|
const notInTextFields = /^(?!INPUT|TEXTAREA)/i;
|
2022-02-25 14:59:34 +00:00
|
|
|
this.connectHotKeyBinding( hotkeys.addTemplate, this.addTemplatePlaceholder.bind( this ) );
|
2022-06-28 14:10:12 +00:00
|
|
|
this.connectHotKeyBinding( hotkeys.addWikitext, this.addWikitext.bind( this ) );
|
2022-02-25 14:59:34 +00:00
|
|
|
this.connectHotKeyBinding( hotkeys.addParameter, this.addParameter.bind( this ) );
|
|
|
|
this.connectHotKeyBinding( hotkeys.moveUp, this.onOutlineControlsMove.bind( this, -1 ), notInTextFields );
|
|
|
|
this.connectHotKeyBinding( hotkeys.moveDown, this.onOutlineControlsMove.bind( this, 1 ), notInTextFields );
|
|
|
|
this.connectHotKeyBinding( hotkeys.remove, this.onOutlineControlsRemove.bind( this ), notInTextFields );
|
|
|
|
if ( isMac ) {
|
|
|
|
this.connectHotKeyBinding( hotkeys.removeBackspace, this.onOutlineControlsRemove.bind( this ), notInTextFields );
|
|
|
|
}
|
2022-02-08 13:30:54 +00:00
|
|
|
|
2024-05-21 14:22:56 +00:00
|
|
|
const controls = this.bookletLayout.getOutlineControls();
|
2022-06-28 14:10:12 +00:00
|
|
|
this.addHotkeyToTitle( controls.addTemplateButton, hotkeys.addTemplate );
|
|
|
|
this.addHotkeyToTitle( controls.addWikitextButton, hotkeys.addWikitext );
|
2022-01-21 14:04:54 +00:00
|
|
|
this.addHotkeyToTitle( controls.upButton, hotkeys.moveUp );
|
|
|
|
this.addHotkeyToTitle( controls.downButton, hotkeys.moveDown );
|
|
|
|
this.addHotkeyToTitle( controls.removeButton, hotkeys.remove );
|
|
|
|
};
|
|
|
|
|
2022-02-25 14:59:34 +00:00
|
|
|
/**
|
|
|
|
* @private
|
|
|
|
* @param {string} hotkey
|
|
|
|
* @param {Function} handler
|
|
|
|
* @param {RegExp} [validTypes]
|
|
|
|
*/
|
|
|
|
ve.ui.MWTransclusionDialog.prototype.connectHotKeyBinding = function ( hotkey, handler, validTypes ) {
|
|
|
|
this.hotkeyTriggers[ hotkey ] = {
|
|
|
|
handler: handler,
|
|
|
|
validTypes: validTypes
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
2022-01-21 14:04:54 +00:00
|
|
|
/**
|
|
|
|
* @private
|
|
|
|
* @param {OO.ui.mixin.TitledElement} element
|
|
|
|
* @param {string} hotkey
|
|
|
|
*/
|
|
|
|
ve.ui.MWTransclusionDialog.prototype.addHotkeyToTitle = function ( element, hotkey ) {
|
|
|
|
// Separated with a space as in {@see OO.ui.Tool.updateTitle}
|
|
|
|
element.setTitle( element.getTitle() + ' ' + new ve.ui.Trigger( hotkey ).getMessage() );
|
2021-09-30 11:07:53 +00:00
|
|
|
};
|
|
|
|
|
2021-09-27 11:16:33 +00:00
|
|
|
/**
|
|
|
|
* Handles key down events.
|
|
|
|
*
|
|
|
|
* @protected
|
|
|
|
* @param {jQuery.Event} e Key down event
|
|
|
|
*/
|
|
|
|
ve.ui.MWTransclusionDialog.prototype.onKeyDown = function ( e ) {
|
2024-05-21 14:22:56 +00:00
|
|
|
const hotkey = new ve.ui.Trigger( e ).toString(),
|
2022-02-25 14:59:34 +00:00
|
|
|
trigger = this.hotkeyTriggers[ hotkey ];
|
2021-09-27 11:16:33 +00:00
|
|
|
|
2022-02-25 14:59:34 +00:00
|
|
|
if ( trigger && ( !trigger.validTypes || trigger.validTypes.test( e.target.nodeName ) ) ) {
|
2022-07-25 14:43:28 +00:00
|
|
|
trigger.handler( e );
|
2021-09-27 11:16:33 +00:00
|
|
|
e.preventDefault();
|
|
|
|
e.stopPropagation();
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2014-03-06 23:39:40 +00:00
|
|
|
/**
|
2014-04-10 19:07:40 +00:00
|
|
|
* @inheritdoc
|
2014-03-04 22:56:14 +00:00
|
|
|
*/
|
2014-04-10 19:07:40 +00:00
|
|
|
ve.ui.MWTransclusionDialog.prototype.getPageFromPart = function ( part ) {
|
2024-05-21 14:22:56 +00:00
|
|
|
const page = ve.ui.MWTransclusionDialog.super.prototype.getPageFromPart.call( this, part );
|
2014-04-10 19:07:40 +00:00
|
|
|
if ( !page && part instanceof ve.dm.MWTransclusionContentModel ) {
|
2019-02-24 12:38:03 +00:00
|
|
|
return new ve.ui.MWTransclusionContentPage( part, part.getId(), { $overlay: this.$overlay, isReadOnly: this.isReadOnly() } );
|
2014-04-10 19:07:40 +00:00
|
|
|
}
|
|
|
|
return page;
|
2014-03-04 22:56:14 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
2021-08-06 10:28:59 +00:00
|
|
|
* Automatically expand or collapse the sidebar according to default logic.
|
2014-03-04 22:56:14 +00:00
|
|
|
*
|
2021-09-20 09:43:18 +00:00
|
|
|
* @protected
|
2014-03-04 22:56:14 +00:00
|
|
|
*/
|
2021-08-06 10:28:59 +00:00
|
|
|
ve.ui.MWTransclusionDialog.prototype.autoExpandSidebar = function () {
|
2024-05-21 14:22:56 +00:00
|
|
|
let expandSidebar;
|
2021-08-06 10:28:59 +00:00
|
|
|
|
2024-05-21 14:22:56 +00:00
|
|
|
const isSmallScreen = this.constructor.static.isSmallScreen();
|
2021-10-28 08:42:44 +00:00
|
|
|
|
2024-05-21 14:22:56 +00:00
|
|
|
const showOtherActions = isSmallScreen ||
|
2024-04-30 16:44:25 +00:00
|
|
|
// Check for unknown actions, show the toolbar if any are available.
|
|
|
|
this.actions.getOthers().some( ( action ) => action.action !== 'mode' );
|
|
|
|
this.actions.forEach( { actions: [ 'mode' ] }, ( action ) => {
|
2022-06-10 15:15:18 +00:00
|
|
|
action.toggle( isSmallScreen );
|
|
|
|
} );
|
|
|
|
this.$otherActions.toggleClass( 'oo-ui-element-hidden', !showOtherActions );
|
2021-10-27 10:19:59 +00:00
|
|
|
|
2022-06-10 15:15:18 +00:00
|
|
|
if ( isSmallScreen && this.transclusionModel.isEmpty() ) {
|
|
|
|
expandSidebar = false;
|
|
|
|
} else if ( isSmallScreen &&
|
|
|
|
// eslint-disable-next-line no-jquery/no-class-state
|
|
|
|
this.$content.hasClass( 've-ui-mwTransclusionDialog-small-screen' )
|
|
|
|
) {
|
|
|
|
// We did this already. If the sidebar is visible or not is now the user's decision.
|
|
|
|
return;
|
2021-08-06 10:28:59 +00:00
|
|
|
} else {
|
2022-06-10 15:15:18 +00:00
|
|
|
expandSidebar = !isSmallScreen;
|
2014-03-04 22:56:14 +00:00
|
|
|
}
|
2014-11-08 00:41:03 +00:00
|
|
|
|
2022-06-10 15:15:18 +00:00
|
|
|
this.$content.toggleClass( 've-ui-mwTransclusionDialog-small-screen', isSmallScreen );
|
|
|
|
|
2021-09-09 10:26:40 +00:00
|
|
|
this.toggleSidebar( expandSidebar );
|
2021-08-06 10:28:59 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Set if the sidebar is visible (which means the dialog is expanded), or collapsed.
|
|
|
|
*
|
2021-09-09 10:26:40 +00:00
|
|
|
* @param {boolean} expandSidebar
|
2021-08-06 10:28:59 +00:00
|
|
|
* @private
|
|
|
|
*/
|
2021-09-09 10:26:40 +00:00
|
|
|
ve.ui.MWTransclusionDialog.prototype.toggleSidebar = function ( expandSidebar ) {
|
|
|
|
if ( this.isSidebarExpanded === expandSidebar ) {
|
2021-08-06 10:28:59 +00:00
|
|
|
return;
|
2014-03-04 22:56:14 +00:00
|
|
|
}
|
2021-08-06 10:28:59 +00:00
|
|
|
|
2021-09-09 10:26:40 +00:00
|
|
|
this.isSidebarExpanded = expandSidebar;
|
2021-08-06 10:28:59 +00:00
|
|
|
this.$content
|
2021-09-09 10:26:40 +00:00
|
|
|
.toggleClass( 've-ui-mwTransclusionDialog-collapsed', !expandSidebar )
|
|
|
|
.toggleClass( 've-ui-mwTransclusionDialog-expanded', expandSidebar );
|
2021-09-09 09:22:56 +00:00
|
|
|
|
2021-09-09 10:26:40 +00:00
|
|
|
this.bookletLayout.toggleOutline( expandSidebar );
|
2021-08-06 10:28:59 +00:00
|
|
|
this.updateTitle();
|
|
|
|
this.updateModeActionState();
|
|
|
|
|
|
|
|
// HACK blur any active input so that its dropdown will be hidden and won't end
|
|
|
|
// up being mispositioned
|
|
|
|
this.$content.find( 'input:focus' ).trigger( 'blur' );
|
2021-09-20 13:49:44 +00:00
|
|
|
|
2022-06-20 13:48:45 +00:00
|
|
|
if ( this.loaded && this.constructor.static.isSmallScreen() ) {
|
2022-05-13 14:28:46 +00:00
|
|
|
// Updates the page sizes when the menu is toggled using the button. This needs
|
|
|
|
// to happen after the animation when the panel is visible.
|
2024-04-30 16:44:25 +00:00
|
|
|
setTimeout( () => {
|
2024-05-01 12:32:49 +00:00
|
|
|
this.bookletLayout.stackLayout.getItems().forEach( ( page ) => {
|
2022-05-13 14:28:46 +00:00
|
|
|
if ( page instanceof ve.ui.MWParameterPage ) {
|
|
|
|
page.updateSize();
|
|
|
|
}
|
|
|
|
} );
|
|
|
|
}, OO.ui.theme.getDialogTransitionDuration() );
|
|
|
|
|
2021-09-20 13:49:44 +00:00
|
|
|
// Reapply selection and scrolling when switching between panes.
|
2024-05-21 14:22:56 +00:00
|
|
|
const selectedPage = this.bookletLayout.getCurrentPage();
|
2021-09-20 13:49:44 +00:00
|
|
|
if ( selectedPage ) {
|
2024-05-21 14:22:56 +00:00
|
|
|
const name = selectedPage.getName();
|
2021-09-20 13:49:44 +00:00
|
|
|
// Align whichever panel is becoming visible, after animation completes.
|
|
|
|
// TODO: Should hook onto an animation promise—but is this possible when pure CSS?
|
2024-04-30 16:44:25 +00:00
|
|
|
setTimeout( () => {
|
2021-09-20 13:49:44 +00:00
|
|
|
if ( expandSidebar ) {
|
2024-05-01 12:32:49 +00:00
|
|
|
this.sidebar.setSelectionByPageName( name );
|
2021-09-20 13:49:44 +00:00
|
|
|
} else {
|
2022-07-04 14:00:39 +00:00
|
|
|
selectedPage.scrollElementIntoView( { alignToTop: true, padding: { top: 20 } } );
|
2022-07-06 12:47:50 +00:00
|
|
|
if ( !OO.ui.isMobile() ) {
|
|
|
|
selectedPage.focus();
|
|
|
|
}
|
2021-09-20 13:49:44 +00:00
|
|
|
}
|
|
|
|
}, OO.ui.theme.getDialogTransitionDuration() );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2014-03-06 23:39:40 +00:00
|
|
|
/**
|
2021-06-18 13:51:25 +00:00
|
|
|
* @inheritdoc
|
2014-03-06 23:39:40 +00:00
|
|
|
*/
|
|
|
|
ve.ui.MWTransclusionDialog.prototype.updateTitle = function () {
|
2021-10-06 15:57:10 +00:00
|
|
|
if ( !this.transclusionModel.isSingleTemplate() ) {
|
2021-06-22 14:02:31 +00:00
|
|
|
this.title.setLabel( ve.msg( 'visualeditor-dialog-transclusion-title-edit-transclusion' ) );
|
2014-04-30 21:34:45 +00:00
|
|
|
} else {
|
|
|
|
// Parent method
|
|
|
|
ve.ui.MWTransclusionDialog.super.prototype.updateTitle.call( this );
|
|
|
|
}
|
2014-03-04 22:56:14 +00:00
|
|
|
};
|
|
|
|
|
2014-11-08 00:41:03 +00:00
|
|
|
/**
|
2017-06-14 15:52:18 +00:00
|
|
|
* Update the state of the 'mode' action
|
2021-06-18 13:51:25 +00:00
|
|
|
*
|
|
|
|
* @private
|
2014-11-08 00:41:03 +00:00
|
|
|
*/
|
2017-06-14 15:52:18 +00:00
|
|
|
ve.ui.MWTransclusionDialog.prototype.updateModeActionState = function () {
|
2024-05-21 14:22:56 +00:00
|
|
|
const isExpanded = this.isSidebarExpanded,
|
2021-06-08 08:37:47 +00:00
|
|
|
label = ve.msg( isExpanded ?
|
2021-06-11 11:08:45 +00:00
|
|
|
'visualeditor-dialog-transclusion-collapse-options' :
|
|
|
|
'visualeditor-dialog-transclusion-expand-options' );
|
2021-06-08 08:37:47 +00:00
|
|
|
|
2024-04-30 16:44:25 +00:00
|
|
|
this.actions.forEach( { actions: [ 'mode' ] }, ( action ) => {
|
2020-03-19 13:39:53 +00:00
|
|
|
action.setLabel( label );
|
2021-06-08 08:37:47 +00:00
|
|
|
action.$button.attr( 'aria-expanded', isExpanded ? 1 : 0 );
|
2014-11-08 00:41:03 +00:00
|
|
|
} );
|
2017-06-14 15:52:18 +00:00
|
|
|
|
2022-06-21 12:22:47 +00:00
|
|
|
// The button is only visible on very narrow screens, {@see autoExpandSidebar}.
|
2021-10-01 13:45:44 +00:00
|
|
|
// It's always needed, except in the initial placeholder state.
|
2024-05-21 14:22:56 +00:00
|
|
|
const isInitialState = !isExpanded && this.transclusionModel.isEmpty(),
|
2022-06-10 15:15:18 +00:00
|
|
|
canCollapse = !isInitialState;
|
2021-10-01 13:45:44 +00:00
|
|
|
this.actions.setAbilities( { mode: canCollapse } );
|
2014-11-08 00:41:03 +00:00
|
|
|
};
|
|
|
|
|
2014-03-04 22:56:14 +00:00
|
|
|
/**
|
|
|
|
* Add a part to the transclusion.
|
|
|
|
*
|
|
|
|
* @param {ve.dm.MWTransclusionPartModel} part Part to add
|
|
|
|
*/
|
|
|
|
ve.ui.MWTransclusionDialog.prototype.addPart = function ( part ) {
|
2024-05-21 14:22:56 +00:00
|
|
|
const parts = this.transclusionModel.getParts(),
|
2022-07-22 13:53:10 +00:00
|
|
|
partId = this.bookletLayout.getTopLevelPartIdForSelection(),
|
|
|
|
selectedPart = this.transclusionModel.getPartFromId( partId );
|
2021-10-08 07:37:01 +00:00
|
|
|
// Insert after selected part, or at the end if nothing is selected
|
2024-05-21 14:22:56 +00:00
|
|
|
const index = selectedPart ? parts.indexOf( selectedPart ) + 1 : parts.length;
|
2021-10-08 07:37:01 +00:00
|
|
|
// Add the part, and if dialog is loaded switch to part page
|
2024-05-21 14:22:56 +00:00
|
|
|
const promise = this.transclusionModel.addPart( part, index );
|
2021-10-08 07:37:01 +00:00
|
|
|
if ( this.loaded && !this.preventReselection ) {
|
2022-06-28 13:31:54 +00:00
|
|
|
promise.done( this.bookletLayout.focusPart.bind( this.bookletLayout, part.getId() ) );
|
2014-03-04 22:56:14 +00:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2024-04-16 13:42:23 +00:00
|
|
|
/**
|
|
|
|
* Show a confirm prompt before closing the dialog
|
|
|
|
*
|
|
|
|
* @param {string} prompt Prompt
|
|
|
|
* @return {jQuery.Promise} Close promise
|
|
|
|
*/
|
|
|
|
ve.ui.MWTransclusionDialog.prototype.closeConfirm = function ( prompt ) {
|
|
|
|
return OO.ui.confirm( prompt, {
|
|
|
|
actions: [
|
|
|
|
{
|
|
|
|
action: 'reject',
|
|
|
|
label: ve.msg( 'visualeditor-dialog-transclusion-confirmation-reject' ),
|
|
|
|
flags: 'safe'
|
|
|
|
},
|
|
|
|
{
|
|
|
|
action: 'accept',
|
|
|
|
label: ve.msg( 'visualeditor-dialog-transclusion-confirmation-discard' ),
|
|
|
|
// TODO: Destructive actions don't get focus by default, but maybe should here?
|
|
|
|
flags: 'destructive'
|
|
|
|
}
|
|
|
|
]
|
|
|
|
} );
|
|
|
|
};
|
|
|
|
|
2014-07-14 21:32:49 +00:00
|
|
|
/**
|
|
|
|
* @inheritdoc
|
|
|
|
*/
|
|
|
|
ve.ui.MWTransclusionDialog.prototype.getActionProcess = function ( action ) {
|
2024-05-21 14:22:56 +00:00
|
|
|
const willLoseProgress = this.getMode() === 'insert' ?
|
2022-01-24 10:28:35 +00:00
|
|
|
// A new template with no parameters is not considered valuable.
|
|
|
|
this.transclusionModel.containsValuableData() :
|
2022-01-21 11:16:50 +00:00
|
|
|
// The user has changed a parameter, and is not on the template search page.
|
|
|
|
( this.altered && !this.transclusionModel.isEmpty() );
|
2022-01-24 10:28:35 +00:00
|
|
|
|
|
|
|
switch ( action ) {
|
|
|
|
case 'back':
|
2024-04-30 21:25:39 +00:00
|
|
|
return new OO.ui.Process( () => {
|
2022-01-24 10:28:35 +00:00
|
|
|
if ( willLoseProgress ) {
|
2024-04-30 16:44:25 +00:00
|
|
|
this.closeConfirm( ve.msg( 'visualeditor-dialog-transclusion-back-confirmation-prompt' ) ).then( ( confirmed ) => {
|
2024-04-16 13:42:23 +00:00
|
|
|
if ( confirmed ) {
|
|
|
|
this.resetDialog();
|
|
|
|
}
|
2024-04-30 16:44:25 +00:00
|
|
|
} );
|
2022-01-24 10:28:35 +00:00
|
|
|
} else {
|
|
|
|
this.resetDialog();
|
|
|
|
}
|
2024-04-30 21:25:39 +00:00
|
|
|
} );
|
2022-01-24 10:28:35 +00:00
|
|
|
case 'mode':
|
2024-04-30 21:25:39 +00:00
|
|
|
return new OO.ui.Process( () => {
|
2022-01-24 10:28:35 +00:00
|
|
|
this.toggleSidebar( !this.isSidebarExpanded );
|
2024-04-30 21:25:39 +00:00
|
|
|
} );
|
2022-01-26 13:40:58 +00:00
|
|
|
case '':
|
2022-01-24 10:28:35 +00:00
|
|
|
// close action
|
|
|
|
if ( willLoseProgress ) {
|
2024-04-30 21:25:39 +00:00
|
|
|
return new OO.ui.Process( () => {
|
2024-04-30 16:44:25 +00:00
|
|
|
this.closeConfirm( ve.msg( 'visualeditor-dialog-transclusion-close-confirmation-prompt' ) ).then( ( confirmed ) => {
|
2024-04-16 13:42:23 +00:00
|
|
|
if ( confirmed ) {
|
|
|
|
this.close();
|
|
|
|
}
|
2024-04-30 16:44:25 +00:00
|
|
|
} );
|
2024-04-30 21:25:39 +00:00
|
|
|
} );
|
2022-01-24 10:28:35 +00:00
|
|
|
}
|
2022-01-10 12:45:04 +00:00
|
|
|
}
|
2022-01-26 13:40:58 +00:00
|
|
|
return ve.ui.MWTransclusionDialog.super.prototype.getActionProcess.call( this, action );
|
2014-07-14 21:32:49 +00:00
|
|
|
};
|
|
|
|
|
2021-05-07 10:40:36 +00:00
|
|
|
/**
|
|
|
|
* Update the widgets in the dialog's action bar.
|
2021-06-18 13:51:25 +00:00
|
|
|
*
|
|
|
|
* @private
|
2021-05-07 10:40:36 +00:00
|
|
|
*/
|
|
|
|
ve.ui.MWTransclusionDialog.prototype.updateActionSet = function () {
|
2024-05-21 14:22:56 +00:00
|
|
|
const backButton = this.actions.get( { flags: [ 'back' ] } ).pop(),
|
2021-06-02 09:59:38 +00:00
|
|
|
saveButton = this.actions.get( { actions: [ 'done' ] } ).pop();
|
|
|
|
|
|
|
|
if ( saveButton && this.getMode() === 'edit' ) {
|
|
|
|
saveButton.setLabel( ve.msg( 'visualeditor-dialog-transclusion-action-save' ) );
|
|
|
|
}
|
2021-05-07 10:40:36 +00:00
|
|
|
|
2024-05-21 14:22:56 +00:00
|
|
|
const closeButton = this.actions.get( { flags: [ 'close' ] } ).pop(),
|
2022-06-15 12:28:22 +00:00
|
|
|
canGoBack = this.getMode() === 'insert' && this.canGoBack && !this.transclusionModel.isEmpty();
|
2021-05-07 10:40:36 +00:00
|
|
|
|
2022-06-15 12:28:22 +00:00
|
|
|
closeButton.toggle( !canGoBack );
|
|
|
|
backButton.toggle( canGoBack );
|
2021-05-07 10:40:36 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Revert the dialog back to its initial state.
|
2021-06-18 13:51:25 +00:00
|
|
|
*
|
|
|
|
* @private
|
2021-05-07 10:40:36 +00:00
|
|
|
*/
|
|
|
|
ve.ui.MWTransclusionDialog.prototype.resetDialog = function () {
|
|
|
|
this.transclusionModel.reset();
|
|
|
|
this.bookletLayout.clearPages();
|
2024-05-21 14:22:56 +00:00
|
|
|
const placeholderPage = new ve.dm.MWTemplatePlaceholderModel( this.transclusionModel );
|
2022-07-06 12:40:06 +00:00
|
|
|
this.transclusionModel.addPart( placeholderPage )
|
2024-04-30 16:44:25 +00:00
|
|
|
.done( () => {
|
2024-05-01 12:32:49 +00:00
|
|
|
this.bookletLayout.focusPart( placeholderPage.getId() );
|
|
|
|
this.autoExpandSidebar();
|
2021-05-07 10:40:36 +00:00
|
|
|
} );
|
|
|
|
};
|
|
|
|
|
2013-11-05 00:29:50 +00:00
|
|
|
/**
|
|
|
|
* @inheritdoc
|
|
|
|
*/
|
|
|
|
ve.ui.MWTransclusionDialog.prototype.initialize = function () {
|
|
|
|
// Parent method
|
2014-04-10 19:07:40 +00:00
|
|
|
ve.ui.MWTransclusionDialog.super.prototype.initialize.call( this );
|
2013-11-05 00:29:50 +00:00
|
|
|
|
2022-01-21 14:04:54 +00:00
|
|
|
this.setupHotkeyTriggers();
|
|
|
|
|
2021-12-06 13:11:14 +00:00
|
|
|
// multipart message gets attached in onReplacePart()
|
2021-08-23 11:25:15 +00:00
|
|
|
this.multipartMessage = new OO.ui.MessageWidget( {
|
2021-12-02 15:42:47 +00:00
|
|
|
label: mw.message( 'visualeditor-dialog-transclusion-multipart-message' ).parseDom(),
|
2021-08-23 11:25:15 +00:00
|
|
|
classes: [ 've-ui-mwTransclusionDialog-multipart-message' ]
|
|
|
|
} );
|
2021-12-02 15:42:47 +00:00
|
|
|
ve.targetLinksToNewWindow( this.multipartMessage.$element[ 0 ] );
|
2021-08-23 11:25:15 +00:00
|
|
|
|
2024-05-21 14:22:56 +00:00
|
|
|
const helpPopup = new ve.ui.MWFloatingHelpElement( {
|
2022-06-10 15:15:18 +00:00
|
|
|
label: mw.message( 'visualeditor-dialog-transclusion-help-title' ).text(),
|
|
|
|
title: mw.message( 'visualeditor-dialog-transclusion-help-title' ).text(),
|
|
|
|
$message: new OO.ui.FieldsetLayout( {
|
|
|
|
items: [
|
|
|
|
new OO.ui.LabelWidget( {
|
|
|
|
label: mw.message( 'visualeditor-dialog-transclusion-help-message' ).text()
|
|
|
|
} ),
|
|
|
|
this.getMessageButton( 'visualeditor-dialog-transclusion-help-page-help', 'helpNotice' ),
|
2022-06-15 11:14:50 +00:00
|
|
|
this.getMessageButton( 'visualeditor-dialog-transclusion-help-page-shortcuts', 'keyboard' )
|
2022-06-10 15:15:18 +00:00
|
|
|
],
|
|
|
|
classes: [ 've-ui-mwTransclusionDialog-floatingHelpElement-fieldsetLayout' ]
|
|
|
|
} ).$element
|
|
|
|
} );
|
|
|
|
helpPopup.$element.addClass( 've-ui-mwTransclusionDialog-floatingHelpElement' );
|
|
|
|
helpPopup.$element.appendTo( this.$body );
|
2021-11-04 17:48:36 +00:00
|
|
|
|
2013-11-05 00:29:50 +00:00
|
|
|
// Events
|
2022-06-10 15:15:18 +00:00
|
|
|
this.getManager().connect( this, { resize: ve.debounce( this.onWindowResize.bind( this ) ) } );
|
2021-08-05 14:31:56 +00:00
|
|
|
this.bookletLayout.getOutlineControls().connect( this, {
|
2022-06-28 14:10:12 +00:00
|
|
|
addTemplate: 'addTemplatePlaceholder',
|
|
|
|
addWikitext: 'addWikitext',
|
2021-08-05 14:31:56 +00:00
|
|
|
move: 'onOutlineControlsMove',
|
|
|
|
remove: 'onOutlineControlsRemove'
|
|
|
|
} );
|
2014-04-17 22:03:43 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @inheritdoc
|
|
|
|
*/
|
2014-05-31 04:47:08 +00:00
|
|
|
ve.ui.MWTransclusionDialog.prototype.getSetupProcess = function ( data ) {
|
|
|
|
return ve.ui.MWTransclusionDialog.super.prototype.getSetupProcess.call( this, data )
|
2024-04-30 21:25:39 +00:00
|
|
|
.next( () => {
|
2022-06-21 12:22:47 +00:00
|
|
|
this.bookletLayout.getOutlineControls().toggle( !this.transclusionModel.isSingleTemplate() );
|
|
|
|
this.$element.toggleClass(
|
|
|
|
've-ui-mwTransclusionDialog-single-transclusion',
|
|
|
|
this.transclusionModel.isSingleTemplate()
|
|
|
|
);
|
2021-08-05 14:31:56 +00:00
|
|
|
|
2017-06-14 15:52:18 +00:00
|
|
|
this.updateModeActionState();
|
2021-08-06 10:28:59 +00:00
|
|
|
this.autoExpandSidebar();
|
2022-08-30 12:15:50 +00:00
|
|
|
|
|
|
|
if ( !this.transclusionModel.isSingleTemplate() ) {
|
|
|
|
this.sidebar.hideAllUnusedParameters();
|
|
|
|
}
|
|
|
|
// We can do this only after the widget is visible on screen
|
|
|
|
this.sidebar.initializeAllStickyHeaderHeights();
|
2024-04-30 21:25:39 +00:00
|
|
|
} );
|
2013-11-05 00:29:50 +00:00
|
|
|
};
|
|
|
|
|
2021-07-23 15:07:39 +00:00
|
|
|
/**
|
|
|
|
* @private
|
|
|
|
*/
|
|
|
|
ve.ui.MWTransclusionDialog.prototype.onWindowResize = function () {
|
2022-06-21 12:22:47 +00:00
|
|
|
if ( this.transclusionModel ) {
|
2021-08-06 10:28:59 +00:00
|
|
|
this.autoExpandSidebar();
|
2022-05-27 13:01:47 +00:00
|
|
|
|
2024-04-30 16:44:25 +00:00
|
|
|
this.bookletLayout.getPagesOrdered().forEach( ( page ) => {
|
2022-06-10 15:15:18 +00:00
|
|
|
if ( page instanceof ve.ui.MWParameterPage ) {
|
|
|
|
page.updateSize();
|
|
|
|
}
|
|
|
|
} );
|
2021-07-23 15:07:39 +00:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2021-05-03 10:40:02 +00:00
|
|
|
/**
|
|
|
|
* @inheritdoc
|
|
|
|
*/
|
|
|
|
ve.ui.MWTransclusionDialog.prototype.getSizeProperties = function () {
|
2022-06-10 15:15:18 +00:00
|
|
|
return ve.extendObject(
|
|
|
|
{ height: '90%' },
|
|
|
|
ve.ui.MWTransclusionDialog.super.prototype.getSizeProperties.call( this )
|
|
|
|
);
|
2021-05-03 10:40:02 +00:00
|
|
|
};
|
|
|
|
|
2021-11-04 17:48:36 +00:00
|
|
|
/**
|
|
|
|
* Converts a message link into an OO.ui.ButtonWidget with an icon.
|
|
|
|
*
|
|
|
|
* @private
|
|
|
|
* @param {string} message i18n message key
|
|
|
|
* @param {string} icon icon name
|
|
|
|
* @return {OO.ui.ButtonWidget}
|
|
|
|
*/
|
|
|
|
ve.ui.MWTemplateDialog.prototype.getMessageButton = function ( message, icon ) {
|
|
|
|
// Messages that can be used here:
|
|
|
|
// * visualeditor-dialog-transclusion-help-page-help
|
2022-06-15 11:14:50 +00:00
|
|
|
// * visualeditor-dialog-transclusion-help-page-shortcuts
|
2024-05-21 14:22:56 +00:00
|
|
|
const $link = mw.message( message ).parseDom(),
|
2021-11-04 17:48:36 +00:00
|
|
|
button = new OO.ui.ButtonWidget( {
|
|
|
|
label: $link.text(),
|
|
|
|
href: $link.attr( 'href' ),
|
|
|
|
target: '_blank',
|
|
|
|
flags: 'progressive',
|
|
|
|
icon: icon,
|
|
|
|
framed: false
|
|
|
|
} );
|
|
|
|
button.$button.attr( 'role', 'link' );
|
|
|
|
return button;
|
|
|
|
};
|
|
|
|
|
2014-03-04 22:56:14 +00:00
|
|
|
/* Registration */
|
|
|
|
|
2014-04-21 22:31:21 +00:00
|
|
|
ve.ui.windowFactory.register( ve.ui.MWTransclusionDialog );
|