mediawiki-extensions-Templa.../modules/ext.templateDataGenerator.data.js
Alex Monk 05c83e5212 Fix renaming of parameters to another existing name
If the user tried to rename a parameter to "asd", but "asd" already existed,
we'd append "asd0", and the user would get "asdasd0". But what we actually
wanted to do was change it to "asd0".

Change-Id: I7fa05ed6fca54bc941e1843ebe6872a87383effb
2016-08-16 02:01:45 +01:00

1134 lines
30 KiB
JavaScript

/**
* TemplateData Model
*
* @class
* @mixins OO.EventEmitter
*
* @constructor
* @param {Object} config Dialog configuration object
*/
mw.TemplateData.Model = function mwTemplateDataModel( config ) {
config = config || {};
// Mixin constructors
OO.EventEmitter.call( this );
// Properties
this.params = {};
this.description = {};
this.paramOrder = [];
this.format = null;
this.paramOrderChanged = false;
this.paramIdentifierCounter = 0;
this.originalTemplateDataObject = null;
this.sourceCodeParameters = [];
};
/* Inheritance */
OO.mixinClass( mw.TemplateData.Model, OO.EventEmitter );
/* Events */
/**
* @event add-param
* @param {string} key Parameter key
* @param {Object} data Parameter data
*/
/**
* @event change-description
* @param {string} description New template description
* @param {Object} [language] Description language, if supplied
*/
/**
* @event change-paramOrder
* @param {string[]} orderArray Parameter key array in order
*/
/**
* @event change-property
* @param {string} paramKey Parameter key
* @param {string} prop Property name
* @param {...Mixed} value Property value
*/
/**
* @event change
*/
/* Static Methods */
/**
* Compare two objects or strings
*
* @param {Object|string} obj1 Base object
* @param {Object|string} obj2 Compared object
* @param {boolean} [allowSubset] Allow the second object to be a
* partial object (or a subset) of the first.
* @return {boolean} Objects have equal values
*/
mw.TemplateData.Model.static.compare = function ( obj1, obj2, allowSubset ) {
if ( allowSubset && obj2 === undefined ) {
return true;
}
// Make sure the objects are of the same type
if ( $.type( obj1 ) !== $.type( obj2 ) ) {
return false;
}
// Comparing objects or arrays
if ( typeof obj1 === 'object' ) {
return OO.compare( obj2, obj1, allowSubset );
}
// Everything else (primitive types, functions, etc)
return obj1 === obj2;
};
/**
* Translate obsolete parameter types into the new types
*
* @param {string} paramType Given type
* @return {string} Normalized non-obsolete type
*/
mw.TemplateData.Model.static.translateObsoleteParamTypes = function ( paramType ) {
switch ( paramType ) {
case 'string/wiki-page-name':
return 'wiki-page-name';
case 'string/wiki-file-name':
return 'wiki-file-name';
case 'string/wiki-user-name':
return 'wiki-user-name';
default:
return paramType;
}
};
/**
* Retrieve information about all legal properties for a parameter.
*
* @param {boolean} getFullData Retrieve full information about each
* parameter. If false, the method will return an array of property
* names only.
* @return {Object|string[]} Legal property names with or without their
* definition data
*/
mw.TemplateData.Model.static.getAllProperties = function ( getFullData ) {
var properties = {
name: {
type: 'string',
// Validation regex
restrict: /[\|=]|}}/
},
aliases: {
type: 'array',
delimiter: mw.msg( 'comma-separator' )
},
label: {
type: 'string',
allowLanguages: true
},
description: {
type: 'string',
allowLanguages: true
},
example: {
type: 'string',
allowLanguages: true
},
type: {
type: 'select',
children: [
'boolean',
'content',
'wiki-file-name',
'line',
'number',
'date',
'wiki-page-name',
'string',
'wiki-template-name',
'unbalanced-wikitext',
'unknown',
'url',
'wiki-user-name'
],
'default': 'unknown'
},
'default': {
type: 'string',
multiline: true,
allowLanguages: true
},
autovalue: {
type: 'string'
},
deprecated: {
type: 'boolean',
// This should only be defined for boolean properties.
// Define the property that represents the text value.
textValue: 'deprecatedValue'
},
deprecatedValue: {
type: 'string',
changesBooleanValue: 'deprecated'
},
required: {
type: 'boolean'
},
suggested: {
type: 'boolean'
}
};
if ( !getFullData ) {
return Object.keys( properties );
} else {
return properties;
}
};
/**
* Retrieve the list of property names that allow for multiple languages.
*
* @return {string[]} Property names
*/
mw.TemplateData.Model.static.getPropertiesWithLanguage = function () {
var prop,
result = [],
propDefinitions = this.getAllProperties( true );
for ( prop in propDefinitions ) {
if ( propDefinitions[ prop ].allowLanguages ) {
result.push( prop );
}
}
return result;
};
/**
* Split a string into an array and clean/trim the values
*
* @param {string} str String to split
* @param {string} [delim] Delimeter
* @return {string[]} Clean array
*/
mw.TemplateData.Model.static.splitAndTrimArray = function ( str, delim ) {
var arr = [];
delim = delim || mw.msg( 'comma-separator' );
$.each( str.split( delim ), function () {
var trimmed = $.trim( this );
if ( trimmed ) {
arr.push( trimmed );
}
} );
return arr;
};
/**
* This is an adjustment of OO.simpleArrayUnion that ignores
* empty values when inserting into the unified array.
*
* @param {...Array} arrays Arrays to union
* @return {Array} Union of the arrays
*/
mw.TemplateData.Model.static.arrayUnionWithoutEmpty = function () {
var result = OO.simpleArrayUnion.apply( this, arguments );
// Trim and filter empty strings
return result.filter( function ( i ) {
return $.trim( i );
} );
};
/**
* Create a new mwTemplateData.Model from templatedata object.
*
* @param {Object} tdObject TemplateData parsed object
* @param {string[]} paramsInSource Parameter names found in template source
* @return {mw.TemplateData.Model} New model
*/
mw.TemplateData.Model.static.newFromObject = function ( tdObject, paramsInSource ) {
var param,
model = new mw.TemplateData.Model();
model.setSourceCodeParameters( paramsInSource || [] );
// Store the original templatedata object for comparison later
model.setOriginalTemplateDataObject( tdObject );
// Initialize the model
model.params = {};
// Add params
if ( tdObject.params ) {
for ( param in tdObject.params ) {
model.addParam( param, tdObject.params[ param ] );
}
}
model.setTemplateDescription( tdObject.description );
// Override the param order if it exists in the templatedata string
if ( tdObject.paramOrder && tdObject.paramOrder.length > 0 ) {
model.setTemplateParamOrder( tdObject.paramOrder );
}
if ( tdObject.format !== undefined ) {
model.setTemplateFormat( tdObject.format );
}
return model;
};
/* Methods */
/**
* Go over the importable parameters and check if they are
* included in the parameter model. Return the parameter names
* that are not included yet.
*
* @return {string[]} Parameters that are not yet included in
* the model
*/
mw.TemplateData.Model.prototype.getMissingParams = function () {
var i,
result = [],
allParamNames = this.getAllParamNames();
// Check source code params
for ( i = 0; i < this.sourceCodeParameters.length; i++ ) {
if ( $.inArray( this.sourceCodeParameters[ i ], allParamNames ) === -1 ) {
result.push( this.sourceCodeParameters[ i ] );
}
}
return result;
};
/**
* Add imported parameters into the model
*
* @return {Object} Parameters added. -1 for failure.
*/
mw.TemplateData.Model.prototype.importSourceCodeParameters = function () {
var i, paramKey,
allParamNames = this.getAllParamNames(),
existingArray = [],
importedArray = [],
skippedArray = [];
// Check existing params
for ( i = 0; i < allParamNames.length; i++ ) {
paramKey = allParamNames[ i ];
if ( $.inArray( paramKey, this.sourceCodeParameters ) !== -1 ) {
existingArray.push( paramKey );
}
}
// Add sourceCodeParameters to the model
for ( i = 0; i < this.sourceCodeParameters.length; i++ ) {
if (
$.inArray( this.sourceCodeParameters[ i ], existingArray ) === -1 &&
this.addParam( this.sourceCodeParameters[ i ] )
) {
importedArray.push( this.sourceCodeParameters[ i ] );
} else {
skippedArray.push( this.sourceCodeParameters[ i ] );
}
}
return {
imported: importedArray,
skipped: skippedArray,
existing: existingArray
};
};
/**
* Retrieve all existing language codes in the current templatedata model
*
* @return {string[]} Language codes in use
*/
mw.TemplateData.Model.prototype.getExistingLanguageCodes = function () {
var param, prop, lang,
result = [],
languageProps = this.constructor.static.getPropertiesWithLanguage();
// Take languages from the template description
if ( $.isPlainObject( this.description ) ) {
result.concat( Object.keys( this.description ) );
}
// Go over description
if ( $.type( this.description ) ) {
for ( lang in this.description ) {
result.push( lang );
}
}
// Go over the parameters
for ( param in this.params ) {
// Go over the properties
for ( prop in this.params[ param ] ) {
if ( $.inArray( prop, languageProps ) !== -1 ) {
result = this.constructor.static.arrayUnionWithoutEmpty( result, Object.keys( this.params[ param ][ prop ] ) );
}
}
}
return result;
};
/**
* Add parameter to the model
*
* @param {string} key Parameter key
* @param {Object} [paramData] Parameter data
* @return {boolean} Parameter was added successfully
* @fires add-param
* @fires change
*/
mw.TemplateData.Model.prototype.addParam = function ( key, paramData ) {
var prop, name, lang, propToSet,
existingNames = this.getAllParamNames(),
data = $.extend( true, {}, paramData ),
language = this.getDefaultLanguage(),
propertiesWithLanguage = this.constructor.static.getPropertiesWithLanguage(),
allProps = this.constructor.static.getAllProperties( true );
name = key;
// Check that the parameter is not already in the model
if ( this.params[ key ] || $.inArray( key, existingNames ) !== -1 ) {
// Change parameter key
key = this.getNewValidParameterKey( key );
}
// Initialize
this.params[ key ] = {};
// Store the key
this.params[ key ].name = name;
// Mark the parameter if it is in the template source
if ( $.inArray( key, this.sourceCodeParameters ) !== -1 ) {
this.params[ key ].inSource = true;
}
// Translate types
if ( data.type !== undefined ) {
data.type = this.constructor.static.translateObsoleteParamTypes( data.type );
this.params[ key ].type = data.type;
}
// Get the deprecated value
if ( $.type( data.deprecated ) === 'string' ) {
this.params[ key ].deprecatedValue = data.deprecated;
}
// Go over the rest of the data
if ( data ) {
for ( prop in data ) {
propToSet = prop;
if (
// This is to make sure that forwards compatibility is achieved
// and the code doesn't die on properties that aren't valid
allProps[ prop ] &&
// Check if property should have its text represented in another internal property
// (for example, deprecated and deprecatedValue)
allProps[ prop ].textValue
) {
// Set the textValue property
propToSet = allProps[ prop ].textValue;
// Set the boolean value in the current property
this.setParamProperty( key, prop, !!data[ prop ], language );
if ( $.type( data[ prop ] ) === 'boolean' ) {
// Only set the value of the dependent if the value is a string or
// language. Otherwise, if the value is boolean, keep the dependent
// empty.
continue;
}
}
if (
$.inArray( propToSet, propertiesWithLanguage ) !== -1 &&
$.isPlainObject( data[ prop ] )
) {
// Add all language properties
for ( lang in data[ prop ] ) {
this.setParamProperty( key, propToSet, data[ prop ], lang );
}
} else {
this.setParamProperty( key, propToSet, data[ prop ], language );
}
}
}
// Add to paramOrder
this.addKeyTemplateParamOrder( key );
// Trigger the add parameter event
this.emit( 'add-param', key, this.params[ key ] );
this.emit( 'change' );
return true;
};
/**
* Retrieve an array of all used parameter names. Note that parameter
* names can be different than their stored keys.
*
* @return {string[]} Used parameter names
*/
mw.TemplateData.Model.prototype.getAllParamNames = function () {
var key, param,
result = [];
for ( key in this.params ) {
param = this.params[ key ];
result.push( param.name );
if ( param.aliases ) {
result = result.concat( param.aliases );
}
}
return result;
};
/**
* Set the template description
*
* @param {string} desc New template description
* @param {Object} [language] Description language, if supplied. If not given,
* will default to the wiki language.
* @fires change-description
* @fires change
*/
mw.TemplateData.Model.prototype.setTemplateDescription = function ( desc, language ) {
language = language || this.getDefaultLanguage();
if ( !this.constructor.static.compare( this.description[ language ], desc ) ) {
if ( $.type( desc ) === 'object' ) {
$.extend( this.description, desc );
this.emit( 'change-description', desc[ language ], language );
} else {
this.description[ language ] = desc;
this.emit( 'change-description', desc, language );
}
this.emit( 'change' );
}
};
/**
* Get the template description.
*
* @param {string} [language] Optional language key
* @return {string|Object} The template description. If it is set
* as multilanguage object and no language is set, the whole object
* will be returned.
*/
mw.TemplateData.Model.prototype.getTemplateDescription = function ( language ) {
language = language || this.getDefaultLanguage();
return this.description[ language ];
};
/**
* Get a specific parameter's localized property
*
* @param {string} paramKey Parameter key
* @param {string} property Property name
* @param {string} [language] Optional language key
* @return {string} Parameter property in specified language
*/
mw.TemplateData.Model.prototype.getParamValue = function ( paramKey, property, language ) {
language = language || this.getDefaultLanguage();
return OO.getProp( this.params, paramKey, property, language ) || '';
};
/**
* Get the current wiki language code. Defaults on 'en'.
*
* @return {string} Wiki language
*/
mw.TemplateData.Model.prototype.getDefaultLanguage = function () {
return mw.config.get( 'wgContentLanguage' ) || 'en';
};
/**
* Set template param order array.
*
* @param {string[]} orderArray Parameter key array in order
* @fires change-paramOrder
* @fires change
*/
mw.TemplateData.Model.prototype.setTemplateParamOrder = function ( orderArray ) {
orderArray = orderArray || [];
// TODO: Make the compare method verify order of array?
// Copy the array
this.paramOrder = orderArray.slice();
this.emit( 'change-paramOrder', orderArray );
this.emit( 'change' );
};
/**
* Set template format.
*
* @param {string|null} [format=null] Preferred format
* @fires change-format
* @fires change
*/
mw.TemplateData.Model.prototype.setTemplateFormat = function ( format ) {
format = format !== undefined ? format : null;
if ( this.format !== format ) {
this.format = format;
this.emit( 'change-format', format );
this.emit( 'change' );
}
};
/**
* Add a key to the end of the paramOrder
*
* @param {string} key New key the add into the paramOrder
* @fires add-paramOrder
* @fires change
*/
mw.TemplateData.Model.prototype.addKeyTemplateParamOrder = function ( key ) {
if ( $.inArray( key, this.paramOrder ) === -1 ) {
this.paramOrder.push( key );
this.emit( 'add-paramOrder', key );
this.emit( 'change' );
}
};
/**
* TODO: document
*
* @fires change-paramOrder
* @fires change
*/
mw.TemplateData.Model.prototype.reorderParamOrderKey = function ( key, newIndex ) {
var keyIndex = this.paramOrder.indexOf( key );
// Move the parameter, account for left shift if moving forwards
this.paramOrder.splice(
newIndex - ( newIndex > keyIndex ? 1 : 0 ),
0,
this.paramOrder.splice( keyIndex, 1 )[ 0 ]
);
this.paramOrderChanged = true;
// Emit event
this.emit( 'change-paramOrder', this.paramOrder );
this.emit( 'change' );
};
/**
* Add a key to the end of the paramOrder
*
* @param {string} key New key the add into the paramOrder
* @fires change-paramOrder
* @fires change
*/
mw.TemplateData.Model.prototype.removeKeyTemplateParamOrder = function ( key ) {
var keyPos = $.inArray( key, this.paramOrder );
if ( keyPos > -1 ) {
this.paramOrder.splice( keyPos, 1 );
this.emit( 'change-paramOrder', this.paramOrder );
this.emit( 'change' );
}
};
/**
* Retrieve the template paramOrder array
*
* @return {string[]} orderArray Parameter keys in order
*/
mw.TemplateData.Model.prototype.getTemplateParamOrder = function () {
return this.paramOrder;
};
/**
* Retrieve the template preferred format
*
* @return {string|null} Preferred format
*/
mw.TemplateData.Model.prototype.getTemplateFormat = function () {
return this.format;
};
/**
* Set a specific parameter's property
*
* @param {string} paramKey Parameter key
* @param {string} prop Property name
* @param {...Mixed} value Property value
* @param {string} [language] Value language
* @return {boolean} Operation was successful
* @fires change-property
* @fires change
*/
mw.TemplateData.Model.prototype.setParamProperty = function ( paramKey, prop, value, language ) {
var propertiesWithLanguage = this.constructor.static.getPropertiesWithLanguage(),
allProps = this.constructor.static.getAllProperties( true ),
status = false,
oldValue;
language = language || this.getDefaultLanguage();
if ( !allProps[ prop ] ) {
// The property isn't supported yet
return status;
}
if ( allProps[ prop ].type === 'array' && $.type( value ) === 'string' ) {
// When we split the string, we want to use a trimmed delimiter
value = this.constructor.static.splitAndTrimArray( value, $.trim( allProps[ prop ].delimiter ) );
}
// Check if the property is split by language code
if ( $.inArray( prop, propertiesWithLanguage ) !== -1 ) {
// Initialize property if necessary
if ( !$.isPlainObject( this.params[ paramKey ][ prop ] ) ) {
this.params[ paramKey ][ prop ] = {};
}
value = $.isPlainObject( value ) ? value[ language ] : value;
// Compare with language
if ( !this.constructor.static.compare( this.params[ paramKey ][ prop ][ language ], value ) ) {
this.params[ paramKey ][ prop ][ language ] = value;
this.emit( 'change-property', paramKey, prop, value, language );
this.emit( 'change' );
status = true;
}
} else {
// Compare without language
if ( !this.constructor.static.compare( this.params[ paramKey ][ prop ], value ) ) {
oldValue = this.params[ paramKey ][ prop ];
if ( prop === 'name' && oldValue !== value ) {
// See if the parameters already has something with this new key
if ( this.params[ value ] && !this.params[ value ].deleted ) {
// Change the key to be something else
value = this.getNewValidParameterKey( value );
}
// Copy param details to new name
this.params[ value ] = this.params[ oldValue ];
// Delete the old param
this.params[ oldValue ] = { deleted: true };
this.params[ value ][ prop ] = value;
} else {
this.params[ paramKey ][ prop ] = value;
}
this.emit( 'change-property', paramKey, prop, value, language );
this.emit( 'change' );
if ( prop === 'name' ) {
this.paramOrder[ this.paramOrder.indexOf( oldValue ) ] = value;
this.paramOrderChanged = true;
this.emit( 'change-paramOrder', this.paramOrder );
this.emit( 'change' );
}
status = true;
}
}
if ( allProps[ prop ].textValue && value === false ) {
// Unset the text value if the boolean it depends on is false
status = this.setParamProperty( paramKey, allProps[ prop ].textValue, '', language );
}
return status;
};
/**
* Mark a parameter for deletion.
* Don't actually delete the parameter so we can make sure it is removed
* from the final output.
*
* @param {string} paramKey Parameter key
* @fires delete-param
* @fires change
*/
mw.TemplateData.Model.prototype.deleteParam = function ( paramKey ) {
this.params[ paramKey ].deleted = true;
// Remove from paramOrder
this.removeKeyTemplateParamOrder( paramKey );
this.emit( 'delete-param', paramKey );
this.emit( 'change' );
};
/**
* Restore parameter by unmarking it as deleted.
*
* @param {string} paramKey Parameter key
* @fires add-param
* @fires change
*/
mw.TemplateData.Model.prototype.restoreParam = function ( paramKey ) {
if ( this.params[ paramKey ] ) {
this.params[ paramKey ].deleted = false;
// Add back to paramOrder
this.addKeyTemplateParamOrder( paramKey );
this.emit( 'add-param', paramKey, this.params[ paramKey ] );
this.emit( 'change' );
}
};
/**
* Delete all data attached to a parameter
*
* @param {string} paramKey Parameter key
*/
mw.TemplateData.Model.prototype.emptyParamData = function ( paramKey ) {
if ( this.params[ paramKey ] ) {
// Delete all data and readd the parameter
delete this.params[ paramKey ];
this.addParam( paramKey );
// Mark this parameter as intentionally emptied
this.params[ paramKey ].emptied = true;
}
};
/**
* Get a parameter property.
*
* @param {string} paramKey Parameter key
* @param {string} prop Parameter property
* @return {...Mixed|null} Property value if it exists. Returns null if the
* parameter key itself doesn't exist.
*/
mw.TemplateData.Model.prototype.getParamProperty = function ( paramKey, prop ) {
if ( this.params[ paramKey ] ) {
return this.params[ paramKey ][ prop ];
}
return null;
};
/**
* Retrieve a specific parameter data
*
* @param {string} key Parameter key
* @return {Object} Parameter data
*/
mw.TemplateData.Model.prototype.getParamData = function ( key ) {
return this.params[ key ];
};
/**
* Return the complete object of all parameters.
*
* @return {Object} All parameters and their data
*/
mw.TemplateData.Model.prototype.getParams = function () {
return this.params;
};
mw.TemplateData.Model.prototype.isParamDeleted = function ( key ) {
return this.params[ key ] && this.params[ key ].deleted === true;
};
mw.TemplateData.Model.prototype.isParamExists = function ( key ) {
return $.inArray( key, Object.keys( this.params ) ) > -1;
};
/**
* Set the original templatedata object
*
* @param {Object} templatedataObj TemplateData object
*/
mw.TemplateData.Model.prototype.setOriginalTemplateDataObject = function ( templatedataObj ) {
this.originalTemplateDataObject = $.extend( true, {}, templatedataObj );
};
/**
* Get full page name
*
* @param {string} pageName Page name
*/
mw.TemplateData.Model.prototype.setFullPageName = function ( pageName ) {
this.fullPageName = pageName;
};
/**
* Set parent page
*
* @param {string} parent Parent page
*/
mw.TemplateData.Model.prototype.setParentPage = function ( parent ) {
this.parentPage = parent;
};
/**
* Get page full name
*
* @return {string} Page full name
*/
mw.TemplateData.Model.prototype.getFullPageName = function () {
return this.fullPageName;
};
/**
* Get parent page
*
* @return {string} Parent page
*/
mw.TemplateData.Model.prototype.getParentPage = function () {
return this.parentPage;
};
/**
* Get original templatedata object
*
* @return {Object} Templatedata object
*/
mw.TemplateData.Model.prototype.getOriginalTemplateDataObject = function () {
return this.originalTemplateDataObject;
};
/**
* Process the current model and output it
*
* @return {Object} Templatedata object
*/
mw.TemplateData.Model.prototype.outputTemplateData = function () {
var paramKey, key, prop, oldKey, name, compareOrig, normalizedValue,
allProps = this.constructor.static.getAllProperties( true ),
original = this.getOriginalTemplateDataObject(),
result = $.extend( true, {}, this.getOriginalTemplateDataObject() ),
defaultLang = this.getDefaultLanguage();
// Template description
if ( this.description[ defaultLang ] !== undefined ) {
normalizedValue = this.propRemoveUnusedLanguages( this.description );
if ( this.isOutputInLanguageObject( result.description, normalizedValue ) ) {
result.description = normalizedValue;
} else {
// Store only one language as a string
result.description = normalizedValue[ defaultLang ];
}
} else {
// Delete description
delete result.description;
}
// Param order
if ( original.paramOrder || this.paramOrderChanged ) {
result.paramOrder = this.paramOrder;
} else {
delete result.paramOrder;
}
// Format
if ( this.format === null ) {
delete result.format;
} else {
result.format = this.format;
}
// Attach sets as-is for now
// TODO: Work properly with sets
if ( original.sets ) {
result.sets = original.sets;
}
// Go over parameters in data
for ( paramKey in this.params ) {
key = paramKey;
if ( this.params[ key ].deleted ) {
delete result.params[ key ];
continue;
}
// If the user intentionally empties a parameter, delete it from
// the result and treat it as a new parameter
if ( this.params[ key ].emptied ) {
delete result.params[ key ];
}
// Check if name was changed and change the key accordingly
name = this.params[ key ].name;
oldKey = key;
// Notice for clarity:
// Whether the parameter name was changed or not the following
// consistency with object keys will be observed:
// * oldKey: original will use oldKey (for comparison to the old value)
// * key: this.params will use key (for storing without conflict)
// * name: result will use name (for valid output)
// Check if param is new
if ( !result.params[ name ] ) {
// New param. Initialize it
result.params[ name ] = {};
}
// Go over all properties
for ( prop in allProps ) {
switch ( prop ) {
case 'deprecatedValue':
case 'name':
continue;
case 'type':
// Only include type if the original included type
// or if the current type is not undefined
if (
original.params[ key ] &&
original.params[ key ].type !== 'unknown' &&
this.params[ key ].type === 'unknown'
) {
result.params[ name ][ prop ] = undefined;
} else {
result.params[ name ][ prop ] = this.params[ key ].type;
}
break;
case 'deprecated':
case 'required':
case 'suggested':
if ( !this.params[ key ][ prop ] ) {
// Only add a literal false value if there was a false
// value before
if ( original.params[ oldKey ] && original.params[ oldKey ][ prop ] === false ) {
result.params[ name ][ prop ] = false;
} else {
// Otherwise, delete this value
delete result.params[ name ][ prop ];
}
} else {
if ( prop === 'deprecated' ) {
result.params[ name ][ prop ] = this.params[ key ].deprecatedValue || true;
// Remove deprecatedValue
delete result.params[ name ].deprecatedValue;
} else {
result.params[ name ][ prop ] = this.params[ key ][ prop ];
}
}
break;
case 'aliases':
// Only update the aliases in if the new templatedata has an
// aliases array that isn't empty
if (
$.type( this.params[ key ][ prop ] ) === 'array' &&
this.params[ key ][ prop ].length > 0
) {
result.params[ name ][ prop ] = this.params[ key ][ prop ];
} else {
// If the new aliases array is empty, delete it from the original
delete result.params[ name ][ prop ];
}
break;
default:
// Check if there's a value in the model
if ( this.params[ key ][ prop ] !== undefined ) {
if ( allProps[ prop ].allowLanguages ) {
normalizedValue = this.propRemoveUnusedLanguages( this.params[ key ][ prop ] );
// Check if this should be displayed with language object or directly as string
compareOrig = original.params[ oldKey ] ? original.params[ oldKey ][ prop ] : {};
if ( this.isOutputInLanguageObject( compareOrig, normalizedValue ) ) {
result.params[ name ][ prop ] = normalizedValue;
} else {
// Store only one language as a string
result.params[ name ][ prop ] = normalizedValue[ defaultLang ];
}
} else {
// Set up the result
result.params[ name ][ prop ] = this.params[ key ][ prop ];
}
}
break;
}
}
}
return result;
};
/**
* Check the key if it already exists in the parameter list. If it does,
* find a new key that doesn't, and return it.
*
* @param {string} key New parameter key
* @return {string} Valid new parameter key
*/
mw.TemplateData.Model.prototype.getNewValidParameterKey = function ( key ) {
var allParamNames = this.getAllParamNames();
if ( this.params[ key ] || $.inArray( key, allParamNames ) !== -1 ) {
// Change the key to be something else
key += this.paramIdentifierCounter;
this.paramIdentifierCounter++;
return this.getNewValidParameterKey( key );
} else {
return key;
}
};
/**
* Go over a language property and remove empty language key values
*
* @return {Object} Property data with only used language keys
*/
mw.TemplateData.Model.prototype.propRemoveUnusedLanguages = function ( propData ) {
var key,
result = {};
if ( $.isPlainObject( propData ) ) {
for ( key in propData ) {
if ( propData[ key ] ) {
result[ key ] = propData[ key ];
}
}
}
return result;
};
/**
* Check whether the output of the current parameter property should be
* outputted in full language mode (object) or a simple string.
*
* @param {string} originalPropValue Original property value
* @param {string} newPropValue New property value
* @return {boolean} Output should be a full language object
*/
mw.TemplateData.Model.prototype.isOutputInLanguageObject = function ( originalPropValue, newPropValue ) {
if (
(
// The original was already split to languages
$.type( originalPropValue ) === 'object' &&
// Original was not an empty object
!$.isEmptyObject( originalPropValue )
) ||
(
// The new value is split to languages
$.type( newPropValue ) === 'object' &&
// New object is not empty
!$.isEmptyObject( newPropValue ) &&
(
// The new value doesn't have the default language
newPropValue[ this.getDefaultLanguage() ] === undefined ||
// There is more than just one language in the new property
Object.keys( newPropValue ).length > 1
)
)
) {
return true;
}
return false;
};
/**
* Set the parameters that are available in the template source code
*
* @param {string[]} sourceParams Parameters available in template source
*/
mw.TemplateData.Model.prototype.setSourceCodeParameters = function ( sourceParams ) {
this.sourceCodeParameters = sourceParams;
};
/**
* Get the parameters that are available in the template source code
*
* @return {string[]} Parameters available in template source
*/
mw.TemplateData.Model.prototype.getSourceCodeParameters = function () {
return this.sourceCodeParameters;
};