Differenze tra le versioni di "MediaWiki:Gadget-minipedia.js"

Da Minipedia.
Jump to navigation Jump to search
m (now the $.when( $.ready ) is not useful)
(introduce stats box)
Riga 8: Riga 8:
 
* @revision 2020-06-27
 
* @revision 2020-06-27
 
*/
 
*/
$( function () {
+
( function ( $, mw ) {
   
 
/*
 
/*
Riga 37: Riga 37:
 
createMinipediaPageTitle: "Accesso Minipedia",
 
createMinipediaPageTitle: "Accesso Minipedia",
 
createMinipediaPageBody: "Sii il primo a creare una versione più ridotta e più accessibile di questa voce, in Minipedia!",
 
createMinipediaPageBody: "Sii il primo a creare una versione più ridotta e più accessibile di questa voce, in Minipedia!",
  +
statsTitle: "Mini Report",
  +
statsWords: "Parole",
  +
statsLines: "Paragrafi",
  +
statsComplexWords: "Parole complesse",
 
};
 
};
   
Riga 51: Riga 55:
 
// default edit intro page title
 
// default edit intro page title
 
mp.editIntro = mp.editIntro || 'Progetto:Minipedia/Creazione voce';
 
mp.editIntro = mp.editIntro || 'Progetto:Minipedia/Creazione voce';
  +
  +
// how much characters should have a word to be considered too much lon
  +
// this somehow help people with dyslexia
  +
mp.longWordLen = 13;
   
 
// localization stuff
 
// localization stuff
Riga 86: Riga 94:
 
// no page no party
 
// no page no party
 
return false;
 
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();
  +
} );
 
};
 
};
   
Riga 96: Riga 115:
 
function pageExists( title ) {
 
function pageExists( title ) {
   
// require the API stuff at runtime
+
// prepare the API request
  +
var request = {
// TODO: check if it's called everytime and eventually put dependency in ResourceLoader
 
  +
action: 'query',
// this returns the Promise created downstream (false or existing page)
 
  +
prop: 'info',
return mw.loader.using( 'mediawiki.api' ).then( function() {
 
  +
titles: title,
  +
};
   
// prepare the API request
+
// eventually load API stuff
  +
return mwApi().then( function( api ) {
var request = {
 
action: 'query',
 
prop: 'info',
 
titles: title
 
};
 
   
// make the API request
+
// make the API request
  +
return api.get( request ).then( function ( response ) {
// resolve the parent Promise with another Promise giving the existing page or false
 
return ( new mw.Api() ).get( request ).then( function ( response ) {
 
   
 
// check if it exists
 
// check if it exists
Riga 120: Riga 136:
 
return false;
 
return false;
 
} );
 
} );
  +
} );
  +
};
  +
  +
/**
  +
* Query the current page plain text
  +
*
  +
* @return Promise
  +
*/
  +
function queryPlainText() {
  +
  +
// complete page title with namespace
  +
var 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;
  +
  +
} );
 
} );
 
} );
 
};
 
};
Riga 214: Riga 269:
 
// 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 action
 
if( data.action === 'accept' ) {
 
if( data.action === 'accept' ) {
   
Riga 235: Riga 292:
 
};
 
};
 
// end prepareNormalWiki()
 
// 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++;
  +
}
  +
}
  +
  +
// how much lines?
  +
stats.totalLines = text.split( "\n+" ).length;
  +
  +
// 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
  +
*
  +
* @return Promise
  +
*/
  +
function queryContentStats() {
  +
return queryPlainText().then( textStats );
  +
};
  +
  +
/**
  +
* Prepare the Minipedia stats box
  +
*/
  +
function prepareMinipediaStatsBox() {
  +
  +
// body container
  +
var $contentText = $( '#mw-content-text' );
  +
  +
// prepare the DOM tree
  +
var $container = $( '<div>' );
  +
var $table = $( '<table>' );
  +
var $thead = $( '<tbody>' );
  +
  +
// prepare the stats container
  +
$container.addClass( 'minipedia-stats' );
  +
  +
// put a title
  +
$container.append( $( '<h2>' ).text( mp.L10N.statsTitle ) );
  +
  +
// put the table
  +
$container.append( $table );
  +
  +
// prepare the table
  +
$table.addClass( 'wikitable' )
  +
.append( $thead );
  +
  +
/**
  +
* Append a row (with a label and a value) into a table
  +
*
  +
* @param {Object} jQuery table
  +
* @param {String} className
  +
* @param {String} label
  +
* @param {String} value
  +
*/
  +
function appendTableStatsRow( $table, className, label, value ) {
  +
var $tr = $( '<tr>' );
  +
$tr.addClass( 'minipedia-stats-row-' + className );
  +
$tr.append( $( '<td>' ).text( label ) );
  +
$tr.append( $( '<td>' ).text( value ) );
  +
$table.append( $tr );
  +
};
  +
  +
// query the page plain text
  +
queryContentStats().then( function( stats ) {
  +
  +
// show the number of words, lines and complex words (actually just long words)
  +
appendTableStatsRow( $table, 'long-words', mp.L10N.statsComplexWords, stats.totalLongWords );
  +
appendTableStatsRow( $table, 'words', mp.L10N.statsWords, stats.totalWords );
  +
appendTableStatsRow( $table, 'lines', mp.L10N.statsLines, stats.totalLines );
  +
} );
  +
  +
// show the stats container at the bottom of the page
  +
$contentText.append( $container );
  +
};
   
 
/**
 
/**
Riga 240: Riga 393:
 
*/
 
