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
$.when( mw.loader.using( 'mediawiki.util' ), $.ready ).then( function () { 	let dialog = null;  	function getTypes() { 		return new Promise( function ( resolve, reject ) { 			Promise.all( [ 				getTypesFromWikivoyage(), 				getGroupsFromWikivoyage() 			] ).then( function ( [ types, groups ] ) { 				resolve( Object.keys( types ).sort( function ( a, b ) { 					return types[ a ].label.localeCompare( types[ b ].label ); 				} ).map( function ( typeName ) { 					const type = types[ typeName ]; 					return { 						color: groups[ type.group ].color || null, 						data: typeName, 						group: type.group, 						groupLabel: groups[ type.group ].label || null, 						label: type.label || typeName 					}; 				} ) ); 			} ).catch( function ( e ) { 				reject( e ); 			} ); 		} ); 	}  	function getGroupsFromWikivoyage() { 		return new Promise( function ( resolve, reject ) { 			$.ajax( { 				data: { 					title: 'Modul:Marker utilities/Groups', 					action: 'raw', 					ctype: 'text/plain' 				}, 				method: 'GET', 				url: mw.util.wikiScript( '' ) 			} ).done( function ( e ) { 				// FIXME: Use same approach as subtype/type 				const lineRegexp = /[ \t]+(([a-zA-Z0-9 _]+?)|'([a-zA-Z0-9 _]+)'|"([a-zA-Z0-9 _]+)"|\['([a-zA-Z0-9 _]+)'\]|\["([a-zA-Z0-9 _]+)"\]) *= *{(.+?)}/g; 				let match; 				const jsonLines = []; 				do { 					match = lineRegexp.exec( e ); 					if ( match ) { 						const parameterName = match[ 2 ] || match[ 3 ] || match[ 4 ] || match[ 5 ] || match[ 6 ]; 						let definition = match[ 7 ]; 						definition = definition.replace( /(^|,) ([^ ]+) = /g, '$1"$2":' ); 						jsonLines.push( `"${parameterName.replace( /_/g, ' ' )}": {${definition}}` ); 					} 				} while ( match ); 				resolve( JSON.parse( `{${jsonLines.join( ',' )}}` ) ); 			} ).fail( function ( e ) { 				reject( e ); 			} ); 		} ); 	}  	function getTypesFromWikivoyage() { 		return new Promise( function ( resolve, reject ) { 			$.ajax( { 				data: { 					title: 'Modul:Marker utilities/Types', 					action: 'raw', 					ctype: 'text/plain' 				}, 				method: 'GET', 				url: mw.util.wikiScript( '' ) 			} ).done( 				/** 				 * @param {string} e 				 */ 				function ( e ) { 					const lines = e.split( '\n' ); 					const declarationRegex = /(?:\["(?<nameBracketed>[^"]+)"\]|(?<nameNonBracketed>[\w]+))[\s\t]*=[\s\t]*\{(?<body>[^\n]+)\}/; // Extracts type name and "body" of declaration. We're assuming that there's at most one declaration per line and that each declaration takes up exactly one line 					const groupRegex = /group[\s\t]*=[\s\t]*"(?<group>[^"]+)"/; // Extracts group, we're using several regexes to prevent requiring a specific order 					const labelRegex = /label[\s\t]*=[\s\t]*"(?<label>[^"]+)"/; // Extracts label 					/** 					 * @type {Object.<string, { group: string, label: string }>} 					 */ 					const groups = {}; 					lines.forEach( function ( line ) { 						const declarationEval = declarationRegex.exec( line ); 						if ( declarationEval ) { 							const name = declarationEval.groups.nameBracketed || declarationEval.groups.nameNonBracketed; 							const body = declarationEval.groups.body; 							// Group 							const groupEval = groupRegex.exec( body ); 							const group = groupEval ? groupEval.groups.group : null; 							const labelEval = labelRegex.exec( body ); 							const label = labelEval ? labelEval.groups.label : null; 							if ( group !== null && label !== null ) { 								groups[ name ] = { 									group: group, 									label: label 								}; 							} 						} 					} ); 					resolve( groups ); 				} ).fail( function ( e ) { 				reject( e ); 			} ); 		} ); 	}  	function getTypesTable() { 		return new Promise( function ( resolve, reject ) { 			getTypes().then( function ( types ) { 				let out = `<table> 					<thead> 						<th>Typ</th> 						<th>Bezeichnung</th> 						<th>Gruppe</th> 						<th>Gruppenbezeichnung</th> 					</thead>`; 				console.log( types ); 				$( types ).each( function ( _i, type ) { 					out += ` 						<tr class="vcata-item" style="${type.color !== null ? `background-color:${type.color};color:#fff` : ''}"> 							<td class="vcata-item-type"><input class="vcata-pseudo-input" type="text" value="${type.data.replace( /_/g, ' ' )}" readonly /></td> 							<td class="vcata-item-label">${type.label}</td> 							<td class="vcata-item-group">${type.group || '&mdash;'}</td> 							<td class="vcata-item-group-label">${type.groupLabel || '&mdash;'}</td> 						</tr>`; 				} ); 				out += '</table>'; 				resolve( out ); 			} ).catch( function ( e ) { 				reject( e ); 			} ); 		} ); 	}  	function search( q ) { 		q = q.toLowerCase(); 		if ( q === '' ) { 			dialog.getContent().find( '.vcata-item' ).each( function ( i, row ) { 				$( row ).show(); 			} ); 		} else { 			dialog.getContent().find( '.vcata-item' ).each( function ( i, row ) { 				const $i = $( row ); 				if ( $i.find( '.vcata-item-type input' ).val().toLowerCase().includes( q ) || $i.find( '.vcata-item-label' ).text().toLowerCase().includes( q ) || $i.find( '.vcata-item-group' ).text().toLowerCase().includes( q ) || $i.find( '.vcata-item-group-label' ).text().toLowerCase().includes( q ) ) { 					$i.show(); 				} else { 					$i.hide(); 				} 			} ); 		} 	}  	function setup() { 		getTypesTable().then( function ( typesTable ) { 			mw.util.addCSS(`.vcata-abbr { 					color: #006699; 				} 				.vcata-pseudo-input { 					background: transparent; 					border: none; 					color: inherit; 					font: inherit; 					padding: unset; 					width: auto; 				} 				#dialog-vcata table { 					border-collapse: collapse; 					min-width: 100%; 				} 				#dialog-vcata td, #dialog-vcata th { 					border-left: 1px solid #888; 					border-right: 1px solid #888; 					padding: .2em .1em; 				} 				#dialog-vcata th { 					border-bottom: 1px solid #888; 				} 				#dialog-vcata .nw520-dialog-inner { 					height: 45vh; 					width: 50vw; 				} 			` ); 			dialog = new nw520.CornerDialog( 'vcata', `<input placeholder="Suche" type="text" style="width:100%" /> 				<div style="margin-top:1em"> 					${typesTable} 				</div>`, '<span class="vcata-abbr">vC</span>ard-<span class="vcata-abbr">T</span>ypen-<span class="vcata-abbr">A</span>ssistent', 'se', true ); 			dialog.getContent().find( 'input[placeholder=Suche][type=text]' ).focus(); 			dialog.getContent().find( 'input[placeholder=Suche][type=text]' ).keyup( function ( e ) { 				e.preventDefault(); 				search( $( this ).val() ); 			} ); 			dialog.getContent().find( '.vcata-pseudo-input' ).click( function ( e ) { 				e.preventDefault(); 				$( this ).trigger( 'select' ); 				try { 					document.execCommand( 'copy' ); 					mw.notify( $( '<span>Typ in Zwischenablage kopiert. Einfügen mit <kbd>STRG</kbd>+<kbd>V</kbd></span>' ), { 						tag: 'vcata' 					} ); 				} catch ( err ) { 					mw.notify( $( '<span>Fehler beim Kopieren in die Zwischenablage. Bitte <kbd>STRG</kbd>+<kbd>C</kbd> drücken</span>' ), { 						tag: 'vcata' 					} ); 				} 			} ); 		} ).catch( function ( e ) { 			console.warn( e ); 			mw.notify( 'Fehler beim Laden von vCaTA', { 				tag: 'vcata', 				title: 'vCaTA', 				type: 'error' 			} ); 		} ); 	}  	function addPortlet() { 		function main() { 			if ( dialog === null || !dialog.isAttached() ) { 				mw.notify( 'vCaTA wird vorbereitet. Augenblick bitte...', { 					tag: 'vcata', 					title: 'vCaTA' 				} ); 				setup(); 			} else { 				dialog.kill(); 			} 		} 		const portlet = mw.util.addPortletLink( 'p-tb', '#vcata', 'vCard-Typen', 'p-vcata', 'vCard-Typen-Assistenten aufrufen' ); 		$( portlet ).on( 'click', function ( e ) { 			e.preventDefault(); 			if ( window.nw520?.CornerDialog ?? null === null ) { 				mw.loader.getScript( '//de.wikivoyage.org/w/index.php?title=User:Nw520/Util.js&action=raw&ctype=text/javascript' ).then( function () { 					main(); 				}, function ( e ) { 					mw.log.error( e.message ); 				} ); 			} else { 				main(); 			} 		} ); 	} 	if ( mw.config.get( 'wgNamespaceNumber' ) === 0 ) { 		addPortlet(); 	} } );