Difference between revisions of "MediaWiki:Gadget-minipedia.js"

From Minipedia
Jump to navigation Jump to search
m (new tabs)
(modernize with class Mini)
Line 3: Line 3:
 
*
 
*
 
* Dependencies: mediawiki.util
 
* Dependencies: mediawiki.util
  +
* Author: 2020-2025 [[User:ValerioBoz-WMCH]]
 
*
 
*
 
* See https://phabricator.wikimedia.org/tag/minipedia/
 
* See https://phabricator.wikimedia.org/tag/minipedia/
*
 
* @revision 2020-06-27
 
 
*/
 
*/
 
( function ( mw, $ ) {
 
( function ( mw, $ ) {
Line 149: Line 148:
 
return mwApi().then( function( api ) {
 
return mwApi().then( function( api ) {
   
// make the API request
+
// make the API request
 
return api.get( request ).then( function ( response ) {
 
return api.get( request ).then( function ( response ) {
   
Line 190: Line 189:
 
// make the API request and return a Promise
 
// make the API request and return a Promise
 
return api.get( request ).then( function( response ) {
 
return api.get( request ).then( function( response ) {
  +
 
 
// resolve the Promise with the page content
 
// resolve the Promise with the page content
 
var page = justFirstQueryPage( response );
 
var page = justFirstQueryPage( response );
 
if( page && page.pageid && page.pageid > 0 && page.extract ) {
 
if( page && page.pageid && page.pageid > 0 && page.extract ) {
return page.extract;
+
return page.extract;
 
}
 
}
   
 
// no content
 
// no content
 
return false;
 
return false;
  +
 
 
} );
 
} );
 
} );
 
} );
Line 205: Line 204:
   
 
/**
 
/**
  +
* Create a new Mini namespace object.
* Prepare the wiki
 
 
*/
 
*/
function prepareNormalpedia() {
+
function Mini(ns, shortLabel, openLabel, emoji) {
  +
this.ns = ns;
 
  +
this.shortLabel = shortLabel;
  +
this.openLabel = openLabel;
  +
this.emoji = emoji;
  +
}
  +
  +
/**
  +
* @return mw.Title
  +
*/
  +
Mini.prototype.createTitle = function(pageName) {
  +
return new mw.Title(pageName, this.ns);
  +
};
  +
  +
Mini.prototype.registerPortlet = function() {
 
// normal page title and mini version
 
// normal page title and mini version
 
var pageName = mw.config.get( 'wgPageName' );
 
var pageName = mw.config.get( 'wgPageName' );
  +
var mwTitle = this.createTitle(pageName);
 
  +
var portlet = mw.util.addPortletLink(
// prepare the Minipedia title object
 
var miniTitleObject = new mw.Title( pageName, mp.namespaceNum );
 
var miniTitleObjectIntermediate = new mw.Title( pageName, mp.namespaceIntermediateNum );
 
var miniTitleObjectAdvanced = new mw.Title( pageName, mp.namespaceAdvancedNum );
 
 
// minipedia page title with prefix
 
var miniPageName = miniTitleObject.getPrefixedText();
 
var miniPageNameIntermediate = miniTitleObjectIntermediate.getPrefixedText();
 
var miniPageNameAdvanced = miniTitleObjectAdvanced.getPrefixedText();
 
 
// minipedia page URL
 
var miniPageUrl = miniTitleObject.getUrl();
 
var miniPageUrlIntermediate = miniTitleObjectIntermediate.getUrl();
 
var miniPageUrlAdvanced = miniTitleObjectAdvanced.getUrl();
 
 
/**
 
* Go to the Minipedia page in edit mode
 
*
 
* The page in the main namespace will be preloaded.
 
 
*/
 
function goToMinipediaEditPage() {
 
 
/**
 
* Build the query string to edit a page
 
*
 
* TODO: eventually add VisualEditor support
 
*/
 
var editPageQueryString = {
 
action: 'edit',
 
title: miniPageName,
 
preload: pageName,
 
editintro: mp.editIntro,
 
};
 
 
// '/index.php'
 
var wgScript = mw.config.get( 'wgScript' );
 
 
// go to the edit page URL
 
window.location = wgScript + '?' + $.param( editPageQueryString );
 
};
 
 
// add a "Minipedia"
 
var miniVersionPortletLink = mw.util.addPortletLink(
 
 
'p-namespaces',
 
'p-namespaces',
  +
mwTitle.getUrl(),
miniPageUrl,
 
L10N.minipediaShort,
+
this.shortLabel,
 
'ca-minipedia',
 
'ca-minipedia',
L10N.openMinipedia,
+
this.openLabel,
 
'n'
 
'n'
 
);
 
);
   
  +
var that = this;
// add a "Minipedia Intermediate"
 
  +
$(portlet).click( function(e) {
var miniVersionPortletLinkIntermediate = mw.util.addPortletLink(
 
  +
that.onPortletClick(e);
'p-namespaces',
 
  +
} );
miniPageUrlIntermediate,
 
L10N.minipediaShortIntermediate,
 
'ca-minipedia',
 
L10N.openMinipediaIntermediate,
 
'n'
 
);
 
   
  +
return portlet;
// add a "Minipedia Intermediate"
 
  +
};
var miniVersionPortletLinkAdvanced = mw.util.addPortletLink(
 
'p-namespaces',
 
miniPageUrlAdvanced,
 
L10N.minipediaShortAdvanced,
 
'ca-minipedia',
 
L10N.openMinipediaAdvanced,
 
'n'
 
);
 
   
  +
Mini.prototype.onPortletClick = function(e) {
// on the mini toolback click, check if a mini version exists
 
  +
var pageName = mw.config.get( 'wgPageName' );
$( miniVersionPortletLink ).click( function( e ) {
 
  +
var miniPageName = this.createTitle(pageName).getPrefixedText();
 
  +
var that = this;
// wait for multiple information
 
  +
// wait for multiple information
$.when(
 
  +
$.when(
// check if the page really exists
 
  +
// check if the page really exists
pageExists( miniPageName ),
 
  +
pageExists( miniPageName ),
   
// allow to open OO UI windows
+
// allow to open OO UI windows
mw.loader.using( 'oojs-ui-windows' )
+
mw.loader.using( 'oojs-ui-windows' )
   
// callback fired when we have all the information
+
// callback fired when we have all the information
).done( function( miniPageExists, loader ) {
+
).done( function( miniPageExists, loader ) {
   
// check if the page already exist
+
// check if the page already exist
if( miniPageExists ) {
+
if( miniPageExists ) {
   
// just redirect to the Minipedia version
+
// just redirect to the Minipedia version
window.location = miniPageUrl;
+
window.location = miniPageUrl;
} else {
+
} else {
   
// ask if you want to create the page
+
// ask if you want to create the page
   
// create message dialog window
+
// create message dialog window
var messageDialog = new OO.ui.MessageDialog();
+
var messageDialog = new OO.ui.MessageDialog();
var windowManager = new OO.ui.WindowManager();
+
var windowManager = new OO.ui.WindowManager();
$( 'body' ).append( windowManager.$element );
+
$( 'body' ).append( windowManager.$element );
windowManager.addWindows( [ messageDialog ] );
+
windowManager.addWindows( [ messageDialog ] );
   
// configure and open dialog
+
// configure and open dialog
var windowInstance = windowManager.openWindow( messageDialog, {
+
var windowInstance = windowManager.openWindow( messageDialog, {
title: L10N.createMinipediaPageTitle,
+
title: L10N.createMinipediaPageTitle,
message: L10N.createMinipediaPageBody,
+
message: L10N.createMinipediaPageBody,
} );
+
} );
   
// check if you accepted the page creation
+
// check if you accepted the page creation
windowInstance.closed.then( function ( data ) {
+
windowInstance.closed.then( function ( data ) {
  +
// user is confirming the action
  +
if( data.action === 'accept' ) {
  +
// go go go! to mini
  +
that.onMinipediaConfirmation();
  +
}
  +
} );
   
  +
}
// user is confirming the action
 
  +
// end if page exists
if( data.action === 'accept' ) {
 
  +
} );
  +
// end $.when()
   
// go go go! to mini
+
// avoid scrolling to the top
  +
e.preventDefault();
goToMinipediaEditPage();
 
  +
};
}
 
} );
 
   
  +
Mini.prototype.onMinipediaConfirmation = function() {
}
 
  +
/**
// end if page exists
 
  +
* Go to the Minipedia page in edit mode
  +
*
  +
* The page in the main namespace will be preloaded.
  +
*
  +
*/
  +
var pageName = mw.config.get( 'wgPageName' );
  +
var mwTitle = this.createTitle(pageName);
  +
var miniPageName = mwTitle.getPrefixedText();
  +
var pageName = mw.config.get( 'wgPageName' );
   
} );
+
/**
  +
* Build the query string to edit a page
// end $.when()
 
  +
*
  +
* TODO: eventually add VisualEditor support
  +
*/
  +
var editPageQueryString = {
  +
action: 'edit',
  +
title: miniPageName,
  +
preload: pageName,
  +
editintro: mp.editIntro,
  +
};
   
  +
// '/index.php'
// avoid scrolling to the top
 
  +
var wgScript = mw.config.get( 'wgScript' );
e.preventDefault();
 
   
  +
// go to the edit page URL
} );
 
  +
window.location = wgScript + '?' + $.param( editPageQueryString );
// end $( miniVersionPortletLink ).click
 
  +
};
  +
  +
/**
  +
* Prepare the wiki
  +
*/
  +
function prepareNormalpedia() {
  +
var miniElementary = new Mini(
  +
mp.namespaceNum,
  +
L10N.minipediaShort,
  +
L10N.openMinipedia,
  +
'🐣');
  +
var miniIntermediate = new Mini(
  +
mp.namespaceIntermediateNum,
  +
L10N.minipediaShortIntermediate,
  +
L10N.openMinipediaIntermediate,
  +
'📚');
  +
var miniAdvanced = new Mini(
  +
mp.namespaceAdvancedNum,
  +
L10N.minipediaShortAdvanced,
  +
L10N.openMinipediaAdvanced,
  +
'🔬');
  +
  +
// add a "Minipedia"
  +
var miniVersionPortletLink = miniElementary.registerPortlet();
  +
  +
// add a "Minipedia Intermediate"
  +
var miniVersionPortletLinkIntermediate = miniIntermediate.registerPortlet();
  +
  +
// add a "Minipedia Advanced"
  +
var miniVersionPortletLinkAdvanced = miniAdvanced.registerPortlet();
   
 
};
 
};
Line 586: Line 599:
 
maxValue: statsMain.totalLines,
 
maxValue: statsMain.totalLines,
 
upstreamLimitPerc: 30,
 
upstreamLimitPerc: 30,
} );
+
} );
   
 
// there is no the related main page
 
// there is no the related main page

Revision as of 17:32, 9 May 2025

/**
 * Make Minipedia magics
 *
 * Dependencies: mediawiki.util
 * Author: 2020-2025 [[User:ValerioBoz-WMCH]]
 *
 * See https://phabricator.wikimedia.org/tag/minipedia/
 */
( function ( mw, $ ) {

	/*
	 * CONFIGURATION/LOCALIZATION INSTRUCTIONS
	 *
	 * Declare somewhere something like this:
	 *
	 *   // assure that you do not overwrite other-people customizations
	 *   window.MiniPedia      = window.MiniPedia      || {};
	 *   window.MiniPedia.L10N = window.MiniPedia.L10N || {};
	 *
	 *   // then customize something
 	 *   window.MiniPedia.editIntro      = 'Project:How to create';
	 *   window.MiniPedia.L10N.minipedia = 'Otherpedia';
	 */

	// load localization defaults
	var DEFAULTS = {
		// namespace prefix configured in your LocalSettings.php
		namespace: 'Mini',

		// namespace number configured in your LocalSettings.php
		namespaceNum: 3002,

		// namespace prefix configured in your LocalSettings.php
		namespaceIntermediate: 'Intermediate',

		// namespace number configured in your LocalSettings.php
		namespaceIntermediateNum: 3004,

		// namespace prefix configured in your LocalSettings.php
		namespaceAdvanced: 'Intermediate',

		// namespace number configured in your LocalSettings.php
		namespaceAdvancedNum: 3006,

		// default edit intro page title
		editIntro: 'Progetto:Minipedia/Creazione voce',

		// how much characters should have a word to be considered too much lon
		// this somehow help people with dyslexia
		longWordLen: 13,

		// maximum number of acceptable complex words to help people with dyslexia
		maxComplexWords: 10,

		// min and max number of suggested words to somehow mitigate attention span problems
		maxWords: 700,

		// maximum number of suggested newlines
		maxTotalLines: 180,

		// localization stuff
		L10N: {
			minipedia: "Minipedia",
			minipediaShort: "Mini",
			minipediaShortIntermediate: "Intermediate",
			minipediaShortAdvanced: "Advanced",
			normalpedia: "Wikipedia Test",
			normalpediaShort: "WikipediaTest",
			openMinipedia: "Apri Minipedia",
			openMinipediaIntermediate: "Open Minipedia Intermediate",
			openMinipediaAdvanced: "Open Minipedia Advanced",
			openNormalpedia: "Apri Wikipedia Test",
			createMinipediaPageTitle: "Accesso Minipedia",
			createMinipediaPageBody: "Sii il primo a creare una versione più ridotta e più accessibile di questa voce, in Minipedia!",
			statsTitle: "Mini Report",
			statsHeadingSubject: "Fattore",
			statsHeadingValue: "Valore attuale",
			statsHeadingExpected: "Limite consigliato",
			statsHeadingSimplicity: "Semplicità",
			statsWords: "Parole",
			statsLines: "Paragrafi",
			statsComplexWords: "Parole complesse",
		},
	};

	// global configuration
	window.MiniPedia = window.MiniPedia || {};

	// shortcut
	var mp = window.MiniPedia;

	// inherit default configurations
	$.extend( true, mp, DEFAULTS );

	// another shortcut
	var L10N = mp.L10N;

	/**
	 * Lazy shortcut to obtain just the first API result
	 *
	 * @param  {Object} response API Response
	 * @return {Object} page object
	 */
	function justFirstQueryPage( response ) {

		// no response no party
		if( !response.query || !response.query.pages ) {
			throw 'no valid API response';
		}

		// the list should contain just one page
		var pages = response.query.pages;
		for( var id in pages ) {
			return pages[ id ];
		}

		// no page no party
		return false;
	};

	/**
	 * Get a fresh MediaWiki API object
	 *
	 * @return mw.Api
	 */
	function mwApi() {
		return mw.loader.using( 'mediawiki.api' ).then( function() {
			return new mw.Api();
		} );
	};

	/**
	 * Check if a page title already exists
	 *
	 * @param title Page title
	 * @return Promise
	 */
	function pageExists( title ) {

		// prepare the API request
		var request = {
			action: 'query',
			prop: 'info',
			titles: title,
		};

		// eventually load API stuff
		return mwApi().then( function( api ) {

			// make the API request
			return api.get( request ).then( function ( response ) {

				// check if it exists
				var page = justFirstQueryPage( response );
				if( page && page.pageid && page.pageid > 0 ) {
					return page;
				}

				return false;
			} );
		} );
	};

	/**
	 * Query the current page plain text
	 *
	 * @param {String} Page name (or none for the current one)
	 * @return Promise
	 */
	function queryPlainText( pageName ) {

		// complete page title with namespace
		pageName = pageName || mw.config.get( 'wgPageName' );

		// prepare the API request
		// See https://phabricator.wikimedia.org/T259332
		var request = {
			action: 'query',
			prop: 'extracts',
			titles: pageName,
			explaintext: 1,
			exlimit: 1,
			exsectionformat: 'plain',
		};

		// this will return a Promise resolving the page plain text, returned upstream
		return mwApi().then( function( api ) {

			// make the API request and return a Promise
			return api.get( request ).then( function( response ) {

				// resolve the Promise with the page content
				var page = justFirstQueryPage( response );
				if( page && page.pageid && page.pageid > 0 && page.extract ) {
					return page.extract;
				}

				// no content
				return false;

			} );
		} );
	};

	/**
	 * Create a new Mini namespace object.
	 */
	function Mini(ns, shortLabel, openLabel, emoji) {
		this.ns = ns;
		this.shortLabel = shortLabel;
		this.openLabel = openLabel;
		this.emoji = emoji;
	}

	/**
	 * @return mw.Title
	 */
	Mini.prototype.createTitle = function(pageName) {
		return new mw.Title(pageName,  this.ns);
	};

	Mini.prototype.registerPortlet = function() {
		// normal page title and mini version
		var pageName = mw.config.get( 'wgPageName' );
		var mwTitle = this.createTitle(pageName);
		var portlet = mw.util.addPortletLink(
			'p-namespaces',
			mwTitle.getUrl(),
			this.shortLabel,
			'ca-minipedia',
			this.openLabel,
			'n'
		);

		var that = this;
		$(portlet).click( function(e) {
			that.onPortletClick(e);
		} );

		return portlet;
	};

	Mini.prototype.onPortletClick = function(e) {
		var pageName = mw.config.get( 'wgPageName' );
		var miniPageName = this.createTitle(pageName).getPrefixedText();
		var that = this;
		// wait for multiple information
		$.when(
			// check if the page really exists
			pageExists( miniPageName ),

			   // allow to open OO UI windows
			   mw.loader.using( 'oojs-ui-windows' )

			   // callback fired when we have all the information
		).done( function( miniPageExists, loader ) {

			// check if the page already exist
			if( miniPageExists ) {

				// just redirect to the Minipedia version
				window.location = miniPageUrl;
			} else {

				// ask if you want to create the page

				// create message dialog window
				var messageDialog = new OO.ui.MessageDialog();
				var windowManager = new OO.ui.WindowManager();
				$( 'body' ).append( windowManager.$element );
				windowManager.addWindows( [ messageDialog ] );

				// configure and open dialog
				var windowInstance = windowManager.openWindow( messageDialog, {
					title:   L10N.createMinipediaPageTitle,
					message: L10N.createMinipediaPageBody,
				} );

				// check if you accepted the page creation
				windowInstance.closed.then( function ( data ) {
					// user is confirming the action
					if( data.action === 'accept' ) {
						// go go go! to mini
						that.onMinipediaConfirmation();
					}
				} );

			}
			// end if page exists
		} );
		// end $.when()

		// avoid scrolling to the top
		e.preventDefault();
	};

	Mini.prototype.onMinipediaConfirmation = function() {
		/**
		 * Go to the Minipedia page in edit mode
		 *
		 * The page in the main namespace will be preloaded.
		 *
		 */
		var pageName = mw.config.get( 'wgPageName' );
		var mwTitle = this.createTitle(pageName);
		var miniPageName = mwTitle.getPrefixedText();
		var pageName = mw.config.get( 'wgPageName' );

		/**
		 * Build the query string to edit a page
		 *
		 * TODO: eventually add VisualEditor support
		 */
		var editPageQueryString = {
			action: 'edit',
			title: miniPageName,
			preload: pageName,
			editintro: mp.editIntro,
		};

		// '/index.php'
		var wgScript = mw.config.get( 'wgScript' );

		// go to the edit page URL
		window.location = wgScript + '?' + $.param( editPageQueryString );
	};

	/**
	 * Prepare the wiki
	 */
	function prepareNormalpedia() {
		var miniElementary = new Mini(
		  mp.namespaceNum,
		  L10N.minipediaShort,
		  L10N.openMinipedia,
		  '🐣');
		var miniIntermediate = new Mini(
		  mp.namespaceIntermediateNum,
		  L10N.minipediaShortIntermediate,
		  L10N.openMinipediaIntermediate,
		  '📚');
		var miniAdvanced = new Mini(
		  mp.namespaceAdvancedNum,
		  L10N.minipediaShortAdvanced,
		  L10N.openMinipediaAdvanced,
		  '🔬');

		// add a "Minipedia"
		var miniVersionPortletLink = miniElementary.registerPortlet();

		// add a "Minipedia Intermediate"
		var miniVersionPortletLinkIntermediate = miniIntermediate.registerPortlet();

		// add a "Minipedia Advanced"
		var miniVersionPortletLinkAdvanced = miniAdvanced.registerPortlet();

	};
	// end prepareNormalWiki()

	/**
	 * Calculate some stats over a text
	 */
	function textStats( text ) {

		var stats = {};

		text = text.trim();

		// count long words
		var totalLongWords = 0;
		var word, words = text.split( /\s+/ );
		for( var i = 0; i < words.length; i++ ) {
			word = words[i];
			if( word.length > mp.longWordLen ) {
				totalLongWords++;
			}
		}

		// total amount of paragraphs
		// paragraphs shorter than this comment are discarded
		var totalLines = 0;
		var paragraphs = text.split( /\n+/ );
		var paragraph;
		for( var i = 0; i < paragraphs.length; i++ ) {
			paragraph = paragraphs[i].trim();
			if( paragraph.length > 30 ) {
				totalLines++;
			}
		}

		// how much lines?
		stats.totalLines = totalLines;

		// how much words?
		stats.totalWords = words.length;

		// how much of these words are so much long?
		// See 'longWordLen'
		stats.totalLongWords = totalLongWords;

		return stats;
	};

	/**
	 * Query content stats of the current page
	 *
	 * @param {String} Page title (or nothing for the current one)
	 * @return Promise
	 */
	function queryContentStats( pageTitle ) {
		return queryPlainText( pageTitle ).then( textStats );
	};


	/**
	 * Apply a stupid percentage
	 *
	 * @param {int} a
	 * @param {int} b
	 * @return The 'b%' applied to 'a'
	 */
	function applyPercentage( a, b ) {
		return parseInt( a * b / 100 );
	};

	/**
	 * Prepare the Minipedia stats box
	 */
	function prepareMinipediaStatsBox() {

		// normal page title (non-mini version)
		var mainPageTitle = mw.config.get( 'wgTitle' );

		// body container
		var $contentText = $( '#mw-content-text' );

		// prepare the DOM tree
		var $container = $( '<div>' );
		var $table = $( '<table>' );
		var $thead = $( '<thead>' );
		var $tbody = $( '<tbody>' );

		// prepare the stats container
		$container.addClass( 'minipedia-stats' );

		// put a title
		$container.append( $( '<h2>' ).text( L10N.statsTitle ) );

		// put the table
		$container.append( $table );

		// add table headers
		$thead.append(
			$( '<tr>' ).append( $( '<th>' ).text( L10N.statsHeadingSubject    ) )
				   .append( $( '<th>' ).text( L10N.statsHeadingValue      ) )
				   .append( $( '<th>' ).text( L10N.statsHeadingExpected   ) )
				   .append( $( '<th>' ).text( L10N.statsHeadingSimplicity ) )
		);

		// prepare the table
		$table.addClass( 'wikitable' )
		      .append( $thead )
		      .append( $tbody );

		/**
		 * Append a row (with a label and a value) into a table
		 *
		 * The data argument accepts an object with:
		 *
		 *   className:        Class name for the row
		 *   label:            Text displayed in row heading (left)
		 *   text:             Text displayed in row data (right)
		 *   value             Value associated to the text of the mini version
		 *   maxValue          Maximum suggested value
		 *   upstreamValue     The value of the upstream (main) version
		 *   upstreamLimitPerc The percentage (0-100) applied to the 'upstreamValue' to inherit a suitable 'maxValue'
		 *
		 * Note: this function uses OOUI widgets. Make sure to have them loaded.
		 *
		 * @param {Object} jQuery table
		 * @param {Object} Data information
		 */
		function appendTableStatsRow( $table, data ) {

			// read arguments
			var className = data.className;
			var label     = data.label;
			var value     = data.value;
			var maxValue  = data.maxValue;
			var text      = data.text || value;

			// eventually calculate the max value
			if( !maxValue && data.upstreamValue && data.upstreamLimitPerc ) {
				maxValue = applyPercentage( data.upstreamValue, data.upstreamLimitPerc );
			}

			// prepare table stats row
			var $tr         = $( '<tr>' );
			var $tdLabel    = $( '<td>' ).text( label    );
			var $tdValue    = $( '<td>' ).text( text     );
			var $tdMax      = $( '<td>' ).text( maxValue );
			var $tdProgress = $( '<td>' );

			// build the table row
			$tr.addClass( 'minipedia-stats-row-' + className );
			$tr.append( $tdLabel    )
			   .append( $tdValue    )
			   .append( $tdMax      )
			   .append( $tdProgress );

			// if possible, plot a cute progress bar
			if( maxValue ) {

				// calculate a 0-100 progress since the value and maxValue
				var realPercentage = parseInt( value / maxValue * 100 );

				/**
				 * Calculate the inverse percentage
				 *
				 * This is the final percentage shown to the user.
				 *
				 * In short, it's always 100% but if you go over
				 * the limit it start decreasing and reaching zero,
				 * to rappresent a kind of 'understanding degradation'.
				 *
				 * To do not underrate the work of the user it should not go
				 * below a certain minimum amount. Example: 10%. :^)
				 *
				 * Actually the function is just linear.
				 */
				var inversePercentage = 100;
				if( realPercentage > 100 ) {
					inversePercentage = Math.max( 10, 200 - realPercentage );
				}

				// generate the progress bar
				var progressBar = new OO.ui.ProgressBarWidget( {
					progress: inversePercentage,
				} );

				// show the progress bar
				$tdProgress.append( progressBar.$element );
			}

			// attach some data to be read by scripts
			$tr.data( 'ministats', data );

			// eventually emphasize if something is wrong
			if( value && maxValue && value > maxValue ) {
				$tr.addClass( 'minipedia-stats-row-problem' );
			}

			// show the row in the stats table
			$table.append( $tr );
		};

		// request multiple stuff at the same time
		$.when(

			// query the page plain text of the current mini page
			queryContentStats(),

			// query the page plain text of the related non-mini page
			queryContentStats( mainPageTitle ),

			// require the progress bar widget
			mw.loader.using( 'oojs-ui-widgets' )

		// callback fired when we have all the information
		).done( function( statsMini, statsMain, mwLoader ) {

			// show the stats container at the bottom of the page when we have something
			$contentText.append( $container );

			// check if we have also some information from the main namespace
			// in this case we can do a comparison
			if( statsMain ) {

				// show the number of words, lines and complex words (actually just long words)
				// they can be compared to the main version
				appendTableStatsRow( $table, {
					className:         'long-words',
					label:             L10N.statsComplexWords,
					value:             statsMini.totalLongWords,
					upstreamValue:     statsMain.totalLongWords,
					upstreamLimitPerc: 10,
				} );
				appendTableStatsRow( $table, {
					className:         'words',
					label:             L10N.statsWords,
					value:             statsMini.totalWords,
					upstreamValue:     statsMain.totalWords,
					upstreamLimitPerc: 30,
				} );
				appendTableStatsRow( $table, {
					className:         'lines',
					label:             L10N.statsLines,
					value:             statsMini.totalLines,
					maxValue:          statsMain.totalLines,
					upstreamLimitPerc: 30,
				} );

			// there is no the related main page
			} else {

				// show the number of words, lines and complex words (actually just long words)
				// they cannot be compared to the main version
				appendTableStatsRow( $table, {
					className: 'long-words',
					label:     L10N.statsComplexWords,
					value:     statsMini.totalLongWords,
					maxValue:  mp.maxComplexWords,
				} );
				appendTableStatsRow( $table, {
					className: 'words',
					label:     L10N.statsWords,
					value:     statsMini.totalWords,
					maxValue:  mp.maxStatsWords,
				} );
				appendTableStatsRow( $table, {
					className: 'lines',
					label:      L10N.statsLines,
					value:      statsMini.totalLines,
					maxValue:   mp.maxTotalLines,
				} );
			}
		} );
	};

	/**
	 * Prepare the Minipedia namespace
	 */
	function prepareMinipedia() {

		// action of the page (edit, view etc.)
		var action = mw.config.get( 'wgAction' );

		// normal page title and mini version
		var pageName = mw.config.get( 'wgTitle' );

		// prepare the Normalpedia title object
		var normalTitleObject = new mw.Title( pageName );

		// minipedia page title with prefix
		var normalPageName = normalTitleObject.getPrefixedText();

		// minipedia page URL
		var normalPageUrl = normalTitleObject.getUrl();

		// add a "Minipedia"
		var normalVersionPortletLink = mw.util.addPortletLink(
			'p-namespaces',
			normalPageUrl,
			L10N.normalpediaShort,
			'ca-normalpedia',
			L10N.openNormalpedia
		);

		// check if we are in view mode
		if( action === 'view' ) {

			// in view mode we can fetch the stats
			prepareMinipediaStatsBox();
		}
	};
	// end prepareMinipedia()

	// work only in the main namespace
	var ns = mw.config.get( 'wgNamespaceNumber' );
	if( ns === 0 ) {
		prepareNormalpedia();
	} else if( ns == mp.namespaceNum ) {
		prepareMinipedia();
	}
	// end namespace zero check

} )( mw, $ );