mirror of
https://gerrit.wikimedia.org/r/mediawiki/extensions/TemplateData
synced 2024-11-23 23:43:54 +00:00
Merge "Implement edit interface for TemplateData documentation"
This commit is contained in:
commit
b82082c46c
|
@ -25,6 +25,22 @@ class TemplateDataHooks {
|
|||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register qunit unit tests
|
||||
*/
|
||||
public static function onResourceLoaderTestModules(
|
||||
array &$testModules,
|
||||
ResourceLoader &$resourceLoader
|
||||
) {
|
||||
$testModules['qunit']['ext.templateData.test'] = array(
|
||||
'scripts' => array( 'tests/ext.templateData.tests.js' ),
|
||||
'dependencies' => array( 'ext.templateDataGenerator.core' ),
|
||||
'localBasePath' => __DIR__ ,
|
||||
'remoteExtPath' => 'TemplateData',
|
||||
);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Page &$page
|
||||
* @param User &$user
|
||||
|
@ -65,6 +81,23 @@ class TemplateDataHooks {
|
|||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parser hook registering the GUI module only in edit pages.
|
||||
*
|
||||
* @param EditPage $editPage
|
||||
* @param OutputPage $output
|
||||
* @return bool
|
||||
*/
|
||||
public static function onEditPage( $editPage, $output ) {
|
||||
global $wgTemplateDataUseGUI;
|
||||
if ( $wgTemplateDataUseGUI ) {
|
||||
if ( $output->getTitle()->getNamespace() === NS_TEMPLATE ) {
|
||||
$output->addModules( 'ext.templateDataGenerator.editPage' );
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parser hook for <templatedata>.
|
||||
* If there is any JSON provided, render the template documentation on the page.
|
||||
|
|
|
@ -32,6 +32,34 @@ $messages['en'] = array(
|
|||
'templatedata-invalid-unknown' => 'Unexpected property "$1".',
|
||||
'templatedata-invalid-value' => 'Invalid value for property "$1".',
|
||||
'templatedata-invalid-length' => 'Data too large to save ({{formatnum:$1}} {{PLURAL:$1|byte|bytes}}, {{PLURAL:$2|limit is}} {{formatnum:$2}})',
|
||||
|
||||
// TemplateData generator GUI:
|
||||
'templatedata-editbutton' => 'Manage template documentation',
|
||||
'templatedata-errormsg-jsonbadformat' => 'Bad JSON format. Either correct it, or delete the current <templatedata> tags and try again.',
|
||||
'templatedata-modal-button-addparam' => 'Add parameter',
|
||||
'templatedata-modal-button-apply' => 'Apply',
|
||||
'templatedata-modal-button-cancel' => 'Cancel',
|
||||
'templatedata-modal-button-delparam' => 'Delete parameter',
|
||||
'templatedata-modal-button-importParams' => 'Import parameters',
|
||||
'templatedata-modal-errormsg' => 'Errors found. Please make sure there are no empty or duplicate parameter names, and that the parameter name does not include "|", "=" or "}}".',
|
||||
'templatedata-modal-errormsg-import-noparams' => 'No new parameters found during import.',
|
||||
'templatedata-modal-notice-import-numparams' => '$1 new {{PLURAL:$1|parameter was|parameters were}} imported.',
|
||||
'templatedata-modal-table-param-actions' => 'Actions',
|
||||
'templatedata-modal-table-param-aliases' => 'Aliases (comma separated)',
|
||||
'templatedata-modal-table-param-default' => 'Default',
|
||||
'templatedata-modal-table-param-desc' => 'Description',
|
||||
'templatedata-modal-table-param-label' => 'Label',
|
||||
'templatedata-modal-table-param-name' => 'Name',
|
||||
'templatedata-modal-table-param-required' => 'Required',
|
||||
'templatedata-modal-table-param-type' => 'Type',
|
||||
'templatedata-modal-table-param-type-number' => 'Number',
|
||||
'templatedata-modal-table-param-type-page' => 'Page',
|
||||
'templatedata-modal-table-param-type-string' => 'String',
|
||||
'templatedata-modal-table-param-type-undefined' => 'Undefined',
|
||||
'templatedata-modal-table-param-type-user' => 'User',
|
||||
'templatedata-modal-title' => 'Template documentation editor',
|
||||
'templatedata-modal-title-templatedesc' => 'Template description',
|
||||
'templatedata-modal-title-templateparams' => 'Template parameters',
|
||||
);
|
||||
|
||||
/** Message documentation (Message documentation)
|
||||
|
@ -91,6 +119,32 @@ $messages['qqq'] = array(
|
|||
'templatedata-invalid-length' => "Error message when generated JSON's length exceed database limits.
|
||||
* $1 - length of generated JSON
|
||||
* $2 - maximal allowed length",
|
||||
'templatedata-editbutton' => 'The label of the button to manage templatedata, appearing above the editor field.',
|
||||
'templatedata-errormsg-jsonbadformat' => 'Error message that appears in case the JSON string is not possible to parse. The user is asked to either correct the json syntax or delete the values between the <templatedata> tags and try again.',
|
||||
'templatedata-modal-button-addparam' => 'Button to add a parameter',
|
||||
'templatedata-modal-button-apply' => 'Label of the apply button',
|
||||
'templatedata-modal-button-cancel' => 'Label of the cancel button',
|
||||
'templatedata-modal-button-delparam' => 'Button to delete a parameter',
|
||||
'templatedata-modal-button-importParams' => 'Label of the import button',
|
||||
'templatedata-modal-errormsg' => 'Error message that appears in the TemplateData generator GUI in case there are empty, duplicate or invalid parameter names',
|
||||
'templatedata-modal-errormsg-import-noparams' => 'message that appears in the TemplateData generator GUI in case no template parameters were found during the import attempt.',
|
||||
'templatedata-modal-notice-import-numparams' => 'message that appears in the TemplateData generator GUI showing how many new parameters were imported into the GUI from an existing template.',
|
||||
'templatedata-modal-table-param-actions' => 'Label for a table heading: Parameter actions in the table',
|
||||
'templatedata-modal-table-param-aliases' => 'Label for a table heading: Aliases of the parameter, instruct the user to separate aliases with commas.',
|
||||
'templatedata-modal-table-param-default' => 'Label for a table heading: Default value of the parameter',
|
||||
'templatedata-modal-table-param-desc' => 'Label for a table heading: Description of the parameter',
|
||||
'templatedata-modal-table-param-label' => 'Label for a table heading: Label of the parameter',
|
||||
'templatedata-modal-table-param-name' => 'Label for a table heading: Name of the parameter',
|
||||
'templatedata-modal-table-param-required' => 'Label for a table heading: Required status of the parameter',
|
||||
'templatedata-modal-table-param-type' => 'Label for a table heading: Type of the parameter',
|
||||
'templatedata-modal-table-param-type-number' => 'A possible parameter type: Number',
|
||||
'templatedata-modal-table-param-type-page' => 'A possible parameter type: Page',
|
||||
'templatedata-modal-table-param-type-string' => 'A possible parameter type: String',
|
||||
'templatedata-modal-table-param-type-undefined' => 'A possible parameter type: Undefined',
|
||||
'templatedata-modal-table-param-type-user' => 'A possible parameter type: User',
|
||||
'templatedata-modal-title' => 'Title of the modal popup.',
|
||||
'templatedata-modal-title-templatedesc' => 'The title for the template description textbox',
|
||||
'templatedata-modal-title-templateparams' => 'The title for the template parameters table',
|
||||
);
|
||||
|
||||
/** Arabic (العربية)
|
||||
|
@ -493,12 +547,38 @@ $messages['he'] = array(
|
|||
'templatedata-doc-param-desc-empty' => 'אין תיאור',
|
||||
'templatedata-invalid-duplicate-value' => 'המאפיין "$1" (ערך: "$3") זהה למאפיין "$2".',
|
||||
'templatedata-invalid-parse' => 'שגיאת תחביר ב־JSON.',
|
||||
'templatedata-invalid-type' => 'המאפיין "$1" צפוי להיות מסוג "$2".',
|
||||
'templatedata-invalid-type' => 'המאפיין "$1" אמור להיות מסוג "$2".',
|
||||
'templatedata-invalid-missing' => 'המאפיין הדרוש "$1" לא נמצא.',
|
||||
'templatedata-invalid-empty-array' => 'למאפיין "$1" צריך להיות לפחות ערך אחד במערך שלו.',
|
||||
'templatedata-invalid-unknown' => 'מאפיין בלתי־צפוי "$1".',
|
||||
'templatedata-invalid-value' => 'ערך בלתי־תקין למאפיין "$1".',
|
||||
'templatedata-invalid-length' => 'הנתונים גדולים מכדי לשמור ({{formatnum:$1}} {{PLURAL:$1|בית|בתים}}, {{PLURAL:$2|המגבלה היא}} {{formatnum:$2}})',
|
||||
'templatedata-invalid-length' => 'הנתונים גדולים מכדי לשמור ({{PLURAL:$1|בית אחד|{{formatnum:$1}} בתים}}, {{PLURAL:$2|המגבלה היא}} {{formatnum:$2}})',
|
||||
'templatedata-editbutton' => 'יצירת נתוני תבנית',
|
||||
'templatedata-errormsg-jsonbadformat' => 'JSON בלתי־תקין. נא לתקן אותו או למחוק את הטקסט בין תגי <templatedata> ולנסות שוב.',
|
||||
'templatedata-modal-errormsg' => 'נמצאו שגיאות. נא לוודא ששמות הפרמטרים אינם ריקים ואינם חוזרים על עצמם, ושבשמות הפרמטרים לא מופיעים התווים |, = או }}',
|
||||
'templatedata-modal-errormsg-import-noparams' => 'לא נמצאו פרמטרים חדשים בעת הייבוא',
|
||||
'templatedata-modal-notice-import-numparams' => '{{PLURAL:$1|יובא פרמטר חדש אחד|יובאו $1 פרמטרים חדשים}}',
|
||||
'templatedata-modal-title' => 'מחולל נתוני תבנית',
|
||||
'templatedata-modal-title-templatedesc' => 'תיאור התבנית',
|
||||
'templatedata-modal-title-templateparams' => 'פרמטרי תבנית',
|
||||
'templatedata-modal-table-param-name' => 'שם',
|
||||
'templatedata-modal-table-param-aliases' => 'כינויים (מופרדים בפסיק)',
|
||||
'templatedata-modal-table-param-label' => 'תווית',
|
||||
'templatedata-modal-table-param-desc' => 'תיאור',
|
||||
'templatedata-modal-table-param-type' => 'סוג',
|
||||
'templatedata-modal-table-param-type-undefined' => 'בלתי־מוגדר',
|
||||
'templatedata-modal-table-param-type-number' => 'מספר',
|
||||
'templatedata-modal-table-param-type-string' => 'מחרוזת',
|
||||
'templatedata-modal-table-param-type-user' => 'משתמש',
|
||||
'templatedata-modal-table-param-type-page' => 'דף',
|
||||
'templatedata-modal-table-param-default' => 'ערך התחלתי',
|
||||
'templatedata-modal-table-param-required' => 'נדרש',
|
||||
'templatedata-modal-table-param-actions' => 'פעולות',
|
||||
'templatedata-modal-button-addparam' => 'הוספת פרמטר',
|
||||
'templatedata-modal-button-delparam' => 'מחיקת פרמטר',
|
||||
'templatedata-modal-button-importParams' => 'ייבוא פרמטרים',
|
||||
'templatedata-modal-buttons-apply' => 'החלה',
|
||||
'templatedata-modal-buttons-cancel' => 'ביטול',
|
||||
);
|
||||
|
||||
/** Upper Sorbian (hornjoserbsce)
|
||||
|
|
|
@ -34,6 +34,8 @@ $wgAutoloadClasses['ApiTemplateData'] = $dir . '/api/ApiTemplateData.php';
|
|||
$wgHooks['ParserFirstCallInit'][] = 'TemplateDataHooks::onParserFirstCallInit';
|
||||
$wgHooks['PageContentSave'][] = 'TemplateDataHooks::onPageContentSave';
|
||||
$wgHooks['UnitTestsList'][] = 'TemplateDataHooks::onUnitTestsList';
|
||||
$wgHooks['ResourceLoaderTestModules'][] = 'TemplateDataHooks::onResourceLoaderTestModules';
|
||||
$wgHooks['EditPage::showEditForm:initial'][] = 'TemplateDataHooks::onEditPage';
|
||||
|
||||
// Register APIs
|
||||
$wgAPIModules['templatedata'] = 'ApiTemplateData';
|
||||
|
@ -48,3 +50,63 @@ $wgResourceModules['ext.templateData'] = array(
|
|||
'localBasePath' => $dir,
|
||||
'remoteExtPath' => 'TemplateData',
|
||||
);
|
||||
|
||||
$wgResourceModules['ext.templateDataGenerator.editPage'] = array(
|
||||
'localBasePath' => $dir,
|
||||
'remoteExtPath' => 'TemplateData',
|
||||
'scripts' => array(
|
||||
'modules/ext.templateDataGenerator.editPage.js',
|
||||
),
|
||||
'dependencies' => array(
|
||||
'ext.templateDataGenerator.core',
|
||||
),
|
||||
'messages' => array(
|
||||
'templatedata-editbutton',
|
||||
'templatedata-errormsg-jsonbadformat',
|
||||
)
|
||||
);
|
||||
|
||||
$wgResourceModules['ext.templateDataGenerator.core'] = array(
|
||||
'localBasePath' => $dir,
|
||||
'remoteExtPath' => 'TemplateData',
|
||||
'styles' => 'modules/ext.templateDataGenerator.css',
|
||||
'scripts' => array(
|
||||
'modules/ext.templateDataGenerator.core.js',
|
||||
),
|
||||
'dependencies' => array(
|
||||
'jquery.ui.dialog',
|
||||
'jquery.ui.button',
|
||||
),
|
||||
'messages' => array(
|
||||
'templatedata-modal-button-addparam',
|
||||
'templatedata-modal-button-apply',
|
||||
'templatedata-modal-button-cancel',
|
||||
'templatedata-modal-button-delparam',
|
||||
'templatedata-modal-button-importParams',
|
||||
'templatedata-modal-errormsg',
|
||||
'templatedata-modal-errormsg-import-noparams',
|
||||
'templatedata-modal-notice-import-numparams',
|
||||
'templatedata-modal-table-param-actions',
|
||||
'templatedata-modal-table-param-aliases',
|
||||
'templatedata-modal-table-param-default',
|
||||
'templatedata-modal-table-param-desc',
|
||||
'templatedata-modal-table-param-label',
|
||||
'templatedata-modal-table-param-name',
|
||||
'templatedata-modal-table-param-required',
|
||||
'templatedata-modal-table-param-type',
|
||||
'templatedata-modal-table-param-type-number',
|
||||
'templatedata-modal-table-param-type-page',
|
||||
'templatedata-modal-table-param-type-string',
|
||||
'templatedata-modal-table-param-type-undefined',
|
||||
'templatedata-modal-table-param-type-user',
|
||||
'templatedata-modal-title',
|
||||
'templatedata-modal-title-templatedesc',
|
||||
'templatedata-modal-title-templateparams',
|
||||
)
|
||||
);
|
||||
|
||||
/* Configuration */
|
||||
|
||||
// Set this to true to use the template documentation
|
||||
// editor feature
|
||||
$wgTemplateDataUseGUI = false;
|
||||
|
|
836
modules/ext.templateDataGenerator.core.js
Normal file
836
modules/ext.templateDataGenerator.core.js
Normal file
|
@ -0,0 +1,836 @@
|
|||
( function ( $, mw ) {
|
||||
/**
|
||||
* TemplateDataGenerator generates the JSON string for templatedata
|
||||
* or reads existing templatedata string and allows for it to be edited
|
||||
* with a visual modal GUI.
|
||||
*
|
||||
* @author Moriel Schottlender
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
mw.libs.templateDataGenerator = ( function () {
|
||||
var paramBase, paramTypes, domObjects, curr;
|
||||
|
||||
/* Private helper functions */
|
||||
|
||||
/**
|
||||
* Show an error message in the main Edit screen
|
||||
*
|
||||
* @param {string} msg The message to display in the error box
|
||||
*/
|
||||
function showErrorEditPage( msg ) {
|
||||
domObjects.$errorBox.text( msg ).show();
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to clean up the aliases string-to-array
|
||||
*
|
||||
* @param {string} str Comma separated string
|
||||
* @returns {string[]} Cleaned-up alias array
|
||||
*/
|
||||
function cleanupAliasArray( str ) {
|
||||
return $.map( str.split( ',' ), function ( item ) {
|
||||
if ( $.trim( item ).length > 0 ) {
|
||||
return $.trim( item );
|
||||
}
|
||||
} );
|
||||
}
|
||||
|
||||
/**
|
||||
* Show an error message in the GUI
|
||||
*
|
||||
* @param {string} msg The message to be displayed in the error box
|
||||
*/
|
||||
function showErrorModal( msg ) {
|
||||
domObjects.$errorModalBox.text( msg ).show();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create `<select>` for parameter type based on the
|
||||
* options given by key/value
|
||||
*
|
||||
* @param {Object} options The key/value pair for the options
|
||||
* that should appear in the select input.
|
||||
* @returns {jQuery} <select> object
|
||||
*/
|
||||
function createTypeSelect( opts ) {
|
||||
var op,
|
||||
$sel = $( '<select>' );
|
||||
|
||||
for ( op in opts ) {
|
||||
$sel.append( $( '<option>' ).val( op ).text( opts[ op ] ) );
|
||||
}
|
||||
|
||||
return $sel;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse the JSON information from the wikitext
|
||||
*
|
||||
* If it exists, and prepare DOM elements from
|
||||
* the parameters into the global param DOM JSON.
|
||||
*
|
||||
* @param {string} wikitext The source of the template text
|
||||
* @returns {Object} Parameters object parsed from the JSON string
|
||||
*/
|
||||
function parseTemplateData( wikitext ) {
|
||||
var attrib,
|
||||
param,
|
||||
trimmedParam,
|
||||
arrayParamNamesForTrimming = [],
|
||||
jsonParams = {},
|
||||
parts = wikitext.match(
|
||||
/(<templatedata>)([\s\S]*?)(<\/templatedata>)/i
|
||||
);
|
||||
|
||||
// Check if <templatedata> exists
|
||||
if ( parts && parts[2] ) {
|
||||
// Make sure it's not empty
|
||||
if ( $.trim( parts[2] ).length > 0 ) {
|
||||
try {
|
||||
jsonParams = $.parseJSON( $.trim( parts[2] ) );
|
||||
} catch ( err ) {
|
||||
// Oops, JSON contains syntax error
|
||||
mw.log( 'TemplateData: ' + mw.msg( 'templatedata-errormsg-jsonbadformat' ) );
|
||||
if ( domObjects ) {
|
||||
showErrorEditPage( mw.msg( 'templatedata-errormsg-jsonbadformat' ) );
|
||||
}
|
||||
return {};
|
||||
}
|
||||
}
|
||||
// See if jsonParams has 'params'
|
||||
if ( jsonParams && jsonParams.params ) {
|
||||
// Add DOM elements to the JSON data params
|
||||
for ( param in jsonParams.params ) {
|
||||
// Trim parameter key if it contains trailing/leading whitespace
|
||||
trimmedParam = $.trim( param );
|
||||
|
||||
// Insert into the array for later trimming
|
||||
if ( trimmedParam !== param ) {
|
||||
arrayParamNamesForTrimming.push( param );
|
||||
}
|
||||
// Only create dom params if needed
|
||||
// This will allow the entire method to be called
|
||||
// individually, as a tool or for qunit tests
|
||||
if ( curr && curr.paramDomElements ) {
|
||||
setupDomParam( trimmedParam, attrib );
|
||||
}
|
||||
}
|
||||
}
|
||||
// Trim the params we need to in the JSON object param keys
|
||||
$.each( arrayParamNamesForTrimming, function ( index, paramid ) {
|
||||
trimmedParam = $.trim( paramid );
|
||||
jsonParams.params[trimmedParam] = jsonParams.params[paramid];
|
||||
delete jsonParams.params[paramid];
|
||||
} );
|
||||
}
|
||||
|
||||
return jsonParams;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a DOM element that correspond to the parameter and field
|
||||
*
|
||||
* @param {String} paramName Parameter name or id
|
||||
* @param {String} attrib The field that will correspond to the dom element
|
||||
*/
|
||||
function setupDomParam( paramName, attrib ) {
|
||||
var $tmpDom;
|
||||
curr.paramDomElements[paramName] = {};
|
||||
|
||||
// Create DOM elements per parameter
|
||||
for ( attrib in paramBase ) {
|
||||
// Set up the DOM element
|
||||
if ( attrib === 'type' ) {
|
||||
$tmpDom = createTypeSelect( paramTypes );
|
||||
} else {
|
||||
$tmpDom = paramBase[attrib].dom;
|
||||
}
|
||||
curr.paramDomElements[paramName][attrib] = $tmpDom.clone( true );
|
||||
curr.paramDomElements[paramName][attrib].data( 'paramid', paramName );
|
||||
curr.paramDomElements[paramName][attrib].attr( 'id', attrib + '_paramid_' + paramName );
|
||||
curr.paramDomElements[paramName][attrib].addClass( 'tdg-element-attr-' + attrib );
|
||||
|
||||
}
|
||||
// Set up the 'delete' button
|
||||
curr.paramDomElements[paramName].delbutton
|
||||
.text( mw.msg( 'templatedata-modal-button-delparam' ) )
|
||||
.addClass( 'tdg-param-del' )
|
||||
.attr( 'id', 'tdg-param-del' )
|
||||
.data( 'paramid', paramName );
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks the wikitext for template parameters and imports
|
||||
* those that aren't yet in the templatedata list.
|
||||
* Adapted from https://he.wikipedia.org/wiki/MediaWiki:Gadget-TemplateParamWizard.js
|
||||
*
|
||||
* @param {string} wikitext The source of the template text
|
||||
*/
|
||||
function importTemplateParams( wikitext ) {
|
||||
var newParam, matches, $row, paramName, paramID,
|
||||
paramExtractor = /{{3,}(.*?)[<|}]/mg,
|
||||
paramCounter = 0,
|
||||
existingParamNamesArray = [];
|
||||
|
||||
// fill up the existingParamNameArray with GUI params
|
||||
// So we can test against it while importing:
|
||||
// We should go by param name, not param ID, because
|
||||
// if the param is new, its id is new_randomString, and so
|
||||
// the actual representation is the value of the name field.
|
||||
for ( paramID in curr.paramDomElements ) {
|
||||
paramName = $.trim( curr.paramDomElements[paramID].name.val() );
|
||||
|
||||
// Validate
|
||||
if (
|
||||
paramName.length > 0 &&
|
||||
!paramName.match( /[\|=]|}}/ ) &&
|
||||
!curr.paramDomElements[paramID].tdgMarkedForDeletion &&
|
||||
$.inArray( paramName, existingParamNamesArray ) === -1
|
||||
) {
|
||||
existingParamNamesArray.push( paramName );
|
||||
}
|
||||
}
|
||||
|
||||
while ( ( matches = paramExtractor.exec( wikitext ) ) !== null ) {
|
||||
paramName = $.trim( matches[1] );
|
||||
|
||||
// Make sure the template itself is not giving us bad params
|
||||
if (
|
||||
paramName.length === 0 &&
|
||||
paramName.match( /[\|=]|}}/ )
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Make sure the param doesn't already exist in the GUI
|
||||
if ( $.inArray( paramName, existingParamNamesArray ) > -1 ) {
|
||||
// This is dupe, ignore it
|
||||
continue;
|
||||
} else {
|
||||
// Add name to the existingParamNamesArray
|
||||
existingParamNamesArray.push( paramName );
|
||||
|
||||
// Add to domParams
|
||||
newParam = addParam();
|
||||
newParam.name.val( paramName );
|
||||
$row = translateParamToRowDom( curr.paramsJson, newParam );
|
||||
domObjects.$modalTable.append( $row );
|
||||
paramCounter++;
|
||||
}
|
||||
}
|
||||
|
||||
if ( paramCounter === 0 ) {
|
||||
showErrorModal( mw.msg( 'templatedata-modal-errormsg-import-noparams' ) );
|
||||
} else {
|
||||
showErrorModal( mw.msg( 'templatedata-modal-notice-import-numparams', paramCounter ) );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a <table> DOM with initial headings for the parameters
|
||||
* The table headings will go by the paramBase
|
||||
*
|
||||
* @returns {jQuery} Table element
|
||||
*/
|
||||
function createParamTableDOM() {
|
||||
var $tbl, attrib,
|
||||
$tr = $( '<tr>' );
|
||||
|
||||
for ( attrib in paramBase ) {
|
||||
$tr.append(
|
||||
$( '<th>' )
|
||||
.text( paramBase[attrib].label )
|
||||
.addClass( 'tdg-title-' + attrib )
|
||||
);
|
||||
}
|
||||
|
||||
$tbl = $( '<table>' )
|
||||
.addClass( 'tdg-editTable' )
|
||||
.append( $tr );
|
||||
|
||||
return $tbl;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a <table> HTMLElement with initial headings for the parameters
|
||||
* The table headings will go by the paramBase
|
||||
*
|
||||
* @param {Object} paramsJson Object with current parameter values
|
||||
* @param {Object} paramAttrObj Object with parameter properties
|
||||
* @returns {jQuery} Table element
|
||||
*/
|
||||
function translateParamToRowDom( paramsJson, paramAttrObj ) {
|
||||
var $tdDom,
|
||||
$trDom,
|
||||
paramAttr,
|
||||
paramid = paramAttrObj.delbutton.data( 'paramid' );
|
||||
|
||||
$trDom = $( '<tr>' )
|
||||
.attr( 'id', 'param-' + paramid )
|
||||
.data( 'paramid', paramid );
|
||||
|
||||
// Go over the attributes for <td>s
|
||||
for ( paramAttr in paramAttrObj ) {
|
||||
// Check if value already exists for this in the original json
|
||||
if (
|
||||
paramsJson.params &&
|
||||
paramsJson.params[paramid] &&
|
||||
paramsJson.params[paramid][paramAttr]
|
||||
) {
|
||||
// make sure we set the value correctly based on the DOM element
|
||||
if ( paramAttrObj[paramAttr].prop( 'type' ) === 'checkbox' ) {
|
||||
paramAttrObj[paramAttr].prop( 'checked', paramsJson.params[paramid][paramAttr] );
|
||||
} else {
|
||||
paramAttrObj[paramAttr].val( paramsJson.params[paramid][paramAttr] );
|
||||
}
|
||||
}
|
||||
|
||||
$tdDom = $( '<td>' ).addClass( 'tdg-attr-' + paramAttr );
|
||||
|
||||
// Add label to 'required' checkbox
|
||||
if ( paramAttr === 'required' ) {
|
||||
$tdDom.append(
|
||||
$( '<label>' )
|
||||
.attr( 'for', paramAttr + '_paramid_' + paramid )
|
||||
.text( paramBase.required.label )
|
||||
.prepend( paramAttrObj[paramAttr] )
|
||||
);
|
||||
} else {
|
||||
$tdDom.append( paramAttrObj[paramAttr] );
|
||||
}
|
||||
|
||||
$trDom.append( $tdDom );
|
||||
}
|
||||
|
||||
// Set up the name
|
||||
if ( paramsJson && curr.paramsJson.params && curr.paramsJson.params[paramid] ) {
|
||||
$trDom.find( '.tdg-element-attr-name' ).val( paramid );
|
||||
}
|
||||
|
||||
return $trDom;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an empty parameter to the paramDomElements list
|
||||
*
|
||||
* @returns {jQuery} Table row element
|
||||
*/
|
||||
function addParam() {
|
||||
var attrib,
|
||||
$tmpDom,
|
||||
// Create a unique identifier for paramid
|
||||
paramid = 'new_' + $.now();
|
||||
|
||||
// Add to the DOM object
|
||||
curr.paramDomElements[paramid] = {};
|
||||
|
||||
for ( attrib in paramBase ) {
|
||||
// Set up the DOM element
|
||||
if ( attrib === 'type' ) {
|
||||
$tmpDom = createTypeSelect( paramTypes );
|
||||
} else {
|
||||
$tmpDom = paramBase[attrib].dom;
|
||||
}
|
||||
|
||||
curr.paramDomElements[paramid][attrib] = $tmpDom.clone( true );
|
||||
curr.paramDomElements[paramid][attrib].data( 'paramid', paramid );
|
||||
curr.paramDomElements[paramid][attrib].attr( 'id', attrib + '_paramid_' + paramid );
|
||||
curr.paramDomElements[paramid][attrib].addClass( 'tdg-element-attr-' + attrib );
|
||||
}
|
||||
|
||||
// Set up the 'delete' button
|
||||
curr.paramDomElements[paramid].delbutton
|
||||
.text( mw.msg( 'templatedata-modal-button-delparam' ) )
|
||||
.addClass( 'tdg-param-del' )
|
||||
.attr( 'id', 'tdg-param-del' )
|
||||
.data( 'paramid', paramid );
|
||||
|
||||
return curr.paramDomElements[paramid];
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate the Modal inputs before continuing to the actual 'apply'
|
||||
*
|
||||
* @returns {boolean} Passed validation
|
||||
*/
|
||||
function isFormValid() {
|
||||
var paramID,
|
||||
paramName,
|
||||
paramNameArray = [],
|
||||
passed = true,
|
||||
paramProblem = false;
|
||||
|
||||
// Reset
|
||||
$( '.tdgerror' ).removeClass( 'tdgerror' );
|
||||
domObjects.$errorModalBox.empty().hide();
|
||||
|
||||
// Go over the paramDomElements object, look for:
|
||||
// * Empty name fields
|
||||
// * Duplicate *name* values:
|
||||
// * Illegal characters in name fields: pipe, equal, }}
|
||||
for ( paramID in curr.paramDomElements ) {
|
||||
paramProblem = false;
|
||||
// Trim:
|
||||
paramName = curr.paramDomElements[paramID].name.val();
|
||||
curr.paramDomElements[paramID].name.val( paramName );
|
||||
|
||||
// Ignore if the param was flagged for deletion
|
||||
if ( curr.paramDomElements[paramID].tdgMarkedForDeletion ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Name field is empty
|
||||
if ( paramName.length === 0 ) {
|
||||
passed = false;
|
||||
paramProblem = true;
|
||||
}
|
||||
|
||||
// Check for illegal characters in param name
|
||||
if ( paramName.match( /[\|=]|}}/ ) ) {
|
||||
passed = false;
|
||||
paramProblem = true;
|
||||
}
|
||||
|
||||
// Check for dupes
|
||||
if ( $.inArray( paramName, paramNameArray ) > -1 ) {
|
||||
// This is dupe!
|
||||
passed = false;
|
||||
paramProblem = true;
|
||||
} else {
|
||||
paramNameArray.push( paramName );
|
||||
}
|
||||
|
||||
if ( paramProblem ) {
|
||||
domObjects.$modalTable.find( '#param-' + paramID ).addClass( 'tdgerror' );
|
||||
}
|
||||
}
|
||||
|
||||
return passed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the GUI completely, including the domElements and the json
|
||||
*/
|
||||
function globalReset() {
|
||||
// Reset Modal GUI
|
||||
domObjects.$modalBox.empty();
|
||||
domObjects.$errorModalBox.empty().hide();
|
||||
|
||||
// Reset vars
|
||||
curr = {
|
||||
paramDomElements: {},
|
||||
paramsJson: {}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Take the amended JSON object and stringify it, putting
|
||||
* it back into the original wikitext.
|
||||
* @param {Object} newJsonObject Edited json object
|
||||
* @param {String} originalWikitext The original wikitext
|
||||
* @returns {String} Thew new wikitext
|
||||
*/
|
||||
function amendWikitext( newJsonObject, originalWikitext ) {
|
||||
var finalOutput = '',
|
||||
wikitext = originalWikitext || domObjects.wikitext;
|
||||
|
||||
// Check if we started with existing <templatedata> tags
|
||||
if ( wikitext.match(
|
||||
/(<templatedata>)([\s\S]*?)(<\/templatedata>)/i)
|
||||
) {
|
||||
// replace the <templatedata> tags
|
||||
finalOutput = wikitext.replace(
|
||||
/(<templatedata>)([\s\S]*?)(<\/templatedata>)/i,
|
||||
'<templatedata>\n' + JSON.stringify( newJsonObject, null, ' ' ) + '\n</templatedata>'
|
||||
);
|
||||
} else {
|
||||
// add <templatedata> tags
|
||||
finalOutput = wikitext + '\n<templatedata>\n';
|
||||
finalOutput += JSON.stringify( newJsonObject, null, ' ' );
|
||||
finalOutput += '\n</templatedata>';
|
||||
}
|
||||
|
||||
return finalOutput;
|
||||
}
|
||||
/**
|
||||
* Apply the changes made to the parameters to the json
|
||||
*
|
||||
* @param {Object} originalJsonObject [description]
|
||||
* @param {Object<String,jQuery>} modalDomElements The structure of the
|
||||
* dom elements in the editor, sorted by parameter id and jQuery editable
|
||||
* object
|
||||
* @param {String} originalWikitext The original wikitext
|
||||
* @param {Boolean} DoNotCheckForm if set to true, the system will not validate the form
|
||||
* used mostly for tests.
|
||||
* @returns {Object} Amended json object
|
||||
*/
|
||||
function applyChangeToJSON( originalJsonObject, modalDomElements, doNotCheckForm ) {
|
||||
var paramid,
|
||||
paramName,
|
||||
paramProp,
|
||||
$domEl,
|
||||
domElements,
|
||||
newValue,
|
||||
paramObj,
|
||||
propExists,
|
||||
tmpjson,
|
||||
// Compare the original to the new changes
|
||||
outputJson = originalJsonObject || curr.paramsJson,
|
||||
paramDomElements = modalDomElements || curr.paramDomElements;
|
||||
|
||||
// Validate
|
||||
if ( !doNotCheckForm ) {
|
||||
if ( !isFormValid() ) {
|
||||
showErrorModal( mw.msg( 'templatedata-modal-errormsg' ) );
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Update the description
|
||||
if ( $( '.tdg-template-description' ).length ) {
|
||||
outputJson.description = $( '.tdg-template-description' ).val();
|
||||
}
|
||||
|
||||
// First check if there's outpuJson.params
|
||||
if ( !outputJson.params ) {
|
||||
outputJson.params = {};
|
||||
}
|
||||
|
||||
// Go over the parameters, check if param was marked as deleted
|
||||
// in curr.paramsJson
|
||||
for ( paramid in paramDomElements ) {
|
||||
domElements = paramDomElements[paramid];
|
||||
// Get the name of the param
|
||||
paramName = domElements.name.val();
|
||||
// New parameter added
|
||||
if ( !outputJson.params[paramid] ) {
|
||||
paramObj = outputJson.params[paramName] = {};
|
||||
} else {
|
||||
// Check if name changed
|
||||
if ( paramName !== paramid ) {
|
||||
// change the param name
|
||||
outputJson.params[paramName] = {};
|
||||
tmpjson = $.extend( true, {}, outputJson.params[paramid] );
|
||||
$.extend( true, outputJson.params[paramName], tmpjson );
|
||||
// delete the old param
|
||||
delete outputJson.params[paramid];
|
||||
}
|
||||
}
|
||||
|
||||
// Parameter marked for deletion
|
||||
if ( domElements.tdgMarkedForDeletion ) {
|
||||
delete outputJson.params[paramName];
|
||||
// Move to next iteration
|
||||
continue;
|
||||
}
|
||||
|
||||
paramObj = outputJson.params[paramName];
|
||||
|
||||
// Go over the properties that have DOM elements
|
||||
for ( paramProp in domElements ) {
|
||||
propExists = ( paramObj.hasOwnProperty( paramProp ) );
|
||||
$domEl = domElements[paramProp];
|
||||
|
||||
// Check if value changed
|
||||
switch ( paramProp ) {
|
||||
// Skip:
|
||||
case 'name':
|
||||
case 'delbutton':
|
||||
break;
|
||||
case 'aliases':
|
||||
newValue = cleanupAliasArray( $domEl.val() );
|
||||
if ( propExists &&
|
||||
newValue.sort().join( '|' ) !== paramObj.aliases.sort().join( '|' ) ) {
|
||||
// Replace:
|
||||
if ( newValue.length === 0 ) {
|
||||
delete paramObj.aliases;
|
||||
continue;
|
||||
} else {
|
||||
paramObj.aliases = newValue;
|
||||
}
|
||||
} else if ( !propExists ) {
|
||||
if ( newValue.length > 0 ) {
|
||||
paramObj.aliases = newValue;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'description':
|
||||
case 'default':
|
||||
case 'label':
|
||||
newValue = $domEl.val();
|
||||
if ( paramObj[paramProp] !== newValue ) {
|
||||
if ( !newValue || newValue.length === 0 ) {
|
||||
delete paramObj[paramProp];
|
||||
continue;
|
||||
} else {
|
||||
paramObj[paramProp] = newValue;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'type':
|
||||
newValue = $domEl.val();
|
||||
if ( paramObj[paramProp] !== newValue ) {
|
||||
if ( newValue === 'undefined' ) {
|
||||
delete paramObj[paramProp];
|
||||
continue;
|
||||
} else {
|
||||
paramObj[paramProp] = newValue;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'required':
|
||||
newValue = $domEl.prop( 'checked' );
|
||||
if ( paramObj[paramProp] !== newValue ) {
|
||||
paramObj[paramProp] = newValue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return outputJson;
|
||||
}
|
||||
/**
|
||||
* Create i18n-compatible Modal Buttons
|
||||
* also contains the 'apply' functionality
|
||||
*
|
||||
* @param {string} btnApply the text for the 'apply' button
|
||||
* @param {string} btnCancel the text for the 'cancel' button
|
||||
* @returns {Array} Button objects with their functionality, for the modal
|
||||
*/
|
||||
function i18nModalButtons( btnApply, btnCancel ) {
|
||||
var modalButtons = {};
|
||||
|
||||
modalButtons[btnApply] = function() {
|
||||
var newJson = applyChangeToJSON(),
|
||||
finalOutput = amendWikitext( newJson );
|
||||
|
||||
|
||||
// Close the modal
|
||||
domObjects.$modalBox.dialog( 'close' );
|
||||
|
||||
// Trigger the closing event so the new output can be put
|
||||
// back to the textbox
|
||||
domObjects.$modalBox.trigger( 'TemplateDataGeneratorDone', [ finalOutput ] );
|
||||
|
||||
return finalOutput;
|
||||
};
|
||||
|
||||
modalButtons[btnCancel] = function () {
|
||||
domObjects.$modalBox.dialog( 'close' );
|
||||
};
|
||||
|
||||
return modalButtons;
|
||||
}
|
||||
|
||||
/** Public Methods **/
|
||||
return {
|
||||
/**
|
||||
* Injects required DOM elements to the edit screen
|
||||
*/
|
||||
init: function () {
|
||||
paramBase = {
|
||||
name: {
|
||||
label: mw.msg( 'templatedata-modal-table-param-name' ),
|
||||
dom: $( '<input>' )
|
||||
},
|
||||
aliases: {
|
||||
label: mw.msg( 'templatedata-modal-table-param-aliases' ),
|
||||
dom: $( '<input>' )
|
||||
},
|
||||
label: {
|
||||
label: mw.msg( 'templatedata-modal-table-param-label' ),
|
||||
dom: $( '<input>' )
|
||||
},
|
||||
description: {
|
||||
label: mw.msg( 'templatedata-modal-table-param-desc' ),
|
||||
dom: $( '<textarea>' )
|
||||
},
|
||||
type: {
|
||||
label: mw.msg( 'templatedata-modal-table-param-type' ),
|
||||
dom: $( '<select>' )
|
||||
},
|
||||
'default': {
|
||||
label: mw.msg( 'templatedata-modal-table-param-default' ),
|
||||
dom: $( '<textarea>' )
|
||||
},
|
||||
'required': {
|
||||
label: mw.msg( 'templatedata-modal-table-param-required' ),
|
||||
dom: $( '<input type="checkbox" />' )
|
||||
},
|
||||
delbutton: {
|
||||
label: mw.msg( 'templatedata-modal-table-param-actions' ),
|
||||
dom: $( '<button>' )
|
||||
.button()
|
||||
.addClass( 'tdg-param-button-del buttonRed' )
|
||||
.click( function () {
|
||||
var paramid = $( this ).data( 'paramid' );
|
||||
// Flag as DELETED in curr.paramDomElements[paramid] (property tdgDELETED)
|
||||
if ( curr.paramDomElements[paramid] ) {
|
||||
curr.paramDomElements[paramid].tdgMarkedForDeletion = true;
|
||||
}
|
||||
// Delete the DOM row from table:
|
||||
// (Don't delete property from paramDomElements,
|
||||
// so when we go over the DOM elements on 'apply' this
|
||||
// parameter is recognized as marked for deletion)
|
||||
$( '#param-' + paramid ).remove();
|
||||
} )
|
||||
}
|
||||
};
|
||||
paramTypes = {
|
||||
'undefined': mw.msg( 'templatedata-modal-table-param-type-undefined' ),
|
||||
'number': mw.msg( 'templatedata-modal-table-param-type-number' ),
|
||||
'string': mw.msg( 'templatedata-modal-table-param-type-string' ),
|
||||
'string/wiki-user-name': mw.msg( 'templatedata-modal-table-param-type-user' ),
|
||||
'string/wiki-page-name': mw.msg( 'templatedata-modal-table-param-type-page' )
|
||||
};
|
||||
domObjects = {
|
||||
$editButton: $( '<button>' )
|
||||
.button()
|
||||
.addClass( 'tdg-editscreen-main-button' )
|
||||
.text( mw.msg( 'templatedata-editbutton' ) ),
|
||||
$errorBox: $( '<div>' )
|
||||
.addClass( 'tdg-editscreen-error-msg' )
|
||||
.hide(),
|
||||
$errorModalBox: $( '<div>' )
|
||||
.addClass( 'tdg-errorbox' )
|
||||
.hide(),
|
||||
$modalBox: $( '<div>' )
|
||||
.addClass( 'tdg-editscreen-modal-form' )
|
||||
.attr( 'id', 'modal-box' )
|
||||
.attr( 'title', mw.msg( 'templatedata-modal-title' ) )
|
||||
.hide(),
|
||||
$modalTable: {},
|
||||
wikitext: ''
|
||||
};
|
||||
curr = {
|
||||
paramDomElements: {},
|
||||
paramsJson: {}
|
||||
};
|
||||
// Return the objects to be added to the edit page
|
||||
return domObjects;
|
||||
},
|
||||
|
||||
/**
|
||||
* Create the modal screen and populate it with existing
|
||||
* data, if available
|
||||
*
|
||||
* @param {jQuery} $wikitextBox Article edit textarea
|
||||
* @returns {jQuery} Modal div element
|
||||
*/
|
||||
createModal: function ( wikitext ) {
|
||||
var $row,
|
||||
paramObj,
|
||||
$descBox;
|
||||
|
||||
// Reset:
|
||||
globalReset();
|
||||
domObjects.wikitext = wikitext;
|
||||
$descBox = $( '<textarea>' ).addClass( 'tdg-template-description' );
|
||||
domObjects.$modalTable = createParamTableDOM();
|
||||
|
||||
// Parse JSON
|
||||
curr.paramsJson = parseTemplateData( wikitext );
|
||||
if ( !$.isEmptyObject( curr.paramsJson ) ) {
|
||||
if ( curr.paramsJson.description ) {
|
||||
$descBox.text( curr.paramsJson.description );
|
||||
}
|
||||
|
||||
// Build the parameter row DOMs
|
||||
for ( paramObj in curr.paramDomElements ) {
|
||||
// Make the row
|
||||
$row = translateParamToRowDom( curr.paramsJson, curr.paramDomElements[paramObj] );
|
||||
domObjects.$modalTable.append( $row );
|
||||
}
|
||||
}
|
||||
|
||||
// Build the Modal window
|
||||
domObjects.$modalBox
|
||||
.append( $( '<h3>' )
|
||||
.addClass( 'tdg-title' )
|
||||
.text( mw.msg( 'templatedata-modal-title-templatedesc' ) )
|
||||
)
|
||||
.append( $descBox )
|
||||
.append( domObjects.$errorModalBox )
|
||||
.append( $( '<h3>' )
|
||||
.addClass( 'tdg-title' )
|
||||
.text( mw.msg( 'templatedata-modal-title-templateparams' ) )
|
||||
)
|
||||
.append(
|
||||
$( '<button>' )
|
||||
.button()
|
||||
.text( mw.msg( 'templatedata-modal-button-importParams' ) )
|
||||
.addClass( 'tdg-addparam' )
|
||||
.click( function () {
|
||||
// TODO: Check that we're not in the /doc subpage
|
||||
importTemplateParams( wikitext );
|
||||
} ) )
|
||||
.append( domObjects.$modalTable )
|
||||
.append(
|
||||
$( '<button>' )
|
||||
.button()
|
||||
.text( mw.msg( 'templatedata-modal-button-addparam' ) )
|
||||
.addClass( 'tdg-addparam' )
|
||||
.click( function () {
|
||||
var newParam = addParam(),
|
||||
$row = translateParamToRowDom( curr.paramsJson, newParam );
|
||||
|
||||
domObjects.$modalTable.append( $row );
|
||||
} )
|
||||
);
|
||||
|
||||
domObjects.$modalBox.dialog( {
|
||||
autoOpen: false,
|
||||
height: $( window ).height() * 0.8,
|
||||
width: $( window ).width() * 0.8,
|
||||
modal: true,
|
||||
buttons: i18nModalButtons(
|
||||
mw.msg( 'templatedata-modal-button-apply' ),
|
||||
mw.msg( 'templatedata-modal-button-cancel' )
|
||||
),
|
||||
close: function () {
|
||||
domObjects.$modalBox.empty();
|
||||
}
|
||||
} );
|
||||
|
||||
// Return the modal object
|
||||
return domObjects.$modalBox;
|
||||
},
|
||||
|
||||
/** Testing functions **/
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @inheritDoc #parseTemplateData
|
||||
*/
|
||||
parseTemplateData: function( wikitext ) {
|
||||
return parseTemplateData( wikitext );
|
||||
},
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @inheritDoc #applyChangesToJSON
|
||||
*/
|
||||
applyChangesToJSON: function( originalJsonObject, modalDomElements, doNotCheckForm ) {
|
||||
return applyChangeToJSON( originalJsonObject, modalDomElements, doNotCheckForm );
|
||||
},
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @inheritDoc #amendWikitext
|
||||
*/
|
||||
amendWikitext: function( newJsonObject, originalWikitext ) {
|
||||
return amendWikitext( newJsonObject, originalWikitext );
|
||||
},
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @inheritDoc #translateParamToRowDom
|
||||
*/
|
||||
translateParamToRowDom: function( paramsJson, paramAttrObj ) {
|
||||
return translateParamToRowDom( paramsJson, paramAttrObj );
|
||||
}
|
||||
|
||||
};
|
||||
} )();
|
||||
}( jQuery, mediaWiki ) );
|
87
modules/ext.templateDataGenerator.css
Normal file
87
modules/ext.templateDataGenerator.css
Normal file
|
@ -0,0 +1,87 @@
|
|||
.tdg-editscreen-main-button {
|
||||
padding: 5px;
|
||||
font-size: 120%;
|
||||
}
|
||||
|
||||
.tdg-editTable {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.tdg-editTable th, .tdg-editTable td{
|
||||
padding:0;
|
||||
}
|
||||
|
||||
.tdg-editTable > tbody > tr {
|
||||
vertical-align: top;
|
||||
}
|
||||
.tdg-editTable th {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.tdg-editTable > tbody > tr > td:hover,
|
||||
.tdg-editTable > tbody > tr:hover > td {
|
||||
background-color: #BECDD4;
|
||||
}
|
||||
|
||||
.tdg-editTable input,
|
||||
.tdg-editTable select,
|
||||
.tdg-editTable textarea,
|
||||
.tdg-template-description{
|
||||
background: transparent;
|
||||
border: 1px solid #999999;
|
||||
padding: 5px;
|
||||
box-sizing: border-box;
|
||||
-moz-box-sizing: border-box;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.tdg-editTable input:not([type="checkbox"]),
|
||||
.tdg-editTable select,
|
||||
.tdg-editTable textarea{
|
||||
height: 40px;
|
||||
}
|
||||
|
||||
.tdg-editTable .tdg-title-delbutton, .tdg-title-required {
|
||||
width: 80px;
|
||||
}
|
||||
.tdg-editTable .tdg-attr-required {
|
||||
text-align: center;
|
||||
}
|
||||
.tdg-editTable .tdg-attr-required label {
|
||||
display: block;
|
||||
}
|
||||
.tdg-attr-type select {
|
||||
padding-top: 15px;
|
||||
}
|
||||
.tdg-attr-type {
|
||||
width: 130px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.tdg-editTable .tdgerror {
|
||||
background-color: #F2CBCB;
|
||||
}
|
||||
|
||||
/** Start small, grow on focus: */
|
||||
.tdg-title-name,
|
||||
.tdg-title-aliases,
|
||||
.tdg-title-label {
|
||||
width: 100px;
|
||||
}
|
||||
|
||||
.tdg-element-attr-name,
|
||||
.tdg-element-attr-aliases,
|
||||
.tdg-element-attr-label {
|
||||
width: 100px;
|
||||
}
|
||||
|
||||
.tdg-element-attr-name:focus,
|
||||
.tdg-element-attr-aliases:focus,
|
||||
.tdg-element-attr-label:focus {
|
||||
width: 200px;
|
||||
}
|
||||
|
||||
.tdg-element-attr-description:focus,
|
||||
.tdg-element-attr-default:focus {
|
||||
height: 70px;
|
||||
}
|
43
modules/ext.templateDataGenerator.editPage.js
Normal file
43
modules/ext.templateDataGenerator.editPage.js
Normal file
|
@ -0,0 +1,43 @@
|
|||
( function ( $, mw ) {
|
||||
/**
|
||||
* TemplateData Generator button fixture
|
||||
* The button will appear on Template namespaces only, above the edit textbox
|
||||
*
|
||||
* @author Moriel Schottlender
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
$( document ).ready(function () {
|
||||
var tmplDataGen, editboxObjects,
|
||||
$textbox = $( '#wpTextbox1' );
|
||||
|
||||
// Check if there's an editor textarea and if we're in the proper namespace
|
||||
if ( $textbox.length > 0 && mw.config.get( 'wgCanonicalNamespace' ) === 'Template' ) {
|
||||
|
||||
tmplDataGen = mw.libs.templateDataGenerator;
|
||||
editboxObjects = tmplDataGen.init();
|
||||
|
||||
// Add the button and modal element to the document
|
||||
$( '#mw-content-text' )
|
||||
.prepend(
|
||||
editboxObjects.$modalBox,
|
||||
editboxObjects.$errorBox,
|
||||
editboxObjects.$editButton
|
||||
);
|
||||
|
||||
$( '.tdg-editscreen-main-button' ).click( function () {
|
||||
var $modalBox = tmplDataGen.createModal( $textbox.val() );
|
||||
|
||||
// open the dialog
|
||||
$modalBox.dialog( 'open' );
|
||||
|
||||
// respond to modal close event
|
||||
$modalBox.on( 'TemplateDataGeneratorDone', function( e, output ) {
|
||||
$textbox.val( output );
|
||||
} );
|
||||
} );
|
||||
}
|
||||
|
||||
} );
|
||||
|
||||
}( jQuery, mediaWiki ) );
|
269
tests/ext.templateData.tests.js
Normal file
269
tests/ext.templateData.tests.js
Normal file
|
@ -0,0 +1,269 @@
|
|||
/**
|
||||
* TemplateData Generator GUI Unit Tests
|
||||
*/
|
||||
|
||||
( function ( $, mw ) {
|
||||
'use strict';
|
||||
|
||||
QUnit.module( 'ext.templateData', QUnit.newMwEnvironment() );
|
||||
|
||||
var wikitext = 'Some initial test sentence.\n' +
|
||||
'<templatedata>\n' +
|
||||
'{\n' +
|
||||
' "description": "This is a description of the template.",\n' +
|
||||
' "params": {\n' +
|
||||
' "user": {\n' +
|
||||
' "label": "Username",\n' +
|
||||
' "type": "string/wiki-user-name",\n' +
|
||||
' "default": "some default value here.",\n' +
|
||||
' "required": true,\n' +
|
||||
' "description": "User name of person who forgot to sign their comment.",\n' +
|
||||
' "aliases": ["1"]\n' +
|
||||
' },\n' +
|
||||
' "date": {\n' +
|
||||
' "label": "Date",\n' +
|
||||
' "aliases": ["2", "3"]\n' +
|
||||
' },\n' +
|
||||
' "year": {\n' +
|
||||
' "label": "Year",\n' +
|
||||
' "type": "number"\n' +
|
||||
' },\n' +
|
||||
' "comment": {\n' +
|
||||
' "required": false\n' +
|
||||
' }\n' +
|
||||
' }\n' +
|
||||
'}\n' +
|
||||
'</templatedata>\n' +
|
||||
'Some following sentence.';
|
||||
|
||||
QUnit.test( 'TemplateData modal display', 11, function ( assert ) {
|
||||
var $modalBox, tmplDataGenTest1;
|
||||
|
||||
tmplDataGenTest1 = mw.libs.templateDataGenerator;
|
||||
tmplDataGenTest1.init();
|
||||
|
||||
$modalBox = tmplDataGenTest1.createModal( wikitext );
|
||||
|
||||
// Tests
|
||||
assert.equal(
|
||||
$modalBox.find( '.tdg-template-description' ).val(),
|
||||
'This is a description of the template.',
|
||||
'Template description'
|
||||
);
|
||||
|
||||
assert.equal(
|
||||
$modalBox.find( '.tdg-element-attr-name' ).length,
|
||||
4,
|
||||
'Number of parameters in edit modal table.'
|
||||
);
|
||||
|
||||
// Check for proper parsing
|
||||
assert.equal(
|
||||
$modalBox.find( '#param-user .tdg-element-attr-name' ).val(),
|
||||
'user',
|
||||
'Parameter details: name.'
|
||||
);
|
||||
|
||||
assert.equal(
|
||||
$modalBox.find( '#param-date .tdg-element-attr-aliases' ).val(),
|
||||
'2,3',
|
||||
'Parameter details: aliases (multiple).'
|
||||
);
|
||||
|
||||
assert.equal(
|
||||
$modalBox.find( '#param-user .tdg-element-attr-aliases' ).val(),
|
||||
'1',
|
||||
'Parameter details: aliases (single).'
|
||||
);
|
||||
|
||||
assert.equal(
|
||||
$modalBox.find( '#param-user .tdg-element-attr-label' ).val(),
|
||||
'Username',
|
||||
'Parameter details: label.'
|
||||
);
|
||||
|
||||
assert.equal(
|
||||
$modalBox.find( '#param-user .tdg-element-attr-description' ).val(),
|
||||
'User name of person who forgot to sign their comment.',
|
||||
'Parameter details: description.'
|
||||
);
|
||||
|
||||
assert.equal(
|
||||
$modalBox.find( '#param-user .tdg-element-attr-type' ).val(),
|
||||
'string/wiki-user-name',
|
||||
'Parameter details: type.'
|
||||
);
|
||||
|
||||
assert.equal(
|
||||
$modalBox.find( '#param-user .tdg-element-attr-default' ).val(),
|
||||
'some default value here.',
|
||||
'Parameter details: default.'
|
||||
);
|
||||
|
||||
assert.equal(
|
||||
$modalBox.find( '#param-user .tdg-element-attr-required' ).prop( 'checked' ),
|
||||
true,
|
||||
'Parameter details: required.'
|
||||
);
|
||||
|
||||
assert.equal(
|
||||
$modalBox.find( '#param-year .tdg-element-attr-required' ).prop( 'checked' ),
|
||||
false,
|
||||
'Parameter details: non required.'
|
||||
);
|
||||
|
||||
} );
|
||||
|
||||
|
||||
QUnit.test( 'TemplateData JSON manipulation', 4, function ( assert ) {
|
||||
var $modalDomElements, parsedJson, expectedTextResult,
|
||||
exampleJson, changedParsedJson, reparsedJson,
|
||||
tmplDataGenTest2 = mw.libs.templateDataGenerator,
|
||||
origText = 'Some initial test sentence.\n' +
|
||||
'<templatedata>\n' +
|
||||
'{\n' +
|
||||
' "description": "This is a description of the template.",\n' +
|
||||
' "params": {\n' +
|
||||
' "user": {\n' +
|
||||
' "label": "Username",\n' +
|
||||
' "type": "string/wiki-user-name",\n' +
|
||||
' "default": "some default value here.",\n' +
|
||||
' "required": true,\n' +
|
||||
' "description": "User name of person who forgot to sign their comment.",\n' +
|
||||
' "aliases": ["1"]\n' +
|
||||
' },\n' +
|
||||
' "date": {\n' +
|
||||
' "label": "Date",\n' +
|
||||
' "aliases": ["2", "3"]\n' +
|
||||
' },\n' +
|
||||
' "year": {\n' +
|
||||
' "label": "Year",\n' +
|
||||
' "type": "number"\n' +
|
||||
' },\n' +
|
||||
' "comment": {\n' +
|
||||
' "required": false,\n' +
|
||||
' "somethingelse": "this should stay"\n' +
|
||||
' }\n' +
|
||||
' },\n' +
|
||||
' "testing": {\n' +
|
||||
' "something": {\n' +
|
||||
' "completely": "random"\n' +
|
||||
' }\n' +
|
||||
' }\n' +
|
||||
'}\n' +
|
||||
'</templatedata>\n' +
|
||||
'Some following sentence.';
|
||||
|
||||
parsedJson = tmplDataGenTest2.parseTemplateData( origText );
|
||||
|
||||
assert.equal(
|
||||
parsedJson.testing.something.completely,
|
||||
'random',
|
||||
'Parse original JSON and preserve all data.'
|
||||
);
|
||||
|
||||
// Copy the parsed JSON object so we can manually
|
||||
// manipulate it
|
||||
changedParsedJson = $.extend( true, {}, parsedJson );
|
||||
|
||||
// Partial dom elements on purpose, to make sure
|
||||
// that the rest of the json object, even fields that are
|
||||
// not represented in the dom elements, are preserved
|
||||
$modalDomElements = {
|
||||
'user': {
|
||||
'name': $( '<input>' ).val( 'user' ),
|
||||
'label': $( '<input>' ).val( 'changed to another label' ),
|
||||
}
|
||||
};
|
||||
|
||||
// Manually change the object we copied earlier to test against
|
||||
changedParsedJson.params.user.label = 'changed to another label';
|
||||
|
||||
assert.deepEqual(
|
||||
mw.libs.templateDataGenerator.applyChangesToJSON( parsedJson, $modalDomElements, true ),
|
||||
changedParsedJson,
|
||||
'Preserve parameters on edit.'
|
||||
);
|
||||
|
||||
// Name change
|
||||
// Notice, parsedJson also had its user.label change, so we have
|
||||
// to do the same to our new test and change both label and name.
|
||||
$modalDomElements = {
|
||||
'user': {
|
||||
'name': $( '<input>' ).val( 'anotherName' ),
|
||||
'label': $( '<input>' ).val( 'changed to another label' ),
|
||||
}
|
||||
};
|
||||
|
||||
// Copy the parsed JSON object so we can manually
|
||||
// manipulate it
|
||||
changedParsedJson = $.extend( true, {}, parsedJson );
|
||||
changedParsedJson.params.anotherName = {};
|
||||
$.extend( true, changedParsedJson.params.anotherName, parsedJson.params.user );
|
||||
delete changedParsedJson.params.user;
|
||||
|
||||
// Re-parse the json so we can manipulate it in exampleJson
|
||||
reparsedJson = mw.libs.templateDataGenerator.parseTemplateData( origText );
|
||||
|
||||
// Get the system's response to changing the name
|
||||
exampleJson = mw.libs.templateDataGenerator.applyChangesToJSON( reparsedJson, $modalDomElements, true );
|
||||
|
||||
assert.deepEqual(
|
||||
exampleJson,
|
||||
changedParsedJson,
|
||||
'Change parameter name.'
|
||||
);
|
||||
|
||||
// Back to wikitext
|
||||
// Since 'parsedJson' was changed in previous tests, we'll use it
|
||||
|
||||
expectedTextResult = 'Some initial test sentence.\n' +
|
||||
'<templatedata>\n' +
|
||||
'{\n' +
|
||||
' "description": "This is a description of the template.",\n' +
|
||||
' "params": {\n' +
|
||||
' "date": {\n' +
|
||||
' "label": "Date",\n' +
|
||||
' "aliases": [\n' +
|
||||
' "2",\n' +
|
||||
' "3"\n' +
|
||||
' ]\n' +
|
||||
' },\n' +
|
||||
' "year": {\n' +
|
||||
' "label": "Year",\n' +
|
||||
' "type": "number"\n' +
|
||||
' },\n' +
|
||||
' "comment": {\n' +
|
||||
' "required": false,\n' +
|
||||
' "somethingelse": "this should stay"\n' +
|
||||
' },\n' +
|
||||
' "anotherName": {\n' +
|
||||
' "label": "changed to another label",\n' +
|
||||
' "type": "string/wiki-user-name",\n' +
|
||||
' "default": "some default value here.",\n' +
|
||||
' "required": true,\n' +
|
||||
' "description": "User name of person who forgot to sign their comment.",\n' +
|
||||
' "aliases": [\n' +
|
||||
' "1"\n' +
|
||||
' ]\n' +
|
||||
' }\n' +
|
||||
' },\n' +
|
||||
' "testing": {\n' +
|
||||
' "something": {\n' +
|
||||
' "completely": "random"\n' +
|
||||
' }\n' +
|
||||
' }\n' +
|
||||
'}\n' +
|
||||
'</templatedata>\n' +
|
||||
'Some following sentence.';
|
||||
|
||||
// Using 'exampleJson' with the previous name change and label change
|
||||
assert.equal(
|
||||
mw.libs.templateDataGenerator.amendWikitext( exampleJson, origText ),
|
||||
expectedTextResult,
|
||||
'Returning edited json into original wikitext.'
|
||||
);
|
||||
|
||||
} );
|
||||
|
||||
}( jQuery, mediaWiki ) );
|
Loading…
Reference in a new issue