mediawiki-extensions-Templa.../modules/ext.templateDataGenerator.ui.tdDialog.js
David Lynch f3566cece2 tdDialog: handle name/alias swaps
If you changed the name to one of its own aliases, then went into the
alias field to remove that alias, the "Done" button would remain
disabled for no obvious reason. As such, rerun the name change handler.

Also, the input error-highlight was too low specificity for current OOUI
CSS, and so we weren't showing which field had an issue.

Bug: T222543
Change-Id: I49ac0587c4b5e58ce1fc3c42e3dffeae8f986672
2019-05-08 11:25:36 -05:00

1099 lines
33 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'
},
{
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',
modes: [ 'list', 'error' ]
},
{
action: 'back',
label: mw.msg( 'templatedata-modal-button-back' ),
flags: 'safe',
modes: [ 'language', 'add' ]
}
];
/**
* 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;
// 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();
// 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' )
}
);
// 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 ]
} );
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,
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 );
this.panels.addItems( [
this.listParamsPanel,
this.editParamPanel,
this.languagePanel,
this.addParamPanel
] );
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.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 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;
};
/**
* 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 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 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.
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 ],
// Known messages, for grepping:
// 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;
propInput.$element
.addClass( 'tdg-templateDataDialog-paramInput tdg-templateDataDialog-paramList-' + props );
this.propFieldLayout[ props ] = new OO.ui.FieldLayout( propInput, {
align: 'left',
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 ];
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;
}
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-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 );
};
/**
* 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 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();
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();
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();
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();
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 === '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 );
};