MediaWiki:Gadget-ListingEditor.js
Erscheinungsbild
Hinweis: Leere nach dem Veröffentlichen den Browser-Cache, um die Änderungen sehen zu können.
- Firefox/Safari: Umschalttaste drücken und gleichzeitig Aktualisieren anklicken oder entweder Strg+F5 oder Strg+R (⌘+R auf dem Mac) drücken
- Google Chrome: Umschalttaste+Strg+R (⌘+Umschalttaste+R auf dem Mac) drücken
- Edge: Strg+F5 drücken oder Strg drücken und gleichzeitig Aktualisieren anklicken
//<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-de',
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: { de: 1, en: 1, es: 1 },
fallbackLang: 'de',
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: 'Gebiet', color: '#0000FF' },
{ 'type': 'buy', group: 'buy', label: 'Einkaufen', color: '#008080' },
{ 'type': 'do', group: 'do', label: 'Aktivität', color: '#808080' },
{ 'type': 'drink', group: 'drink', label: 'Trinken', color: '#000000' },
{ 'type': 'eat', group: 'eat', label: 'Küche', color: '#D2691E' },
{ 'type': 'go', group: 'go', label: 'Anreise', color: '#A52A2A' },
{ 'type': 'other', group: 'other', label: 'Anderes', color: '#228B22' },
{ 'type': 'populated', group: 'populated', label: 'Besiedeltes Gebiet', color: '#0000FF' },
{ 'type': 'see', group: 'see', label: 'Sehenswürdigkeit', color: '#4682B4' },
{ 'type': 'sleep', group: 'sleep', label: 'Unterkunft', color: '#000080' },
{ 'type': 'view', group: 'view', label: 'Aussicht', 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>