/**
* This is an on-site JS hack to make editing and adding descriptions easier.
* This has only been tested on Information templates, but it probably works elsewhere...
* To use, add 'importScript( 'User:MarkTraceur/editDescriptions.js' );' to [[Special:MyPage/common.js]]
* and visit a file page that contains descriptions.
*/
( function ( $, mw ) {
var api = new mw.Api(),
debug = true,
pageTitle = mw.Title.newFromText( mw.config.get( 'wgPageName' ) ).getPrefixedDb();
function plog( msg ) {
if ( debug === true ) {
console.log( msg );
}
}
function getLanguageDropdown( defaultLang ) {
return api.get( {
format: 'json',
action: 'query',
list: 'categorymembers',
cmtitle: 'Category:Language_templates',
cmprop: 'title',
cmlimit: 500
} ).then( function ( data ) {
var $dropdown = $( '<select>' ),
tpls = [];
$.each( data.query.categorymembers, function ( i, tpl ) {
var title = mw.Title.newFromText( tpl.title ),
langCode = title.getMain().toLowerCase();
if ( tpl.ns === 10 && tpl.title !== 'Template:Unknown language' ) {
$dropdown.append(
$( '<option>' )
.prop( 'selected', langCode === defaultLang )
.text( langCode )
.val( langCode )
);
}
} );
return $.Deferred().resolve( $dropdown );
} );
}
function getParsedDescription( newText ) {
return api.post( {
action: 'parse',
format: 'json',
title: pageTitle,
text: newText,
prop: 'text'
} ).then( function ( data ) {
if ( data && data.parse && data.parse.text ) {
return $.Deferred().resolve( $( $.parseHTML( data.parse.text['*'] ) ) );
} else {
return $.Deferred().reject();
}
} );
}
function getWikitext() {
return api.get( {
format: 'json',
action: 'query',
titles: pageTitle,
prop: 'revisions',
rvprop: 'content|timestamp',
indexpageids: true
} ).then( function ( data ) {
var revision, timestamp,
q = data.query,
pages = q.pages,
pageids = q.pageids,
page = pages[pageids[0]];
$.each( page.revisions, function ( i, rev ) {
content = rev['*'];
timestamp = rev.timestamp;
return false;
} );
return $.Deferred().resolve( content, timestamp );
} );
}
function updateWikitext( content, summary, timestamp ) {
summary = summary || 'Modified descriptions with [[User:MarkTraceur/editDescriptions.js]].';
return api.postWithToken( 'edit', {
format: 'json',
action: 'edit',
title: pageTitle,
summary: summary,
text: content
} );
}
function modifyDescriptions( wt, replaceFn, addFn ) {
var i, replacementStart, replacementEnd,
languagesFound = 0,
curlang, newdesc, replacement,
descSearchString, start,
lastIndex = 0,
replacements = [],
capturing = '',
lbraces = 0,
rbraces = 0,
bracelevels = 0,
matches = wt.match( /\|\s*[Dd]escription\s*=/ ),
newWt = '';
try {
descSearchString = matches[0];
start = wt.indexOf( descSearchString ) + descSearchString.length;
} catch ( e ) {
// There was probably no description field. Fail!
return;
}
if ( start < descSearchString.length ) {
return;
}
addFn = addFn || function () { return {}; };
for ( i = start; i < wt.length; i++ ) {
c = wt[i];
if ( c === '{' ) {
lbraces++;
plog( 'Now at ' + lbraces + ' left braces.' );
}
if ( lbraces === 2 ) {
bracelevels++;
lbraces = 0;
plog( 'Now at ' + bracelevels + ' template scopes' );
}
if ( c === '}' ) {
rbraces++;
plog( 'Now at ' + rbraces + ' right braces.' );
}
if ( rbraces === 2 ) {
bracelevels--;
rbraces = 0;
plog( 'Now at ' + bracelevels + ' template scopes' );
if ( curlang && capturing ) {
languagesFound ++;
newdesc = capturing.split( '=', 2 );
if ( newdesc.length > 1 ) {
newdesc = newdesc[1];
} else {
newdesc = newdesc[0];
}
replacement = replaceFn( curlang, newdesc );
if ( replacement ) {
replacementStart = i - 2 - capturing.length - curlang.length;
replacementEnd = i - 1;
plog( 'Pushing replacement for "' + wt.substring( replacementStart, replacementEnd ) + '": "' + replacement.join( '|1=' ) );
replacements.push( {
start: replacementStart,
end: replacementEnd,
value: replacement.join( '|1=' )
} );
}
capturing = '';
curlang = null;
}
}
if ( c === '|' && bracelevels === 0 ) {
additions = addFn();
$.each( additions, function ( lang, content ) {
replacements.push( {
start: i - 1,
end: i - 1,
value: '{{' + lang + '|1=' + content + '}}'
} );
} );
if ( languagesFound === 0 && capturing ) {
plog( 'Found unset language for description "' + capturing + '".' );
replacement = replaceFn( 'NO_LANGUAGE_SET', capturing );
if ( replacement ) {
plog( 'Pushing replacement for description with no language template: Set language to "' + replacement[0] + '" and set description to "' + replacement[1] + '".' );
if ( replacement[0] === 'NO_LANGUAGE_SET' ) {
replacement[0] = 'unknown language'; // God you hate to do this, but I don't really have a choice
}
replacements.push( {
start: i - capturing.length,
end: i - 1,
value: '{{' + replacement.join( '|1=' ) + '}}'
} );
}
}
plog( 'Found closing |, leaving loop' );
break;
}
if ( !curlang && c === '|' ) {
plog( 'Description template call for ' + capturing );
curlang = capturing;
capturing = '';
} else if ( c !== '{' && c !== '}' ) {
capturing += c;
}
}
if ( replacements.length === 0 ) {
return wt;
}
for ( i = 0; i < replacements.length; i++ ) {
// Replacements should be in order, so no need to worry about messing
// that up.
replacement = replacements[i];
newWt += wt.substring( lastIndex, replacement.start );
newWt += replacement.value;
lastIndex = replacement.end;
plog( 'Replaced "' + wt.substring( replacement.start, replacement.end ) + '" with "' + replacement.value );
}
newWt += wt.substring( lastIndex );
return newWt;
}
function addEditButtons ( $spans ) {
$spans.after(
$( '<sup>' ).append(
$( '<a>' ).attr( 'href', '#' ).text( '[edit]' ).click( function () {
showEditInterface( $( this ).closest( '.description' ) );
return false;
} )
)
);
}
function showAddInterface() {
getLanguageDropdown().done( function ( $dropdown ) {
plog( $dropdown );
$( 'td.description' ).append(
$( '<div>' ).addClass( 'description' ).append(
$dropdown,
$( '<textarea>' ),
$( '<button>' ).text( 'Save' ).click( function ( e ) {
var $this = $( this ),
$desc = $this.closest( '.description' );
e.stopPropagation();
e.preventDefault();
$desc.hide();
getWikitext().done( function ( content, timestamp ) {
var lang = $desc.find( 'select' ).val(),
desc = $desc.find( 'textarea' ).val(),
newDescWikitext = '{{' + lang + '|1=' + desc + '}}',
newText = modifyDescriptions( content, function () {}, function () {
var retval = {};
retval[lang] = desc;
return retval;
} );
plog( newText );
updateWikitext(
newText,
'Added description for language \'' + lang + '\' with [[User:MarkTraceur/editDescriptions.js]]',
timestamp
).done( function () {
getParsedDescription( newDescWikitext ).done( function ( $newDesc ) {
$desc.empty().append( $newDesc ).show();
addEditButtons( $desc.find( 'span.language' ) );
} );
} ).fail( function () {
$desc.show();
} );
} );
} )
)
);
} );
}
function showEditInterface( $desc ) {
var langCode = $desc.attr( 'lang' );
getWikitext().done( function ( content, timestamp ) {
getLanguageDropdown( langCode ).done( function ( $dropdown ) {
var $descForm,
oldDesc = '',
$langName = $desc.find( 'span.language' ).clone();
modifyDescriptions( content, function ( lang, existingDesc ) {
plog( lang + ' - ' + existingDesc );
if ( lang === langCode ) {
oldDesc = existingDesc;
}
} );
$descForm = $( '<div>' )
.addClass( 'mw-description-edit-form' )
.css( {
padding: '10px',
width: '40%'
} )
.append(
$dropdown,
$( '<textarea>' ).val( oldDesc ),
$( '<button>' ).text( 'Save' ).click( function ( e ) {
var desc = $desc.find( 'textarea' ).val(),
lang = $desc.find( 'select' ).val(),
newDescWikitext = '{{' + lang + '|1=' + desc + '}}',
newText = modifyDescriptions( content, function ( thislang ) {
if ( thislang === langCode ) {
return [ lang, desc ];
}
} );
e.stopPropagation();
e.preventDefault();
$desc.hide();
plog( newText );
updateWikitext(
newText,
'Edited description for language \'' + lang + '\' with [[User:MarkTraceur/editDescriptions.js]]',
timestamp
).done( function () {
getParsedDescription( newDescWikitext ).done( function ( $newDesc ) {
$desc.empty().append( $newDesc ).show();
addEditButtons( $desc.find( 'span.language' ) );
} );
} ).fail( function () {
$desc.show();
} );
} ),
$( '<button>' ).text( 'Preview' ).click( function ( e ) {
var desc = $desc.find( 'textarea' ).val(),
lang = $desc.find( 'select' ).val(),
newDescWikitext = '{{' + lang + '|1=' + desc + '}}';
e.stopPropagation();
e.preventDefault();
$desc.find( '.mw-description-preview' ).remove();
getParsedDescription( newDescWikitext ).done( function ( $newDesc ) {
$( '<div>' )
.addClass( 'mw-description-preview' )
.css( {
border: '1px solid grey',
width: '40%',
padding: '20px',
backgroundColor: '#DDDDFF'
} )
.append( $newDesc )
.prependTo( $desc );
} );
} )
);
$desc.empty().append( $descForm );
} );
} );
}
$( function () {
var $unattachedDesc, $langSpan,
$descTd = $( 'td.description' ),
$descriptionDivs = $descTd.find( 'div.description' );
$( '#fileinfotpl_desc' ).append(
$( '<a>' )
.attr( 'href', '#' )
.text( 'Add another language' )
.css( {
fontWeight: 'normal',
fontSize: '.75em',
display: 'block'
} )
.click( function () {
showAddInterface();
return false;
} )
);
if (( $descriptionDivs.find( 'span.language' ).length === 0 )
&& ($descTd.find('div.multilingual').length === 0)) {
$unattachedDesc = $descTd.contents().detach();
if ( $descriptionDivs.length === 0 ) {
$descriptionDivs = $( '<div>' ).addClass( 'description' ).attr( 'lang', 'NO_LANGUAGE_SET' ).appendTo( $descTd );
}
if ( $descriptionDivs.length > 1 ) {
// Pretty sure this will never happen, but may as well be sure.
$descriptionDivs = $descriptionDivs.first();
}
$langSpan = $( '<span>' ).addClass( 'language NO_LANGUAGE_SET' ).html( '<b>Language not set:</b>' ).appendTo( $descriptionDivs );
$descriptionDivs.append( $unattachedDesc );
}
addEditButtons( $descriptionDivs.find( 'span.language' ) );
} );
}( jQuery, mediaWiki ) );