From Wikipedia, the free encyclopedia
Note: After saving, you have to bypass your browser's cache to see the changes. Google Chrome, Firefox, Microsoft Edge and Safari: Hold down the ⇧ Shift key and click the Reload toolbar button. For details and instructions about other browsers, see Wikipedia:Bypass your cache.

/***************************************************************************************************

 TextDiff --- by Evad37

 > Shows a simpler, text-only diff

 > Alpha version

***************************************************************************************************/

// <nowiki>

$( function($) {

	

/* ========== Load dependencies ================================================================= */

// Load resoucre loader modules

mw.loader.using( 'mediawiki.util', 'mediawiki.api', 'ext.gadget.libExtraUtil'], function () {



// Do not operate if not viewing a diff, or if there is no difference

if ( !mw.util.getParamValue('diff') || $('.mw-diff-empty').length ) {

	return;

}



var config = {

	'script': {

		'version': '0.3.0-alpha'

	},

	'params': {

		'diff': mw.util.getParamValue('diff'),

		'oldid': mw.util.getParamValue('oldid')

	},

	'mw': mw.config.get(['wgPageName', 'wgCurRevisionId'])

};



var api = new mw.Api( {

    ajax: {

        headers: { 

			'Api-User-Agent': 'TextDiff/' + config.script.version + 

				' ( /info/en/?search=User:Evad37/TextDiff )'

		}

    }

} );



/* ========= Processing functions =============================================================== */



/**

 * normaliseIds

 *

 * Get normalised revision ids (lookup the current / previous / next id if present)

 * @param

 */

var normaliseIds = function(diffParam, oldidParam, title) {

	var relativeDiffWords = 'cur', 'prev', 'next'];

	

	if ( !diffParam ) {

		return $.Deferred().reject('No diff specified');

	}

	if ( !$.isNumeric(diffParam) && relativeDiffWords.indexOf(diffParam) === -1 ) {

		return $.Deferred().reject("Bad diff specified: Must be numeric or one of 'cur', 'prev', 'next'");

	}



	// Both params have numeric ids

	if ( $.isNumeric(diffParam) && $.isNumeric(oldidParam) ) {

		return $.Deferred().resolve({

			'from': oldidParam,

			'to': diffParam

		});

	}

	

	// Neither param has a numeric ids 

	if ( !$.isNumeric(diffParam) && !$.isNumeric(oldidParam) ) {

		return lookupIdsFromRelation(

			'prev',

			config.mw.wgCurRevisionId,

			title

		);

	}

	

	// Only diff param has a numeric id

	if ( $.isNumeric(diffParam) ) {

		return lookupIdsFromRelation(oldidParam, diffParam, title);

	}

	

	// Only oldid param has a numeric id

	return lookupIdsFromRelation(diffParam, oldidParam, title);

};



/**

 * lookupIdsFromRelation

 *

 * @param

 */

var lookupIdsFromRelation = function(relation, otherId, title) {

	return api.get({

		action: 'compare',

		format: 'json',

		fromrev: otherId, //|| false,

		fromtitle: ( $.isNumeric(otherId) ) ? '' : title,

		torelative: relation,

		prop: 'ids'

	})

	.then(function(result) {

		return {

			'from': result.compare.fromrevid,

			'to': result.compare.torevid || config.mw.wgCurRevisionId

		};

	});

};



/**

 * makeText

 *

 * Shorthand for #grabWikitext then #toPlaintext

 */

var makeText = function(id) { return grabWikitext(id).then(toPlaintext); };



/**

 * grabWikitext

 *

 * Gets the wikitext and page title of a specific revision of a page

 *

 * @param {string} page

 *   Page title

 * @param {int} revid

 *   Old revision id

 * @return {Deferred} Promise that is resolved with: {object} results, with keys

 *     wikitext: {string},

 *     pageTitle: {string}

 */

var grabWikitext = function(revid) {

	

	return api.get({

		"action": "query",

		"format": "json",

		"prop": "revisions",

		"revids": revid,

		"rvprop": "content"

	})

	.then(function(response) {

		var pageid = Object.keys(response.query.pages)[0];

		var wikitext = response.query.pagespageid].revisions0]['*'];

		var title = response.query.pagespageid].title;

		return { 'wikitext':wikitext, 'pageTitle':title };

	});



};



