2018-09-26 12:49:10 +00:00
|
|
|
/*!
|
2014-04-10 19:07:40 +00:00
|
|
|
* VisualEditor user interface MWTemplateDialog class.
|
|
|
|
*
|
2020-01-08 17:13:04 +00:00
|
|
|
* @copyright 2011-2020 VisualEditor Team and others; see AUTHORS.txt
|
2014-04-10 19:07:40 +00:00
|
|
|
* @license The MIT License (MIT); see LICENSE.txt
|
|
|
|
*/
|
|
|
|
|
|
|
|
/**
|
2021-06-03 16:25:15 +00:00
|
|
|
* Abstract base class for dialogs that allow to insert and edit 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. Currently used for:
|
|
|
|
* - {@see ve.ui.MWTransclusionDialog} for arbitrary transclusions. Registered via the name
|
|
|
|
* "transclusion".
|
|
|
|
* - {@see ve.ui.MWCitationDialog} in the Cite extension for the predefined citation types from
|
|
|
|
* [[MediaWiki:visualeditor-cite-tool-definition.json]]. These are strictly limited to a single
|
|
|
|
* template invocation. Registered via the name "cite".
|
2014-04-10 19:07:40 +00:00
|
|
|
*
|
|
|
|
* @class
|
|
|
|
* @abstract
|
2014-04-24 00:22:45 +00:00
|
|
|
* @extends ve.ui.NodeDialog
|
2014-04-10 19:07:40 +00:00
|
|
|
*
|
|
|
|
* @constructor
|
|
|
|
* @param {Object} [config] Configuration options
|
|
|
|
*/
|
2014-08-21 00:50:54 +00:00
|
|
|
ve.ui.MWTemplateDialog = function VeUiMWTemplateDialog( config ) {
|
2014-04-10 19:07:40 +00:00
|
|
|
// Parent constructor
|
2014-08-21 00:50:54 +00:00
|
|
|
ve.ui.MWTemplateDialog.super.call( this, config );
|
2014-04-10 19:07:40 +00:00
|
|
|
|
|
|
|
// Properties
|
2014-04-24 00:22:45 +00:00
|
|
|
this.transclusionModel = null;
|
2014-04-10 19:07:40 +00:00
|
|
|
this.loaded = false;
|
2014-12-09 23:11:41 +00:00
|
|
|
this.altered = false;
|
2014-04-10 19:07:40 +00:00
|
|
|
this.preventReselection = false;
|
2021-06-25 16:23:38 +00:00
|
|
|
this.expandedParamList = {};
|
2021-08-19 08:13:16 +00:00
|
|
|
this.useNewSidebar = mw.config.get( 'wgVisualEditorConfig' ).transclusionDialogNewSidebar;
|
2014-08-27 15:44:07 +00:00
|
|
|
|
2015-07-22 22:13:09 +00:00
|
|
|
this.confirmOverlay = new ve.ui.Overlay( { classes: [ 've-ui-overlay-global' ] } );
|
2014-11-21 21:58:53 +00:00
|
|
|
this.confirmDialogs = new ve.ui.WindowManager( { factory: ve.ui.windowFactory, isolate: true } );
|
2014-08-27 15:44:07 +00:00
|
|
|
this.confirmOverlay.$element.append( this.confirmDialogs.$element );
|
2018-12-13 16:44:25 +00:00
|
|
|
$( document.body ).append( this.confirmOverlay.$element );
|
2014-04-10 19:07:40 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
/* Inheritance */
|
|
|
|
|
2014-04-24 00:22:45 +00:00
|
|
|
OO.inheritClass( ve.ui.MWTemplateDialog, ve.ui.NodeDialog );
|
2014-04-10 19:07:40 +00:00
|
|
|
|
|
|
|
/* Static Properties */
|
|
|
|
|
2014-04-24 00:22:45 +00:00
|
|
|
ve.ui.MWTemplateDialog.static.modelClasses = [ ve.dm.MWTransclusionNode ];
|
2014-04-10 19:07:40 +00:00
|
|
|
|
2014-04-24 00:22:45 +00:00
|
|
|
/**
|
2021-07-16 12:24:46 +00:00
|
|
|
* Configuration for the {@see OO.ui.BookletLayout} used in this dialog.
|
2014-04-24 00:22:45 +00:00
|
|
|
*
|
|
|
|
* @static
|
|
|
|
* @property {Object}
|
|
|
|
* @inheritable
|
|
|
|
*/
|
2014-04-10 19:07:40 +00:00
|
|
|
ve.ui.MWTemplateDialog.static.bookletLayoutConfig = {
|
2014-08-22 20:50:48 +00:00
|
|
|
continuous: true,
|
|
|
|
outlined: false
|
2014-04-10 19:07:40 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
/* Methods */
|
|
|
|
|
|
|
|
/**
|
2017-06-01 14:28:15 +00:00
|
|
|
* @inheritdoc
|
2014-04-10 19:07:40 +00:00
|
|
|
*/
|
2017-06-01 14:28:15 +00:00
|
|
|
ve.ui.MWTemplateDialog.prototype.getReadyProcess = function ( data ) {
|
|
|
|
return ve.ui.MWTemplateDialog.super.prototype.getReadyProcess.call( this, data )
|
|
|
|
.next( function () {
|
|
|
|
this.bookletLayout.focus( 1 );
|
2021-04-22 16:59:30 +00:00
|
|
|
|
|
|
|
this.bookletLayout.stackLayout.getItems().forEach( function ( page ) {
|
|
|
|
if ( page instanceof ve.ui.MWParameterPage ) {
|
|
|
|
page.updateSize();
|
|
|
|
}
|
|
|
|
} );
|
2017-06-01 14:28:15 +00:00
|
|
|
}, this );
|
2014-04-10 19:07:40 +00:00
|
|
|
};
|
|
|
|
|
2014-12-09 23:11:41 +00:00
|
|
|
/**
|
|
|
|
* Called when the transclusion model changes. E.g. parts changes, parameter values changes.
|
2021-06-18 13:51:25 +00:00
|
|
|
*
|
|
|
|
* @private
|
2014-12-09 23:11:41 +00:00
|
|
|
*/
|
|
|
|
ve.ui.MWTemplateDialog.prototype.onTransclusionModelChange = function () {
|
|
|
|
if ( this.loaded ) {
|
|
|
|
this.altered = true;
|
|
|
|
this.setApplicableStatus();
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2014-04-10 19:07:40 +00:00
|
|
|
/**
|
|
|
|
* Handle parts being replaced.
|
|
|
|
*
|
2021-06-18 13:51:25 +00:00
|
|
|
* @protected
|
2021-07-16 12:24:46 +00:00
|
|
|
* @param {ve.dm.MWTransclusionPartModel|null} removed Removed part
|
|
|
|
* @param {ve.dm.MWTransclusionPartModel|null} added Added part
|
2014-04-10 19:07:40 +00:00
|
|
|
*/
|
|
|
|
ve.ui.MWTemplateDialog.prototype.onReplacePart = function ( removed, added ) {
|
2021-08-18 12:52:11 +00:00
|
|
|
var reselect,
|
2014-04-10 19:07:40 +00:00
|
|
|
removePages = [];
|
|
|
|
|
|
|
|
if ( removed ) {
|
|
|
|
// Remove parameter pages of removed templates
|
2021-06-21 08:10:27 +00:00
|
|
|
var partPage = this.bookletLayout.getPage( removed.getId() );
|
2014-04-10 19:07:40 +00:00
|
|
|
if ( removed instanceof ve.dm.MWTemplateModel ) {
|
2021-06-21 08:10:27 +00:00
|
|
|
var params = removed.getParameters();
|
|
|
|
for ( var name in params ) {
|
2015-08-19 17:33:02 +00:00
|
|
|
removePages.push( this.bookletLayout.getPage( params[ name ].getId() ) );
|
2021-06-25 16:23:38 +00:00
|
|
|
delete this.expandedParamList[ params[ name ].getId() ];
|
2014-04-10 19:07:40 +00:00
|
|
|
}
|
|
|
|
removed.disconnect( this );
|
|
|
|
}
|
|
|
|
if ( this.loaded && !this.preventReselection && partPage.isActive() ) {
|
2021-08-20 09:22:17 +00:00
|
|
|
var closestPage = this.bookletLayout.findClosestPage( partPage );
|
|
|
|
reselect = closestPage && closestPage.getName();
|
2014-04-10 19:07:40 +00:00
|
|
|
}
|
|
|
|
removePages.push( partPage );
|
|
|
|
this.bookletLayout.removePages( removePages );
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( added ) {
|
2021-06-21 08:10:27 +00:00
|
|
|
var page = this.getPageFromPart( added );
|
2014-04-10 19:07:40 +00:00
|
|
|
if ( page ) {
|
2014-04-24 00:22:45 +00:00
|
|
|
this.bookletLayout.addPages( [ page ], this.transclusionModel.getIndex( added ) );
|
2014-04-10 19:07:40 +00:00
|
|
|
if ( reselect ) {
|
|
|
|
// Use added page instead of closest page
|
2021-08-19 13:44:35 +00:00
|
|
|
reselect = added.getId();
|
2014-04-10 19:07:40 +00:00
|
|
|
}
|
2021-08-18 12:52:11 +00:00
|
|
|
|
2014-04-10 19:07:40 +00:00
|
|
|
if ( added instanceof ve.dm.MWTemplateModel ) {
|
2021-08-18 12:52:11 +00:00
|
|
|
// Prevent selection changes while parameters are added
|
2014-04-10 19:07:40 +00:00
|
|
|
this.preventReselection = true;
|
2021-08-18 12:52:11 +00:00
|
|
|
|
|
|
|
// Add existing params to templates (the template might be being moved)
|
|
|
|
var names = added.getOrderedParameterNames();
|
2021-06-21 08:10:27 +00:00
|
|
|
for ( var i = 0; i < names.length; i++ ) {
|
2021-06-04 12:04:10 +00:00
|
|
|
this.onAddParameter( added.getParameter( names[ i ] ) );
|
2014-04-10 19:07:40 +00:00
|
|
|
}
|
2014-08-22 20:50:48 +00:00
|
|
|
added.connect( this, { add: 'onAddParameter', remove: 'onRemoveParameter' } );
|
2014-04-10 19:07:40 +00:00
|
|
|
|
2021-08-18 12:52:11 +00:00
|
|
|
// Add required and suggested params to user created templates
|
|
|
|
var shouldAddPlaceholder = this.loaded && added.addPromptedParameters() === 0;
|
|
|
|
|
2014-04-10 19:07:40 +00:00
|
|
|
this.preventReselection = false;
|
2021-08-18 12:52:11 +00:00
|
|
|
|
2014-04-10 19:07:40 +00:00
|
|
|
if ( names.length ) {
|
2021-08-18 12:52:11 +00:00
|
|
|
// Focus the first element when parameters are present
|
2021-08-19 13:44:35 +00:00
|
|
|
reselect = added.getParameter( names[ 0 ] ).getId();
|
2021-08-23 13:44:28 +00:00
|
|
|
} else if ( shouldAddPlaceholder && !this.useNewSidebar ) {
|
2021-07-16 12:26:07 +00:00
|
|
|
page.addPlaceholderParameter();
|
2014-04-10 19:07:40 +00:00
|
|
|
}
|
2021-08-18 13:00:48 +00:00
|
|
|
|
2021-08-23 13:44:28 +00:00
|
|
|
if ( this.useNewSidebar ) {
|
2021-08-18 13:00:48 +00:00
|
|
|
var documentedParameters = added.getSpec().getDocumentedParameterOrder(),
|
|
|
|
undocumentedParameters = added.getSpec().getUndocumentedParameterNames();
|
|
|
|
|
|
|
|
if ( !documentedParameters.length || undocumentedParameters.length ) {
|
|
|
|
page.addPlaceholderParameter();
|
|
|
|
}
|
|
|
|
}
|
2014-04-10 19:07:40 +00:00
|
|
|
}
|
|
|
|
}
|
2021-08-19 13:44:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if ( reselect ) {
|
2021-08-27 17:07:17 +00:00
|
|
|
this.focusPart( reselect );
|
2014-04-10 19:07:40 +00:00
|
|
|
}
|
2014-07-14 21:32:49 +00:00
|
|
|
|
2014-12-09 23:11:41 +00:00
|
|
|
if ( this.loaded && ( added || removed ) ) {
|
|
|
|
this.altered = true;
|
|
|
|
}
|
|
|
|
|
2014-10-24 22:14:01 +00:00
|
|
|
this.setApplicableStatus();
|
2014-04-10 19:07:40 +00:00
|
|
|
this.updateTitle();
|
|
|
|
};
|
|
|
|
|
2021-06-25 16:23:38 +00:00
|
|
|
/**
|
|
|
|
* Respond to showAll event in the placeholder page.
|
|
|
|
* Cache this so we can make sure the parameter list is expanded
|
|
|
|
* when we next load this same pageId placeholder.
|
|
|
|
*
|
|
|
|
* @param {string} pageId Page Id
|
|
|
|
*/
|
|
|
|
ve.ui.MWTemplateDialog.prototype.onParameterPlaceholderShowAll = function ( pageId ) {
|
|
|
|
this.expandedParamList[ pageId ] = true;
|
|
|
|
};
|
|
|
|
|
2014-04-10 19:07:40 +00:00
|
|
|
/**
|
|
|
|
* Handle add param events.
|
|
|
|
*
|
2021-06-18 13:51:25 +00:00
|
|
|
* @private
|
2014-04-10 19:07:40 +00:00
|
|
|
* @param {ve.dm.MWParameterModel} param Added param
|
|
|
|
*/
|
|
|
|
ve.ui.MWTemplateDialog.prototype.onAddParameter = function ( param ) {
|
|
|
|
var page;
|
|
|
|
|
|
|
|
if ( param.getName() ) {
|
2019-02-08 19:18:22 +00:00
|
|
|
page = new ve.ui.MWParameterPage( param, param.getId(), { $overlay: this.$overlay, readOnly: this.isReadOnly() } );
|
2021-08-06 11:56:40 +00:00
|
|
|
} else if ( this.useNewSidebar ) {
|
|
|
|
page = new ve.ui.MWAddParameterPage( param, param.getId(), {
|
|
|
|
$overlay: this.$overlay
|
2021-09-07 15:53:37 +00:00
|
|
|
} )
|
|
|
|
.connect( this, {
|
|
|
|
focusTemplateParameterById: 'focusPart'
|
|
|
|
} );
|
2014-04-10 19:07:40 +00:00
|
|
|
} else {
|
2021-06-07 14:03:48 +00:00
|
|
|
// This branch is triggered when we receive a synthetic placeholder event with name=''.
|
2021-06-25 16:23:38 +00:00
|
|
|
page = new ve.ui.MWParameterPlaceholderPage( param, param.getId(), {
|
|
|
|
$overlay: this.$overlay,
|
|
|
|
expandedParamList: !!this.expandedParamList[ param.getId() ]
|
|
|
|
} )
|
2021-09-07 15:53:37 +00:00
|
|
|
.connect( this, {
|
|
|
|
showAll: 'onParameterPlaceholderShowAll',
|
|
|
|
focusTemplateParameterById: 'focusPart'
|
|
|
|
} );
|
2014-04-10 19:07:40 +00:00
|
|
|
}
|
2014-04-24 00:22:45 +00:00
|
|
|
this.bookletLayout.addPages( [ page ], this.transclusionModel.getIndex( param ) );
|
2014-12-09 23:11:41 +00:00
|
|
|
if ( this.loaded ) {
|
2021-09-07 15:53:37 +00:00
|
|
|
// Unconditionally focus parameter placeholders. Named parameters must be focused manually.
|
|
|
|
if ( !this.preventReselection && !param.getName() ) {
|
2021-08-27 17:07:17 +00:00
|
|
|
this.focusPart( param.getId() );
|
2014-12-09 23:11:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
this.altered = true;
|
|
|
|
this.setApplicableStatus();
|
2021-04-22 16:59:30 +00:00
|
|
|
|
|
|
|
if ( page instanceof ve.ui.MWParameterPage ) {
|
|
|
|
page.updateSize();
|
|
|
|
}
|
2014-04-10 19:07:40 +00:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Handle remove param events.
|
|
|
|
*
|
2021-06-18 13:51:25 +00:00
|
|
|
* @private
|
2014-04-10 19:07:40 +00:00
|
|
|
* @param {ve.dm.MWParameterModel} param Removed param
|
|
|
|
*/
|
|
|
|
ve.ui.MWTemplateDialog.prototype.onRemoveParameter = function ( param ) {
|
|
|
|
var page = this.bookletLayout.getPage( param.getId() ),
|
2017-09-05 02:08:36 +00:00
|
|
|
reselect = this.bookletLayout.findClosestPage( page );
|
2014-04-10 19:07:40 +00:00
|
|
|
|
2018-06-21 18:45:06 +00:00
|
|
|
// Select the desired page first. Otherwise, if the page we are removing is selected,
|
|
|
|
// OOUI will try to select the first page after it is removed, and scroll to the top.
|
2021-09-07 15:53:37 +00:00
|
|
|
if ( this.loaded && !this.preventReselection && !this.pocSidebar ) {
|
2021-08-27 17:07:17 +00:00
|
|
|
this.focusPart( reselect.getName() );
|
2018-06-21 18:45:06 +00:00
|
|
|
}
|
|
|
|
|
2014-04-10 19:07:40 +00:00
|
|
|
this.bookletLayout.removePages( [ page ] );
|
2014-12-09 23:11:41 +00:00
|
|
|
|
2018-06-21 18:45:06 +00:00
|
|
|
if ( this.loaded ) {
|
2014-12-09 23:11:41 +00:00
|
|
|
this.altered = true;
|
|
|
|
this.setApplicableStatus();
|
2014-04-10 19:07:40 +00:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
2014-10-24 22:14:01 +00:00
|
|
|
* Sets transclusion applicable status
|
2014-04-10 19:07:40 +00:00
|
|
|
*
|
|
|
|
* If the transclusion is empty or only contains a placeholder it will not be insertable.
|
2014-10-24 22:14:01 +00:00
|
|
|
* If the transclusion only contains a placeholder it will not be editable.
|
2021-06-18 13:51:25 +00:00
|
|
|
*
|
|
|
|
* @private
|
2014-04-10 19:07:40 +00:00
|
|
|
*/
|
2014-10-24 22:14:01 +00:00
|
|
|
ve.ui.MWTemplateDialog.prototype.setApplicableStatus = function () {
|
2021-06-18 12:14:18 +00:00
|
|
|
var parts = this.transclusionModel && this.transclusionModel.getParts(),
|
|
|
|
startsWithPlaceholder = parts && parts[ 0 ] instanceof ve.dm.MWTemplatePlaceholderModel,
|
|
|
|
canSave = !startsWithPlaceholder;
|
2014-04-10 19:07:40 +00:00
|
|
|
|
2021-06-18 12:14:18 +00:00
|
|
|
this.actions.setAbilities( { done: canSave && this.altered } );
|
2014-04-10 19:07:40 +00:00
|
|
|
};
|
|
|
|
|
2014-07-14 21:32:49 +00:00
|
|
|
/**
|
|
|
|
* @inheritdoc
|
|
|
|
*/
|
|
|
|
ve.ui.MWTemplateDialog.prototype.getBodyHeight = function () {
|
2014-07-19 02:14:30 +00:00
|
|
|
return 400;
|
2014-07-14 21:32:49 +00:00
|
|
|
};
|
|
|
|
|
2014-04-10 19:07:40 +00:00
|
|
|
/**
|
|
|
|
* Get a page for a transclusion part.
|
|
|
|
*
|
2021-06-18 13:51:25 +00:00
|
|
|
* @protected
|
2014-04-10 19:07:40 +00:00
|
|
|
* @param {ve.dm.MWTransclusionModel} part Part to get page for
|
|
|
|
* @return {OO.ui.PageLayout|null} Page for part, null if no matching page could be found
|
|
|
|
*/
|
|
|
|
ve.ui.MWTemplateDialog.prototype.getPageFromPart = function ( part ) {
|
|
|
|
if ( part instanceof ve.dm.MWTemplateModel ) {
|
2019-02-24 12:38:03 +00:00
|
|
|
return new ve.ui.MWTemplatePage( part, part.getId(), { $overlay: this.$overlay, isReadOnly: this.isReadOnly() } );
|
2014-04-10 19:07:40 +00:00
|
|
|
} else if ( part instanceof ve.dm.MWTemplatePlaceholderModel ) {
|
2014-10-28 00:36:19 +00:00
|
|
|
return new ve.ui.MWTemplatePlaceholderPage(
|
|
|
|
part,
|
|
|
|
part.getId(),
|
2015-04-09 23:47:15 +00:00
|
|
|
{ $overlay: this.$overlay }
|
2014-10-28 00:36:19 +00:00
|
|
|
);
|
2014-04-10 19:07:40 +00:00
|
|
|
}
|
|
|
|
return null;
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
2014-04-24 00:22:45 +00:00
|
|
|
* @inheritdoc
|
2014-04-10 19:07:40 +00:00
|
|
|
*/
|
2014-04-24 00:22:45 +00:00
|
|
|
ve.ui.MWTemplateDialog.prototype.getSelectedNode = function ( data ) {
|
|
|
|
var selectedNode = ve.ui.MWTemplateDialog.super.prototype.getSelectedNode.call( this );
|
|
|
|
|
|
|
|
// Data initialization
|
|
|
|
data = data || {};
|
|
|
|
|
|
|
|
// Require template to match if specified
|
|
|
|
if ( selectedNode && data.template && !selectedNode.isSingleTemplate( data.template ) ) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
return selectedNode;
|
2014-04-10 19:07:40 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Update the dialog title.
|
2021-06-18 13:51:25 +00:00
|
|
|
*
|
|
|
|
* @protected
|
2014-04-10 19:07:40 +00:00
|
|
|
*/
|
|
|
|
ve.ui.MWTemplateDialog.prototype.updateTitle = function () {
|
2021-06-02 10:06:36 +00:00
|
|
|
var parts = this.transclusionModel && this.transclusionModel.getParts(),
|
|
|
|
title = ve.msg( 'visualeditor-dialog-transclusion-loading' );
|
|
|
|
|
2021-07-16 12:26:07 +00:00
|
|
|
if ( parts && parts.length === 1 ) {
|
2021-06-02 10:06:36 +00:00
|
|
|
if ( parts[ 0 ] instanceof ve.dm.MWTemplateModel ) {
|
|
|
|
title = ve.msg(
|
|
|
|
this.getMode() === 'insert' ?
|
|
|
|
'visualeditor-dialog-transclusion-title-insert-known-template' :
|
|
|
|
'visualeditor-dialog-transclusion-title-edit-known-template',
|
|
|
|
parts[ 0 ].getSpec().getLabel()
|
|
|
|
);
|
|
|
|
} else {
|
|
|
|
title = ve.msg( 'visualeditor-dialog-transclusion-title-insert-template' );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
this.title.setLabel( title );
|
2014-04-10 19:07:40 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @inheritdoc
|
|
|
|
*/
|
|
|
|
ve.ui.MWTemplateDialog.prototype.initialize = function () {
|
|
|
|
// Parent method
|
|
|
|
ve.ui.MWTemplateDialog.super.prototype.initialize.call( this );
|
|
|
|
|
|
|
|
// Properties
|
2015-04-09 23:47:15 +00:00
|
|
|
this.bookletLayout = new OO.ui.BookletLayout( this.constructor.static.bookletLayoutConfig );
|
2014-04-10 19:07:40 +00:00
|
|
|
|
|
|
|
// Initialization
|
2014-08-21 00:50:54 +00:00
|
|
|
this.$content.addClass( 've-ui-mwTemplateDialog' );
|
2018-06-21 21:08:17 +00:00
|
|
|
// bookletLayout is appended after the form has been built in getSetupProcess for performance
|
2014-04-10 19:07:40 +00:00
|
|
|
};
|
|
|
|
|
2014-08-27 15:44:07 +00:00
|
|
|
/**
|
|
|
|
* If the user has left blank required parameters, confirm that they actually want to do this.
|
|
|
|
* If no required parameters were left blank, or if they were but the user decided to go ahead
|
|
|
|
* anyway, the returned deferred will be resolved.
|
|
|
|
* Otherwise, the returned deferred will be rejected.
|
2015-08-19 18:21:01 +00:00
|
|
|
*
|
2021-06-18 13:51:25 +00:00
|
|
|
* @private
|
2015-08-19 18:09:34 +00:00
|
|
|
* @return {jQuery.Deferred}
|
2014-08-27 15:44:07 +00:00
|
|
|
*/
|
|
|
|
ve.ui.MWTemplateDialog.prototype.checkRequiredParameters = function () {
|
2015-01-31 00:41:37 +00:00
|
|
|
var blankRequired = [],
|
2019-11-02 05:06:28 +00:00
|
|
|
deferred = ve.createDeferred();
|
2019-02-08 19:18:22 +00:00
|
|
|
|
|
|
|
this.bookletLayout.stackLayout.getItems().forEach( function ( page ) {
|
|
|
|
if ( !( page instanceof ve.ui.MWParameterPage ) ) {
|
|
|
|
return;
|
2014-08-27 15:44:07 +00:00
|
|
|
}
|
2019-02-08 19:18:22 +00:00
|
|
|
if ( page.parameter.isRequired() && !page.valueInput.getValue() ) {
|
2014-08-27 15:44:07 +00:00
|
|
|
blankRequired.push( mw.msg(
|
|
|
|
'quotation-marks',
|
2019-02-08 19:18:22 +00:00
|
|
|
page.parameter.template.getSpec().getParameterLabel( page.parameter.getName() )
|
2014-08-27 15:44:07 +00:00
|
|
|
) );
|
|
|
|
}
|
|
|
|
} );
|
|
|
|
if ( blankRequired.length ) {
|
|
|
|
this.confirmDialogs.openWindow( 'requiredparamblankconfirm', {
|
|
|
|
message: mw.msg(
|
|
|
|
'visualeditor-dialog-transclusion-required-parameter-is-blank',
|
|
|
|
mw.language.listToText( blankRequired ),
|
|
|
|
blankRequired.length
|
|
|
|
),
|
|
|
|
title: mw.msg(
|
|
|
|
'visualeditor-dialog-transclusion-required-parameter-dialog-title',
|
|
|
|
blankRequired.length
|
|
|
|
)
|
2017-06-01 22:21:58 +00:00
|
|
|
} ).closed.then( function ( data ) {
|
|
|
|
if ( data.action === 'ok' ) {
|
|
|
|
deferred.resolve();
|
|
|
|
} else {
|
|
|
|
deferred.reject();
|
|
|
|
}
|
2014-08-27 15:44:07 +00:00
|
|
|
} );
|
|
|
|
} else {
|
|
|
|
deferred.resolve();
|
|
|
|
}
|
|
|
|
return deferred.promise();
|
|
|
|
};
|
|
|
|
|
2014-07-14 21:32:49 +00:00
|
|
|
/**
|
|
|
|
* @inheritdoc
|
|
|
|
*/
|
|
|
|
ve.ui.MWTemplateDialog.prototype.getActionProcess = function ( action ) {
|
2021-06-21 08:10:27 +00:00
|
|
|
var dialog = this;
|
2020-12-11 15:09:45 +00:00
|
|
|
|
2021-06-18 08:57:45 +00:00
|
|
|
if ( action === 'done' ) {
|
2014-07-14 21:32:49 +00:00
|
|
|
return new OO.ui.Process( function () {
|
2019-11-02 05:06:28 +00:00
|
|
|
var deferred = ve.createDeferred();
|
2014-12-16 21:14:01 +00:00
|
|
|
dialog.checkRequiredParameters().done( function () {
|
2021-06-18 07:03:16 +00:00
|
|
|
var surfaceModel = dialog.getFragment().getSurface(),
|
|
|
|
obj = dialog.transclusionModel.getPlainObject(),
|
|
|
|
modelPromise = ve.createDeferred().resolve().promise();
|
2014-08-27 15:44:07 +00:00
|
|
|
|
2016-05-15 11:08:13 +00:00
|
|
|
dialog.pushPending();
|
|
|
|
|
2014-12-16 21:14:01 +00:00
|
|
|
if ( dialog.selectedNode instanceof ve.dm.MWTransclusionNode ) {
|
|
|
|
dialog.transclusionModel.updateTransclusionNode( surfaceModel, dialog.selectedNode );
|
2016-05-15 11:08:13 +00:00
|
|
|
// TODO: updating the node could result in the inline/block state change
|
2014-08-27 15:44:07 +00:00
|
|
|
} else if ( obj !== null ) {
|
2014-12-16 21:14:01 +00:00
|
|
|
// Collapse returns a new fragment, so update dialog.fragment
|
|
|
|
dialog.fragment = dialog.getFragment().collapseToEnd();
|
2016-05-15 11:08:13 +00:00
|
|
|
modelPromise = dialog.transclusionModel.insertTransclusionNode( dialog.getFragment() );
|
2014-08-27 15:44:07 +00:00
|
|
|
}
|
|
|
|
|
2020-12-11 15:09:45 +00:00
|
|
|
// TODO tracking will only be implemented temporarily to answer questions on
|
|
|
|
// template usage for the Technical Wishes topic area see T258917
|
2021-06-21 08:10:27 +00:00
|
|
|
var templateEvent = {
|
2020-12-11 15:09:45 +00:00
|
|
|
action: 'save',
|
|
|
|
// eslint-disable-next-line camelcase
|
|
|
|
template_names: []
|
|
|
|
};
|
2021-06-21 08:10:27 +00:00
|
|
|
var editCountBucket = mw.config.get( 'wgUserEditCountBucket' );
|
2021-01-21 12:17:03 +00:00
|
|
|
if ( editCountBucket !== null ) {
|
|
|
|
// eslint-disable-next-line camelcase
|
|
|
|
templateEvent.user_edit_count_bucket = editCountBucket;
|
|
|
|
}
|
2021-06-21 08:10:27 +00:00
|
|
|
for ( var i = 0; i < dialog.transclusionModel.getParts().length; i++ ) {
|
2020-12-11 15:09:45 +00:00
|
|
|
if ( dialog.transclusionModel.getParts()[ i ].getTitle ) {
|
|
|
|
templateEvent.template_names.push( dialog.transclusionModel.getParts()[ i ].getTitle() );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
mw.track( 'event.VisualEditorTemplateDialogUse', templateEvent );
|
|
|
|
|
2016-05-15 11:08:13 +00:00
|
|
|
return modelPromise.then( function () {
|
2017-06-01 22:21:58 +00:00
|
|
|
dialog.close( { action: action } ).closed.always( dialog.popPending.bind( dialog ) );
|
2016-05-15 11:08:13 +00:00
|
|
|
} );
|
2014-12-16 21:14:01 +00:00
|
|
|
} ).always( deferred.resolve );
|
2014-07-14 21:32:49 +00:00
|
|
|
|
2014-08-27 15:44:07 +00:00
|
|
|
return deferred;
|
2014-12-16 21:14:01 +00:00
|
|
|
} );
|
2014-07-14 21:32:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return ve.ui.MWTemplateDialog.super.prototype.getActionProcess.call( this, action );
|
|
|
|
};
|
|
|
|
|
2014-04-10 19:07:40 +00:00
|
|
|
/**
|
|
|
|
* @inheritdoc
|
|
|
|
*/
|
2014-05-31 04:47:08 +00:00
|
|
|
ve.ui.MWTemplateDialog.prototype.getSetupProcess = function ( data ) {
|
2014-07-15 22:49:51 +00:00
|
|
|
data = data || {};
|
2014-05-31 04:47:08 +00:00
|
|
|
return ve.ui.MWTemplateDialog.super.prototype.getSetupProcess.call( this, data )
|
|
|
|
.next( function () {
|
2021-06-21 08:10:27 +00:00
|
|
|
var promise,
|
2018-11-16 00:08:29 +00:00
|
|
|
dialog = this;
|
2014-05-31 04:47:08 +00:00
|
|
|
|
|
|
|
// Properties
|
|
|
|
this.loaded = false;
|
2014-12-09 23:11:41 +00:00
|
|
|
this.altered = false;
|
2018-05-04 13:30:10 +00:00
|
|
|
this.transclusionModel = new ve.dm.MWTransclusionModel( this.getFragment().getDocument() );
|
2014-05-31 04:47:08 +00:00
|
|
|
|
|
|
|
// Events
|
2014-12-09 23:11:41 +00:00
|
|
|
this.transclusionModel.connect( this, {
|
|
|
|
replace: 'onReplacePart',
|
|
|
|
change: 'onTransclusionModelChange'
|
|
|
|
} );
|
2014-05-31 04:47:08 +00:00
|
|
|
|
2016-05-09 22:24:22 +00:00
|
|
|
// Detach the form while building for performance
|
2018-11-16 00:08:29 +00:00
|
|
|
this.bookletLayout.$element.detach();
|
2018-07-18 23:42:09 +00:00
|
|
|
// HACK: Prevent any setPage() calls (from #onReplacePart) from focussing stuff, it messes
|
|
|
|
// with OOUI logic for marking fields as invalid (T199838). We set it back to true below.
|
2018-11-16 00:08:29 +00:00
|
|
|
this.bookletLayout.autoFocus = false;
|
2016-05-09 22:24:22 +00:00
|
|
|
|
2021-08-19 08:13:16 +00:00
|
|
|
if ( this.useNewSidebar ) {
|
2021-07-15 13:01:35 +00:00
|
|
|
// FIXME: This is created at the wrong time. That's why we run into the situation
|
|
|
|
// where an old instance exists. Should be in initialize().
|
2021-07-16 12:37:34 +00:00
|
|
|
if ( !this.pocSidebar ) {
|
2021-08-19 11:02:11 +00:00
|
|
|
this.pocSidebar = new ve.ui.MWTransclusionOutlineContainerWidget();
|
|
|
|
this.pocSidebar.connect( this, {
|
2021-08-31 08:05:25 +00:00
|
|
|
focusPageByName: 'focusPart',
|
|
|
|
filterPagesByName: 'onFilterPagesByName'
|
2021-08-19 11:02:11 +00:00
|
|
|
} );
|
2021-09-01 09:15:37 +00:00
|
|
|
// FIXME: Check if we can merge these two "set"/"focusin" event handlers
|
2021-08-30 15:00:52 +00:00
|
|
|
this.bookletLayout.connect( this, {
|
|
|
|
set: 'onBookletLayoutSetPage'
|
|
|
|
} );
|
2021-09-01 09:15:37 +00:00
|
|
|
this.bookletLayout.stackLayout.$element.on(
|
|
|
|
'focusin',
|
|
|
|
this.onBookletLayoutPageFocused.bind( this )
|
|
|
|
);
|
2021-07-16 12:37:34 +00:00
|
|
|
} else {
|
|
|
|
this.pocSidebar.clear();
|
2021-07-15 13:01:35 +00:00
|
|
|
}
|
2021-07-15 15:07:21 +00:00
|
|
|
this.transclusionModel.connect( this.pocSidebar, {
|
|
|
|
replace: 'onReplacePart',
|
|
|
|
change: [ 'onTransclusionModelChange', this.transclusionModel ]
|
|
|
|
} );
|
2021-06-17 11:20:50 +00:00
|
|
|
}
|
|
|
|
|
2014-05-31 04:47:08 +00:00
|
|
|
// Initialization
|
|
|
|
if ( !this.selectedNode ) {
|
|
|
|
if ( data.template ) {
|
|
|
|
// New specified template
|
2021-06-21 08:10:27 +00:00
|
|
|
var template = ve.dm.MWTemplateModel.newFromName(
|
2014-05-31 04:47:08 +00:00
|
|
|
this.transclusionModel, data.template
|
|
|
|
);
|
2017-06-01 14:28:15 +00:00
|
|
|
promise = this.transclusionModel.addPart( template ).then(
|
2014-10-29 01:43:03 +00:00
|
|
|
this.initializeNewTemplateParameters.bind( this )
|
2014-10-14 19:27:26 +00:00
|
|
|
);
|
2014-05-31 04:47:08 +00:00
|
|
|
} else {
|
|
|
|
// New template placeholder
|
|
|
|
promise = this.transclusionModel.addPart(
|
|
|
|
new ve.dm.MWTemplatePlaceholderModel( this.transclusionModel )
|
|
|
|
);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// Load existing template
|
2020-12-11 15:09:45 +00:00
|
|
|
|
|
|
|
// TODO tracking will only be implemented temporarily to answer questions on
|
|
|
|
// template usage for the Technical Wishes topic area see T258917
|
2021-06-21 08:10:27 +00:00
|
|
|
var templateEvent = {
|
2020-12-11 15:09:45 +00:00
|
|
|
action: 'edit',
|
|
|
|
// eslint-disable-next-line camelcase
|
|
|
|
template_names: []
|
|
|
|
};
|
2021-06-21 08:10:27 +00:00
|
|
|
var editCountBucket = mw.config.get( 'wgUserEditCountBucket' );
|
2021-01-21 12:17:03 +00:00
|
|
|
if ( editCountBucket !== null ) {
|
|
|
|
// eslint-disable-next-line camelcase
|
|
|
|
templateEvent.user_edit_count_bucket = editCountBucket;
|
|
|
|
}
|
2021-06-21 08:10:27 +00:00
|
|
|
for ( var i = 0; i < this.selectedNode.partsList.length; i++ ) {
|
2020-12-11 15:09:45 +00:00
|
|
|
if ( this.selectedNode.partsList[ i ].templatePage ) {
|
|
|
|
templateEvent.template_names.push( this.selectedNode.partsList[ i ].templatePage );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
mw.track( 'event.VisualEditorTemplateDialogUse', templateEvent );
|
|
|
|
|
2014-05-31 04:47:08 +00:00
|
|
|
promise = this.transclusionModel
|
2014-10-07 00:06:02 +00:00
|
|
|
.load( ve.copy( this.selectedNode.getAttribute( 'mw' ) ) )
|
2017-06-01 14:28:15 +00:00
|
|
|
.then( this.initializeTemplateParameters.bind( this ) );
|
2014-05-31 04:47:08 +00:00
|
|
|
}
|
2021-06-18 08:57:45 +00:00
|
|
|
this.actions.setAbilities( { done: false } );
|
2018-06-21 21:08:17 +00:00
|
|
|
|
2018-11-16 00:08:29 +00:00
|
|
|
return promise.then( function () {
|
|
|
|
// Add missing required and suggested parameters to each transclusion.
|
|
|
|
dialog.transclusionModel.addPromptedParameters();
|
2018-06-21 21:08:17 +00:00
|
|
|
|
2018-11-16 00:08:29 +00:00
|
|
|
dialog.loaded = true;
|
|
|
|
dialog.$element.addClass( 've-ui-mwTemplateDialog-ready' );
|
2021-05-17 15:43:49 +00:00
|
|
|
|
2021-07-13 07:07:59 +00:00
|
|
|
dialog.$body.append( dialog.bookletLayout.$element );
|
2021-07-07 08:18:41 +00:00
|
|
|
if ( dialog.pocSidebar ) {
|
2021-06-17 11:20:50 +00:00
|
|
|
// TODO: bookletLayout will be deprecated.
|
2021-07-15 13:01:35 +00:00
|
|
|
var $debugContainer = dialog.bookletLayout.outlinePanel.$element
|
|
|
|
.children( '.ve-ui-mwTemplateDialog-pocSidebar-debug-container' );
|
|
|
|
if ( !$debugContainer.length ) {
|
|
|
|
$debugContainer = $( '<div>' )
|
|
|
|
.addClass( 've-ui-mwTemplateDialog-pocSidebar-debug-container' )
|
|
|
|
.prependTo( dialog.bookletLayout.outlinePanel.$element );
|
|
|
|
}
|
|
|
|
$debugContainer.append(
|
|
|
|
dialog.pocSidebar.$element,
|
|
|
|
dialog.bookletLayout.outlineSelectWidget.$element
|
|
|
|
);
|
2021-08-04 12:37:28 +00:00
|
|
|
dialog.bookletLayout.outlineSelectWidget.toggle( false );
|
2021-05-17 15:43:49 +00:00
|
|
|
}
|
|
|
|
|
2018-11-16 00:08:29 +00:00
|
|
|
dialog.bookletLayout.autoFocus = true;
|
2018-07-18 23:42:09 +00:00
|
|
|
} );
|
2014-05-31 04:47:08 +00:00
|
|
|
}, this );
|
2014-04-10 19:07:40 +00:00
|
|
|
};
|
|
|
|
|
2014-10-07 00:06:02 +00:00
|
|
|
/**
|
|
|
|
* Initialize parameters for new template insertion
|
2021-06-18 13:51:25 +00:00
|
|
|
*
|
|
|
|
* @private
|
2014-10-07 00:06:02 +00:00
|
|
|
*/
|
|
|
|
ve.ui.MWTemplateDialog.prototype.initializeNewTemplateParameters = function () {
|
2021-06-21 08:10:27 +00:00
|
|
|
var parts = this.transclusionModel.getParts();
|
|
|
|
for ( var i = 0; i < parts.length; i++ ) {
|
2015-08-19 17:33:02 +00:00
|
|
|
if ( parts[ i ] instanceof ve.dm.MWTemplateModel ) {
|
|
|
|
parts[ i ].addPromptedParameters();
|
2014-10-07 00:06:02 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Intentionally empty. This is provided for Wikia extensibility.
|
|
|
|
*/
|
|
|
|
ve.ui.MWTemplateDialog.prototype.initializeTemplateParameters = function () {};
|
|
|
|
|
2021-08-05 12:36:10 +00:00
|
|
|
/**
|
|
|
|
* @private
|
|
|
|
*/
|
2021-08-31 08:05:25 +00:00
|
|
|
ve.ui.MWTemplateDialog.prototype.onFilterPagesByName = function ( visibility ) {
|
|
|
|
for ( var pageName in visibility ) {
|
|
|
|
var page = this.bookletLayout.getPage( pageName );
|
2021-08-11 06:06:22 +00:00
|
|
|
if ( page ) {
|
2021-08-31 08:05:25 +00:00
|
|
|
page.toggle( visibility[ pageName ] );
|
2021-08-11 06:06:22 +00:00
|
|
|
}
|
2021-08-05 12:36:10 +00:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2021-08-19 11:02:11 +00:00
|
|
|
/**
|
|
|
|
* @private
|
2021-08-31 08:05:25 +00:00
|
|
|
* @param {string} pageName
|
2021-08-19 11:02:11 +00:00
|
|
|
*/
|
2021-08-31 08:05:25 +00:00
|
|
|
ve.ui.MWTemplateDialog.prototype.focusPart = function ( pageName ) {
|
2021-08-27 17:07:17 +00:00
|
|
|
// The new sidebar does not focus template parameters, only top-level parts
|
2021-08-31 08:05:25 +00:00
|
|
|
if ( this.pocSidebar && pageName.indexOf( '/' ) === -1 ) {
|
2021-08-27 17:07:17 +00:00
|
|
|
// FIXME: This is currently needed because the event that adds a new part to the new sidebar
|
|
|
|
// is executed later than this here.
|
2021-08-31 08:05:25 +00:00
|
|
|
setTimeout( this.pocSidebar.selectPartById.bind( this.pocSidebar, pageName ) );
|
2021-08-27 17:07:17 +00:00
|
|
|
this.bookletLayout.focus();
|
2021-08-31 08:05:25 +00:00
|
|
|
this.bookletLayout.setPage( pageName );
|
2021-08-27 17:07:17 +00:00
|
|
|
} else if ( this.bookletLayout.isOutlined() ) {
|
2021-08-31 08:05:25 +00:00
|
|
|
this.bookletLayout.getOutline().selectItemByData( pageName );
|
2021-08-27 17:07:17 +00:00
|
|
|
} else {
|
2021-08-31 08:05:25 +00:00
|
|
|
this.bookletLayout.setPage( pageName );
|
2021-08-27 17:07:17 +00:00
|
|
|
}
|
2021-08-19 11:02:11 +00:00
|
|
|
};
|
|
|
|
|
2021-08-30 15:00:52 +00:00
|
|
|
/**
|
|
|
|
* @private
|
|
|
|
* @param {OO.ui.PageLayout} page
|
|
|
|
*/
|
|
|
|
ve.ui.MWTemplateDialog.prototype.onBookletLayoutSetPage = function ( page ) {
|
|
|
|
// FIXME: This triggers twice for the same page. Why?
|
|
|
|
|
|
|
|
// The sidebar (currently) can't focus individual template parameters with composite ids like
|
|
|
|
// "part_1/param1". Make sure at least the top-level part is focused.
|
|
|
|
var partId = page.getName().split( '/', 2 )[ 0 ];
|
|
|
|
this.pocSidebar.selectPartById( partId );
|
|
|
|
};
|
|
|
|
|
2021-09-01 09:15:37 +00:00
|
|
|
/**
|
|
|
|
* Modeled after {@see OO.ui.BookletLayout.onStackLayoutFocus}.
|
|
|
|
*
|
|
|
|
* @private
|
|
|
|
* @param {jQuery.Event} e
|
|
|
|
*/
|
|
|
|
ve.ui.MWTemplateDialog.prototype.onBookletLayoutPageFocused = function ( e ) {
|
|
|
|
var $focusedPage = $( e.target ).closest( '.oo-ui-pageLayout' );
|
|
|
|
for ( var pageName in this.bookletLayout.pages ) {
|
|
|
|
if ( this.bookletLayout.getPage( pageName ).$element[ 0 ] === $focusedPage[ 0 ] ) {
|
|
|
|
this.pocSidebar.highlightSubItemByPageName( pageName );
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2014-04-10 19:07:40 +00:00
|
|
|
/**
|
|
|
|
* @inheritdoc
|
|
|
|
*/
|
2014-05-31 04:47:08 +00:00
|
|
|
ve.ui.MWTemplateDialog.prototype.getTeardownProcess = function ( data ) {
|
|
|
|
return ve.ui.MWTemplateDialog.super.prototype.getTeardownProcess.call( this, data )
|
|
|
|
.first( function () {
|
|
|
|
// Cleanup
|
|
|
|
this.$element.removeClass( 've-ui-mwTemplateDialog-ready' );
|
|
|
|
this.transclusionModel.disconnect( this );
|
2021-07-06 13:43:13 +00:00
|
|
|
this.transclusionModel.abortAllApiRequests();
|
2014-05-31 04:47:08 +00:00
|
|
|
this.transclusionModel = null;
|
|
|
|
this.bookletLayout.clearPages();
|
|
|
|
this.content = null;
|
|
|
|
}, this );
|
2014-04-10 19:07:40 +00:00
|
|
|
};
|