*/
 
function prepareMinipedia() {
 
function prepareMinipedia() {
  +
  +
// action of the page (edit, view etc.)
  +
var action = mw.config.get( 'wgAction' );
   
 
// normal page title and mini version
 
// normal page title and mini version
Riga 261: Riga 417:
 
L10N.openNormalpedia
 
L10N.openNormalpedia
 
);
 
);
  +
  +
// check if we are in view mode
  +
if( action === 'view' ) {
  +
  +
// in view mode we can fetch the stats
  +
prepareMinipediaStatsBox();
  +
}
 
};
 
};
 
// end prepareMinipedia()
 
// end prepareMinipedia()
Riga 273: Riga 436:
 
// end namespace zero check
 
// end namespace zero check
   
} );
+
} )( $, mw );

Versione delle 17:21, 31 lug 2020

/**
 * Make Minipedia magics
 *
 * Dependencies: mediawiki.util
 *
 * See https://phabricator.wikimedia.org/tag/minipedia/
 *
 * @revision 2020-06-27
 */
( 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';
	 *
	 * For all the strings available in the 'MiniPedia.L10N' object
	 * see the L10N_DEFAULTS variable below.
	 */

	// load localization defaults
	var L10N_DEFAULTS = {
		minipedia: "Minipedia",
		minipediaShort: "Mini",
		normalpedia: "Wikipedia Test",
		normalpediaShort: "WikipediaTest",
		openMinipedia: "Apri Minipedia",
		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",
		statsWords: "Parole",
		statsLines: "Paragrafi",
		statsComplexWords: "Parole complesse",
	};

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

	// shortcut for the global configuration file (mp = mini-pedia)
	var mp = window.MiniPedia;

	// default namespace informations
	mp.namespace    = mp.namespace    || 'Mini';
	mp.namespaceNum = mp.namespaceNum || 3002;

	// default edit intro page title
	mp.editIntro = mp.editIntro || 'Progetto:Minipedia/Creazione voce';

	// how much characters should have a word to be considered too much lon
	// this somehow help people with dyslexia
	mp.longWordLen = 13;

	// localization stuff
	mp.L10N = mp.L10N || {};

	// shortcut
	var L10N = mp.L10N;

	// load the defaults
	for( var key in L10N_DEFAULTS ) {
		if( !L10N[ key ] ) {
			L10N[ key ] = L10N_DEFAULTS[ key ];
		}
	}

	/**
	 * 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
	 *
	 * @return Promise
	 */
	function queryPlainText() {

		// complete page title with namespace
		var 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;
				
			} );
		} );
	};

	/**
	 * Prepare the wiki
	 */
	function prepareNormalpedia() {
	
		// normal page title and mini version
		var pageName = mw.config.get( 'wgPageName' );

		// prepare the Minipedia title object
		var miniTitleObject = new mw.Title( pageName,  mp.namespaceNum );

		// minipedia page title with prefix
		var miniPageName = miniTitleObject.getPrefixedText();

		// minipedia page URL
		var miniPageUrl = miniTitleObject.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',
			miniPageUrl,
			L10N.minipediaShort,
			'ca-minipedia',
			L10N.openMinipedia,
			'n'
		);

		// on the mini toolback click, check if a mini version exists
		$( miniVersionPortletLink ).click( function( e ) {
		
			// wait for some information
			$.when(
				// check if the page really exists
				pageExists( miniPageName ),

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

			).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 action
						if( data.action === 'accept' ) {

							// go go go!
							goToMinipediaEditPage();
						}
					} );

				}
				// end if page exists

			} );
			// end $.when()

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

		} );
		// end $( miniVersionPortletLink ).click

	};
	// 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++;
			}
		}

		// how much lines?
		stats.totalLines = text.split( "\n+" ).length;

		// 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
	 *
	 * @return Promise
	 */
	function queryContentStats() {
		return queryPlainText().then( textStats );
	};

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

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

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

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

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

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

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

		/**
		 * Append a row (with a label and a value) into a table
		 *
		 * @param {Object} jQuery table
		 * @param {String} className
		 * @param {String} label
		 * @param {String} value
		 */
		function appendTableStatsRow( $table, className, label, value ) {
			var $tr = $( '<tr>' );
			$tr.addClass( 'minipedia-stats-row-' + className );
			$tr.append( $( '<td>' ).text( label ) );
			$tr.append( $( '<td>' ).text( value ) );
			$table.append( $tr );
		};

		// query the page plain text
		queryContentStats().then( function( stats ) {

			// show the number of words, lines and complex words (actually just long words)
			appendTableStatsRow( $table, 'long-words', mp.L10N.statsComplexWords, stats.totalLongWords );
			appendTableStatsRow( $table, 'words',      mp.L10N.statsWords,        stats.totalWords     );
			appendTableStatsRow( $table, 'lines',      mp.L10N.statsLines,        stats.totalLines     );
		} );

		// show the stats container at the bottom of the page
		$contentText.append( $container );
	};

	/**
	 * 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 );