/**

 * toPlaintext

 *

 * Transforms a wikitext string into a plaintext string. Images are replaced with alt text.

 *

 * @param {object} info

 *	 @param {string} info.wikitext

 *     Wikitext to be expanded

 *   @param {string} info.pageTitle

 *     Page title, to give context to variables like {{PAGENAME}} 

 * @return {Deferred} Promise that is resolved with: {string} transformed wikitext

 */

var toPlaintext = function(info) {

	return api.post({

		"action": "parse",

		"format": "json",

		"title": info.pageTitle,

		"text": info.wikitext,

		"prop": "text",

		"disablelimitreport": 1,

		"disableeditsection": 1,

		"contentmodel": "wikitext",

		"mobileformat": 1,

		"noimages": 1

	})

	.then( function(response) { return response.parse.text'*']; } )

	.then( function(parsetext) { return $(parsetext).text(); } );

};



/*

Strip lines where that line, the two previous lines, and the two next lines, are identical.

Parameters are arrays of text, with one line of text per array element

*/

var stripIdenticalLines = function(lines, otherLines) {

	return lines.map(function(line, index) {

		if (

			linesindex-2 === otherLinesindex-2 &&

			linesindex-1 === otherLinesindex-1 &&

			linesindex === otherLinesindex &&

			linesindex+1 === otherLinesindex+1 &&

			linesindex+2 === otherLinesindex+2

		) {

			return '';

		}

		return line;

	});

};



var stripIdenticalText = function(fromText, toText) {

	var fromLines = fromText.split('\n');

	var toLines = toText.split('\n');

	return $.Deferred().resolve(

		stripIdenticalLines(fromLines, toLines).join('\n'),

		stripIdenticalLines(toLines, fromLines).join('\n')

	);

};



/**

 * makeDiff

 *

 * @param

 */

var makeDiff = function(fromText, toText) {

	return api.post({

		"action": "compare",

		"format": "json",

		"fromtext": fromText,

		"fromcontentmodel": "text",

		"totext": toText,

		"tocontentmodel": "text",

		"prop": "diff"

	})

	.then( function(response) { return response.compare'*']; });

};



/**

 * showDiff

 *

 * @param

 */

var showDiff = function(diffRow) {

	$(diffRow).filter('tr').addClass('textdiff-row').hide().insertAfter('#textDiff-buttonRow').show('fast');

	$('tr.textdiff-row').last().nextAll().addClass('origdiff-row').hide('fast');

	$('#textDiff-button').prop('disabled', false).text('Toggle textual diff');

};



var onError = function(code, jqxhr) {

	$('#textDiff-button').text('Error loading textual diff').after(

		$('<div>').addClass('error').append( extraJs.makeErrorMsg(code, jqxhr) )

	);

};





/* ========= Set up =============================================================== */

var doTextDiff = function(diffParam, oldIdParam, pageName) {

	normaliseIds(diffParam, oldIdParam, pageName)

	.then(function(ids) {

		return $.when(makeText(ids.from), makeText(ids.to));

	})

	.then(stripIdenticalText)

	.then(makeDiff)

	.then(showDiff, onError);

};



var $buttonRow = $('<tr>').attr('id', 'textDiff-buttonRow').append(

	$('<td>').attr('id', 'textDiff-buttonCell').append(

		$('<div>').css('text-align', 'center').append(

			$('<button>')

			.attr('title', 'Show textual diff view')

			.text('Textual diff')

			.click(function() {

				$(this).hide().next().show();

				doTextDiff(config.params.diff, config.params.oldid, config.mw.wgPageName);

			}),

			

			$('<button>')

			.attr({'id':'textDiff-button', 'title':'Toggle textual diff view'})

			.text('Textual diff loading...')

			.prop('disabled', 'true')

			.hide()

			.click(function() {

				$('tr.textdiff-row, tr.origdiff-row').toggle();

			})

		)

	)

);



$('table.diff').find('tr').first().after($buttonRow);

$('#textDiff-buttonCell').attr('colspan', '4');



/* ========== Export code for testing by /test.js =============================================== */

window.testTextDiff = {

	'config': config,

	'api': api,

	'normaliseIds': normaliseIds,

	'lookupIdsFromRelation': lookupIdsFromRelation,

	'makeText': makeText,	

	'grabWikitext': grabWikitext,

	'toPlaintext': toPlaintext,

	'makeDiff': makeDiff,

	'showDiff': showDiff,

	'doTextDiff': doTextDiff

};



});

});

// </nowiki>