Nota: Después de publicar, quizás necesite actualizar la caché de su navegador para ver los cambios.
- Firefox/Safari: Mantenga presionada la tecla Shift mientras pulsa el botón Actualizar, o presiona Ctrl+F5 o Ctrl+R (⌘+R en Mac)
- Google Chrome: presione Ctrl+Shift+R (⌘+Shift+R en Mac)
- Edge: mantenga presionada Ctrl mientras pulsa Actualizar, o presione Ctrl+F5
//<nowiki> /*************************************************************************** * MarkerTooltip.js v1.5, 2024-07-28 * Displays an extended marker tooltip on mouse over on desktops * or on click on smartphones * Displays tooltip for abbreviations on smartphones * Original author: Roland Unger * Support of both desktop and mobile views * Documentation: https://de.wikivoyage.org/wiki/Wikivoyage:Gadget-MarkerTooltip.js * License: GPL-2.0+, CC-by-sa 3.0 ***************************************************************************/ /* eslint-disable mediawiki/class-doc */ ( function ( $ ) { 'use strict'; var mkTooltip = function() { const strings = { de: { hint: 'Click auf den Marker öffnet die Karte direkt.', hint2: 'Benutzen Sie zur Anzeige die Kartendienste.', ch1903: 'CH1903', ch1903Title: 'Es folgt die Koordinate in der Form der Schweizer Landeskoordinaten.', dec: 'Dezimal', decTitle: 'Es folgt die Koordinate in Dezimalform. Über den nebenstehenden Geo-URI-Link kann eine Karten-Anwendung gestartet werden.', geoUriTitle: 'Über diesen Link startet der Browser eine Karten-Anwendung, z. B. Google Maps. Auf vielen Smartphones bereits eingerichtet.', hex: 'GMS', hexTitle: 'Es folgt die Koordinate in der Form Grad-Minuten-Sekunden.', plus: 'Plus Code', plusTitle: 'Es folgt die Koordinate als Plus Code.', anchor: 'Anker', anchorTitle: 'Zeigt den/die Namen des/der Vorlagen-Anker(s) an.', anchorText: 'Der/die Name(n) des/der Vorlagen-Anker(s) lauten:\n\n', clipboard: 'Ablage', clipboardTitle:'Kopiert die nebenstehende Angabe in die Zwischenablage. Insbesondere ältere Browser unterstützten diese Funktion leider nicht.', mapSources: 'Kartendienste', mapSourcesTitle: 'Es folgen verschiedene Listen mit Kartenquellen und -diensten', tools: 'Werkzeuge', toolsTitle: 'Es folgen verschiedene Vorlagen-Werkzeuge', voy: 'Wikivoyage', voyTitle: 'Öffnet eine Wikivoyage-eigene Internetseite, die zahlreiche Kartenquellen und -dienste auflistet.', voyURL: '/w/index.php?title=Special%3AMapsources', wmflabs: 'WMF-Labs', wmflabsTitle: 'Öffnet eine Internetseite von WMF Labs, die zahlreiche Kartenquellen und -dienste auflistet.', wmflabsURL: 'https://tools.wmflabs.org/geohack/geohack.php?', EW: 'OW', // international: 'EW' NS: 'NS' }, en: { hint: 'Clicking on the marker directly opens the map.', hint2: 'Use the map tools for display.', ch1903: 'CH1903', ch1903Title: 'Coordinates are shown in the form of the Swiss national coordinates.', dec: 'Decimal', decTitle: 'Coordinates are shown as decimal values. A map application can be started via the adjacent Geo-URI link.', geoUriTitle: 'The browser starts a map application using this link, e. g. Google Maps. Already set up on many smartphones.', hex: 'DMS', hexTitle: 'Coordinates are shown as degree-minutes-seconds.', plus: 'Plus Code', plusTitle: 'Coordinates are shown as Plus Code.', anchor: 'Anchor', anchorTitle: 'Shows the name(s) of the template anchor(s).', anchorText: 'The name(s) of the template anchor(s) are:\n\n', clipboard: 'Clipboard', clipboardTitle:'Copies the adjacent information to the clipboard. Unfortunately, older browsers do not support this feature.', mapSources: 'Map tools', mapSourcesTitle: 'The following lists with map sources and services are available', tools: 'Tools', toolsTitle: 'The following tools are available', voy: 'Wikivoyage', voyTitle: 'Opens Wikivoyage’s own webpage which lists numerous map sources and services.', voyURL: '/w/index.php?title=Special%3AMapsources', wmflabs: 'WMF Labs', wmflabsTitle: 'Opens a WMF Labs webpage which lists numerous map sources and services.', wmflabsURL: 'https://tools.wmflabs.org/geohack/geohack.php?', EW: 'EW', NS: 'NS' }, es: { hint: 'Haga clic aquí para abrir el mapa.', hint2: 'Utilice los servicios de mapas para visualizar.', ch1903: 'CH1903', ch1903Title: 'A esto le sigue la coordenada en forma de coordenadas nacionales suizas.', dec: 'Decimal', decTitle: 'La coordenada sigue en forma decimal. Se puede iniciar una aplicación de mapas a través del enlace Geo-URI adyacente.', geoUriTitle: 'El navegador inicia una aplicación de mapas a través de este enlace, p. ej. mapas de Google. Ya está configurado en muchos teléfonos inteligentes Android.', hex: 'GMS', hexTitle: 'La coordenada sigue en forma de grados-minutos-segundos.', plus: 'Plus Code', plusTitle: 'La coordenada sigue como un código de ubicación abierto.', anchor: 'Ancla', anchorTitle: 'Muestra el nombre del ancla(s) de la plantilla.', anchorText: 'El nombre del ancla de la plantilla es:\n\n', clipboard: 'Portapapeles', clipboardTitle:'Copia la información adyacente al portapapeles. Lamentablemente, los navegadores más antiguos no admiten esta función.', mapSources: 'Instrumentos de mapas', mapSourcesTitle: 'Las siguientes listas con fuentes y instrumentos de mapas están disponibles.', tools: 'Instrumentos', toolsTitle: 'Los siguientes instrumentos están disponibles.', voy: 'Wikiviajes', voyTitle: 'Abre un sitio web de Wikiviajes que enumera numerosas fuentes y instrumentos de mapas.', voyURL: '/w/index.php?title=Special%3AMapsources', wmflabs: 'WMF Labs', wmflabsTitle: 'Abre un sitio web de WMF Labs que enumera numerosas fuentes y instrumentos de mapas.', wmflabsURL: 'https://tools.wmflabs.org/geohack/geohack.php?', EW: 'EO', // international: 'EW' NS: 'NS' } }; const fallbackLang = 'en', maxZoomLevel = 19; // see also getScaleFromZoom const options = { plusCode: false, ch1903: true }; const classes = { copyMarker: 'voy-copy-marker', listingTooltip: 'listing-tooltip', listingTooltipMobile: 'listing-tooltip-mobile', withoutMarker: 'voy-without-marker' }; const selectors = { kartographerLink: '.mw-kartographer-maplink', latitude: '.p-latitude', longitude: '.p-longitude', lCoordinates: '.listing-coordinates', lEditButton: '.listing-edit-button button', lInfoButton: '.listing-info-button button', lMap: '.listing-map, .listing-without-marker', lName: '.listing-name', vcard: '.vcard' }; const data = { color: 'data-color', id: 'data-id', lat: 'data-lat', lon: 'data-lon', mAttribute: 'data-copy-marker-attribute', mContent: 'data-copy-marker-content', name: 'data-name', region: 'data-region', wikilang: 'data-wikilang', zoom: 'data-zoom' }; // internal use const pageLang = mw.config.get( 'wgPageContentLanguage' ), userLang = mw.config.get( 'wgUserLanguage' ), // isMobile = window.matchMedia( '(any-pointer: coarse)' ).matches && // has touch screen or similar // !window.matchMedia( '(any-pointer: fine)' ).matches, // has mouse isMobile = ( /android|webos|iphone|ipad|ipod|blackberry|iemobile|opera mini/i.test( navigator.userAgent.toLowerCase() ) ), timeouts = []; var messages = {}; function addMessages( str, chain ) { for ( var i = chain.length - 1; i >= 0; i-- ) { if ( str.hasOwnProperty( chain[ i ] ) ) { $.extend( messages, strings[ chain[ i ] ] ); } } } function setupMessages() { const chain = ( userLang == pageLang ) ? [ pageLang, fallbackLang ] : [ userLang, pageLang, fallbackLang ]; addMessages( strings, chain ); } // Only n digits function round( coord, n ) { const m = Math.pow( 10, n ); return Math.round( coord * m ) / m; } // Converting decimal to DMS coordinates function toDMS( dec, letters ) { const letter = letters.charAt( ( dec >= 0 ) ? 0 : 1 ); const angle = Math.abs( dec ); var deg = Math.floor( angle ); var min = ( angle - deg ) * 60; var sec = Math.round( ( min - Math.floor( min ) ) * 60 ); min = Math.floor( min ); if ( sec >= 60 ) { sec -= 60; min += 1; } if ( min >= 60 ) { min -= 60; deg += 1; } return deg + '° ' + min + '′ ' + sec + '″ ' + letter; } // Converting decimal to CH1903 coordinates // see: https://de.wikipedia.org/wiki/Schweizer_Landeskoordinaten function toCH1903( lat, lon ) { const ch1903 = { easting: 0, northing: 0, error: true }; if ( lat < 45.5 || lat > 48 || lon < 5.0 || lon > 11 ) { return ch1903; } const phi = ( lat * 3600 - 169028.66 ) / 10000; const phi2 = phi * phi; const lambda = ( lon * 3600 - 26782.5 ) / 10000; const lambda2 = lambda * lambda; ch1903.northing = Math.round( 200147.07 + 308807.95 * phi + 3745.25 * lambda2 + 76.63 * phi2 - 194.56 * lambda2 * phi + 119.79 * phi2 * phi ); ch1903.easting = Math.round( 600072.37 + 211455.93 * lambda - 10938.51 * lambda * phi - 0.36 * lambda * phi2 - 44.54 * lambda2 * lambda ); ch1903.error = false; return ch1903; } // Converting decimal to Open Location Code (Plus Code) // see: https://en.wikipedia.org/wiki/Open_Location_Code function toPlusCode( lat, lon ) { const codeChars = '23456789CFGHJMPQRVWX'; const resolutions = [ 20.0, 1.0, 0.05, 0.0025, 0.000125 ]; var code = ''; var modLat = lat; modLat = Math.max( -90, modLat ); modLat = Math.min( modLat, 90 - 0.000025 ); // 0.000025 = resolutions[ 4 ] / 5 [rows] modLat += 90; // starting from 0 var modLon = lon; while ( modLon < -180 ) { modLon += 360; } while ( modLon >= 180 ) { modLon -= 360; } modLon += 180; // starting from 0 // first 10 + 1 digits for ( var i = 0; i < 5; i++ ) { const res = resolutions[ i ]; var digit = Math.floor( modLat / res ); modLat -= digit * res; code += codeChars.charAt( digit ); digit = Math.floor( modLon / res ); modLon -= digit * res; code += codeChars.charAt( digit ); if ( i === 3 ) { code += '+'; } } // last digit const row = Math.floor( 5 * modLat / resolutions[ 4 ] ); const col = Math.floor( 4 * modLon / resolutions[ 4 ] ); code += codeChars.charAt( 4 * row + col ); return code; } // zoom level 19 -> 1:1000, 0 -> 500000000 function getScaleFromZoom( zoom ) { const scales = [ 1000, 2000, 4000, 8000, 15000, 35000, 70000, 150000, 250000, 500000, 1000000, 2000000, 4000000, 10000000, 15000000, 35000000, 70000000, 150000000, 250000000, 500000000 ]; if ( zoom >= maxZoomLevel ) { return scales[ 0 ]; } else if ( zoom <= 0 ) { return scales[ scales.length - 1 ]; } return scales[ maxZoomLevel - Math.round( zoom ) ]; } function copyToClipboard( selector, container ) { const clipboard = $( '<textarea id="mkClipboard"></textarea>' ) .css( { 'width': 1, 'border': 'none', 'opacity': 0 } ); $( 'body' ).append( clipboard ); const text = ( selector !== '.mkClip5' ) ? $( selector, container ).text() : $( '#anchorId', container ).text(); clipboard.val( text ).select(); document.execCommand( 'copy' ); clipboard.remove(); } function clipboardLink( aClass ) { return mw.format( '[ <a href="javascript:" class="$1" title="$2">$3</a> ]', aClass, messages.clipboardTitle, messages.clipboard ); } function makeTableRow( label, title, clipClass, buttonClass, text ) { return mw.format( '<tr><td><span title="$1">$2:</span> <span class="$3">$4</span></td><td>$5</td></tr>', title, label, clipClass, text, clipboardLink( buttonClass ) ); } function makeContent( $origin ) { var link = $( selectors.kartographerLink, $origin ).first(); var lat, lon, withMarker, zoom; if ( link.length ) { lat = round( link.attr( data.lat ), 6 ); lon = round( link.attr( data.lon ), 6 ); zoom = link.attr( data.zoom ); withMarker = true; } else { link = $origin.closest( selectors.vcard ).find( selectors.lCoordinates ); lat = round( $( selectors.latitude, link ).text(), 6 ); lon = round( $( selectors.longitude, link ).text(), 6 ); zoom = 16; withMarker = false; } const latStr = toDMS( lat, messages.NS ); const lonStr = toDMS( lon, messages.EW ); const wrapper = $origin.closest( selectors.vcard ); const color = wrapper.attr( data.color ); const lang = wrapper.attr( data.wikilang ); var region = wrapper.attr( data.region ); if ( !region ) { region = ''; } var name = wrapper.attr( data.name ); if ( !name ) { name = $( selectors.lName, wrapper ).first(); var wikiLink = $( 'a', name ).first(); name = ( wikiLink.length ) ? wikiLink.text() : name = name.text(); } name = encodeURI( name.replace( /\s/g, '+' ) ).replace( /&/g, '%26' ); const id = $( selectors.lName, wrapper ).attr( 'id' ); const id2 = wrapper.attr( 'id' ); var params = '¶ms='; params += Math.abs( lat ) + ( ( lat < 0 ) ? '_S_' : '_N_' ); params += Math.abs( lon ) + ( ( lon < 0 ) ? '_W' : '_E' ); params += '_scale%3A' + getScaleFromZoom( zoom ) + '_type%3Alandmark_globe%3Aearth'; if ( region !== '' ) { params += '_region%3A' + region; } const ch1903 = toCH1903( lat, lon ); const plusCode = toPlusCode( lat, lon ); var table = '<table>' + makeTableRow( messages.hex, messages.hexTitle, 'mkClip1', 'mkButton1', latStr + ' ' + lonStr ) + makeTableRow( messages.dec, messages.decTitle, 'mkClip2', 'mkButton2', '<a href="geo:' + lat + ',' + lon + '" title="' + messages.geoUriTitle + '">' + lat + ', ' + lon + '</a>' ); if ( options.plusCode ) { table += makeTableRow( messages.plus, messages.plusTitle, 'mkClip3', 'mkButton3', '<span class="voy-mkTooltipPlusCode">' + plusCode.substr( 0, 4 ) + '</span>' + plusCode.substr( 4 ) ); } if ( options.ch1903 && !ch1903.error ) { table += makeTableRow( messages.ch1903, messages.ch1903Title, 'mkClip4', 'mkButton4', '<span title="CH1903 easting">' + ch1903.easting + '</span> / <span title="CH1903 northing">' + ch1903.northing + '</span>' ); } if ( id ) { const html = []; const infobutton = $( selectors.lInfoButton, wrapper ).prop( 'outerHTML' ) || ''; if ( infobutton !== '' ) { html.push( '<span id="infobutton">' + infobutton + '</span>' ); } const editbutton = $( selectors.lEditButton, wrapper ).prop( 'outerHTML' ) || ''; if ( editbutton !== '' ) { html.push( '<span id="editbutton">' + editbutton + '</span>' ); } var anchor = mw.format( '<a href="#" id="anchorIdLink" title="$1">$2</a>', messages.anchorTitle, messages.anchor ) + mw.format( '<span id="anchorId" style="display: none">$1</span>', id ); if ( id2 ) { anchor += mw.format( '<span id="anchorId2" style="display: none">$1</span>', id2 ); } html.push( anchor ); table += makeTableRow( messages.tools, messages.toolsTitle, 'mkClip5', 'mkButton5', html.join( ' | ' ) ); } table += '</table>'; var mapSources = mw.format( '<div title="$1">$2: ', messages.mapSourcesTitle, messages.mapSources ) + mw.format( '<a href="$1&locname=$2" title="$3" target="_blank" rel="noopener">$4</a> | ', messages.voyURL + params, name, messages.voyTitle, messages.voy ) + mw.format( '<a href="$1pagename=$2&language=$3" title="$4" target="_blank" rel="noopener">$5</a>', messages.wmflabsURL, name, lang + params, messages.wmflabsTitle, messages.wmflabs ) + '</div>'; return $( '<div class="voy-mkTooltipInner"></div>' ) .css( 'border-left-color', color ) .append( $( '<div class="voy-mkTooltipHint">' + (withMarker ? messages.hint : messages.hint2 ) + '</div>' ) .css( { 'margin-bottom': '0.5em' } ) ) .append( $( table ) ) .append( $( mapSources ) ) .append( $( '<div class="voy-mkTooltipTail"></div>' ) ); } // setting tooltip position function setTooltipPosition( e, tooltip, $this ) { const tail = $( '.voy-mkTooltipTail', tooltip ); const winWidth = $( window ).width(); var left, offset, right, width; if ( e.clientY < $( window ).height() / 2 ) { tooltip.css( 'top', $this.innerHeight() - 4 ) .addClass('voy-mkBelow'); } else { tooltip.css( 'bottom', $this.innerHeight() - 4 ) .addClass('voy-mkAbove'); } if ( e.clientX < winWidth / 2 ) { tooltip.css( 'left', $this.innerWidth() / 2 - 16 ) .addClass('voy-mkLeft'); if ( isMobile ) { offset = tooltip.offset(); right = offset.left + tooltip.outerWidth(); if ( right > winWidth - 1 ) { left = offset.left - ( right - winWidth ) - 2; if ( left < 2 ) { left = 2; } width = tooltip.innerWidth(); tooltip.offset( { top: offset.top, left: left } ); tooltip.innerWidth( width ); width = offset.left - left; offset = tail.offset(); offset.left += width; tail.offset( offset ); } } } else { tooltip.css( 'right', $this.innerWidth() / 2 - 13 ) .addClass('voy-mkRight'); if ( isMobile ) { offset = tooltip.offset(); left = offset.left; if ( left < 2 ) { width = tooltip.innerWidth(); tooltip.offset( { top: offset.top, left: 2 } ); tooltip.innerWidth( width ); offset = tail.offset(); offset.left += left; tail.offset( offset ); } } } } function showMarkerTooltip( e ) { const $this = $( e.target ).closest( '.' + classes.listingTooltip ), id = $this.attr( data.id ), isCopyMarker = $this.hasClass( classes.copyMarker ), wrapper = $this.closest( selectors.vcard ), withoutMarker = wrapper.hasClass( classes.withoutMarker ), triggerWrapper = withoutMarker || !isCopyMarker; e.stopPropagation(); var $origin = $this; if ( isCopyMarker ) { // getting from original marker const attr = $this.attr( data.mAttribute ); const content = $this.attr( data.mContent ); $origin = $( '*[' + attr + '="' + content + '"]' ).first(); } const tooltip = $( '<div class="voy-mkTooltip" role="tooltip"/>' ) .append( makeContent( $origin ) ); if ( isMobile ) { tooltip.addClass( 'voy-mkTooltipMobile' ); } else { tooltip.hide(); // later fade-in; } $this.append( tooltip ); setTooltipPosition( e, tooltip, $this ); $( '.mkButton1', tooltip ) .click( function() { copyToClipboard( '.mkClip1', tooltip ); } ); $( '.mkButton2', tooltip ) .click( function() { copyToClipboard( '.mkClip2', tooltip ); } ); $( '.mkButton3', tooltip ) .click( function() { copyToClipboard( '.mkClip3', tooltip ); } ); $( '.mkButton4', tooltip ) .click( function() { copyToClipboard( '.mkClip4', tooltip ); } ); $( '.mkButton5', tooltip ) .click( function() { copyToClipboard( '.mkClip5', tooltip ); } ); $( '#anchorIdLink', tooltip ) .click( function() { var alertText = messages.anchorText + $( '#anchorId', tooltip ).text(); const anchor2 = $( '#anchorId2', tooltip ).text(); if ( anchor2 && anchor2 != '' ) { alertText += ', ' + anchor2; } removeAllTooltips(); alert( alertText ); } ); $( '#infobutton', tooltip ) .click( function() { $( selectors.lInfoButton, triggerWrapper ? wrapper : $origin ).trigger( 'click' ); removeAllTooltips(); } ); $( '#editbutton', tooltip ) .click( function() { $( selectors.lEditButton, triggerWrapper ? wrapper : $origin ).trigger( 'click' ); removeAllTooltips(); } ); if ( isMobile ) { // removing tooltip after 10 sec in mobile mode timeouts[ id ] = setTimeout( function() { removeTooltip( $this ); }, 10000 ); $( 'body' ).click( handleOutsideClick ); } else { // fading-in hidden tooltip in desktop mode setTimeout( function() { tooltip.fadeIn( 500 ); }, 300 ); } return tooltip; } // Click event handler if clicked outside any tooltip function handleOutsideClick( event ) { if ( !$( event.target ).closest( '.voy-mkTooltip' ).length && $( '.voy-mkTooltip' ).is( ':visible' ) ) { removeAllTooltips(); } } function removeTooltip( marker ) { const id = marker.attr( data.id ); if ( id ) { clearTimeout( timeouts[ id ] ); } $( '.voy-mkTooltip', marker ).remove(); $( '.voy-mkTooltipButton', marker ).text( '▼' ); } function removeAllTooltips() { if ( isMobile ) { $( 'body' ).off( 'click', handleOutsideClick ); } var markers = $( '.' + classes.listingTooltip ).add( $( 'abbr' ) ); markers.each( function() { removeTooltip( $( this ) ); }); } function showMobileMarker( e ) { const $this = $( e.target ).closest( '.voy-mkTooltipButton' ); const text = $this.text(); removeAllTooltips(); if ( text === '▼' ) { $this.text( '▲' ); showMarkerTooltip( e ); } } function initMarkerTooltip() { $( selectors.lMap ).addClass( classes.listingTooltip ); if ( isMobile ) { $( selectors.lMap ).addClass( classes.listingTooltipMobile ); } const markers = $( '.' + classes.listingTooltip ) .attr( 'title', '' ); var id = 0; // setting id for timeout handler markers.each( function() { $( this ).attr( data.id, 'tt' + id ); id += 1; } ); if ( isMobile ) { const mobileMarker = $( '<span class="voy-mkTooltipButton">▼</span>' ) .click( function( e ) { showMobileMarker( e ); }); markers.append( mobileMarker ); } else { markers.mouseenter( function( e ) { showMarkerTooltip( e ); }) .mouseleave( function( e ) { $( '.voy-mkTooltip' ).remove(); }); } } function initAbbrTooltip() { const abbr = $( 'abbr' ) .css( { 'position': 'relative', 'cursor': 'pointer' } ); var id = 0; // setting id for timeout handler abbr.each( function() { $( this ).attr( data.id, 'at' + id ); id += 1; } ); abbr.click( function( e ) { e.stopPropagation(); const $this = $( e.target ).closest( 'abbr' ); const id = $this.attr( data.id ); var tooltip = $( '.voy-mkTooltip', $this ); removeAllTooltips(); if ( tooltip.length === 0 ) { const title = $this.attr( 'title' ); if ( title ) { const div = $( '<div class="voy-mkTooltipInner voy-mkTooltipMaxWidth">' + title + '</div>' ) .append( $( '<div class="voy-mkTooltipTail"></div>' ) ); tooltip = $( '<div class="voy-mkTooltip voy-mkTooltipMobile" role="tooltip"></div>' ) .append( div ); $this.append( tooltip ); setTooltipPosition( e, tooltip, $this ); timeouts[ id ] = setTimeout( function() { removeTooltip( $this ); }, 10000 ); $( 'body' ).click( handleOutsideClick ); } } } ); } function init() { setupMessages(); initMarkerTooltip(); if ( isMobile ) { initAbbrTooltip(); } } return { init: init }; } (); $( mkTooltip.init ); } ( jQuery ) ); //</nowiki>