MediaWiki:InitListingTools.js

De Wikiviajes, la guía libre de viajes

Nota: Después de publicar, quizás necesite actualizar la caché de su navegador para ver los cambios.

  • Firefox/Safari: Mantenga presionada la tecla Shift mientras pulsa el botón Actualizar, o presiona Ctrl+F5 o Ctrl+R (⌘+R en Mac)
  • Google Chrome: presione Ctrl+Shift+R (⌘+Shift+R en Mac)
  • Internet Explorer/Edge: mantenga presionada Ctrl mientras pulsa Actualizar, o presione Ctrl+F5
  • Opera: Presiona Ctrl+F5.
//<nowiki>
/**	initListingTools v1.0, 2021-11-06
	Initialization of listing editor and listing info
	Original author: Roland Unger
	Support of desktop and mobile views
	Documentation: https://de.wikivoyage.org/wiki/Wikivoyage:initListingTools.js
	License: GPL-2.0+, CC-by-sa 3.0
*/

( function( $, mw ) {
	'use strict';

	var initListingTools = function() {

		// scripts' page names
		var scripts = {
			listingEditor: 'MediaWiki:ListingEditor.js',
			listingInfo:   'MediaWiki:ListingInfo.js'
		};

		// allowed namespaces for Listing Editor
		var allowedNamespaces = [
			0, // Main
			2, // User
			4, // Wikivoyage
		];

		// options for module import
		var options = [
			{
				page: 'Module:Marker utilities/Types', // name of module to import
				index: 'type',                         // name of key field
				start: /^.*types *= *{/g,              // to remove from start
				end: /,? *},? *} *$/g,                 // to remove at the end
				label: 'label',                        // second sort key
				alias: 'alias',                        // alias for index
				arrayName: 'types',                    // name of the new array
				defaultArray: [
					{ 'type': 'area', group: 'area', label: 'área' },
					{ 'type': 'buy', group: 'buy', label: 'comprar' },
					{ 'type': 'do', group: 'do', label: 'hacer' },
					{ 'type': 'drink', group: 'drink', label: 'beber' },
					{ 'type': 'eat', group: 'eat', label: 'comer' },
					{ 'type': 'go', group: 'go', label: 'ir' },
					{ 'type': 'other', group: 'other', label: 'otro' },
					{ 'type': 'populated', group: 'populated', label: 'habitadas' },
					{ 'type': 'see', group: 'see', label: 'ver' },
					{ 'type': 'sleep', group: 'sleep', label: 'dormir' },
					{ 'type': 'view', group: 'view', label: 'vista' },
				]
			},
			{
				page: 'Module:Marker utilities/Groups',
				index: 'group',
				start: /^.*groups *= *{/g,
				end: /,? *},? *} *$/g,
				label: 'label',
				alias: 'alias',
				arrayName: 'groups',
				defaultArray: [
					{ group: 'area', label: 'área', color: '#0000FF' },
					{ group: 'buy', label: 'comprar', color: '#008080' },
					{ group: 'do', label: 'hacer', color: '#808080' },
					{ group: 'drink', label: 'beber', color: '#000000' },
					{ group: 'eat', label: 'comer', color: '#D2691E' },
					{ group: 'go', label: 'ir', color: '#A52A2A' },
					{ group: 'other', label: 'otro', color: '#228B22' },
					{ group: 'populated', label: 'habitadas', color: '#0000FF' },
					{ group: 'see', label: 'ver', color: '#4682B4' },
					{ group: 'sleep', label: 'dormir', color: '#000080' },
					{ group: 'view', label: 'vista', color: '#4169E1' },
				]
			},
			{
				page: 'Module:VCard/Subtypes',
				index: 'type',
				start: /^.* f *= *{/g,
				end: /,? *} *, *g *=.*$/g,
				sortKey: 'sortkey', // first sort key
				label: 'n',         // second sort key
				arrayName: 'subtypes',
				defaultArray: [
					{ 'type': 'budget',   g: 1, w: '', n: '', f: '' },
					{ 'type': 'midrange', g: 1, w: '', n: '', f: '' },
					{ 'type': 'upmarket', g: 1, w: '', n: '', f: '' },
				]
			},
			{
				page: 'Module:VCard/Cards',
				index: '',                  // only import, no rearranging
				start: /^ *return *{/g,
				end: /,? *} *$/g,
				arrayName: 'payments',
				defaultArray: {}
			},
			{
				page: 'Module:Hours/i18n',
				index: '',
				start: /^.*dateIds *= *{/g,
				end: /,? *},? *} *$/g,
				arrayName: 'hours',
				defaultArray: {}
			},
			{
				page: 'Module:VCard/Qualifiers',
				index: '',
				start: /^.*labels *= *{/g,
				end: /,? *},? *} *$/g,
				arrayName: 'qualifiers',
				defaultArray: {}
			},
			{
				page: 'Module:CountryData/Currencies',
				index: '',
				start: /^ *return *{/g,
				end: /,? *} *$/g,
				arrayName: 'currencies',
				defaultArray: {}
			}
		];
	
		// data: data array from module
		// item: single item from options array
		// isDefault: data are defaults from options array
		var analyzeAndCopyData = function( data, item, isDefault ) {
			var i, dataItem;

			// adding missing label from index
			for ( i = 0; i < data.length; i++ ) {
				dataItem = data[ i ];
				dataItem[ item.label ] = dataItem[ item.label ] || '';
				if ( dataItem[ item.label ] === '' ) {
					if ( typeof dataItem[ item.alias ] === 'undefined' )
						dataItem[ item.label ] = dataItem[ item.index ].replace( /_/g, ' ' );
					else
						if ( typeof( dataItem[ item.alias ] ) === 'string' )
							dataItem[ item.label ] = dataItem[ item.alias ].replace( /_/g, ' ' );
						else if ( dataItem[ item.alias ][ 0 ] )
							dataItem[ item.label ] = dataItem[ item.alias ][ 0 ].replace( /_/g, ' ' );
				}
			}
			// sorting by label in alphabetic order
			data.sort( function( a, b ) {
				if ( item.sortKey ) {
					a = a[ item.sortKey ] || a[ item.label ];
					b = b[ item.sortKey ] || b[ item.label ];
				} else {
					a = a[ item.label ];
					b = b[ item.label ];
				}
				return a.localeCompare( b );
			} );

			// copying
			if ( isDefault ) {
				// copy only if window.ListingEditor.array is empty
				if ( typeof window.ListingEditor[ item.arrayName ] === 'undefined' ||
					window.ListingEditor[ item.arrayName ].length < 1 ) {
					window.ListingEditor[ item.arrayName ] = [].concat( data );
				}
			} else {
				window.ListingEditor[ item.arrayName ] = [].concat( data );
			}
		};

		// item: item from options array
		var getDataFromSingleModule = function( item ) {
			return $.ajax( {
				type: 'GET',
				url: mw.util.wikiScript( '' ),
				data: { title: item.page, action: 'raw', ctype: 'text/plain' },
				timeout: 3000,
				dataType: 'text'
			} ).done( function( data ) {
				data = data.replace( /\-\-.*\n/g, '' )      // remove comments
					.replace( /[\s+\t+]/gm, ' ' );          // remove line breaks and tabs

				if ( item.index !== '' )
					// convert to (sortable) array
					data = data.replace( item.start, '[' )  // delete beginning
						.replace( item.end, ']' )           // delete end
						.replace( /([,{]) *(wd|alias) *= *\{([^}]*)\}/g, '$1 "$2": [$3]' )
						.replace( /( *\[ *")([\w\-]+)(" *\] *= *\{)/g, '{ "' + item.index + '": "$2", ' )
						.replace( /( *)([\w\-]+)( *= *\{)/g, '{ "' + item.index + '": "$2", ' )
						.replace( /(, *)([\w\-]+)( *=)/g, ', "$2":' );
				else
					// keep as object
					data = data.replace( item.start, '{' )  // delete beginning
						.replace( item.end, '}' )           // delete end
						.replace( /( *\[ *")([\w\-]+)(" *\] *= *)/g, '"$2":' )
						.replace( /([,\{]) *([\w\-]+)( *=)/g, '$1 "$2":' );

				// check if data string is valid JSON
				var isDefault = false;
				try {
					data = JSON.parse( data );
				} catch ( e ) {
					// invalid JSON
					data = item.defaultArray;
					isDefault = true;
					console.log( e.message + ', data: ' + data );
				}
				if ( item.index !== '' )
					analyzeAndCopyData( data, item, isDefault );
				else
					window.ListingEditor[ item.arrayName ] = data;
			} ).fail( function() {
				var data = item.defaultArray;
				if ( item.index !== '' )
					analyzeAndCopyData( data, item, true );
				else
					window.ListingEditor[ item.arrayName ] = data;
			} );
		};

		var loadEditor = function() {
			mw.loader.using( [ 'mediawiki.util', 'mediawiki.api', 'jquery.ui', 'jquery.chosen' ] ).then( function() {
				mw.loader.load( '/w/index.php?title=' + scripts.listingEditor + '&action=raw&ctype=text/javascript' );
			});
		};

		var getDataFromModules = function() {
			var promiseArray = [], i;

			// mw already exists but maybe not the ListingEditor object
			if ( typeof window.ListingEditor === 'undefined' )
				window.ListingEditor = {};

			for ( i = 0; i < options.length; i++ )
				promiseArray.push( getDataFromSingleModule( options[ i ] ) );

			// wait for getting all external data
			var isIE11 = !!window.MSInputMethodContext && !!document.documentMode;
			if ( isIE11 )
				$.when.apply( $, promiseArray ).then( function() {
					loadEditor();
				} );
			else
				if ( typeof Promise !== 'undefined' )
					Promise.all( promiseArray )
						.then( function() {
							loadEditor();
						} )
						.catch( function() {
							loadEditor();
							// error warning
						} );
			return;
		};

		// *********************************************************************
		// getting JSON object from Wikidata search
		var ajaxSearch = function( url, data, success ) {
			data.format = 'json';
			$.ajax( {
				url: url,
				data: data,
				dataType: 'jsonp',
				success: success,
				cache: false, // it will force requested pages not to be cached by
				              // the browser in case of script and jsonp data types
				timeout: 3000
			} );
		};

		// parse jsonObj for suboject with index id
		var parseJsonEntitiesObj = function( jsonObj, id ) {
			if ( !jsonObj || !jsonObj.entities || !jsonObj.entities[ id ] )
				return null;
			else
				return jsonObj.entities[ id ];
		};

		// getting first value of a set of Wikidata statements
		var getWikidataValue = function( jsonObj, id, property ) {
			var entity = parseJsonEntitiesObj( jsonObj, id );
			if ( !entity || !entity.claims || !entity.claims[ property ] )
				return null;

			var statements = entity.claims[ property ];
			if ( !statements || statements.length < 1 || !statements[ 0 ].mainsnak
				|| !statements[ 0 ].mainsnak.datavalue )
				return null;
			else
				return statements[ 0 ].mainsnak.datavalue.value;
		};

		// adding currency, country calling code and local calling code to
		// body-tag data attributes for use in listing editor
		var addDataToBodyTag = function() {
			var data, i, id, success, url, value;

			// add wv-user-login class if user is logged-in
			var body = $( 'body' );
			if ( mw.config.get( 'wgUserName' ) )
				body.addClass( 'wv-user-login' );

			// copying data-currency data-country-calling-code, etc. from
			// indicator or listings to body tag for use in listing editor
			var dataTags = $( '.wv-coord-indicator' );
			if ( !dataTags.length || dataTags.attr( 'data-country' ) === undefined )
				dataTags = $( '.vCard' );
			var list = [ 'data-currency', 'data-country-calling-code', 'data-lang', 'data-lang-name', 'data-dir', 'data-trunk-prefix' ];
			for ( i = 0; i < list.length; i++ ) {
				data = dataTags.attr( list [ i ] ) || '';
				if ( data !== '' )
					body.attr( list [ i ], data );
			}

			// copying local calling code from Wikidata to body tag
			// if Wikidata id exists
			id = mw.config.get( 'wgWikibaseItemId' );
			if ( id ) {
				url = '//www.wikidata.org' + '/w/api.php';
				data = {
					action: 'wbgetentities',
					ids: id,
					languages: mw.config.get( 'wgPageContentLanguage' ),
				};
				success = function( jsonObj ) {
					value = getWikidataValue( jsonObj, id, 'P473' );
					if ( value )
						body.attr( 'data-local-calling-code', value );
				};
				ajaxSearch( url, data, success );			
			}
		};
		
		// *********************************************************************
		/**	Return false if the current page should not enable the listing editor.
			Examples where the listing editor should not be enabled include talk
			pages, edit pages, history pages, etc.
		*/
		var checkIfAllowed = function() {
			var namespace = mw.config.get( 'wgNamespaceNumber' );
			if ( !allowedNamespaces.includes( namespace )
				|| mw.config.get( 'wgAction' ) != 'view' || $( '#mw-revision-info' ).length
				|| mw.config.get( 'wgCurRevisionId' ) != mw.config.get( 'wgRevisionId' )
				|| !mw.config.get( 'wgRelevantPageIsProbablyEditable' )
				|| $( '#ca-viewsource' ).length )
				return false;
			else
				return true;
		};

		var initEditor = function() {
			var suppressLE = ( window.suppressListingEditor ) ?
				window.suppressListingEditor : false;
    		if ( !suppressLE )
				getDataFromModules();
		};

		var initInfo = function() {
			mw.loader.load( '/w/index.php?title=' + scripts.listingInfo + '&action=raw&ctype=text/javascript' );
		};

		// *********************************************************************
		// support of Skype links. Skype protocol is not supported by the
		// Mediawiki software, and the link is to be added later
		var addSkypeLinks = function() {
			var p, t;
			$( '.listing-skype-link' ).each( function() {
				p = $( this );
				t = p.text();
				p.html( '<a href="skype:' + t + '">' + t.replace( /\?.*$/, '' ) + '</a>' );
			});
		};

		var init = function() {
			addDataToBodyTag();
			addSkypeLinks();

			if ( checkIfAllowed() ) {
				initEditor();
				initInfo();
			}

			// in future: creation of edit and info links
		};

		return { init: init };
	} ();

	$( initListingTools.init );

} ( jQuery, mediaWiki ) );
// </nowiki>