Участник:Bok/vector.js
Замечание: Возможно, после публикации вам придётся очистить кэш своего браузера, чтобы увидеть изменения.
- Firefox / Safari: Удерживая клавишу Shift, нажмите на панели инструментов Обновить либо нажмите Ctrl+F5 или Ctrl+R (⌘+R на Mac)
- Google Chrome: Нажмите Ctrl+Shift+R (⌘+Shift+R на Mac)
- Internet Explorer / Edge: Удерживая Ctrl, нажмите Обновить либо нажмите Ctrl+F5
- Opera: Нажмите Ctrl+F5.
//<nowiki>
/*********************************************************************
* poi2gpx v1.4, 2022-06-02
* Download of article’s points of interest and tracks to a GPX file
* Original author: Roland Unger
* adaptation: Bok
* Support of desktop and mobile views
* Documentation: https://de.wikivoyage.org/wiki/Wikivoyage:Gadget-Poi2gpx.js
* License: GPL-2.0+, CC-by-sa 3.0
*********************************************************************/
( function ( $, mw ) {
'use strict';
var poi2gpx = function() {
var imgSrc = 'https://upload.wikimedia.org/wikivoyage/de/thumb/f/f0/WV-poi2gpx.svg/50px-WV-poi2gpx.svg.png';
var imgSrcMinerva = 'https://upload.wikimedia.org/wikivoyage/de/thumb/6/63/WV-poi2gpx-black.svg/50px-WV-poi2gpx-black.svg.png';
// image for download link
var download = 'Download der Kartenpositionen als GPX-Datei'; // tooltip
var mainDesc = 'Kartenpositionen aus dem deutschen Wikivoyage-Artikel'; // GPX description
var gpxLabel = 'GPX';
var containerClass = 'vcard'; // contains wrapper markup of a single marker or listing
var noGpxClass = 'listing-no-gpx';
var kartographerClass = 'mw-kartographer-maplink';
var nameClass = 'listing-name';
var contentClass = 'listing-content';
var dataName = 'data-name';
var dataColor = 'data-color';
var dataType = 'data-group'; // other wikis: 'data-type'
var dataUrl = 'data-url';
var dataPhone = 'data-phone';
var comments = [ 'listing-hours', 'listing-checkin', 'listing-checkout', 'listing-price', 'listing-credit' ];
var gpxFile = null; // check for URL object
var trackdata = null;
var translations = {
area: 'Gebiet',
buy: 'Покупки',
'do': 'Чем заняться',
drink: 'Ночная жизнь',
eat: 'Еда',
error: 'Неизвестно',
go: 'Как добраться',
other: 'Разное',
populated: 'Besiedelt',
see: 'Достопримечательности',
sleep: 'Где остановиться',
track: 'Track', // group name of tracks, intl. version: 'track'
view: 'Aussicht',
cosmos: 'Cosmos',
gold: 'Gold',
lime: 'Hellgrün',
mediumaquamarine: 'Aquamarinblau',
orange: 'Orange',
plum: 'Pflaumenblau',
purple: 'Violett',
red: 'Rot',
silver: 'Silber'
};
var makeFile = function( text ) { // modern Browsers
var data = new Blob( [text], { type: 'application/gpx+xml' } );
if ( gpxFile !== null ) window.URL.revokeObjectURL( gpxFile );
gpxFile = window.URL.createObjectURL( data );
return gpxFile;
};
var ieSaveFile = function( text, fileName ) { // IE 11
var data = new Blob( [text], { type: 'application/gpx+xml' } );
window.navigator.msSaveOrOpenBlob( data, fileName );
};
var replace = function( text ) { // to use text in XML tags
return text.replace( /\&/g, '&' )
.replace( /"/g, '"' )
.replace( /</g, '<' )
.replace( />/g, '>' );
};
var getPhone = function( selector, $this ) {
var r = '';
var v = $( selector, $this ).first();
if ( v.length !== 0 ) {
v = v.attr( dataPhone );
if ( v !== undefined ) r = v;
}
return r;
};
// Getting GeoJSON data sets from external sources (OSM, Commons)
var getGeoJSON = function( obj ) {
var promise, coordinates, geometry, i;
var properties = obj.properties; // for all but not for 'page'
promise = $.getJSON( obj.url ).then( function( geoJSON ) {
switch ( obj.service ) {
case 'page': // from Commons
if ( geoJSON.jsondata && geoJSON.jsondata.data )
$.extend( obj, geoJSON.jsondata.data );
break;
case 'geoline': // from OSM
case 'geoshape':
$.extend( obj, geoJSON );
if ( properties ) {
for ( i = 0; i < obj.features.length; i++ )
if ( $.isEmptyObject( obj.features[ i ].properties ) )
obj.features[ i ].properties = properties;
else
obj.features[ i ].properties =
$.extend( {}, properties, obj.features[ i ].properties );
}
}
}, function() {
// failed, no tracks will be added
} );
return promise;
};
// getting Kartographer live data
var getKartographerLiveData = function() {
var i, obj;
var promiseArray = [];
trackdata = mw.config.get( 'wgKartographerLiveData' );
if ( trackdata ) {
trackdata = trackdata[ translations.track ];
if ( trackdata && trackdata.length === 0 )
trackdata = null;
if ( trackdata )
for ( i = 0; i < trackdata.length; i++ ) {
obj = trackdata[ i ];
if ( obj.type === 'ExternalData' && obj.url )
promiseArray.push( getGeoJSON( obj ) );
}
}
// wait for getting all external data
var isIE11 = !!window.MSInputMethodContext && !!document.documentMode;
if ( isIE11 )
$.when.apply( $, promiseArray ).then( function() {
createFile();
} );
else
if ( typeof Promise !== 'undefined' )
Promise.all( promiseArray )
.then( function() { createFile(); } )
.catch( function() { createFile(); } );
// create file also in case of failures
else
createFile();
return;
};
var getString = function( prop ) {
if ( !prop ) return '';
if ( typeof( prop ) == 'string' ) return prop;
var wikiLang = mw.config.get( 'wgPageContentLanguage' );
if ( prop[ wikiLang ] ) return prop[ wikiLang ];
if ( prop.en ) return prop.en;
for ( var i in prop ) { return prop[ i ] }
return '';
};
var removeTags = function( s ) {
return $( '<div>' + s + '</div>' ).text();
};
var writeTrack = function( coordinates, tracks, type, properties ) {
var j, k, s, coords;
if ( !coordinates || coordinates.length === 0 ) return tracks;
tracks += '\n <trk>\n';
if ( properties ) {
s = replace( removeTags( getString( properties.title ) ) );
if ( s !== '' ) tracks += ' <name>' + s + '</name>\n';
s = replace( removeTags( getString( properties.desc ) ) );
if ( s !== '' ) tracks += ' <desc>' + s + '</desc>\n';
if ( properties.stroke ) {
tracks += ' <extensions>\n' +
' <gpxx:WaypointExtension xmlns:gpxx="http://www.garmin.com/xmlschemas/GpxExtensions/v3">\n' +
' <gpxx:DisplayColor>' + properties.stroke + '</gpxx:DisplayColor>\n' +
' </gpxx:WaypointExtension>\n' +
' </extensions>\n';
}
}
for ( j = 0; j < coordinates.length; j++ ) {
coords = coordinates[ j ];
if ( type === 'MultiPolygon' ) coords = coordinates[ j ][ 0 ];
// only first polygon
// tests not yet completed
if ( coords.length > 0 ) {
tracks += ' <trkseg>\n';
for ( k = 0; k < coords.length; k++ ) {
tracks += ' <trkpt lat="' + coords[ k ][ 1 ]
+ '" lon="' + coords[ k ][ 0 ] + '" />\n';
}
tracks += ' </trkseg>\n';
}
}
return tracks + ' </trk>\n';
};
var writeTracks = function() {
var tracks = '';
if ( !trackdata || trackdata.length === 0 ) return '';
var i, geoJSON, geometry, coordinates, properties;
for ( i = 0; i < trackdata.length; i++ ) {
if ( trackdata[ i ].features ) {
geoJSON = trackdata[ i ].features[ 0 ];
geometry = geoJSON.type === 'Feature' ? geoJSON.geometry : geoJSON;
coordinates = geometry ? geometry.coordinates : null;
properties = geoJSON.properties;
switch ( geometry.type ) {
case 'LineString':
tracks = writeTrack( [ coordinates ], tracks, 'MultiLineString', properties );
break;
case 'MultiLineString':
tracks = writeTrack( coordinates, tracks, 'MultiLineString', properties );
break;
case 'Polygon':
tracks = writeTrack( coordinates, tracks, 'MultiLineString', properties );
break;
case 'MultiPolygon':
tracks = writeTrack( coordinates, tracks, 'MultiPolygon', properties );
}
}
}
return tracks;
};
var getText = function() { // generate GPX output
var markers = $( '.' + containerClass ).not( '.' + noGpxClass );
if ( markers.length === 0 ) return '';
var link, lat, lon, count, i, name, aType, translType, color,
desc, cmt, v, gpxx;
var text = '';
var minlat = null, minlon = null, maxlat = null, maxlon = null; // for bounds
markers.each( function() {
var $this = $( this );
link = $( '.' + kartographerClass, $this ).first();
if ( link.length > 0 ) {
lat = link.attr( 'data-lat' );
lon = link.attr( 'data-lon' );
if ( minlat === null ) minlat = lat;
if ( minlon === null ) minlon = lon;
if ( maxlat === null ) maxlat = lat;
if ( maxlon === null ) maxlon = lon;
if ( lat < minlat ) minlat = lat;
if ( lat > maxlat ) maxlat = lat;
if ( lon < minlon ) minlon = lon;
if ( lon > maxlon ) maxlon = lon;
color = $this.attr( dataColor );
if ( color === undefined ) {
color = link.attr("style").split(";")[0].split(":")[1].trim();
}
aType = $this.attr( dataType );
translType = aType;
if ( aType in translations && translations[aType] !== '' )
translType = translations[aType];
count = link.html();
if ( count.indexOf('<') >= 0 ) count= ''; // no number but html tag
else count = ' ' + ('0' + count).slice(-2);
name = $this.attr( dataName );
if ( name === undefined ) {
name = $( '.' + nameClass, $this ).first().text(); // исправлено в русской версии
}
name = replace( removeTags( name ) );
desc = $( '.' + contentClass, $this ).first();
if ( desc.length === 0 ) desc = '';
else desc = replace( desc.text() );
cmt = '';
for ( i = 0; i < comments.length; i++ ) {
v = $( '.' + comments[ i ], $this ).first();
if ( v.length !== 0 ) {
if ( cmt !== '' ) cmt += ' ';
cmt += replace( v.text() );
}
}
gpxx = '';
v = $( '.listing-address', $this ).first();
if ( v.length !== 0 ) {
gpxx += ' <gpxx:Address>\n' +
' <gpxx:StreetAddress>' + replace( v.text() ) + '</gpxx:StreetAddress>\n' +
' </gpxx:Address>\n';
}
v = getPhone( '.listing-landline .listing-phone-number', $this );
if ( v !== '' )
gpxx += ' <gpxx:PhoneNumber Category="Phone">' + v + '</gpxx:PhoneNumber>\n';
v = getPhone( '.listing-tollfree .listing-phone-number', $this );
if ( v !== '' )
gpxx += ' <gpxx:PhoneNumber Category="Tollfree">' + v + '</gpxx:PhoneNumber>\n';
v = getPhone( '.listing-mobile .listing-phone-number', $this );
if ( v !== '' )
gpxx += ' <gpxx:PhoneNumber Category="Mobile">' + v + '</gpxx:PhoneNumber>\n';
v = getPhone( '.listing-fax .listing-phone-number', $this );
if ( v !== '' )
gpxx += ' <gpxx:PhoneNumber Category="Fax">' + v + '</gpxx:PhoneNumber>\n';
v = $( '.listing-email a', $this ).first();
if ( v.length !== 0 ) {
gpxx += ' <gpxx:PhoneNumber Category="Email">' + v.text() + '</gpxx:PhoneNumber>\n';
}
v = $this.attr( dataUrl );
if ( v !== undefined ) {
gpxx += ' <gpxx:PhoneNumber Category="URL">' + replace( v ) + '</gpxx:PhoneNumber>\n';
}
text += ' <wpt lat="' + lat + '" lon="' + lon + '">\n' +
' <name>[' + translType + count + '] ' + name + '</name>\n' +
' <type>' + aType + '</type>\n' +
' <extensions>\n' +
// ' <color>' + color + '</color>\n' +
' <gpxx:WaypointExtension xmlns:gpxx="http://www.garmin.com/xmlschemas/GpxExtensions/v3">\n' +
' <gpxx:DisplayColor>' + color + '</gpxx:DisplayColor>\n' +
' <gpxx:DisplayMode>SymbolAndName</gpxx:DisplayMode>\n' +
gpxx +
' </gpxx:WaypointExtension>\n' +
' </extensions>\n';
if ( desc !== '' ) text += ' <desc>' + desc + '</desc>\n';
if ( cmt !== '' ) text += ' <cmt>' + cmt + '</cmt>\n';
text += ' </wpt>\n';
}
});
var tracks = writeTracks();
text += tracks;
if ( text === '' ) return '';
return '<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>\n' +
'<gpx version="1.1" creator="Wikivoyage"\n' +
' xmlns="http://www.topografix.com/GPX/1/1"\n' +
' xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"\n' +
' xmlns:gpxx="http://www.garmin.com/xmlschemas/GpxExtensions/v3"\n' +
' xsi:schemaLocation="http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd\n' +
' http://www.garmin.com/xmlschemas/GpxExtensions/v3 http://www8.garmin.com/xmlschemas/GpxExtensions/v3/GpxExtensionsv3.xsd">\n\n' +
' <metadata>\n' +
' <name>' + replace( mw.config.get( 'wgTitle' ) ) + '</name>\n' +
' <desc>' + mainDesc + '</desc>\n' +
' <author>\n' +
' <name>Wikivoyage</name>\n' +
' </author>\n' +
' <copyright>\n' +
// ' <license>https://creativecommons.org/publicdomain/zero/1.0/</license>\n' +
' <license>https://creativecommons.org/licenses/by-sa/3.0/</license>\n' + // desc is CC-by-sa 3.0
' </copyright>\n' +
' <bounds minlat="'+ minlat + '" maxlat="' + maxlat + '" minlon="' + minlon +'" maxlon="' + maxlon + '"></bounds>\n' +
' </metadata>\n\n' +
text +
'</gpx>';
};
var allowedForCurrentPage = function() {
var namespace = mw.config.get( 'wgNamespaceNumber' );
if (namespace !== 0 && namespace !== 2 && namespace !== 4) {
return false;
}
return mw.config.get('wgAction') == 'view';
};
var createFile = function() {
if ( typeof Blob === "undefined" ) return; // very old browsers
if ( !allowedForCurrentPage() ) return;
var downloadText = getText();
if ( downloadText === '' ) return;
var isMinerva = mw.config.get( 'skin' ) === 'minerva'; // mobile view
var fileName = mw.config.get( 'wgTitle' ) + '_'
+ mw.config.get( 'wgPageContentLanguage' ) + '.gpx';
fileName = fileName.replace( / |:|\/|\\/gi, '_' );
var image = $( '<img>', {
src: isMinerva ? imgSrcMinerva : imgSrc,
width: isMinerva ? '15' : '25',
height: isMinerva ? '20' : '25',
alt: download,
title: download
});
if ( isMinerva ) {
image.css( { 'margin-left': '3px' } ); // = (20-15)/2
}
var indicator = $( '<a>', {
id: 'mw-indicator-i3-gpx',
class: 'mw-indicator',
title: download
} )
.css( { display: 'inline-block' } )
.append( image );
if ( !window.navigator.msSaveOrOpenBlob ) { // modern browsers
indicator.attr( {
href: makeFile( downloadText ),
download: fileName
} );
} else { // ie 11
indicator.click( function() {
ieSaveFile( downloadText, fileName );
} );
}
if ( isMinerva ) { // mobile view
indicator.append( document.createTextNode( ' ' + gpxLabel ) );
indicator = $( '<li></li>', {
id: 'page-actions-poi2gpx',
class: 'page-actions-menu__list-item'
} )
.append( indicator );
$( '#page-actions #page-actions-edit' ).after( indicator );
} else {
var indicators = $( '.mw-indicators' ).first(); // always on desktop
var geoIndicator;
// international version: only:
// indicators.prepend( indicator );
indicators.each( function() {
geoIndicator = $( '#mw-indicator-i3-geo', $(this) );
if ( geoIndicator.length === 0 ) {
$(this).prepend( indicator );
}
else {
geoIndicator.after( indicator );
}
});
}
};
var init = function() {
getKartographerLiveData();
// calls createFile
};
return { init: init };
} ();
$( poi2gpx.init );
} ( jQuery, mediaWiki ) );
//</nowiki>