注意: 保存後、変更を確認するにはブラウザーのキャッシュを消去する必要がある場合があります。
- Firefox / Safari: Shift を押しながら 再読み込み をクリックするか、Ctrl-F5 または Ctrl-R を押してください (Mac では ⌘-R)
- Google Chrome: Ctrl-Shift-R を押してください (Mac では ⌘-Shift-R)
- Microsoft Edge: Ctrl を押しながら 最新の情報に更新 をクリックするか、Ctrl-F5 を押してください。
//<nowiki> /** initListingTools v1.1, 2024-07-28 Loading Lua modules and i18n strings 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() { var SYSTEM = { version: '1.1-ja', listingEditor: 'ListingEditor', // key of the window variable script: 'MediaWiki:Gadget-ListingEditor$1.js', isMobile: ( /android|webos|iphone|ipad|ipod|blackberry|iemobile|opera mini/i.test( navigator.userAgent.toLowerCase() ) ), Wikidata_API: '//www.wikidata.org/w/api.php', wikiLang: mw.config.get( 'wgPageContentLanguage' ), userLang: mw.config.get( 'wgUserLanguage' ), intl: { ja: 1, en: 1 }, fallbackLang: 'ja', localPhoneCode: 'P473' }; // allowed namespaces for Listing Editor var allowedNamespaces = [ 0, // Main 2, // User 4 // Wikivoyage ]; // definitions for module import var defaultArray = [ { 'type': 'area', group: 'area', label: '地域', color: '#0000FF' }, { 'type': 'buy', group: 'buy', label: '買う', color: '#008080' }, { 'type': 'do', group: 'do', label: 'する', color: '#808080' }, { 'type': 'drink', group: 'drink', label: '飲む', color: '#000000' }, { 'type': 'eat', group: 'eat', label: '食べる', color: '#D2691E' }, { 'type': 'go', group: 'go', label: '行く', color: '#A52A2A' }, { 'type': 'other', group: 'other', label: 'その他', color: '#228B22' }, { 'type': 'populated', group: 'populated', label: '都市', color: '#0000FF' }, { 'type': 'see', group: 'see', label: '観る', color: '#4682B4' }, { 'type': 'sleep', group: 'sleep', label: '泊まる', color: '#000080' }, { 'type': 'view', group: 'view', label: '眺める', color: '#4169E1' }, ]; var luaModules = [ // select-list options for types, groups, and subtypes { title: 'Module:Marker utilities/Types', // name of module to import index: 'type', // name of key field, rearranging 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: defaultArray }, { title: 'Module:Marker utilities/Groups', index: 'group', start: /^.*groups *= *{/g, end: /,? *},? *} *$/g, label: 'label', alias: 'alias', arrayName: 'groups', defaultArray: defaultArray }, { title: '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: '' }, ] }, // Wikidata Q-identifier translation tables which are used for // Wikidata-content placeholders { title: 'Module:VCard/Cards', start: /^.*cards *= *{/g, end: /,? *},? *} *$/g, arrayName: 'payments' }, { title: 'Module:Hours/i18n', start: /^.*dateIds *= *{/g, end: /,? *},? *} *$/g, arrayName: 'hours' }, { title: 'Module:VCard/Qualifiers', start: /^.*labels *= *{/g, end: /,? *},? *} *$/g, arrayName: 'qualifiers' }, { title: 'Module:CountryData/Currencies', start: /^.*currencies *= *{/g, end: /,? *} *, *isoToQid *=.*$/g, arrayName: 'currencies' } ]; // index may be type, group var addAlias = function( tab, aliasObj, index ) { if ( !tab || !aliasObj || !aliasObj.alias ) return null; var t = aliasObj[ index ].replace( /[_\s]+/g, '_' ); if ( typeof( aliasObj.alias ) === 'string' ) tab[ aliasObj.alias ] = t; else for ( var alias of aliasObj.alias ) tab[ alias ] = t; }; // data: data array from module // luaModule: single module definition from luaModules array // isDefault: data are defaults from luaModules array var analyzeAndCopyData = function( data, luaModule, isDefault ) { var alias, i, item, assoc = {}, aliases = {}; // adding missing label from index, generating additional objects for ( i = 0; i < data.length; i++ ) { item = data[ i ]; if ( ( item[ luaModule.label ] || '' ) === '' ) item[ luaModule.label ] = item[ luaModule.index ].replace( /_/g, ' ' ); if ( luaModule.label !== 'label' ) item.label = item[ luaModule.label ]; assoc[ item[ luaModule.index ] ] = item; addAlias( aliases, item, luaModule.index ); } // sorting by label in alphabetic order data.sort( function( a, b ) { if ( luaModule.sortKey ) { a = a[ luaModule.sortKey ] || a[ luaModule.label ]; b = b[ luaModule.sortKey ] || b[ luaModule.label ]; } else { a = a[ luaModule.label ]; b = b[ luaModule.label ]; } return a.localeCompare( b ); } ); // copying var win = window[ SYSTEM.listingEditor ]; if ( !isDefault || !win[ luaModule.arrayName ] || !win[ luaModule.arrayName ].length ) { win[ luaModule.arrayName ] = [].concat( data ); win[ `${luaModule.arrayName}-assoc` ] = assoc; win[ `${luaModule.arrayName}-aliases` ] = aliases; } }; // luaModule: single module definition from luaModules array var getDataFromSingleModule = function( luaModule ) { return $.ajax( { url: mw.util.wikiScript( '' ), method: 'GET', data: { title: luaModule.title, action: 'raw', ctype: 'text/plain' }, timeout: 3000 } ).done( function( data ) { data = data.replace( /\-\-.*\n/g, '' ) // remove comments .replace( /\s+/gm, ' ' ) // remove line breaks and tabs .replace( luaModule.start, ' ' ) // delete beginning .replace( luaModule.end, ' ' ); // delete end if ( luaModule.index ) // convert to (sortable) array [ … ] data = '[' + data.replace( /([,{]) *(\[ *")?(wd|alias)(" *\])? *= *\{([^}]*)\}/g, '$1 "$3": [$5]' ) // ["xyz"] { --> { "index": "xyz" .replace( / *(\[ *")?([\w\-]+)(" *\])? *= *\{/g, `{ "${luaModule.index}": "$2", ` ) .replace( /, *(\[ *")?([\w\-]+)(" *\])? *=/g, ', "$2":' ) + ']'; else // keep as object { … } data = '{' + data.replace( /([ ,\{]) *(\[ *")?([\w\-]+)(" *\])? *=/g, '$1 "$3":' ) + '}'; // check if data string is valid JSON var isDefault = false, dataArray, win = window[ SYSTEM.listingEditor ]; try { dataArray = JSON.parse( data ); } catch ( e ) { // invalid JSON dataArray = luaModule.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: ${luaModule.title}, text: ${pos}` ); } if ( luaModule.index ) analyzeAndCopyData( dataArray, luaModule, isDefault ); else win[ luaModule.arrayName ] = dataArray; } ).fail( function() { var dataArray = luaModule.defaultArray || {}; if ( luaModule.index ) analyzeAndCopyData( dataArray, luaModule, true ); else win[ luaModule.arrayName ] = dataArray; } ); }; var getScript = function ( inset ) { var script = mw.format( SYSTEM.script, inset || '' ); return `/w/index.php?title=${script}&action=raw&ctype=text/javascript`; }; var loadEditor = function() { mw.loader.using( [ 'mediawiki.util', 'mediawiki.api', 'jquery.ui', 'jquery.chosen' ] ).then( function() { mw.loader.load( getScript( 'Main' ) ); }); }; var getDataFromModules = function() { var promiseArray = [], i; var lang = SYSTEM.fallbackLang; if ( SYSTEM.intl[ SYSTEM.userLang ] ) { lang = SYSTEM.userLang; } promiseArray.push( mw.loader.load( getScript( '-i18n-' + lang ) ) ); promiseArray.push( mw.loader.load( getScript( '-Config' ) ) ); // mw already exists but maybe not the ListingEditor object if ( !window[ SYSTEM.listingEditor ] ) window[ SYSTEM.listingEditor ] = {}; var startTime = performance.now(); for ( i = 0; i < luaModules.length; i++ ) promiseArray.push( getDataFromSingleModule( luaModules[ 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() { var endTime = performance.now(); console.log( `Call to get Lua arrays took ${endTime - startTime} milliseconds` ); loadEditor(); } ) .catch( function() { console.log( 'Error loading listing editor modules' ); } ); return; }; // ********************************************************************* // getting first value of a set of Wikidata statements var getWikidataValue = function( jsonObj, id, property ) { var entity = jsonObj && jsonObj.entities ? jsonObj.entities[ id ] : null; if ( entity && entity.claims ) { var statements = entity.claims[ property ]; if ( statements && statements.length && statements[ 0 ].mainsnak ) return statements[ 0 ].mainsnak.datavalue.value; } return null; }; // adding currency, country calling code and local calling code to // body-tag data attributes for use in listing editor var addDataToBodyTag = function() { var body = $( 'body' ), data; // copying data-currency data-country-calling-code, etc. from // indicator or listings to body tag for use in listing editor var dataTags = $( '.voy-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 ( var 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 the Wikidata id of the current page exists var id = mw.config.get( 'wgWikibaseItemId' ); if ( id ) { var success = function( jsonObj ) { var value = getWikidataValue( jsonObj, id, SYSTEM.localPhoneCode ); if ( value ) body.attr( 'data-local-calling-code', value ); }; // getting JSON object from Wikidata search $.ajax( { url: SYSTEM.Wikidata_API, data: { action: 'wbgetentities', ids: id, languages: SYSTEM.wikiLang, format: 'json' }, 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 } ); } }; // ********************************************************************* /** 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' ); return ( !SYSTEM.isMobile && allowedNamespaces.includes( namespace ) && mw.config.get( 'wgAction' ) === 'view' && $( '#mw-revision-info' ).length === 0 && mw.config.get( 'wgCurRevisionId' ) === mw.config.get( 'wgRevisionId' ) && mw.config.get( 'wgRelevantPageIsProbablyEditable' ) && $( '#ca-viewsource' ).length === 0 ); }; var init = function() { if ( checkIfAllowed() ) { addDataToBodyTag(); getDataFromModules(); } }; return { init: init }; } (); $( initListingTools.init ); } ( jQuery, mediaWiki ) ); // </nowiki>