mirror of
https://gerrit.wikimedia.org/r/mediawiki/extensions/TemplateData
synced 2024-11-27 17:20:01 +00:00
05c83e5212
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
1134 lines
30 KiB
JavaScript
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;
|
|
};
|