mediawiki_arch/extensions/TemplateData/modules/ext.templateDataGenerator.e.../Dialog.js

1254 lines
39 KiB
JavaScript

/**
* TemplateData Dialog
*
* @class
* @extends OO.ui.ProcessDialog
*
* @constructor
* @param {Object} config Dialog configuration object
*/
mw.TemplateData.Dialog = function mwTemplateDataDialog( config ) {
// Parent constructor
mw.TemplateData.Dialog.parent.call( this, config );
this.model = null;
this.modified = false;
this.language = null;
this.availableLanguages = [];
this.selectedParamKey = '';
this.propInputs = {};
this.propFieldLayout = {};
this.isSetup = false;
// Initialize
this.$element.addClass( 'tdg-templateDataDialog' );
};
/* Inheritance */
OO.inheritClass( mw.TemplateData.Dialog, OO.ui.ProcessDialog );
/* Static properties */
mw.TemplateData.Dialog.static.name = 'TemplateDataDialog';
mw.TemplateData.Dialog.static.title = mw.msg( 'templatedata-modal-title' );
mw.TemplateData.Dialog.static.size = 'large';
mw.TemplateData.Dialog.static.actions = [
{
action: 'apply',
label: mw.msg( 'templatedata-modal-button-apply' ),
flags: [ 'primary', 'progressive' ],
modes: 'list'
},
{
action: 'done',
label: mw.msg( 'templatedata-modal-button-done' ),
flags: [ 'primary', 'progressive' ],
modes: [ 'edit', 'maps' ]
},
{
action: 'add',
label: mw.msg( 'templatedata-modal-button-addparam' ),
flags: [ 'progressive' ],
modes: 'list'
},
{
action: 'delete',
label: mw.msg( 'templatedata-modal-button-delparam' ),
modes: 'edit',
flags: 'destructive'
},
{
label: mw.msg( 'templatedata-modal-button-cancel' ),
flags: [ 'safe', 'close' ],
modes: [ 'list', 'error' ]
},
{
action: 'back',
label: mw.msg( 'templatedata-modal-button-back' ),
flags: [ 'safe', 'back' ],
modes: [ 'language', 'add', 'maps' ]
}
];
/**
* Initialize window contents.
*
* The first time the window is opened, #initialize is called so that changes to the window that
* will persist between openings can be made. See #getSetupProcess for a way to make changes each
* time the window opens.
*
* @throws {Error} If not attached to a manager
* @chainable
*/
mw.TemplateData.Dialog.prototype.initialize = function () {
var templateParamsFieldset, addParamFieldlayout, languageActionFieldLayout, templateFormatFieldSet, mapsActionFieldLayout, templateMapsFieldSet;
// Parent method
mw.TemplateData.Dialog.super.prototype.initialize.call( this );
this.$spinner = $( '<div>' ).addClass( 'tdg-spinner' ).text( 'working...' );
this.$body.append( this.$spinner );
this.noticeLabel = new OO.ui.LabelWidget();
this.noticeLabel.$element.hide();
this.panels = new OO.ui.StackLayout( { continuous: false } );
this.listParamsPanel = new OO.ui.PanelLayout( { scrollable: true } );
this.editParamPanel = new OO.ui.PanelLayout();
this.languagePanel = new OO.ui.PanelLayout();
this.addParamPanel = new OO.ui.PanelLayout();
this.editMapsPanel = new OO.ui.PanelLayout();
// Language panel
this.newLanguageSearch = new mw.TemplateData.LanguageSearchWidget();
// Add parameter panel
this.newParamInput = new OO.ui.TextInputWidget( {
placeholder: mw.msg( 'templatedata-modal-placeholder-paramkey' )
} );
this.addParamButton = new OO.ui.ButtonWidget( {
label: mw.msg( 'templatedata-modal-button-addparam' )
} );
addParamFieldlayout = new OO.ui.ActionFieldLayout(
this.newParamInput,
this.addParamButton,
{
align: 'top',
label: mw.msg( 'templatedata-modal-title-addparam' )
}
);
// Maps panel
this.templateMapsInput = new OO.ui.MultilineTextInputWidget( {
classes: [ 'mw-tempateData-template-maps-input' ],
autosize: true,
disabled: true,
rows: 5,
maxRows: 200,
placeholder: mw.msg( 'templatedata-modal-placeholder-mapinfo' )
} );
templateMapsFieldSet = new OO.ui.FieldsetLayout(
this.templateMapsInput,
{
align: 'top',
label: mw.msg( 'templatedata-modal-title-map' )
}
);
// Param list panel (main)
this.languageDropdownWidget = new OO.ui.DropdownWidget();
this.languagePanelButton = new OO.ui.ButtonWidget( {
label: mw.msg( 'templatedata-modal-button-add-language' )
} );
languageActionFieldLayout = new OO.ui.ActionFieldLayout(
this.languageDropdownWidget,
this.languagePanelButton,
{
align: 'left',
label: mw.msg( 'templatedata-modal-title-language' )
}
);
this.descriptionInput = new OO.ui.MultilineTextInputWidget( {
autosize: true
} );
this.templateDescriptionFieldset = new OO.ui.FieldsetLayout( {
items: [ this.descriptionInput ]
} );
// Add Maps panel button
this.mapsPanelButton = new OO.ui.ButtonWidget( {
label: mw.msg( 'templatedata-modal-button-map' ),
classes: [ 'mw-tempateData-maps-panel-button' ]
} );
mapsActionFieldLayout = new OO.ui.ActionFieldLayout(
this.mapsPanelButton,
{
align: 'left',
label: mw.msg( 'templatedata-modal-button-map' )
}
);
this.paramListNoticeLabel = new OO.ui.LabelWidget();
this.paramListNoticeLabel.$element.hide();
this.paramSelect = new mw.TemplateData.ParamSelectWidget();
templateParamsFieldset = new OO.ui.FieldsetLayout( {
label: mw.msg( 'templatedata-modal-title-templateparams' )
} );
this.paramImport = new mw.TemplateData.ParamImportWidget();
templateParamsFieldset.$element.append( this.paramSelect.$element, this.paramImport.$element );
this.templateFormatSelectWidget = new OO.ui.ButtonSelectWidget();
this.templateFormatSelectWidget.addItems( [
new OO.ui.ButtonOptionWidget( {
data: null,
label: mw.msg( 'templatedata-modal-format-null' )
} ),
new OO.ui.ButtonOptionWidget( {
data: 'inline',
icon: 'template-format-inline',
label: mw.msg( 'templatedata-modal-format-inline' )
} ),
new OO.ui.ButtonOptionWidget( {
data: 'block',
icon: 'template-format-block',
label: mw.msg( 'templatedata-modal-format-block' )
} ),
new OO.ui.ButtonOptionWidget( {
data: 'custom',
icon: 'settings',
label: mw.msg( 'templatedata-modal-format-custom' )
} )
] );
this.templateFormatInputWidget = new OO.ui.TextInputWidget( {
placeholder: mw.msg( 'templatedata-modal-format-placeholder' )
} );
templateFormatFieldSet = new OO.ui.FieldsetLayout( {
label: mw.msg( 'templatedata-modal-title-templateformat' )
} );
templateFormatFieldSet.addItems( [
new OO.ui.FieldLayout( this.templateFormatSelectWidget, {
} ),
new OO.ui.FieldLayout( this.templateFormatInputWidget, {
align: 'top',
label: mw.msg( 'templatedata-modal-title-templateformatstring' )
} )
] );
// Param details panel
this.$paramDetailsContainer = $( '<div>' )
.addClass( 'tdg-templateDataDialog-paramDetails' );
this.listParamsPanel.$element
.addClass( 'tdg-templateDataDialog-listParamsPanel' )
.append(
this.paramListNoticeLabel.$element,
languageActionFieldLayout.$element,
this.templateDescriptionFieldset.$element,
mapsActionFieldLayout.$element,
templateFormatFieldSet.$element,
templateParamsFieldset.$element
);
this.paramEditNoticeLabel = new OO.ui.LabelWidget();
this.paramEditNoticeLabel.$element.hide();
// Edit panel
this.editParamPanel.$element
.addClass( 'tdg-templateDataDialog-editParamPanel' )
.append(
this.paramEditNoticeLabel.$element,
this.$paramDetailsContainer
);
// Language panel
this.languagePanel.$element
.addClass( 'tdg-templateDataDialog-languagePanel' )
.append(
this.newLanguageSearch.$element
);
this.addParamPanel.$element
.addClass( 'tdg-templateDataDialog-addParamPanel' )
.append( addParamFieldlayout.$element );
// Maps panel
this.editMapsPanel.$element
.addClass( 'tdg-templateDataDialog-editMapsPanel' )
.append( templateMapsFieldSet.$element );
this.panels.addItems( [
this.listParamsPanel,
this.editParamPanel,
this.languagePanel,
this.addParamPanel,
this.editMapsPanel
] );
this.panels.setItem( this.listParamsPanel );
this.panels.$element.addClass( 'tdg-templateDataDialog-panels' );
// Build param details panel
this.$paramDetailsContainer.append( this.createParamDetails() );
// Initialization
this.$body.append(
this.noticeLabel.$element,
this.panels.$element
);
// Events
this.newLanguageSearch.getResults().connect( this, { choose: 'onNewLanguageSearchResultsChoose' } );
this.newParamInput.connect( this, { change: 'onAddParamInputChange' } );
this.addParamButton.connect( this, { click: 'onAddParamButtonClick' } );
this.descriptionInput.connect( this, { change: 'onDescriptionInputChange' } );
this.languagePanelButton.connect( this, { click: 'onLanguagePanelButton' } );
this.languageDropdownWidget.getMenu().connect( this, { select: 'onLanguageDropdownWidgetSelect' } );
this.mapsPanelButton.connect( this, { click: 'onMapsPanelButton' } );
this.templateMapsInput.connect( this, { change: 'onMapInfoChange' } );
this.paramSelect.connect( this, {
choose: 'onParamSelectChoose',
reorder: 'onParamSelectReorder'
} );
this.paramImport.connect( this, { click: 'importParametersFromTemplateCode' } );
this.templateFormatSelectWidget.connect( this, { choose: 'onTemplateFormatSelectWidgetChoose' } );
this.templateFormatInputWidget.connect( this, {
change: 'onTemplateFormatInputWidgetChange',
enter: 'onTemplateFormatInputWidgetEnter'
} );
};
/**
* Respond to model change of description event
*
* @param {string} description New description
*/
mw.TemplateData.Dialog.prototype.onModelChangeDescription = function ( description ) {
this.descriptionInput.setValue( description );
};
/**
* Respond to model change of map info event
*
* @param {string} map New description
*/
mw.TemplateData.Dialog.prototype.onModelChangeMapInfo = function ( map ) {
this.templateMapsInput.setValue( map );
};
/**
* Respond to add param input change.
*
* @param {string} value New parameter name
*/
mw.TemplateData.Dialog.prototype.onAddParamInputChange = function ( value ) {
var allProps = mw.TemplateData.Model.static.getAllProperties( true );
if (
value.match( allProps.name.restrict ) ||
(
this.model.isParamExists( value ) &&
!this.model.isParamDeleted( value )
)
) {
// Disable the add button
this.addParamButton.setDisabled( true );
} else {
this.addParamButton.setDisabled( false );
}
};
/**
* Respond to change of param order from the model
*
* @param {...string[]} paramOrderArray The array of keys in order
*/
mw.TemplateData.Dialog.prototype.onModelChangeParamOrder = function () {
// Refresh the parameter widget
this.repopulateParamSelectWidget();
};
/**
* Respond to change of param property from the model
*
* @param {string} paramKey Parameter key
* @param {string} prop Property name
* @param {...Mixed} value Property value
* @param {string} [language] Value language
*/
mw.TemplateData.Dialog.prototype.onModelChangeProperty = function ( paramKey, prop, value ) {
// Refresh the parameter widget
if ( paramKey === this.selectedParamKey && prop === 'name' ) {
this.selectedParamKey = value;
}
};
/**
* Respond to a change in the model
*/
mw.TemplateData.Dialog.prototype.onModelChange = function () {
this.modified = true;
this.updateActions();
};
/**
* Set action abilities according to whether the model is modified
*/
mw.TemplateData.Dialog.prototype.updateActions = function () {
this.actions.setAbilities( { apply: this.modified } );
};
/**
* Respond to param order widget reorder event
*
* @param {mw.TemplateData.ParamWidget} item Item reordered
* @param {number} newIndex New index of the item
*/
mw.TemplateData.Dialog.prototype.onParamSelectReorder = function ( item, newIndex ) {
this.model.reorderParamOrderKey( item.getData(), newIndex );
};
/**
* Respond to description input change event
*
* @param {string} value Description value
*/
mw.TemplateData.Dialog.prototype.onDescriptionInputChange = function ( value ) {
if ( this.model.getTemplateDescription() !== value ) {
this.model.setTemplateDescription( value, this.language );
}
};
/**
* Respond to edit maps input change event
*
* @param {string} value map info value
*/
mw.TemplateData.Dialog.prototype.onMapInfoChange = function ( value ) {
if ( this.model.getMapInfo() !== value ) {
this.model.setMapInfo( value );
}
};
/**
* Respond to add language button click
*/
mw.TemplateData.Dialog.prototype.onLanguagePanelButton = function () {
this.switchPanels( 'language' );
};
/**
* Respond to language select widget select event
*
* @param {OO.ui.OptionWidget} item Selected item
*/
mw.TemplateData.Dialog.prototype.onLanguageDropdownWidgetSelect = function ( item ) {
var language = item ? item.getData() : this.language;
// Change current language
if ( language !== this.language ) {
this.language = language;
// Update description label
this.templateDescriptionFieldset.setLabel( mw.msg( 'templatedata-modal-title-templatedesc', this.language ) );
// Update description value
this.descriptionInput.setValue( this.model.getTemplateDescription( language ) );
// Update all param descriptions in the param select widget
this.repopulateParamSelectWidget();
// Update the parameter detail page
this.updateParamDetailsLanguage( this.language );
this.emit( 'change-language', this.language );
}
};
/**
* Handle choose events from the new language search widget
*
* @param {mw.TemplateData.LanguageResultWidget} item Chosen item
*/
mw.TemplateData.Dialog.prototype.onNewLanguageSearchResultsChoose = function ( item ) {
var languageButton,
newLanguage = item.getData().code;
if ( newLanguage ) {
if ( this.availableLanguages.indexOf( newLanguage ) === -1 ) {
// Add new language
this.availableLanguages.push( newLanguage );
languageButton = new OO.ui.MenuOptionWidget( {
data: newLanguage,
label: $.uls.data.getAutonym( newLanguage )
} );
this.languageDropdownWidget.getMenu().addItems( [ languageButton ] );
}
// Select the new item
this.languageDropdownWidget.getMenu().selectItemByData( newLanguage );
}
// Go to the main panel
this.switchPanels( 'listParams' );
};
/**
* Respond to edit maps button click
*/
mw.TemplateData.Dialog.prototype.onMapsPanelButton = function () {
this.switchPanels( 'editMaps' );
};
/**
* Respond to add parameter button
*/
mw.TemplateData.Dialog.prototype.onAddParamButtonClick = function () {
var newParamKey = this.newParamInput.getValue(),
allProps = mw.TemplateData.Model.static.getAllProperties( true );
// Validate parameter
if ( !newParamKey.match( allProps.name.restrict ) ) {
if ( this.model.isParamDeleted( newParamKey ) ) {
// Empty param
this.model.emptyParamData( newParamKey );
} else if ( !this.model.isParamExists( newParamKey ) ) {
// Add to model
if ( this.model.addParam( newParamKey ) ) {
// Add parameter to list
this.addParamToSelectWidget( newParamKey );
}
}
}
// Reset the input
this.newParamInput.setValue( '' );
// Go back to list
this.switchPanels( 'listParams' );
};
/**
* Respond to choose event from the param select widget
*
* @param {OO.ui.OptionWidget} item Parameter item
*/
mw.TemplateData.Dialog.prototype.onParamSelectChoose = function ( item ) {
var paramKey = item.getData();
this.selectedParamKey = paramKey;
// Fill in parameter detail
this.getParameterDetails( paramKey );
this.switchPanels( 'editParam' );
};
/**
* Respond to choose event from the template format select widget
*
* @param {OO.ui.OptionWidget} item Format item
*/
mw.TemplateData.Dialog.prototype.onTemplateFormatSelectWidgetChoose = function ( item ) {
var format = item.getData(),
shortcuts = {
inline: '{{_|_=_}}',
block: '{{_\n| _ = _\n}}'
};
if ( format !== 'custom' ) {
this.model.setTemplateFormat( format );
this.templateFormatInputWidget.setDisabled( true );
if ( format !== null ) {
this.templateFormatInputWidget.setValue(
this.formatToDisplay( shortcuts[ format ] )
);
}
} else {
this.templateFormatInputWidget.setDisabled( false );
this.onTemplateFormatInputWidgetChange(
this.templateFormatInputWidget.getValue()
);
}
};
mw.TemplateData.Dialog.prototype.formatToDisplay = function ( s ) {
// Use '↵' (\u21b5) as a fancy newline (which doesn't start a new line).
return s.replace( /\n/g, '\u21b5' );
};
mw.TemplateData.Dialog.prototype.displayToFormat = function ( s ) {
// Allow user to type \n or \\n (literal backslash, n) for a new line.
return s.replace( /\n|\\n|\u21b5/g, '\n' );
};
/**
* Respond to change event from the template format input widget
*
* @param {string} value Input widget value
*/
mw.TemplateData.Dialog.prototype.onTemplateFormatInputWidgetChange = function ( value ) {
var item = this.templateFormatSelectWidget.findSelectedItem(),
format,
newValue;
if ( item.getData() === 'custom' ) {
// Convert literal newlines or backslash-n to our fancy character
// replacement.
format = this.displayToFormat( value );
newValue = this.formatToDisplay( format );
if ( newValue !== value ) {
this.templateFormatInputWidget.setValue( newValue );
// Will recurse to actually set value in model.
} else {
this.model.setTemplateFormat( this.displayToFormat( value.trim() ) );
}
}
};
/**
* Respond to enter event from the template format input widget
*/
mw.TemplateData.Dialog.prototype.onTemplateFormatInputWidgetEnter = function () {
/* Synthesize a '\n' when enter is pressed. */
this.templateFormatInputWidget.insertContent(
this.formatToDisplay( '\n' )
);
};
mw.TemplateData.Dialog.prototype.onParamPropertyInputChange = function ( property, value ) {
var err = [],
anyInputError = false,
allProps = mw.TemplateData.Model.static.getAllProperties( true );
if ( property === 'type' ) {
value = this.propInputs[ property ].getMenu().findSelectedItem() ? this.propInputs[ property ].getMenu().findSelectedItem().getData() : 'unknown';
}
if ( property === 'name' ) {
if ( value.length === 0 ) {
err.push( mw.msg( 'templatedata-modal-errormsg', '|', '=', '}}' ) );
}
if ( value !== this.selectedParamKey && this.model.getAllParamNames().indexOf( value ) !== -1 ) {
// We're changing the name. Make sure it doesn't conflict.
err.push( mw.msg( 'templatedata-modal-errormsg-duplicate-name' ) );
}
}
if ( allProps[ property ].restrict ) {
if ( value.match( allProps[ property ].restrict ) ) {
// Error! Don't fix the model
err.push( mw.msg( 'templatedata-modal-errormsg', '|', '=', '}}' ) );
}
}
this.propInputs[ property ].$element.toggleClass( 'tdg-editscreen-input-error', !!err.length );
// Check if there is a dependent input to activate
if ( allProps[ property ].textValue && this.propFieldLayout[ allProps[ property ].textValue ] ) {
// The textValue property depends on this property
// toggle its view
this.propFieldLayout[ allProps[ property ].textValue ].toggle( !!value );
this.propInputs[ allProps[ property ].textValue ].setValue( this.model.getParamProperty( this.selectedParamKey, allProps[ property ].textValue ) );
}
// Validate
// FIXME: Don't read model information from the DOM
// eslint-disable-next-line no-jquery/no-global-selector
anyInputError = !!$( '.tdg-templateDataDialog-paramInput.tdg-editscreen-input-error' ).length;
// Disable the 'done' button if there are any errors in the inputs
this.actions.setAbilities( { done: !anyInputError } );
if ( err.length ) {
this.toggleNoticeMessage( 'edit', true, 'error', err.length === 1 ? err[ 0 ] : err );
} else {
this.toggleNoticeMessage( 'edit', false );
this.model.setParamProperty( this.selectedParamKey, property, value, this.language );
}
// If we're changing the aliases and the name has an error, poke its change
// handler in case that error was because of a duplicate name with its own
// aliases.
// FIXME: Don't read model information from the DOM
// eslint-disable-next-line no-jquery/no-class-state
if ( property === 'aliases' && this.propInputs.name.$element.hasClass( 'tdg-editscreen-input-error' ) ) {
this.onParamPropertyInputChange( 'name', this.propInputs.name.getValue() );
}
};
/**
* Set the parameter details in the detail panel.
*
* @param {Object} paramKey Parameter details
*/
mw.TemplateData.Dialog.prototype.getParameterDetails = function ( paramKey ) {
var prop,
paramData = this.model.getParamData( paramKey ),
allProps = mw.TemplateData.Model.static.getAllProperties( true );
for ( prop in this.propInputs ) {
this.changeParamPropertyInput( paramKey, prop, paramData[ prop ], this.language );
// Show/hide dependents
if ( allProps[ prop ].textValue ) {
this.propFieldLayout[ allProps[ prop ].textValue ].toggle( !!paramData[ prop ] );
}
}
};
/**
* Reset contents on reload
*/
mw.TemplateData.Dialog.prototype.reset = function () {
this.language = null;
this.availableLanguages = [];
if ( this.paramSelect ) {
this.paramSelect.clearItems();
this.selectedParamKey = '';
}
if ( this.languageDropdownWidget ) {
this.languageDropdownWidget.getMenu().clearItems();
}
};
/**
* Empty and repopulate the parameter select widget.
*/
mw.TemplateData.Dialog.prototype.repopulateParamSelectWidget = function () {
var i, paramKey, missingParams, paramList, paramOrder;
if ( !this.isSetup ) {
return;
}
missingParams = this.model.getMissingParams();
paramList = this.model.getParams();
paramOrder = this.model.getTemplateParamOrder();
this.paramSelect.clearItems();
// Update all param descriptions in the param select widget
for ( i in paramOrder ) {
paramKey = paramList[ paramOrder[ i ] ];
if ( paramKey && !paramKey.deleted ) {
this.addParamToSelectWidget( paramOrder[ i ] );
}
}
// Check if there are potential parameters to add
// from the template source code
if ( missingParams.length > 0 ) {
this.paramImport
.toggle( true )
.buildParamLabel( missingParams );
} else {
this.paramImport.toggle( false );
}
};
/**
* Change parameter property
*
* @param {string} paramKey Parameter key
* @param {string} propName Property name
* @param {string} value Property value
* @param {string} [lang] Language
*/
mw.TemplateData.Dialog.prototype.changeParamPropertyInput = function ( paramKey, propName, value, lang ) {
var languageProps = mw.TemplateData.Model.static.getPropertiesWithLanguage(),
allProps = mw.TemplateData.Model.static.getAllProperties( true ),
prop = allProps[ propName ],
propInput = typeof this.propInputs[ propName ].getMenu === 'function' ?
this.propInputs[ propName ].getMenu() : this.propInputs[ propName ];
lang = lang || this.language;
if ( value !== undefined ) {
// Change the actual input
if ( prop.type === 'select' ) {
propInput.selectItem( propInput.findItemFromData( value ) );
} else if ( prop.type === 'boolean' ) {
propInput.setSelected( !!value );
} else {
if ( languageProps.indexOf( propName ) !== -1 ) {
propInput.setValue( value[ lang ] );
} else {
if ( prop.type === 'array' && Array.isArray( value ) ) {
value = value.join( prop.delimiter );
}
propInput.setValue( value );
}
}
} else {
// Empty the input
if ( prop.type === 'select' ) {
propInput.selectItem( propInput.findItemFromData( prop.default ) );
} else if ( prop.type === 'boolean' ) {
propInput.setSelected( false );
} else {
propInput.setValue( '' );
}
}
};
/**
* Add parameter to the list
*
* @param {string} paramKey Parameter key in the model
*/
mw.TemplateData.Dialog.prototype.addParamToSelectWidget = function ( paramKey ) {
var paramItem,
data = this.model.getParamData( paramKey );
paramItem = new mw.TemplateData.ParamWidget( {
key: paramKey,
label: this.model.getParamValue( paramKey, 'label', this.language ),
aliases: data.aliases,
description: this.model.getParamValue( paramKey, 'description', this.language )
} );
this.paramSelect.addItems( [ paramItem ] );
};
/**
* Create the information page about individual parameters
*
* @return {jQuery} Editable details page for the parameter
*/
mw.TemplateData.Dialog.prototype.createParamDetails = function () {
var props, type, propInput, config, paramProperties,
paramFieldset,
typeItemArray = [];
paramProperties = mw.TemplateData.Model.static.getAllProperties( true );
// Fieldset
paramFieldset = new OO.ui.FieldsetLayout();
for ( props in paramProperties ) {
config = {
multiline: paramProperties[ props ].multiline
};
if ( paramProperties[ props ].multiline ) {
config.autosize = true;
}
// Create the property inputs
switch ( props ) {
case 'type':
propInput = new OO.ui.DropdownWidget( config );
for ( type in paramProperties[ props ].children ) {
typeItemArray.push( new OO.ui.MenuOptionWidget( {
data: paramProperties[ props ].children[ type ],
// The following messages are used here:
// * templatedata-doc-param-type-boolean, templatedata-doc-param-type-content,
// * templatedata-doc-param-type-date, templatedata-doc-param-type-line,
// * templatedata-doc-param-type-number, templatedata-doc-param-type-string,
// * templatedata-doc-param-type-unbalanced-wikitext, templatedata-doc-param-type-unknown,
// * templatedata-doc-param-type-url, templatedata-doc-param-type-wiki-file-name,
// * templatedata-doc-param-type-wiki-page-name, templatedata-doc-param-type-wiki-template-name,
// * templatedata-doc-param-type-wiki-user-name
label: mw.msg( 'templatedata-doc-param-type-' + paramProperties[ props ].children[ type ] )
} ) );
}
propInput.getMenu().addItems( typeItemArray );
break;
case 'deprecated':
case 'required':
case 'suggested':
propInput = new OO.ui.CheckboxInputWidget( config );
break;
default:
if ( config.multiline === true ) {
delete config.multiline;
propInput = new OO.ui.MultilineTextInputWidget( config );
} else {
delete config.multiline;
propInput = new OO.ui.TextInputWidget( config );
}
break;
}
this.propInputs[ props ] = propInput;
// The following classes are used here:
// * tdg-templateDataDialog-paramInput tdg-templateDataDialog-paramList-actions
// * tdg-templateDataDialog-paramInput tdg-templateDataDialog-paramList-aliases
// * tdg-templateDataDialog-paramInput tdg-templateDataDialog-paramList-autovalue
// * tdg-templateDataDialog-paramInput tdg-templateDataDialog-paramList-default
// * tdg-templateDataDialog-paramInput tdg-templateDataDialog-paramList-deprecated
// * tdg-templateDataDialog-paramInput tdg-templateDataDialog-paramList-deprecatedValue
// * tdg-templateDataDialog-paramInput tdg-templateDataDialog-paramList-description
// * tdg-templateDataDialog-paramInput tdg-templateDataDialog-paramList-example
// * tdg-templateDataDialog-paramInput tdg-templateDataDialog-paramList-importoption
// * tdg-templateDataDialog-paramInput tdg-templateDataDialog-paramList-importoption-subtitle
// * tdg-templateDataDialog-paramInput tdg-templateDataDialog-paramList-label
// * tdg-templateDataDialog-paramInput tdg-templateDataDialog-paramList-name
// * tdg-templateDataDialog-paramInput tdg-templateDataDialog-paramList-required
// * tdg-templateDataDialog-paramInput tdg-templateDataDialog-paramList-suggested
// * tdg-templateDataDialog-paramInput tdg-templateDataDialog-paramList-type
// * tdg-templateDataDialog-paramInput tdg-templateDataDialog-paramList-uneditablefield
propInput.$element
.addClass( 'tdg-templateDataDialog-paramInput tdg-templateDataDialog-paramList-' + props );
this.propFieldLayout[ props ] = new OO.ui.FieldLayout( propInput, {
align: 'left',
// The following messages are used here:
// * templatedata-modal-table-param-actions
// * templatedata-modal-table-param-aliases
// * templatedata-modal-table-param-autovalue
// * templatedata-modal-table-param-default
// * templatedata-modal-table-param-deprecated
// * templatedata-modal-table-param-deprecatedValue
// * templatedata-modal-table-param-description
// * templatedata-modal-table-param-example
// * templatedata-modal-table-param-importoption
// * templatedata-modal-table-param-importoption-subtitle
// * templatedata-modal-table-param-label
// * templatedata-modal-table-param-name
// * templatedata-modal-table-param-required
// * templatedata-modal-table-param-suggested
// * templatedata-modal-table-param-type
// * templatedata-modal-table-param-uneditablefield
label: mw.msg( 'templatedata-modal-table-param-' + props )
} );
// Event
if ( props === 'type' ) {
propInput.getMenu().connect( this, { choose: [ 'onParamPropertyInputChange', props ] } );
} else {
propInput.connect( this, { change: [ 'onParamPropertyInputChange', props ] } );
}
// Append to parameter section
paramFieldset.$element.append( this.propFieldLayout[ props ].$element );
}
// Update parameter property fields with languages
this.updateParamDetailsLanguage( this.language );
return paramFieldset.$element;
};
/**
* Update the labels for parameter property inputs that include language, so
* they show the currently used language.
*
* @param {string} [lang] Language. If not used, will use currently defined
* language.
*/
mw.TemplateData.Dialog.prototype.updateParamDetailsLanguage = function ( lang ) {
var i, prop, label,
languageProps = mw.TemplateData.Model.static.getPropertiesWithLanguage();
lang = lang || this.language;
for ( i = 0; i < languageProps.length; i++ ) {
prop = languageProps[ i ];
// The following messages are used here:
// * templatedata-modal-table-param-actions
// * templatedata-modal-table-param-aliases
// * templatedata-modal-table-param-autovalue
// * templatedata-modal-table-param-default
// * templatedata-modal-table-param-deprecated
// * templatedata-modal-table-param-deprecatedValue
// * templatedata-modal-table-param-description
// * templatedata-modal-table-param-example
// * templatedata-modal-table-param-importoption
// * templatedata-modal-table-param-importoption-subtitle
// * templatedata-modal-table-param-label
// * templatedata-modal-table-param-name
// * templatedata-modal-table-param-required
// * templatedata-modal-table-param-suggested
// * templatedata-modal-table-param-type
// * templatedata-modal-table-param-uneditablefield
label = mw.msg( 'templatedata-modal-table-param-' + prop, lang );
this.propFieldLayout[ prop ].setLabel( label );
}
};
/**
* Override getBodyHeight to create a tall dialog relative to the screen.
*
* @return {number} Body height
*/
mw.TemplateData.Dialog.prototype.getBodyHeight = function () {
return window.innerHeight - 200;
};
/**
* Show or hide the notice message in the dialog with a set message.
*
* Hides all other notices messages when called, not just the one specified.
*
* @param {string} type Which notice label to show: 'list', 'edit' or 'global'; defaults to 'list'
* @param {boolean} isShowing Show or hide the message
* @param {string} status Message status 'error' or 'success'
* @param {string|string[]} noticeMessage The message to display
*/
mw.TemplateData.Dialog.prototype.toggleNoticeMessage = function ( type, isShowing, status, noticeMessage ) {
var noticeReference,
$message;
type = type || 'list';
// Hide all
this.noticeLabel.$element.hide();
this.paramEditNoticeLabel.$element.hide();
this.paramListNoticeLabel.$element.hide();
if ( noticeMessage ) {
// See which error to display
if ( type === 'global' ) {
noticeReference = this.noticeLabel;
} else if ( type === 'edit' ) {
noticeReference = this.paramEditNoticeLabel;
} else {
noticeReference = this.paramListNoticeLabel;
}
// FIXME: Don't read model information from the DOM
// eslint-disable-next-line no-jquery/no-sizzle
isShowing = isShowing || !noticeReference.$element.is( ':visible' );
if ( Array.isArray( noticeMessage ) ) {
$message = $( '<div>' );
noticeMessage.forEach( function ( msg ) {
$message.append( $( '<p>' ).text( msg ) );
} );
noticeReference.setLabel( $message );
} else {
noticeReference.setLabel( noticeMessage );
}
noticeReference.$element
.toggle( isShowing )
.toggleClass( 'errorbox', status === 'error' )
.toggleClass( 'successbox', status === 'success' );
}
};
/**
* Import parameters from the source code.
*/
mw.TemplateData.Dialog.prototype.importParametersFromTemplateCode = function () {
var combinedMessage = [],
state = 'success',
response = this.model.importSourceCodeParameters();
// Repopulate the list
this.repopulateParamSelectWidget();
if ( response.imported.length === 0 ) {
combinedMessage.push( mw.msg( 'templatedata-modal-errormsg-import-noparams' ) );
state = 'error';
} else {
combinedMessage.push( mw.msg( 'templatedata-modal-notice-import-numparams', response.imported.length, response.imported.join( mw.msg( 'comma-separator' ) ) ) );
}
this.toggleNoticeMessage( 'list', true, state, combinedMessage );
};
/**
* Get a process for setting up a window for use.
*
* @param {Object} [data] Dialog opening data
*/
mw.TemplateData.Dialog.prototype.getSetupProcess = function ( data ) {
return mw.TemplateData.Dialog.super.prototype.getSetupProcess.call( this, data )
.next( function () {
var i, language, languages,
languageItems = [];
this.isSetup = false;
this.reset();
// The dialog must be supplied with a reference to a model
this.model = data.model;
this.modified = false;
// Hide the panels and display a spinner
this.$spinner.show();
this.panels.$element.hide();
this.toggleNoticeMessage( 'global', false );
this.toggleNoticeMessage( 'list', false );
// Start with parameter list
this.switchPanels( 'listParams' );
// Events
this.model.connect( this, {
'change-description': 'onModelChangeDescription',
'change-paramOrder': 'onModelChangeParamOrder',
'change-map': 'onModelChangeMapInfo',
'change-property': 'onModelChangeProperty',
change: 'onModelChange'
} );
// Setup the dialog
this.setupDetailsFromModel();
this.newLanguageSearch.addResults();
languageItems = [];
language = this.model.getDefaultLanguage();
languages = this.model.getExistingLanguageCodes();
// Fill up the language selection
if (
languages.length === 0 ||
languages.indexOf( language ) === -1
) {
// Add the default language
languageItems.push( new OO.ui.MenuOptionWidget( {
data: language,
label: $.uls.data.getAutonym( language )
} ) );
this.availableLanguages.push( language );
}
// Add all available languages
for ( i = 0; i < languages.length; i++ ) {
languageItems.push( new OO.ui.MenuOptionWidget( {
data: languages[ i ],
label: $.uls.data.getAutonym( languages[ i ] )
} ) );
// Store available languages
this.availableLanguages.push( languages[ i ] );
}
this.languageDropdownWidget.getMenu().addItems( languageItems );
// Trigger the initial language choice
this.languageDropdownWidget.getMenu().selectItemByData( language );
this.isSetup = true;
this.repopulateParamSelectWidget();
// Show the panel
this.$spinner.hide();
this.panels.$element.show();
this.actions.setAbilities( { apply: false } );
}, this );
};
/**
* Set up the list of parameters from the model. This should happen
* after initialization of the model.
*/
mw.TemplateData.Dialog.prototype.setupDetailsFromModel = function () {
var format;
// Set up description
this.descriptionInput.setValue( this.model.getTemplateDescription( this.language ) );
// set up maps
this.templateMapsInput.setValue( this.model.getMapInfo() );
// Set up format
format = this.model.getTemplateFormat();
if ( format === 'inline' || format === 'block' || format === null ) {
this.templateFormatSelectWidget.selectItemByData( format );
this.templateFormatInputWidget.setDisabled( true );
} else {
this.templateFormatSelectWidget.selectItemByData( 'custom' );
this.templateFormatInputWidget.setValue( this.formatToDisplay( format ) );
this.templateFormatInputWidget.setDisabled( false );
}
// Repopulate the parameter list
this.repopulateParamSelectWidget();
};
/**
* Switch between stack layout panels
*
* @param {string} panel Panel key to switch to
*/
mw.TemplateData.Dialog.prototype.switchPanels = function ( panel ) {
switch ( panel ) {
case 'listParams':
this.actions.setMode( 'list' );
this.panels.setItem( this.listParamsPanel );
// Reset message
this.toggleNoticeMessage( 'list', false );
// Deselect parameter
this.paramSelect.selectItem( null );
// Repopulate the list to account for any changes
if ( this.model ) {
this.repopulateParamSelectWidget();
}
// Hide/show panels
this.listParamsPanel.$element.show();
this.editParamPanel.$element.hide();
this.addParamPanel.$element.hide();
this.languagePanel.$element.hide();
this.editMapsPanel.$element.hide();
break;
case 'editParam':
this.actions.setMode( 'edit' );
this.panels.setItem( this.editParamPanel );
// Deselect parameter
this.paramSelect.selectItem( null );
// Hide/show panels
this.listParamsPanel.$element.hide();
this.languagePanel.$element.hide();
this.addParamPanel.$element.hide();
this.editParamPanel.$element.show();
this.editMapsPanel.$element.hide();
break;
case 'addParam':
this.actions.setMode( 'add' );
this.panels.setItem( this.addParamPanel );
// Hide/show panels
this.listParamsPanel.$element.hide();
this.editParamPanel.$element.hide();
this.languagePanel.$element.hide();
this.addParamPanel.$element.show();
this.editMapsPanel.$element.hide();
break;
case 'editMaps':
this.actions.setMode( 'maps' );
this.panels.setItem( this.editMapsPanel );
// Hide/show panels
this.listParamsPanel.$element.hide();
this.editParamPanel.$element.hide();
this.languagePanel.$element.hide();
this.addParamPanel.$element.hide();
this.editMapsPanel.$element.show();
this.templateMapsInput.adjustSize( true );
break;
case 'language':
this.actions.setMode( 'language' );
this.panels.setItem( this.languagePanel );
// Hide/show panels
this.listParamsPanel.$element.hide();
this.editParamPanel.$element.hide();
this.addParamPanel.$element.hide();
this.languagePanel.$element.show();
this.newLanguageSearch.query.focus();
this.editMapsPanel.$element.hide();
break;
}
};
/**
* Get a process for taking action.
*
* @param {string} [action] Symbolic name of action
* @return {OO.ui.Process} Action process
*/
mw.TemplateData.Dialog.prototype.getActionProcess = function ( action ) {
if ( action === 'back' || action === 'done' ) {
return new OO.ui.Process( function () {
this.switchPanels( 'listParams' );
}, this );
}
if ( action === 'add' ) {
return new OO.ui.Process( function () {
this.switchPanels( 'addParam' );
}, this );
}
if ( action === 'maps' ) {
return new OO.ui.Process( function () {
this.switchPanels( 'editMaps' );
}, this );
}
if ( action === 'delete' ) {
return new OO.ui.Process( function () {
this.model.deleteParam( this.selectedParamKey );
this.switchPanels( 'listParams' );
}, this );
}
if ( action === 'apply' ) {
return new OO.ui.Process( function () {
this.emit( 'apply', this.model.outputTemplateData() );
this.close( { action: action } );
}, this );
}
if ( !action && this.modified ) {
return new OO.ui.Process( function () {
var dialog = this;
return OO.ui.confirm( mw.msg( 'templatedata-modal-confirmcancel' ) )
.then( function ( result ) {
if ( result ) {
dialog.close();
} else {
return $.Deferred().resolve().promise();
}
} );
}, this );
}
// Fallback to parent handler
return mw.TemplateData.Dialog.super.prototype.getActionProcess.call( this, action );
};