MediaWiki:Gadget-WetterGenerator.js

Version vom 22. Juli 2026, 17:43 Uhr von Admin (Diskussion | Beiträge) (Die Seite wurde neu angelegt: „/** * MediaWiki:Gadget-WetterGenerator.js (Ausbaustufe 2) * ===================================================================== * Formular zum Erstellen von Wetterwidget-Bausteinen für Gemeindeseiten * – jetzt zusätzlich mit: * * A) WIKIMAP-EXPORT: Ein Klick holt die aktuelle Stationsliste live von * GeoSphere und erzeugt fertige Einträge für WikiMap-Orte.json * (Kategorie "Wetterstation", einstellbarer Umkreis, leer = ganz *…“)
(Unterschied) ← Nächstältere Version | Aktuelle Version (Unterschied) | Nächstjüngere Version → (Unterschied)

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
/**
 * MediaWiki:Gadget-WetterGenerator.js  (Ausbaustufe 2)
 * =====================================================================
 * Formular zum Erstellen von Wetterwidget-Bausteinen für Gemeindeseiten
 * – jetzt zusätzlich mit:
 *
 *  A) WIKIMAP-EXPORT: Ein Klick holt die aktuelle Stationsliste live von
 *     GeoSphere und erzeugt fertige Einträge für WikiMap-Orte.json
 *     (Kategorie "Wetterstation", einstellbarer Umkreis, leer = ganz
 *     Österreich). Die Einträge werden in die Liste "orte" der Datei
 *     kopiert – am besten VOR einen bestehenden Eintrag, dann stimmen
 *     die Kommas. Einmalig im Block "kategorien" ergänzen:
 *         "Wetterstation": "#2a6fb8"
 *
 *  B) KARTEN-RÜCKSPRUNG: Die exportierten Einträge verweisen im Feld
 *     "seite" auf diese Generator-Seite samt Adress-Parametern
 *     (ws_station, ws_name, ws_lat, ws_lon, ws_hoehe). Öffnet jemand
 *     einen Stationsmarker auf der WikiMap und folgt dem Link, steht
 *     hier sofort der fertige {{Wetterwidget|...}}-Baustein bereit.
 *
 * Datenquelle: GeoSphere Austria Dataset-API (Open Data, CC BY 4.0),
 * kein API-Schlüssel nötig. Doku: https://dataset.api.hub.geosphere.at/v1/docs/
 *
 * EINRICHTUNG: unverändert gegenüber Ausbaustufe 1 – dieser Code ersetzt
 * einfach den bisherigen Inhalt von [[MediaWiki:Gadget-WetterGenerator.js]].
 * Der Eintrag in [[MediaWiki:Gadgets-definition]] bleibt gleich:
 *      * WetterGenerator[ResourceLoader|default|hidden]|WetterGenerator.js
 * Die Generator-Seite enthält weiterhin nur:
 *      <div id="wetter-generator"></div>
 * =====================================================================
 */
( function () {
	'use strict';

	/* Stationsliste der 10-Minuten-Messstationen (TAWES) samt Metadaten */
	var META_URL = 'https://dataset.api.hub.geosphere.at/v1/' +
		'station/current/tawes-v1-10min/metadata';

	/* So viele nächstgelegene Stationen werden zur Auswahl angeboten */
	var MAX_STATIONEN = 5;

	/* Bezugspunkt für den Export-Umkreis: grobe Mitte des Schwarzatals
	 * (Raum Payerbach/Gloggnitz). */
	var EXPORT_ZENTRUM = { lat: 47.70, lon: 15.95 };
	var EXPORT_RADIUS_VORGABE = '50';

	var stationen = null;      /* geladene Stationsliste (wird nur 1x geholt) */
	var topStationen = [];     /* Ergebnis der letzten Suche, sortiert */
	var letzteSuche = null;    /* { lat, lon } der letzten Suche */
	var el = {};               /* Referenzen auf die Formularelemente */

	$( function () {
		var container = document.getElementById( 'wetter-generator' );
		if ( container ) {
			baueFormular( $( container ) );
			uebernimmAusKarte();
		}
	} );

	/* ------------------------------------------------------------------
	 * Formular aufbauen
	 * ------------------------------------------------------------------ */
	function baueFormular( $container ) {
		el.ort = $( '<input>' )
			.attr( { type: 'text', placeholder: 'z. B. Reichenau an der Rax' } )
			.css( 'width', '20em' )
			.on( 'input', aktualisiereBaustein );

		el.lat = $( '<input>' )
			.attr( { type: 'text', placeholder: 'z. B. 47,6969' } )
			.css( 'width', '10em' );

		el.lon = $( '<input>' )
			.attr( { type: 'text', placeholder: 'z. B. 15,8446' } )
			.css( 'width', '10em' );

		el.suchen = $( '<button>' )
			.attr( 'type', 'button' )
			.text( 'Stationen suchen' )
			.on( 'click', sucheStationen );

		el.status = $( '<div>' ).css( 'margin', '0.7em 0' );

		el.liste = $( '<div>' ).css( 'margin', '0.5em 0' );

		el.ausgabe = $( '<textarea>' )
			.attr( { rows: 8, readonly: 'readonly' } )
			.css( textFeldStil() );

		el.kopieren = $( '<button>' )
			.attr( 'type', 'button' )
			.text( 'Baustein kopieren' )
			.css( 'display', 'none' )
			.on( 'click', function () {
				kopiereFeld( el.ausgabe, setzeStatus,
					'Baustein kopiert – jetzt auf der Zielseite einfügen.' );
			} );

		/* --- Export-Bereich (WikiMap) --- */
		el.exportRadius = $( '<input>' )
			.attr( { type: 'text' } )
			.val( EXPORT_RADIUS_VORGABE )
			.css( 'width', '6em' );

		el.exportKnopf = $( '<button>' )
			.attr( 'type', 'button' )
			.text( 'Stationen exportieren' )
			.on( 'click', exportiereStationen );

		el.exportStatus = $( '<div>' ).css( 'margin', '0.7em 0' );

		el.exportAusgabe = $( '<textarea>' )
			.attr( { rows: 10, readonly: 'readonly' } )
			.css( textFeldStil() );

		el.exportKopieren = $( '<button>' )
			.attr( 'type', 'button' )
			.text( 'Einträge kopieren' )
			.css( 'display', 'none' )
			.on( 'click', function () {
				kopiereFeld( el.exportAusgabe, setzeExportStatus,
					'Einträge kopiert – jetzt in WikiMap-Orte.json einfügen.' );
			} );

		el.exportHinweis = $( '<div>' )
			.css( { 'margin-top': '0.5em', 'font-size': '85%', color: '#54595d',
				display: 'none' } )
			.text( 'Einmalig im Block "kategorien" von WikiMap-Orte.json ' +
				'ergänzen:  "Wetterstation": "#2a6fb8"  – dann bekommen die ' +
				'Stationen ihre eigene Pinfarbe und ihren Filter.' );

		$container.empty().append(
			kasten().append(
				$( '<p>' ).text(
					'Ort und Koordinaten eingeben – das Formular sucht die ' +
					'nächstgelegenen GeoSphere-Wetterstationen und erstellt ' +
					'den fertigen Baustein für die Gemeindeseite.'
				),
				feld( 'Ortsname', el.ort ),
				feld( 'Breitengrad (Dezimalgrad)', el.lat ),
				feld( 'Längengrad (Dezimalgrad)', el.lon ),
				$( '<div>' ).css( 'margin', '0.7em 0' ).append( el.suchen ),
				el.status,
				el.liste,
				el.ausgabe,
				$( '<div>' ).css( 'margin', '0.5em 0' ).append( el.kopieren )
			),
			kasten().css( 'margin-top', '1em' ).append(
				$( '<div>' )
					.css( { 'font-weight': 'bold', 'margin-bottom': '0.3em' } )
					.text( 'Stationen für die WikiMap' ),
				$( '<p>' ).css( 'margin', '0.3em 0' ).text(
					'Erzeugt fertige Einträge für WikiMap-Orte.json – alle ' +
					'derzeit aktiven Wetterstationen im gewählten Umkreis. ' +
					'Der Klick auf einen Stationsmarker der Karte führt ' +
					'danach hierher zurück und liefert sofort den passenden ' +
					'Widget-Baustein.'
				),
				feld( 'Umkreis in km (leer = ganz Österreich)', el.exportRadius ),
				$( '<div>' ).css( 'margin', '0.7em 0' ).append( el.exportKnopf ),
				el.exportStatus,
				el.exportAusgabe,
				$( '<div>' ).css( 'margin', '0.5em 0' ).append( el.exportKopieren ),
				el.exportHinweis
			),
			$( '<div>' )
				.css( { 'margin-top': '0.8em', 'font-size': '85%', color: '#54595d' } )
				.text( 'Stationsdaten: GeoSphere Austria, Dataset-API (CC BY 4.0)' )
		);
	}

	function kasten() {
		return $( '<div>' ).css( {
			border: '1px solid #a2a9b1',
			background: '#f8f9fa',
			padding: '1em',
			'max-width': '42em'
		} );
	}

	function feld( beschriftung, $eingabe ) {
		return $( '<div>' ).css( 'margin', '0.5em 0' ).append(
			$( '<label>' )
				.css( { display: 'block', 'font-weight': 'bold' } )
				.text( beschriftung ),
			$eingabe
		);
	}

	function textFeldStil() {
		return {
			width: '100%',
			'max-width': '38em',
			'font-family': 'monospace',
			display: 'none'
		};
	}

	/* ------------------------------------------------------------------
	 * Stationsliste laden (gemeinsam für Suche und Export)
	 * ------------------------------------------------------------------ */
	function ladeStationsliste( beiErfolg, beiFehler ) {
		if ( stationen ) {
			beiErfolg();
			return;
		}
		$.getJSON( META_URL )
			.done( function ( daten ) {
				stationen = lesbareStationen( daten );
				if ( !stationen.length ) {
					stationen = null;
					beiFehler( 'Die Antwort der GeoSphere-API enthielt keine ' +
						'Stationen. Bitte später erneut versuchen.' );
					return;
				}
				beiErfolg();
			} )
			.fail( function ( antwort ) {
				beiFehler( ladeFehlerText( antwort ) );
			} );
	}

	function ladeFehlerText( antwort ) {
		if ( !antwort || antwort.status === 0 ) {
			return 'Die Stationsliste konnte nicht geladen werden: Der Browser ' +
				'hat die Anfrage blockiert oder es besteht keine ' +
				'Internetverbindung.';
		}
		return 'Die Stationsliste konnte nicht geladen werden (Serverantwort ' +
			antwort.status + '). Bitte später erneut versuchen.';
	}

	/* API-Antwort in eine einheitliche Stationsliste umwandeln */
	function lesbareStationen( daten ) {
		var roh = ( daten && daten.stations ) || [];
		var ergebnis = [];
		$.each( roh, function ( i, st ) {
			var lat = holeZahl( st, [ 'lat', 'latitude' ] );
			var lon = holeZahl( st, [ 'lon', 'longitude' ] );
			var name;
			if ( lat === null || lon === null ) {
				return; /* ohne Koordinaten unbrauchbar */
			}
			if ( st.is_active === false ) {
				return; /* stillgelegte Stationen ausblenden */
			}
			name = String( st.name || 'Unbenannte Station' );
			ergebnis.push( {
				id: String( st.id ),
				name: name,
				anzeige: huebscherName( name ),
				bundesland: st.state ? String( st.state ) : '',
				lat: lat,
				lon: lon,
				hoehe: holeZahl( st, [ 'altitude', 'elevation' ] )
			} );
		} );
		return ergebnis;
	}

	/* ------------------------------------------------------------------
	 * Einzel-Suche (unverändertes Verhalten)
	 * ------------------------------------------------------------------ */
	function sucheStationen() {
		var lat = parseKoordinate( el.lat.val() );
		var lon = parseKoordinate( el.lon.val() );

		if ( lat === null || lon === null ) {
			setzeStatus( 'Bitte beide Koordinaten in Dezimalgrad eingeben, ' +
				'z. B. 47,6969 und 15,8446.', true );
			return;
		}
		letzteSuche = { lat: lat, lon: lon };
		setzeStatus( 'Stationsliste wird geladen …', false );
		ladeStationsliste( werteAus, function ( fehlerText ) {
			setzeStatus( fehlerText, true );
		} );
	}

	function werteAus() {
		var i;
		for ( i = 0; i < stationen.length; i++ ) {
			stationen[ i ].entfernung = entfernungKm(
				letzteSuche.lat, letzteSuche.lon,
				stationen[ i ].lat, stationen[ i ].lon
			);
		}
		topStationen = stationen.slice().sort( function ( a, b ) {
			return a.entfernung - b.entfernung;
		} ).slice( 0, MAX_STATIONEN );

		var hinweis = 'Nächstgelegene Stationen – die erste ist vorausgewählt:';
		if ( !imBereichOesterreich( letzteSuche.lat, letzteSuche.lon ) ) {
			hinweis = 'Achtung: Die Koordinaten liegen außerhalb Österreichs – ' +
				'bitte prüfen. ' + hinweis;
		}
		setzeStatus( hinweis, false );

		el.liste.empty();
		$.each( topStationen, function ( idx, st ) {
			el.liste.append( radioZeile( idx, st.anzeige +
				( st.bundesland ? ' (' + st.bundesland + ')' : '' ) +
				' – ' + formatKm( st.entfernung ) + ' entfernt, ' +
				( st.hoehe !== null ?
					Math.round( st.hoehe ) + ' m Seehöhe' : 'Höhe unbekannt' ),
				idx === 0 ) );
		} );

		el.ausgabe.show();
		el.kopieren.show();
		aktualisiereBaustein();
	}

	function radioZeile( idx, beschreibung, angehakt ) {
		return $( '<label>' )
			.css( { display: 'block', margin: '0.25em 0' } )
			.append(
				$( '<input>' )
					.attr( { type: 'radio', name: 'wg-station' } )
					.val( idx )
					.prop( 'checked', angehakt )
					.on( 'change', aktualisiereBaustein ),
				document.createTextNode( ' ' + beschreibung )
			);
	}

	/* ------------------------------------------------------------------
	 * Rücksprung von der WikiMap: ?ws_station=...&ws_lat=...
	 * ------------------------------------------------------------------ */
	function uebernimmAusKarte() {
		var id = leseUrlParameter( 'ws_station' );
		var lat = parseKoordinate( leseUrlParameter( 'ws_lat' ) || '' );
		var lon = parseKoordinate( leseUrlParameter( 'ws_lon' ) || '' );
		var name, hoehe;
		if ( !id || lat === null || lon === null ) {
			return;
		}
		name = leseUrlParameter( 'ws_name' ) || ( 'Nr. ' + id );
		hoehe = parseKoordinate( leseUrlParameter( 'ws_hoehe' ) || '' );

		el.ort.val( name );
		el.lat.val( String( lat ).replace( '.', ',' ) );
		el.lon.val( String( lon ).replace( '.', ',' ) );
		letzteSuche = { lat: lat, lon: lon };
		topStationen = [ {
			id: id, name: name, anzeige: name, bundesland: '',
			lat: lat, lon: lon, hoehe: hoehe, entfernung: 0
		} ];

		setzeStatus( 'Station aus der Karte übernommen – der Baustein steht ' +
			'unten bereit. Für andere Vorschläge einfach „Stationen suchen" ' +
			'klicken.', false );
		el.liste.empty().append( radioZeile( 0, name +
			( hoehe !== null ? ', ' + Math.round( hoehe ) + ' m Seehöhe' : '' ) +
			' (aus der Karte übernommen)', true ) );
		el.ausgabe.show();
		el.kopieren.show();
		aktualisiereBaustein();
	}

	function leseUrlParameter( name ) {
		var treffer = new RegExp( '[?&]' + name + '=([^&#]*)' )
			.exec( location.search );
		if ( !treffer ) {
			return null;
		}
		try {
			return decodeURIComponent( treffer[ 1 ].replace( /\+/g, ' ' ) );
		} catch ( e ) {
			return null;
		}
	}

	/* ------------------------------------------------------------------
	 * Export für WikiMap-Orte.json
	 * ------------------------------------------------------------------ */
	function exportiereStationen() {
		var radiusText = $.trim( el.exportRadius.val() );
		var radius = radiusText === '' ? null : parseKoordinate( radiusText );

		if ( radiusText !== '' && ( radius === null || radius <= 0 ) ) {
			setzeExportStatus( 'Bitte einen Umkreis in Kilometern angeben – ' +
				'oder das Feld leeren für ganz Österreich.', true );
			return;
		}
		setzeExportStatus( 'Stationsliste wird geladen …', false );
		ladeStationsliste( function () {
			baueExport( radius );
		}, function ( fehlerText ) {
			setzeExportStatus( fehlerText, true );
		} );
	}

	function baueExport( radius ) {
		var treffer = [];
		var zeilen = [];
		var i, st;

		for ( i = 0; i < stationen.length; i++ ) {
			st = stationen[ i ];
			if ( radius === null || entfernungKm( EXPORT_ZENTRUM.lat,
					EXPORT_ZENTRUM.lon, st.lat, st.lon ) <= radius ) {
				treffer.push( st );
			}
		}
		if ( !treffer.length ) {
			setzeExportStatus( 'Keine Station im gewählten Umkreis gefunden – ' +
				'bitte den Umkreis vergrößern.', true );
			return;
		}
		treffer.sort( function ( a, b ) {
			return a.anzeige.localeCompare( b.anzeige );
		} );

		for ( i = 0; i < treffer.length; i++ ) {
			st = treffer[ i ];
			zeilen.push( '  { "id": ' +
				JSON.stringify( 'Wetterstation ' + st.anzeige ) +
				', "lat": ' + st.lat.toFixed( 4 ) +
				', "lon": ' + st.lon.toFixed( 4 ) +
				', "kategorie": "Wetterstation"' +
				', "seite": ' + JSON.stringify( urlFuerStation( st ) ) +
				' },' );
		}

		el.exportAusgabe.val( zeilen.join( '\n' ) ).show();
		el.exportKopieren.show();
		el.exportHinweis.show();
		setzeExportStatus( treffer.length + ' Stationen bereit. Den Block in ' +
			'die Liste "orte" von WikiMap-Orte.json einfügen – am besten VOR ' +
			'einen bestehenden Eintrag, dann stimmen die Kommas. Die ' +
			'JSON-Seite meldet beim Speichern selbst, falls eines fehlt.',
			false );
	}

	/* Absolute Adresse dieser Generator-Seite samt Stations-Parametern.
	 * Absolut deshalb, weil die WikiMap externe Ziele (http/https)
	 * unverändert übernimmt – Wiki-Seitennamen dagegen könnten keine
	 * Parameter tragen. */
	function urlFuerStation( st ) {
		return location.protocol + '//' + location.host +
			mw.config.get( 'wgScript' ) +
			'?title=' + encodeURIComponent( mw.config.get( 'wgPageName' ) ) +
			'&ws_station=' + encodeURIComponent( st.id ) +
			'&ws_name=' + encodeURIComponent( st.anzeige ) +
			'&ws_lat=' + st.lat.toFixed( 4 ) +
			'&ws_lon=' + st.lon.toFixed( 4 ) +
			( st.hoehe !== null ? '&ws_hoehe=' + Math.round( st.hoehe ) : '' );
	}

	/* ------------------------------------------------------------------
	 * Baustein erzeugen und kopieren
	 * ------------------------------------------------------------------ */
	function aktualisiereBaustein() {
		var gewaehlt = el.liste.find( 'input:checked' ).val();
		if ( gewaehlt === undefined || !letzteSuche ) {
			return;
		}
		var st = topStationen[ parseInt( gewaehlt, 10 ) ];
		if ( !st ) {
			return;
		}
		el.ausgabe.val(
			'{{Wetterwidget\n' +
			'|ort = ' + $.trim( el.ort.val() ) + '\n' +
			'|lat = ' + letzteSuche.lat.toFixed( 4 ) + '\n' +
			'|lon = ' + letzteSuche.lon.toFixed( 4 ) + '\n' +
			'|station = ' + st.id + '\n' +
			'|stationsname = ' + ( st.anzeige || st.name ) + '\n' +
			'|stationshoehe = ' + ( st.hoehe !== null ? Math.round( st.hoehe ) : '' ) + '\n' +
			'}}'
		);
	}

	function kopiereFeld( $feld, statusSetzer, erfolgsMeldung ) {
		var text = $feld.val();
		if ( !text ) {
			statusSetzer( 'Noch nichts zu kopieren vorhanden.', true );
			return;
		}
		function fallback() {
			try {
				$feld[ 0 ].select();
				document.execCommand( 'copy' );
				statusSetzer( erfolgsMeldung, false );
			} catch ( e ) {
				statusSetzer( 'Kopieren nicht möglich – bitte den Text im ' +
					'Feld markieren und manuell kopieren.', true );
			}
		}
		if ( navigator.clipboard && navigator.clipboard.writeText ) {
			navigator.clipboard.writeText( text ).then( function () {
				statusSetzer( erfolgsMeldung, false );
			}, fallback );
		} else {
			fallback();
		}
	}

	/* ------------------------------------------------------------------
	 * Kleine Helfer
	 * ------------------------------------------------------------------ */

	/* GROSSSCHREIBUNG der API hübsch umwandeln: "WIEN/HOHE WARTE" wird zu
	 * "Wien/Hohe Warte", "LUNZ AM SEE" zu "Lunz am See". Umlaut-
	 * Umschreibungen (OE/UE/AE) werden nur zurückgewandelt, wenn kein
	 * Vokal davorsteht – so bleibt "Feuerkogel" verschont. Heuristik:
	 * Einzelfälle lassen sich im JSON-Export nachträglich anpassen. */
	function huebscherName( roh ) {
		var fuellwoerter = [ 'am', 'an', 'im', 'in', 'bei', 'ob', 'auf',
			'unter', 'der', 'die', 'das', 'und', 'zu' ];
		var text = String( roh || '' ).toLowerCase()
			.replace( /(^|[^aeiouq])ae/g, '$1ä' )
			.replace( /(^|[^aeiouq])oe/g, '$1ö' )
			.replace( /(^|[^aeiouq])ue/g, '$1ü' )
			.replace( /(^|[\s\/\-.(])([a-zäöü])/g, function ( ganz, davor, buchstabe ) {
				return davor + buchstabe.toUpperCase();
			} );
		var teile = text.split( ' ' );
		var i;
		for ( i = 1; i < teile.length; i++ ) {
			if ( $.inArray( teile[ i ].toLowerCase(), fuellwoerter ) !== -1 ) {
				teile[ i ] = teile[ i ].toLowerCase();
			}
		}
		return teile.join( ' ' );
	}

	function parseKoordinate( text ) {
		var bereinigt = String( text || '' )
			.replace( /,/g, '.' )
			.replace( /[^0-9.\-]/g, '' );
		var wert = parseFloat( bereinigt );
		return isFinite( wert ) ? wert : null;
	}

	function holeZahl( objekt, namen ) {
		var i, wert;
		for ( i = 0; i < namen.length; i++ ) {
			wert = parseFloat( objekt[ namen[ i ] ] );
			if ( isFinite( wert ) ) {
				return wert;
			}
		}
		return null;
	}

	/* Luftlinie in Kilometern (Haversine-Formel) */
	function entfernungKm( lat1, lon1, lat2, lon2 ) {
		var rad = Math.PI / 180;
		var dLat = ( lat2 - lat1 ) * rad;
		var dLon = ( lon2 - lon1 ) * rad;
		var a = Math.sin( dLat / 2 ) * Math.sin( dLat / 2 ) +
			Math.cos( lat1 * rad ) * Math.cos( lat2 * rad ) *
			Math.sin( dLon / 2 ) * Math.sin( dLon / 2 );
		return 6371 * 2 * Math.atan2( Math.sqrt( a ), Math.sqrt( 1 - a ) );
	}

	function imBereichOesterreich( lat, lon ) {
		return lat >= 46.3 && lat <= 49.1 && lon >= 9.4 && lon <= 17.3;
	}

	function formatKm( km ) {
		return km.toFixed( 1 ).replace( '.', ',' ) + ' km';
	}

	function setzeStatus( text, istFehler ) {
		el.status
			.text( text )
			.css( 'color', istFehler ? '#b32424' : '#202122' );
	}

	function setzeExportStatus( text, istFehler ) {
		el.exportStatus
			.text( text )
			.css( 'color', istFehler ? '#b32424' : '#202122' );
	}
}() );