Bahnhof anlegen: Unterschied zwischen den Versionen
Admin (Diskussion | Beiträge) Die Seite wurde geleert. Markierungen: Geleert Manuelle Zurücksetzung |
Admin (Diskussion | Beiträge) Keine Bearbeitungszusammenfassung |
||
| Zeile 1: | Zeile 1: | ||
[[Hauptseite]] > [[Hauptseite#Schwarzatal.org|Schwarzatal.org]] | |||
/** | |||
* Gadget-Bahnhof_anlegen.js | |||
* ============================================================================ | |||
* Komfortables Anlegen von Bahnhofsseiten. Button erscheint nur auf der Seite | |||
* "Bahnhof anlegen" und nur für angemeldete Nutzer. | |||
* | |||
* Erzeugt eine Seite "Bahnhof <Name>" mit: | |||
* - Breadcrumb + 🌐 Website + Wikipedia | |||
* - WikiMap-Karte (aktiver Ort = Seitentitel, Filter "Bahnhof", | |||
* Start-Layer "Zug + Luftbild + Karte") | |||
* - Standort-Zeilen (📍 Adresse, OSM, 🧭 Koordinaten, CoMaps, Google Maps) | |||
* mit Auto-Links aus Koordinaten | |||
* - Abschnitt "Monitore" mit zwei Bahnsteiganzeigern (dep/arr) und je einem | |||
* Link zur detaillierten ÖBB-Tafel | |||
* | |||
* Die evaId wird von Hand eingetragen ("Prüfen" öffnet die Monitore-Ansicht | |||
* dieser evaId zur Kontrolle). Die WikiMap-Orte.json wird NICHT angefasst | |||
* (pflegst du selbst). | |||
* ============================================================================ | |||
*/ | |||
( function () { | |||
'use strict'; | |||
// Button nur auf dieser Seite (wgPageName nutzt Unterstriche). | |||
if ( mw.config.get( 'wgPageName' ) !== 'Bahnhof_anlegen' ) { | |||
return; | |||
} | |||
if ( !mw.config.get( 'wgUserName' ) ) { | |||
return; | |||
} | |||
// Start-Layer der Karte auf Bahnhofsseiten. | |||
var KARTE_LAYER = 'Zug + Luftbild + Karte'; | |||
function v( val ) { | |||
return ( val || '' ).trim(); | |||
} | |||
/* ------------------------------------------------------------------ * | |||
* Koordinaten-Parsing + Link-Bau. | |||
* ------------------------------------------------------------------ */ | |||
function parseCoordinates( input ) { | |||
if ( !input ) { | |||
return null; | |||
} | |||
var s = input.trim(); | |||
var m; | |||
m = /^(-?\d+,\d+)\s*[;\s]\s*(-?\d+,\d+)$/.exec( s ) || | |||
/^(-?\d+,\d+),\s+(-?\d+,\d+)$/.exec( s ); | |||
if ( m ) { | |||
var latDE = parseFloat( m[ 1 ].replace( ',', '.' ) ); | |||
var lngDE = parseFloat( m[ 2 ].replace( ',', '.' ) ); | |||
if ( isFinite( latDE ) && isFinite( lngDE ) && | |||
Math.abs( latDE ) <= 90 && Math.abs( lngDE ) <= 180 ) { | |||
return { lat: latDE, lng: lngDE }; | |||
} | |||
} | |||
m = /^(-?\d+(?:\.\d+)?)\s*[,;\s]\s*(-?\d+(?:\.\d+)?)$/.exec( s ); | |||
if ( m ) { | |||
var lat = parseFloat( m[ 1 ] ); | |||
var lng = parseFloat( m[ 2 ] ); | |||
if ( isFinite( lat ) && isFinite( lng ) && | |||
Math.abs( lat ) <= 90 && Math.abs( lng ) <= 180 ) { | |||
return { lat: lat, lng: lng }; | |||
} | |||
} | |||
m = /^(\d+(?:[.,]\d+)?)\s*°?\s*([NS])\s*[,;\s]?\s*(\d+(?:[.,]\d+)?)\s*°?\s*([EW])$/i.exec( s ); | |||
if ( m ) { | |||
var lat2 = parseFloat( m[ 1 ].replace( ',', '.' ) ); | |||
if ( m[ 2 ].toUpperCase() === 'S' ) { lat2 = -lat2; } | |||
var lng2 = parseFloat( m[ 3 ].replace( ',', '.' ) ); | |||
if ( m[ 4 ].toUpperCase() === 'W' ) { lng2 = -lng2; } | |||
if ( isFinite( lat2 ) && isFinite( lng2 ) && | |||
Math.abs( lat2 ) <= 90 && Math.abs( lng2 ) <= 180 ) { | |||
return { lat: lat2, lng: lng2 }; | |||
} | |||
} | |||
m = /^(\d+)\s*°\s*(\d+)\s*['\u2019\u2032]\s*(\d+(?:[.,]\d+)?)\s*["\u201D\u2033]\s*([NS])\s*[,;\s]?\s*(\d+)\s*°\s*(\d+)\s*['\u2019\u2032]\s*(\d+(?:[.,]\d+)?)\s*["\u201D\u2033]\s*([EW])$/i.exec( s ); | |||
if ( m ) { | |||
var lat3 = parseInt( m[ 1 ], 10 ) + parseInt( m[ 2 ], 10 ) / 60 + parseFloat( m[ 3 ].replace( ',', '.' ) ) / 3600; | |||
if ( m[ 4 ].toUpperCase() === 'S' ) { lat3 = -lat3; } | |||
var lng3 = parseInt( m[ 5 ], 10 ) + parseInt( m[ 6 ], 10 ) / 60 + parseFloat( m[ 7 ].replace( ',', '.' ) ) / 3600; | |||
if ( m[ 8 ].toUpperCase() === 'W' ) { lng3 = -lng3; } | |||
if ( Math.abs( lat3 ) <= 90 && Math.abs( lng3 ) <= 180 ) { | |||
return { lat: lat3, lng: lng3 }; | |||
} | |||
} | |||
m = /^(\d+)\s*°\s*(\d+(?:[.,]\d+)?)\s*['\u2019\u2032]\s*([NS])\s*[,;\s]?\s*(\d+)\s*°\s*(\d+(?:[.,]\d+)?)\s*['\u2019\u2032]\s*([EW])$/i.exec( s ); | |||
if ( m ) { | |||
var lat4 = parseInt( m[ 1 ], 10 ) + parseFloat( m[ 2 ].replace( ',', '.' ) ) / 60; | |||
if ( m[ 3 ].toUpperCase() === 'S' ) { lat4 = -lat4; } | |||
var lng4 = parseInt( m[ 4 ], 10 ) + parseFloat( m[ 5 ].replace( ',', '.' ) ) / 60; | |||
if ( m[ 6 ].toUpperCase() === 'W' ) { lng4 = -lng4; } | |||
if ( Math.abs( lat4 ) <= 90 && Math.abs( lng4 ) <= 180 ) { | |||
return { lat: lat4, lng: lng4 }; | |||
} | |||
} | |||
return null; | |||
} | |||
function fmtCoord( n ) { | |||
return n.toFixed( 7 ); | |||
} | |||
function buildOsmUrl( c ) { | |||
var lat = fmtCoord( c.lat ), lng = fmtCoord( c.lng ); | |||
return 'https://www.openstreetmap.org/?mlat=' + lat + '&mlon=' + lng + '#map=17/' + lat + '/' + lng; | |||
} | |||
function buildGoogleMapsUrl( c ) { | |||
return 'https://www.google.com/maps?q=' + fmtCoord( c.lat ) + ',' + fmtCoord( c.lng ); | |||
} | |||
function buildCoMapsUrl( c, name ) { | |||
// CoMaps/Organic Maps: ll = Koordinaten, n = URL-kodierter Name. | |||
return 'https://comaps.at/map?v=1&ll=' + fmtCoord( c.lat ) + ',' + fmtCoord( c.lng ) + | |||
'&n=' + encodeURIComponent( name || '' ); | |||
} | |||
/* ------------------------------------------------------------------ * | |||
* ÖBB-Detaillink (volle Tafel). typ: "dep"/"arr" -> sqView 2/3. | |||
* "mon" -> Monitore-Ansicht (sqView 1), für den Prüf-Knopf. | |||
* ------------------------------------------------------------------ */ | |||
function baueDetailUrl( eva, typ ) { | |||
var boardType = ( typ === 'arr' ) ? 'arr' : 'dep'; | |||
var sqView = ( typ === 'arr' ) ? '3' : ( typ === 'mon' ? '1' : '2' ); | |||
return 'https://fahrplan.oebb.at/bin/stboard.exe/dn?protocol=https:&input=' + | |||
encodeURIComponent( eva ) + | |||
'&boardType=' + boardType + | |||
'&sqView=' + sqView + | |||
'&productsFilter=1111111111111&maxJourneys=40&start=yes&newrequest=yes&'; | |||
} | |||
/* ------------------------------------------------------------------ * | |||
* Wikitext der Bahnhofsseite. | |||
* Abstand: nach jedem BLOCK zwei <br> + Leerzeile. Die einzelnen | |||
* Standort-Zeilen bleiben eng (nur je eine Leerzeile = eigener Absatz). | |||
* ------------------------------------------------------------------ */ | |||
function buildWikitext( f ) { | |||
var L = []; | |||
function block() { | |||
L.push( '<br><br>' ); | |||
L.push( '' ); | |||
} | |||
L.push( '[[Hauptseite]] > [[Hauptseite#Öffentlicher Verkehr|Öffentlicher Verkehr]] > [[Zug]] > [[Zug#Bahnhöfe|Bahnhöfe]]' ); | |||
block(); | |||
if ( f.website ) { | |||
L.push( '🌐 ' + f.website ); | |||
block(); | |||
} | |||
if ( f.wikipedia ) { | |||
L.push( '[[file:Wikipedia.png|24px|link=|Wikipedia]] ' + f.wikipedia ); | |||
block(); | |||
} | |||
// Karte: aktiver Ort = Seitentitel, nur Bahnhof-Pins, Start-Layer. | |||
L.push( '<div class="wikimap" data-aktiv-ort="' + f.titel + | |||
'" data-filter="Bahnhof" data-layer="' + KARTE_LAYER + '"></div>' ); | |||
block(); | |||
// Standort-Zeilen – eng, jede in eigenem Absatz (Leerzeile dazwischen). | |||
var hatStandort = false; | |||
var first = true; | |||
function loc( line ) { | |||
if ( !first ) { L.push( '' ); } | |||
L.push( line ); | |||
first = false; | |||
hatStandort = true; | |||
} | |||
if ( f.adresse ) { loc( '📍 ' + f.adresse ); } | |||
if ( f.osmUrl ) { loc( '[[file:osm.png|24px|link=]] ' + f.osmUrl ); } | |||
if ( f.koordinaten ) { loc( '🧭 ' + f.koordinaten ); } | |||
if ( f.comapsUrl ) { loc( '[[file:CoMaps.png|24px|link=]] ' + f.comapsUrl ); } | |||
if ( f.gmapsUrl ) { loc( '[[file:GoogleMaps.png|24px|link=]] ' + f.gmapsUrl ); } | |||
if ( hatStandort ) { | |||
L.push( '<br><br>' ); | |||
block(); | |||
} | |||
// Monitore (intern: Bahnsteiganzeiger). Link jeweils direkt unter dem Anzeiger. | |||
L.push( '== Monitore ==' ); | |||
block(); | |||
L.push( '<div class="bahnsteiganzeiger" data-evaid="' + f.evaId + '" data-typ="dep"></div>' ); | |||
L.push( '' ); | |||
L.push( '[' + baueDetailUrl( f.evaId, 'dep' ) + ' Zur detaillierten Abfahrtstafel]' ); | |||
L.push( '<br><br>' ); | |||
block(); | |||
L.push( '<div class="bahnsteiganzeiger" data-evaid="' + f.evaId + '" data-typ="arr"></div>' ); | |||
L.push( '' ); | |||
L.push( '[' + baueDetailUrl( f.evaId, 'arr' ) + ' Zur detaillierten Ankunftstafel]' ); | |||
block(); | |||
return L.join( '\n' ); | |||
} | |||
/* ------------------------------------------------------------------ * | |||
* Oberfläche. | |||
* ------------------------------------------------------------------ */ | |||
mw.loader.using( [ | |||
'oojs-ui-core', | |||
'oojs-ui-windows', | |||
'oojs-ui.styles.icons-interactions', | |||
'oojs-ui.styles.icons-content', | |||
'mediawiki.api', | |||
'mediawiki.util' | |||
] ).then( function () { | |||
function BahnhofDialog( config ) { | |||
BahnhofDialog.super.call( this, config ); | |||
} | |||
OO.inheritClass( BahnhofDialog, OO.ui.ProcessDialog ); | |||
BahnhofDialog.static.name = 'bahnhofDialog'; | |||
BahnhofDialog.static.title = 'Neuen Bahnhof anlegen'; | |||
BahnhofDialog.static.actions = [ | |||
{ action: 'save', label: 'Seite anlegen', flags: [ 'primary', 'progressive' ] }, | |||
{ label: 'Abbrechen', flags: 'safe' } | |||
]; | |||
BahnhofDialog.prototype.initialize = function () { | |||
BahnhofDialog.super.prototype.initialize.apply( this, arguments ); | |||
var dialog = this; | |||
this.panel = new OO.ui.PanelLayout( { padded: true, expanded: false } ); | |||
this.nameInput = new OO.ui.TextInputWidget( { placeholder: 'z.B. Pottschach', required: true } ); | |||
this.evaidInput = new OO.ui.TextInputWidget( { placeholder: 'z.B. 1131839', required: true } ); | |||
this.pruefButton = new OO.ui.ButtonWidget( { label: 'Prüfen', icon: 'linkExternal' } ); | |||
this.websiteInput = new OO.ui.TextInputWidget( { placeholder: 'https://bahnhof.oebb.at/…' } ); | |||
this.wikipediaInput = new OO.ui.TextInputWidget( { placeholder: 'https://de.wikipedia.org/wiki/…' } ); | |||
this.adresseInput = new OO.ui.TextInputWidget( { placeholder: '2630 Pottschach, Putzmannsdorfer Straße 2' } ); | |||
this.koordinatenInput = new OO.ui.TextInputWidget( { placeholder: '47.69694, 16.00583' } ); | |||
this.osmUrlInput = new OO.ui.TextInputWidget( { placeholder: 'https://www.openstreetmap.org/…' } ); | |||
this.comapsUrlInput = new OO.ui.TextInputWidget( { placeholder: 'https://comaps.at/…' } ); | |||
this.gmapsUrlInput = new OO.ui.TextInputWidget( { placeholder: 'https://maps.app.goo.gl/…' } ); | |||
// evaId im Browser prüfen -> Monitore-Ansicht (sqView=1). | |||
this.pruefButton.on( 'click', function () { | |||
var eva = v( dialog.evaidInput.getValue() ).replace( /^0+/, '' ); | |||
if ( !eva ) { return; } | |||
window.open( baueDetailUrl( eva, 'mon' ), '_blank', 'noopener' ); | |||
} ); | |||
var fsPflicht = new OO.ui.FieldsetLayout( { label: 'Bahnhof (Pflicht)' } ); | |||
fsPflicht.addItems( [ | |||
new OO.ui.FieldLayout( this.nameInput, { | |||
label: 'Bahnhofsname', align: 'left', | |||
help: 'Wird zu „Bahnhof <Name>" – das ist Seitentitel UND Karten-ID (data-aktiv-ort). Der gleichnamige Eintrag muss in WikiMap-Orte.json existieren.' | |||
} ), | |||
new OO.ui.ActionFieldLayout( this.evaidInput, this.pruefButton, { | |||
label: 'evaId', align: 'left', | |||
help: 'Stationsnummer, nur Ziffern (z.B. 1131839). Findest du in der alten „Zug"-Vorlage oder in der ÖBB-Adresse hinter evaId=. „Prüfen" öffnet die Monitore-Ansicht dieser evaId in einem neuen Tab.' | |||
} ) | |||
] ); | |||
var fsStandort = new OO.ui.FieldsetLayout( { label: 'Standort (optional)' } ); | |||
fsStandort.addItems( [ | |||
new OO.ui.FieldLayout( this.websiteInput, { | |||
label: '🌐 Website', align: 'left', | |||
help: 'z.B. die ÖBB-Bahnhofsseite. Steht als erste Zeile oben; das Panorama kommt später von Hand darüber.' | |||
} ), | |||
new OO.ui.FieldLayout( this.wikipediaInput, { | |||
label: 'Wikipedia', align: 'left', | |||
help: 'Link zum Wikipedia-Artikel des Bahnhofs (optional).' | |||
} ), | |||
new OO.ui.FieldLayout( this.adresseInput, { label: '📍 Adresse', align: 'left' } ), | |||
new OO.ui.FieldLayout( this.koordinatenInput, { | |||
label: '🧭 Koordinaten', align: 'left', | |||
help: 'Akzeptiert u.a.: 47.69694, 16.00583 · deutsches Komma-Format · 47°45\'00"N 15°40\'48"E. Wenn gesetzt, werden für leere OSM/CoMaps/Google-Maps-Felder automatisch Links erzeugt. (Der Karten-Pin kommt aus WikiMap-Orte.json, nicht von hier.)' | |||
} ), | |||
new OO.ui.FieldLayout( this.osmUrlInput, { | |||
label: 'OpenStreetMap-URL', align: 'left', | |||
help: 'Leer lassen -> wird bei gegebenen Koordinaten automatisch erzeugt.' | |||
} ), | |||
new OO.ui.FieldLayout( this.comapsUrlInput, { | |||
label: 'CoMaps-URL', align: 'left', | |||
help: 'Leer lassen -> wird bei gegebenen Koordinaten automatisch erzeugt (mit dem Seitentitel als Pin-Namen).' | |||
} ), | |||
new OO.ui.FieldLayout( this.gmapsUrlInput, { | |||
label: 'Google-Maps-URL', align: 'left', | |||
help: 'Leer lassen -> wird bei gegebenen Koordinaten automatisch erzeugt.' | |||
} ) | |||
] ); | |||
this.panel.$element.append( fsPflicht.$element, fsStandort.$element ); | |||
this.$body.append( this.panel.$element ); | |||
}; | |||
BahnhofDialog.prototype.getActionProcess = function ( action ) { | |||
var dialog = this; | |||
function fehler( msg ) { | |||
return $.Deferred().reject( new OO.ui.Error( msg, { recoverable: true } ) ).promise(); | |||
} | |||
if ( action === 'save' ) { | |||
return new OO.ui.Process( function () { | |||
var name = v( dialog.nameInput.getValue() ); | |||
var evaId = v( dialog.evaidInput.getValue() ).replace( /^0+/, '' ); | |||
if ( !name ) { | |||
return fehler( 'Bitte den Bahnhofsnamen ausfüllen.' ); | |||
} | |||
if ( !/^\d+$/.test( evaId ) ) { | |||
return fehler( 'Bitte eine gültige evaId (nur Ziffern) eintragen.' ); | |||
} | |||
var titel = /^Bahnhof\s/i.test( name ) ? name : 'Bahnhof ' + name; | |||
var fields = { | |||
titel: titel, | |||
evaId: evaId, | |||
website: v( dialog.websiteInput.getValue() ), | |||
wikipedia: v( dialog.wikipediaInput.getValue() ), | |||
adresse: v( dialog.adresseInput.getValue() ), | |||
koordinaten: v( dialog.koordinatenInput.getValue() ), | |||
osmUrl: v( dialog.osmUrlInput.getValue() ), | |||
comapsUrl: v( dialog.comapsUrlInput.getValue() ), | |||
gmapsUrl: v( dialog.gmapsUrlInput.getValue() ) | |||
}; | |||
var coords = parseCoordinates( fields.koordinaten ); | |||
if ( coords ) { | |||
if ( !fields.osmUrl ) { fields.osmUrl = buildOsmUrl( coords ); } | |||
if ( !fields.comapsUrl ) { fields.comapsUrl = buildCoMapsUrl( coords, titel ); } | |||
if ( !fields.gmapsUrl ) { fields.gmapsUrl = buildGoogleMapsUrl( coords ); } | |||
} | |||
var wikitext = buildWikitext( fields ); | |||
var api = new mw.Api(); | |||
return api.get( { | |||
action: 'query', titles: titel, formatversion: 2 | |||
} ).then( function ( data ) { | |||
var page = data.query.pages[ 0 ]; | |||
if ( !page.missing ) { | |||
return fehler( 'Eine Seite „' + titel + '" existiert bereits.' ); | |||
} | |||
return api.postWithToken( 'csrf', { | |||
action: 'edit', | |||
title: titel, | |||
text: wikitext, | |||
createonly: true, | |||
summary: 'Bahnhofsseite über Bahnhof-anlegen-Gadget angelegt' | |||
} ); | |||
} ).then( function () { | |||
window.location.href = mw.util.getUrl( titel ); | |||
}, function ( err ) { | |||
if ( err instanceof OO.ui.Error ) { | |||
return $.Deferred().reject( err ).promise(); | |||
} | |||
return fehler( 'Fehler beim Anlegen: ' + ( err && err.toString ? err.toString() : 'unbekannt' ) ); | |||
} ); | |||
} ); | |||
} | |||
return BahnhofDialog.super.prototype.getActionProcess.call( this, action ); | |||
}; | |||
BahnhofDialog.prototype.getBodyHeight = function () { | |||
return 600; | |||
}; | |||
var $container = $( '#bahnhof-form-container' ); | |||
if ( !$container.length ) { | |||
$container = $( '<div id="bahnhof-form-container"></div>' ) | |||
.appendTo( '#mw-content-text .mw-parser-output' ); | |||
} | |||
var button = new OO.ui.ButtonWidget( { | |||
label: 'Neuen Bahnhof anlegen', | |||
flags: [ 'primary', 'progressive' ], | |||
icon: 'add' | |||
} ); | |||
var windowManager = new OO.ui.WindowManager(); | |||
$( document.body ).append( windowManager.$element ); | |||
var dialog = new BahnhofDialog( { size: 'large' } ); | |||
windowManager.addWindows( [ dialog ] ); | |||
button.on( 'click', function () { | |||
windowManager.openWindow( dialog ); | |||
} ); | |||
$container.empty().append( button.$element ); | |||
} ); | |||
}() ); | |||
Version vom 10. Juli 2026, 08:01 Uhr
/**
* Gadget-Bahnhof_anlegen.js
* ============================================================================
* Komfortables Anlegen von Bahnhofsseiten. Button erscheint nur auf der Seite
* "Bahnhof anlegen" und nur für angemeldete Nutzer.
*
* Erzeugt eine Seite "Bahnhof <Name>" mit:
* - Breadcrumb + 🌐 Website + Wikipedia
* - WikiMap-Karte (aktiver Ort = Seitentitel, Filter "Bahnhof",
* Start-Layer "Zug + Luftbild + Karte")
* - Standort-Zeilen (📍 Adresse, OSM, 🧭 Koordinaten, CoMaps, Google Maps)
* mit Auto-Links aus Koordinaten
* - Abschnitt "Monitore" mit zwei Bahnsteiganzeigern (dep/arr) und je einem
* Link zur detaillierten ÖBB-Tafel
*
* Die evaId wird von Hand eingetragen ("Prüfen" öffnet die Monitore-Ansicht
* dieser evaId zur Kontrolle). Die WikiMap-Orte.json wird NICHT angefasst
* (pflegst du selbst).
* ============================================================================
*/
( function () { 'use strict';
// Button nur auf dieser Seite (wgPageName nutzt Unterstriche). if ( mw.config.get( 'wgPageName' ) !== 'Bahnhof_anlegen' ) { return; } if ( !mw.config.get( 'wgUserName' ) ) { return; }
// Start-Layer der Karte auf Bahnhofsseiten. var KARTE_LAYER = 'Zug + Luftbild + Karte';
function v( val ) { return ( val || ).trim(); }
/* ------------------------------------------------------------------ * * Koordinaten-Parsing + Link-Bau. * ------------------------------------------------------------------ */ function parseCoordinates( input ) { if ( !input ) { return null; } var s = input.trim(); var m;
m = /^(-?\d+,\d+)\s*[;\s]\s*(-?\d+,\d+)$/.exec( s ) || /^(-?\d+,\d+),\s+(-?\d+,\d+)$/.exec( s ); if ( m ) { var latDE = parseFloat( m[ 1 ].replace( ',', '.' ) ); var lngDE = parseFloat( m[ 2 ].replace( ',', '.' ) ); if ( isFinite( latDE ) && isFinite( lngDE ) && Math.abs( latDE ) <= 90 && Math.abs( lngDE ) <= 180 ) { return { lat: latDE, lng: lngDE }; } }
m = /^(-?\d+(?:\.\d+)?)\s*[,;\s]\s*(-?\d+(?:\.\d+)?)$/.exec( s ); if ( m ) { var lat = parseFloat( m[ 1 ] ); var lng = parseFloat( m[ 2 ] ); if ( isFinite( lat ) && isFinite( lng ) && Math.abs( lat ) <= 90 && Math.abs( lng ) <= 180 ) { return { lat: lat, lng: lng }; } }
m = /^(\d+(?:[.,]\d+)?)\s*°?\s*([NS])\s*[,;\s]?\s*(\d+(?:[.,]\d+)?)\s*°?\s*([EW])$/i.exec( s ); if ( m ) { var lat2 = parseFloat( m[ 1 ].replace( ',', '.' ) ); if ( m[ 2 ].toUpperCase() === 'S' ) { lat2 = -lat2; } var lng2 = parseFloat( m[ 3 ].replace( ',', '.' ) ); if ( m[ 4 ].toUpperCase() === 'W' ) { lng2 = -lng2; } if ( isFinite( lat2 ) && isFinite( lng2 ) && Math.abs( lat2 ) <= 90 && Math.abs( lng2 ) <= 180 ) { return { lat: lat2, lng: lng2 }; } }
m = /^(\d+)\s*°\s*(\d+)\s*['\u2019\u2032]\s*(\d+(?:[.,]\d+)?)\s*["\u201D\u2033]\s*([NS])\s*[,;\s]?\s*(\d+)\s*°\s*(\d+)\s*['\u2019\u2032]\s*(\d+(?:[.,]\d+)?)\s*["\u201D\u2033]\s*([EW])$/i.exec( s ); if ( m ) { var lat3 = parseInt( m[ 1 ], 10 ) + parseInt( m[ 2 ], 10 ) / 60 + parseFloat( m[ 3 ].replace( ',', '.' ) ) / 3600; if ( m[ 4 ].toUpperCase() === 'S' ) { lat3 = -lat3; } var lng3 = parseInt( m[ 5 ], 10 ) + parseInt( m[ 6 ], 10 ) / 60 + parseFloat( m[ 7 ].replace( ',', '.' ) ) / 3600; if ( m[ 8 ].toUpperCase() === 'W' ) { lng3 = -lng3; } if ( Math.abs( lat3 ) <= 90 && Math.abs( lng3 ) <= 180 ) { return { lat: lat3, lng: lng3 }; } }
m = /^(\d+)\s*°\s*(\d+(?:[.,]\d+)?)\s*['\u2019\u2032]\s*([NS])\s*[,;\s]?\s*(\d+)\s*°\s*(\d+(?:[.,]\d+)?)\s*['\u2019\u2032]\s*([EW])$/i.exec( s ); if ( m ) { var lat4 = parseInt( m[ 1 ], 10 ) + parseFloat( m[ 2 ].replace( ',', '.' ) ) / 60; if ( m[ 3 ].toUpperCase() === 'S' ) { lat4 = -lat4; } var lng4 = parseInt( m[ 4 ], 10 ) + parseFloat( m[ 5 ].replace( ',', '.' ) ) / 60; if ( m[ 6 ].toUpperCase() === 'W' ) { lng4 = -lng4; } if ( Math.abs( lat4 ) <= 90 && Math.abs( lng4 ) <= 180 ) { return { lat: lat4, lng: lng4 }; } }
return null; }
function fmtCoord( n ) { return n.toFixed( 7 ); } function buildOsmUrl( c ) { var lat = fmtCoord( c.lat ), lng = fmtCoord( c.lng ); return 'https://www.openstreetmap.org/?mlat=' + lat + '&mlon=' + lng + '#map=17/' + lat + '/' + lng; } function buildGoogleMapsUrl( c ) { return 'https://www.google.com/maps?q=' + fmtCoord( c.lat ) + ',' + fmtCoord( c.lng ); } function buildCoMapsUrl( c, name ) { // CoMaps/Organic Maps: ll = Koordinaten, n = URL-kodierter Name. return 'https://comaps.at/map?v=1&ll=' + fmtCoord( c.lat ) + ',' + fmtCoord( c.lng ) + '&n=' + encodeURIComponent( name || ); }
/* ------------------------------------------------------------------ * * ÖBB-Detaillink (volle Tafel). typ: "dep"/"arr" -> sqView 2/3. * "mon" -> Monitore-Ansicht (sqView 1), für den Prüf-Knopf. * ------------------------------------------------------------------ */ function baueDetailUrl( eva, typ ) { var boardType = ( typ === 'arr' ) ? 'arr' : 'dep'; var sqView = ( typ === 'arr' ) ? '3' : ( typ === 'mon' ? '1' : '2' ); return 'https://fahrplan.oebb.at/bin/stboard.exe/dn?protocol=https:&input=' + encodeURIComponent( eva ) + '&boardType=' + boardType + '&sqView=' + sqView + '&productsFilter=1111111111111&maxJourneys=40&start=yes&newrequest=yes&'; }
/* ------------------------------------------------------------------ *
* Wikitext der Bahnhofsseite.
* Abstand: nach jedem BLOCK zwei
+ Leerzeile. Die einzelnen
* Standort-Zeilen bleiben eng (nur je eine Leerzeile = eigener Absatz).
* ------------------------------------------------------------------ */
function buildWikitext( f ) {
var L = [];
function block() {
L.push( '
' );
L.push( );
}
L.push( 'Hauptseite > Öffentlicher Verkehr > Zug > Bahnhöfe' ); block();
if ( f.website ) { L.push( '🌐 ' + f.website ); block(); }
if ( f.wikipedia ) {
L.push( '
' + f.wikipedia );
block();
}
// Karte: aktiver Ort = Seitentitel, nur Bahnhof-Pins, Start-Layer.
L.push( '
' );
block();
// Standort-Zeilen – eng, jede in eigenem Absatz (Leerzeile dazwischen).
var hatStandort = false;
var first = true;
function loc( line ) {
if ( !first ) { L.push( ); }
L.push( line );
first = false;
hatStandort = true;
}
if ( f.adresse ) { loc( '📍 ' + f.adresse ); }
if ( f.osmUrl ) { loc( '
' + f.osmUrl ); }
if ( f.koordinaten ) { loc( '🧭 ' + f.koordinaten ); }
if ( f.comapsUrl ) { loc( '
' + f.comapsUrl ); }
if ( f.gmapsUrl ) { loc( '
' + f.gmapsUrl ); }
if ( hatStandort ) {
L.push( '
' );
block();
}
// Monitore (intern: Bahnsteiganzeiger). Link jeweils direkt unter dem Anzeiger. L.push( '== Monitore ==' ); block();
L.push( '
' );
L.push( );
L.push( '[' + baueDetailUrl( f.evaId, 'dep' ) + ' Zur detaillierten Abfahrtstafel]' );
L.push( '
' );
block();
L.push( '
' );
L.push( ); L.push( '[' + baueDetailUrl( f.evaId, 'arr' ) + ' Zur detaillierten Ankunftstafel]' ); block();
return L.join( '\n' ); }
/* ------------------------------------------------------------------ * * Oberfläche. * ------------------------------------------------------------------ */ mw.loader.using( [ 'oojs-ui-core', 'oojs-ui-windows', 'oojs-ui.styles.icons-interactions', 'oojs-ui.styles.icons-content', 'mediawiki.api', 'mediawiki.util' ] ).then( function () {
function BahnhofDialog( config ) { BahnhofDialog.super.call( this, config ); } OO.inheritClass( BahnhofDialog, OO.ui.ProcessDialog );
BahnhofDialog.static.name = 'bahnhofDialog'; BahnhofDialog.static.title = 'Neuen Bahnhof anlegen'; BahnhofDialog.static.actions = [ { action: 'save', label: 'Seite anlegen', flags: [ 'primary', 'progressive' ] }, { label: 'Abbrechen', flags: 'safe' } ];
BahnhofDialog.prototype.initialize = function () { BahnhofDialog.super.prototype.initialize.apply( this, arguments ); var dialog = this;
this.panel = new OO.ui.PanelLayout( { padded: true, expanded: false } );
this.nameInput = new OO.ui.TextInputWidget( { placeholder: 'z.B. Pottschach', required: true } );
this.evaidInput = new OO.ui.TextInputWidget( { placeholder: 'z.B. 1131839', required: true } ); this.pruefButton = new OO.ui.ButtonWidget( { label: 'Prüfen', icon: 'linkExternal' } );
this.websiteInput = new OO.ui.TextInputWidget( { placeholder: 'https://bahnhof.oebb.at/…' } ); this.wikipediaInput = new OO.ui.TextInputWidget( { placeholder: 'https://de.wikipedia.org/wiki/…' } ); this.adresseInput = new OO.ui.TextInputWidget( { placeholder: '2630 Pottschach, Putzmannsdorfer Straße 2' } ); this.koordinatenInput = new OO.ui.TextInputWidget( { placeholder: '47.69694, 16.00583' } ); this.osmUrlInput = new OO.ui.TextInputWidget( { placeholder: 'https://www.openstreetmap.org/…' } ); this.comapsUrlInput = new OO.ui.TextInputWidget( { placeholder: 'https://comaps.at/…' } ); this.gmapsUrlInput = new OO.ui.TextInputWidget( { placeholder: 'https://maps.app.goo.gl/…' } );
// evaId im Browser prüfen -> Monitore-Ansicht (sqView=1). this.pruefButton.on( 'click', function () { var eva = v( dialog.evaidInput.getValue() ).replace( /^0+/, ); if ( !eva ) { return; } window.open( baueDetailUrl( eva, 'mon' ), '_blank', 'noopener' ); } );
var fsPflicht = new OO.ui.FieldsetLayout( { label: 'Bahnhof (Pflicht)' } ); fsPflicht.addItems( [ new OO.ui.FieldLayout( this.nameInput, { label: 'Bahnhofsname', align: 'left', help: 'Wird zu „Bahnhof <Name>" – das ist Seitentitel UND Karten-ID (data-aktiv-ort). Der gleichnamige Eintrag muss in WikiMap-Orte.json existieren.' } ), new OO.ui.ActionFieldLayout( this.evaidInput, this.pruefButton, { label: 'evaId', align: 'left', help: 'Stationsnummer, nur Ziffern (z.B. 1131839). Findest du in der alten „Zug"-Vorlage oder in der ÖBB-Adresse hinter evaId=. „Prüfen" öffnet die Monitore-Ansicht dieser evaId in einem neuen Tab.' } ) ] );
var fsStandort = new OO.ui.FieldsetLayout( { label: 'Standort (optional)' } ); fsStandort.addItems( [ new OO.ui.FieldLayout( this.websiteInput, { label: '🌐 Website', align: 'left', help: 'z.B. die ÖBB-Bahnhofsseite. Steht als erste Zeile oben; das Panorama kommt später von Hand darüber.' } ), new OO.ui.FieldLayout( this.wikipediaInput, { label: 'Wikipedia', align: 'left', help: 'Link zum Wikipedia-Artikel des Bahnhofs (optional).' } ), new OO.ui.FieldLayout( this.adresseInput, { label: '📍 Adresse', align: 'left' } ), new OO.ui.FieldLayout( this.koordinatenInput, { label: '🧭 Koordinaten', align: 'left', help: 'Akzeptiert u.a.: 47.69694, 16.00583 · deutsches Komma-Format · 47°45\'00"N 15°40\'48"E. Wenn gesetzt, werden für leere OSM/CoMaps/Google-Maps-Felder automatisch Links erzeugt. (Der Karten-Pin kommt aus WikiMap-Orte.json, nicht von hier.)' } ), new OO.ui.FieldLayout( this.osmUrlInput, { label: 'OpenStreetMap-URL', align: 'left', help: 'Leer lassen -> wird bei gegebenen Koordinaten automatisch erzeugt.' } ), new OO.ui.FieldLayout( this.comapsUrlInput, { label: 'CoMaps-URL', align: 'left', help: 'Leer lassen -> wird bei gegebenen Koordinaten automatisch erzeugt (mit dem Seitentitel als Pin-Namen).' } ), new OO.ui.FieldLayout( this.gmapsUrlInput, { label: 'Google-Maps-URL', align: 'left', help: 'Leer lassen -> wird bei gegebenen Koordinaten automatisch erzeugt.' } ) ] );
this.panel.$element.append( fsPflicht.$element, fsStandort.$element ); this.$body.append( this.panel.$element ); };
BahnhofDialog.prototype.getActionProcess = function ( action ) { var dialog = this; function fehler( msg ) { return $.Deferred().reject( new OO.ui.Error( msg, { recoverable: true } ) ).promise(); } if ( action === 'save' ) { return new OO.ui.Process( function () { var name = v( dialog.nameInput.getValue() ); var evaId = v( dialog.evaidInput.getValue() ).replace( /^0+/, );
if ( !name ) { return fehler( 'Bitte den Bahnhofsnamen ausfüllen.' ); } if ( !/^\d+$/.test( evaId ) ) { return fehler( 'Bitte eine gültige evaId (nur Ziffern) eintragen.' ); }
var titel = /^Bahnhof\s/i.test( name ) ? name : 'Bahnhof ' + name;
var fields = { titel: titel, evaId: evaId, website: v( dialog.websiteInput.getValue() ), wikipedia: v( dialog.wikipediaInput.getValue() ), adresse: v( dialog.adresseInput.getValue() ), koordinaten: v( dialog.koordinatenInput.getValue() ), osmUrl: v( dialog.osmUrlInput.getValue() ), comapsUrl: v( dialog.comapsUrlInput.getValue() ), gmapsUrl: v( dialog.gmapsUrlInput.getValue() ) };
var coords = parseCoordinates( fields.koordinaten ); if ( coords ) { if ( !fields.osmUrl ) { fields.osmUrl = buildOsmUrl( coords ); } if ( !fields.comapsUrl ) { fields.comapsUrl = buildCoMapsUrl( coords, titel ); } if ( !fields.gmapsUrl ) { fields.gmapsUrl = buildGoogleMapsUrl( coords ); } }
var wikitext = buildWikitext( fields ); var api = new mw.Api();
return api.get( { action: 'query', titles: titel, formatversion: 2 } ).then( function ( data ) { var page = data.query.pages[ 0 ]; if ( !page.missing ) { return fehler( 'Eine Seite „' + titel + '" existiert bereits.' ); } return api.postWithToken( 'csrf', { action: 'edit', title: titel, text: wikitext, createonly: true, summary: 'Bahnhofsseite über Bahnhof-anlegen-Gadget angelegt' } ); } ).then( function () { window.location.href = mw.util.getUrl( titel ); }, function ( err ) { if ( err instanceof OO.ui.Error ) { return $.Deferred().reject( err ).promise(); } return fehler( 'Fehler beim Anlegen: ' + ( err && err.toString ? err.toString() : 'unbekannt' ) ); } ); } ); } return BahnhofDialog.super.prototype.getActionProcess.call( this, action ); };
BahnhofDialog.prototype.getBodyHeight = function () { return 600; };
var $container = $( '#bahnhof-form-container' ); if ( !$container.length ) {
$container = $( '
' )
.appendTo( '#mw-content-text .mw-parser-output' ); }
var button = new OO.ui.ButtonWidget( { label: 'Neuen Bahnhof anlegen', flags: [ 'primary', 'progressive' ], icon: 'add' } );
var windowManager = new OO.ui.WindowManager(); $( document.body ).append( windowManager.$element ); var dialog = new BahnhofDialog( { size: 'large' } ); windowManager.addWindows( [ dialog ] );
button.on( 'click', function () { windowManager.openWindow( dialog ); } );
$container.empty().append( button.$element );
} ); }() );