MediaWiki:Gadget-KartoEditor.js

Note : après avoir enregistré la page, vous devrez forcer le rechargement complet du cache de votre navigateur pour voir les changements.

Mozilla / Firefox / Konqueror / Safari : maintenez la touche Majuscule (Shift) en cliquant sur le bouton Actualiser (Reload) ou pressez Maj-Ctrl-R (Cmd-R sur Apple Mac) ;

Chrome / Internet Explorer / Opera : maintenez la touche Ctrl en cliquant sur le bouton Actualiser ou pressez Ctrl-F5.
/*
* KartoEditor
*
* Permet, depuis la page [[Special:Blankpage/KartoEditor]], d'éditer une carte
* interactive en y ajoutant des marqueurs, lignes ou polygones, puis d'exporter
* cette carte au format GeoJSON sur une page du namespace ''Data:'' sur commons.
*
* Auteur : [[User:0x010C]]
* Création : 30 mars 2017
* Dernière révision : 6 avril 2017
* {{Projet:JavaScript/Script|KartoEditor}}
*/
//<nowiki>

( function () {
	var GeoLayer = function( kartoEditor, type, layer, initialStyle ) {
		this.kartoEditor = kartoEditor;
		this.type = type.toLowerCase();
		this.layer = layer;
		this.GJStyle = {};
		initialStyle = initialStyle || {};
		if ( this.type === 'polygon' || this.type === 'rectangle' ) {
			this.updatePolygon( {
				'stroke': initialStyle[ 'stroke' ] || '#555555',
				'stroke-width': initialStyle[ 'stroke-width' ] || 2,
				'stroke-opacity': initialStyle[ 'stroke-opacity' ] || 1,
				'fill': initialStyle[ 'fill' ] || '#555555',
				'fill-opacity': initialStyle[ 'fill-opacity' ] || 0.3,
				'title': initialStyle[ 'title' ] || '',
				'description': initialStyle[ 'description' ] || '',
			} );
		} else if ( this.type === 'polyline' || this.type === 'linestring' ) {
			this.updateLine( {
				'stroke': initialStyle[ 'stroke' ] || '#555555',
				'stroke-width': initialStyle[ 'stroke-width' ] || 2,
				'stroke-opacity': initialStyle[ 'stroke-opacity' ] || 1,
				'title': initialStyle[ 'title' ] || '',
				'description': initialStyle[ 'description' ] || '',
			} );
		} else {
			this.updateMarker( {
				'marker-size': initialStyle[ 'marker-size' ] || 'small',
				'marker-color': initialStyle[ 'marker-color' ] || '#555555',
				'marker-symbol': initialStyle[ 'marker-symbol' ] || '',
				'title': initialStyle[ 'title' ] || '',
				'description': initialStyle[ 'description' ] || '',
			} );
		}

		this.layer.bindPopup( this.createPopup() );
	};

	GeoLayer.prototype.updatePolygon = function( GJStyle ) {
		this.GJStyle = GJStyle || this.GJStyle;
		this.layer.setStyle( {
			weight: GJStyle[ 'stroke-width' ],
			opacity: GJStyle[ 'stroke-opacity' ],
			color: GJStyle[ 'stroke' ],
			fillOpacity: GJStyle[ 'fill-opacity' ],
			fillColor: GJStyle[ 'fill' ],
		} );
		this.kartoEditor.updateOutputs();
	};
	GeoLayer.prototype.updateLine = function( GJStyle ) {
		this.GJStyle = GJStyle || this.GJStyle;
		this.layer.setStyle( {
			weight: GJStyle[ 'stroke-width' ],
			opacity: GJStyle[ 'stroke-opacity' ],
			color: GJStyle[ 'stroke' ],
		} );
		this.kartoEditor.updateOutputs();
	};
	GeoLayer.prototype.updateMarker = function( GJStyle ) {
		this.GJStyle = GJStyle || this.GJStyle;
		this.layer.setIcon( WMIcon(GJStyle[ 'marker-size' ], GJStyle[ 'marker-color' ].substring( 1 ), GJStyle[ 'marker-symbol' ] ) );
		this.kartoEditor.updateOutputs();
	};

	GeoLayer.prototype.isPresent = function() {
		if ( this.layer._map !== null ) {
			return true;
		}
		return false;
	};

	GeoLayer.prototype.createPopup = function() {
		var popupContent;
		if ( this.type === 'polygon' || this.type === 'rectangle' ) {
			popupContent = this.generatePolygonForm();
		} else if ( this.type === 'polyline' ) {
			popupContent = this.generateLineForm();
		} else {
			popupContent = this.generateMarkerForm();
		}

		return new L.Popup( {
			minWidth: 300,
			maxWidth: 500,
		} ).setContent( popupContent );
	};
	GeoLayer.prototype.generatePolygonForm = function() {
		var self = this;

		var propertyInputs = {
			'stroke': new OO.ui.ColorInputWidget( { value: this.GJStyle[ 'stroke' ] } ),
			'stroke-width': new OO.ui.NumberInputWidget( { input: { value: this.GJStyle[ 'stroke-width' ] }, min: 0, isInteger: true, step: 1 } ),
			'stroke-opacity': new OO.ui.NumberInputWidget( { input: { value: this.GJStyle[ 'stroke-opacity' ] }, min: 0, max: 1, isInteger: false, step: 0.1 } ),
			'fill': new OO.ui.ColorInputWidget( { value: this.GJStyle[ 'fill' ], } ),
			'fill-opacity': new OO.ui.NumberInputWidget( { input: { value: this.GJStyle[ 'fill-opacity' ]}, min: 0, max: 1, isInteger: false, step: 0.1 } ),
			'title': new OO.ui.TextInputWidget(),
			'description': new OO.ui.MultilineTextInputWidget( { autoresize: true } ),
		};

		var strokeFieldset = new OO.ui.FieldsetLayout( { label: 'Stroke' } );
		strokeFieldset.addItems( [
			new OO.ui.FieldLayout(propertyInputs[ 'stroke' ], { label: 'Color', align: 'right' } ),
			new OO.ui.FieldLayout(propertyInputs[ 'stroke-width' ], { label: 'Width', align: 'right' } ),
			new OO.ui.FieldLayout(propertyInputs[ 'stroke-opacity' ], { label: 'Opacity', align: 'right' } ),
		] );

		var backgroundFieldset = new OO.ui.FieldsetLayout( { label: 'Fill' } );
		backgroundFieldset.addItems( [
			new OO.ui.FieldLayout(propertyInputs[ 'fill' ], { label: 'Color', align: 'right' } ),
			new OO.ui.FieldLayout(propertyInputs[ 'fill-opacity' ], { label: 'Opacity', align: 'right' } ),
		] );

		var popupFieldset = new OO.ui.FieldsetLayout( { label: 'Popup' } );
		popupFieldset.addItems( [
			new OO.ui.FieldLayout(propertyInputs[ 'title' ], { label: 'Title', align: 'right' } ),
			new OO.ui.FieldLayout(propertyInputs[ 'description' ], { label: 'Description', align: 'right' } ),
		] );

		var saveButton = new OO.ui.ButtonWidget( { label: 'Save', title: 'Save', flags: [ 'primary', 'progressive' ] } );

		var mainFieldset = new OO.ui.FieldsetLayout( {} );
		mainFieldset.addItems( [
			strokeFieldset,
			backgroundFieldset,
			popupFieldset,
			saveButton,
		] );

		saveButton.on( 'click', function() {
			self.updatePolygon( {
				'stroke': propertyInputs[ 'stroke' ].getValue(),
				'stroke-width': propertyInputs[ 'stroke-width' ].getValue(),
				'stroke-opacity': propertyInputs[ 'stroke-opacity' ].getValue(),
				'fill': propertyInputs[ 'fill' ].getValue(),
				'fill-opacity': propertyInputs[ 'fill-opacity' ].getValue(),
				'title': propertyInputs[ 'title' ].getValue(),
				'description': propertyInputs[ 'description' ].getValue(),
			} );
		} );
		return mainFieldset.$element[ 0 ];
	};
	GeoLayer.prototype.generateLineForm = function() {
		var self = this;

		var propertyInputs = {
			'stroke': new OO.ui.ColorInputWidget( { value: this.GJStyle[ 'stroke' ] } ),
			'stroke-width': new OO.ui.NumberInputWidget( { input: { value: this.GJStyle[ 'stroke-width' ] }, min: 0, isInteger: true, step: 1 } ),
			'stroke-opacity': new OO.ui.NumberInputWidget( { input: { value: this.GJStyle[ 'stroke-opacity' ] }, min: 0, max: 1, isInteger: false, step: 0.1 } ),
			'title': new OO.ui.TextInputWidget(),
			'description': new OO.ui.MultilineTextInputWidget( { autoresize: true } ),
		};

		var strokeFieldset = new OO.ui.FieldsetLayout( { label: 'Stroke' } );
		strokeFieldset.addItems( [
			new OO.ui.FieldLayout(propertyInputs[ 'stroke' ], { label: 'Color', align: 'right' } ),
			new OO.ui.FieldLayout(propertyInputs[ 'stroke-width' ], { label: 'Width', align: 'right' } ),
			new OO.ui.FieldLayout(propertyInputs[ 'stroke-opacity' ], { label: 'Opacity', align: 'right' } ),
		] );

		var popupFieldset = new OO.ui.FieldsetLayout( { label: 'Popup' } );
		popupFieldset.addItems( [
			new OO.ui.FieldLayout(propertyInputs[ 'title' ], { label: 'Title', align: 'right' } ),
			new OO.ui.FieldLayout(propertyInputs[ 'description' ], { label: 'Description', align: 'right' } ),
		] );

		var saveButton = new OO.ui.ButtonWidget( { label: 'Save', title: 'Save', flags: [ 'primary', 'progressive' ] } );

		var mainFieldset = new OO.ui.FieldsetLayout( {} );
		mainFieldset.addItems( [
			strokeFieldset,
			popupFieldset,
			saveButton,
		] );

		saveButton.on( 'click', function() {
			self.updateLine( {
				'stroke': propertyInputs[ 'stroke' ].getValue(),
				'stroke-width': propertyInputs[ 'stroke-width' ].getValue(),
				'stroke-opacity': roundProperty( parseFloat( propertyInputs[ 'stroke-opacity' ].getValue() ) ),
				'title': propertyInputs[ 'title' ].getValue(),
				'description': propertyInputs[ 'description' ].getValue(),
			} );
		} );
		return mainFieldset.$element[ 0 ];
	};
	GeoLayer.prototype.generateMarkerForm = function() {
		var self = this;
		const symbols = [ {data:'', label:'(aucun symbolle)'}, {data:'aerialway', label:'aerialway'}, {data:'airfield', label:'airfield'}, {data:'airport', label:'airport'}, {data:'alcohol-shop', label:'alcohol-shop'}, {data:'america-football', label:'america-football'}, {data:'art-gallery', label:'art-gallery'}, {data:'bakery', label:'bakery'}, {data:'bank', label:'bank'}, {data:'bar', label:'bar'}, {data:'baseball', label:'baseball'}, {data:'basketball', label:'basketball'}, {data:'beer', label:'beer'}, {data:'bicycle', label:'bicycle'}, {data:'building', label:'building'}, {data:'bus', label:'bus'}, {data:'cafe', label:'cafe'}, {data:'campsite', label:'campsite'}, {data:'car', label:'car'}, {data:'cemetery', label:'cemetery'}, {data:'cinema', label:'cinema'}, {data:'circle', label:'circle'}, {data:'circle-stroked', label:'circle-stroked'}, {data:'city', label:'city'}, {data:'clothing-store', label:'clothing-store'}, {data:'college', label:'college'}, {data:'commercial', label:'commercial'}, {data:'cricket', label:'cricket'}, {data:'cross', label:'cross'}, {data:'dam', label:'dam'}, {data:'danger', label:'danger'}, {data:'dog-park', label:'dog-park'}, {data:'embassy', label:'embassy'}, {data:'entrance', label:'entrance'}, {data:'farm', label:'farm'}, {data:'fast-food', label:'fast-food'}, {data:'ferry', label:'ferry'}, {data:'fire-station', label:'fire-station'}, {data:'fuel', label:'fuel'}, {data:'garden', label:'garden'}, {data:'gift', label:'gift'}, {data:'golf', label:'golf'}, {data:'grocery', label:'grocery'}, {data:'hairdresser', label:'hairdresser'}, {data:'harbor', label:'harbor'}, {data:'heart', label:'heart'}, {data:'heliport', label:'heliport'}, {data:'hospital', label:'hospital'}, {data:'ice-cream', label:'ice-cream'}, {data:'laundry', label:'laundry'}, {data:'library', label:'library'}, {data:'lighthouse', label:'lighthouse'}, {data:'lodging', label:'lodging'}, {data:'logging', label:'logging'}, {data:'marker', label:'marker'}, {data:'marker-stroked', label:'marker-stroked'}, {data:'monument', label:'monument'}, {data:'museum', label:'museum'}, {data:'music', label:'music'}, {data:'park', label:'park'}, {data:'parking', label:'parking'}, {data:'parking-garage', label:'parking-garage'}, {data:'pharmacy', label:'pharmacy'}, {data:'pitch', label:'pitch'}, {data:'place-of-worship', label:'place-of-worship'}, {data:'playground', label:'playground'}, {data:'police', label:'police'}, {data:'post', label:'post'}, {data:'prison', label:'prison'}, {data:'rail', label:'rail'}, {data:'rail-light', label:'rail-light'}, {data:'rail-metro', label:'rail-metro'}, {data:'religious-christian', label:'religious-christian'}, {data:'religious-jewish', label:'religious-jewish'}, {data:'religious-muslim', label:'religious-muslim'}, {data:'restaurant', label:'restaurant'}, {data:'roadblock', label:'roadblock'}, {data:'rocket', label:'rocket'}, {data:'school', label:'school'}, {data:'scooter', label:'scooter'}, {data:'shop', label:'shop'}, {data:'skiing', label:'skiing'}, {data:'slaughterhouse', label:'slaughterhouse'}, {data:'soccer', label:'soccer'}, {data:'square', label:'square'}, {data:'square-stroked', label:'square-stroked'}, {data:'star', label:'star'}, {data:'star-stroked', label:'star-stroked'}, {data:'suitcase', label:'suitcase'}, {data:'swimming', label:'swimming'}, {data:'telephone', label:'telephone'}, {data:'tennis', label:'tennis'}, {data:'theatre', label:'theatre'}, {data:'town', label:'town'}, {data:'town-hall', label:'town-hall'}, {data:'triangle', label:'triangle'}, {data:'triangle-stroked', label:'triangle-stroked'}, {data:'village', label:'village'}, {data:'warehouse', label:'warehouse'}, {data:'waste-basket', label:'waste-basket'}, {data:'water', label:'water'}, {data:'wetland', label:'wetland'}, {data:'zoo', label:'zoo'} ];

		var propertyInputs = {
			'marker-size': new OO.ui.DropdownInputWidget( {
				options: [
					{
						data: 'small',
						label: 'small'
					},
					{
						data: 'medium',
						label: 'medium'
					},
					{
						data: 'large',
						label: 'large'
					},
				]
			} ),
			'marker-color': new OO.ui.ColorInputWidget( { value: this.GJStyle[ 'marker-color' ] } ),
			'marker-symbol': new OO.ui.DropdownInputWidget( { options: symbols } ),
			'title': new OO.ui.TextInputWidget(),
			'description': new OO.ui.MultilineTextInputWidget( { autoresize: true } ),
		};

		var markerFieldset = new OO.ui.FieldsetLayout( { label: 'Marker' } );
		markerFieldset.addItems( [
			new OO.ui.FieldLayout(propertyInputs[ 'marker-size' ], { label: 'Size', align: 'right' } ),
			new OO.ui.FieldLayout(propertyInputs[ 'marker-color' ], { label: 'Color', align: 'right' } ),
			new OO.ui.FieldLayout(propertyInputs[ 'marker-symbol' ], { label: 'Symbol', align: 'right' } ),
		] );

		var popupFieldset = new OO.ui.FieldsetLayout( { label: 'Popup' } );
		popupFieldset.addItems( [
			new OO.ui.FieldLayout(propertyInputs[ 'title' ], { label: 'Title', align: 'right' } ),
			new OO.ui.FieldLayout(propertyInputs[ 'description' ], { label: 'Description', align: 'right' } ),
		] );

		var saveButton = new OO.ui.ButtonWidget( { label: 'Save', title: 'Save', flags: [ 'primary', 'progressive' ] } );

		var mainFieldset = new OO.ui.FieldsetLayout( {} );
		mainFieldset.addItems( [
			markerFieldset,
			popupFieldset,
			saveButton,
		] );

		saveButton.on( 'click', function() {
			self.updateMarker( {
				'marker-size': propertyInputs[ 'marker-size' ].getValue(),
				'marker-color': propertyInputs[ 'marker-color' ].getValue(),
				'marker-symbol': propertyInputs[ 'marker-symbol' ].getValue(),
				'title': propertyInputs[ 'title' ].getValue(),
				'description': propertyInputs[ 'description' ].getValue(),
			} );
		} );
		return mainFieldset.$element[0];
	};

	function WMIcon( size, color, icon ) {
		const iconData = {
			'small': { size: [ 20, 50 ], anchor: [ 10, 25 ], popupAnchor: [ 0, -25 ] },
			'medium': { size: [ 30, 70 ], anchor: [ 15, 35 ], popupAnchor: [ 0, -35 ] },
			'large': { size: [ 35, 90 ], anchor: [ 17, 45 ], popupAnchor: [ 0, -45 ] },
		};
		size = size || 'medium';
		color = color || '555555';
		icon = icon || '';
		if ( icon !== '' ) {
			icon = '-' + icon;
		}
		url = 'https://maps.wikimedia.org/v4/marker/pin-' + size.charAt(0) + icon + '+' + color + '.png';
		return L.icon( {
			iconUrl: url,
			iconSize: iconData[size].size,
			iconAnchor: iconData[size].anchor,
			popupAnchor: iconData[size].popupAnchor,
		} );
	}
	function OoUiColorInputWidget( config ) {
		// Configuration initialization
		config = Object.assign( {
			type: 'color',
		}, config);

		// Parent constructor
		OO.ui.ColorInputWidget.parent.call( this, config );

		// Properties
		this.type = 'color';
		this.readOnly = false;
		this.required = false;
		this.validate = null;

		// Initialization
		this.$element
			.addClass( 'oo-ui-textInputWidget oo-ui-textInputWidget-type-' + this.type )
			.append( this.$icon, this.$indicator );
		this.$input.attr( 'type', config.type );
	};
	var OutputPageLayout = function( name, label, $element ) {
		this.label = label;
		OutputPageLayout.super.call( this, name, {} );

		this.$element.append( $element );
	};
	function roundCoordinate( value ) {
		return Math.round( value * 1000000 ) / 1000000;
	}
	function roundProperty( value ) {
		if ( value !== undefined ) {
			return value.toFixed(1);
		}
	}

	var KartoEditor = function( container, lat, lng, zoom, initialData ) {
		this.container = container;
		this.lat = lat;
		this.lng = lng;
		this.zoom = zoom;
		this.geoLayers = [];
		this.map = undefined;
		this.drawnItems = undefined;
		this.baseLayer = undefined;
		this.GJOutputArea = undefined;
		this.templateOutputArea = undefined;
		this.nbLayer = 0;

		var self = this;
		mw.loader.using( [ 'ext.kartographer.editor', 'oojs-ui' ], function() {
			OO.ui.ColorInputWidget = OoUiColorInputWidget;
			OO.inheritClass( OO.ui.ColorInputWidget, OO.ui.InputWidget );
			OO.inheritClass( OutputPageLayout, OO.ui.PageLayout );

			self.initializeMap();
			self.initializeEditor();
			self.initializeOutputArea();
			if ( initialData !== undefined ) {
				self.importGeoJSON( initialData );
			}
			self.updateOutputs();
			mw.hook( 'kartoeditor.ready' ).fire( self );
		} );
	};
	KartoEditor.prototype.initializeMap = function() {
		var self = this;
		this.baseLayer = L.tileLayer( 'https://maps.wikimedia.org/osm-intl/{z}/{x}/{y}{r}.png', { maxZoom: 18, attribution: '<a href="https://foundation.wikimedia.org/wiki/Maps_Terms_of_Use">Wikimedia</a> | &copy; <a href="http://openstreetmap.org/copyright">OpenStreetMap</a> contributors' } );
		mapContainer = $( '<div>' ).css( 'height', 600).css( 'border', '1px solid #a2a9b1' ).css( 'border-radius', '2px' ).css( 'position', 'relative' );
		$( this.container ).append( mapContainer );
		this.map = new L.Map( mapContainer[ 0 ], { center: new L.LatLng( this.lat, this.lng ), zoom: this.zoom } );
		this.drawnItems = L.featureGroup().addTo( this.map );
		this.baseLayer.addTo( this.map );

		this.rightClickPopup = L.popup();
		this.map.on( 'contextmenu', function( e ) {
			self.rightClickPopup.setLatLng( e.latlng );
			self.rightClickPopup.setContent( $( '<p>' ).css( 'text-align', 'center' )
				.append( 'latitude : ' )
				.append( roundCoordinate( e.latlng.lat ) )
				.append( '<br/>' )
				.append( 'longitude : ' )
				.append( roundCoordinate( e.latlng.lng) )
				.append( '<br/>' )
				.append( 'zoom : ' )
				.append( this.getZoom() )
				.prop( 'outerHTML' )
			);
			self.rightClickPopup.openOn( this );
		} );
		this.map.on( 'zoomend moveend', function( e ) {
			self.updateOutputs();
		} );
	};
	KartoEditor.prototype.initializeEditor = function() {
		L.drawLocal.draw.toolbar.finish.text = 'Valider';
		L.drawLocal.draw.toolbar.finish.title = 'Valider le dessin';
		this.map.addControl( new L.Control.Draw( {
			edit: {
				featureGroup: this.drawnItems,
			},
			draw: {
				polygon: {
					showArea: true,
					allowIntersection: false,
					drawError: {
						color: '#e12100',
						message: '<strong>Hum...</strong> les côtés d\'un polygone ne peuvent se croiser...'
					},
					shapeOptions: {
						color: '#555555',
					}
				},
				rectangle: {
					shapeOptions: {
						color: '#555555',
					}
				},
				polyline: {
					shapeOptions: {
						weight: 10,
						color: '#555555',
					}
				},
				marker: {
					icon: WMIcon( 'small', '555555', '' ),
				},
				circle: false,
			}
		} ) );

		var self = this;
		this.map.on( 'draw:created', function( event ) {
			var layer = event.layer;
			self.drawnItems.addLayer( layer );
			var geoLayer = new GeoLayer( self, event.layerType, layer );
			self.geoLayers.push( geoLayer );
			self.nbLayer++;
			self.updateOutputs();
		} );
		this.map.on( 'draw:edited', function( event ) {
			self.updateOutputs();
		} );
		this.map.on( 'draw:deleted', function( event ) {
			self.nbLayer--;
			self.updateOutputs();
		} );
	};
	KartoEditor.prototype.initializeOutputArea = function() {
		var self = this;
		var pages = [];
		this.booklet = new OO.ui.BookletLayout( { outlined: true } );

		this.GJOutputArea = new OO.ui.MultilineTextInputWidget( { label: 'GeoJSON', value: '', rows: 12, readOnly: true } );

		var summaryInput = new OO.ui.TextInputWidget( { placeholder: '' } );
		var editButton = new OO.ui.ButtonWidget( { label: 'Save', title: 'Save', flags: [ 'primary', 'progressive' ] } );
		var self = this;
		editButton.on( 'click', function() {
			self.saveCommons( summaryInput.getValue() );
		} );

		var editFieldset = new OO.ui.FieldsetLayout( {
			label: 'Save your changes',
			items: [
				new OO.ui.ActionFieldLayout( summaryInput, editButton, { label: 'Edit summary:', align: 'top', help: 'Le code GeoJSON servant à générer la carte sera enregistré dans l\'espace de nom « Data: » sur Commons, et pourra par la suite être importé via un modèle sur Wikipédia, un peu à la manière des images.' } ),
			],
		} );

		pages.push( new OutputPageLayout( 'edit', 'Save', editFieldset.$element ) );
		pages.push( new OutputPageLayout( 'geojson', 'GeoJSON', this.GJOutputArea.$element ) );

		var self = this;

		$.each( pages, function( key, page ) {
			self.booklet.addPages( [ page ] );
			page.outlineItem.setLabel( page.label );
		} );

		$( this.container ).append( $( '<div>' ).css('height', '300px').css( 'border', '1px solid #a2a9b1' ).css( 'border-radius', '2px' ).css( 'position', 'relative' ).css( 'margin-top', '0.5em' ).append( this.booklet.$element[ 0 ] ) );
		$( this.container ).append( $( '<div>' ).css( 'text-align', 'right' ).append( $( '<a>' ).attr( 'href', mw.util.getUrl( 'Discussion projet:JavaScript/Notices/KartoEditor' ) ).text( 'Poser une question, proposer une amélioration ou signaler un bug' ) ) );
	};
	KartoEditor.prototype.getGeoJSON = function() {
		var features = [];
		$.each(this.geoLayers, function( key, geoLayer ) {
			if ( geoLayer.isPresent() ) {
				var geoJSON = geoLayer.layer.toGeoJSON();
				geoJSON.properties = geoLayer.GJStyle;
				features.push( geoJSON );
			}
		} );
		return {
			'type': 'FeatureCollection',
			'features': features
		};
	};
	KartoEditor.prototype.updateOutputs = function() {
		this.updateGeoJSON();
	};
	KartoEditor.prototype.updateGeoJSON = function() {
		this.GJOutputArea.setValue( JSON.stringify( this.getGeoJSON(), null, 4 ) );
	};
	KartoEditor.prototype.saveCommons = function( summary ) {
		mapData = {
			license: 'CC0-1.0',
			latitude: this.map.getCenter().lat,
			longitude: this.map.getCenter().lng,
			zoom: this.map.getZoom(),
			data: this.getGeoJSON(),
		};
		var api = new mw.Api();
		api.postWithToken( 'csrf', {
			action: 'edit',
			title: String( mw.config.get( 'wgPageName' ) ),
			text: JSON.stringify( mapData, null, 4 ),
			formatversion: '2',
			summary: summary + ' (using KartoEditor)',
			assert: 'user',
		} ).then(function( data ) {
			location.reload();
		} ).fail( function( data ) {
			OO.ui.alert( 'Oops... An unexpected error happened.', {
				title: 'Erreur'
			} );
		} );
	};
	KartoEditor.prototype.importGeoJSON = function( geojson ) {
		var self = this;
		L.geoJson(geojson, {
			onEachFeature: function (feature, layer) {
				self.drawnItems.addLayer(layer);
				var geoLayer = new GeoLayer( self, feature.geometry.type, layer, feature.properties );
				self.geoLayers.push( geoLayer );
				self.nbLayer++;
				self.updateOutputs();
			}
		});
	};

	if ( mw.config.get( 'wgCanonicalNamespace' ) === 'Data' ) {
		var tab = mw.util.addPortletLink( 'p-views', '#', 'KartoEditor', 'ca-kartoeditor', 'Edit this map using KartoEditor', 'k', $( '#ca-history' ) );
		$( tab ).click( function( e ) {
			e.preventDefault();
			$( tab ).off( 'click' );

			$( '#ca-view' ).removeClass( 'selected' );
			$( '#ca-edit' ).removeClass( 'selected' );
			$( '#ca-history' ).removeClass( 'selected' );
			$( tab ).addClass( 'selected' );

			if ( mw.config.get( 'wgArticleId' ) !== 0 ) {
			var api = new mw.Api();
			api.get( {
				action: 'query',
				prop: 'revisions',
				rvprop: [ 'content' ],
				titles: mw.config.get( 'wgPageName' ),
				formatversion: '2',
			} ).then( function( data ) {
				var geojson = JSON.parse( data.query.pages[ 0 ].revisions[ 0 ].content );
				ke = new KartoEditor( $( '#bodyContent' ).empty()[ 0 ], ( geojson.latitude || 30 ), ( geojson.longitude || 10 ), ( geojson.zoom || 3 ), geojson.data );
			} );
		}
		else {
			ke = new KartoEditor( $( '#bodyContent' ).empty()[ 0 ], 30, 10, 3 );
		}
		} );
	}
	else if ( mw.config.get( 'wgCanonicalSpecialPageName' ) === 'Blankpage' && mw.config.get( 'wgPageName' ).split( '/' ).slice( -1 )[ 0 ] === 'KartoEditor' ) {
		$( '#firstHeading' ).text( 'KartoEditor' );
		ke = new KartoEditor( $( '#bodyContent' ).empty()[ 0 ], 30, 10, 3 );
		document.title = 'KartoEditor';
	}
	portletLink = mw.util.addPortletLink( 'p-tb', mw.util.getUrl( 'Special:Blankpage/KartoEditor' ), 'KartoEditor', 't-kartoeditor' );
	$( portletLink ).append( ' <a href="' + mw.util.getUrl( 'Projet:JavaScript/Notices/KartoEditor' ) + '"><small>(aide)</small></a>' );

	window.KartoEditor = KartoEditor;
} )();

//</nowiki>