MediaWiki:Gadget-InitListingTools.js

提供:ウィキボヤージュ

注意: 保存後、変更を確認するにはブラウザーのキャッシュを消去する必要がある場合があります。

  • Firefox / Safari: Shift を押しながら 再読み込み をクリックするか、Ctrl-F5 または Ctrl-R を押してください (Mac では ⌘-R)
  • Google Chrome: Ctrl-Shift-R を押してください (Mac では ⌘-Shift-R)
  • Internet Explorer / Microsoft Edge: Ctrl を押しながら 最新の情報に更新 をクリックするか、Ctrl-F5 を押してください
  • Opera: Ctrl-F5を押してください
//<nowiki>
/**	initListingTools v1.0, 2023-02-26
	Initialization of listing editor and listing info
	Original author: Roland Unger
    Additional contributor: Tmv
	Support of desktop and mobile views
	Documentation: https://de.wikivoyage.org/wiki/Wikivoyage:initListingTools.js
	License: GPL-2.0+, CC-by-sa 3.0
*/

module.exports = ( function( $, mw ) {
	'use strict';

	var initListingTools = function() {
		// 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: '地域' },
					{ 'type': 'buy', group: 'buy', label: '買う' },
					{ 'type': 'do', group: 'do', label: 'する' },
					{ 'type': 'drink', group: 'drink', label: '飲む' },
					{ 'type': 'eat', group: 'eat', label: '食べる' },
					{ 'type': 'go', group: 'go', label: '行く' },
					{ 'type': 'other', group: 'other', label: 'その他' },
					{ 'type': 'populated', group: 'populated', label: '都市' },
					{ 'type': 'see', group: 'see', label: '観る' },
					{ 'type': 'sleep', group: 'sleep', label: '泊まる' },
					{ 'type': 'view', group: 'view', label: '眺める' },
				]
			},
			{
				page: 'Module:Marker utilities/Groups',
				index: 'group',
				start: /^.*groups *= *{/g,
				end: /,? *},? *} *$/g,
				label: 'label',
				alias: 'alias',
				arrayName: 'groups',
				defaultArray: [
					{ group: 'area', label: '地域', color: '#0000FF' },
					{ group: 'buy', label: '買う', color: '#008080' },
					{ group: 'do', label: 'する', color: '#808080' },
					{ group: 'drink', label: '飲む', color: '#000000' },
					{ group: 'eat', label: '食べる', color: '#D2691E' },
					{ group: 'go', label: '行く', color: '#A52A2A' },
					{ group: 'other', label: 'その他', color: '#228B22' },
					{ group: 'populated', label: '都市', color: '#0000FF' },
					{ group: 'see', label: '観る', color: '#4682B4' },
					{ group: 'sleep', label: '泊まる', color: '#000080' },
					{ group: 'view', label: '眺める', 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: /^.*cards *= *{/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: /^.*currencies *= *{/g,
				end: /,? *} *, *isoToQid *=.*$/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 );
			}

            return 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'
			} ).then( 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, json, fmtData;
				try {
					json = JSON.parse( data );
				} catch ( e ) {
					// invalid JSON
					json = item.defaultArray;
					isDefault = true;
					var pos = e.message.match( /column (\d+) of/i )[ 1 ];
					pos = data.substring( pos - 10, pos + 10 );
					console.log( e.message + ', data: ' + item.page + ', text: ' + pos );
				}
				if ( item.index !== '' )
                    fmtData = analyzeAndCopyData( json, item, isDefault );
				else
                    fmtData = json;
					window.ListingEditor[ item.arrayName ] = json;
                var dataObj = {}; dataObj[ item.arrayName ] = fmtData;
                return dataObj;
			} ).catch( function() {
				var json = item.defaultArray, fmtData;
				if ( item.index !== '' )
                    fmtData = analyzeAndCopyData( json, item, true );
				else
                    fmtData = json;
					window.ListingEditor[ item.arrayName ] = json;
                var dataObj = {}; dataObj[ item.arrayName ] = fmtData;
                return dataObj;
			} );
		};

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

			// 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
            if ( typeof Promise !== 'undefined' ) 
                Promise.all( promiseArray ).then( function( data ) {
                    for ( const dataUnit of data ) {
                        Object.assign(allData, dataUnit);
                    }
                } );
			return allData;
		};

		var init = function() {
			return getDataFromModules();
		};

		return { init: init };
	}();

	return initListingTools.init();

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