summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorYaco <franco@reevo.org>2021-10-19 20:24:11 -0300
committerYaco <franco@reevo.org>2021-10-19 20:24:11 -0300
commite3880a1c86acaa3bbd05786ad2f5c586e6511a58 (patch)
treeec77bfc5b69f259a159c95188797bd0dade92357
parent20ca0685509f8010580d3b45036a64ab48616af1 (diff)
updates Maps to 7.13.0
-rw-r--r--www/wiki/extensions/Maps/.travis.yml12
-rw-r--r--www/wiki/extensions/Maps/DefaultSettings.php9
-rw-r--r--www/wiki/extensions/Maps/INSTALL.md36
-rw-r--r--www/wiki/extensions/Maps/README.md11
-rw-r--r--www/wiki/extensions/Maps/RELEASE-NOTES.md130
-rw-r--r--www/wiki/extensions/Maps/composer.json5
-rw-r--r--www/wiki/extensions/Maps/extension.json220
-rw-r--r--www/wiki/extensions/Maps/i18n/ar.json44
-rw-r--r--www/wiki/extensions/Maps/i18n/ast.json41
-rw-r--r--www/wiki/extensions/Maps/i18n/be-tarask.json2
-rw-r--r--www/wiki/extensions/Maps/i18n/bn.json7
-rw-r--r--www/wiki/extensions/Maps/i18n/de.json3
-rw-r--r--www/wiki/extensions/Maps/i18n/el.json3
-rw-r--r--www/wiki/extensions/Maps/i18n/en.json54
-rw-r--r--www/wiki/extensions/Maps/i18n/es.json29
-rw-r--r--www/wiki/extensions/Maps/i18n/fi.json9
-rw-r--r--www/wiki/extensions/Maps/i18n/fr.json250
-rw-r--r--www/wiki/extensions/Maps/i18n/gl.json6
-rw-r--r--www/wiki/extensions/Maps/i18n/he.json7
-rw-r--r--www/wiki/extensions/Maps/i18n/hr.json3
-rw-r--r--www/wiki/extensions/Maps/i18n/ia.json2
-rw-r--r--www/wiki/extensions/Maps/i18n/it.json23
-rw-r--r--www/wiki/extensions/Maps/i18n/ja.json3
-rw-r--r--www/wiki/extensions/Maps/i18n/ko.json11
-rw-r--r--www/wiki/extensions/Maps/i18n/lb.json8
-rw-r--r--www/wiki/extensions/Maps/i18n/lt.json41
-rw-r--r--www/wiki/extensions/Maps/i18n/mk.json58
-rw-r--r--www/wiki/extensions/Maps/i18n/nb.json48
-rw-r--r--www/wiki/extensions/Maps/i18n/nl.json3
-rw-r--r--www/wiki/extensions/Maps/i18n/pl.json45
-rw-r--r--www/wiki/extensions/Maps/i18n/pt-br.json39
-rw-r--r--www/wiki/extensions/Maps/i18n/pt.json7
-rw-r--r--www/wiki/extensions/Maps/i18n/qqq.json29
-rw-r--r--www/wiki/extensions/Maps/i18n/roa-tara.json40
-rw-r--r--www/wiki/extensions/Maps/i18n/ru.json41
-rw-r--r--www/wiki/extensions/Maps/i18n/sh.json69
-rw-r--r--www/wiki/extensions/Maps/i18n/sv.json45
-rw-r--r--www/wiki/extensions/Maps/i18n/tt-cyrl.json73
-rw-r--r--www/wiki/extensions/Maps/i18n/uk.json3
-rw-r--r--www/wiki/extensions/Maps/i18n/vi.json3
-rw-r--r--www/wiki/extensions/Maps/i18n/zh-hans.json10
-rw-r--r--www/wiki/extensions/Maps/i18n/zh-hant.json46
-rw-r--r--www/wiki/extensions/Maps/resources/GoogleMaps/ext.maps.googlemaps3.js31
-rw-r--r--www/wiki/extensions/Maps/resources/GoogleMaps/geoxml3/ProjectedOverlay.js186
-rw-r--r--www/wiki/extensions/Maps/resources/GoogleMaps/geoxml3/README2
-rw-r--r--www/wiki/extensions/Maps/resources/GoogleMaps/geoxml3/ZipFile.complete.js3939
-rw-r--r--www/wiki/extensions/Maps/resources/GoogleMaps/geoxml3/geoxml3.js3398
-rw-r--r--www/wiki/extensions/Maps/resources/GoogleMaps/googlemaps3ajax.js (renamed from www/wiki/extensions/Maps/resources/GoogleMaps/ext.sm.googlemaps3ajax.js)2
-rw-r--r--www/wiki/extensions/Maps/resources/GoogleMaps/jquery.googlemap.js10
-rw-r--r--www/wiki/extensions/Maps/resources/MapSaver.js57
-rw-r--r--www/wiki/extensions/Maps/resources/WikitextEditor/css/jquery.miniColors.css (renamed from www/wiki/extensions/Maps/resources/editor/css/jquery.miniColors.css)0
-rw-r--r--www/wiki/extensions/Maps/resources/WikitextEditor/css/mapeditor.css (renamed from www/wiki/extensions/Maps/resources/editor/css/mapeditor.css)0
-rw-r--r--www/wiki/extensions/Maps/resources/WikitextEditor/images/circle.gif (renamed from www/wiki/extensions/Maps/resources/editor/images/circle.gif)bin78 -> 78 bytes
-rw-r--r--www/wiki/extensions/Maps/resources/WikitextEditor/images/gradient.png (renamed from www/wiki/extensions/Maps/resources/editor/images/gradient.png)bin6548 -> 6548 bytes
-rw-r--r--www/wiki/extensions/Maps/resources/WikitextEditor/images/line.gif (renamed from www/wiki/extensions/Maps/resources/editor/images/line.gif)bin1104 -> 1104 bytes
-rw-r--r--www/wiki/extensions/Maps/resources/WikitextEditor/images/rainbow.png (renamed from www/wiki/extensions/Maps/resources/editor/images/rainbow.png)bin1665 -> 1665 bytes
-rw-r--r--www/wiki/extensions/Maps/resources/WikitextEditor/images/trigger.png (renamed from www/wiki/extensions/Maps/resources/editor/images/trigger.png)bin538 -> 538 bytes
-rw-r--r--www/wiki/extensions/Maps/resources/WikitextEditor/js/README (renamed from www/wiki/extensions/Maps/resources/editor/js/README)0
-rw-r--r--www/wiki/extensions/Maps/resources/WikitextEditor/js/jquery.miniColors.js (renamed from www/wiki/extensions/Maps/resources/editor/js/jquery.miniColors.js)0
-rw-r--r--www/wiki/extensions/Maps/resources/WikitextEditor/js/mapeditor.iefixes.js (renamed from www/wiki/extensions/Maps/resources/editor/js/mapeditor.iefixes.js)0
-rw-r--r--www/wiki/extensions/Maps/resources/WikitextEditor/js/mapeditor.js (renamed from www/wiki/extensions/Maps/resources/editor/js/mapeditor.js)2
-rw-r--r--www/wiki/extensions/Maps/resources/api.js44
-rw-r--r--www/wiki/extensions/Maps/resources/geoJsonPage.js96
-rw-r--r--www/wiki/extensions/Maps/resources/geojson.new.page.js38
-rw-r--r--www/wiki/extensions/Maps/resources/leaflet/FeatureBuilder.js220
-rw-r--r--www/wiki/extensions/Maps/resources/leaflet/GeoJson.js74
-rw-r--r--www/wiki/extensions/Maps/resources/leaflet/LeafletCluster.js69
-rw-r--r--www/wiki/extensions/Maps/resources/leaflet/LeafletEditor.js256
-rw-r--r--www/wiki/extensions/Maps/resources/leaflet/LeafletLoader.js15
-rw-r--r--www/wiki/extensions/Maps/resources/leaflet/cluster/m1.pngbin3288 -> 3003 bytes
-rw-r--r--www/wiki/extensions/Maps/resources/leaflet/cluster/m2.pngbin3707 -> 3259 bytes
-rw-r--r--www/wiki/extensions/Maps/resources/leaflet/cluster/m3.pngbin5605 -> 3956 bytes
-rw-r--r--www/wiki/extensions/Maps/resources/leaflet/cluster/m4.pngbin6515 -> 5705 bytes
-rw-r--r--www/wiki/extensions/Maps/resources/leaflet/cluster/m5.pngbin7249 -> 6839 bytes
-rw-r--r--www/wiki/extensions/Maps/resources/leaflet/ext.maps.leaflet.js4
-rw-r--r--www/wiki/extensions/Maps/resources/leaflet/ext.sm.leafletajax.js44
-rw-r--r--www/wiki/extensions/Maps/resources/leaflet/images/edit-solid.svg1
-rw-r--r--www/wiki/extensions/Maps/resources/leaflet/images/save-solid.svg1
-rw-r--r--www/wiki/extensions/Maps/resources/leaflet/jquery.leaflet.js567
-rw-r--r--www/wiki/extensions/Maps/resources/leaflet/leaflet.editable/Leaflet.Editable.js1945
-rw-r--r--www/wiki/extensions/Maps/resources/leaflet/leaflet.editor.js15
-rw-r--r--www/wiki/extensions/Maps/resources/leaflet/leaflet.fullscreen/.jshintrc12
-rw-r--r--www/wiki/extensions/Maps/resources/leaflet/leaflet.fullscreen/Control.FullScreen.css4
-rw-r--r--www/wiki/extensions/Maps/resources/leaflet/leaflet.fullscreen/Control.FullScreen.js164
-rw-r--r--www/wiki/extensions/Maps/resources/leaflet/leaflet.fullscreen/LICENSE22
-rw-r--r--www/wiki/extensions/Maps/resources/leaflet/leaflet.fullscreen/README.md68
-rw-r--r--www/wiki/extensions/Maps/resources/leaflet/leaflet.fullscreen/bower.json30
-rw-r--r--www/wiki/extensions/Maps/resources/leaflet/leaflet.fullscreen/icon-fullscreen-2x.pngbin228 -> 0 bytes
-rw-r--r--www/wiki/extensions/Maps/resources/leaflet/leaflet.fullscreen/icon-fullscreen.pngbin153 -> 0 bytes
-rw-r--r--www/wiki/extensions/Maps/resources/leaflet/leaflet.fullscreen/index.html48
-rw-r--r--www/wiki/extensions/Maps/resources/leaflet/leaflet.markercluster/leaflet.markercluster.js3
-rw-r--r--www/wiki/extensions/Maps/resources/leaflet/leaflet/images/marker-icon-2x.pngbin4230 -> 0 bytes
-rw-r--r--www/wiki/extensions/Maps/resources/leaflet/leaflet/images/marker-icon.pngbin1870 -> 0 bytes
-rw-r--r--www/wiki/extensions/Maps/resources/leaflet/leaflet/leaflet.js5
-rw-r--r--www/wiki/extensions/Maps/resources/lib/leaflet-providers/leaflet-providers.js (renamed from www/wiki/extensions/Maps/resources/leaflet/leaflet-providers/leaflet-providers.js)0
-rw-r--r--www/wiki/extensions/Maps/resources/lib/leaflet.EasyButton/easy-button.css56
-rw-r--r--www/wiki/extensions/Maps/resources/lib/leaflet.EasyButton/easy-button.js377
-rw-r--r--www/wiki/extensions/Maps/resources/lib/leaflet.StyleEditor/css/Leaflet.StyleEditor.min.css306
-rw-r--r--www/wiki/extensions/Maps/resources/lib/leaflet.StyleEditor/img/control.svg114
-rw-r--r--www/wiki/extensions/Maps/resources/lib/leaflet.StyleEditor/img/icon.pngbin0 -> 4546 bytes
-rw-r--r--www/wiki/extensions/Maps/resources/lib/leaflet.StyleEditor/javascript/Leaflet.StyleEditor.min.js2
-rw-r--r--www/wiki/extensions/Maps/resources/lib/leaflet.draw/images/spritesheet-2x.pngbin0 -> 3581 bytes
-rw-r--r--www/wiki/extensions/Maps/resources/lib/leaflet.draw/images/spritesheet.svg156
-rw-r--r--www/wiki/extensions/Maps/resources/lib/leaflet.draw/leaflet.draw-src.css325
-rw-r--r--www/wiki/extensions/Maps/resources/lib/leaflet.draw/leaflet.draw-src.js4774
-rw-r--r--www/wiki/extensions/Maps/resources/lib/leaflet.draw/leaflet.draw-src.map1
-rw-r--r--www/wiki/extensions/Maps/resources/lib/leaflet.draw/leaflet.draw.css10
-rw-r--r--www/wiki/extensions/Maps/resources/lib/leaflet.draw/leaflet.draw.js10
-rw-r--r--www/wiki/extensions/Maps/resources/lib/leaflet.fullscreen/Control.FullScreen.css8
-rw-r--r--www/wiki/extensions/Maps/resources/lib/leaflet.fullscreen/Control.FullScreen.js228
-rw-r--r--www/wiki/extensions/Maps/resources/lib/leaflet.fullscreen/icon-fullscreen-2x.pngbin0 -> 215 bytes
-rw-r--r--www/wiki/extensions/Maps/resources/lib/leaflet.fullscreen/icon-fullscreen.pngbin0 -> 139 bytes
-rw-r--r--www/wiki/extensions/Maps/resources/lib/leaflet.fullscreen/package.json (renamed from www/wiki/extensions/Maps/resources/leaflet/leaflet.fullscreen/package.json)6
-rw-r--r--www/wiki/extensions/Maps/resources/lib/leaflet.markercluster/MarkerCluster.css (renamed from www/wiki/extensions/Maps/resources/leaflet/leaflet.markercluster/MarkerCluster.css)0
-rw-r--r--www/wiki/extensions/Maps/resources/lib/leaflet.markercluster/leaflet.markercluster.js2689
-rw-r--r--www/wiki/extensions/Maps/resources/lib/leaflet/images/layers-2x.png (renamed from www/wiki/extensions/Maps/resources/leaflet/leaflet/images/layers-2x.png)bin1259 -> 1259 bytes
-rw-r--r--www/wiki/extensions/Maps/resources/lib/leaflet/images/layers.png (renamed from www/wiki/extensions/Maps/resources/leaflet/leaflet/images/layers.png)bin696 -> 696 bytes
-rw-r--r--www/wiki/extensions/Maps/resources/lib/leaflet/images/marker-icon-2x.pngbin0 -> 2586 bytes
-rw-r--r--www/wiki/extensions/Maps/resources/lib/leaflet/images/marker-icon.pngbin0 -> 1466 bytes
-rw-r--r--www/wiki/extensions/Maps/resources/lib/leaflet/images/marker-shadow.png (renamed from www/wiki/extensions/Maps/resources/leaflet/leaflet/images/marker-shadow.png)bin618 -> 618 bytes
-rw-r--r--www/wiki/extensions/Maps/resources/lib/leaflet/leaflet.css (renamed from www/wiki/extensions/Maps/resources/leaflet/leaflet/leaflet.css)7
-rw-r--r--www/wiki/extensions/Maps/resources/lib/leaflet/leaflet.js14079
-rw-r--r--www/wiki/extensions/Maps/resources/maps.common.js4
-rw-r--r--www/wiki/extensions/Maps/resources/maps.services.js86
-rw-r--r--www/wiki/extensions/Maps/resources/semanticMaps.js (renamed from www/wiki/extensions/Maps/resources/sm.common.js)34
-rw-r--r--www/wiki/extensions/Maps/src/DataAccess/GeoJsonFetcher.php93
-rw-r--r--www/wiki/extensions/Maps/src/DataAccess/GeoJsonFetcherResult.php31
-rw-r--r--www/wiki/extensions/Maps/src/DataAccess/JsonFileParser.php79
-rw-r--r--www/wiki/extensions/Maps/src/GoogleMapsService.php71
-rw-r--r--www/wiki/extensions/Maps/src/LeafletService.php66
-rw-r--r--www/wiki/extensions/Maps/src/MappingService.php6
-rw-r--r--www/wiki/extensions/Maps/src/MapsFactory.php34
-rw-r--r--www/wiki/extensions/Maps/src/MapsFunctions.php32
-rw-r--r--www/wiki/extensions/Maps/src/MapsSetup.php87
-rw-r--r--www/wiki/extensions/Maps/src/MediaWiki/Content/GeoJsonContent.php57
-rw-r--r--www/wiki/extensions/Maps/src/MediaWiki/Content/GeoJsonContentHandler.php6
-rw-r--r--www/wiki/extensions/Maps/src/MediaWiki/MapsHooks.php61
-rw-r--r--www/wiki/extensions/Maps/src/MediaWiki/ParserHooks/DisplayMapFunction.php117
-rw-r--r--www/wiki/extensions/Maps/src/MediaWiki/ParserHooks/DisplayMapRenderer.php7
-rw-r--r--www/wiki/extensions/Maps/src/MediaWiki/ParserHooks/FindDestinationFunction.php4
-rw-r--r--www/wiki/extensions/Maps/src/MediaWiki/ParserHooks/MapsDocFunction.php19
-rw-r--r--www/wiki/extensions/Maps/src/MediaWiki/Specials/SpecialMapEditor.php3
-rw-r--r--www/wiki/extensions/Maps/src/Presentation/GeoJsonMapPageUi.php83
-rw-r--r--www/wiki/extensions/Maps/src/Presentation/GeoJsonNewPageUi.php27
-rw-r--r--www/wiki/extensions/Maps/src/Presentation/MapHtmlBuilder.php10
-rw-r--r--www/wiki/extensions/Maps/src/Presentation/MapsDistanceParser.php2
-rw-r--r--www/wiki/extensions/Maps/src/Presentation/OutputFacade.php62
-rw-r--r--www/wiki/extensions/Maps/src/SemanticMW/ResultPrinters/MapPrinter.php17
-rw-r--r--www/wiki/extensions/Maps/tests/Integration/DataAccess/GeoJsonFetcherTest.php (renamed from www/wiki/extensions/Maps/tests/Integration/parsers/JsonFileParserTest.php)43
-rw-r--r--www/wiki/extensions/Maps/tests/Integration/MediaWiki/ParserHooks/CoordinatesTest.php8
-rw-r--r--www/wiki/extensions/Maps/tests/Integration/MediaWiki/ParserHooks/ParserHookTest.php13
-rw-r--r--www/wiki/extensions/Maps/tests/Integration/Parser/DisplayMapTest.php39
-rw-r--r--www/wiki/extensions/Maps/tests/Integration/ResourceModulesTest.php56
-rw-r--r--www/wiki/extensions/Maps/tests/Unit/MediaWiki/GeoJsonContentTest.php22
-rw-r--r--www/wiki/extensions/Maps/tests/js/leaflet/GeoJsonTest.js112
155 files changed, 30376 insertions, 7063 deletions
diff --git a/www/wiki/extensions/Maps/.travis.yml b/www/wiki/extensions/Maps/.travis.yml
index 49373ca8..a0e21f15 100644
--- a/www/wiki/extensions/Maps/.travis.yml
+++ b/www/wiki/extensions/Maps/.travis.yml
@@ -8,14 +8,14 @@ sudo: false
matrix:
fast_finish: true
include:
- - env: DBTYPE=mysql; MW=1.31.3
+ - env: DBTYPE=mysql; MW=1.31.3; SMW=3.0.0
php: 7.1
- - env: DBTYPE=mysql; MW=master; SMW=dev-master
- php: 7.3
- - env: DBTYPE=sqlite; MW=1.31.3; SMW=3.0.0
- php: 7.1
- - env: DBTYPE=sqlite; MW=master; TYPE=coverage
+ - env: DBTYPE=sqlite; MW=1.33.0; SMW=3.1.0
php: 7.2
+ - env: DBTYPE=mysql; MW=master; SMW=dev-master; TYPE=coverage
+ php: 7.3
+ - env: DBTYPE=mysql; MW=master
+ php: 7.4snapshot
install:
- travis_retry composer self-update
diff --git a/www/wiki/extensions/Maps/DefaultSettings.php b/www/wiki/extensions/Maps/DefaultSettings.php
index a3834788..146a1730 100644
--- a/www/wiki/extensions/Maps/DefaultSettings.php
+++ b/www/wiki/extensions/Maps/DefaultSettings.php
@@ -1,8 +1,9 @@
<?php
+// DO NOT INCLUDE THIS FILE IN LocalSettings.php! Instead include Maps_Settings.php.
+
// This file lists all settings you can use to configure the Maps extension and their default values.
// Do not modify this file to change settings. See https://www.semantic-mediawiki.org/wiki/Maps/Configuration
-// Do not include this file in LocalSettings. Instead include Maps_Settings.php.
return [
// Mapping services that will be available in the wiki.
@@ -156,6 +157,10 @@ return [
'egMapsLeafletLayer' => 'OpenStreetMap',
'egMapsLeafletLayers' => [ 'OpenStreetMap' ],
+ // Array of strings. The default layers for Leaflet to be used in dark theme mode.
+ // This will override any user parameter set for "layers" or "layer".
+ 'egMapsLeafletLayersDark' => [ 'CartoDB.DarkMatter' ],
+
'egMapsLeafletOverlayLayers' => [],
// The definitions for the layers that should be available for the user.
@@ -362,4 +367,4 @@ return [
'egMapsDebugJS' => false,
'egMapsGlobalJSVars' => [],
-]; \ No newline at end of file
+];
diff --git a/www/wiki/extensions/Maps/INSTALL.md b/www/wiki/extensions/Maps/INSTALL.md
index 20a4b3ad..a0fc3c48 100644
--- a/www/wiki/extensions/Maps/INSTALL.md
+++ b/www/wiki/extensions/Maps/INSTALL.md
@@ -18,27 +18,41 @@ minimum requirements are indicated in bold. For a detailed list of changes, see
<table>
<tr>
- <th>Maps</th>
- <th>PHP</th>
- <th>MediaWiki</th>
- <th>Semantic<br>MediaWiki</th>
- <th>Release status</th>
+ <th>Maps<br>&nbsp;</th>
+ <th>PHP<br>&nbsp;</th>
+ <th>MediaWiki<br>&nbsp;</th>
+ <th>Semantic MediaWiki<br>(optional)</th>
+ <th>Release status<br>&nbsp;</th>
</tr>
<tr>
- <th>7.4.x</th>
+ <th>7.14.x</th>
<td>7.1 - 7.4+</td>
- <td>1.31 - 1.33+</td>
- <td>3.0+</td>
- <td>Planned Q3 2019</td>
+ <td>1.31 - 1.34+</td>
+ <td>3.0 - 3.1</td>
+ <td>Planned Q4 2019</td>
</tr>
<tr>
- <th>7.3.x</th>
+ <th>7.13.x</th>
<td>7.1 - 7.4</td>
- <td>1.31 - 1.33</td>
+ <td>1.31 - 1.34</td>
<td>3.0 - 3.1</td>
<td><strong>Stable release</strong></td>
</tr>
<tr>
+ <th>7.12.x<br>-<br>7.10.x</th>
+ <td>7.1 - 7.4</td>
+ <td>1.31 - 1.34</td>
+ <td>3.0 - 3.1</td>
+ <td>Obsolete release, no support</td>
+ </tr>
+ <tr>
+ <th>7.9.x<br>-<br>7.3.x</th>
+ <td>7.1 - 7.4</td>
+ <td>1.31 - 1.33</td>
+ <td>3.0 - 3.1</td>
+ <td>Obsolete release, no support</td>
+ </tr>
+ <tr>
<th>7.2.x</th>
<td>7.1 - 7.3</td>
<td>1.31 - 1.32</td>
diff --git a/www/wiki/extensions/Maps/README.md b/www/wiki/extensions/Maps/README.md
index 230f3183..9baf56a3 100644
--- a/www/wiki/extensions/Maps/README.md
+++ b/www/wiki/extensions/Maps/README.md
@@ -1,7 +1,7 @@
# Maps
-Maps is a [MediaWiki](https://www.mediawiki.org) extension to work with and visualize geographical
-information.
+Maps is the [MediaWiki](https://www.mediawiki.org) extension to visualize and work with geographical
+information. It has been maintained since 2009 and is installed on 1000+ wikis.
Features:
@@ -12,14 +12,15 @@ Features:
* Export your coordinates as KML or RDF
* Combine coordinates with other structured data stored in your wiki
* Geocoding via several supported services with the [`#geocode`](https://www.semantic-mediawiki.org/wiki/Maps/Geocoding) parser function.
+* [GeoJson support](https://www.semantic-mediawiki.org/wiki/Extension:Maps/GeoJSON) including a basic visual editor
* Coordinate formatting and format conversion via the [`#coordinates`](https://www.semantic-mediawiki.org/wiki/Maps/Coordinates) parser function.
* Geospatial operations
* Calculating the distance between two points with [`#geodistance`](https://www.semantic-mediawiki.org/wiki/Maps/Geodistance)
* Finding a destination given a starting point, bearing and distance with [`#finddestination`](https://www.semantic-mediawiki.org/wiki/Maps/Finddestination)
* Distance formatting and format conversion via the [`#distance`](https://www.semantic-mediawiki.org/wiki/Maps/Distance) parser function.
-* Visual map editor ([Special:MapEditor](https://www.semantic-mediawiki.org/wiki/Special:MapEditor)) to edit [`#display_map`](https://www.semantic-mediawiki.org/wiki/Extension:Maps/Displaying_maps) wikitext (requires Google Maps).
+* Visual map editor (Special:MapEditor) to edit [`#display_map`](https://www.semantic-mediawiki.org/wiki/Extension:Maps/Displaying_maps) wikitext (requires Google Maps).
-Maps has been maintained since 2009 and is installed on 1000+ wikis. [Professional.Wiki](https://professional.wiki/) provides professional support.
+Missing a feature? [Professional.Wiki](https://professional.wiki/) does custom development at a discount if it is open sourced.
## User manual
@@ -49,7 +50,7 @@ Maps has been maintained since 2009 and is installed on 1000+ wikis. [Profession
* TravisCI &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; [![Build Status](https://secure.travis-ci.org/JeroenDeDauw/Maps.png?branch=master)](http://travis-ci.org/JeroenDeDauw/Maps)
* Code quality &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; [![Scrutinizer Quality Score](https://scrutinizer-ci.com/g/JeroenDeDauw/Maps/badges/quality-score.png?s=3881a27e63cb64e7511d766bfec2e2db5d39bec3)](https://scrutinizer-ci.com/g/JeroenDeDauw/Maps/)
* [Open bugs and feature requests](https://github.com/JeroenDeDauw/Maps/issues)
-* [Maps on OpenHub](https://www.ohloh.net/p/maps/)
+* [Maps on OpenHub](https://www.openhub.net/p/maps/)
* [Blog posts about Maps](https://www.entropywins.wtf/blog/tag/maps/)
## Contributing
diff --git a/www/wiki/extensions/Maps/RELEASE-NOTES.md b/www/wiki/extensions/Maps/RELEASE-NOTES.md
index 118272e2..48a08e49 100644
--- a/www/wiki/extensions/Maps/RELEASE-NOTES.md
+++ b/www/wiki/extensions/Maps/RELEASE-NOTES.md
@@ -3,6 +3,136 @@ different releases and which versions of PHP and MediaWiki they support, see the
[platform compatibility tables](INSTALL.md#platform-compatibility-and-release-status).
+## Mps 7.13.0
+
+Released on December 14th, 2019.
+
+* The GeoJSON editor now shows in #display_maps and #ask for Leaflet maps using the geojson parameter.
+* Removed the need to manually include `Maps_Settings.php` in `LocalSettings.php` when modifying maps settings.
+* Improved compatibility with MediaWiki 1.35
+
+## Maps 7.12.2
+
+Released on December 9th, 2019.
+
+* Invalid KML file names are no longer passed to Google Maps
+
+## Maps 7.12.1
+
+Released on December 9th, 2019.
+
+* Map query output is no longer incorrectly handled by the MediaWiki parser
+* Added logging of debug information when KML parsing fails
+* Upgraded Google Maps GeoXML parsing library for KML
+
+## Maps 7.12.0
+
+Released on December 9th, 2019.
+
+* Enhanced GeoJSON editor
+ * Added editing of titles and descriptions (by clicking markers/shapes)
+ * Added save button and removed auto-save
+ * Added ability to specify an edit summary
+ * Polygon intersections are now allowed
+* Added [simplestyle](https://github.com/mapbox/simplestyle-spec/tree/master/1.1.0) support for GeoJSON
+ * Popup text (property key `text`) (Only plaintext, HTML and wikitext are not supported)
+ * Popup description (property key `description`) (Only plaintext, HTML and wikitext are not supported)
+ * Fill color (property key `fill`)
+ * Fill opacity (property key `fill-opacity`)
+ * Border color (property key `stroke`)
+ * Border width (property key `stroke-width`)
+ * Border opacity (property key `stroke-opacity`)
+ * `marker-size`, `marker-symbol` and `marker-color` are not yet supported and will be ignored
+ * Display only, editing in the visual editor is not yet supported
+* Marker clustering now also cluster markers from the GeoJSON layer
+* Marker clustering now also cluster markers dynamically loaded via the `ajaxquery` feature
+* The Leaflet layer control is now always shown when there are overlays
+* Added `cluster` alias to the `markercluster` parameter for both Leaflet and Google Maps
+* Added `overlays` alias to the `overlaylayers` parameter for Leaflet
+* Leaflet maps with no markers or shapes are now zoomed out by default
+* Upgraded Leaflet from 1.3.4 to 1.6.0
+* Upgraded Leaflet marker cluster plugin from 1.3.0 to 1.4.1
+* Added missing "KML parsing failed" message to Google Maps
+
+## Maps 7.11.0
+
+Released on November 7th, 2019.
+
+* Fixed maps not loading without reloading the page after edit with Visual Editor
+* Fixed Leaflet Ajax functionality
+
+## Maps 7.10.0
+
+Released on October 24th, 2019.
+
+* Added dark mode support for Leaflet. Configurable via the new `egMapsLeafletLayersDark` setting (by @vedmaka)
+* Fixed PHP notice on some MediaWiki versions when running maintenance scripts
+
+## Maps 7.9.0
+
+Released on October 4th, 2019.
+
+* Added `clicktarget` parameter for Leaflet. `clicktarget=http://your.url?latitude=%lat%&longitude=%long%`
+* The `#mapsdoc` parser function now shows all parameters, not just those specific to a mapping service
+* The `visitedicon` parameter is no longer incorrectly shown as supported for Leaflet
+* The `wmsoverlay` parameter is no longer incorrectly shown as supported for Leaflet
+
+## Maps 7.8.3
+
+Released on October 3rd, 2019.
+
+* The "create page" button on GeoJson pages is now only shown to users with `createpage` permission
+
+## Maps 7.8.2
+
+Released on October 2nd, 2019.
+
+* Fixed recent Google Maps regression
+
+## Maps 7.8.1
+
+Released on October 2nd, 2019.
+
+* Fixed double display of marker icons in the GeoJson namespace
+
+## Maps 7.8.0
+
+Released on October 2nd, 2019.
+
+* Loading messages for Leaflet maps are no longer visible when zooming out far or loading new tiles
+* Added entirely visual creation flow for pages in the GeoJson namespace
+* Enhanced validation of content in the GeoJson namespace
+* Improved text on the creation and edit tabs in the GeoJson namespace
+* Added "Visual map edit" tag to revisions created by the GeoJson visual editor
+
+## Maps 7.7.0
+
+Released on September 29th, 2019.
+
+* Fixed GeoJson visual editor on MediaWiki 1.31.x (7.6.0 regression)
+* Internationalized most of the GeoJson visual editor
+* Added fullscreen control to the GeoJson visual editor
+* Added `fullscreen` alias for the `enablefullscreen` parameter
+
+## Maps 7.6.0
+
+Released on September 27th, 2019.
+
+* Fixed GeoJson map preview on MediaWiki 1.33+ (7.5.0 regression)
+* Added `scrollzoom` alias for the `scrollwheelzoom` parameter
+
+## Maps 7.5.0
+
+Released on September 24th, 2019.
+
+* Added visual editing UI to maps in the GeoJson namespace
+
+## Maps 7.4.1
+
+Released on August 31st, 2019.
+
+* Fixed critical map loading bug that caused many maps to not load without a page refresh
+
## Maps 7.4.0
Released on August 9th, 2019.
diff --git a/www/wiki/extensions/Maps/composer.json b/www/wiki/extensions/Maps/composer.json
index ae9b63ed..c8532790 100644
--- a/www/wiki/extensions/Maps/composer.json
+++ b/www/wiki/extensions/Maps/composer.json
@@ -35,7 +35,7 @@
"composer/installers": "^1.0.1",
"mediawiki/validator": "~2.2",
"mediawiki/parser-hooks": "~1.5",
- "param-processor/param-processor": "^1.4.2",
+ "param-processor/param-processor": "^1.10",
"data-values/geo": "~4.0|~3.0",
"jeroen/file-fetcher": "~6.0|~5.0",
"jeroen/file-fetcher-cache": "~1.0",
@@ -52,6 +52,9 @@
},
"classmap": [
"MapsRegistration.php"
+ ],
+ "files": [
+ "Maps_Settings.php"
]
},
"extra": {
diff --git a/www/wiki/extensions/Maps/extension.json b/www/wiki/extensions/Maps/extension.json
index f0bcddc8..6ddee628 100644
--- a/www/wiki/extensions/Maps/extension.json
+++ b/www/wiki/extensions/Maps/extension.json
@@ -1,6 +1,6 @@
{
"name": "Maps",
- "version": "7.4.0",
+ "version": "7.13.0",
"author": [
"[https://www.entropywins.wtf/mediawiki Jeroen De Dauw]",
@@ -54,62 +54,31 @@
},
"ResourceModules": {
- "ext.maps.common": {
+ "ext.sm.common": {
"scripts": [
- "maps.common.js"
- ],
- "messages": [
- "maps-load-failed"
+ "semanticMaps.js"
]
},
- "ext.maps.services": {
+ "ext.maps.geojson.new.page": {
"dependencies": [
- "ext.maps.common"
+ "mediawiki.api.edit"
],
"scripts": [
- "maps.services.js"
- ]
- },
-
- "ext.sm.common": {
- "dependencies": [
- "ext.maps.common",
- "ext.maps.services"
+ "geojson.new.page.js"
],
- "scripts": [
- "sm.common.js"
+ "messages": [
+ "maps-geo-json-create-page-creating",
+ "maps-geo-json-create-page-summary"
]
},
- "mapeditor": {
+ "ext.maps.geojson.page": {
"dependencies": [
- "ext.maps.common",
- "jquery.ui.autocomplete",
- "jquery.ui.slider",
- "jquery.ui.dialog"
+ "ext.maps.leaflet.editor"
],
"scripts": [
- "editor/js/jquery.miniColors.js",
- "editor/js/mapeditor.iefixes.js",
- "editor/js/mapeditor.js"
- ],
- "styles": [
- "editor/css/jquery.miniColors.css",
- "editor/css/mapeditor.css"
- ],
- "messages": [
- "mapeditor-parser-error",
- "mapeditor-none-text",
- "mapeditor-done-button",
- "mapeditor-remove-button",
- "mapeditor-import-button",
- "mapeditor-export-button",
- "mapeditor-import-button2",
- "mapeditor-select-button",
- "mapeditor-mapparam-button",
- "mapeditor-clear-button",
- "mapeditor-imageoverlay-button"
+ "geoJsonPage.js"
]
},
@@ -119,97 +88,129 @@
]
},
- "ext.maps.leaflet.base": {
+ "ext.maps.leaflet.library": {
"scripts": [
- "leaflet/leaflet/leaflet.js"
+ "lib/leaflet/leaflet.js",
+ "lib/leaflet-providers/leaflet-providers.js"
+
],
"styles": [
- "leaflet/leaflet/leaflet.css"
+ "lib/leaflet/leaflet.css"
+ ]
+ },
+
+ "ext.maps.leaflet.geojson": {
+ "scripts": [
+ "leaflet/GeoJson.js"
]
},
- "ext.maps.leaflet": {
+ "ext.maps.leaflet.loader": {
"dependencies": [
- "ext.maps.common",
- "ext.maps.services",
- "ext.maps.leaflet.base"
+ "ext.maps.leaflet.library",
+ "ext.maps.leaflet.geojson",
+ "ext.sm.common"
],
"scripts": [
+ "leaflet/FeatureBuilder.js",
"leaflet/jquery.leaflet.js",
- "leaflet/ext.maps.leaflet.js"
+ "leaflet/LeafletLoader.js"
],
"messages": [
- "maps-markers",
- "maps-copycoords-prompt",
- "maps-searchmarkers-text"
+ "maps-copycoords-prompt"
]
},
"ext.maps.leaflet.fullscreen": {
"dependencies": [
- "ext.maps.leaflet.base"
+ "ext.maps.leaflet.library"
],
"scripts": [
- "leaflet/leaflet.fullscreen/Control.FullScreen.js"
+ "lib/leaflet.fullscreen/Control.FullScreen.js"
],
"styles": [
- "leaflet/leaflet.fullscreen/Control.FullScreen.css"
+ "lib/leaflet.fullscreen/Control.FullScreen.css"
]
},
"ext.maps.leaflet.markercluster": {
"dependencies": [
- "ext.maps.leaflet.base"
+ "ext.maps.leaflet.library"
],
"scripts": [
- "leaflet/leaflet.markercluster/leaflet.markercluster.js"
+ "lib/leaflet.markercluster/leaflet.markercluster.js",
+ "leaflet/LeafletCluster.js"
],
"styles": [
- "leaflet/leaflet.markercluster/MarkerCluster.css"
+ "lib/leaflet.markercluster/MarkerCluster.css"
]
},
- "ext.maps.leaflet.providers": {
+ "ext.maps.leaflet.editor": {
"dependencies": [
- "ext.maps.leaflet.base"
+ "ext.maps.leaflet.geojson",
+ "ext.maps.leaflet.library",
+ "mediawiki.api.edit",
+ "mediawiki.jqueryMsg",
+ "ext.maps.leaflet.fullscreen"
],
"scripts": [
- "leaflet/leaflet-providers/leaflet-providers.js"
- ]
- },
-
- "ext.maps.leaflet.editable": {
- "dependencies": [
- "ext.maps.leaflet.base"
+ "lib/leaflet.StyleEditor/javascript/Leaflet.StyleEditor.min.js",
+ "lib/leaflet.draw/leaflet.draw-src.js",
+ "lib/leaflet.EasyButton/easy-button.js",
+ "api.js",
+ "MapSaver.js",
+ "leaflet/LeafletEditor.js"
],
- "scripts": [
- "leaflet/leaflet.editable/Leaflet.Editable.js"
- ]
- },
-
- "ext.maps.leaflet.editor": {
- "dependencies": [
- "ext.maps.leaflet"
+ "styles": [
+ "lib/leaflet.StyleEditor/css/Leaflet.StyleEditor.min.css",
+ "lib/leaflet.draw/leaflet.draw.css",
+ "lib/leaflet.EasyButton/easy-button.css"
],
- "scripts": [
- "leaflet/leaflet.editor.js"
- ]
- },
+ "messages": [
+ "maps-json-editor-button-marker",
+ "maps-json-editor-button-line",
+ "maps-json-editor-button-polygon",
+ "maps-json-editor-button-rectangle",
+ "maps-json-editor-button-circle",
- "ext.maps.leaflet.leafletajax": {
- "dependencies": [
- "ext.maps.leaflet",
- "ext.sm.common"
- ],
- "scripts": [
- "leaflet/ext.sm.leafletajax.js"
+ "maps-json-editor-tooltip-marker",
+ "maps-json-editor-tooltip-line",
+ "maps-json-editor-tooltip-polygon",
+ "maps-json-editor-tooltip-rectangle",
+ "maps-json-editor-tooltip-circle",
+
+ "maps-json-editor-added-marker",
+ "maps-json-editor-added-line",
+ "maps-json-editor-added-polygon",
+ "maps-json-editor-added-rectangle",
+ "maps-json-editor-added-circle",
+
+ "maps-json-editor-edit-removed-shapes",
+ "maps-json-editor-edit-modified",
+ "maps-json-editor-edit-other",
+ "maps-json-editor-edit-failed",
+
+ "maps-json-editor-toolbar-save-title",
+ "maps-json-editor-toolbar-save-text",
+ "maps-json-editor-toolbar-cancel-title",
+ "maps-json-editor-toolbar-cancel-text",
+ "maps-json-editor-toolbar-clear-title",
+ "maps-json-editor-toolbar-clear-text",
+
+ "maps-json-editor-toolbar-button-save",
+ "maps-json-editor-changes-saved",
+
+ "maps-json-editor-toolbar-button-edit",
+ "maps-json-editor-toolbar-button-edit-disabled",
+ "maps-json-editor-toolbar-button-remove",
+ "maps-json-editor-toolbar-button-remove-disabled",
+
+ "maps-editor-edit-geojson"
]
},
"ext.maps.googlemaps3": {
- "dependencies": [
- "ext.maps.common"
- ],
"scripts": [
"GoogleMaps/jquery.googlemap.js",
"GoogleMaps/ext.maps.googlemaps3.js"
@@ -219,7 +220,8 @@
"maps-copycoords-prompt",
"maps-searchmarkers-text",
"maps-fullscreen-button",
- "maps-fullscreen-button-tooltip"
+ "maps-fullscreen-button-tooltip",
+ "maps-kml-parsing-failed"
]
},
@@ -264,13 +266,43 @@
]
},
- "ext.sm.googlemaps3ajax": {
+ "ext.maps.googlemaps3ajax": {
"dependencies": [
"ext.maps.googlemaps3",
"ext.sm.common"
],
"scripts": [
- "GoogleMaps/ext.sm.googlemaps3ajax.js"
+ "GoogleMaps/googlemaps3ajax.js"
+ ]
+ },
+
+ "ext.maps.wikitext.editor": {
+ "dependencies": [
+ "jquery.ui.autocomplete",
+ "jquery.ui.slider",
+ "jquery.ui.dialog"
+ ],
+ "scripts": [
+ "WikitextEditor/js/jquery.miniColors.js",
+ "WikitextEditor/js/mapeditor.iefixes.js",
+ "WikitextEditor/js/mapeditor.js"
+ ],
+ "styles": [
+ "WikitextEditor/css/jquery.miniColors.css",
+ "WikitextEditor/css/mapeditor.css"
+ ],
+ "messages": [
+ "mapeditor-parser-error",
+ "mapeditor-none-text",
+ "mapeditor-done-button",
+ "mapeditor-remove-button",
+ "mapeditor-import-button",
+ "mapeditor-export-button",
+ "mapeditor-import-button2",
+ "mapeditor-select-button",
+ "mapeditor-mapparam-button",
+ "mapeditor-clear-button",
+ "mapeditor-imageoverlay-button"
]
}
},
diff --git a/www/wiki/extensions/Maps/i18n/ar.json b/www/wiki/extensions/Maps/i18n/ar.json
index 3dcd2223..8909c222 100644
--- a/www/wiki/extensions/Maps/i18n/ar.json
+++ b/www/wiki/extensions/Maps/i18n/ar.json
@@ -7,7 +7,7 @@
"ديفيد"
]
},
- "maps-desc": "يسمح بتضمين خرائط ديناميكية إلى صفحات الويكي، كود العناوين وعمليات جغرافية أخرى",
+ "maps-desc": "يسمح بتضمين الخرائط الديناميكية في صفحات الويكي باستخدام خرائط جوجل أو Leaflet، لديه محرر مرئي، يتكامل اختياريا مع ميدياويكي الدلالي، ويدعم GeoJSON ويضيف إمكانيات الترميز الجغرافي.",
"right-geocode": "الترميز الجغرافي",
"action-geocode": "تفعيل الترميز الجغرافي في هذا الويكي",
"maps_map": "خريطة",
@@ -116,9 +116,11 @@
"maps_leaflet": "الطبقة",
"maps-leaflet-par-defzoom": "يسمح بتعيين مستوى التكبير الافتراضي للخريطة.",
"maps-leaflet-par-layers": "الطبقات التي ستكون متاحة في محدد الطبقة، سيتم عرض الطبقة الأولى عند تحميل الخريطة.",
+ "maps-leaflet-par-layers-dark": "الطبقات التي ستكون متاحة في محدد الطبقة عند تنشيط الوضع الغامق، سيتم عرض الطبقة الأولى عند تحميل الخريطة.",
"maps-leaflet-par-overlaylayers": "طبقات التراكب التي سيتم عرضها عند تحميل الخريطة.",
"maps-leaflet-par-maxclusterradius": "الحد الأقصى لنصف القطر الذي سيغطيه العنقود من العلامة المركزية (بالبكسل).",
"maps-leaflet-par-clusterspiderfy": "عند النقر فوق عنقود في مستوى التكبير السفلي سنكبره حتى تتمكن من رؤية كل علاماته.",
+ "maps-leaflet-par-clicktarget": "عند النقر فوق الخريطة، سيتم إرسالك إلى هذا المسار، يتم استبدال %lat% بخط العرض و% بخط الطول",
"maps_click_to_activate": "اضغط لتنشيط الخريطة",
"maps_centred_on": "الخريطة مركزها في $1، $2.",
"maps-par-mappingservice": "يسمح بتعيين خدمة رسم الخرائط التي سيتم استخدامها لإنشاء الخريطة.",
@@ -223,5 +225,43 @@
"validator-type-mapspolygon": "المضلع الجغرافي",
"validator-type-mapspolygon-list": "قائمة المضلعات الجغرافية",
"validator-type-wmsoverlay": "تراكب خدمة خريطة الويب",
- "validator-type-jsonfile": "نص"
+ "maps-json-editor-button-marker": "ضع علامة",
+ "maps-json-editor-button-line": "ارسم خطا",
+ "maps-json-editor-button-polygon": "ارسم مضلعا",
+ "maps-json-editor-button-rectangle": "ضع مستطيلا",
+ "maps-json-editor-button-circle": "ضع دائرة",
+ "maps-json-editor-tooltip-marker": "انقر على الخريطة لوضع العلامة.",
+ "maps-json-editor-tooltip-line": "انقر على الخريطة لرسم خط.",
+ "maps-json-editor-tooltip-polygon": "انقر على الخريطة لرسم مضلع.",
+ "maps-json-editor-tooltip-rectangle": "انقر فوق الخريطة لوضع مستطيل.",
+ "maps-json-editor-tooltip-circle": "انقر على الخريطة لوضع دائرة.",
+ "maps-json-editor-added-marker": "إضافة علامة",
+ "maps-json-editor-added-line": "إضافة خط",
+ "maps-json-editor-added-polygon": "إضافة مضلع",
+ "maps-json-editor-added-rectangle": "إضافة مستطيل",
+ "maps-json-editor-added-circle": "إضافة دائرة",
+ "maps-json-editor-edit-removed-shapes": "إزالة $1 {{PLURAL:$1|شكل|أشكال}}",
+ "maps-json-editor-edit-modified": "تعديل أشكال الخريطة الحالية",
+ "maps-json-editor-edit-other": "تعديل خريطة مرئي",
+ "maps-json-editor-edit-failed": "فشل في حفظ الخريطة",
+ "maps-json-editor-toolbar-save-title": "الخروج من وضع التحرير مع الحفاظ على جميع التغييرات",
+ "maps-json-editor-toolbar-save-text": "تم",
+ "maps-json-editor-toolbar-cancel-title": "الخروج من وضع التحرير، يتجاهل كل التغييرات",
+ "maps-json-editor-toolbar-cancel-text": "إلغاء",
+ "maps-json-editor-toolbar-clear-title": "مسح كل الطبقات",
+ "maps-json-editor-toolbar-clear-text": "مسح الكل",
+ "maps-json-editor-toolbar-button-edit": "عدل الطبقات",
+ "maps-json-editor-toolbar-button-edit-disabled": "لا طبقات للتعديل",
+ "maps-json-editor-toolbar-button-remove": "حذف الطبقات",
+ "maps-json-editor-toolbar-button-remove-disabled": "لا طبقات للحذف",
+ "maps-json-editor-toolbar-button-save": "حفظ التغييرات",
+ "maps-json-editor-changes-saved": "تم حفظ تعديلاتك.",
+ "maps-geo-json-edit-source": "عدل المصدر",
+ "maps-geo-json-create-source": "إنشاء من مصدر GeoJSON",
+ "maps-geo-json-create-page-button": "إنشاء هذه الصفحة",
+ "maps-geo-json-create-page-creating": "جارٍ إنشاء الصفحة...",
+ "maps-geo-json-create-page-summary": "إنشاء صفحة GeoJSON",
+ "maps-editor-edit-geojson": "تحرير طبقة GeoJSON",
+ "tag-maps-visual-edit": "تعديل خريطة مرئي",
+ "tag-maps-visual-edit-description": "تم التعديل باستخدام المحرر المرئي الذي يوفره [https://www.mediawiki.org/wiki/Extension:Maps امتداد الخرائط]"
}
diff --git a/www/wiki/extensions/Maps/i18n/ast.json b/www/wiki/extensions/Maps/i18n/ast.json
index 7f11fc02..afdffa1d 100644
--- a/www/wiki/extensions/Maps/i18n/ast.json
+++ b/www/wiki/extensions/Maps/i18n/ast.json
@@ -113,9 +113,11 @@
"maps_leaflet": "Leaflet",
"maps-leaflet-par-defzoom": "Permite configurar el nivel predetermináu d'ampliación del mapa.",
"maps-leaflet-par-layers": "La capa que va apaecer cuando se cargue'l mapa.",
+ "maps-leaflet-par-layers-dark": "Les capes que tarán disponibles nel selector de capes cuando ta activáu el mou escuru. La primera capa s'amosará al cargar el mapa.",
"maps-leaflet-par-overlaylayers": "Les capas sobrepuestes qu'apaecerán cuando se cargue'l mapa.",
"maps-leaflet-par-maxclusterradius": "El radiu máximu que cubrirá un grupu dende'l marcador central (en pixels).",
"maps-leaflet-par-clusterspiderfy": "Cuando faes click nun grupu al mínimu d'ampliación, espardémoslu pa que puedan vese los marcadores.",
+ "maps-leaflet-par-clicktarget": "Al pulsiar nel mapa, unviaráte a esta URL. %lat% trócase pola llatitú y %long% pola llonxitú",
"maps_click_to_activate": "Calca p'activar el mapa",
"maps_centred_on": "Mapa centráu en $1, $2.",
"maps-par-mappingservice": "Permite configurar el serviciu de mapes que s'usará pa xenerar el mapa.",
@@ -123,7 +125,7 @@
"maps-par-searchmarkers": "Permite buscar marcadores específicos con un campu incrustáu nel mapa.",
"maps-par-zoom": "Nivel de zoom del mapa. Para los mapes con marcadores el valor predetermináu sedrá el nivel máximu d'ampliación qu'amuese tolos marcadores.",
"maps-par-width": "Permite configurar l'anchor del mapa. De mou predetermináu s'asume el pixel como unidá, pero se pue conseñar esplícitamente una d'estes unidaes: px, ex, em, %.",
- "maps-par-height": "Permite configurar l'altor del mapa. De mou predetermináu s'asume el pixel como unidá, pero se pue conseñar esplícitamente una d'estes unidaes: px, ex, em, %.",
+ "maps-par-height": "Permite configurar l'altor del mapa. De mou predetermináu s'asumen los pixeles como unidá, pero se pue conseñar esplícitamente una d'estes unidaes: px, ex, em.",
"maps-par-centre": "El llugar nel que se tien de centrar el mapa",
"maps-par-enable-fullscreen": "Activar el botón de pantalla completa",
"maps-par-kml": "Ficheros KML a cargar nel mapa.",
@@ -219,5 +221,40 @@
"validator-type-mapspolygon": "Polígonu xeográficu",
"validator-type-mapspolygon-list": "Llista de polígonos xeográficos",
"validator-type-wmsoverlay": "Superposición de Serviciu de Mapes web",
- "validator-type-jsonfile": "testu"
+ "maps-json-editor-button-marker": "Asitiar un marcador",
+ "maps-json-editor-button-line": "Trazar una llinia",
+ "maps-json-editor-button-polygon": "Dibuxar un polígonu",
+ "maps-json-editor-button-rectangle": "Asitiar un rectángulu",
+ "maps-json-editor-button-circle": "Asitiar un círculu",
+ "maps-json-editor-tooltip-marker": "Pulsia nel mapa p'asitiar un marcador.",
+ "maps-json-editor-tooltip-line": "Pulsia nel mapa pa trazar una llinia.",
+ "maps-json-editor-tooltip-polygon": "Pulsia nel mapa pa dibuxar un polígonu.",
+ "maps-json-editor-tooltip-rectangle": "Pulsia nel mapa p'asitiar un rectángulu.",
+ "maps-json-editor-tooltip-circle": "Pulsia nel mapa p'asitiar un círculu.",
+ "maps-json-editor-added-marker": "Añadíu marcador",
+ "maps-json-editor-added-line": "Añadida llinia",
+ "maps-json-editor-added-polygon": "Polígonu añadíu",
+ "maps-json-editor-added-rectangle": "Rectángulu añadíu",
+ "maps-json-editor-added-circle": "Círculu añadíu",
+ "maps-json-editor-edit-removed-shapes": "{{PLURAL:$1|Desanicióse $1 forma|Desaniciáronse $1 formes}}",
+ "maps-json-editor-edit-modified": "Camudaron formes esistentes nel mapa",
+ "maps-json-editor-edit-other": "Edición cartográfica visual",
+ "maps-json-editor-edit-failed": "Nun pudo guardase'l mapa",
+ "maps-json-editor-toolbar-save-title": "Guardar los cambios",
+ "maps-json-editor-toolbar-save-text": "Guardar",
+ "maps-json-editor-toolbar-cancel-title": "Encaboxar la edición, descartar tolos cambeos",
+ "maps-json-editor-toolbar-cancel-text": "Encaboxar",
+ "maps-json-editor-toolbar-clear-title": "Llimpiar toles capes",
+ "maps-json-editor-toolbar-clear-text": "Llimpiar too",
+ "maps-json-editor-toolbar-button-edit": "Editar capes",
+ "maps-json-editor-toolbar-button-edit-disabled": "Nun hai capes qu'editar",
+ "maps-json-editor-toolbar-button-remove": "Desaniciar capes",
+ "maps-json-editor-toolbar-button-remove-disabled": "Nun hai capes que desaniciar",
+ "maps-geo-json-edit-source": "Editar la fonte",
+ "maps-geo-json-create-source": "Crear dende códigu GeoJSON",
+ "maps-geo-json-create-page-button": "Crear esta páxina",
+ "maps-geo-json-create-page-creating": "Creando páxina...",
+ "maps-geo-json-create-page-summary": "Creada una páxina en GeoJSON",
+ "tag-maps-visual-edit": "Edición cartográfica visual",
+ "tag-maps-visual-edit-description": "Edición fecha col editor visual proporcionáu pola [https://www.mediawiki.org/wiki/Extension:Maps estensión Maps]"
}
diff --git a/www/wiki/extensions/Maps/i18n/be-tarask.json b/www/wiki/extensions/Maps/i18n/be-tarask.json
index 58b98b66..76db69e0 100644
--- a/www/wiki/extensions/Maps/i18n/be-tarask.json
+++ b/www/wiki/extensions/Maps/i18n/be-tarask.json
@@ -92,7 +92,7 @@
"maps-par-resizable": "Дазваляе зьмяняць памеры мапы, перацягваючы яе ніжні правы кут.",
"maps-par-zoom": "Маштаб мапы. Для мапаў з пазначэньнямі маштаб будзе такім, пры якім яшчэ будуць паказвацца ўсе пазначэньні.",
"maps-par-width": "Дазваляе наладжваць шырыню мапы. Па змоўчваньні піксэлі выкарыстоўваюцца як адзінкі вымярэньня, але Вы можаце непасрэдна вызначыць адну з гэтых адзінак вымярэньня: px, ex, em, %.",
- "maps-par-height": "Дазваляе наладжваць вышыню мапы. Па змоўчваньні піксэлі выкарыстоўваюцца як адзінкі вымярэньня, але Вы можаце непасрэдна вызначыць адну з гэтых адзінак вымярэньня: px, ex, em, %.",
+ "maps-par-height": "Дазваляе наладжваць вышыню мапы. Па змоўчваньні піксэлі выкарыстоўваюцца як адзінкі вымярэньня, але Вы можаце непасрэдна вызначыць адну з гэтых адзінак вымярэньня: px, ex, em.",
"maps-par-kml": "KML-файлы для загрузкі ў мапу.",
"maps-googlemaps3-incompatbrowser": "Ваш браўзэр не сумяшчальны з Google Maps v3.",
"maps-googlemaps3-par-type": "Тып мапы, які будзе паказвацца ў пачатку.",
diff --git a/www/wiki/extensions/Maps/i18n/bn.json b/www/wiki/extensions/Maps/i18n/bn.json
index 891ea3e4..105052c6 100644
--- a/www/wiki/extensions/Maps/i18n/bn.json
+++ b/www/wiki/extensions/Maps/i18n/bn.json
@@ -5,7 +5,8 @@
"Wikitanvir",
"Aftabuzzaman",
"আজিজ",
- "আফতাবুজ্জামান"
+ "আফতাবুজ্জামান",
+ "Kupulak"
]
},
"right-geocode": "জিওকোড",
@@ -110,5 +111,7 @@
"validator-type-mapsrectangle": "ভৌগলিক আয়তক্ষেত্র",
"validator-type-mapsrectangle-list": "আয়তক্ষেত্রের তালিকা",
"validator-type-mapspolygon": "ভৌগলিক বহুভুজ",
- "validator-type-mapspolygon-list": "ভৌগলিক বহুভুজের তালিকা"
+ "validator-type-mapspolygon-list": "ভৌগলিক বহুভুজের তালিকা",
+ "maps-json-editor-toolbar-save-text": "সম্পন্ন",
+ "maps-json-editor-toolbar-button-save": "পরিবর্তন সংরক্ষণ করুন"
}
diff --git a/www/wiki/extensions/Maps/i18n/de.json b/www/wiki/extensions/Maps/i18n/de.json
index 71905a10..a7962575 100644
--- a/www/wiki/extensions/Maps/i18n/de.json
+++ b/www/wiki/extensions/Maps/i18n/de.json
@@ -226,6 +226,5 @@
"validator-type-mapsrectangle-list": "Liste der Rechtecke",
"validator-type-mapspolygon": "Geografisches Vieleck",
"validator-type-mapspolygon-list": "Liste geografischer Vielecke",
- "validator-type-wmsoverlay": "Web-Map-Service-Überlagerung",
- "validator-type-jsonfile": "text"
+ "validator-type-wmsoverlay": "Web-Map-Service-Überlagerung"
}
diff --git a/www/wiki/extensions/Maps/i18n/el.json b/www/wiki/extensions/Maps/i18n/el.json
index 755e14b0..1c0c2216 100644
--- a/www/wiki/extensions/Maps/i18n/el.json
+++ b/www/wiki/extensions/Maps/i18n/el.json
@@ -160,6 +160,5 @@
"semanticmaps-kml-linkabsolute": "Να είναι οι σύνδεσμοι απόλυτοι (ως αντιπαράθεση με τους σχετικούς)",
"semanticmaps-kml-pagelinktext": "Το κείμενο που θα χρησιμοποιείται για τους συνδέσμους προς τη σελίδα, στο οποίο το $1 θα αντικαθίσταται από τον τίτλο της σελίδας",
"semanticmaps-shapes-improperformat": "Εσφαλμένη μορφοποίηση του $1, ανατρέξτε στην τεκμηρίωση περί μορφοποίησης",
- "semanticmaps-shapes-missingshape": "Δεν βρέθηκαν σχήματα για το $1, ανατρέξτε στην τεκμηρίωση για διαθέσιμα σχήματα",
- "validator-type-jsonfile": "κείμενο"
+ "semanticmaps-shapes-missingshape": "Δεν βρέθηκαν σχήματα για το $1, ανατρέξτε στην τεκμηρίωση για διαθέσιμα σχήματα"
}
diff --git a/www/wiki/extensions/Maps/i18n/en.json b/www/wiki/extensions/Maps/i18n/en.json
index 1b5fa3b2..7eb770f6 100644
--- a/www/wiki/extensions/Maps/i18n/en.json
+++ b/www/wiki/extensions/Maps/i18n/en.json
@@ -5,7 +5,7 @@
"Karsten Hoffmeyer (kghbln)"
]
},
- "maps-desc": "Enables embedding of dynamic maps into wiki pages, geocoding of addresses and other geographical operations",
+ "maps-desc": "Allows embedding of dynamic maps into wiki pages using Google Maps or Leaflet. Has a visual editor, optionally integrates with Semantic MediaWiki, supports GeoJSON and adds geocoding capabilities.",
"right-geocode": "Geocode",
"action-geocode": "do geocoding on this wiki",
"maps_map": "Map",
@@ -114,9 +114,11 @@
"maps_leaflet": "Leaflet",
"maps-leaflet-par-defzoom": "Allows setting the default zoom level of the map.",
"maps-leaflet-par-layers": "The layers that will be available in the layer selector. The first layer will be shown when the map loads.",
+ "maps-leaflet-par-layers-dark": "The layers that will be available in the layer selector when a dark mode is activated. The first layer will be shown when the map loads.",
"maps-leaflet-par-overlaylayers": "The overlay layers that will be shown when the map loads.",
"maps-leaflet-par-maxclusterradius": "The maximum radius that a cluster will cover from the central marker (in pixels).",
"maps-leaflet-par-clusterspiderfy": "When you click a cluster at the bottom zoom level we spiderfy it so you can see all of its markers.",
+ "maps-leaflet-par-clicktarget": "When clicking on the map you will be send to this URL. %lat% is replaced by the latitude and %long% by the longitude",
"maps_click_to_activate": "Click to activate map",
"maps_centred_on": "Map centered on $1, $2.",
"maps-par-mappingservice": "Allows setting the mapping service that will be used to generate the map.",
@@ -221,5 +223,53 @@
"validator-type-mapspolygon": "Geographical polygon",
"validator-type-mapspolygon-list": "List of geographical polygons",
"validator-type-wmsoverlay": "Web Map Service overlay",
- "validator-type-jsonfile": "text"
+
+ "maps-json-editor-button-marker": "Place a marker",
+ "maps-json-editor-button-line": "Draw a line",
+ "maps-json-editor-button-polygon": "Draw a polygon",
+ "maps-json-editor-button-rectangle": "Place a rectangle",
+ "maps-json-editor-button-circle": "Place a circle",
+
+ "maps-json-editor-tooltip-marker": "Click map to place marker.",
+ "maps-json-editor-tooltip-line": "Click map to draw line.",
+ "maps-json-editor-tooltip-polygon": "Click map to draw polygon.",
+ "maps-json-editor-tooltip-rectangle": "Click map to place rectangle.",
+ "maps-json-editor-tooltip-circle": "Click map to place circle.",
+
+ "maps-json-editor-added-marker": "Added marker",
+ "maps-json-editor-added-line": "Added line",
+ "maps-json-editor-added-polygon": "Added polygon",
+ "maps-json-editor-added-rectangle": "Added rectangle",
+ "maps-json-editor-added-circle": "Added circle",
+
+ "maps-json-editor-edit-removed-shapes": "Removed $1 {{PLURAL:$1|shape|shapes}}",
+ "maps-json-editor-edit-modified": "Modified existing map shapes",
+ "maps-json-editor-edit-other": "Visual map edit",
+ "maps-json-editor-edit-failed": "Failed to save the map",
+
+ "maps-json-editor-toolbar-save-title": "Exit edit mode while keeping all changes",
+ "maps-json-editor-toolbar-save-text": "Done",
+ "maps-json-editor-toolbar-cancel-title": "Exit edit mode, discards all changes",
+ "maps-json-editor-toolbar-cancel-text": "Cancel",
+ "maps-json-editor-toolbar-clear-title": "Clear all layers",
+ "maps-json-editor-toolbar-clear-text": "Clear All",
+
+ "maps-json-editor-toolbar-button-edit": "Edit layers",
+ "maps-json-editor-toolbar-button-edit-disabled": "No layers to edit",
+ "maps-json-editor-toolbar-button-remove": "Delete layers",
+ "maps-json-editor-toolbar-button-remove-disabled": "No layers to delete",
+
+ "maps-json-editor-toolbar-button-save": "Save changes",
+ "maps-json-editor-changes-saved": "Your changes have been saved",
+
+ "maps-geo-json-edit-source": "Edit source",
+ "maps-geo-json-create-source": "Create from GeoJSON source",
+ "maps-geo-json-create-page-button": "Create this page",
+ "maps-geo-json-create-page-creating": "Creating page...",
+ "maps-geo-json-create-page-summary": "Created GeoJSON page",
+
+ "maps-editor-edit-geojson": "Edit GeoJSON layer",
+
+ "tag-maps-visual-edit": "Visual map edit",
+ "tag-maps-visual-edit-description": "Edit made with the visual editor provided by the [https://www.mediawiki.org/wiki/Extension:Maps Maps extension]"
}
diff --git a/www/wiki/extensions/Maps/i18n/es.json b/www/wiki/extensions/Maps/i18n/es.json
index e9eaa1ed..fa61b971 100644
--- a/www/wiki/extensions/Maps/i18n/es.json
+++ b/www/wiki/extensions/Maps/i18n/es.json
@@ -25,7 +25,10 @@
"Rubentl134",
"AlvaroMolina",
"Dgstranz",
- "Carlosmg.dg"
+ "Carlosmg.dg",
+ "Madamebiblio",
+ "Hasley",
+ "Ihojose"
]
},
"maps-desc": "Habilita la inserción de mapas dinámicos en páginas wikis, la geocodificación de direcciones y otras operaciones geográficas",
@@ -229,5 +232,27 @@
"validator-type-mapsrectangle-list": "Lista de rectángulos",
"validator-type-mapspolygon": "Polígono geográfico",
"validator-type-mapspolygon-list": "Lista de polígonos geográficos",
- "validator-type-jsonfile": "texto"
+ "maps-json-editor-button-line": "Trazar una línea",
+ "maps-json-editor-button-polygon": "Trazar un polígono",
+ "maps-json-editor-added-polygon": "Polígono añadido",
+ "maps-json-editor-added-rectangle": "Rectángulo añadido",
+ "maps-json-editor-added-circle": "Círculo añadido",
+ "maps-json-editor-edit-other": "Edición cartográfica visual",
+ "maps-json-editor-toolbar-save-title": "Salir del modo edición mientras se mantienen todos los cambios",
+ "maps-json-editor-toolbar-save-text": "Hecho",
+ "maps-json-editor-toolbar-cancel-title": "Salir del modo edición, descartar todos los cambios",
+ "maps-json-editor-toolbar-cancel-text": "Cancelar",
+ "maps-json-editor-toolbar-clear-title": "Borrar todas las capas",
+ "maps-json-editor-toolbar-button-edit": "Editar capas",
+ "maps-json-editor-toolbar-button-remove": "Eliminar capas",
+ "maps-json-editor-toolbar-button-remove-disabled": "No hay capas que eliminar",
+ "maps-json-editor-toolbar-button-save": "Guardar cambios",
+ "maps-json-editor-changes-saved": "Tus cambios han sido guardados",
+ "maps-geo-json-edit-source": "Editar código",
+ "maps-geo-json-create-source": "Crear a partir de código GeoJSON",
+ "maps-geo-json-create-page-button": "Crear esta página",
+ "maps-geo-json-create-page-creating": "Creando página…",
+ "maps-geo-json-create-page-summary": "Se creó una página en GeoJSON",
+ "maps-editor-edit-geojson": "Editar capa GeoJSON",
+ "tag-maps-visual-edit": "Edición cartográfica visual"
}
diff --git a/www/wiki/extensions/Maps/i18n/fi.json b/www/wiki/extensions/Maps/i18n/fi.json
index 516c2703..3b864dbf 100644
--- a/www/wiki/extensions/Maps/i18n/fi.json
+++ b/www/wiki/extensions/Maps/i18n/fi.json
@@ -10,7 +10,8 @@
"ZeiP",
"Macofe",
"Pahkiqaz",
- "Pyscowicz"
+ "Pyscowicz",
+ "Silvonen"
]
},
"maps-desc": "Mahdollistaa dynaamisten karttojen upottamisen wikisivuille, osoitteiden geokoodauksen ja muita karttoihin liittyviä toimintoja",
@@ -154,5 +155,9 @@
"semanticmaps-kml-pagelinktext": "Sivulinkeissä käytettävä teksti, jossa $1 korvataan sivun otsikolla",
"semanticmaps-shapes-improperformat": "$1 on muotoiltu väärin. Katso muotoilun dokumentaatiota.",
"semanticmaps-shapes-missingshape": "$1: muotoja ei löytynyt. Dokumentaatiossa kerrotaan sallituista muodoista.",
- "validator-type-mapslocation-list": "Luettelo sijainneista"
+ "validator-type-mapslocation-list": "Luettelo sijainneista",
+ "maps-json-editor-button-line": "Piirrä viiva",
+ "maps-json-editor-toolbar-save-title": "Tallenna muutokset",
+ "maps-json-editor-toolbar-save-text": "Tallenna",
+ "maps-json-editor-toolbar-clear-text": "Tyhjennä kaikki"
}
diff --git a/www/wiki/extensions/Maps/i18n/fr.json b/www/wiki/extensions/Maps/i18n/fr.json
index b1984cc9..b89b7f59 100644
--- a/www/wiki/extensions/Maps/i18n/fr.json
+++ b/www/wiki/extensions/Maps/i18n/fr.json
@@ -22,156 +22,160 @@
"Trial",
"Wladek92",
"Urhixidur",
- "Grondin"
+ "Grondin",
+ "Thibaut120094",
+ "Pols12"
]
},
- "maps-desc": "Permet d’inclure des cartes dynamiques dans les pages du wiki, des adresses géo-codées et d'autres opérations géographiques.",
- "right-geocode": "Géocode",
+ "maps-desc": "Permet d’inclure des cartes dynamiques dans les pages du wiki, des adresses géocodées et d'autres opérations géographiques.",
+ "right-geocode": "Géocoder",
"action-geocode": "géocoder sur ce wiki",
"maps_map": "Carte",
- "maps-tracking-category": "Pages avec une carte provenant de l’extension Maps",
+ "maps-tracking-category": "Pages avec une carte rendue par l’extension Maps",
"maps-loading-map": "Chargement de la carte...",
- "maps-load-failed": "Impossible de charger la carte !",
+ "maps-load-failed": "Impossible de charger la carte !",
"maps-markers": "Marqueurs",
"maps-copycoords-prompt": "CTRL+C, ENTRÉE",
"maps-searchmarkers-text": "Marqueurs de filtre",
"maps-others": "autres",
- "maps-kml-parsing-failed": "Échec de l’analyse d’un ou plusieurs fichiers KML, le plus souvent à cause d’échecs de récupération ou de XML mal formé.",
+ "maps-kml-parsing-failed": "Échec de l’analyse d’un ou plusieurs fichiers KML. Survient habituellement à cause d’un échec de récupération ou d’un codage XML mal formé.",
"maps-ns-layer": "Couche",
"maps-ns-layer-talk": "Page de discussion des couches",
"maps-layer-property": "Propriété",
"maps-layer-value": "Valeur",
"maps-layer-errors": "Erreurs",
- "maps-layerdef-invalid": "{{PLURAL:$1|Définition non valide|Définitions non valides}}",
+ "maps-layerdef-invalid": "Définition{{PLURAL:$1||s}} non valide{{PLURAL:$1||s}}",
"maps-layerdef-invalid-fatal": "Définition non valide fatale",
"maps-layerdef-wrong-namespace": "Les définitions de couche ne sont valides que sur les pages de l’espace de noms « $1 »",
"maps-layerdef-equal-layer-name": "Les noms de couche doivent être uniques dans la même page de couche. « $1 » est déjà utilisé par une autre couche.",
"maps-layerpage-usage": "Pages avec des cartes utilisant la couche « $1 »",
"maps-layerpage-nousage": "Aucune page n’utilise cette couche pour l’instant.",
- "maps-error-invalid-layertype": "Il n’y a pas de couche de type « $1 ». Seul {{PLURAL:$3|ce type est|ces types sont}} pris en charge : $2",
- "maps-error-no-layertype": "Vous devez spécifier le type de couche. {{PLURAL:$2|Seul ce type est|Ces types sont}} pris en charge : $1",
- "validation-error-invalid-layer": "Le paramètre $1 doit être une couche valide.",
- "validation-error-invalid-layers": "Le paramètre $1 doit être une ou plusieurs couche(s) valide(s).",
- "validation-error-no-non-numeric": "Le paramètre « $1 » doit être une chaîne non numérique.",
- "validation-error-no-non-numerics": "Le paramètre « $1 » doit être une ou plusieurs chaînes non numériques.",
- "maps-layer-of-type": "Couche de type « $1 »",
+ "maps-error-invalid-layertype": "Il n’y a aucune couche de type « $1 ». Seul {{PLURAL:$3|ce type est|ces types sont}} pris en charge : $2",
+ "maps-error-no-layertype": "Vous devez spécifier le type de couche. {{PLURAL:$2|Seul ce type est|Les types suivants sont}} pris en charge : $1",
+ "validation-error-invalid-layer": "Le paramètre « $1 » doit indiquer une couche valide.",
+ "validation-error-invalid-layers": "Le paramètre « $1 » doit indiquer une ou plusieurs couches valides.",
+ "validation-error-no-non-numeric": "Le paramètre « $1 » doit indiquer une chaîne non numérique.",
+ "validation-error-no-non-numerics": "Le paramètre « $1 » doit indiquer une ou plusieurs chaînes non numériques.",
+ "maps-layer-of-type": "Couche de type « $1 »",
"maps-layer-of-type-and-name": "Couche « $2 » de type « $1 »",
- "maps-layer-type-supported-by": "Ce type de couche peut {{PLURAL:$2|être utilisé uniquement avec le service de cartographie $1|être utilisé avec les services de cartographie suivants : $1}}.",
+ "maps-layer-type-supported-by": "Ce type de couche peut être utilisé avec {{PLURAL:$2|le service|les services}} de cartographie {{PLURAL:$2| $1 uniquement|suivants : $1}}.",
"maps-coordinates-description": "Crochet de l’analyseur syntaxique pour formater les coordonnées, depuis et vers n’importe quel format pris en charge.",
"maps-displaymap-description": "Affiche les cartes géographiques sans aucun marqueur wiki associé.",
- "maps-distance-description": "Convertit une distance d'une certaine unité prise en charge à son équivalent utilisant une autre unité.",
- "maps-finddestination-description": "Trouver une destination à partir d'un point de départ donné (qui peut être dans n’importe lequel des formats pris en charge), d'une orientation initiale et d'une distance.",
- "maps-geocode-description": "Permet le géocodage d'adresses, en d'autres termes, la transformation des positions humainement lisible en ensembles de coordonnées. Plusieurs services de géocodage sont pris en charge, qui ne doivent pas être confondus avec les services de cartographie.",
- "maps-geodistance-description": "Calculer la distance géographique entre deux points, depuis et vers n'importe quel format pris en charge.",
+ "maps-distance-description": "Convertit une distance exprimée dans une certaine unité prise en charge à son équivalent exprimée dans une autre unité.",
+ "maps-finddestination-description": "Trouver une destination à partir d’un point de départ donné (qui peut être dans n’importe lequel des formats pris en charge), d’une orientation initiale et d’une distance.",
+ "maps-geocode-description": "Permet le géocodage d’adresses, en d’autres termes, la transformation de positions humainement lisibles en ensembles de coordonnées. Plusieurs services de géocodage sont pris en charge, qui ne doivent pas être confondus avec les services de cartographie.",
+ "maps-geodistance-description": "Calculer la distance géographique entre deux points, depuis et vers n’importe quel format pris en charge.",
"maps-mapsdoc-description": "Affiche une table avec les paramètres pour un service de cartographie spécifié, avec leurs valeurs par défaut et leur description.",
"maps-layerdefinition-description": "Décrit une couche personnalisée qui peut être affichée avec d’autres fonctions de carte.",
"maps-mapsdoc-par-service": "Le service de cartographie pour afficher la documentation concernant les paramètres.",
- "maps-mapsdoc-par-language": "La langue dans laquelle afficher la documentation. Si aucune traduction n'est disponible, l'anglais sera utilisé à la place.",
+ "maps-mapsdoc-par-language": "La langue dans laquelle afficher la documentation. Si aucune traduction n’est disponible, la documentation en anglais sera utilisée en repli.",
"maps-coordinates-par-location": "Les coordonnées que vous souhaitez formater.",
"maps-coordinates-par-format": "Le format cible pour les coordonnées.",
- "maps-coordinates-par-directional": "Indique si les coordonnées doivent être imprimées directionnellement ou non.",
+ "maps-coordinates-par-directional": "Indique si les coordonnées doivent être affichées de façon directionnelle ou non.",
"maps-par-scrollwheelzoom": "Indique si le défilement à la souris doit être ou non activé.",
- "maps-distance-par-distance": "La distance à convertir dans son équivalent avec une unité spécifiée.",
+ "maps-distance-par-distance": "La distance à convertir dans son équivalent exprimé dans une unité spécifiée.",
"maps-distance-par-decimals": "Le nombre maximal de chiffres après la virgule à utiliser dans le résultat.",
- "maps-distance-par-unit": "L'unité utilisée pour afficher la distance.",
- "maps-finddestination-par-location": "L'emplacement initial.",
+ "maps-distance-par-unit": "L’unité utilisée pour afficher la distance.",
+ "maps-finddestination-par-location": "L’emplacement initial.",
"maps-finddestination-par-bearing": "La direction initiale.",
"maps-finddestination-par-distance": "La distance à parcourir.",
"maps-finddestination-par-format": "Le format dans lequel afficher la destination.",
"maps-finddestination-par-directional": "Indique si le format de destination doit être directionnel ou non.",
- "maps-geocode-par-location": "L'adresse que vous souhaitez géocoder.",
+ "maps-geocode-par-location": "L’adresse que vous souhaitez géocoder.",
"maps-geocode-par-format": "Le format des coordonnées qui en résultent.",
- "maps-geocode-par-directional": "Indique si les coordonnées doivent être imprimées directionnellement ou non.",
+ "maps-geocode-par-directional": "Indique si les coordonnées doivent être affichées de façon directionnelle ou non.",
"maps-geodistance-par-location1": "Le premier point dans le jeu pour à utiliser pour calculer la distance.",
"maps-geodistance-par-location2": "Le deuxième point dans le jeu pour à utiliser pour calculer la distance.",
- "maps-geodistance-par-unit": "L'unité utilisée pour afficher la distance.",
- "maps-geodistance-par-decimals": "Le nombre maximal de chiffres après la virgule à utiliser dans le résultat.",
+ "maps-geodistance-par-unit": "L’unité utilisée pour afficher la distance.",
+ "maps-geodistance-par-decimals": "Le nombre maximal de chiffres après la virgule à utiliser dans la valeur en résultat.",
"maps-displaymap-par-mappingservice": "Permet de définir le service de cartographie qui sera utilisé pour générer la carte.",
- "maps-displaymap-par-coordinates": "Un ou plusieurs emplacements à afficher sur la carte. Ils seront représentés par un indicateur.",
- "maps-displaymap-par-visitedicon": "Le nom de fichier d’une image à utiliser comme icône de marquage une fois que les marqueurs d’origine auront été cliqués",
- "maps-displaymap-par-zoom": "Permet de définir le niveau de zoom de la carte.\nLorsqu'il n'est pas fourni et que plusieurs marqueurs sont présents sur la carte, le meilleur zoom sera pris, mais non pas la valeur par défaut configurable.",
- "maps-displaymap-par-centre": "Permet de définir les coordonnées du centre de la carte pour display_point(s).\nAccepte les adresses et les coordonnées.\nLorsque cette propriété n'est pas fournie, la carte se recentre sur le marqueur fourni, ou entre les marqueurs fournis.",
- "maps-displaymap-par-title": "Permet de définir le texte qui sera affiché dans les pop-ups de tous les marqueurs qui ne disposent pas d'un titre spécifique.\nLorsqu'il est utilisé avec le label, le titre sera en gras et souligné.",
- "maps-displaymap-par-label": "Permet de définir le texte qui sera affiché dans les fenêtres surgissantes de tous les marqueurs qui n'ont pas d'étiquette spécifique.",
- "maps-displaymap-par-icon": "Permet de définir l'icône utilisée pour tous les marqueurs.",
+ "maps-displaymap-par-coordinates": "Un ou plusieurs emplacements à afficher sur la carte. Ils seront représentés par un marqueur.",
+ "maps-displaymap-par-visitedicon": "Le nom de fichier d’une image à utiliser comme icônes de marqueur après avoir cliqué les marqueurs d’origine",
+ "maps-displaymap-par-zoom": "Permet de définir le niveau de zoom de la carte.\nLorsqu’il n'est pas fourni et que plusieurs marqueurs sont présents sur la carte, le meilleur zoom sera pris, mais non pas la valeur par défaut configurable.",
+ "maps-displaymap-par-centre": "Permet de définir les coordonnées du centre de la carte pour display_point(s).\nAccepte aussi bien des adresses que des coordonnées.\nLorsque cette propriété n’est pas fournie, la carte se recentrera sur le marqueur fourni ou entre les marqueurs fournis.",
+ "maps-displaymap-par-title": "Permet de définir le texte qui sera affiché dans les bulles contextuelles de tous les marqueurs qui ne disposent pas d’un titre spécifique.\nLorsqu’il est utilisé avec le libellé, le titre sera en gras et souligné.",
+ "maps-displaymap-par-label": "Permet de définir le texte qui sera affiché dans les bulles contextuelles de tous les marqueurs qui n’ont pas de libellé spécifique.",
+ "maps-displaymap-par-icon": "Permet de définir l’icône utilisée pour tous les marqueurs.",
"maps-displaymap-par-circles": "Cercles à afficher",
- "maps-displaymap-par-copycoords": "Afficher une boîte de dialogue lors du clic sur un emplacement depuis lequel ses coordonnées peuvent être copiées.",
+ "maps-displaymap-par-copycoords": "Afficher une boîte de dialogue lors du clic sur un emplacement, depuis laquelle ses coordonnées peuvent être copiées.",
"maps-displaymap-par-lines": "Lignes à afficher",
- "maps-displaymap-par-maxzoom": "Le niveau maximal de grossissement",
- "maps-displaymap-par-minzoom": "Le niveau minimal de grossissement",
+ "maps-displaymap-par-maxzoom": "Le niveau maximal d’agrandissement",
+ "maps-displaymap-par-minzoom": "Le niveau minimal d’agrandissement",
"maps-displaymap-par-polygons": "Polygones à afficher",
"maps-displaymap-par-rectangles": "Rectangles à afficher",
"maps-displaymap-par-static": "Rendre la carte statique",
- "maps-displaymap-par-wmsoverlay": "Utiliser une superposition WMS",
+ "maps-displaymap-par-wmsoverlay": "Utiliser une surcouche WMS",
"maps-displaymap-par-geojson": "URL de fichier ou nom de page contenant des données GeoJSON",
"maps-fullscreen-button": "Basculer en plein écran",
"maps-fullscreen-button-tooltip": "Visualiser la carte en plein écran ou incorporé.",
- "validation-error-invalid-location": "Le paramètre « $1 » doit être un emplacement valide.",
- "validation-error-invalid-locations": "Le paramètre « $1 » doit être un ou plusieurs emplacement(s) valide(s).",
- "validation-error-invalid-width": "Le paramètre « $1 » doit être une largeur valide.",
- "validation-error-invalid-height": "Le paramètre « $1 » doit être une hauteur valide.",
- "validation-error-invalid-distance": "Le paramètre « $1 » doit être une distance valide.",
- "validation-error-invalid-distances": "Le paramètre « $1 » doit être une ou plusieurs distance(s) valide(s).",
- "validation-error-invalid-image": "Le paramètre « $1 » doit être une image valide.",
- "validation-error-invalid-images": "Le paramètre « $1 » doit être une ou plusieurs image(s) valide(s).",
- "validation-error-invalid-goverlay": "Le paramètre « $1 » doit être un recouvrement valide.",
- "validation-error-invalid-goverlays": "Le paramètre « $1 » doit être un ou plusieurs recouvrement(s) valide(s).",
+ "validation-error-invalid-location": "Le paramètre « $1 » doit indiquer un emplacement valide.",
+ "validation-error-invalid-locations": "Le paramètre « $1 » doit indiquer un ou plusieurs emplacements valides.",
+ "validation-error-invalid-width": "Le paramètre « $1 » doit indiquer une largeur valide.",
+ "validation-error-invalid-height": "Le paramètre « $1 » doit indiquer une hauteur valide.",
+ "validation-error-invalid-distance": "Le paramètre « $1 » doit indiquer une distance valide.",
+ "validation-error-invalid-distances": "Le paramètre « $1 » doit indiquer une ou plusieurs distances valides.",
+ "validation-error-invalid-image": "Le paramètre « $1 » doit indiquer une image valide.",
+ "validation-error-invalid-images": "Le paramètre « $1 » doit indiquer une ou plusieurs images valides.",
+ "validation-error-invalid-goverlay": "Le paramètre « $1 » doit indiquer une surcouche valide.",
+ "validation-error-invalid-goverlays": "Le paramètre « $1 » doit indiquer une ou plusieurs surcouches valides.",
"maps-abb-north": "N",
"maps-abb-east": "E",
"maps-abb-south": "S",
"maps-abb-west": "O",
- "maps-latitude": "Latitude :",
- "maps-longitude": "Longitude :",
- "maps-invalid-coordinates": "La valeur $1 n'a pas été reconnue comme un ensemble valide de coordonnées.",
- "maps_coordinates_missing": "Aucune coordonnée n'a été fournie pour le plan.",
- "maps_geocoding_failed": "{{PLURAL:$2|L′adresse suivante n'as pu être géocodée|Les adresses suivantes n'ont pas pu être géocodées}} : $1.",
- "maps_geocoding_failed_for": "{{PLURAL:$2|L′adresse suivante n’as pu être géocodée|Les adresses suivantes n’ont pas pu être géocodées}} et {{PLURAL:$2|n’est pas affichée|ne sont pas affichées}} sur le plan : \n$1",
- "maps_unrecognized_coords": "{{PLURAL:$2|La coordonnée suivante n'a pas été reconnue|Les coordonnées suivantes n'ont pas été reconnues}} : $1.",
- "maps_unrecognized_coords_for": "{{PLURAL:$2|La coordonnée suivante n'a pas été reconnue|Les coordonnées suivantes n'ont pas été reconnues}} et {{PLURAL:$2|a été omise|ont été omises}} sur la carte :\n$1",
+ "maps-latitude": "Latitude :",
+ "maps-longitude": "Longitude :",
+ "maps-invalid-coordinates": "La valeur $1 n’a pas été reconnue comme un ensemble valide de coordonnées.",
+ "maps_coordinates_missing": "Aucune coordonnée n’a été fournie pour la carte.",
+ "maps_geocoding_failed": "{{PLURAL:$2|L’adresse suivante n’a pas pu être géocodée|Les adresses suivantes n’ont pas pu être géocodées}} : $1.",
+ "maps_geocoding_failed_for": "{{PLURAL:$2|L′adresse suivante n’a pas pu être géocodée|Les adresses suivantes n’ont pas pu être géocodées}} et {{PLURAL:$2|n’est pas affichée|ne sont pas affichées}} sur la carte :\n$1",
+ "maps_unrecognized_coords": "{{PLURAL:$2|La coordonnée suivante n’a pas été reconnue|Les coordonnées suivantes n’ont pas été reconnues}} : $1.",
+ "maps_unrecognized_coords_for": "{{PLURAL:$2|La coordonnée suivante n’a pas été reconnue|Les coordonnées suivantes n’ont pas été reconnues}} et {{PLURAL:$2|a été omise|ont été omises}} sur la carte :\n$1",
"maps_map_cannot_be_displayed": "La carte ne peut pas être affichée.",
- "maps-geocoder-not-available": "La fonctionnalité géocodage des cartes n'est pas disponible. Votre emplacement ne peut être géocodé.",
+ "maps-geocoder-not-available": "La fonctionnalité des Cartes pour le géocodage n’est pas disponible. Votre emplacement ne peut être géocodé.",
"maps_leaflet": "Dépliant",
"maps-leaflet-par-defzoom": "Permet de définir le niveau de zoom par défaut de la carte.",
"maps-leaflet-par-layers": "Les couches qui seront disponibles dans le sélecteur de couche. La première est celle qui sera affichée pendant le chargement de la carte.",
+ "maps-leaflet-par-layers-dark": "Les couches qui seront disponibles dans le sélecteur de couche lorsqu’un mode sombre sera activé. La première couche sera affichée lors du chargement de la carte.",
"maps-leaflet-par-overlaylayers": "Les surcouches qui seront affichées pendant que la carte se charge.",
- "maps-leaflet-par-maxclusterradius": "Rayon maximal qu'un agrégat peut couvrir en partant du marqueur central (en pixels).",
- "maps-leaflet-par-clusterspiderfy": "Lorsque vous cliquez sur un cluster à bas niveau de zoom nous l'explicitons afin que vous puissiez voir l'ensemble de ses marqueurs.",
+ "maps-leaflet-par-maxclusterradius": "Rayon maximal qu’un agrégat peut couvrir en partant du marqueur central (en pixels).",
+ "maps-leaflet-par-clusterspiderfy": "Lorsque vous cliquez sur un agrégat à bas niveau de zoom nous l’explicitons afin que vous puissiez voir l’ensemble de ses marqueurs.",
+ "maps-leaflet-par-clicktarget": "En cliquant sur la carte, vous serez redirigé vers cet URL. %lat% est remplacé par la latitude et %long% par la longitude.",
"maps_click_to_activate": "Cliquer pour activer la carte",
"maps_centred_on": "Carte centrée sur $1, $2.",
"maps-par-mappingservice": "Permet de régler le service de cartographie qui sera utilisé pour générer la carte.",
"maps-par-resizable": "Rendre la carte redimensionnable en faisant glisser son coin inférieur droit.",
"maps-par-searchmarkers": "Permet de rechercher des marqueurs spécifiques via un champ inclus dans la carte.",
- "maps-par-zoom": "Le niveau de zoom de la carte. Pour les cartes avec des marqueurs, ceci positionne la valeur par défaut du plus grand zoom qui montre encore tous les marqueurs.",
- "maps-par-width": "Permet de définir la largeur de la carte. Par défaut les pixels seront considérés comme unité, mais vous pouvez spécifier explicitement une de ces unités : px, ex, em, %.",
- "maps-par-height": "Permet de définir la hauteur de la carte. Par défaut les pixels seront considérés comme unité, mais vous pouvez spécifier explicitement une de ces unités : px, ex, em.",
+ "maps-par-zoom": "Le niveau de zoom de la carte. Pour les cartes avec marqueurs, ceci positionne la valeur par défaut du plus grand zoom qui permet encore de montrer tous les marqueurs.",
+ "maps-par-width": "Permet de définir la largeur de la carte. Par défaut les pixels seront considérés comme unité, mais vous pouvez spécifier explicitement une de ces unités : px, ex, em, %.",
+ "maps-par-height": "Permet de définir la hauteur de la carte. Par défaut les pixels seront considérés comme unité, mais vous pouvez spécifier explicitement une de ces unités : px, ex, em.",
"maps-par-centre": "Le lieu sur lequel la carte devra être centrée",
- "maps-par-enable-fullscreen": "Activer le bouton plein écran",
+ "maps-par-enable-fullscreen": "Activer le bouton du mode plein écran",
"maps-par-kml": "Fichiers KML à charger sur la carte.",
"maps-par-markercluster": "Autoriser la fusion de plusieurs repères à proximité en un seul repère",
- "maps-googlemaps3-incompatbrowser": "Votre navigateur n'est pas compatible avec Google Maps v3.",
- "maps-googlemaps3-par-imageoverlays": "Permet d'ajouter une image à l'emplacement indiqué sur la carte.",
+ "maps-googlemaps3-incompatbrowser": "Votre navigateur n’est pas compatible avec Google Maps v3.",
+ "maps-googlemaps3-par-imageoverlays": "Permet d’ajouter une image à l’emplacement indiqué sur la carte.",
"maps-googlemaps3-par-type": "Le type de carte à afficher initialement.",
"maps-googlemaps3-par-types": "Les types de carte qui seront disponibles via le contrôle de type.",
"maps-googlemaps3-par-layers": "Couches spéciales à charger sur la carte.",
"maps-googlemaps3-par-controls": "Les contrôles à placer sur la carte.",
"maps-googlemaps3-par-zoomstyle": "Style du contrôle de zoom.",
"maps-googlemaps3-par-typestyle": "Style du contrôle de type.",
- "maps-googlemaps3-par-autoinfowindows": "Ouvrir automatiquement toutes les fenêtres d'information après le chargement de la page.",
+ "maps-googlemaps3-par-autoinfowindows": "Ouvrir automatiquement toutes les fenêtres d’information après le chargement de la page.",
"maps-googlemaps3-par-gkml": "Les fichiers KML hébergés par Google à charger sur la carte.",
"maps-googlemaps3-par-kmlrezoom": "Zoomer de nouveau la carte une fois que les couches KML ont été chargées.",
"maps-googlemaps3-par-poi": "Afficher les points d’intérêt.",
- "maps-googlemaps3-par-clustergridsize": "La taille de la grille (en pixels) d'un agrégat.",
+ "maps-googlemaps3-par-clustergridsize": "La taille de la grille (en pixels) d’un agrégat.",
"maps-par-clustermaxzoom": "Niveau maximal de zoom pour lequel des agrégats peuvent exister.",
- "maps-par-clusterzoomonclick": "Si le comportement par défaut de cliquer sur un agrégat est de zoomer.",
- "maps-par-maxclusterradius": "Le rayon maximal que ce groupe couvrira.",
- "maps-googlemaps3-par-clusteraveragecenter": "Si le centre de chaque groupe est le barycentre des marqueurs de l'agrégat.",
- "maps-googlemaps3-par-clusterminsize": "Le nombre minimum de marqueurs dans un agrégat avant que les marqueurs ne soient cachés et qu'un compteur ne les remplace.",
+ "maps-par-clusterzoomonclick": "Si le comportement lorsqu’on clique sur un agrégat est par défaut de zoomer dessus.",
+ "maps-par-maxclusterradius": "Le rayon maximal qu’un agrégat couvrira.",
+ "maps-googlemaps3-par-clusteraveragecenter": "Si le centre de chaque agrégat est le barycentre des marqueurs dans l’agrégat.",
+ "maps-googlemaps3-par-clusterminsize": "Le nombre minimum de marqueurs dans un agrégat avant que les marqueurs ne soient cachés et qu’un compteur ne les remplace.",
"mapeditor": "Éditeur de carte",
"specialpages-group-maps": "Cartes",
- "mapeditor-parser-error": "Une erreur s'est produite lors de l'analyse des métadonnées. Entrées de l'utilisateur ignorées.",
+ "mapeditor-parser-error": "Une erreur s’est produite lors de l’analyse des métadonnées. Entrées de l’utilisateur ignorées.",
"mapeditor-none-text": "Aucun",
- "mapeditor-done-button": "Fait",
- "mapeditor-remove-button": "Supprimer",
+ "mapeditor-done-button": "Terminé",
+ "mapeditor-remove-button": "Enlever",
"mapeditor-import-button2": "Importer",
"mapeditor-export-button": "Exporter en code wiki",
"mapeditor-import-button": "Importer à partir de code wiki",
@@ -180,16 +184,16 @@
"mapeditor-clear-button": "Effacer la carte",
"mapeditor-code-title": "Code wiki",
"mapeditor-import-title": "Importer le code wiki",
- "mapeditor-import-note": "Veuillez noter que l'analyseur syntaxique s'attend à un format très strict sur le code wiki. Le code saisi ici doit correspondre au code généré par la fonctionnalité d'exportation.",
+ "mapeditor-import-note": "Veuillez noter que l’analyseur syntaxique n’accepte qu’un format très strict sur le code wiki. Le code saisi ici doit correspondre au code généré par la fonctionnalité d’exportation.",
"mapeditor-form-title": "Modifier les détails",
- "mapeditor-link-title-switcher-popup-text": "Popup avec texte",
+ "mapeditor-link-title-switcher-popup-text": "Bulle contextuelle avec texte",
"mapeditor-link-title-switcher-link-text": "Lien",
"mapeditor-form-field-title": "Titre",
"mapeditor-form-field-text": "Texte",
"mapeditor-form-field-link": "Lien",
"mapeditor-form-field-icon": "Icône",
"mapeditor-form-field-group": "Groupe",
- "mapeditor-form-field-inlinelabel": "Étiquette en ligne",
+ "mapeditor-form-field-inlinelabel": "Libellé en ligne",
"mapeditor-form-field-strokecolor": "Couleur de trait",
"mapeditor-form-field-strokeopacity": "Opacité du trait",
"mapeditor-form-field-strokeweight": "Poids du trait",
@@ -197,47 +201,85 @@
"mapeditor-form-field-fillopcaity": "Opacité de remplissage",
"mapeditor-form-field-showonhover": "Montrer seulement au survol",
"mapeditor-mapparam-title": "Modifier les paramètres de la carte",
- "mapeditor-mapparam-defoption": "-Sélectionnez un paramètre-",
- "mapeditor-imageoverlay-button": "Ajouter la couverture d'image",
+ "mapeditor-mapparam-defoption": "–Sélectionner le paramètre–",
+ "mapeditor-imageoverlay-button": "Ajouter une surcouche d’image",
"mapeditor-form-field-image": "Image",
- "mapeditor-imageoverlay-title": "Détails de la couverture d'image",
+ "mapeditor-imageoverlay-title": "Détails de la surcouche d’image",
"mapeditor-form-field-visitedicon": "Icône visitée",
- "semanticmaps-unrecognizeddistance": "La valeur $1 n'est pas une distance valide.",
+ "semanticmaps-unrecognizeddistance": "La valeur $1 n’est pas une distance valide.",
"semanticmaps-kml-link": "Voir le fichier KML",
"semanticmaps-default-kml-pagelink": "Voir l’article $1",
- "semanticmaps-latitude": "Latitude : $1",
- "semanticmaps-longitude": "Longitude : $1",
- "semanticmaps-altitude": "Altitude : $1",
+ "semanticmaps-latitude": "Latitude : $1",
+ "semanticmaps-longitude": "Longitude : $1",
+ "semanticmaps-altitude": "Altitude : $1",
"semanticmaps-forminput-locations": "Emplacements",
- "semanticmaps-par-staticlocations": "Une liste des endroits à ajouter à la carte avec les données demandées. Comme avec display_points, vous pouvez ajouter un titre, une description et une icône par emplacement en utilisant le tilde « ~ » comme séparateur.",
- "semanticmaps-par-showtitle": "Afficher un titre dans la fenêtre d'informations des marqueurs ou non. La désactivation de ceci est souvent utile lorsque vous utilisez un modèle pour formater le contenu de la fenêtre d'informations.",
+ "semanticmaps-par-staticlocations": "Une liste de lieux à ajouter à la carte avec les données demandées. Comme avec « display_points », vous pouvez ajouter un titre, une description et une icône par emplacement en utilisant le tilde « ~ » comme séparateur.",
+ "semanticmaps-par-showtitle": "Afficher un titre dans la fenêtre d’information des marqueurs ou non. La désactivation de ceci est souvent utile lorsque vous utilisez un modèle pour formater le contenu de la fenêtre d’informations.",
"semanticmaps-par-hidenamespace": "Afficher le titre de l’espace de noms dans la fenêtre d’information du marqueur",
- "semanticmaps-par-centre": "Le centre de la carte. Lorsqu'il n'est pas fourni, la carte va choisir automatiquement le centre optimal pour afficher tous les marqueurs sur la carte.",
- "semanticmaps-par-template": "Un modèle à utiliser pour mettre en forme le contenu de la fenêtre d'informations.",
+ "semanticmaps-par-centre": "Le centre de la carte. Lorsqu’il n’est pas fourni, la carte va choisir automatiquement le centre optimal qui permet d’afficher tous les marqueurs sur la carte.",
+ "semanticmaps-par-template": "Un modèle à utiliser pour mettre en forme le contenu de la fenêtre d’informations.",
"semanticmaps-par-geocodecontrol": "Afficher le contrôle de géocodage.",
"semanticmaps-par-activeicon": "Icône à afficher à la place du marqueur par défaut, quand la page active est égale au résultat de la recherche",
- "semanticmaps-par-pagelabel": "Quand il vaut « oui », tous les marqueurs auront un « inlineLabel » avec un lien vers la page contenant les coordonnées du marqueur",
+ "semanticmaps-par-pagelabel": "Quand il vaut « yes » (oui), tous les marqueurs auront un « inlineLabel » avec un lien vers la page contenant les coordonnées du marqueur",
"semanticmaps-par-ajaxcoordproperty": "Nom de la propriété de coordonnées utilisée pour construire la requête Ajax.",
- "semanticmaps-par-ajaxquery": "Une seconde requête qui est envoyée via Ajax pour récupérer les coordonnées supplémentaires.",
+ "semanticmaps-par-ajaxquery": "Une seconde requête qui est envoyée via Ajax pour récupérer des coordonnées supplémentaires.",
"semanticmaps-par-userparam": "Une valeur passée dans chaque appel de modèle, si un modèle est utilisé",
- "semanticmaps-kml-text": "Le texte associé avec chaque page. Remplacé par des propriétés récupérées supplémentaires s'il y en a.",
+ "semanticmaps-kml-text": "Le texte associé avec chaque page. Remplacé par les éventuelles propriétés supplémentaires récupérées.",
"semanticmaps-kml-title": "Le titre par défaut pour les résultats",
- "semanticmaps-kml-linkabsolute": "Si les titres doivent être absolus (au contraire de relatifs)",
- "semanticmaps-kml-pagelinktext": "Le texte à utiliser pour les liens vers la page, dans lesquels $1 sera remplacé par le titre de la page",
- "semanticmaps-shapes-improperformat": "Format de $1 incorrect, veuillez vous reporter à la documentation pour le format attendu",
- "semanticmaps-shapes-missingshape": "Aucune forme trouvée pour $1; veuillez voir dans la documentation les formes disponibles",
+ "semanticmaps-kml-linkabsolute": "Si les titres doivent être absolus (et non relatifs)",
+ "semanticmaps-kml-pagelinktext": "Le texte à utiliser pour les liens vers la page, dans lequel $1 sera remplacé par le titre de la page cible",
+ "semanticmaps-shapes-improperformat": "Format incorrect pour $1. Veuillez consulter la documentation pour les formats pris en charge.",
+ "semanticmaps-shapes-missingshape": "Aucune forme trouvée pour $1 ; veuillez consulter la documentation pour les formes disponibles",
"validator-type-mapscircle": "Cercle géographique",
- "validator-type-mapscircle-list": "Liste des cercles",
- "validator-type-mapsimageoverlay": "Superposition d’image",
- "validator-type-mapsimageoverlay-list": "Liste des superpositions d’image",
+ "validator-type-mapscircle-list": "Liste de cercles",
+ "validator-type-mapsimageoverlay": "Surcouche d’image",
+ "validator-type-mapsimageoverlay-list": "Liste de surcouches d’images",
"validator-type-mapsline": "Ligne géographique",
- "validator-type-mapsline-list": "Liste des lignes",
+ "validator-type-mapsline-list": "Liste de lignes",
"validator-type-mapslocation": "Emplacement géographique",
- "validator-type-mapslocation-list": "Liste des emplacements",
+ "validator-type-mapslocation-list": "Liste d’emplacements",
"validator-type-mapsrectangle": "Rectangle géographique",
- "validator-type-mapsrectangle-list": "Liste des rectangles",
+ "validator-type-mapsrectangle-list": "Liste de rectangles",
"validator-type-mapspolygon": "Polygone géographique",
"validator-type-mapspolygon-list": "Liste des polygones géographiques",
- "validator-type-wmsoverlay": "Surcouche de Service de Carte web",
- "validator-type-jsonfile": "texte"
+ "validator-type-wmsoverlay": "Surcouche du Service de carte web",
+ "maps-json-editor-button-marker": "Placer un marqueur",
+ "maps-json-editor-button-line": "Tracer une ligne",
+ "maps-json-editor-button-polygon": "Tracer un polygone",
+ "maps-json-editor-button-rectangle": "Placer un rectangle",
+ "maps-json-editor-button-circle": "Placer un cercle",
+ "maps-json-editor-tooltip-marker": "Cliquer la carte pour placer un marqueur.",
+ "maps-json-editor-tooltip-line": "Cliquer la carte pour tracer une ligne.",
+ "maps-json-editor-tooltip-polygon": "Cliquer la carte pour tracer un polygone.",
+ "maps-json-editor-tooltip-rectangle": "Cliquer une carte pour placer un rectangle.",
+ "maps-json-editor-tooltip-circle": "Cliquer la carte pour placer un cercle.",
+ "maps-json-editor-added-marker": "Marqueur ajouté",
+ "maps-json-editor-added-line": "Ligne ajoutée",
+ "maps-json-editor-added-polygon": "Polygone ajouté",
+ "maps-json-editor-added-rectangle": "Rectangle ajouté",
+ "maps-json-editor-added-circle": "Cercle ajouté",
+ "maps-json-editor-edit-removed-shapes": "$1 forme{{PLURAL:$1||s}} supprimée{{PLURAL:$1||s}}",
+ "maps-json-editor-edit-modified": "Formes existantes de la carte modifiées",
+ "maps-json-editor-edit-other": "Modifier visuellement la carte",
+ "maps-json-editor-edit-failed": "Échec lors de l’enregistrement de la carte",
+ "maps-json-editor-toolbar-save-title": "Quitte le mode modification en conservant tous les changements",
+ "maps-json-editor-toolbar-save-text": "Terminé",
+ "maps-json-editor-toolbar-cancel-title": "Quitte le mode modification en abandonnant tous les changements",
+ "maps-json-editor-toolbar-cancel-text": "Annuler",
+ "maps-json-editor-toolbar-clear-title": "Effacer toutes les couches",
+ "maps-json-editor-toolbar-clear-text": "Effacer tout",
+ "maps-json-editor-toolbar-button-edit": "Modifier les couches",
+ "maps-json-editor-toolbar-button-edit-disabled": "Aucune couche à modifier",
+ "maps-json-editor-toolbar-button-remove": "Supprimer des couches",
+ "maps-json-editor-toolbar-button-remove-disabled": "Aucune couche à supprimer",
+ "maps-json-editor-toolbar-button-save": "Enregistrer les modifications",
+ "maps-json-editor-changes-saved": "Vos modifications ont été enregistrées",
+ "maps-geo-json-edit-source": "Modifier la source",
+ "maps-geo-json-create-source": "Créer depuis une source GeoJSON",
+ "maps-geo-json-create-page-button": "Créer cette page",
+ "maps-geo-json-create-page-creating": "Création de la page...",
+ "maps-geo-json-create-page-summary": "Page GeoJSON créée",
+ "maps-editor-edit-geojson": "Modifier la couche GeoJSON",
+ "tag-maps-visual-edit": "Modification de carte visuelle",
+ "tag-maps-visual-edit-description": "Modification faite avec l’éditeur visuel fourni par l’[https://www.mediawiki.org/wiki/Extension:Maps extension Maps]"
}
diff --git a/www/wiki/extensions/Maps/i18n/gl.json b/www/wiki/extensions/Maps/i18n/gl.json
index a69ecd90..9bdc727f 100644
--- a/www/wiki/extensions/Maps/i18n/gl.json
+++ b/www/wiki/extensions/Maps/i18n/gl.json
@@ -5,7 +5,8 @@
"Toliño",
"Banjo",
"Elisardojm",
- "Navhy"
+ "Navhy",
+ "Iváns"
]
},
"maps-desc": "Permite incorporar mapas dinámicos, enderezos xeocodificados e outras operacións xeográficas nas páxinas do wiki",
@@ -215,6 +216,5 @@
"validator-type-mapsrectangle-list": "Lista de rectángulos",
"validator-type-mapspolygon": "Polígono xeográfico",
"validator-type-mapspolygon-list": "Lista dos polígonos xeográficos",
- "validator-type-wmsoverlay": "Transparencia de Servizo de Mapa web",
- "validator-type-jsonfile": "texto"
+ "validator-type-wmsoverlay": "Transparencia de Servizo de Mapa web"
}
diff --git a/www/wiki/extensions/Maps/i18n/he.json b/www/wiki/extensions/Maps/i18n/he.json
index 5f99e108..88942e91 100644
--- a/www/wiki/extensions/Maps/i18n/he.json
+++ b/www/wiki/extensions/Maps/i18n/he.json
@@ -147,7 +147,7 @@
"maps-googlemaps3-par-kmlrezoom": "לקרב מחדש את המפה אחרי ששכבות KML נטענו.",
"maps-googlemaps3-par-poi": "הצגת נקודות עניין.",
"maps-googlemaps3-par-clustergridsize": "גודל הרשת של האשכול בפיקסלים.",
- "maps-par-clusterzoomonclick": "האם ההתנהגות הראשונית ללחיצה על אשכולית היא להתקרב לתוכו.",
+ "maps-par-clusterzoomonclick": "האם ההתנהגות הראשונית ללחיצה על אשכול היא להתקרב לתוכו.",
"maps-googlemaps3-par-clusteraveragecenter": "האם המרכז של כל אשכול אמור להיות הממוצע של כל הסמנים באשכול.",
"maps-googlemaps3-par-clusterminsize": "המספר המזערי של הסמנים שאמורים להיות באשכול לפני שהסמנים מוסתרים ומוצג מונה.",
"mapeditor": "עורך מפות",
@@ -205,13 +205,12 @@
"semanticmaps-par-ajaxquery": "שאילתה שנייה שנשלחת ב־ajax כדי לאחזר נקודות ציון נוספות.",
"semanticmaps-par-userparam": "ערך שמועבר לכל קריאה לתבנית, אם משמשת תבנית",
"semanticmaps-kml-text": "הטקסט משויך לכל עמוד ועמוד. נדרס במאפיינים אחרים שנעשית עליהם שאילתה, אם יש כאלה.",
- "semanticmaps-kml-title": "כותרת לתוצאות לפי בררת המחדל.",
+ "semanticmaps-kml-title": "כותרת לתוצאות לפי ברירת המחדל.",
"semanticmaps-kml-linkabsolute": "האם הקישורים צריכים להיות מוחלטים (או יחסיים)",
"semanticmaps-kml-pagelinktext": "הטקסט שישמש לקישורים לדף, כאשר $1 יוחלף בכותרת הדף",
"semanticmaps-shapes-improperformat": "עיצוב לא מתאים עבור $1. נא לראות את התיעוד על עיצוב",
"semanticmaps-shapes-missingshape": "לא נמצאו צורות עבור $1. נא לראות את התיעוד עבור צורות",
"validator-type-mapscircle-list": "רשימת מעגלים",
"validator-type-mapsline-list": "רשימת קווים",
- "validator-type-mapsrectangle-list": "רשימה של משולשים",
- "validator-type-jsonfile": "טקסט"
+ "validator-type-mapsrectangle-list": "רשימה של משולשים"
}
diff --git a/www/wiki/extensions/Maps/i18n/hr.json b/www/wiki/extensions/Maps/i18n/hr.json
index 7b10792c..0605c4eb 100644
--- a/www/wiki/extensions/Maps/i18n/hr.json
+++ b/www/wiki/extensions/Maps/i18n/hr.json
@@ -87,6 +87,5 @@
"validator-type-mapsrectangle": "Zemljopisni pravokutnik",
"validator-type-mapsrectangle-list": "Popis pravokutnika",
"validator-type-mapspolygon": "Zemljopisni mnogokut",
- "validator-type-mapspolygon-list": "Popis zemljopisnih mnogokuta",
- "validator-type-jsonfile": "tekst"
+ "validator-type-mapspolygon-list": "Popis zemljopisnih mnogokuta"
}
diff --git a/www/wiki/extensions/Maps/i18n/ia.json b/www/wiki/extensions/Maps/i18n/ia.json
index 8f57ca02..99c3cb08 100644
--- a/www/wiki/extensions/Maps/i18n/ia.json
+++ b/www/wiki/extensions/Maps/i18n/ia.json
@@ -93,7 +93,7 @@
"maps-par-resizable": "Rende le carta redimensionabile per traher lo per su angulo dextre inferior.",
"maps-par-zoom": "Le nivello de zoom pro le carta. Pro cartas con marcatores isto es predefinite como le nivello de zoom le plus alte que ancora monstra tote le marcatores.",
"maps-par-width": "Permitte fixar le latitude del carta. Le unitate assumite es pixels, ma tu pote specificar un de iste unitates: px, ex, em, %.",
- "maps-par-height": "Permitte fixar le altitude del carta. Le unitate assumite es pixels, ma tu pote specificar un de iste unitates: px, ex, em, %.",
+ "maps-par-height": "Permitte fixar le altitude del carta. Le unitate assumite es pixels, ma tu pote specificar un de iste unitates: px, ex, em.",
"maps-par-kml": "Files KML pro cargar in le carta.",
"maps-googlemaps3-incompatbrowser": "Tu navigator de web non es compatibile con Google Maps version 3.",
"maps-googlemaps3-par-type": "Le typo de carta a monstrar initialmente.",
diff --git a/www/wiki/extensions/Maps/i18n/it.json b/www/wiki/extensions/Maps/i18n/it.json
index 9eecfe15..ec016b3f 100644
--- a/www/wiki/extensions/Maps/i18n/it.json
+++ b/www/wiki/extensions/Maps/i18n/it.json
@@ -11,10 +11,11 @@
"Viscontino",
"Matteocng",
"Kaspo",
- "S4b1nuz E.656"
+ "S4b1nuz E.656",
+ "JackLantern"
]
},
- "maps-desc": "Consente di includere mappe dinamiche nelle pagine wiki, la geocodifica degli indirizzi ed altre operazioni geografiche",
+ "maps-desc": "Consente di includere mappe dinamiche nelle pagine wiki utilizzando Google Maps o Leaflet. Ha un editor visuale, eventualmente integrato con Semantic MediaWiki, supporta GeoJSON e aggiunge funzionalità di geocodifica.",
"right-geocode": "Effettua la geocodifica",
"maps_map": "Mappa",
"maps-loading-map": "Caricamento mappa ...",
@@ -159,5 +160,21 @@
"validator-type-mapsrectangle-list": "Elenco di rettangoli",
"validator-type-mapspolygon": "Poligono geografico",
"validator-type-mapspolygon-list": "Elenco dei poligoni geografici",
- "validator-type-jsonfile": "testo"
+ "maps-json-editor-tooltip-marker": "Clicca sulla mappa per posizionare un marcatore.",
+ "maps-json-editor-tooltip-line": "Clicca sulla mappa per disegnare una linea.",
+ "maps-json-editor-tooltip-polygon": "Clicca sulla mappa per disegnare un poligono.",
+ "maps-json-editor-added-marker": "Marcatore aggiunto",
+ "maps-json-editor-added-line": "Linea aggiunta",
+ "maps-json-editor-toolbar-save-text": "Fatto",
+ "maps-json-editor-toolbar-cancel-title": "Uscendo dalla modalità modifica, scarti tutti i cambiamenti.",
+ "maps-json-editor-toolbar-cancel-text": "Annulla",
+ "maps-json-editor-toolbar-clear-title": "Pulisci tutti i livelli",
+ "maps-json-editor-toolbar-button-edit-disabled": "Nessun livello da modificare",
+ "maps-json-editor-toolbar-button-remove": "Cancella livelli",
+ "maps-json-editor-toolbar-button-remove-disabled": "Nessun livello da cancellare",
+ "maps-json-editor-toolbar-button-save": "Salva modifiche",
+ "maps-json-editor-changes-saved": "Le tue modifiche sono state salvate",
+ "maps-geo-json-edit-source": "Modifica sorgente",
+ "maps-geo-json-create-page-button": "Crea questa pagina",
+ "maps-geo-json-create-page-summary": "Pagina GeoJSON creata"
}
diff --git a/www/wiki/extensions/Maps/i18n/ja.json b/www/wiki/extensions/Maps/i18n/ja.json
index 7fa54348..27320207 100644
--- a/www/wiki/extensions/Maps/i18n/ja.json
+++ b/www/wiki/extensions/Maps/i18n/ja.json
@@ -171,6 +171,5 @@
"validator-type-mapsimageoverlay-list": "画像オーバーレイの一覧",
"validator-type-mapslocation-list": "位置の一覧",
"validator-type-mapspolygon": "地図ポリゴン",
- "validator-type-mapspolygon-list": "地図ポリゴンの一覧",
- "validator-type-jsonfile": "テキスト"
+ "validator-type-mapspolygon-list": "地図ポリゴンの一覧"
}
diff --git a/www/wiki/extensions/Maps/i18n/ko.json b/www/wiki/extensions/Maps/i18n/ko.json
index b687427b..e458853f 100644
--- a/www/wiki/extensions/Maps/i18n/ko.json
+++ b/www/wiki/extensions/Maps/i18n/ko.json
@@ -204,5 +204,14 @@
"validator-type-mapslocation-list": "위치 목록",
"validator-type-mapsrectangle-list": "직사각형 목록",
"validator-type-wmsoverlay": "웹 지도 서비스 오버레이",
- "validator-type-jsonfile": "텍스트"
+ "maps-json-editor-toolbar-save-title": "모든 변경사항을 유지한 채 편집 모드를 빠져나옵니다",
+ "maps-json-editor-toolbar-save-text": "완료",
+ "maps-json-editor-toolbar-cancel-title": "편집 모드를 빠져나오고 모든 변경사항을 버립니다",
+ "maps-json-editor-toolbar-cancel-text": "취소",
+ "maps-json-editor-toolbar-clear-text": "모두 지우기",
+ "maps-json-editor-toolbar-button-save": "변경사항 저장",
+ "maps-json-editor-changes-saved": "변경사항이 저장되었습니다",
+ "maps-geo-json-edit-source": "원본 편집",
+ "maps-geo-json-create-page-button": "이 문서 만들기",
+ "maps-editor-edit-geojson": "GeoJSON 레이어 편집"
}
diff --git a/www/wiki/extensions/Maps/i18n/lb.json b/www/wiki/extensions/Maps/i18n/lb.json
index 387ab13a..1579d3d7 100644
--- a/www/wiki/extensions/Maps/i18n/lb.json
+++ b/www/wiki/extensions/Maps/i18n/lb.json
@@ -119,5 +119,11 @@
"validator-type-mapsrectangle-list": "Lëscht vun de Rechtecken",
"validator-type-mapspolygon": "Geographesche Polygon",
"validator-type-mapspolygon-list": "Lëscht vun de geographesche Polygonen",
- "validator-type-jsonfile": "Text"
+ "maps-json-editor-button-line": "Eng Linn zeechnen",
+ "maps-json-editor-toolbar-save-title": "Ännerunge späicheren",
+ "maps-json-editor-toolbar-save-text": "Fäerdeg",
+ "maps-json-editor-toolbar-cancel-text": "Ofbriechen",
+ "maps-json-editor-toolbar-clear-text": "Alles ewechemaachen",
+ "maps-json-editor-toolbar-button-save": "Ännerunge späicheren",
+ "maps-json-editor-changes-saved": "Är Ännerunge goufe gespäichert"
}
diff --git a/www/wiki/extensions/Maps/i18n/lt.json b/www/wiki/extensions/Maps/i18n/lt.json
index a54d50ee..2a30424c 100644
--- a/www/wiki/extensions/Maps/i18n/lt.json
+++ b/www/wiki/extensions/Maps/i18n/lt.json
@@ -5,7 +5,8 @@
"Hugo.arg",
"Aswanas",
"Zygimantus",
- "Agne1992"
+ "Agne1992",
+ "Tomasdd"
]
},
"maps-desc": "Suteikia galimybę atvaizduoti koordinačių duomenis žemėlapiuose ir geografinio kodavimo adresus ([http://mapping.referata.com/wiki/Maps_examples demo]).\nKatrografavimo paslaugos pasiekiamos: $1",
@@ -115,5 +116,41 @@
"validator-type-mapslocation": "Geografinė vieta",
"validator-type-mapslocation-list": "Vietų sąrašas",
"validator-type-mapsrectangle": "Geografinis stačiakampis",
- "validator-type-mapsrectangle-list": "Stačiakampių sąrašas"
+ "validator-type-mapsrectangle-list": "Stačiakampių sąrašas",
+ "maps-json-editor-button-marker": "Pridėti žymeklį",
+ "maps-json-editor-button-line": "Piešti liniją",
+ "maps-json-editor-button-polygon": "Piešti daugiakampį",
+ "maps-json-editor-button-rectangle": "Piešti stačiakampį",
+ "maps-json-editor-button-circle": "Pridėti apskritimą",
+ "maps-json-editor-tooltip-marker": "Spustelėkite žemėlapį, kad padėtumėte žymeklį.",
+ "maps-json-editor-tooltip-line": "Paspauskite žemėlapį, kad nupieštumėte liniją.",
+ "maps-json-editor-tooltip-polygon": "Paspauskite žemėlapį, kad nupieštumėte daugiakampį.",
+ "maps-json-editor-tooltip-rectangle": "Paspauskite žemėlapį, kad pridėtumėte keturkampį.",
+ "maps-json-editor-tooltip-circle": "Spustelėkite žemėlapį, kad pridėtumėte apskritimą.",
+ "maps-json-editor-added-marker": "Pridėtas žymeklis",
+ "maps-json-editor-added-line": "Pridėta linija",
+ "maps-json-editor-added-polygon": "Pridėtas daugiakampis",
+ "maps-json-editor-added-rectangle": "Pridėtas keturkampis",
+ "maps-json-editor-added-circle": "Pridėtas apskritimas",
+ "maps-json-editor-edit-removed-shapes": "Pašalinti $1 {{PLURAL:$1|formą|formas}}",
+ "maps-json-editor-edit-other": "Vizualus žemėlapio redaktorius",
+ "maps-json-editor-edit-failed": "Nepavyko išsaugoti žemėlapio",
+ "maps-json-editor-toolbar-save-title": "Išeiti iš redagavimo ir išsaugoti pakeitimus",
+ "maps-json-editor-toolbar-save-text": "Atlikta",
+ "maps-json-editor-toolbar-cancel-title": "Išeiti iš redagavimo, atmesti visus pakeitimus",
+ "maps-json-editor-toolbar-cancel-text": "Atšaukti",
+ "maps-json-editor-toolbar-clear-title": "Valyti visus sluoksnius",
+ "maps-json-editor-toolbar-clear-text": "Valyti Viską",
+ "maps-json-editor-toolbar-button-edit": "Redaguoti sluoksnius",
+ "maps-json-editor-toolbar-button-edit-disabled": "Nėra sluoksnių redagavimui",
+ "maps-json-editor-toolbar-button-remove": "Trinti sluoksnius",
+ "maps-json-editor-toolbar-button-remove-disabled": "Nėra sluoksnių pašalinimui",
+ "maps-json-editor-toolbar-button-save": "Išsaugoti pakeitimus",
+ "maps-json-editor-changes-saved": "Jūsų pakeitimai buvo išsaugoti",
+ "maps-geo-json-edit-source": "Redaguoti šaltinį",
+ "maps-geo-json-create-source": "Sukurti iš GeoJSON šaltinio",
+ "maps-geo-json-create-page-button": "Sukurti šį puslapį",
+ "maps-geo-json-create-page-creating": "Puslapis kuriamas...",
+ "maps-geo-json-create-page-summary": "Sukūrė GeoJSON puslapį",
+ "maps-editor-edit-geojson": "Redaguoti GeoJSON sluoksnį"
}
diff --git a/www/wiki/extensions/Maps/i18n/mk.json b/www/wiki/extensions/Maps/i18n/mk.json
index 78a20ef8..19db3773 100644
--- a/www/wiki/extensions/Maps/i18n/mk.json
+++ b/www/wiki/extensions/Maps/i18n/mk.json
@@ -76,7 +76,7 @@
"maps-displaymap-par-title": "Овозможува задавање на текст што ќе се прикажува во скокачките прозорчиња на сите обележувачи што немаат конкретен наслов.\nАко се користат заедно со ознака, насловот ќе биде задебелен и ќе има линија под него.",
"maps-displaymap-par-label": "Овозможува задавање на текст што ќе се прикажува во скокачките прозорчиња на сите обележувачи што немаат конкретна ознака.",
"maps-displaymap-par-icon": "Овозможува задавање на икона што ќе се користи за сите обележувачи.",
- "maps-displaymap-par-circles": "Кругови за приказ",
+ "maps-displaymap-par-circles": "Кружници за приказ",
"maps-displaymap-par-copycoords": "Прикажувај прозорче со координатите на местото при стискање на место од кајшто можат да се прекопираат.",
"maps-displaymap-par-lines": "Линии за приказ",
"maps-displaymap-par-maxzoom": "Најголема приближеност",
@@ -102,8 +102,8 @@
"maps-abb-east": "И",
"maps-abb-south": "Ј",
"maps-abb-west": "З",
- "maps-latitude": "Геог. ширина",
- "maps-longitude": "Геог. должина:",
+ "maps-latitude": "Гео. ширина",
+ "maps-longitude": "Гео. должина:",
"maps-invalid-coordinates": "Вредноста $1 не беше препознаена како правилен збир координати.",
"maps_coordinates_missing": "Нема координати за картата.",
"maps_geocoding_failed": "{{PLURAL:$2|Следнава адреса не можеше да се геокодира|Следниве адреси не можеа да се геокодираат}}: $1.\nКартата не може да се прикаже.",
@@ -116,9 +116,11 @@
"maps_leaflet": "Леток",
"maps-leaflet-par-defzoom": "Овозможува задавање на основен степен на приближеност на картата.",
"maps-leaflet-par-layers": "Слојот што ќе се покажува кога се вчитува картата.",
+ "maps-leaflet-par-layers-dark": "Слоевите што ќе бидат достапни во бирачот на слоеви кога ќе се вклучи темниот режим. Кога ќе се вчита картата ќе се прикаже првиот слој.",
"maps-leaflet-par-overlaylayers": "Слоеви на облогата што ќе се покажуваат кога се вчитува картата.",
"maps-leaflet-par-maxclusterradius": "Најголемиот полупречник што може да го покрие еден грозд од средишниот обележувач (во пиксели).",
"maps-leaflet-par-clusterspiderfy": "Кога ќе стиснете на грозд при најслаба приближеност, истиот ќе се разграни за да можете да ги видите сите негови обележувачи.",
+ "maps-leaflet-par-clicktarget": "Стискајќи на картата, ќе бидете пренасочени кон следнава URL. %lat% се замеува со геоградската ширина, а %long% со географската должина",
"maps_click_to_activate": "Стиснете за активирање на картата",
"maps_centred_on": "Средиште на картата во $1, $2.",
"maps-par-mappingservice": "Овозможува назначување на картографска служба што ќе се користи за создавање на картата.",
@@ -126,7 +128,7 @@
"maps-par-searchmarkers": "Овозможува пребарување на поединечни бележници од поле вметнато во картата.",
"maps-par-zoom": "Степенот на приближеност на картата. Картите со обележувачи по основно се прикажуваат во најголем степен на приближеност што воедно ги прикажува сите обележувачи.",
"maps-par-width": "Овозможува задавање на ширина на картата во пиксели по основно, но по желба можете да изберете една од следниве единици: px, ex, em, %.",
- "maps-par-height": "Овозможува задавање на висина на картата во пиксели по основно, но по желба можете да изберете една од следниве единици: px, ex, em, %.",
+ "maps-par-height": "Овозможува задавање на висина на картата во пиксели по основно, но по желба можете да изберете една од следниве единици: px, ex, em.",
"maps-par-centre": "На која местоположба да се сосредоточи картата",
"maps-par-enable-fullscreen": "Овозможи копче за цел екран",
"maps-par-kml": "KML-податотеки за вчитување во картата.",
@@ -188,8 +190,8 @@
"semanticmaps-unrecognizeddistance": "Вредноста $1 не претставува важечко растојание.",
"semanticmaps-kml-link": "Преглед на KML-податотеката",
"semanticmaps-default-kml-pagelink": "Преглед на статијата $1",
- "semanticmaps-latitude": "Геог. ширина: $1",
- "semanticmaps-longitude": "Геог. должина: $1",
+ "semanticmaps-latitude": "Гео. ширина: $1",
+ "semanticmaps-longitude": "Гео. должина: $1",
"semanticmaps-altitude": "Надм. вис: $1",
"semanticmaps-forminput-locations": "Места",
"semanticmaps-par-staticlocations": "Список на места за додавање во картатата заедно со побараните податоци. Како и со „display_points“, тука можете да додадете наслов, опис и икона за секое место, користејќи тилда (~) како одделувач.",
@@ -209,8 +211,8 @@
"semanticmaps-kml-pagelinktext": "Текстот на врските на страницата, каде $1 ќе се замени со насловот на страницата",
"semanticmaps-shapes-improperformat": "Несоодветно форматирање на $1. Погледајте во документацијата како треба да се форматира.",
"semanticmaps-shapes-missingshape": "Не пронајдов облици за $1. Погледајте во документацијата кои облици ви се на располагање.",
- "validator-type-mapscircle": "Географски круг",
- "validator-type-mapscircle-list": "Список на кругови",
+ "validator-type-mapscircle": "Географска кружница",
+ "validator-type-mapscircle-list": "Список на кружници",
"validator-type-mapsimageoverlay": "Обложна слика",
"validator-type-mapsimageoverlay-list": "Список на обложни слики",
"validator-type-mapsline": "Географска линија",
@@ -222,5 +224,43 @@
"validator-type-mapspolygon": "Географски многуаголник",
"validator-type-mapspolygon-list": "Список на географски многуаголници",
"validator-type-wmsoverlay": "Облога Web Map Service",
- "validator-type-jsonfile": "текст"
+ "maps-json-editor-button-marker": "Стави обележувач",
+ "maps-json-editor-button-line": "Нацртај линија",
+ "maps-json-editor-button-polygon": "Нацртај многуаголник",
+ "maps-json-editor-button-rectangle": "Стави правоаголник",
+ "maps-json-editor-button-circle": "Стави кружница",
+ "maps-json-editor-tooltip-marker": "Стиснете на картата за да ставите обележувач.",
+ "maps-json-editor-tooltip-line": "Стиснете на картата за да нацртате линија.",
+ "maps-json-editor-tooltip-polygon": "Стиснете на картата за да нацртате многусголник.",
+ "maps-json-editor-tooltip-rectangle": "Стиснете на картата за да ставите правоаголник.",
+ "maps-json-editor-tooltip-circle": "Стиснете на картата за да ставите кружница.",
+ "maps-json-editor-added-marker": "Додаден обележувач",
+ "maps-json-editor-added-line": "Додадена линија",
+ "maps-json-editor-added-polygon": "Додаден многуаголник",
+ "maps-json-editor-added-rectangle": "Додаден правоаголник",
+ "maps-json-editor-added-circle": "Додадена кружница",
+ "maps-json-editor-edit-removed-shapes": "{{PLURAL:$1|Отстранет $1 облик|Отстранети $1 облици}}",
+ "maps-json-editor-edit-modified": "Изменети постоечки облици",
+ "maps-json-editor-edit-other": "Нагледно уредување на карта",
+ "maps-json-editor-edit-failed": "Не успеав да ја зачувам картата.",
+ "maps-json-editor-toolbar-save-title": "Излези од уредувањето, задржувајќи ги сите промени",
+ "maps-json-editor-toolbar-save-text": "Готово",
+ "maps-json-editor-toolbar-cancel-title": "Излези од уредувањето, отфрлајќи ги сите промени",
+ "maps-json-editor-toolbar-cancel-text": "Откажи",
+ "maps-json-editor-toolbar-clear-title": "Исчисти ги сите слоеви",
+ "maps-json-editor-toolbar-clear-text": "Исчисти сè",
+ "maps-json-editor-toolbar-button-edit": "Уреди слоеви",
+ "maps-json-editor-toolbar-button-edit-disabled": "Нема слоеви за уредување",
+ "maps-json-editor-toolbar-button-remove": "Избиши слоеви",
+ "maps-json-editor-toolbar-button-remove-disabled": "Нема слоеви за бришење",
+ "maps-json-editor-toolbar-button-save": "Зачувај промени",
+ "maps-json-editor-changes-saved": "Вашите промени се зачувани",
+ "maps-geo-json-edit-source": "Измени извор",
+ "maps-geo-json-create-source": "Создај од извор на GeoJSON",
+ "maps-geo-json-create-page-button": "Создај ја страницава",
+ "maps-geo-json-create-page-creating": "Ја создавам страницата...",
+ "maps-geo-json-create-page-summary": "Создадена GeoJSON-страница",
+ "maps-editor-edit-geojson": "Уреди GeoJSON-слој",
+ "tag-maps-visual-edit": "Нагледно уредување на картата",
+ "tag-maps-visual-edit-description": "Уредување направено со нагледниот уредник овозможен од додатокот „[https://www.mediawiki.org/wiki/Extension:Maps Карти]“"
}
diff --git a/www/wiki/extensions/Maps/i18n/nb.json b/www/wiki/extensions/Maps/i18n/nb.json
index 7da896db..9d169971 100644
--- a/www/wiki/extensions/Maps/i18n/nb.json
+++ b/www/wiki/extensions/Maps/i18n/nb.json
@@ -10,7 +10,7 @@
"Jon Harald Søby"
]
},
- "maps-desc": "Gir mulighet for å bygge inn dynamiske kart i wikisider, geokoding av adresser og andre geografiske operasjoner",
+ "maps-desc": "Gjør det mulig å bygge inn dynamiske kart i wikisider med Google Maps eller Leaflet. Har visuell redigering, kan integreres med Semantic MediaWiki, støtter GeoJSON og legger til geokodingsmuligheter.",
"right-geocode": "Geokode",
"action-geocode": "utføre geokoding på denne wikien",
"maps_map": "Kart",
@@ -88,7 +88,7 @@
"maps-displaymap-par-rectangles": "Rektangler å vise",
"maps-displaymap-par-static": "Gjør kartet statisk",
"maps-displaymap-par-wmsoverlay": "Legger på et WMS-lag",
- "maps-displaymap-par-geojson": "URL-en til ei GeoJSON-fil",
+ "maps-displaymap-par-geojson": "URL-en til en side eller fil som inneholder GeoJSON-data",
"maps-fullscreen-button": "Slå av/på fullskjerm",
"maps-fullscreen-button-tooltip": "Vis kartet som fullskjerm eller innbygd.",
"validation-error-invalid-location": "Parameter $1 må være en gyldig lokasjon.",
@@ -118,9 +118,11 @@
"maps_leaflet": "Leaflet",
"maps-leaflet-par-defzoom": "Tillater å sette standard zoom-nivå for kartet.",
"maps-leaflet-par-layers": "Laget som vil vises mens kartet laster.",
+ "maps-leaflet-par-layers-dark": "Lagene som vil være tilgjengelige i lagvelgeren når mørk modus er aktivert. Det første laget vises når kartet lastes.",
"maps-leaflet-par-overlaylayers": "Overleggslagene som vises mens kartet lastes.",
"maps-leaflet-par-maxclusterradius": "Minimum radius som et kluster dekker fra den sentrale markøren (angitt i piksler)",
"maps-leaflet-par-clusterspiderfy": "Når du klikker et kluster ved laveste zoom-nivå, så spres den ut slik at du kan se alle dens markører.",
+ "maps-leaflet-par-clicktarget": "Når du klikker på kartet blir du sendt til denne URL-en. %lat% erstattes av breddegraden og %long% av lengdegraden",
"maps_click_to_activate": "Klikk for å aktivere kartet",
"maps_centred_on": "Kart sentrert om $1, $2.",
"maps-par-mappingservice": "Tillater å sette karttjenesten som vil brukes for å skape kartet.",
@@ -128,7 +130,7 @@
"maps-par-searchmarkers": "Tillater å søke etter spesifikke markører via et felt som er innfestet i kartet.",
"maps-par-zoom": "Zoomnivået for kartet. For kart med markører vil standardverdien være den med mest høyest zoom som viser alle markørene.",
"maps-par-width": "Tillater at kartbredden angis. Standard enhet er pixler /(px), men du kan alternativt angi ex, em, %.",
- "maps-par-height": "Tillater at karthøyden angis. Standard enhet er pixler /(px), men du kan alternativt angi ex, em, %.",
+ "maps-par-height": "Tillater at karthøyden angis. Standard enhet er piksler (px), men du kan alternativt angi enhetene ex eller em.",
"maps-par-centre": "Stedet som kartet sentreres rundt",
"maps-par-enable-fullscreen": "Aktiver fullskjermknappen",
"maps-par-kml": "KML-filer som lastes inn på kortet.",
@@ -224,5 +226,43 @@
"validator-type-mapspolygon": "Geografisk polygon",
"validator-type-mapspolygon-list": "Liste over geografiske polygoner",
"validator-type-wmsoverlay": "Webmappetjensteoverlegg",
- "validator-type-jsonfile": "URL"
+ "maps-json-editor-button-marker": "Plasser en markør",
+ "maps-json-editor-button-line": "Tegn ei linje",
+ "maps-json-editor-button-polygon": "Tegn et polygon",
+ "maps-json-editor-button-rectangle": "Plasser et rektangel",
+ "maps-json-editor-button-circle": "Plasser en sirkel",
+ "maps-json-editor-tooltip-marker": "Klikk på kartet for å plassere markør.",
+ "maps-json-editor-tooltip-line": "Klikk på kartet for å tegne linje.",
+ "maps-json-editor-tooltip-polygon": "Klikk på kartet for å tegne polygon.",
+ "maps-json-editor-tooltip-rectangle": "Klikk på kartet for å plassere rektangel.",
+ "maps-json-editor-tooltip-circle": "Klikk på kartet for å plassere sirkel.",
+ "maps-json-editor-added-marker": "La til markør",
+ "maps-json-editor-added-line": "La til linje",
+ "maps-json-editor-added-polygon": "La til polygon",
+ "maps-json-editor-added-rectangle": "La til rektangel",
+ "maps-json-editor-added-circle": "La til sirkel",
+ "maps-json-editor-edit-removed-shapes": "Fjernet {{PLURAL:$1|én form|$1 former}}",
+ "maps-json-editor-edit-modified": "Modifiserte eksisterende kartformer",
+ "maps-json-editor-edit-other": "Visuell kartredigering",
+ "maps-json-editor-edit-failed": "Kunne ikke lagre kartet",
+ "maps-json-editor-toolbar-save-title": "Gå ut av redigeringsmodus uten å forkaste endringer",
+ "maps-json-editor-toolbar-save-text": "Ferdig",
+ "maps-json-editor-toolbar-cancel-title": "Avbryt redigering og forkast alle endringer",
+ "maps-json-editor-toolbar-cancel-text": "Avbryt",
+ "maps-json-editor-toolbar-clear-title": "Tøm alle lag",
+ "maps-json-editor-toolbar-clear-text": "Tøm alt",
+ "maps-json-editor-toolbar-button-edit": "Rediger lag",
+ "maps-json-editor-toolbar-button-edit-disabled": "Ingen lag å redigere",
+ "maps-json-editor-toolbar-button-remove": "Slett lag",
+ "maps-json-editor-toolbar-button-remove-disabled": "Ingen lag å slette",
+ "maps-json-editor-toolbar-button-save": "Lagre endringer",
+ "maps-json-editor-changes-saved": "Endringene dine har blitt lagret",
+ "maps-geo-json-edit-source": "Rediger kilde",
+ "maps-geo-json-create-source": "Opprett fra GeoJSON-kilde",
+ "maps-geo-json-create-page-button": "Opprett denne siden",
+ "maps-geo-json-create-page-creating": "Oppretter side …",
+ "maps-geo-json-create-page-summary": "Opprettet GeoJSON-side",
+ "maps-editor-edit-geojson": "Rediger GeoJSON-lag",
+ "tag-maps-visual-edit": "Visuell kartredigering",
+ "tag-maps-visual-edit-description": "Redigering gjort med visuell redigering fra [https://www.mediawiki.org/wiki/Extension:Maps kartutvidelsen]"
}
diff --git a/www/wiki/extensions/Maps/i18n/nl.json b/www/wiki/extensions/Maps/i18n/nl.json
index 53b3c869..3dd7eef1 100644
--- a/www/wiki/extensions/Maps/i18n/nl.json
+++ b/www/wiki/extensions/Maps/i18n/nl.json
@@ -201,6 +201,5 @@
"validator-type-mapscircle-list": "Lijst van circels",
"validator-type-mapsline-list": "Lijst met lijnen",
"validator-type-mapslocation-list": "Lijst met locaties",
- "validator-type-mapsrectangle-list": "Lijst met rechthoeken",
- "validator-type-jsonfile": "tekst"
+ "validator-type-mapsrectangle-list": "Lijst met rechthoeken"
}
diff --git a/www/wiki/extensions/Maps/i18n/pl.json b/www/wiki/extensions/Maps/i18n/pl.json
index bb71af4d..1097913b 100644
--- a/www/wiki/extensions/Maps/i18n/pl.json
+++ b/www/wiki/extensions/Maps/i18n/pl.json
@@ -9,7 +9,9 @@
"Alan ffm",
"Macofe",
"Deejay1",
- "Railfail536"
+ "Railfail536",
+ "Rail",
+ "FunPL"
]
},
"maps-desc": "Umożliwia zamieszczanie na stronach wiki map dynamicznych, geokodowanych adresów i innych danych geograficznych",
@@ -21,6 +23,7 @@
"maps-load-failed": "Nie można załadować mapy!",
"maps-markers": "Zaznaczenia",
"maps-copycoords-prompt": "CTRL+C, ENTER",
+ "maps-searchmarkers-text": "Filtr markerów",
"maps-others": "inne",
"maps-kml-parsing-failed": "Błąd podczas parsowania jednego lub więcej plików KML, najczęściej jest to wynik błędnego XML",
"maps-ns-layer": "Warstwa",
@@ -166,5 +169,43 @@
"validator-type-mapsrectangle-list": "Lista prostokątów",
"validator-type-mapspolygon": "Wielokąt geograficzny",
"validator-type-mapspolygon-list": "Lista wielokątów geograficznych",
- "validator-type-jsonfile": "tekst"
+ "maps-json-editor-button-marker": "Umieść znacznik",
+ "maps-json-editor-button-line": "Narysuj linię",
+ "maps-json-editor-button-polygon": "Narysuj wielokąt",
+ "maps-json-editor-button-rectangle": "Umieść wielokąt",
+ "maps-json-editor-button-circle": "Umieść okrąg",
+ "maps-json-editor-tooltip-marker": "Kliknij na mapie, aby umieścić znacznik.",
+ "maps-json-editor-tooltip-line": "Kliknij na mapę, aby narysować linię.",
+ "maps-json-editor-tooltip-polygon": "Kliknij na mapę, aby narysować wielokąt.",
+ "maps-json-editor-tooltip-rectangle": "Kliknij na mapie, aby umieścić prostokąt.",
+ "maps-json-editor-tooltip-circle": "Kliknij na mapie, aby umieścić okrągły znacznik.",
+ "maps-json-editor-added-marker": "Dodano znacznik",
+ "maps-json-editor-added-line": "Dodano linię",
+ "maps-json-editor-added-polygon": "Dodano wielokąt",
+ "maps-json-editor-added-rectangle": "Dodano prostokąt",
+ "maps-json-editor-added-circle": "Dodano okrąg",
+ "maps-json-editor-edit-removed-shapes": "Usunięto $1 {{PLURAL:$1|kształt|kształty|kształtów}}",
+ "maps-json-editor-edit-modified": "Zmodyfikowano kształty istniejące na mapie",
+ "maps-json-editor-edit-other": "Wizualna edycja mapy",
+ "maps-json-editor-edit-failed": "Nie udało się zapisać mapy",
+ "maps-json-editor-toolbar-save-title": "Opuść tryb edycji zachowując wszystkie zmiany",
+ "maps-json-editor-toolbar-save-text": "Gotowe",
+ "maps-json-editor-toolbar-cancel-title": "Opuść tryb edycji, odrzuca wszystkie zmiany",
+ "maps-json-editor-toolbar-cancel-text": "Anuluj",
+ "maps-json-editor-toolbar-clear-title": "Wyczyść wszystkie warstwy",
+ "maps-json-editor-toolbar-clear-text": "Wyczyść wszystko",
+ "maps-json-editor-toolbar-button-edit": "Edytuj warstwy",
+ "maps-json-editor-toolbar-button-edit-disabled": "Brak warstw do edycji",
+ "maps-json-editor-toolbar-button-remove": "Usuń warstwy",
+ "maps-json-editor-toolbar-button-remove-disabled": "Brak warstw, które można usunąć",
+ "maps-json-editor-toolbar-button-save": "Zapisz zmiany",
+ "maps-json-editor-changes-saved": "Twoje zmiany zostały zapisane",
+ "maps-geo-json-edit-source": "Edytuj kod źródłowy",
+ "maps-geo-json-create-source": "Utwórz ze źródła GeoJSON",
+ "maps-geo-json-create-page-button": "Utwórz tę stronę",
+ "maps-geo-json-create-page-creating": "Tworzenie strony…",
+ "maps-geo-json-create-page-summary": "Utworzono stronę GeoJSON",
+ "maps-editor-edit-geojson": "Edytuj warstwę GeoJSON",
+ "tag-maps-visual-edit": "Wizualna edycja mapy",
+ "tag-maps-visual-edit-description": "Edycja wykonana za pośrednictwem wizualnego edytora wprowadzonego przez [https://www.mediawiki.org/wiki/Extension:Maps rozszerzenie map]."
}
diff --git a/www/wiki/extensions/Maps/i18n/pt-br.json b/www/wiki/extensions/Maps/i18n/pt-br.json
index e1b7df59..2dd99219 100644
--- a/www/wiki/extensions/Maps/i18n/pt-br.json
+++ b/www/wiki/extensions/Maps/i18n/pt-br.json
@@ -121,9 +121,11 @@
"maps_leaflet": "Folheto",
"maps-leaflet-par-defzoom": "Permite definir o nível de zoom predefinido do mapa.",
"maps-leaflet-par-layers": "A camada que será mostrada quando o mapa é carregado.",
+ "maps-leaflet-par-layers-dark": "As camadas que estarão disponíveis no seletor de camadas quando um modo escuro estiver ativado. A primeira camada será mostrada quando o mapa carregar",
"maps-leaflet-par-overlaylayers": "As camadas de sobreposição que serão mostradas quando o mapa é carregado.",
"maps-leaflet-par-maxclusterradius": "O raio máximo que um agregado irá cobrir a partir do marcador central (em píxeis).",
"maps-leaflet-par-clusterspiderfy": "Quando clica um agregado no nível de zoom mais baixo os marcadores são enteados para que possa vê-los todos.",
+ "maps-leaflet-par-clicktarget": "Ao clicar no mapa, você será enviado para este URL. %lat% é substituído pela latitude e %long% pela longitude",
"maps_click_to_activate": "Clique para ativar o mapa",
"maps_centred_on": "Mapa centrado nas coordenadas $1, $2.",
"maps-par-mappingservice": "Permite definir o serviço de cartografia que será usado para gerar o mapa.",
@@ -227,5 +229,40 @@
"validator-type-mapspolygon": "Polígono geográfico",
"validator-type-mapspolygon-list": "Lista de polígonos geográficos",
"validator-type-wmsoverlay": "Sobreposição Web Map Service",
- "validator-type-jsonfile": "texto"
+ "maps-json-editor-button-marker": "Colocar um marcador",
+ "maps-json-editor-button-line": "Desenhar uma linha",
+ "maps-json-editor-button-polygon": "Desenhar um polígono",
+ "maps-json-editor-button-rectangle": "Colocar um retângulo",
+ "maps-json-editor-button-circle": "Colocar um círculo",
+ "maps-json-editor-tooltip-marker": "Clique no mapa para colocar o marcador.",
+ "maps-json-editor-tooltip-line": "Clique no mapa para desenhar uma linha.",
+ "maps-json-editor-tooltip-polygon": "Clique no mapa para desenhar polígono.",
+ "maps-json-editor-tooltip-rectangle": "Clique no mapa para colocar o retângulo.",
+ "maps-json-editor-tooltip-circle": "Clique no mapa para colocar o círculo.",
+ "maps-json-editor-added-marker": "Marcador adicionado",
+ "maps-json-editor-added-line": "Linha adicionada",
+ "maps-json-editor-added-polygon": "Polígono adicionado",
+ "maps-json-editor-added-rectangle": "Retângulo adicionado",
+ "maps-json-editor-added-circle": "Círculo adicionado",
+ "maps-json-editor-edit-removed-shapes": "$1 removido {{PLURAL:$1|forma|formas}}",
+ "maps-json-editor-edit-modified": "Formas de mapa existentes modificadas",
+ "maps-json-editor-edit-other": "Edição visual do mapa",
+ "maps-json-editor-edit-failed": "Falha ao salvar o mapa",
+ "maps-json-editor-toolbar-save-title": "Salvar alterações",
+ "maps-json-editor-toolbar-save-text": "Salvar",
+ "maps-json-editor-toolbar-cancel-title": "Cancelar edição, descartar todas as alterações",
+ "maps-json-editor-toolbar-cancel-text": "Cancelar",
+ "maps-json-editor-toolbar-clear-title": "Limpar todas as camadas",
+ "maps-json-editor-toolbar-clear-text": "Limpar tudo",
+ "maps-json-editor-toolbar-button-edit": "Editar camadas",
+ "maps-json-editor-toolbar-button-edit-disabled": "Não há camadas para editar",
+ "maps-json-editor-toolbar-button-remove": "Excluir camadas",
+ "maps-json-editor-toolbar-button-remove-disabled": "Não há camadas para excluir",
+ "maps-geo-json-edit-source": "Editar código-fonte",
+ "maps-geo-json-create-source": "Criar a partir da fonte GeoJSON",
+ "maps-geo-json-create-page-button": "Criar esta página",
+ "maps-geo-json-create-page-creating": "Criando página...",
+ "maps-geo-json-create-page-summary": "Página GeoJSON criada",
+ "tag-maps-visual-edit": "Edição visual do mapa",
+ "tag-maps-visual-edit-description": "Edição feita com o editor visual fornecido pelo [https://www.mediawiki.org/wiki/Extension:Maps Maps extension]"
}
diff --git a/www/wiki/extensions/Maps/i18n/pt.json b/www/wiki/extensions/Maps/i18n/pt.json
index 7bf9b166..f4a6a331 100644
--- a/www/wiki/extensions/Maps/i18n/pt.json
+++ b/www/wiki/extensions/Maps/i18n/pt.json
@@ -12,7 +12,8 @@
"Macofe",
"Malafaya",
"Fúlvio",
- "Athena in Wonderland"
+ "Athena in Wonderland",
+ "Mansil alfalb"
]
},
"maps-desc": "Permite incorporar mapas dinâmicos nas páginas da wiki, converter endereços em geocódigos e outras operações geográficas",
@@ -133,7 +134,7 @@
"maps-par-searchmarkers": "Permite pesquisar marcadores específicos usando um campo incorporado no mapa.",
"maps-par-zoom": "O nível de zoom do mapa. Nos mapas com marcadores será usado o maior zoom que, mesmo assim, mostre todos os marcadores.",
"maps-par-width": "Permite definir a largura do mapa. A unidade por omissão é o píxel, mas pode defini-la explicitamente como: px, ex, em, %.",
- "maps-par-height": "Permite definir a altura do mapa. A unidade por omissão é o píxel, mas pode defini-la explicitamente como: px, ex, em, %.",
+ "maps-par-height": "Permite definir a altura do mapa. A unidade por omissão é o píxel, mas pode especificar explicitamente uma destas unidades: px, ex, em.",
"maps-par-centre": "A localização na qual o mapa deve estar centrado",
"maps-par-enable-fullscreen": "Ativar o botão de ecrã completo",
"maps-par-kml": "Ficheiros KML que serão carregados no mapa.",
@@ -229,5 +230,5 @@
"validator-type-mapspolygon": "Polígono geográfico",
"validator-type-mapspolygon-list": "Lista de polígonos geográficos",
"validator-type-wmsoverlay": "Sobreposição Web Map Service",
- "validator-type-jsonfile": "texto"
+ "maps-json-editor-toolbar-save-text": "Gravar"
}
diff --git a/www/wiki/extensions/Maps/i18n/qqq.json b/www/wiki/extensions/Maps/i18n/qqq.json
index 89298677..bd3f62b5 100644
--- a/www/wiki/extensions/Maps/i18n/qqq.json
+++ b/www/wiki/extensions/Maps/i18n/qqq.json
@@ -126,7 +126,7 @@
"maps_leaflet": "This is a field label.\n\n{{optional}}",
"maps-leaflet-par-defzoom": "{{maps-par|leaflet|defzoom}}",
"maps-leaflet-par-layers": "{{maps-par|leaflet|layers}}",
- "maps-leaflet-par-overlaylayers": "{{maps-par|leaflet|overlaylayers}}",
+ "maps-leaflet-par-overlaylayers": "{{maps-par|leaflet|overlays}}",
"maps-leaflet-par-maxclusterradius": "{{maps-par|leaflet|maxclusterradius}}",
"maps-leaflet-par-clusterspiderfy": "{{maps-par|leaflet|clusterspiderfy}}",
"maps_click_to_activate": "This is an informatory message.",
@@ -138,9 +138,9 @@
"maps-par-width": "{{maps-par-all|width}}",
"maps-par-height": "{{maps-par-all|height}}",
"maps-par-centre": "{{maps-par-all|centre}}",
- "maps-par-enable-fullscreen": "{{maps-par-all|enablefullscreen}}",
+ "maps-par-enable-fullscreen": "{{maps-par-all|fullscreen}}",
"maps-par-kml": "System message used by several maps services:\n\n{{maps-par|googlemaps3|kml}}",
- "maps-par-markercluster": "{{maps-par-all|markercluster}}",
+ "maps-par-markercluster": "{{maps-par-all|cluster}}",
"maps-googlemaps3-incompatbrowser": "This is an error message shown to the user instead of a map.",
"maps-googlemaps3-par-imageoverlays": "{{maps-par|googlemaps3|imageoverlays}}",
"maps-googlemaps3-par-type": "{{maps-par|googlemaps3|type}}",
@@ -231,5 +231,26 @@
"validator-type-mapsrectangle": "This is the name of a type of values that may be assigned to a parameter.",
"validator-type-mapsrectangle-list": "This is the name of a type of values that may be assigned to a parameter.",
"validator-type-wmsoverlay": "This is the name of a type of values that may be assigned to a parameter. WMS stands for [[wikipedia:en:Web_Map_Service|Web Map Service]].",
- "validator-type-jsonfile": "{{Identical|Text}}"
+ "maps-json-editor-button-marker": "Text shown when hovering over the 'add marker' button in the visual map editor",
+ "maps-json-editor-button-line": "Text shown when hovering over the 'add line' button in the visual map editor",
+ "maps-json-editor-button-polygon": "Text shown when hovering over the 'add polygon' button in the visual map editor",
+ "maps-json-editor-button-rectangle": "Text shown when hovering over the 'add rectangle' button in the visual map editor",
+ "maps-json-editor-button-circle": "Text shown when hovering over the 'add circle' button in the visual map editor",
+ "maps-json-editor-tooltip-marker": "Text show after clicking the 'add marker' button in the visual map editor",
+ "maps-json-editor-tooltip-line": "Text show after clicking the 'add line' button in the visual map editor",
+ "maps-json-editor-tooltip-polygon": "Text show after clicking the 'add polygon' button in the visual map editor",
+ "maps-json-editor-tooltip-rectangle": "Text show after clicking the 'add rectangle' button in the visual map editor",
+ "maps-json-editor-tooltip-circle": "Text show after clicking the 'add circle' button in the visual map editor",
+ "maps-json-editor-added-marker": "Edit summary created by the visual map editor when adding a marker",
+ "maps-json-editor-added-line": "Edit summary created by the visual map editor when adding a line",
+ "maps-json-editor-added-polygon": "Edit summary created by the visual map editor when adding a polygon",
+ "maps-json-editor-added-rectangle": "Edit summary created by the visual map editor when adding a rectangle",
+ "maps-json-editor-added-circle": "Edit summary created by the visual map editor when adding a circle",
+ "maps-json-editor-edit-removed-shapes": "Edit summary created by the visual map editor when removing one or more shapes",
+ "maps-json-editor-edit-modified": "Edit summary created by the visual map editor when modifying existing shapes",
+ "maps-json-editor-edit-other": "Edit summary created by the visual map editor",
+ "maps-json-editor-edit-failed": "Error message shown when saving a change in the visual map editor fails",
+ "maps-json-editor-toolbar-cancel-text": "{{Identical|Cancel}}",
+ "maps-geo-json-edit-source": "Edit tab text in the GeoJson namespace. This tab leads to editing of raw GeoJson without visual aids",
+ "maps-geo-json-create-source": "Page creation tab text in the GeoJson namespace. This tab leads to editing of raw GeoJson without visual aids"
}
diff --git a/www/wiki/extensions/Maps/i18n/roa-tara.json b/www/wiki/extensions/Maps/i18n/roa-tara.json
index 5fa0db84..2622bb6b 100644
--- a/www/wiki/extensions/Maps/i18n/roa-tara.json
+++ b/www/wiki/extensions/Maps/i18n/roa-tara.json
@@ -36,6 +36,7 @@
"maps-abb-west": "O",
"maps-latitude": "Latitudine:",
"maps-longitude": "Longitudine:",
+ "maps-leaflet-par-layers-dark": "Le strate ca sò disponibbele jndr'à 'u scacchiatore de strate quanne 'a modalità gnure jè abbiltiate. 'U prime strate avène 'ndrucate quanne ste careche 'a mappe.",
"specialpages-group-maps": "Mappe",
"mapeditor-none-text": "Ninde",
"mapeditor-done-button": "Fatte",
@@ -61,5 +62,42 @@
"validator-type-mapsrectangle": "Rettangole sciugrafeche",
"validator-type-mapsrectangle-list": "Elenghe de le rettangole",
"validator-type-mapspolygon": "Poligone sciugrafeche",
- "validator-type-mapspolygon-list": "Elenghe de le poligone sciugrafece"
+ "validator-type-mapspolygon-list": "Elenghe de le poligone sciugrafece",
+ "maps-json-editor-button-marker": "Sckaffe 'nu marcatore",
+ "maps-json-editor-button-line": "Disegne 'na linèe",
+ "maps-json-editor-button-polygon": "Disegne 'nu poligone",
+ "maps-json-editor-button-rectangle": "Sckaffe 'nu rettangole",
+ "maps-json-editor-button-circle": "Sckaffe 'nu cerchie",
+ "maps-json-editor-tooltip-marker": "Cazze sus 'a mappe pe sckaffà 'nu marcatore.",
+ "maps-json-editor-tooltip-line": "Cazze sus 'a mappe pe disegnà 'na linèe.",
+ "maps-json-editor-tooltip-polygon": "Cazze sus 'a mappe pe disegnà 'nu poligone.",
+ "maps-json-editor-tooltip-rectangle": "Cazze sus 'a mappe pe sckaffà 'nu rettangole.",
+ "maps-json-editor-tooltip-circle": "Cazze sus 'a mappe pe sckaffà 'nu cerchie.",
+ "maps-json-editor-added-marker": "Aggiunde marcatore",
+ "maps-json-editor-added-line": "Aggiunde 'na linèe",
+ "maps-json-editor-added-polygon": "Aggiunde 'nu poligone",
+ "maps-json-editor-added-rectangle": "Aggiunde 'nu rettangole",
+ "maps-json-editor-added-circle": "Aggiudne circhie",
+ "maps-json-editor-edit-removed-shapes": "Luate $1 {{PLURAL:$1|figure}}",
+ "maps-json-editor-edit-modified": "Cangiate le figure d'a mappe esistende",
+ "maps-json-editor-edit-other": "Cangiamende d'a mappe visuale",
+ "maps-json-editor-edit-failed": "Reggistrazzione d'a mappe fallite",
+ "maps-json-editor-toolbar-save-title": "Isse da 'u cangiamende e mandine tutte le cangiaminde",
+ "maps-json-editor-toolbar-save-text": "Fatte",
+ "maps-json-editor-toolbar-cancel-title": "Isse da 'u mode d'e cangiamende, scitte tutte le cangiaminde",
+ "maps-json-editor-toolbar-cancel-text": "Annulle",
+ "maps-json-editor-toolbar-clear-title": "Pulizze tutte le strate",
+ "maps-json-editor-toolbar-clear-text": "Pulizze tutte",
+ "maps-json-editor-toolbar-button-edit": "Cange le layer",
+ "maps-json-editor-toolbar-button-edit-disabled": "Nisciune layer da cangià",
+ "maps-json-editor-toolbar-button-remove": "Scangìlle le layer",
+ "maps-json-editor-toolbar-button-remove-disabled": "Nisciune layer da scangellà",
+ "maps-json-editor-toolbar-button-save": "Reggistre le cangiaminde",
+ "maps-json-editor-changes-saved": "Le cangiaminde tune onne state salvate",
+ "maps-geo-json-edit-source": "Cange 'a sorgende",
+ "maps-geo-json-create-source": "Ccreje da 'a sorgende GeoJSON",
+ "maps-geo-json-create-page-button": "Ccreje sta pàgene",
+ "maps-geo-json-create-page-creating": "Stoche a ccreje 'a pàgene...",
+ "maps-geo-json-create-page-summary": "Ccrejate 'na pàgene GeoJSON",
+ "maps-editor-edit-geojson": "Cange 'u strate GeoJSON"
}
diff --git a/www/wiki/extensions/Maps/i18n/ru.json b/www/wiki/extensions/Maps/i18n/ru.json
index ac387d8e..3446cc78 100644
--- a/www/wiki/extensions/Maps/i18n/ru.json
+++ b/www/wiki/extensions/Maps/i18n/ru.json
@@ -30,7 +30,8 @@
"Vlad5250",
"Shaleych",
"NBS",
- "Diralik"
+ "Diralik",
+ "Katunchik"
]
},
"maps-desc": "Позволяет встраивать динамические карты в вики-страницы, геокодировать адреса и выполнять другие географические действия",
@@ -144,6 +145,7 @@
"maps-leaflet-par-overlaylayers": "Слои подложки, которые будут показаны при загрузке карты.",
"maps-leaflet-par-maxclusterradius": "Максимальный радиус, который кластер будет покрывать от центрального маркера (в пикселях).",
"maps-leaflet-par-clusterspiderfy": "Когда вы нажимаете на кластер на нижнем уровне масштаба, то он разворачивается таким образом, чтобы вы могли увидеть все его маркеры.",
+ "maps-leaflet-par-clicktarget": "При нажатии на карту вы будете перенаправлены на этот URL. %lat% заменяется на широту, а %long% на долготу",
"maps_click_to_activate": "Нажмите для активации карты",
"maps_centred_on": "Центр карты — $1, $2.",
"maps-par-mappingservice": "Позволяет выбрать сервис карт, который будет использоваться.",
@@ -247,5 +249,40 @@
"validator-type-mapspolygon": "Географический полигон",
"validator-type-mapspolygon-list": "Перечень географических полигонов",
"validator-type-wmsoverlay": "Оверлей веб-карты",
- "validator-type-jsonfile": "текст"
+ "maps-json-editor-button-marker": "Разместить маркер",
+ "maps-json-editor-button-line": "Нарисовать линию",
+ "maps-json-editor-button-polygon": "Нарисовать многоугольник",
+ "maps-json-editor-button-rectangle": "Разместить прямоугольник",
+ "maps-json-editor-button-circle": "Разместить круг",
+ "maps-json-editor-tooltip-marker": "Кликните на карте, чтобы разместить маркер.",
+ "maps-json-editor-tooltip-line": "Кликните на карте, чтобы нарисовать линию.",
+ "maps-json-editor-tooltip-polygon": "Кликните на карте, чтобы нарисовать многоугольник.",
+ "maps-json-editor-tooltip-rectangle": "Кликните на карте, чтобы разместить прямоугольник.",
+ "maps-json-editor-tooltip-circle": "Кликните на карте, чтобы разместить круг.",
+ "maps-json-editor-added-marker": "Добавлен маркер",
+ "maps-json-editor-added-line": "Добавлена линия",
+ "maps-json-editor-added-polygon": "Добавлен многоугольник",
+ "maps-json-editor-added-rectangle": "Добавлен прямоугольник",
+ "maps-json-editor-added-circle": "Добавлен круг",
+ "maps-json-editor-edit-removed-shapes": "Удалены $1 {{PLURAL:$1|фигура|фигур}}",
+ "maps-json-editor-edit-modified": "Модифицированы существующие фигуры карты",
+ "maps-json-editor-edit-other": "Редактирование визуальной карты",
+ "maps-json-editor-edit-failed": "Не удалось сохранить карту.",
+ "maps-json-editor-toolbar-save-title": "Сохранить изменения",
+ "maps-json-editor-toolbar-save-text": "Сохранить",
+ "maps-json-editor-toolbar-cancel-title": "Отменить редактирование, сбросить все изменения",
+ "maps-json-editor-toolbar-cancel-text": "Отменить",
+ "maps-json-editor-toolbar-clear-title": "Очистить все слои",
+ "maps-json-editor-toolbar-clear-text": "Очистить всё",
+ "maps-json-editor-toolbar-button-edit": "Редактировать слои",
+ "maps-json-editor-toolbar-button-edit-disabled": "Нет слоёв для редактирования",
+ "maps-json-editor-toolbar-button-remove": "Удалить слои",
+ "maps-json-editor-toolbar-button-remove-disabled": "Нет слоёв для удаления",
+ "maps-geo-json-edit-source": "Править код",
+ "maps-geo-json-create-source": "Создать исходный код GeoJSON",
+ "maps-geo-json-create-page-button": "Создать эту страницу",
+ "maps-geo-json-create-page-creating": "Создаётся страница...",
+ "maps-geo-json-create-page-summary": "Создана страница GeoJSON",
+ "tag-maps-visual-edit": "Редактирование визуальной карты",
+ "tag-maps-visual-edit-description": "Редактирование производится с помощью визуального редактора, предоставленного [https://www.mediawiki.org/wiki/Extension:Maps Maps extension]"
}
diff --git a/www/wiki/extensions/Maps/i18n/sh.json b/www/wiki/extensions/Maps/i18n/sh.json
new file mode 100644
index 00000000..395e05c4
--- /dev/null
+++ b/www/wiki/extensions/Maps/i18n/sh.json
@@ -0,0 +1,69 @@
+{
+ "@metadata": {
+ "authors": [
+ "Vlad5250"
+ ]
+ },
+ "right-geocode": "Geokodiranje",
+ "action-geocode": "izvršavanje geokodiranja na ovom wikiju",
+ "maps-tracking-category": "Stranica sa zemljovidom iscrtanim proširenjem \"Zemljovidi\"",
+ "maps-markers": "Označivači",
+ "maps-searchmarkers-text": "Filtriranje označivača",
+ "maps-others": "ostali",
+ "maps-ns-layer": "Sloj",
+ "maps-ns-layer-talk": "Razgovor o sloju",
+ "maps-layer-property": "Svojstvo",
+ "maps-layer-value": "Vrijednost",
+ "maps-layer-errors": "Greške",
+ "maps-layerdef-invalid": "{{PLURAL:$1|Nevrijedeća odredba|Nevrijedeće odreabe}}",
+ "maps-layerdef-invalid-fatal": "Kobna nevrijedeća odredba",
+ "maps-layerdef-wrong-namespace": "Odredbe slojeva su vrijedeće samo na stranicama imenskog prostora »$1«",
+ "maps-layerpage-nousage": "Zasad nema stranica što koriste ovaj sloj.",
+ "maps-error-invalid-layertype": "Nema slojeva tipa \"$1\". {{PLURAL:$3|Podržan je samo ovaj tip|Podržani su samo sljedeći tipovi}}: $2",
+ "maps-error-no-layertype": "Će treba da navedete tip sloja. {{PLURAL:$2|Podržan je samo ovaj tip|Podržani su samo sljedeći tipovi}}: $1",
+ "validation-error-invalid-layer": "Parametar $1 mora da bude valjani sloj.",
+ "validation-error-invalid-layers": "Parametar $1 mora da bude jedan ili više valjanih slojeva.",
+ "maps-displaymap-par-polygons": "Mnogokuti za prikaz",
+ "maps-displaymap-par-rectangles": "Pravokutnici za prikaz",
+ "maps-displaymap-par-static": "Naparvi zemljovid nedvižnim",
+ "validation-error-invalid-location": "Parametar $1 mora da predstavlja validna lokacija.",
+ "maps_click_to_activate": "Škljocnite za aktiviranje zemljovida",
+ "maps_centred_on": "Zemljovid je centriran na $1, $2.",
+ "mapeditor": "Uređivač zemljovida",
+ "specialpages-group-maps": "Zemljovidi",
+ "mapeditor-none-text": "Nema",
+ "mapeditor-done-button": "Gotovo",
+ "mapeditor-remove-button": "Ukloni",
+ "mapeditor-import-button2": "Uvezi - Увези",
+ "mapeditor-export-button": "Izvezi u vikikôdu",
+ "mapeditor-import-button": "Uvezi iz wikikôda",
+ "mapeditor-mapparam-button": "Uredi parametre zemljovida",
+ "mapeditor-clear-button": "Očisti zemljovid",
+ "mapeditor-form-title": "Uredi detalje",
+ "mapeditor-link-title-switcher-popup-text": "Otskok s tekstom",
+ "mapeditor-link-title-switcher-link-text": "Poveznica (link)",
+ "mapeditor-form-field-title": "Naslov",
+ "mapeditor-form-field-text": "Tekst",
+ "mapeditor-form-field-link": "Poveznica (link)",
+ "mapeditor-form-field-icon": "Ikona",
+ "mapeditor-form-field-group": "Grupa",
+ "mapeditor-mapparam-title": "Uredi parametre zemljovida",
+ "mapeditor-imageoverlay-button": "Dodaj oblogu od slike",
+ "mapeditor-form-field-image": "Slika",
+ "maps-json-editor-added-marker": "Dodan označivač",
+ "maps-json-editor-added-line": "Dodana linija",
+ "maps-json-editor-added-polygon": "Dodan mnogokutnik",
+ "maps-json-editor-added-rectangle": "Dodan pravokutnik",
+ "maps-json-editor-added-circle": "Dodan krug",
+ "maps-json-editor-edit-removed-shapes": "Uklonjenih $1 oblika",
+ "maps-json-editor-toolbar-save-title": "Spremi promjene",
+ "maps-json-editor-toolbar-save-text": "Snimi",
+ "maps-json-editor-toolbar-cancel-title": "Odustani uređivanje (odbacivanje sve promjene)",
+ "maps-json-editor-toolbar-cancel-text": "Otkaži",
+ "maps-json-editor-toolbar-clear-title": "Očisti sve slojeve",
+ "maps-json-editor-toolbar-clear-text": "‎Očisti sve",
+ "maps-json-editor-toolbar-button-edit": "Uredi slojeve",
+ "maps-json-editor-toolbar-button-edit-disabled": "Nema slojeva da urediti",
+ "maps-json-editor-toolbar-button-remove": "Izbiši slojeve",
+ "maps-json-editor-toolbar-button-remove-disabled": "Nema slojeva za obrisati"
+}
diff --git a/www/wiki/extensions/Maps/i18n/sv.json b/www/wiki/extensions/Maps/i18n/sv.json
index 0e40304e..58a95034 100644
--- a/www/wiki/extensions/Maps/i18n/sv.json
+++ b/www/wiki/extensions/Maps/i18n/sv.json
@@ -125,14 +125,15 @@
"maps-leaflet-par-overlaylayers": "Överläggslagret som kommer att visas när kartan läser in.",
"maps-leaflet-par-maxclusterradius": "Den maximala radien som ett kluster kommer att täcka från centrummarkören (i bildpunkter)",
"maps-leaflet-par-clusterspiderfy": "När du klickar på ett kluster på den lägsta zoomnivån sprider vi ut den så du kan se alla dess markörer.",
+ "maps-leaflet-par-clicktarget": "När du klickar på kartan kommer du skickas till denna webbadress. %lat% ersätts med latituden och %long% med longituden",
"maps_click_to_activate": "Klicka för att aktivera karta",
"maps_centred_on": "Karta centrerad på $1, $2.",
"maps-par-mappingservice": "Tillåter att karttjänsten som kommer att användas för att skapa kartan anges.",
"maps-par-resizable": "Gör att kartans storlek kan ändras genom att dra i dess nedre högra hörnet.",
"maps-par-searchmarkers": "Gör det möjligt att söka efter specifika markörer via ett fält som är inbäddat i kartan.",
"maps-par-zoom": "Zoomnivån för kartan. För kartor med markörer kommer som standard den mest inzoomade nivå som visar alla markörer att användas.",
- "maps-par-width": "Tillåter att kartans bredd ställs in. Som standard kommer pixlar att antas som enhet, men du kan uttryckligen ange en av dessa enheter: px, ex, em, %.",
- "maps-par-height": "Tillåter att kartans höjd ställs in. Som standard kommer pixlar att antas som enhet, men du kan uttryckligen ange en av dessa enheter: px, ex, em, %.",
+ "maps-par-width": "Gör det möjligt att ändra kartans bredd. Som standard kommer den antagna enheten vara bildpunkter, men du kan uttryckligen ange en av dessa enheter: px, ex, em.",
+ "maps-par-height": "Gör det möjligt att ändra kartans höjd. Som standard kommer den antagna enheten vara bildpunkter, men du kan uttryckligen ange en av dessa enheter: px, ex, em.",
"maps-par-centre": "Platsen där kartan ska vara centrerad",
"maps-par-enable-fullscreen": "Aktivera helskärmsknappen",
"maps-par-kml": "KML-filer att ladda upp på kartan.",
@@ -228,5 +229,43 @@
"validator-type-mapspolygon": "Geografisk polygon",
"validator-type-mapspolygon-list": "Lista över geografiska polygoner",
"validator-type-wmsoverlay": "Webbmappstjänst-överlägg",
- "validator-type-jsonfile": "text"
+ "maps-json-editor-button-marker": "Placera en markör",
+ "maps-json-editor-button-line": "Rita en linje",
+ "maps-json-editor-button-polygon": "Rita en polygon",
+ "maps-json-editor-button-rectangle": "Placera en rektangel",
+ "maps-json-editor-button-circle": "Placera en cirkel",
+ "maps-json-editor-tooltip-marker": "Klicka på kartan för att placera markören.",
+ "maps-json-editor-tooltip-line": "Klicka på kartan för att rita en linje.",
+ "maps-json-editor-tooltip-polygon": "Klicka på kartan för att rita en polygon.",
+ "maps-json-editor-tooltip-rectangle": "Klicka på kartan för att placera rektangeln.",
+ "maps-json-editor-tooltip-circle": "Klicka på kartan för att placera cirkeln.",
+ "maps-json-editor-added-marker": "Lade till markör",
+ "maps-json-editor-added-line": "Lade till linje",
+ "maps-json-editor-added-polygon": "Lade till polygon",
+ "maps-json-editor-added-rectangle": "Lade till rektangel",
+ "maps-json-editor-added-circle": "Lade till cirkel",
+ "maps-json-editor-edit-removed-shapes": "Tog bort $1 {{PLURAL:$1|form|former}}",
+ "maps-json-editor-edit-modified": "Modifierade befintliga kartformer",
+ "maps-json-editor-edit-other": "Visuell kartredigering",
+ "maps-json-editor-edit-failed": "Misslyckades med att spara kartan",
+ "maps-json-editor-toolbar-save-title": "Avsluta redigeringsläget och behåll alla ändringar",
+ "maps-json-editor-toolbar-save-text": "Klar",
+ "maps-json-editor-toolbar-cancel-title": "Avslutar redigeringen, kasserar alla ändringar",
+ "maps-json-editor-toolbar-cancel-text": "Avbryt",
+ "maps-json-editor-toolbar-clear-title": "Rensa alla lager",
+ "maps-json-editor-toolbar-clear-text": "Rensa alla",
+ "maps-json-editor-toolbar-button-edit": "Redigera lager",
+ "maps-json-editor-toolbar-button-edit-disabled": "Ingen lager att redigera",
+ "maps-json-editor-toolbar-button-remove": "Radera lager",
+ "maps-json-editor-toolbar-button-remove-disabled": "Inga lager att radera",
+ "maps-json-editor-toolbar-button-save": "Spara ändringar",
+ "maps-json-editor-changes-saved": "Dina ändringar har sparats",
+ "maps-geo-json-edit-source": "Redigera källa",
+ "maps-geo-json-create-source": "Skapa från GeoJSON-källa",
+ "maps-geo-json-create-page-button": "Skapa denna sida",
+ "maps-geo-json-create-page-creating": "Skapar sida...",
+ "maps-geo-json-create-page-summary": "Skapade GeoJSON-sida",
+ "maps-editor-edit-geojson": "Redigera GeoJSON-lager",
+ "tag-maps-visual-edit": "Visuell kartredigering",
+ "tag-maps-visual-edit-description": "Redigeringen gjordes med den visuella redigeraren som tillhandahålls av tillägget [https://www.mediawiki.org/wiki/Extension:Maps Maps]"
}
diff --git a/www/wiki/extensions/Maps/i18n/tt-cyrl.json b/www/wiki/extensions/Maps/i18n/tt-cyrl.json
new file mode 100644
index 00000000..8ddffba4
--- /dev/null
+++ b/www/wiki/extensions/Maps/i18n/tt-cyrl.json
@@ -0,0 +1,73 @@
+{
+ "@metadata": {
+ "authors": [
+ "Ерней",
+ "Ильнар"
+ ]
+ },
+ "right-geocode": "Геокодлау",
+ "maps_map": "Харита",
+ "maps-copycoords-prompt": "CTRL+C, ENTER",
+ "maps-others": "башка",
+ "maps-ns-layer": "Катлам",
+ "maps-ns-layer-talk": "Катлам бәхәсе",
+ "maps-layer-errors": "Хаталар",
+ "maps-layerdef-invalid": "Ярамаган {{PLURAL:$1|билгеләмә|билгеләмәләр}}",
+ "maps-layerdef-invalid-fatal": "Котылгысыз ярамаган билгеләмә",
+ "maps-layer-of-type": "$1 төрендәге катлам",
+ "maps-layer-of-type-and-name": "$1 төрендәге $2 катламы",
+ "maps-abb-north": "Тя",
+ "maps-abb-east": "Кч",
+ "maps-abb-south": "Кя",
+ "maps-abb-west": "Кб",
+ "maps-latitude": "Киңлек:",
+ "maps-longitude": "Озынлык:",
+ "mapeditor": "Харита төзәткече",
+ "specialpages-group-maps": "Хариталар",
+ "mapeditor-none-text": "Һични",
+ "mapeditor-done-button": "Тәмам",
+ "mapeditor-remove-button": "Бетерү",
+ "mapeditor-import-button2": "Кертү",
+ "mapeditor-link-title-switcher-link-text": "Сылтама",
+ "mapeditor-form-field-text": "Текст",
+ "mapeditor-form-field-link": "Сылтама",
+ "mapeditor-form-field-strokecolor": "Кайма төсе",
+ "mapeditor-form-field-strokeopacity": "Кайма тоныклыгы",
+ "mapeditor-form-field-strokeweight": "Кайма калынлыгы",
+ "mapeditor-form-field-image": "Сурәт",
+ "semanticmaps-latitude": "Киңлек: $1",
+ "semanticmaps-longitude": "Озынлык: $1",
+ "semanticmaps-altitude": "Югарылык: $1",
+ "semanticmaps-forminput-locations": "Урыннар",
+ "validator-type-mapscircle-list": "Түгәрәкләр исемлеге",
+ "validator-type-mapsline-list": "Сызыклар исемлеге",
+ "validator-type-mapsrectangle-list": "Турыпочмаклыклар исемлеге",
+ "maps-json-editor-button-line": "Сызык сызу",
+ "maps-json-editor-button-polygon": "Күппочмаклык ясау",
+ "maps-json-editor-button-rectangle": "Турыпочмаклыкны урнаштыру",
+ "maps-json-editor-button-circle": "Түгәрәкне урнаштыру",
+ "maps-json-editor-tooltip-line": "Сызык сызу өчен харитага басыгыз.",
+ "maps-json-editor-tooltip-polygon": "Күппочмаклык ясау өчен харитага басыгыз.",
+ "maps-json-editor-tooltip-rectangle": "Турыпочмаклыкны урнаштыру өчен харитага басыгыз",
+ "maps-json-editor-tooltip-circle": "Түгәрәкне урнаштыру өчен харитага басыгыз",
+ "maps-json-editor-added-line": "Сызык өстәлде",
+ "maps-json-editor-added-polygon": "Күппочмаклык өстәлде",
+ "maps-json-editor-added-rectangle": "Турыпочмаклык өстәлде",
+ "maps-json-editor-added-circle": "Түгәрәк өстәлде",
+ "maps-json-editor-edit-removed-shapes": "$1 {{PLURAL:$1|шәкел}} бетерелде",
+ "maps-json-editor-edit-other": "Күрсәтмәле хаританы үзгәртү",
+ "maps-json-editor-edit-failed": "Хаританы саклап булмады",
+ "maps-json-editor-toolbar-save-title": "Үзгәрешләрне саклау",
+ "maps-json-editor-toolbar-save-text": "Саклау",
+ "maps-json-editor-toolbar-cancel-text": "Кире алу",
+ "maps-json-editor-toolbar-clear-title": "Барлык катламнарны бушату",
+ "maps-json-editor-toolbar-clear-text": "Барысын да бушату",
+ "maps-json-editor-toolbar-button-edit": "Катламнарны үзгәртү",
+ "maps-json-editor-toolbar-button-edit-disabled": "Үзгәртергә катламнар юк",
+ "maps-json-editor-toolbar-button-remove": "Катламнарны бетерү",
+ "maps-json-editor-toolbar-button-remove-disabled": "Бетерергә катламнар юк",
+ "maps-geo-json-edit-source": "Вики-текстны үзгәртү",
+ "maps-geo-json-create-page-button": "Бу битне төзү",
+ "maps-geo-json-create-page-creating": "Бит төзелә…",
+ "tag-maps-visual-edit": "Күрсәтмәле хаританы үзгәртү"
+}
diff --git a/www/wiki/extensions/Maps/i18n/uk.json b/www/wiki/extensions/Maps/i18n/uk.json
index 8cadcaec..341da513 100644
--- a/www/wiki/extensions/Maps/i18n/uk.json
+++ b/www/wiki/extensions/Maps/i18n/uk.json
@@ -227,6 +227,5 @@
"validator-type-mapsrectangle-list": "Список прямокутників",
"validator-type-mapspolygon": "Географічний багатокутник",
"validator-type-mapspolygon-list": "Список географічних багатокутників",
- "validator-type-wmsoverlay": "Накладення Web Map Service",
- "validator-type-jsonfile": "текст"
+ "validator-type-wmsoverlay": "Накладення Web Map Service"
}
diff --git a/www/wiki/extensions/Maps/i18n/vi.json b/www/wiki/extensions/Maps/i18n/vi.json
index fa1873d0..b8717a5f 100644
--- a/www/wiki/extensions/Maps/i18n/vi.json
+++ b/www/wiki/extensions/Maps/i18n/vi.json
@@ -102,6 +102,5 @@
"semanticmaps-par-showtitle": "Tên tùy chọn của cửa sổ thông tin đánh dấu. Có thể để trống để định dạng nội dung cửa sổ thông tin dùng bản mẫu.",
"semanticmaps-par-centre": "Trung tâm của bản đồ. Nếu không có, bản đồ sẽ tự động chọn trung tâm tối ưu bao gồm tất cả các dấu trên bản đồ.",
"semanticmaps-par-template": "Bản đồ dùng để định dạng nội dung của cửa sổ thông tin.",
- "semanticmaps-par-geocodecontrol": "Hiện điều khiển mã hóa địa lý.",
- "validator-type-jsonfile": "chữ"
+ "semanticmaps-par-geocodecontrol": "Hiện điều khiển mã hóa địa lý."
}
diff --git a/www/wiki/extensions/Maps/i18n/zh-hans.json b/www/wiki/extensions/Maps/i18n/zh-hans.json
index 13fc0e63..21af4ef2 100644
--- a/www/wiki/extensions/Maps/i18n/zh-hans.json
+++ b/www/wiki/extensions/Maps/i18n/zh-hans.json
@@ -14,7 +14,8 @@
"阿pp",
"Hanyanbo98",
"夢蝶葬花",
- "Angrydog001"
+ "Angrydog001",
+ "Josephine W."
]
},
"maps-desc": "允许嵌入动态地图到wiki页面,将地址转换为地理编码,及进行其他地理学操作",
@@ -231,5 +232,10 @@
"validator-type-mapspolygon": "地理多边形",
"validator-type-mapspolygon-list": "地理多边形列表",
"validator-type-wmsoverlay": "网络地图服务覆盖",
- "validator-type-jsonfile": "文本"
+ "maps-json-editor-toolbar-save-title": "保存修改",
+ "maps-json-editor-toolbar-save-text": "保存",
+ "maps-json-editor-toolbar-cancel-text": "取消",
+ "maps-json-editor-toolbar-clear-text": "清空",
+ "maps-geo-json-create-page-button": "创建本页面",
+ "maps-geo-json-create-page-creating": "正在创建页面……"
}
diff --git a/www/wiki/extensions/Maps/i18n/zh-hant.json b/www/wiki/extensions/Maps/i18n/zh-hant.json
index e96f3010..39711ed9 100644
--- a/www/wiki/extensions/Maps/i18n/zh-hant.json
+++ b/www/wiki/extensions/Maps/i18n/zh-hant.json
@@ -17,7 +17,7 @@
"Kly"
]
},
- "maps-desc": "可讓 Wiki 頁面嵌入動態地圖,支援使用地理編碼標記位置,以及地理相關操作",
+ "maps-desc": "允許使用 Google 地圖或 Leaflet 讓 Wiki 頁面嵌入動態地圖。具備視覺化編輯器,且可選擇與 Semantic MediaWiki 整合,並支援 GeoJSON 地理編碼功能。",
"right-geocode": "地理編碼",
"action-geocode": "在此 wiki 上進行地理編碼",
"maps_map": "地圖",
@@ -125,9 +125,11 @@
"maps_leaflet": "Leaflet",
"maps-leaflet-par-defzoom": "允許設定地圖的預設縮放層級。",
"maps-leaflet-par-layers": "此圖層將於地圖加載時顯示",
+ "maps-leaflet-par-layers-dark": "當暗黑模式啟動時,圖層會在圖層選擇器裡可用。當地圖載入會先秀出第一個圖層。",
"maps-leaflet-par-overlaylayers": "此覆蓋圖層將於地圖加載時顯示。",
"maps-leaflet-par-maxclusterradius": "叢集自中央標記起會涵蓋的最大半徑(像素)。",
"maps-leaflet-par-clusterspiderfy": "當您在下方縮放層級點擊叢集時,我們會將其放射狀化來讓您可以查看所有的標記。",
+ "maps-leaflet-par-clicktarget": "當您在地圖上點擊時,您將會被發送到此 URL。緯度會替換成 %lat%,經度會替換成 %long%",
"maps_click_to_activate": "按一下以啟動地圖",
"maps_centred_on": "地圖以 $1, $2 為中心。",
"maps-par-mappingservice": "允許設定用於產生地圖的地圖服務。",
@@ -135,7 +137,7 @@
"maps-par-searchmarkers": "允許透過內嵌在地圖的欄位來搜尋特定標記。",
"maps-par-zoom": "用於地圖的縮放層級。可用在帶標記的地圖,預設會放大到最高層級並顯示所有標記。",
"maps-par-width": "允許設定地圖寬度。預設會以像素為單位,而您可明確指定以下單位之一:px、ex、em、%。",
- "maps-par-height": "允許設定地圖寬度。預設會以像素為單位,而您可明確指定以下單位之一:px、ex、em、%。",
+ "maps-par-height": "允許設定地圖寬度。預設會以像素為單位,而您可明確指定以下單位之一:px、ex、em。",
"maps-par-centre": "在地圖上的位置應置中",
"maps-par-enable-fullscreen": "開啟全螢幕的按鈕",
"maps-par-kml": "載入到地圖的 KML 檔案。",
@@ -231,5 +233,43 @@
"validator-type-mapspolygon": "地理多邊形",
"validator-type-mapspolygon-list": "地理多邊形清單",
"validator-type-wmsoverlay": "網路地圖服務疊加",
- "validator-type-jsonfile": "文字"
+ "maps-json-editor-button-marker": "放置一個標記",
+ "maps-json-editor-button-line": "畫一條直線",
+ "maps-json-editor-button-polygon": "畫一個多邊形",
+ "maps-json-editor-button-rectangle": "放置一個矩形",
+ "maps-json-editor-button-circle": "放置一個圓形",
+ "maps-json-editor-tooltip-marker": "點擊地圖來放置標記。",
+ "maps-json-editor-tooltip-line": "點擊地圖來畫出直線。",
+ "maps-json-editor-tooltip-polygon": "點擊地圖來畫出多邊形。",
+ "maps-json-editor-tooltip-rectangle": "點擊地圖來放置矩形。",
+ "maps-json-editor-tooltip-circle": "點擊地圖來放置圓形。",
+ "maps-json-editor-added-marker": "已新增標記",
+ "maps-json-editor-added-line": "已新增直線",
+ "maps-json-editor-added-polygon": "已新增多邊形",
+ "maps-json-editor-added-rectangle": "已新增矩形",
+ "maps-json-editor-added-circle": "已新增圓形",
+ "maps-json-editor-edit-removed-shapes": "已移除 $1 個{{PLURAL:$1|形狀}}",
+ "maps-json-editor-edit-modified": "已修改現有地圖形狀",
+ "maps-json-editor-edit-other": "視覺化地圖編輯",
+ "maps-json-editor-edit-failed": "儲存地圖失敗。",
+ "maps-json-editor-toolbar-save-title": "離開編輯模式時保持所有變更",
+ "maps-json-editor-toolbar-save-text": "完成",
+ "maps-json-editor-toolbar-cancel-title": "離開編輯模式,放棄所有變更",
+ "maps-json-editor-toolbar-cancel-text": "取消",
+ "maps-json-editor-toolbar-clear-title": "清除全部圖層",
+ "maps-json-editor-toolbar-clear-text": "清除全部",
+ "maps-json-editor-toolbar-button-edit": "編輯圖層",
+ "maps-json-editor-toolbar-button-edit-disabled": "沒有可編輯的圖層",
+ "maps-json-editor-toolbar-button-remove": "刪除圖層",
+ "maps-json-editor-toolbar-button-remove-disabled": "沒有可刪除的圖層",
+ "maps-json-editor-toolbar-button-save": "儲存變更",
+ "maps-json-editor-changes-saved": "已儲存您的變更。",
+ "maps-geo-json-edit-source": "編輯來源",
+ "maps-geo-json-create-source": "從 GeoJSON 來源建立",
+ "maps-geo-json-create-page-button": "建立此頁面",
+ "maps-geo-json-create-page-creating": "正在建立頁面…",
+ "maps-geo-json-create-page-summary": "已建立 GeoJSON 頁面",
+ "maps-editor-edit-geojson": "編輯 GeoJSON 圖層",
+ "tag-maps-visual-edit": "視覺化地圖編輯",
+ "tag-maps-visual-edit-description": "以[https://www.mediawiki.org/wiki/Extension:Maps 地圖擴充套件]所提供的視覺化編輯器做出編輯"
}
diff --git a/www/wiki/extensions/Maps/resources/GoogleMaps/ext.maps.googlemaps3.js b/www/wiki/extensions/Maps/resources/GoogleMaps/ext.maps.googlemaps3.js
index ce5880c2..8a7b9b49 100644
--- a/www/wiki/extensions/Maps/resources/GoogleMaps/ext.maps.googlemaps3.js
+++ b/www/wiki/extensions/Maps/resources/GoogleMaps/ext.maps.googlemaps3.js
@@ -1,25 +1,16 @@
-/**
- * JavaScript for Google Maps v3 maps in the Maps extension.
- * @see https://www.mediawiki.org/wiki/Extension:Maps
- *
- * @licence GNU GPL v2+
- * @author Jeroen De Dauw <jeroendedauw at gmail dot com>
- */
-(function( $, mw ) {
-
- $( document ).ready( function() {
+window.mapsGoogleList = [];
- if ( typeof google === 'undefined' ) {
- $( '.maps-googlemaps3' ).text( mw.msg( 'maps-googlemaps3-incompatbrowser' ) );
- }
- else {
- $( '.maps-googlemaps3' ).each( function() {
+(function( $, mw ) {
+ mw.hook( 'wikipage.content' ).add( function ( $content ) {
+ if( typeof google === 'undefined' ) {
+ $content.find( '.maps-googlemaps3' ).text( mw.msg( 'maps-googlemaps3-incompatbrowser' ) );
+ } else {
+ $content.find( '.maps-googlemaps3' ).each( function() {
var $this = $( this );
- var map = $this.googlemaps( $.parseJSON( $this.find( 'div').text() ) );
- window.maps.googlemapsList.push(map);
+ window.mapsGoogleList.push(
+ $this.googlemaps( JSON.parse( $this.find( 'div.mapdata' ).text() ) )
+ );
} );
}
-
} );
-
-})( window.jQuery, mediaWiki );
+})( window.jQuery, window.mediaWiki );
diff --git a/www/wiki/extensions/Maps/resources/GoogleMaps/geoxml3/ProjectedOverlay.js b/www/wiki/extensions/Maps/resources/GoogleMaps/geoxml3/ProjectedOverlay.js
index 4056c2c3..d655ed9e 100644
--- a/www/wiki/extensions/Maps/resources/GoogleMaps/geoxml3/ProjectedOverlay.js
+++ b/www/wiki/extensions/Maps/resources/GoogleMaps/geoxml3/ProjectedOverlay.js
@@ -13,127 +13,139 @@
// addZoom: Added Zoom factor as a parameter to the imageUrl (include complete parameter, including separater like '?zoom='
// percentOpacity: Default 50, percent opacity to use when the image is loaded 0-100.
// id: Default imageUrl, ID of the div
+// rotation: default 0, degrees clockwise to rotate the image
//
-
+
function ProjectedOverlay(map, imageUrl, bounds, opts)
{
- google.maps.OverlayView.call(this);
-
- this.map_ = map;
- this.url_ = imageUrl ;
- this.bounds_ = bounds ;
- this.addZ_ = opts.addZoom || '' ; // Add the zoom to the image as a parameter
- this.id_ = opts.id || this.url_ ; // Added to allow for multiple images
- this.percentOpacity_ = opts.percentOpacity || 50 ;
-
- this.setMap(map);
+ google.maps.OverlayView.call(this);
+
+ this.map_ = map;
+ this.url_ = imageUrl ;
+ this.bounds_ = bounds ;
+ this.addZ_ = opts.addZoom || '' ; // Add the zoom to the image as a parameter
+ this.id_ = opts.id || this.url_ ; // Added to allow for multiple images
+ this.percentOpacity_ = opts.percentOpacity || 50 ;
+ this.rotation_ = opts.rotation || 0;
+ this.setMap(map);
}
ProjectedOverlay.prototype = new google.maps.OverlayView();
ProjectedOverlay.prototype.createElement = function()
{
- var panes = this.getPanes() ;
- var div = this.div_ ;
-
- if (!div)
- {
- div = this.div_ = document.createElement("div");
- div.style.position = "absolute" ;
- div.setAttribute('id',this.id_) ;
- this.div_ = div ;
- this.lastZoom_ = -1 ;
- if( this.percentOpacity_ )
- {
- this.setOpacity(this.percentOpacity_) ;
- }
- panes.overlayLayer.appendChild(div);
- }
+ var panes = this.getPanes() ;
+ var div = this.div_ ;
+
+ if (!div)
+ {
+ div = this.div_ = document.createElement("div");
+ div.style.position = "absolute" ;
+ div.setAttribute('id',this.id_) ;
+ this.div_ = div ;
+ this.lastZoom_ = -1 ;
+ if( this.percentOpacity_ )
+ {
+ this.setOpacity(this.percentOpacity_) ;
+ }
+ if ( this.rotation_ )
+ {
+ this.setRotation(this.rotation_) ;
+ }
+ panes.overlayLayer.appendChild(div);
+ }
}
// Remove the main DIV from the map pane
ProjectedOverlay.prototype.remove = function()
{
- if (this.div_)
- {
- this.div_.parentNode.removeChild(this.div_);
- this.div_ = null;
- this.setMap(null);
- }
+ if (this.div_)
+ {
+ this.div_.parentNode.removeChild(this.div_);
+ this.div_ = null;
+ this.setMap(null);
+ }
}
// Redraw based on the current projection and zoom level...
ProjectedOverlay.prototype.draw = function(firstTime)
{
- // Creates the element if it doesn't exist already.
+ // Creates the element if it doesn't exist already.
+
+ this.createElement();
- this.createElement();
+ if (!this.div_)
+ {
+ return ;
+ }
- if (!this.div_)
- {
- return ;
- }
+ var c1 = this.get('projection').fromLatLngToDivPixel(this.bounds_.getSouthWest());
+ var c2 = this.get('projection').fromLatLngToDivPixel(this.bounds_.getNorthEast());
- var c1 = this.get('projection').fromLatLngToDivPixel(this.bounds_.getSouthWest());
- var c2 = this.get('projection').fromLatLngToDivPixel(this.bounds_.getNorthEast());
+ if (!c1 || !c2) return;
- if (!c1 || !c2) return;
+ // Now position our DIV based on the DIV coordinates of our bounds
- // Now position our DIV based on the DIV coordinates of our bounds
+ this.div_.style.width = Math.abs(c2.x - c1.x) + "px";
+ this.div_.style.height = Math.abs(c2.y - c1.y) + "px";
+ this.div_.style.left = Math.min(c2.x, c1.x) + "px";
+ this.div_.style.top = Math.min(c2.y, c1.y) + "px";
- this.div_.style.width = Math.abs(c2.x - c1.x) + "px";
- this.div_.style.height = Math.abs(c2.y - c1.y) + "px";
- this.div_.style.left = Math.min(c2.x, c1.x) + "px";
- this.div_.style.top = Math.min(c2.y, c1.y) + "px";
+ var url = this.url_ ;
- // Do the rest only if the zoom has changed...
-
- if ( this.lastZoom_ == this.map_.getZoom() )
- {
- return ;
- }
+ if ( this.addZ_ )
+ {
+ url += this.addZ_ + this.map_.getZoom() ;
+ }
- this.lastZoom_ = this.map_.getZoom() ;
+ this.div_.innerHTML = '<img src="' + url + '" width=' + this.div_.style.width + ' height=' + this.div_.style.height + ' >' ;
- var url = this.url_ ;
+ // Do the rest only if the zoom has changed...
- if ( this.addZ_ )
- {
- url += this.addZ_ + this.map_.getZoom() ;
- }
+ if ( this.lastZoom_ == this.map_.getZoom() )
+ {
+ return ;
+ }
- this.div_.innerHTML = '<img src="' + url + '" width=' + this.div_.style.width + ' height=' + this.div_.style.height + ' >' ;
+ this.lastZoom_ = this.map_.getZoom() ;
}
ProjectedOverlay.prototype.setOpacity=function(opacity)
{
- if (opacity < 0)
- {
- opacity = 0 ;
- }
- if(opacity > 100)
- {
- opacity = 100 ;
- }
- var c = opacity/100 ;
-
- if (typeof(this.div_.style.filter) =='string')
- {
- this.div_.style.filter = 'alpha(opacity:' + opacity + ')' ;
- }
- if (typeof(this.div_.style.KHTMLOpacity) == 'string' )
- {
- this.div_.style.KHTMLOpacity = c ;
- }
- if (typeof(this.div_.style.MozOpacity) == 'string')
- {
- this.div_.style.MozOpacity = c ;
- }
- if (typeof(this.div_.style.opacity) == 'string')
- {
- this.div_.style.opacity = c ;
- }
+ if (opacity < 0)
+ {
+ opacity = 0 ;
+ }
+ if(opacity > 100)
+ {
+ opacity = 100 ;
+ }
+ var c = opacity/100 ;
+
+ if (typeof(this.div_.style.filter) =='string')
+ {
+ this.div_.style.filter = 'alpha(opacity:' + opacity + ')' ;
+ }
+ if (typeof(this.div_.style.KHTMLOpacity) == 'string' )
+ {
+ this.div_.style.KHTMLOpacity = c ;
+ }
+ if (typeof(this.div_.style.MozOpacity) == 'string')
+ {
+ this.div_.style.MozOpacity = c ;
+ }
+ if (typeof(this.div_.style.opacity) == 'string')
+ {
+ this.div_.style.opacity = c ;
+ }
+}
+ProjectedOverlay.prototype.setRotation=function(deg)
+{
+ this.div_.style.webkitTransform = 'rotate('+deg+'deg)';
+ this.div_.style.mozTransform = 'rotate('+deg+'deg)';
+ this.div_.style.msTransform = 'rotate('+deg+'deg)';
+ this.div_.style.oTransform = 'rotate('+deg+'deg)';
+ this.div_.style.transform = 'rotate('+deg+'deg)';
}
-
diff --git a/www/wiki/extensions/Maps/resources/GoogleMaps/geoxml3/README b/www/wiki/extensions/Maps/resources/GoogleMaps/geoxml3/README
index a82f556e..bcc2ca97 100644
--- a/www/wiki/extensions/Maps/resources/GoogleMaps/geoxml3/README
+++ b/www/wiki/extensions/Maps/resources/GoogleMaps/geoxml3/README
@@ -2,4 +2,4 @@ geoxml3.js and ZipFile.complete.js has been fetched from the googlecode project
from the branch kmz (which seems to be the branch containing the most features)
http://code.google.com/p/geoxml3/source/browse/#svn%2Fbranches%2Fkmz
-The latest version fetched when writing this was rev 79. \ No newline at end of file
+The latest version fetched when writing this was https://github.com/geocodezip/geoxml3/tree/a07796fa205d9d40a3c6f898a44324245935984f.
diff --git a/www/wiki/extensions/Maps/resources/GoogleMaps/geoxml3/ZipFile.complete.js b/www/wiki/extensions/Maps/resources/GoogleMaps/geoxml3/ZipFile.complete.js
index d49bb056..f2f374cf 100644
--- a/www/wiki/extensions/Maps/resources/GoogleMaps/geoxml3/ZipFile.complete.js
+++ b/www/wiki/extensions/Maps/resources/GoogleMaps/geoxml3/ZipFile.complete.js
@@ -19,143 +19,143 @@
(function(){
- if (typeof JSIO == "object"){
- var e1 = new Error("JSIO is already defined");
- e1.source = "JSIO.core.js";
- throw e1;
- }
-
- JSIO = {};
-
- JSIO.version = "2.0 2012Feb";
-
- JSIO.throwError = function(msg, source, sub) {
- var error = new Error("Error: " + msg);
- error.source = (source || this._typename || 'JSIO') + (sub ? '.'+sub : '');
- throw error;
- }
-
- // Format a number as hex. Quantities over 7ffffff will be displayed properly.
- JSIO.decimalToHexString = function(number, digits) {
- if (number < 0) {
- number = 0xFFFFFFFF + number + 1;
- }
- var r1 = number.toString(16).toUpperCase();
- if (digits) {
- r1 = "00000000" + r1;
- r1 = r1.substring(r1.length - digits);
- }
- return r1;
- };
-
- JSIO.FileType = {
- Text : 0,
- Binary : 1,
- XML : 2,
- Unknown : 3
- };
-
-
- JSIO.guessFileType = function(name) {
- if (name == "makefile") { return JSIO.FileType.Text; }
-
- var lastDot = name.lastIndexOf(".");
- if (lastDot <= 0) { return JSIO.FileType.Unknown; }
-
- var ext= name.substring(lastDot);
- if (ext == ".zip") { return JSIO.FileType.Binary; }
- if (ext == ".xlsx") { return JSIO.FileType.Binary; }
- if (ext == ".docx") { return JSIO.FileType.Binary; }
- if (ext == ".dll") { return JSIO.FileType.Binary; }
- if (ext == ".obj") { return JSIO.FileType.Binary; }
- if (ext == ".pdb") { return JSIO.FileType.Binary; }
- if (ext == ".exe") { return JSIO.FileType.Binary; }
- if (ext == ".kmz") { return JSIO.FileType.Binary; }
-
- if (ext == ".xml") { return JSIO.FileType.XML; }
- if (ext == ".xsl") { return JSIO.FileType.XML; }
- if (ext == ".kml") { return JSIO.FileType.XML; }
- if (ext == ".csproj") { return JSIO.FileType.XML; }
- if (ext == ".vbproj") { return JSIO.FileType.XML; }
- if (ext == ".shfbproj") { return JSIO.FileType.XML; }
- if (ext == ".resx") { return JSIO.FileType.XML; }
- if (ext == ".xslt") { return JSIO.FileType.XML; }
-
- if (ext == ".sln") { return JSIO.FileType.Text; }
- if (ext == ".htm") { return JSIO.FileType.Text; }
- if (ext == ".html") { return JSIO.FileType.Text; }
- if (ext == ".js") { return JSIO.FileType.Text; }
- if (ext == ".vb") { return JSIO.FileType.Text; }
- if (ext == ".txt") { return JSIO.FileType.Text; }
- if (ext == ".rels") { return JSIO.FileType.Text; }
- if (ext == ".css") { return JSIO.FileType.Text; }
- if (ext == ".cs") { return JSIO.FileType.Text; }
- if (ext == ".asp") { return JSIO.FileType.Text; }
-
- return JSIO.FileType.Unknown;
- };
-
- JSIO.stringOfLength = function (charCode, length) {
- var s3 = "";
- for (var i = 0; i < length; i++) {
- s3 += String.fromCharCode(charCode);
- }
- return s3;
- };
-
- JSIO.formatByteArray = function(b) {
- var s1 = "0000 ";
- var s2 = "";
- for (var i = 0; i < b.length; i++) {
- if (i !== 0 && i % 16 === 0) {
- s1 += " " + s2 +"\n" + JSIO.decimalToHexString(i, 4) + " ";
- s2 = "";
- }
- s1 += JSIO.decimalToHexString(b[i], 2) + " ";
- if (b[i] >=32 && b[i] <= 126) {
- s2 += String.fromCharCode(b[i]);
- } else {
- s2 += ".";
- }
- }
- if (s2.length > 0) {
- s1 += JSIO.stringOfLength(32, ((i%16>0)? ((16 - i%16) * 3) : 0) + 4) + s2;
- }
- return s1;
- };
-
- JSIO.htmlEscape = function(str) {
- return str
- .replace(new RegExp( "&", "g" ), "&amp;")
- .replace(new RegExp( "<", "g" ), "&lt;")
- .replace(new RegExp( ">", "g" ), "&gt;")
- .replace(new RegExp( "\x13", "g" ), "<br/>")
- .replace(new RegExp( "\x10", "g" ), "<br/>");
- };
-
- JSIO.massApply = function(func, funcThis, arr, needReturn) {
- var arrayLimit = 99999; // Chrome has an apply/array limit of 99999; Firefox = 491519
- if (arr.length < arrayLimit) return func.apply(funcThis, arr);
- else {
- var newThis = funcThis;
- var offset = 0;
- var end = 99999;
-
- while (offset < arr.length) {
- var arrSlice;
- if (arr.subarray) arrSlice = arr.subarray(offset, end);
- else if (arr.slice) arrSlice = arr.slice(offset, end);
-
- if (needReturn) newThis += func.apply(newThis, arrSlice);
- else func.apply(funcThis, arrSlice);
-
- offset += arrayLimit;
- end += arrayLimit;
- end = Math.min(arr.length, end);
- }
- return newThis;
- }
- }
+ if (typeof JSIO == "object"){
+ var e1 = new Error("JSIO is already defined");
+ e1.source = "JSIO.core.js";
+ throw e1;
+ }
+
+ JSIO = {};
+
+ JSIO.version = "2.0 2012Feb";
+
+ JSIO.throwError = function(msg, source, sub) {
+ var error = new Error("Error: " + msg);
+ error.source = (source || this._typename || 'JSIO') + (sub ? '.'+sub : '');
+ throw error;
+ }
+
+ // Format a number as hex. Quantities over 7ffffff will be displayed properly.
+ JSIO.decimalToHexString = function(number, digits) {
+ if (number < 0) {
+ number = 0xFFFFFFFF + number + 1;
+ }
+ var r1 = number.toString(16).toUpperCase();
+ if (digits) {
+ r1 = "00000000" + r1;
+ r1 = r1.substring(r1.length - digits);
+ }
+ return r1;
+ };
+
+ JSIO.FileType = {
+ Text : 0,
+ Binary : 1,
+ XML : 2,
+ Unknown : 3
+ };
+
+
+ JSIO.guessFileType = function(name) {
+ if (name == "makefile") { return JSIO.FileType.Text; }
+
+ var lastDot = name.lastIndexOf(".");
+ if (lastDot <= 0) { return JSIO.FileType.Unknown; }
+
+ var ext= name.substring(lastDot);
+ if (ext == ".zip") { return JSIO.FileType.Binary; }
+ if (ext == ".xlsx") { return JSIO.FileType.Binary; }
+ if (ext == ".docx") { return JSIO.FileType.Binary; }
+ if (ext == ".dll") { return JSIO.FileType.Binary; }
+ if (ext == ".obj") { return JSIO.FileType.Binary; }
+ if (ext == ".pdb") { return JSIO.FileType.Binary; }
+ if (ext == ".exe") { return JSIO.FileType.Binary; }
+ if (ext == ".kmz") { return JSIO.FileType.Binary; }
+
+ if (ext == ".xml") { return JSIO.FileType.XML; }
+ if (ext == ".xsl") { return JSIO.FileType.XML; }
+ if (ext == ".kml") { return JSIO.FileType.XML; }
+ if (ext == ".csproj") { return JSIO.FileType.XML; }
+ if (ext == ".vbproj") { return JSIO.FileType.XML; }
+ if (ext == ".shfbproj") { return JSIO.FileType.XML; }
+ if (ext == ".resx") { return JSIO.FileType.XML; }
+ if (ext == ".xslt") { return JSIO.FileType.XML; }
+
+ if (ext == ".sln") { return JSIO.FileType.Text; }
+ if (ext == ".htm") { return JSIO.FileType.Text; }
+ if (ext == ".html") { return JSIO.FileType.Text; }
+ if (ext == ".js") { return JSIO.FileType.Text; }
+ if (ext == ".vb") { return JSIO.FileType.Text; }
+ if (ext == ".txt") { return JSIO.FileType.Text; }
+ if (ext == ".rels") { return JSIO.FileType.Text; }
+ if (ext == ".css") { return JSIO.FileType.Text; }
+ if (ext == ".cs") { return JSIO.FileType.Text; }
+ if (ext == ".asp") { return JSIO.FileType.Text; }
+
+ return JSIO.FileType.Unknown;
+ };
+
+ JSIO.stringOfLength = function (charCode, length) {
+ var s3 = "";
+ for (var i = 0; i < length; i++) {
+ s3 += String.fromCharCode(charCode);
+ }
+ return s3;
+ };
+
+ JSIO.formatByteArray = function(b) {
+ var s1 = "0000 ";
+ var s2 = "";
+ for (var i = 0; i < b.length; i++) {
+ if (i !== 0 && i % 16 === 0) {
+ s1 += " " + s2 +"\n" + JSIO.decimalToHexString(i, 4) + " ";
+ s2 = "";
+ }
+ s1 += JSIO.decimalToHexString(b[i], 2) + " ";
+ if (b[i] >=32 && b[i] <= 126) {
+ s2 += String.fromCharCode(b[i]);
+ } else {
+ s2 += ".";
+ }
+ }
+ if (s2.length > 0) {
+ s1 += JSIO.stringOfLength(32, ((i%16>0)? ((16 - i%16) * 3) : 0) + 4) + s2;
+ }
+ return s1;
+ };
+
+ JSIO.htmlEscape = function(str) {
+ return str
+ .replace(new RegExp( "&", "g" ), "&amp;")
+ .replace(new RegExp( "<", "g" ), "&lt;")
+ .replace(new RegExp( ">", "g" ), "&gt;")
+ .replace(new RegExp( "\x13", "g" ), "<br/>")
+ .replace(new RegExp( "\x10", "g" ), "<br/>");
+ };
+
+ JSIO.massApply = function(func, funcThis, arr, needReturn) {
+ var arrayLimit = 65536; // Chrome has an apply/array limit of 99999; Firefox = 491519, Safari = 65536
+ if (arr.length < arrayLimit) return func.apply(funcThis, arr);
+ else {
+ var newThis = funcThis;
+ var offset = 0;
+ var end = 65536;
+
+ while (offset < arr.length) {
+ var arrSlice;
+ if (arr.subarray) arrSlice = arr.subarray(offset, end);
+ else if (arr.slice) arrSlice = arr.slice(offset, end);
+
+ if (needReturn) newThis += func.apply(newThis, arrSlice);
+ else func.apply(funcThis, arrSlice);
+
+ offset += arrayLimit;
+ end += arrayLimit;
+ end = Math.min(arr.length, end);
+ }
+ return newThis;
+ }
+ }
})();
@@ -197,364 +197,364 @@
(function(){
- var version = "2.0 2012Feb";
-
- if (typeof JSIO !== "object") { JSIO = {}; }
- if ((typeof JSIO.version !== "string")) {
- JSIO.version = version;
- }
- else if ((JSIO.version.length < 3) ||
- (JSIO.version.substring(0,3) !== "2.0")) {
- JSIO.version += " " + version;
- }
-
- // =======================================================
- // the base object, used as the prototype of all ByteReader objects.
- var _byteReaderBase = function () {
- this.position = 0;
- // position must be incremented in .readByte() for all derived classes
- };
-
- _byteReaderBase.prototype._throwError = JSIO.throwError;
-
- _byteReaderBase.prototype._limitCheck = function(len, startPos) {
- var LOE = {
- len: len,
- pos: startPos,
- end: startPos+len
- };
-
- if (len === 0) return {len:0, pos:0, end:0};
- if (len < 0) this._throwError("Invalid read length");
- if (!this.length) return {len:len, pos:this.position, end:len+this.position};
- if (!startPos >= 0) LOE.pos = this.position;
- if (this.length <= LOE.pos) this._throwError("EOF reached");
-
- LOE.end = LOE.pos+len;
- if (this.length < LOE.end) LOE.end = LOE.pos+(LOE.len = this.length-this.position);
- return LOE;
- }
-
- JSIO.SeekOrigin = {
- Begin : 0,
- Current : 1,
- End : 2,
- SEEK_SET : 0,
- SEEK_CUR : 1,
- SEEK_END : 2
- };
-
- _byteReaderBase.prototype.seek = function(offset, origin) {
- switch (origin) {
- case JSIO.SeekOrigin.Begin:
- if (offset == this.position) return this.position;
- if (!this.length) {
- if (offset < this.position) this._throwError('Uni-directional stream cannot seek backwards', null, 'seek');
- else if (offset > this.position) return this.read(offset - this.position); // read will limit check
- }
- else {
- if (this.length < offset) this._throwError('Cannot seek past reader length', null, 'seek');
- this.position = offset;
- }
- break;
- case JSIO.SeekOrigin.Current:
- return this.seek(this.position + offset, JSIO.SeekOrigin.Begin);
- break;
- case JSIO.SeekOrigin.End:
- if (!this.length) this._throwError('Uni-directional stream has no known end length for seek', null, 'seek');
- return this.seek(this.length - 1 + offset, JSIO.SeekOrigin.Begin);
- break;
- default:
- this._throwError('Invalid seek method', null, 'seek');
- break;
- }
-
- return this.position;
- };
-
- _byteReaderBase.prototype.read = function(len, startPos) {
- var LOE = this._limitCheck(len, startPos);
- if (LOE.len === 0) return [];
- if (LOE.pos != this.position) this.seek(LOE.pos, JSIO.SeekOrigin.Begin);
-
- var bytesRead = [];
-
- // Faster methods with an array or stream
- if (this.array && this.array.subarray) bytesRead = this.array.subarray(LOE.pos, LOE.end);
- else if (this.array && this.array.slice) bytesRead = this.array.slice(LOE.pos, LOE.end);
- else if (this.stream) bytesRead = this.stream.read(LOE.len, LOE.pos);
- else if (this.length) { // Random-access stream
- for(var i=LOE.pos; i<LOE.end; i++) { bytesRead.push(this.readByteAt(i)); }
- }
- else { // Uni-directional stream
- for(var i=LOE.pos; i<LOE.end; i++) {
- var b = this.readByte();
- if (b === null || b === undefined) break;
- bytesRead.push(b);
- }
- }
- this.position = LOE.end;
- return bytesRead;
- };
-
- _byteReaderBase.prototype.beginRead = function(len, startPos, callback) {
- var LOE = this._limitCheck(len, startPos);
- if (LOE.len === 0) return setTimeout(function() { callback([]); }, 1);
- if (LOE.pos != this.position) this.seek(LOE.pos, JSIO.SeekOrigin.Begin);
-
- var bytesRead = [];
- var thisReader = this;
- var leftToRead = LOE.len;
-
- var readBatchAsync = function() {
- var c = 0;
- var pos = thisReader.position;
-
- // read a 32k batch
- var l = (leftToRead >= 32768) ? 32768 : leftToRead;
- var newBytes = thisReader.read(l);
- JSIO.massApply(bytesRead.push, bytesRead, newBytes);
- c += l;
- leftToRead -= l;
- if (newBytes.length < l) leftToRead = 0;
-
- if (leftToRead>0) setTimeout(readBatchAsync, 1);
- else callback(bytesRead);
- };
-
- // kickoff
- setTimeout(readBatchAsync, 1); // always async, in ALL situations
- return null;
- };
-
- _byteReaderBase.prototype.readToEnd = function() {
- if (this.array && this.array.subarray) return this.array.subarray(this.position);
- else if (this.array && this.array.slice) return this.array.slice(this.position);
- else if (this.length) return this.read(this.length - this.position);
- else return this.read(9000 * 9000); // over 9000
- };
-
- _byteReaderBase.prototype.beginReadToEnd = function(callback) {
- if (this.array && this.array.subarray) setTimeout(function() { callback( this.array.subarray(this.position) ); }, 1);
- else if (this.array && this.array.slice) setTimeout(function() { callback( this.array.slice(this.position) ); }, 1);
- else if (this.length) return this.beginRead(this.length - this.position, this.position, callback);
- else return this.beginRead(9000 * 9000, this.position, callback);
- };
-
- // Generic routines; one of these two MUST be overloaded (preferrably both)
- _byteReaderBase.prototype.readByte = function(){
- if (this.length && this.position >= this.length) return null; // EOF
-
- var byte;
- if (this.array) byte = this.array[this.position++];
- else if (this.length) byte = this.readByteAt(this.position++);
- else if (this.stream) byte = this.stream.read(1)[0];
- else byte = this.read(1)[0];
- return (byte === null || byte === undefined) ? null : byte;
- };
- _byteReaderBase.prototype.readByteAt = function(i) {
- var pos = this.position; // no position changes on this one
- if (i === null || i === undefined) i = this.position;
-
- var byte;
- if (this.array) byte = this.array[i];
- else if (i === pos) byte = this.readByte();
- else if (this.stream) byte = this.stream.read(1, i)[0];
- else byte = this.read(1, i)[0];
-
- this.position = pos;
- return (byte === null || byte === undefined) ? null : byte;
- }
-
- _byteReaderBase.prototype.readBytes = _byteReaderBase.prototype.read;
- _byteReaderBase.prototype.beginReadBytes = function(len, callback) { return this.beginRead(len, this.position, callback); };
-
- _byteReaderBase.prototype.readNumber = function(len, startPos){
- var LOE = this._limitCheck(len, startPos);
- if (LOE.len === 0) LOE.len = 1;
- if (LOE.pos != this.position) this.seek(LOE.pos, JSIO.SeekOrigin.Begin);
-
- var result = 0;
- var bytes = this.read(LOE.len, LOE.pos);
- for (var i=bytes.length-1; i>=0; i--) {
- // IE only supports 32-bit integer shifting
- //result = result << 8 | bytes[i];
- result = result*256 + bytes[i];
- }
- return result;
- };
-
- _byteReaderBase.prototype.readString = function(len, startPos){
- var LOE = this._limitCheck(len, startPos);
- if (LOE.len === 0) return '';
- if (LOE.pos != this.position) this.seek(LOE.pos, JSIO.SeekOrigin.Begin);
-
- var result = '';
- var bytes = this.read(LOE.len, LOE.pos);
- for(var i=0; i<bytes.length; i++){
- result += String.fromCharCode(bytes[i]);
- }
- return result;
- };
-
- _byteReaderBase.prototype.readNullTerminatedString = function(startPos){
- var pos = startPos || this.position;
- if (this.length && this.length < pos) this._throwError('EOF reached', null, 'readNullTerminatedString');
- if (pos != this.position) this.seek(pos, JSIO.SeekOrigin.Begin);
-
- var slarge = "";
- var s = "";
- var c = 0;
-
- // Faster method with an array
- if (this.array && this.array.indexOf) {
- var len = pos - this.array.indexOf(0, pos);
- if (len > 0) return this.readString(len, pos);
- }
-
- var ch;
- while(1) {
- ch = String.fromCharCode(this.readByteAt(pos+c));
- if (ch === null) break;
-
- s += ch;
- c++;
- if(c >= 32768) {
- slarge += s;
- s = "";
- pos += c;
- this.position += c;
- c = 0;
- }
- };
- this.position = pos + c;
- return slarge + s;
- };
-
- _byteReaderBase.prototype.beginReadNullTerminatedString = function(callback, startPos){
- var pos = startPos || this.position;
- if (this.length && this.length < pos) this._throwError('EOF reached', null, 'beginReadNullTerminatedString');
-
- var slarge = "";
- var s = "";
- var thisBinStream = this;
-
- var readBatchAsync = function() {
- var c = 0;
-
- var ch;
- while(1) {
- ch = String.fromCharCode(this.readByteAt(pos+c));
- if (ch === null) break;
-
- s += ch;
- c++;
- if(c >= 32768) {
- slarge += s;
- s = "";
- pos += c;
- this.position += c;
- c = 0;
- }
- };
-
- thisBinStream.position = pos + c;
- if (ch!==null) setTimeout(readBatchAsync, 1);
- else callback(slarge+s);
- };
-
- // Faster method with an array
- if (this.array && this.array.indexOf) {
- var len = pos - this.array.indexOf(0, pos);
- if (len > 0) readBatchASync = function() { callback(thisBinStream.readString(len, pos)); };
- }
-
- // kickoff
- setTimeout(readBatchAsync, 1); // always async, in ALL situations
- return null;
- };
-
-
-
- JSIO._ByteReaderBase = _byteReaderBase;
- // =======================================================
-
-
-
-
- // =======================================================
- // reads from an array of bytes.
- // This basically wraps a readByte() fn onto array access.
- var _arrayReader = function(array) {
- if (! (this instanceof arguments.callee) ) this._throwError('You must use new to instantiate this class', 'JSIO.ArrayReader', 'ctor');
- this.position = 0;
- this.array = array;
- this.length = array.length;
- this._typename = "JSIO.ArrayReader";
- this._version = version;
- return this;
- };
-
- _arrayReader.prototype = new JSIO._ByteReaderBase();
-
- _arrayReader.prototype.readByte = function() {
- if (this.position >= this.array.length) return null; // EOF
- return this.array[this.position++];
- };
- _arrayReader.prototype.readByteAt = function(i) {
- return this.array[i];
- };
-
- // =======================================================
-
-
- // =======================================================
- // reads bytes at a time from a defined segment of a stream.
- var _streamSegmentReader = function(stream, offset, len) {
- if (! (this instanceof arguments.callee) ) this._throwError('You must use new to instantiate this class', 'JSIO.StreamSegmentReader', 'ctor');
- if (!stream) this._throwError('You must pass a non-null stream', 'JSIO.StreamSegmentReader', 'ctor');
-
- if (!(offset >= 1)) offset = 0;
- if (!(len >= 1)) len = 0;
-
- this.stream = stream;
- this.array = null;
- if (stream.array) {
- var end = len ? offset + len : null;
- if (stream.array.subarray) this.array = stream.array.subarray(offset, end);
- else if (stream.array.slice) this.array = stream.array.slice(offset, end);
- }
- this.length = this.array ? this.array.length : (stream.length ? stream.length - offset : null);
- this.offset = offset;
- this.limit = len;
- this.position = 0;
- this._typeName = 'JSIO.StreamSegmentReader';
- this._version = version;
-
- if (this.array) {
- this.readByte = _arrayReader.prototype.readByte;
- this.readByteAt = _arrayReader.prototype.readByteAt;
- }
- return this;
- };
-
- _streamSegmentReader.prototype = new JSIO._ByteReaderBase();
-
- _streamSegmentReader.prototype.readByte = function() {
- if (this.limit && this.position >= this.limit) return null; // EOF
- this.position++;
- return this.stream.readByteAt(this.offset + this.position - 1);
- };
- _streamSegmentReader.prototype.readByteAt = function(i) {
- if (this.limit && i >= this.limit) return null; // EOF
- return this.stream.readByteAt(this.offset + i);
- };
-
- // =======================================================
-
- JSIO.ArrayReader = _arrayReader;
- JSIO.StreamReader = _streamSegmentReader;
- JSIO.StreamSegmentReader = _streamSegmentReader;
+ var version = "2.0 2012Feb";
+
+ if (typeof JSIO !== "object") { JSIO = {}; }
+ if ((typeof JSIO.version !== "string")) {
+ JSIO.version = version;
+ }
+ else if ((JSIO.version.length < 3) ||
+ (JSIO.version.substring(0,3) !== "2.0")) {
+ JSIO.version += " " + version;
+ }
+
+ // =======================================================
+ // the base object, used as the prototype of all ByteReader objects.
+ var _byteReaderBase = function () {
+ this.position = 0;
+ // position must be incremented in .readByte() for all derived classes
+ };
+
+ _byteReaderBase.prototype._throwError = JSIO.throwError;
+
+ _byteReaderBase.prototype._limitCheck = function(len, startPos) {
+ var LOE = {
+ len: len,
+ pos: startPos,
+ end: startPos+len
+ };
+
+ if (len === 0) return {len:0, pos:0, end:0};
+ if (len < 0) this._throwError("Invalid read length");
+ if (!this.length) return {len:len, pos:this.position, end:len+this.position};
+ if (!startPos >= 0) LOE.pos = this.position;
+ if (this.length <= LOE.pos) this._throwError("EOF reached");
+
+ LOE.end = LOE.pos+len;
+ if (this.length < LOE.end) LOE.end = LOE.pos+(LOE.len = this.length-this.position);
+ return LOE;
+ }
+
+ JSIO.SeekOrigin = {
+ Begin : 0,
+ Current : 1,
+ End : 2,
+ SEEK_SET : 0,
+ SEEK_CUR : 1,
+ SEEK_END : 2
+ };
+
+ _byteReaderBase.prototype.seek = function(offset, origin) {
+ switch (origin) {
+ case JSIO.SeekOrigin.Begin:
+ if (offset == this.position) return this.position;
+ if (!this.length) {
+ if (offset < this.position) this._throwError('Uni-directional stream cannot seek backwards', null, 'seek');
+ else if (offset > this.position) return this.read(offset - this.position); // read will limit check
+ }
+ else {
+ if (this.length < offset) this._throwError('Cannot seek past reader length', null, 'seek');
+ this.position = offset;
+ }
+ break;
+ case JSIO.SeekOrigin.Current:
+ return this.seek(this.position + offset, JSIO.SeekOrigin.Begin);
+ break;
+ case JSIO.SeekOrigin.End:
+ if (!this.length) this._throwError('Uni-directional stream has no known end length for seek', null, 'seek');
+ return this.seek(this.length - 1 + offset, JSIO.SeekOrigin.Begin);
+ break;
+ default:
+ this._throwError('Invalid seek method', null, 'seek');
+ break;
+ }
+
+ return this.position;
+ };
+
+ _byteReaderBase.prototype.read = function(len, startPos) {
+ var LOE = this._limitCheck(len, startPos);
+ if (LOE.len === 0) return [];
+ if (LOE.pos != this.position) this.seek(LOE.pos, JSIO.SeekOrigin.Begin);
+
+ var bytesRead = [];
+
+ // Faster methods with an array or stream
+ if (this.array && this.array.subarray) bytesRead = this.array.subarray(LOE.pos, LOE.end);
+ else if (this.array && this.array.slice) bytesRead = this.array.slice(LOE.pos, LOE.end);
+ else if (this.stream) bytesRead = this.stream.read(LOE.len, LOE.pos);
+ else if (this.length) { // Random-access stream
+ for(var i=LOE.pos; i<LOE.end; i++) { bytesRead.push(this.readByteAt(i)); }
+ }
+ else { // Uni-directional stream
+ for(var i=LOE.pos; i<LOE.end; i++) {
+ var b = this.readByte();
+ if (b === null || b === undefined) break;
+ bytesRead.push(b);
+ }
+ }
+ this.position = LOE.end;
+ return bytesRead;
+ };
+
+ _byteReaderBase.prototype.beginRead = function(len, startPos, callback) {
+ var LOE = this._limitCheck(len, startPos);
+ if (LOE.len === 0) return setTimeout(function() { callback([]); }, 1);
+ if (LOE.pos != this.position) this.seek(LOE.pos, JSIO.SeekOrigin.Begin);
+
+ var bytesRead = [];
+ var thisReader = this;
+ var leftToRead = LOE.len;
+
+ var readBatchAsync = function() {
+ var c = 0;
+ var pos = thisReader.position;
+
+ // read a 32k batch
+ var l = (leftToRead >= 32768) ? 32768 : leftToRead;
+ var newBytes = thisReader.read(l);
+ JSIO.massApply(bytesRead.push, bytesRead, newBytes);
+ c += l;
+ leftToRead -= l;
+ if (newBytes.length < l) leftToRead = 0;
+
+ if (leftToRead>0) setTimeout(readBatchAsync, 1);
+ else callback(bytesRead);
+ };
+
+ // kickoff
+ setTimeout(readBatchAsync, 1); // always async, in ALL situations
+ return null;
+ };
+
+ _byteReaderBase.prototype.readToEnd = function() {
+ if (this.array && this.array.subarray) return this.array.subarray(this.position);
+ else if (this.array && this.array.slice) return this.array.slice(this.position);
+ else if (this.length) return this.read(this.length - this.position);
+ else return this.read(9000 * 9000); // over 9000
+ };
+
+ _byteReaderBase.prototype.beginReadToEnd = function(callback) {
+ if (this.array && this.array.subarray) setTimeout(function() { callback( this.array.subarray(this.position) ); }, 1);
+ else if (this.array && this.array.slice) setTimeout(function() { callback( this.array.slice(this.position) ); }, 1);
+ else if (this.length) return this.beginRead(this.length - this.position, this.position, callback);
+ else return this.beginRead(9000 * 9000, this.position, callback);
+ };
+
+ // Generic routines; one of these two MUST be overloaded (preferrably both)
+ _byteReaderBase.prototype.readByte = function(){
+ if (this.length && this.position >= this.length) return null; // EOF
+
+ var oneByte;
+ if (this.array) oneByte = this.array[this.position++];
+ else if (this.length) oneByte = this.readByteAt(this.position++);
+ else if (this.stream) oneByte = this.stream.read(1)[0];
+ else oneByte = this.read(1)[0];
+ return (oneByte === null || oneByte === undefined) ? null : oneByte;
+ };
+ _byteReaderBase.prototype.readByteAt = function(i) {
+ var pos = this.position; // no position changes on this one
+ if (i === null || i === undefined) i = this.position;
+
+ var oneByte;
+ if (this.array) oneByte = this.array[i];
+ else if (i === pos) oneByte = this.readByte();
+ else if (this.stream) oneByte = this.stream.read(1, i)[0];
+ else oneByte = this.read(1, i)[0];
+
+ this.position = pos;
+ return (oneByte === null || oneByte === undefined) ? null : oneByte;
+ }
+
+ _byteReaderBase.prototype.readBytes = _byteReaderBase.prototype.read;
+ _byteReaderBase.prototype.beginReadBytes = function(len, callback) { return this.beginRead(len, this.position, callback); };
+
+ _byteReaderBase.prototype.readNumber = function(len, startPos){
+ var LOE = this._limitCheck(len, startPos);
+ if (LOE.len === 0) LOE.len = 1;
+ if (LOE.pos != this.position) this.seek(LOE.pos, JSIO.SeekOrigin.Begin);
+
+ var result = 0;
+ var bytes = this.read(LOE.len, LOE.pos);
+ for (var i=bytes.length-1; i>=0; i--) {
+ // IE only supports 32-bit integer shifting
+ //result = result << 8 | bytes[i];
+ result = result*256 + bytes[i];
+ }
+ return result;
+ };
+
+ _byteReaderBase.prototype.readString = function(len, startPos){
+ var LOE = this._limitCheck(len, startPos);
+ if (LOE.len === 0) return '';
+ if (LOE.pos != this.position) this.seek(LOE.pos, JSIO.SeekOrigin.Begin);
+
+ var result = '';
+ var bytes = this.read(LOE.len, LOE.pos);
+ for(var i=0; i<bytes.length; i++){
+ result += String.fromCharCode(bytes[i]);
+ }
+ return result;
+ };
+
+ _byteReaderBase.prototype.readNullTerminatedString = function(startPos){
+ var pos = startPos || this.position;
+ if (this.length && this.length < pos) this._throwError('EOF reached', null, 'readNullTerminatedString');
+ if (pos != this.position) this.seek(pos, JSIO.SeekOrigin.Begin);
+
+ var slarge = "";
+ var s = "";
+ var c = 0;
+
+ // Faster method with an array
+ if (this.array && this.array.indexOf) {
+ var len = pos - this.array.indexOf(0, pos);
+ if (len > 0) return this.readString(len, pos);
+ }
+
+ var ch;
+ while(1) {
+ ch = String.fromCharCode(this.readByteAt(pos+c));
+ if (ch === null) break;
+
+ s += ch;
+ c++;
+ if(c >= 32768) {
+ slarge += s;
+ s = "";
+ pos += c;
+ this.position += c;
+ c = 0;
+ }
+ };
+ this.position = pos + c;
+ return slarge + s;
+ };
+
+ _byteReaderBase.prototype.beginReadNullTerminatedString = function(callback, startPos){
+ var pos = startPos || this.position;
+ if (this.length && this.length < pos) this._throwError('EOF reached', null, 'beginReadNullTerminatedString');
+
+ var slarge = "";
+ var s = "";
+ var thisBinStream = this;
+
+ var readBatchAsync = function() {
+ var c = 0;
+
+ var ch;
+ while(1) {
+ ch = String.fromCharCode(this.readByteAt(pos+c));
+ if (ch === null) break;
+
+ s += ch;
+ c++;
+ if(c >= 32768) {
+ slarge += s;
+ s = "";
+ pos += c;
+ this.position += c;
+ c = 0;
+ }
+ };
+
+ thisBinStream.position = pos + c;
+ if (ch!==null) setTimeout(readBatchAsync, 1);
+ else callback(slarge+s);
+ };
+
+ // Faster method with an array
+ if (this.array && this.array.indexOf) {
+ var len = pos - this.array.indexOf(0, pos);
+ if (len > 0) readBatchASync = function() { callback(thisBinStream.readString(len, pos)); };
+ }
+
+ // kickoff
+ setTimeout(readBatchAsync, 1); // always async, in ALL situations
+ return null;
+ };
+
+
+
+ JSIO._ByteReaderBase = _byteReaderBase;
+ // =======================================================
+
+
+
+
+ // =======================================================
+ // reads from an array of bytes.
+ // This basically wraps a readByte() fn onto array access.
+ var _arrayReader = function(array) {
+ if (! (this instanceof arguments.callee) ) this._throwError('You must use new to instantiate this class', 'JSIO.ArrayReader', 'ctor');
+ this.position = 0;
+ this.array = array;
+ this.length = array.length;
+ this._typename = "JSIO.ArrayReader";
+ this._version = version;
+ return this;
+ };
+
+ _arrayReader.prototype = new JSIO._ByteReaderBase();
+
+ _arrayReader.prototype.readByte = function() {
+ if (this.position >= this.array.length) return null; // EOF
+ return this.array[this.position++];
+ };
+ _arrayReader.prototype.readByteAt = function(i) {
+ return this.array[i];
+ };
+
+ // =======================================================
+
+
+ // =======================================================
+ // reads bytes at a time from a defined segment of a stream.
+ var _streamSegmentReader = function(stream, offset, len) {
+ if (! (this instanceof arguments.callee) ) this._throwError('You must use new to instantiate this class', 'JSIO.StreamSegmentReader', 'ctor');
+ if (!stream) this._throwError('You must pass a non-null stream', 'JSIO.StreamSegmentReader', 'ctor');
+
+ if (!(offset >= 1)) offset = 0;
+ if (!(len >= 1)) len = 0;
+
+ this.stream = stream;
+ this.array = null;
+ if (stream.array) {
+ var end = len ? offset + len : null;
+ if (stream.array.subarray) this.array = stream.array.subarray(offset, end);
+ else if (stream.array.slice) this.array = stream.array.slice(offset, end);
+ }
+ this.length = this.array ? this.array.length : (stream.length ? stream.length - offset : null);
+ this.offset = offset;
+ this.limit = len;
+ this.position = 0;
+ this._typeName = 'JSIO.StreamSegmentReader';
+ this._version = version;
+
+ if (this.array) {
+ this.readByte = _arrayReader.prototype.readByte;
+ this.readByteAt = _arrayReader.prototype.readByteAt;
+ }
+ return this;
+ };
+
+ _streamSegmentReader.prototype = new JSIO._ByteReaderBase();
+
+ _streamSegmentReader.prototype.readByte = function() {
+ if (this.limit && this.position >= this.limit) return null; // EOF
+ this.position++;
+ return this.stream.readByteAt(this.offset + this.position - 1);
+ };
+ _streamSegmentReader.prototype.readByteAt = function(i) {
+ if (this.limit && i >= this.limit) return null; // EOF
+ return this.stream.readByteAt(this.offset + i);
+ };
+
+ // =======================================================
+
+ JSIO.ArrayReader = _arrayReader;
+ JSIO.StreamReader = _streamSegmentReader;
+ JSIO.StreamSegmentReader = _streamSegmentReader;
})();
@@ -574,195 +574,195 @@
// This work is licensed under the GPLv3.
(function(){
- var version = "2.0 2012Feb";
- var typename = "JSIO.BinaryUrlStream";
-
- if ((typeof JSIO !== "object") ||
- (typeof JSIO.version !== "string") ||
- (JSIO.version.length < 3) ||
- (JSIO.version.substring(0,3) !== "2.0"))
- JSIO.throwError('This extension requires JSIO.core.js v2.0', typename);
-
- if (typeof JSIO._ByteReaderBase !== "function")
- JSIO.throwError('This extension requires JSIO.BasicByteReaders.js', typename);
-
- if (/msie/i.test(navigator.userAgent) && !/opera/i.test(navigator.userAgent)) {
- var IEBinaryToArray_ByteStr_Script =
- "<!-- IEBinaryToArray_ByteStr -->\r\n"+
- "<script type='text/vbscript'>\r\n"+
- "Function IEBinaryToArray_ByteStr(Binary)\r\n"+
- " IEBinaryToArray_ByteStr = CStr(Binary)\r\n"+
- "End Function\r\n"+
- "Function IEBinaryToArray_ByteAsc_Last(Binary)\r\n"+
- " Dim lastIndex\r\n"+
- " lastIndex = LenB(Binary)\r\n"+
- " if lastIndex mod 2 Then\r\n"+
- " IEBinaryToArray_ByteAsc_Last = AscB( MidB( Binary, lastIndex, 1 ) )\r\n"+
- " Else\r\n"+
- " IEBinaryToArray_ByteAsc_Last = -1\r\n"+
- " End If\r\n"+
- "End Function\r\n"+
- "</script>\r\n";
-
- // inject VBScript
- document.write(IEBinaryToArray_ByteStr_Script);
- }
-
- JSIO.IEByteMapping = null;
-
- var bus = function(url, callback) {
- if (! (this instanceof arguments.callee) ) this._throwError('You must use new to instantiate this class', 'JSIO.BinaryUrlStream', 'ctor');
-
- this.callback = callback;
- this.position = 0;
- this.length = null;
- this.readByte = JSIO.ArrayReader.prototype.readByte;
- this.readByteAt = JSIO.ArrayReader.prototype.readByteAt;
- this.req = null;
- this._typename = typename;
- this._version = version;
- this.status = "-none-";
-
- var _IeGetBinResource = function(fileURL){
- var binStream = this;
- // http://stackoverflow.com/questions/1919972/how-do-i-access-xhr-responsebody-for-binary-data-from-javascript-in-ie
-
- // my helper to convert from responseBody to a byte array
- var convertResponseBodyToArray = function (binary) {
- var byteArray = new Array;
-
- try {
- // very fast; very little work involved
- byteArray = new VBArray(binary).toArray();
- }
- catch(err) {
- // use the BinaryToArray VBScript
- if (!JSIO.IEByteMapping) {
- JSIO.IEByteMapping = {};
- for ( var i = 0; i < 256; i++ ) {
- for ( var j = 0; j < 256; j++ ) {
- JSIO.IEByteMapping[ String.fromCharCode( i + j * 256 ) ] = [ i, j ];
- }
- }
- }
- var rawBytes = IEBinaryToArray_ByteStr(binary);
- var lastAsc = IEBinaryToArray_ByteAsc_Last(binary);
-
- for ( var i = 0; i < rawBytes.length; i++ ) {
- byteArray.push.apply(byteArray, JSIO.IEByteMapping[ rawBytes.substr(i,1) ]);
- }
- if (lastAsc >= 0) byteArray.push(lastAsc);
- }
-
- return byteArray;
- };
-
- this.req = (function() {
- if (window.XMLHttpRequest) return new window.XMLHttpRequest();
- else if (window.ActiveXObject) {
- // the many versions of IE's XML fetchers
- var AXOs = [
- 'MSXML2.XMLHTTP.6.0',
- 'MSXML2.XMLHTTP.5.0',
- 'MSXML2.XMLHTTP.4.0',
- 'MSXML2.XMLHTTP.3.0',
- 'MSXML2.XMLHTTP',
- 'Microsoft.XMLHTTP',
- 'MSXML.XMLHTTP'
- ];
- for (var i = 0; i < AXOs.length; i++) {
- try { return new ActiveXObject(AXOs[i]); }
- catch(e) { continue; }
- }
- }
- return null;
- })();
- this.req.open("GET", fileURL, true);
- this.req.setRequestHeader("Accept-Charset", "x-user-defined");
- this.req.onreadystatechange = function(event){
- if (binStream.req.readyState == 4) {
- binStream.status = "Status: " + binStream.req.status + ' ' + binStream.req.statusText;
- if (binStream.req.status == 200) {
- binStream.array = convertResponseBodyToArray(binStream.req.responseBody);
- binStream.length = binStream.array.length;
- if (binStream.length < 0) this._throwError('Failed to load "'+ fileURL + '" after converting');
-
- if (typeof binStream.callback == "function") binStream.callback(binStream);
- }
- else {
- binStream._throwError('Failed to load "'+ fileURL + '": HTTP ' + binStream.status);
- }
- }
- };
- this.req.send();
- };
-
- var _NormalGetBinResource = function(fileURL){
- var binStream= this;
- this.req = new XMLHttpRequest();
- this.req.open('GET', fileURL, true);
- this.req.onreadystatechange = function(aEvt) {
- if (binStream.req.readyState == 4) {
- binStream.status = "Status: " + binStream.req.status + ' ' + binStream.req.statusText;
- if(binStream.req.status == 200){
- var fileContents = binStream.req.responseText;
- binStream.length = fileContents.byteLength;
- binStream.array = fileContents.split('');
- for ( var i = 0; i < binStream.array.length; i++ ) {
- binStream.array[i] = binStream.array[i].charCodeAt(0) & 0xff;
- }
-
- if (typeof binStream.callback == "function") binStream.callback(binStream);
- }
- else {
- binStream._throwError('Failed to load "'+ fileURL + '": HTTP ' + binStream.status);
- }
- }
- };
- //XHR binary charset opt by Marcus Granado 2006 [http://mgran.blogspot.com]
- this.req.overrideMimeType('text/plain; charset=x-user-defined');
- this.req.send(null);
- };
-
- // http://stackoverflow.com/questions/327685/is-there-a-way-to-read-binary-data-into-javascript
- var _ArrayBufferGetBinResource = function(fileURL){
- var binStream= this;
- this.req = new XMLHttpRequest();
- this.req.open('GET', fileURL, true);
- this.req.onreadystatechange = function(aEvt) {
- if (binStream.req.readyState == 4) {
- binStream.status = "Status: " + binStream.req.status + ' ' + binStream.req.statusText;
- if(binStream.req.status == 200){
- var fileContents = binStream.req.response;
- binStream.length = fileContents.byteLength;
- binStream.array = new Uint8Array(fileContents);
-
- if (typeof binStream.callback == "function") binStream.callback(binStream);
- }
- else {
- binStream._throwError('Failed to load "'+ fileURL + '": HTTP ' + binStream.status);
- }
- }
- };
- this.req.responseType = 'arraybuffer';
- this.req.overrideMimeType('text/plain; charset=x-user-defined');
- this.req.send(null);
- };
-
-
- if (typeof ArrayBuffer !== 'undefined') _ArrayBufferGetBinResource.apply(this, [url]);
- else if (/msie/i.test(navigator.userAgent) && !/opera/i.test(navigator.userAgent)) _IeGetBinResource.apply(this, [url]);
- else _NormalGetBinResource.apply(this, [url]);
- };
-
- bus.prototype = new JSIO._ByteReaderBase();
-
- bus.prototype.readByte = function(){
- var byte = this.readByteAt(this.position++);
- return (byte === null || byte === undefined) ? null : byte;
- };
-
- JSIO.BinaryUrlStream = bus;
+ var version = "2.0 2012Feb";
+ var typename = "JSIO.BinaryUrlStream";
+
+ if ((typeof JSIO !== "object") ||
+ (typeof JSIO.version !== "string") ||
+ (JSIO.version.length < 3) ||
+ (JSIO.version.substring(0,3) !== "2.0"))
+ JSIO.throwError('This extension requires JSIO.core.js v2.0', typename);
+
+ if (typeof JSIO._ByteReaderBase !== "function")
+ JSIO.throwError('This extension requires JSIO.BasicByteReaders.js', typename);
+
+ if (/msie/i.test(navigator.userAgent) && !/opera/i.test(navigator.userAgent)) {
+ var IEBinaryToArray_ByteStr_Script =
+ "<!-- IEBinaryToArray_ByteStr -->\r\n"+
+ "<script type='text/vbscript'>\r\n"+
+ "Function IEBinaryToArray_ByteStr(Binary)\r\n"+
+ " IEBinaryToArray_ByteStr = CStr(Binary)\r\n"+
+ "End Function\r\n"+
+ "Function IEBinaryToArray_ByteAsc_Last(Binary)\r\n"+
+ " Dim lastIndex\r\n"+
+ " lastIndex = LenB(Binary)\r\n"+
+ " if lastIndex mod 2 Then\r\n"+
+ " IEBinaryToArray_ByteAsc_Last = AscB( MidB( Binary, lastIndex, 1 ) )\r\n"+
+ " Else\r\n"+
+ " IEBinaryToArray_ByteAsc_Last = -1\r\n"+
+ " End If\r\n"+
+ "End Function\r\n"+
+ "</script>\r\n";
+
+ // inject VBScript
+ document.write(IEBinaryToArray_ByteStr_Script);
+ }
+
+ JSIO.IEByteMapping = null;
+
+ var bus = function(url, callback) {
+ if (! (this instanceof arguments.callee) ) this._throwError('You must use new to instantiate this class', 'JSIO.BinaryUrlStream', 'ctor');
+
+ this.callback = callback;
+ this.position = 0;
+ this.length = null;
+ this.readByte = JSIO.ArrayReader.prototype.readByte;
+ this.readByteAt = JSIO.ArrayReader.prototype.readByteAt;
+ this.req = null;
+ this._typename = typename;
+ this._version = version;
+ this.status = "-none-";
+
+ var _IeGetBinResource = function(fileURL){
+ var binStream = this;
+ // http://stackoverflow.com/questions/1919972/how-do-i-access-xhr-responsebody-for-binary-data-from-javascript-in-ie
+
+ // my helper to convert from responseBody to a byte array
+ var convertResponseBodyToArray = function (binary) {
+ var byteArray = new Array;
+
+ try {
+ // very fast; very little work involved
+ byteArray = new VBArray(binary).toArray();
+ }
+ catch(err) {
+ // use the BinaryToArray VBScript
+ if (!JSIO.IEByteMapping) {
+ JSIO.IEByteMapping = {};
+ for ( var i = 0; i < 256; i++ ) {
+ for ( var j = 0; j < 256; j++ ) {
+ JSIO.IEByteMapping[ String.fromCharCode( i + j * 256 ) ] = [ i, j ];
+ }
+ }
+ }
+ var rawBytes = IEBinaryToArray_ByteStr(binary);
+ var lastAsc = IEBinaryToArray_ByteAsc_Last(binary);
+
+ for ( var i = 0; i < rawBytes.length; i++ ) {
+ byteArray.push.apply(byteArray, JSIO.IEByteMapping[ rawBytes.substr(i,1) ]);
+ }
+ if (lastAsc >= 0) byteArray.push(lastAsc);
+ }
+
+ return byteArray;
+ };
+
+ this.req = (function() {
+ if (window.XMLHttpRequest) return new window.XMLHttpRequest();
+ else if (window.ActiveXObject) {
+ // the many versions of IE's XML fetchers
+ var AXOs = [
+ 'MSXML2.XMLHTTP.6.0',
+ 'MSXML2.XMLHTTP.5.0',
+ 'MSXML2.XMLHTTP.4.0',
+ 'MSXML2.XMLHTTP.3.0',
+ 'MSXML2.XMLHTTP',
+ 'Microsoft.XMLHTTP',
+ 'MSXML.XMLHTTP'
+ ];
+ for (var i = 0; i < AXOs.length; i++) {
+ try { return new ActiveXObject(AXOs[i]); }
+ catch(e) { continue; }
+ }
+ }
+ return null;
+ })();
+ this.req.open("GET", fileURL, true);
+ this.req.setRequestHeader("Accept-Charset", "x-user-defined");
+ this.req.onreadystatechange = function(event){
+ if (binStream.req.readyState == 4) {
+ binStream.status = "Status: " + binStream.req.status + ' ' + binStream.req.statusText;
+ if (binStream.req.status == 200) {
+ binStream.array = convertResponseBodyToArray(binStream.req.responseBody);
+ binStream.length = binStream.array.length;
+ if (binStream.length < 0) this._throwError('Failed to load "'+ fileURL + '" after converting');
+
+ if (typeof binStream.callback == "function") binStream.callback(binStream);
+ }
+ else {
+ binStream._throwError('Failed to load "'+ fileURL + '": HTTP ' + binStream.status);
+ }
+ }
+ };
+ this.req.send();
+ };
+
+ var _NormalGetBinResource = function(fileURL){
+ var binStream= this;
+ this.req = new XMLHttpRequest();
+ this.req.open('GET', fileURL, true);
+ this.req.onreadystatechange = function(aEvt) {
+ if (binStream.req.readyState == 4) {
+ binStream.status = "Status: " + binStream.req.status + ' ' + binStream.req.statusText;
+ if(binStream.req.status == 200){
+ var fileContents = binStream.req.responseText;
+ binStream.length = fileContents.byteLength;
+ binStream.array = fileContents.split('');
+ for ( var i = 0; i < binStream.array.length; i++ ) {
+ binStream.array[i] = binStream.array[i].charCodeAt(0) & 0xff;
+ }
+
+ if (typeof binStream.callback == "function") binStream.callback(binStream);
+ }
+ else {
+ binStream._throwError('Failed to load "'+ fileURL + '": HTTP ' + binStream.status);
+ }
+ }
+ };
+ //XHR binary charset opt by Marcus Granado 2006 [http://mgran.blogspot.com]
+ if (!!this.req.overrideMimeType) this.req.overrideMimeType('text/plain; charset=x-user-defined');
+ this.req.send(null);
+ };
+
+ // http://stackoverflow.com/questions/327685/is-there-a-way-to-read-binary-data-into-javascript
+ var _ArrayBufferGetBinResource = function(fileURL){
+ var binStream= this;
+ this.req = new XMLHttpRequest();
+ this.req.open('GET', fileURL, true);
+ this.req.onreadystatechange = function(aEvt) {
+ if (binStream.req.readyState == 4) {
+ binStream.status = "Status: " + binStream.req.status + ' ' + binStream.req.statusText;
+ if(binStream.req.status == 200){
+ var fileContents = binStream.req.response;
+ binStream.length = fileContents.byteLength;
+ binStream.array = new Uint8Array(fileContents);
+ if (typeof binStream.callback == "function") binStream.callback(binStream);
+ }
+ else {
+ binStream._throwError('Failed to load "'+ fileURL + '": HTTP ' + binStream.status);
+ }
+ }
+ };
+ this.req.responseType = 'arraybuffer';
+ // http://stackoverflow.com/questions/11284728/how-do-i-access-8-bit-binary-data-from-javascript-in-opera
+ if (!!this.req.overrideMimeType) this.req.overrideMimeType('application/octet-stream; charset=x-user-defined');
+ this.req.send(null);
+ };
+
+
+ if (typeof ArrayBuffer !== 'undefined') _ArrayBufferGetBinResource.apply(this, [url]);
+ else if (/msie/i.test(navigator.userAgent) && !/opera/i.test(navigator.userAgent)) _IeGetBinResource.apply(this, [url]);
+ else _NormalGetBinResource.apply(this, [url]);
+ };
+
+ bus.prototype = new JSIO._ByteReaderBase();
+
+ bus.prototype.readByte = function(){
+ var oneByte = this.readByteAt(this.position++);
+ return (oneByte === null || oneByte === undefined) ? null : oneByte;
+ };
+
+ JSIO.BinaryUrlStream = bus;
})();
@@ -786,147 +786,147 @@
// This work is licensed under the GPLv3.
(function(){
- var version = "2.0 2012Feb";
- var typename = "JSIO.TextDecoder";
-
- if ((typeof JSIO !== "object") ||
- (typeof JSIO.version !== "string") ||
- (JSIO.version.length < 3) ||
- (JSIO.version.substring(0,3) !== "2.0"))
- JSIO.throwError('This extension requires JSIO.core.js v2.0', typename);
-
- if (typeof JSIO._ByteReaderBase !== "function")
- JSIO.throwError('This extension requires JSIO.BasicByteReaders.js', typename);
-
- var _ansi = function(reader) {
- if (! (this instanceof arguments.callee) ) this._throwError('You must use new to instantiate this class', 'JSIO.TextDecoder.ANSI', 'ctor');
- this.byteReader = reader;
- this.charWidth = 1;
- this._version = version;
- this._typename = typename + ".ANSI";
- return this;
- };
-
- _ansi.prototype.readChar = function() {
- var code = this.byteReader.readByte();
- return (code < 0) ? null : String.fromCharCode(code);
- };
-
- _ansi.prototype.parseChar = function(code) {
- return (code < 0) ? null : String.fromCharCode(code);
- };
-
- var _utf16 = function(reader) {
- if (! (this instanceof arguments.callee) ) this._throwError('You must use new to instantiate this class', 'JSIO.TextDecoder.UTF16', 'ctor');
- this.byteReader = reader;
- this.charWidth = 2;
- this.bomState = 0;
- this._version = version;
- this._typename = typename + ".UTF16";
- return this;
- };
-
- _utf16.prototype.readChar = function() {
- var b1 = this.byteReader.readByte();
- if (b1 < 0) return null;
- var b2 = this.byteReader.readByte();
- if (b2 < 0) this._throwError('Incomplete UTF16 character', null, 'readChar');
-
- if ((this.bomState === 0) && ((b1 + b2) == 509)) {
- this.bomState = (b2 == 254) ? 1 : 2;
-
- b1 = this.byteReader.readByte();
- if (b1 < 0) return null;
- b2 = this.byteReader.readByte();
- if (b2 < 0) this._throwError('Incomplete UTF16 character', null, 'readChar');
- }
- else {
- this.bomState = 1;
- }
- return this.parseChar(b1, b2);
- };
-
- _utf16.prototype.parseChar = function(b1, b2) {
- return String.fromCharCode( this.bomState == 1 ? (b2 << 8 | b1) : (b1 << 8 | b2) );
- }
-
- /* RFC 3629 */
- var _utf8 = function(reader) {
- if (! (this instanceof arguments.callee) ) this._throwError('You must use new to instantiate this class', 'JSIO.TextDecoder.UTF8', 'ctor');
- this.byteReader = reader;
- this.charWidth = null;
- this.waitBom = true;
- this.strict = false;
- this.pendingChar = null;
- this._version = version;
- this._typename = typename + ".UTF8";
- return this;
- };
-
- _utf8.prototype.readChar = function() {
- var ch = null;
- do {
- if (this.pendingChar !== null) {
- ch = this.pendingChar;
- this.pendingChar = null;
- }
- else {
- var b1 = this.byteReader.readByte();
- if (b1 === null) return null;
-
- if ((b1 & 0x80) === 0) ch = String.fromCharCode(b1);
- else {
- var currentPrefix = 0xC0;
- var ttlBytes = 0;
- do {
- var mask = currentPrefix >> 1 | 0x80;
- if((b1 & mask) == currentPrefix) break;
- currentPrefix = currentPrefix >> 1 | 0x80;
- } while(++ttlBytes < 5);
-
- if (ttlBytes > 0) {
- var code;
- if (ttlBytes === 1) code = (b1 & 0x1F) << 6 | (this.byteReader.readByte() & 0x3F);
- else {
- code = code << 6*ttlBytes
- var bytes = this.byteReader.read(ttlBytes);
- for (var i = 0; i > ttlBytes; i++) {
- var bi = bytes[i];
- if ((bi & 0xC0) != 0x80) this._throwError('Invalid sequence character', null, 'readChar');
- code = (code << 6) | (bi & 0x3F);
- }
- }
-
- if (code <= 0xFFFF) {
- ch = (code == 0xFEFF && this.waitBom) ? null : String.fromCharCode(code);
- }
- else {
- var v = code - 0x10000;
- var w1 = 0xD800 | ((v >> 10) & 0x3FF);
- var w2 = 0xDC00 | (v & 0x3FF);
- this.pendingChar = String.fromCharCode(w2);
- ch = String.fromCharCode(w1);
- }
- }
- else {
- // a byte higher than 0x80.
- if (this.strict) this._throwError('Invalid character', null, 'readChar');
- // fall back to "super ascii" (eg IBM-437)
- else ch = String.fromCharCode(b1);
- }
- }
- }
- this.waitBom = false;
- } while(ch === null);
- return ch;
- };
-
- JSIO.TextDecoder = {
- Default : _ansi,
- ANSI : _ansi,
- UTF16 : _utf16,
- UTF8 : _utf8
- };
+ var version = "2.0 2012Feb";
+ var typename = "JSIO.TextDecoder";
+
+ if ((typeof JSIO !== "object") ||
+ (typeof JSIO.version !== "string") ||
+ (JSIO.version.length < 3) ||
+ (JSIO.version.substring(0,3) !== "2.0"))
+ JSIO.throwError('This extension requires JSIO.core.js v2.0', typename);
+
+ if (typeof JSIO._ByteReaderBase !== "function")
+ JSIO.throwError('This extension requires JSIO.BasicByteReaders.js', typename);
+
+ var _ansi = function(reader) {
+ if (! (this instanceof arguments.callee) ) this._throwError('You must use new to instantiate this class', 'JSIO.TextDecoder.ANSI', 'ctor');
+ this.byteReader = reader;
+ this.charWidth = 1;
+ this._version = version;
+ this._typename = typename + ".ANSI";
+ return this;
+ };
+
+ _ansi.prototype.readChar = function() {
+ var code = this.byteReader.readByte();
+ return (code < 0) ? null : String.fromCharCode(code);
+ };
+
+ _ansi.prototype.parseChar = function(code) {
+ return (code < 0) ? null : String.fromCharCode(code);
+ };
+
+ var _utf16 = function(reader) {
+ if (! (this instanceof arguments.callee) ) this._throwError('You must use new to instantiate this class', 'JSIO.TextDecoder.UTF16', 'ctor');
+ this.byteReader = reader;
+ this.charWidth = 2;
+ this.bomState = 0;
+ this._version = version;
+ this._typename = typename + ".UTF16";
+ return this;
+ };
+
+ _utf16.prototype.readChar = function() {
+ var b1 = this.byteReader.readByte();
+ if (b1 < 0) return null;
+ var b2 = this.byteReader.readByte();
+ if (b2 < 0) this._throwError('Incomplete UTF16 character', null, 'readChar');
+
+ if ((this.bomState === 0) && ((b1 + b2) == 509)) {
+ this.bomState = (b2 == 254) ? 1 : 2;
+
+ b1 = this.byteReader.readByte();
+ if (b1 < 0) return null;
+ b2 = this.byteReader.readByte();
+ if (b2 < 0) this._throwError('Incomplete UTF16 character', null, 'readChar');
+ }
+ else {
+ this.bomState = 1;
+ }
+ return this.parseChar(b1, b2);
+ };
+
+ _utf16.prototype.parseChar = function(b1, b2) {
+ return String.fromCharCode( this.bomState == 1 ? (b2 << 8 | b1) : (b1 << 8 | b2) );
+ }
+
+ /* RFC 3629 */
+ var _utf8 = function(reader) {
+ if (! (this instanceof arguments.callee) ) this._throwError('You must use new to instantiate this class', 'JSIO.TextDecoder.UTF8', 'ctor');
+ this.byteReader = reader;
+ this.charWidth = null;
+ this.waitBom = true;
+ this.strict = false;
+ this.pendingChar = null;
+ this._version = version;
+ this._typename = typename + ".UTF8";
+ return this;
+ };
+
+ _utf8.prototype.readChar = function() {
+ var ch = null;
+ do {
+ if (this.pendingChar !== null) {
+ ch = this.pendingChar;
+ this.pendingChar = null;
+ }
+ else {
+ var b1 = this.byteReader.readByte();
+ if (b1 === null) return null;
+
+ if ((b1 & 0x80) === 0) ch = String.fromCharCode(b1);
+ else {
+ var currentPrefix = 0xC0;
+ var ttlBytes = 0;
+ do {
+ var mask = currentPrefix >> 1 | 0x80;
+ if((b1 & mask) == currentPrefix) break;
+ currentPrefix = currentPrefix >> 1 | 0x80;
+ } while(++ttlBytes < 5);
+
+ if (ttlBytes > 0) {
+ var code;
+ if (ttlBytes === 1) code = (b1 & 0x1F) << 6 | (this.byteReader.readByte() & 0x3F);
+ else {
+ code = code << 6*ttlBytes
+ var bytes = this.byteReader.read(ttlBytes);
+ for (var i = 0; i > ttlBytes; i++) {
+ var bi = bytes[i];
+ if ((bi & 0xC0) != 0x80) this._throwError('Invalid sequence character', null, 'readChar');
+ code = (code << 6) | (bi & 0x3F);
+ }
+ }
+
+ if (code <= 0xFFFF) {
+ ch = (code == 0xFEFF && this.waitBom) ? null : String.fromCharCode(code);
+ }
+ else {
+ var v = code - 0x10000;
+ var w1 = 0xD800 | ((v >> 10) & 0x3FF);
+ var w2 = 0xDC00 | (v & 0x3FF);
+ this.pendingChar = String.fromCharCode(w2);
+ ch = String.fromCharCode(w1);
+ }
+ }
+ else {
+ // a byte higher than 0x80.
+ if (this.strict) this._throwError('Invalid character', null, 'readChar');
+ // fall back to "super ascii" (eg IBM-437)
+ else ch = String.fromCharCode(b1);
+ }
+ }
+ }
+ this.waitBom = false;
+ } while(ch === null);
+ return ch;
+ };
+
+ JSIO.TextDecoder = {
+ Default : _ansi,
+ ANSI : _ansi,
+ UTF16 : _utf16,
+ UTF8 : _utf8
+ };
})();
@@ -962,122 +962,122 @@
(function(){
- var version = "2.0 2012Feb";
- var typename = "JSIO.TextReader";
-
- if (typeof JSIO._ByteReaderBase !== "function")
- JSIO.throwError('This extension requires JSIO.BasicByteReaders.js', typename);
-
- var tr = function(textDecoder) {
- if (! (this instanceof arguments.callee) ) this._throwError('You must use new to instantiate this class', typename, 'ctor');
- this.decoder = textDecoder;
- this._version = version;
- this._typename = typename;
- this.unreads = [];
- };
-
- // read one char
- tr.prototype.readChar = function() {
- return (this.unreads.length > 0) ? this.unreads.pop() : this.decoder.readChar();
- };
-
- // read a length of data
- tr.prototype.read = function(n) {
- // ANSI makes this easy
- if (this.decoder.charWidth === 1) return JSIO.massApply(String.fromCharCode, new String, this.decoder.byteReader.read(n), true);
-
- var s = "";
- for (vari=0; i<n; i++) {
- var ch = this.readChar();
- if (ch !== null) s+= ch;
- else break;
- }
- return s;
- };
-
- tr.prototype.unreadChar = function(ch) {
- this.unreads.push(ch);
- };
-
- tr.prototype.readToEnd = function() {
- // ANSI makes this easy
- if (this.decoder.charWidth === 1) return JSIO.massApply(String.fromCharCode, new String, this.decoder.byteReader.readToEnd(n), true);
-
- var slarge = "";
- var s = "";
- var c = 0;
- var ch = this.readChar();
- while(ch !== null) {
- s += ch;
- c++;
- if(c >= 32768) {
- slarge += s;
- s = "";
- c = 0;
- }
- ch = this.readChar();
- }
- return slarge + s;
- };
-
- tr.prototype.beginReadToEnd = function(callback) {
- // ANSI makes this easy
- if (this.decoder.charWidth === 1) {
- this.decoder.byteReader.beginReadToEnd(function (bytes) {
- callback( JSIO.massApply(String.fromCharCode, new String, bytes, true) );
- });
- return null;
- }
-
- var slarge = "";
- var s = "";
- var txtrdr = this;
-
- var readBatchAsync = function() {
- var c = 0;
- var ch = txtrdr.readChar();
- while(ch !== null) {
- s += ch;c++;
- if(c >= 32768) {
- slarge += s;
- s = "";
- break;
- }
- ch = txtrdr.readChar();
- }
- if (ch!==null){
- setTimeout(readBatchAsync, 1);
- }
- else {
- callback(slarge+s);
- }
- };
-
- // kickoff
- setTimeout(readBatchAsync, 1); // always async, in ALL situations
- return null;
- };
-
- tr.prototype.readLine = function() {
- var s = "";
- var ch = this.readChar();
- if (ch === null) return null;
-
- while(ch != "\r" && ch != "\n") {
- s += ch;
- ch = this.readChar();
- if (ch === null) return s;
- }
- if(ch == "\r") {
- ch = this.readChar();
- if(ch !== null && ch != "\n"){
- this.unreadChar(ch);
- }
- }
- return s;
- };
-
- JSIO.TextReader = tr;
+ var version = "2.0 2012Feb";
+ var typename = "JSIO.TextReader";
+
+ if (typeof JSIO._ByteReaderBase !== "function")
+ JSIO.throwError('This extension requires JSIO.BasicByteReaders.js', typename);
+
+ var tr = function(textDecoder) {
+ if (! (this instanceof arguments.callee) ) this._throwError('You must use new to instantiate this class', typename, 'ctor');
+ this.decoder = textDecoder;
+ this._version = version;
+ this._typename = typename;
+ this.unreads = [];
+ };
+
+ // read one char
+ tr.prototype.readChar = function() {
+ return (this.unreads.length > 0) ? this.unreads.pop() : this.decoder.readChar();
+ };
+
+ // read a length of data
+ tr.prototype.read = function(n) {
+ // ANSI makes this easy
+ if (this.decoder.charWidth === 1) return JSIO.massApply(String.fromCharCode, new String, this.decoder.byteReader.read(n), true);
+
+ var s = "";
+ for (vari=0; i<n; i++) {
+ var ch = this.readChar();
+ if (ch !== null) s+= ch;
+ else break;
+ }
+ return s;
+ };
+
+ tr.prototype.unreadChar = function(ch) {
+ this.unreads.push(ch);
+ };
+
+ tr.prototype.readToEnd = function() {
+ // ANSI makes this easy
+ if (this.decoder.charWidth === 1) return JSIO.massApply(String.fromCharCode, new String, this.decoder.byteReader.readToEnd(n), true);
+
+ var slarge = "";
+ var s = "";
+ var c = 0;
+ var ch = this.readChar();
+ while(ch !== null) {
+ s += ch;
+ c++;
+ if(c >= 32768) {
+ slarge += s;
+ s = "";
+ c = 0;
+ }
+ ch = this.readChar();
+ }
+ return slarge + s;
+ };
+
+ tr.prototype.beginReadToEnd = function(callback) {
+ // ANSI makes this easy
+ if (this.decoder.charWidth === 1) {
+ this.decoder.byteReader.beginReadToEnd(function (bytes) {
+ callback( JSIO.massApply(String.fromCharCode, new String, bytes, true) );
+ });
+ return null;
+ }
+
+ var slarge = "";
+ var s = "";
+ var txtrdr = this;
+
+ var readBatchAsync = function() {
+ var c = 0;
+ var ch = txtrdr.readChar();
+ while(ch !== null) {
+ s += ch;c++;
+ if(c >= 32768) {
+ slarge += s;
+ s = "";
+ break;
+ }
+ ch = txtrdr.readChar();
+ }
+ if (ch!==null){
+ setTimeout(readBatchAsync, 1);
+ }
+ else {
+ callback(slarge+s);
+ }
+ };
+
+ // kickoff
+ setTimeout(readBatchAsync, 1); // always async, in ALL situations
+ return null;
+ };
+
+ tr.prototype.readLine = function() {
+ var s = "";
+ var ch = this.readChar();
+ if (ch === null) return null;
+
+ while(ch != "\r" && ch != "\n") {
+ s += ch;
+ ch = this.readChar();
+ if (ch === null) return s;
+ }
+ if(ch == "\r") {
+ ch = this.readChar();
+ if(ch !== null && ch != "\n"){
+ this.unreadChar(ch);
+ }
+ }
+ return s;
+ };
+
+ JSIO.TextReader = tr;
})();
@@ -1116,111 +1116,111 @@
// This work is licensed under the GPLv3.
(function(){
- var version = "2.0 2012Feb";
- var typename = "JSIO.Crc32";
-
- if (typeof JSIO._ByteReaderBase !== "function")
- JSIO.throwError('This extension requires JSIO.BasicByteReaders.js', typename);
-
- JSIO.crc32Table = null;
- JSIO.crc32Polynomial = 0xEDB88320;
-
- var crc32TableCalc = function () {
- // do this once only, for all instances
- if (JSIO.crc32Table) return;
- JSIO.crc32Table = new Array(256);
- for (var i = 0; i < 256; i++) {
- var c=i;
- for (var k = 0; k < 8; k++) {
- if ((c & 1) == 1) c = JSIO.crc32Polynomial ^ (c>>>1);
- else c >>>= 1;
- }
- JSIO.crc32Table[i] = c;
- }
- };
-
- JSIO.computeCrc32 = function(str) {
- crc32TableCalc(); // once
- var c = 0xFFFFFFFF;
- var sL = str.length;
- if (typeof str == "object") {
- for (var n1=0; n1<sL; n1++) {
- c = JSIO.crc32Table[(c&0xff) ^ str[n1]] ^ (c>>>8);
- }
- } else {
- for (var n2=0; n2<sL; n2++) {
- c = JSIO.crc32Table[(c&0xff) ^ str.charCodeAt(n2)] ^ (c>>>8);
- }
- }
- c ^= 0xFFFFFFFF;
- if (c < 0) c += 0xFFFFFFFF+1;
- return c;
- };
-
- // =======================================================
- var _crc32 = function() {
- if (! (this instanceof arguments.callee) ) this._throwError('You must use new to instantiate this class', typename, 'ctor');
- crc32TableCalc(); // once
- this._typename = typename;
- this._version = version;
- this._runningCrc32 = 0xFFFFFFFF;
- };
-
- _crc32.prototype.slurpByte = function(b) {
- var r = this._runningCrc32;
- this._runningCrc32 = r>>>8 ^ JSIO.crc32Table[b ^ (r & 0x000000FF)];
- };
-
- _crc32.prototype.result = function() {
- var c = this._runningCrc32 ^ 0xFFFFFFFF;
- if (c < 0) c += 0xFFFFFFFF+1;
- return c;
- };
- // =======================================================
-
-
-
- var _crc32CalculatingReader = function(reader) {
- if (! (this instanceof arguments.callee) ) this._throwError('You must use new to instantiate this class', 'JSIO.Crc32Reader', 'ctor');
- this._byteReader = reader;
- this._typename = "JSIO.Crc32Reader";
- this._version = version;
- this._crc32 = new JSIO.Crc32();
- };
-
- _crc32CalculatingReader.prototype = new JSIO._ByteReaderBase();
-
- _crc32CalculatingReader.prototype.readByte = function() {
- var b = this._byteReader.readByte();
- if (b !== null) this._crc32.slurpByte(b);
- this.position++;
- return b;
- };
-
- _crc32CalculatingReader.prototype.read = function(len) {
- if (len === 0) return [];
- var bytes = this._byteReader.read(len);
- len = bytes.length;
-
- var tbl = JSIO.crc32Table;
- var r = this._crc32._runningCrc32;
- var t;
- for (var i = 0; i < len; i++) {
- t = tbl[ bytes[i] ^ (r & 0x000000FF) ];
- r = (r >>> 8) ^ t;
- }
- this._crc32._runningCrc32 = r;
-
- this.position += len;
- return bytes;
- }
-
- _crc32CalculatingReader.prototype.crc32 = function() {
- return this._crc32.result();
- };
-
- JSIO.Crc32 = _crc32;
- JSIO.Crc32Reader = _crc32CalculatingReader;
+ var version = "2.0 2012Feb";
+ var typename = "JSIO.Crc32";
+
+ if (typeof JSIO._ByteReaderBase !== "function")
+ JSIO.throwError('This extension requires JSIO.BasicByteReaders.js', typename);
+
+ JSIO.crc32Table = null;
+ JSIO.crc32Polynomial = 0xEDB88320;
+
+ var crc32TableCalc = function () {
+ // do this once only, for all instances
+ if (JSIO.crc32Table) return;
+ JSIO.crc32Table = new Array(256);
+ for (var i = 0; i < 256; i++) {
+ var c=i;
+ for (var k = 0; k < 8; k++) {
+ if ((c & 1) == 1) c = JSIO.crc32Polynomial ^ (c>>>1);
+ else c >>>= 1;
+ }
+ JSIO.crc32Table[i] = c;
+ }
+ };
+
+ JSIO.computeCrc32 = function(str) {
+ crc32TableCalc(); // once
+ var c = 0xFFFFFFFF;
+ var sL = str.length;
+ if (typeof str == "object") {
+ for (var n1=0; n1<sL; n1++) {
+ c = JSIO.crc32Table[(c&0xff) ^ str[n1]] ^ (c>>>8);
+ }
+ } else {
+ for (var n2=0; n2<sL; n2++) {
+ c = JSIO.crc32Table[(c&0xff) ^ str.charCodeAt(n2)] ^ (c>>>8);
+ }
+ }
+ c ^= 0xFFFFFFFF;
+ if (c < 0) c += 0xFFFFFFFF+1;
+ return c;
+ };
+
+ // =======================================================
+ var _crc32 = function() {
+ if (! (this instanceof arguments.callee) ) this._throwError('You must use new to instantiate this class', typename, 'ctor');
+ crc32TableCalc(); // once
+ this._typename = typename;
+ this._version = version;
+ this._runningCrc32 = 0xFFFFFFFF;
+ };
+
+ _crc32.prototype.slurpByte = function(b) {
+ var r = this._runningCrc32;
+ this._runningCrc32 = r>>>8 ^ JSIO.crc32Table[b ^ (r & 0x000000FF)];
+ };
+
+ _crc32.prototype.result = function() {
+ var c = this._runningCrc32 ^ 0xFFFFFFFF;
+ if (c < 0) c += 0xFFFFFFFF+1;
+ return c;
+ };
+ // =======================================================
+
+
+
+ var _crc32CalculatingReader = function(reader) {
+ if (! (this instanceof arguments.callee) ) this._throwError('You must use new to instantiate this class', 'JSIO.Crc32Reader', 'ctor');
+ this._byteReader = reader;
+ this._typename = "JSIO.Crc32Reader";
+ this._version = version;
+ this._crc32 = new JSIO.Crc32();
+ };
+
+ _crc32CalculatingReader.prototype = new JSIO._ByteReaderBase();
+
+ _crc32CalculatingReader.prototype.readByte = function() {
+ var b = this._byteReader.readByte();
+ if (b !== null) this._crc32.slurpByte(b);
+ this.position++;
+ return b;
+ };
+
+ _crc32CalculatingReader.prototype.read = function(len) {
+ if (len === 0) return [];
+ var bytes = this._byteReader.read(len);
+ len = bytes.length;
+
+ var tbl = JSIO.crc32Table;
+ var r = this._crc32._runningCrc32;
+ var t;
+ for (var i = 0; i < len; i++) {
+ t = tbl[ bytes[i] ^ (r & 0x000000FF) ];
+ r = (r >>> 8) ^ t;
+ }
+ this._crc32._runningCrc32 = r;
+
+ this.position += len;
+ return bytes;
+ }
+
+ _crc32CalculatingReader.prototype.crc32 = function() {
+ return this._crc32.result();
+ };
+
+ JSIO.Crc32 = _crc32;
+ JSIO.Crc32Reader = _crc32CalculatingReader;
})();
@@ -1263,436 +1263,436 @@
(function(){
- var version = "2.0 2012Feb";
- var typename = "JSIO.InflatingReader";
-
- if (typeof JSIO._ByteReaderBase !== "function")
- JSIO.throwError('This extension requires JSIO.BasicByteReaders.js', typename);
-
- // =======================================================
- // _InternalBitReader is used internally in the InflatingReader class.
- //
-
- JSIO.bitShiftTable = null;
-
- var bitShiftTableCalc = function () {
- // do this once only, for all instances
- if (JSIO.bitShiftTable) return;
-
- var bits = 8;
- JSIO.bitShiftTable = {
- LSB: new Array(bits),
- MSB: new Array(bits)
- }
- for (var bp = 0; bp < bits; bp++) {
- var lim = bits - bp;
- JSIO.bitShiftTable.LSB[bp] = new Array(lim);
- JSIO.bitShiftTable.MSB[bp] = new Array(lim);
-
- var maskLSB = 1 << bp;
- var maskMSB = 1 << lim-1;
- for (var len = 1; len <= lim; len++) {
- JSIO.bitShiftTable.LSB[bp][len-1] = maskLSB;
- JSIO.bitShiftTable.MSB[bp][len-1] = maskMSB;
- maskLSB |= 1 << bp+len;
- maskMSB |= 1 << lim-len-1;
- }
- }
- };
-
- var _InternalBitReader = function(reader) {
- if (! (this instanceof arguments.callee) ) this._throwError('You must use new to instantiate this class', typename + '._InternalBitReader.ctor');
- this.bitsLength = 0;
- this.bits = 0;
- this.byteReader = reader;
- this._typeName = typename + "._InternalBitReader";
- this._version = version;
- bitShiftTableCalc();
- };
-
- _InternalBitReader.prototype._throwError = JSIO.throwError;
-
- _InternalBitReader.prototype.readBit = function() {
- if (this.bitsLength === 0) {
- var nextByte = this.byteReader.readByte();
- if (nextByte === null) this._throwError('Unexpected end of stream', null, 'readBit');
- this.bits = nextByte;
- this.bitsLength = 8;
- }
-
- var bit = (this.bits & 1 << 8-this.bitsLength) !== 0;
- this.bitsLength--;
- return bit;
- };
-
- _InternalBitReader.prototype.align = function() { this.bitsLength = 0; };
-
- _InternalBitReader.prototype.readBits = function(len, type) {
- var data = 0;
- type = type || 'LSB';
- var tbl = JSIO.bitShiftTable[type];
- var dl = 0;
- while (len > 0) {
- if (this.bitsLength === 0) {
- var nextByte = this.byteReader.readByte();
- if (nextByte === null) this._throwError('Unexpected end of stream', null, 'read'+type);
- this.bits = nextByte;
- this.bitsLength = 8;
- }
-
- var maskLen = (this.bitsLength >= len) ? len : this.bitsLength;
- var bitsPos = 8-this.bitsLength;
- data |= (this.bits & tbl[bitsPos][maskLen-1]) >>> bitsPos << dl;
-
- dl += maskLen;
- len -= maskLen;
- this.bitsLength -= maskLen;
- };
- return data;
- };
-
- _InternalBitReader.prototype.readLSB = function(len) { return this.readBits(len, 'LSB'); }
- _InternalBitReader.prototype.readMSB = function(len) { return this.readBits(len, 'MSB'); }
-
- //
- // =======================================================
-
-
- /* inflating ByteReader - RFC 1951 */
- var _inflatingReader = function(reader) {
- if (! (this instanceof arguments.callee) ) this._throwError('You must use new to instantiate this class', typename, 'ctor');
- this._byteReader = reader;
- this._bitReader = new _InternalBitReader(reader);
- this._buffer = [];
- this._bufferPosition = 0;
- this._state = 0;
- this._blockFinal = false;
- this._typeName = typename;
- this._version = version;
- return this;
- };
-
-
- // shared fns and variables
-
- var staticCodes = null;
- var staticDistances = null;
-
- var clenMap = [16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15];
-
- var buildCodes = function(lengths){
- var i=0;
- var codes = new Array(lengths.length);
- var maxBits = lengths[0];
- for (i=1; i<lengths.length; i++) {
- if (maxBits < lengths[i]) maxBits = lengths[i];
- }
-
- var bitLengthsCount = new Array(maxBits + 1);
- for (i=0; i<=maxBits; i++) bitLengthsCount[i]=0;
-
- for (i=0; i<lengths.length; i++) {
- ++bitLengthsCount[lengths[i]];
- }
-
- var nextCode = new Array(maxBits + 1);
- var code = 0;
- bitLengthsCount[0] = 0;
- for (var bits=1; bits<=maxBits; bits++) {
- code = (code + bitLengthsCount[bits - 1]) << 1;
- nextCode[bits] = code;
- }
-
- for (i=0; i<codes.length; i++) {
- var len = lengths[i];
- if (len !== 0) {
- codes[i] = nextCode[len];
- nextCode[len]++;
- }
- }
- return codes;
- };
-
- var buildTree = function(codes, lengths){
- var nonEmptyCodes = [];
- for(var i=0; i<codes.length; ++i) {
- if(lengths[i] > 0) {
- var code = {};
- code.bits = codes[i];
- code.length = lengths[i];
- code.index = i;
- nonEmptyCodes.push(code);
- }
- }
- return buildTreeBranch(nonEmptyCodes, 0, 0);
- };
-
-
- var buildTreeBranch = function(codes, prefix, prefixLength){
- if (codes.length === 0) return null;
-
- var zeros = [];
- var ones = [];
- var branch = {};
- branch.isLeaf = false;
- for(var i=0; i<codes.length; ++i) {
- if (codes[i].length == prefixLength && codes[i].bits == prefix) {
- branch.isLeaf = true;
- branch.index = codes[i].index;
- break;
- }
- else {
- var nextBit = ((codes[i].bits >> (codes[i].length - prefixLength - 1)) & 1) > 0;
- if (nextBit) ones.push(codes[i]);
- else zeros.push(codes[i]);
- }
- }
- if(!branch.isLeaf) {
- branch.zero = buildTreeBranch(zeros, (prefix << 1), prefixLength + 1);
- branch.one = buildTreeBranch(ones, (prefix << 1) | 1, prefixLength + 1);
- }
- return branch;
- };
-
-
- var encodedLengthStart = [3,4,5,6,7,8,9,10,
- 11,13,15,17,19,23,27,31,35,43,51,59,67,83,99,
- 115,131,163,195,227,258];
-
- var encodedLengthAdditionalBits = [0,0,0,0,0,0,0,0,
- 1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0];
-
- var encodedDistanceStart = [1,2,3,4, 5,7,9, 13,17,25, 33,49,65,
- 97,129,193,257,385,513,769,1025,1537,2049,
- 3073,4097,6145,8193,12289,16385,24577];
-
- var encodedDistanceAdditionalBits = [0,0,0,0,1,1,2,2,3,3,4,4,
- 5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13];
-
-
- var readDynamicTrees = function(bitReader){
- var hlit = bitReader.readLSB(5) + 257;
- var hdist = bitReader.readLSB(5) + 1;
- var hclen = bitReader.readLSB(4) + 4;
- var clen = new Array(19);
- var i;
- for (i=0; i<clen.length; i++) { clen[i] = 0; }
- for (i=0; i<hclen; i++) { clen[clenMap[i]] = bitReader.readLSB(3); }
-
- var clenCodes = buildCodes(clen);
- var clenTree = buildTree(clenCodes, clen);
-
- var lengthsSequence = [];
- while(lengthsSequence.length < hlit + hdist) {
- var p = clenTree;
- while(!p.isLeaf) { p = bitReader.readBit() ? p.one : p.zero; }
-
- var code = p.index;
- if (code <= 15) lengthsSequence.push(code);
- else if (code == 16) {
- // TODO: replace with faster repeat algorythm
- var repeat = bitReader.readLSB(2) + 3;
- for(var q=0; q<repeat; ++q){
- lengthsSequence.push(lengthsSequence[lengthsSequence.length - 1]);
- }
- }
- else if (code == 17) {
- var repeat1 = bitReader.readLSB(3) + 3;
- for(var q1=0; q1<repeat1; ++q1) {
- lengthsSequence.push(0);
- }
- }
- else if (code == 18) {
- var repeat2 = bitReader.readLSB(7) + 11;
- for(var q2=0; q2<repeat2; ++q2){
- lengthsSequence.push(0);
- }
- }
- }
-
- var codesLengths = lengthsSequence.slice(0, hlit);
- var codes = buildCodes(codesLengths);
- var distancesLengths = lengthsSequence.slice(hlit, hlit + hdist);
- var distances = buildCodes(distancesLengths);
-
- return {
- codesTree : buildTree(codes, codesLengths),
- distancesTree : buildTree(distances, distancesLengths)
- };
- };
-
-
- _inflatingReader.prototype = new JSIO._ByteReaderBase();
-
-
- // internal instance fns
- _inflatingReader.prototype._decodeItem = function() {
- if (this._state == 2) return null; // end-of-blocks
-
- var item;
- if(this._state === 0) {
- this._blockFinal = this._bitReader.readBit();
- var blockType = this._bitReader.readLSB(2);
- switch(blockType) {
- case 0:
- this._bitReader.align();
- var len = this._bitReader.readLSB(16); // low-byte first, as opposed to readNumber's HBF
- var nlen = this._bitReader.readLSB(16);
- if ((len & ~nlen) != len) this._throwError('Invalid block type 0 length', null, '_decodeItem');
-
- item = {};
- item.itemType = 0;
- item.array = this._bitReader.byteReader.read(len);
- if (item.array.length < len) this._throwError('Incomplete block', null, '_decodeItem');
- if (this._blockFinal) this._state = 2;
- return item;
- case 1:
- this._codesTree = staticCodes;
- this._distancesTree = staticDistances;
- this._state = 1;
- break;
- case 2:
- var dTrees = readDynamicTrees(this._bitReader);
- this._codesTree = dTrees.codesTree;
- this._distancesTree = dTrees.distancesTree;
- this._state = 1;
- break;
- default:
- this._throwError('Invalid block type ('+ blockType +')', null, '_decodeItem');
- }
- }
-
- item = {};
-
- var p = this._codesTree;
- while (!p.isLeaf) { p = this._bitReader.readBit() ? p.one : p.zero; }
- if(p.index < 256) {
- item.itemType = 2;
- item.symbol = p.index;
- } else if(p.index > 256) {
- var lengthCode = p.index;
- if(lengthCode > 285) this._throwError('Invalid length code', null, '_decodeItem');
-
- var length = encodedLengthStart[lengthCode - 257];
- if(encodedLengthAdditionalBits[lengthCode - 257] > 0) {
- length += this._bitReader.readLSB(encodedLengthAdditionalBits[lengthCode - 257]);
- }
-
- p = this._distancesTree;
- while (!p.isLeaf) { p = this._bitReader.readBit() ? p.one : p.zero; }
-
- var distanceCode = p.index;
- var distance = encodedDistanceStart[distanceCode];
- if (encodedDistanceAdditionalBits[distanceCode] > 0)
- distance += this._bitReader.readLSB(encodedDistanceAdditionalBits[distanceCode]);
-
- item.itemType = 3;
- item.distance = distance;
- item.length = length;
- } else {
- item.itemType = 1;
- this._state = this._blockFinal ? 2 : 0; // EOB
- }
- return item;
- };
-
-
-
- // public instance functions
-
- _inflatingReader.prototype.readByte = function() {
- var byte = this.read(1)[0];
- return (byte === null || byte === undefined) ? null : byte;
- };
-
- _inflatingReader.prototype.read = function(len) {
- var b = this._buffer; // (since we use this so much...)
-
- // Keep reading until we get to the right length
- while (this._bufferPosition+len > b.length) {
- var item = this._decodeItem();
- if (item === null) { // EOF
- len = b.length - this._bufferPosition;
- break;
- }
- switch(item.itemType) {
- case 0:
- JSIO.massApply(b.push, b, item.array);
- break;
- case 2:
- b.push(item.symbol);
- break;
- case 3:
- var j = b.length - item.distance;
- if (item.distance >= item.length)
- JSIO.massApply(b.push, b, b.slice(j, j+item.length));
- // sometimes DEFLATE tries some trickery with "look-ahead" compression
- else {
- // this is basically just a repetition of the same string, plus some possible cutoff
- var count = parseInt(item.length / item.distance);
- var repArr = b.slice(j);
- // http://stackoverflow.com/questions/202605/repeat-string-javascript/5450113#5450113
- while (count > 0) {
- if (count & 1) JSIO.massApply( b.push, b, repArr);
- if (count >>= 1) JSIO.massApply(repArr.push, repArr, repArr);
- }
- // add any remaining cutoff
- var r;
- if (r = item.length % item.distance)
- JSIO.massApply(b.push, b, b.slice(j, j+r));
- }
- break;
- }
- }
- var bytes = b.slice(this._bufferPosition, this._bufferPosition+len);
- this._bufferPosition += len;
- this.position += len;
-
- if (this._bufferPosition > 0xC000) {
- var shift = b.length - 0x8000;
- if (shift > this._bufferPosition) shift = this._bufferPosition;
- b.splice(0, shift);
- this._bufferPosition -= shift;
- }
-
- return bytes;
- };
-
- // initialization routine - once per type
- (function(){
-
- var codes = new Array(288);
- var codesLengths = new Array(288);
- var i=0;
- for ( i = 0; i <= 143; i++) {
- codes[i] = 0x0030 + i;
- codesLengths[i] = 8;
- }
- for ( i = 144; i <= 255; i++) {
- codes[i] = 0x0190 + i - 144;
- codesLengths[i] = 9;
- }
- for ( i = 256; i <= 279; i++) {
- codes[i] = 0x0000 + i - 256;
- codesLengths[i] = 7;
- }
- for ( i = 280; i <= 287; i++) {
- codes[i] = 0x00C0 + i - 280;
- codesLengths[i] = 8;
- }
- staticCodes = buildTree(codes, codesLengths);
-
- var distances = new Array(32);
- var distancesLengths = new Array(32);
- for ( i = 0; i <= 31; i++) {
- distances[i] = i;
- distancesLengths[i] = 5;
- }
- staticDistances = buildTree(distances, distancesLengths);
- })();
-
-
- JSIO.InflatingReader = _inflatingReader;
+ var version = "2.0 2012Feb";
+ var typename = "JSIO.InflatingReader";
+
+ if (typeof JSIO._ByteReaderBase !== "function")
+ JSIO.throwError('This extension requires JSIO.BasicByteReaders.js', typename);
+
+ // =======================================================
+ // _InternalBitReader is used internally in the InflatingReader class.
+ //
+
+ JSIO.bitShiftTable = null;
+
+ var bitShiftTableCalc = function () {
+ // do this once only, for all instances
+ if (JSIO.bitShiftTable) return;
+
+ var bits = 8;
+ JSIO.bitShiftTable = {
+ LSB: new Array(bits),
+ MSB: new Array(bits)
+ }
+ for (var bp = 0; bp < bits; bp++) {
+ var lim = bits - bp;
+ JSIO.bitShiftTable.LSB[bp] = new Array(lim);
+ JSIO.bitShiftTable.MSB[bp] = new Array(lim);
+
+ var maskLSB = 1 << bp;
+ var maskMSB = 1 << lim-1;
+ for (var len = 1; len <= lim; len++) {
+ JSIO.bitShiftTable.LSB[bp][len-1] = maskLSB;
+ JSIO.bitShiftTable.MSB[bp][len-1] = maskMSB;
+ maskLSB |= 1 << bp+len;
+ maskMSB |= 1 << lim-len-1;
+ }
+ }
+ };
+
+ var _InternalBitReader = function(reader) {
+ if (! (this instanceof arguments.callee) ) this._throwError('You must use new to instantiate this class', typename + '._InternalBitReader.ctor');
+ this.bitsLength = 0;
+ this.bits = 0;
+ this.byteReader = reader;
+ this._typeName = typename + "._InternalBitReader";
+ this._version = version;
+ bitShiftTableCalc();
+ };
+
+ _InternalBitReader.prototype._throwError = JSIO.throwError;
+
+ _InternalBitReader.prototype.readBit = function() {
+ if (this.bitsLength === 0) {
+ var nextByte = this.byteReader.readByte();
+ if (nextByte === null) this._throwError('Unexpected end of stream', null, 'readBit');
+ this.bits = nextByte;
+ this.bitsLength = 8;
+ }
+
+ var bit = (this.bits & 1 << 8-this.bitsLength) !== 0;
+ this.bitsLength--;
+ return bit;
+ };
+
+ _InternalBitReader.prototype.align = function() { this.bitsLength = 0; };
+
+ _InternalBitReader.prototype.readBits = function(len, type) {
+ var data = 0;
+ type = type || 'LSB';
+ var tbl = JSIO.bitShiftTable[type];
+ var dl = 0;
+ while (len > 0) {
+ if (this.bitsLength === 0) {
+ var nextByte = this.byteReader.readByte();
+ if (nextByte === null) this._throwError('Unexpected end of stream', null, 'read'+type);
+ this.bits = nextByte;
+ this.bitsLength = 8;
+ }
+
+ var maskLen = (this.bitsLength >= len) ? len : this.bitsLength;
+ var bitsPos = 8-this.bitsLength;
+ data |= (this.bits & tbl[bitsPos][maskLen-1]) >>> bitsPos << dl;
+
+ dl += maskLen;
+ len -= maskLen;
+ this.bitsLength -= maskLen;
+ };
+ return data;
+ };
+
+ _InternalBitReader.prototype.readLSB = function(len) { return this.readBits(len, 'LSB'); }
+ _InternalBitReader.prototype.readMSB = function(len) { return this.readBits(len, 'MSB'); }
+
+ //
+ // =======================================================
+
+
+ /* inflating ByteReader - RFC 1951 */
+ var _inflatingReader = function(reader) {
+ if (! (this instanceof arguments.callee) ) this._throwError('You must use new to instantiate this class', typename, 'ctor');
+ this._byteReader = reader;
+ this._bitReader = new _InternalBitReader(reader);
+ this._buffer = [];
+ this._bufferPosition = 0;
+ this._state = 0;
+ this._blockFinal = false;
+ this._typeName = typename;
+ this._version = version;
+ return this;
+ };
+
+
+ // shared fns and variables
+
+ var staticCodes = null;
+ var staticDistances = null;
+
+ var clenMap = [16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15];
+
+ var buildCodes = function(lengths){
+ var i=0;
+ var codes = new Array(lengths.length);
+ var maxBits = lengths[0];
+ for (i=1; i<lengths.length; i++) {
+ if (maxBits < lengths[i]) maxBits = lengths[i];
+ }
+
+ var bitLengthsCount = new Array(maxBits + 1);
+ for (i=0; i<=maxBits; i++) bitLengthsCount[i]=0;
+
+ for (i=0; i<lengths.length; i++) {
+ ++bitLengthsCount[lengths[i]];
+ }
+
+ var nextCode = new Array(maxBits + 1);
+ var code = 0;
+ bitLengthsCount[0] = 0;
+ for (var bits=1; bits<=maxBits; bits++) {
+ code = (code + bitLengthsCount[bits - 1]) << 1;
+ nextCode[bits] = code;
+ }
+
+ for (i=0; i<codes.length; i++) {
+ var len = lengths[i];
+ if (len !== 0) {
+ codes[i] = nextCode[len];
+ nextCode[len]++;
+ }
+ }
+ return codes;
+ };
+
+ var buildTree = function(codes, lengths){
+ var nonEmptyCodes = [];
+ for(var i=0; i<codes.length; ++i) {
+ if(lengths[i] > 0) {
+ var code = {};
+ code.bits = codes[i];
+ code.length = lengths[i];
+ code.index = i;
+ nonEmptyCodes.push(code);
+ }
+ }
+ return buildTreeBranch(nonEmptyCodes, 0, 0);
+ };
+
+
+ var buildTreeBranch = function(codes, prefix, prefixLength){
+ if (codes.length === 0) return null;
+
+ var zeros = [];
+ var ones = [];
+ var branch = {};
+ branch.isLeaf = false;
+ for(var i=0; i<codes.length; ++i) {
+ if (codes[i].length == prefixLength && codes[i].bits == prefix) {
+ branch.isLeaf = true;
+ branch.index = codes[i].index;
+ break;
+ }
+ else {
+ var nextBit = ((codes[i].bits >> (codes[i].length - prefixLength - 1)) & 1) > 0;
+ if (nextBit) ones.push(codes[i]);
+ else zeros.push(codes[i]);
+ }
+ }
+ if(!branch.isLeaf) {
+ branch.zero = buildTreeBranch(zeros, (prefix << 1), prefixLength + 1);
+ branch.one = buildTreeBranch(ones, (prefix << 1) | 1, prefixLength + 1);
+ }
+ return branch;
+ };
+
+
+ var encodedLengthStart = [3,4,5,6,7,8,9,10,
+ 11,13,15,17,19,23,27,31,35,43,51,59,67,83,99,
+ 115,131,163,195,227,258];
+
+ var encodedLengthAdditionalBits = [0,0,0,0,0,0,0,0,
+ 1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0];
+
+ var encodedDistanceStart = [1,2,3,4, 5,7,9, 13,17,25, 33,49,65,
+ 97,129,193,257,385,513,769,1025,1537,2049,
+ 3073,4097,6145,8193,12289,16385,24577];
+
+ var encodedDistanceAdditionalBits = [0,0,0,0,1,1,2,2,3,3,4,4,
+ 5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13];
+
+
+ var readDynamicTrees = function(bitReader){
+ var hlit = bitReader.readLSB(5) + 257;
+ var hdist = bitReader.readLSB(5) + 1;
+ var hclen = bitReader.readLSB(4) + 4;
+ var clen = new Array(19);
+ var i;
+ for (i=0; i<clen.length; i++) { clen[i] = 0; }
+ for (i=0; i<hclen; i++) { clen[clenMap[i]] = bitReader.readLSB(3); }
+
+ var clenCodes = buildCodes(clen);
+ var clenTree = buildTree(clenCodes, clen);
+
+ var lengthsSequence = [];
+ while(lengthsSequence.length < hlit + hdist) {
+ var p = clenTree;
+ while(!p.isLeaf) { p = bitReader.readBit() ? p.one : p.zero; }
+
+ var code = p.index;
+ if (code <= 15) lengthsSequence.push(code);
+ else if (code == 16) {
+ // TODO: replace with faster repeat algorythm
+ var repeat = bitReader.readLSB(2) + 3;
+ for(var q=0; q<repeat; ++q){
+ lengthsSequence.push(lengthsSequence[lengthsSequence.length - 1]);
+ }
+ }
+ else if (code == 17) {
+ var repeat1 = bitReader.readLSB(3) + 3;
+ for(var q1=0; q1<repeat1; ++q1) {
+ lengthsSequence.push(0);
+ }
+ }
+ else if (code == 18) {
+ var repeat2 = bitReader.readLSB(7) + 11;
+ for(var q2=0; q2<repeat2; ++q2){
+ lengthsSequence.push(0);
+ }
+ }
+ }
+
+ var codesLengths = lengthsSequence.slice(0, hlit);
+ var codes = buildCodes(codesLengths);
+ var distancesLengths = lengthsSequence.slice(hlit, hlit + hdist);
+ var distances = buildCodes(distancesLengths);
+
+ return {
+ codesTree : buildTree(codes, codesLengths),
+ distancesTree : buildTree(distances, distancesLengths)
+ };
+ };
+
+
+ _inflatingReader.prototype = new JSIO._ByteReaderBase();
+
+
+ // internal instance fns
+ _inflatingReader.prototype._decodeItem = function() {
+ if (this._state == 2) return null; // end-of-blocks
+
+ var item;
+ if(this._state === 0) {
+ this._blockFinal = this._bitReader.readBit();
+ var blockType = this._bitReader.readLSB(2);
+ switch(blockType) {
+ case 0:
+ this._bitReader.align();
+ var len = this._bitReader.readLSB(16); // low-byte first, as opposed to readNumber's HBF
+ var nlen = this._bitReader.readLSB(16);
+ if ((len & ~nlen) != len) this._throwError('Invalid block type 0 length', null, '_decodeItem');
+
+ item = {};
+ item.itemType = 0;
+ item.array = this._bitReader.byteReader.read(len);
+ if (item.array.length < len) this._throwError('Incomplete block', null, '_decodeItem');
+ if (this._blockFinal) this._state = 2;
+ return item;
+ case 1:
+ this._codesTree = staticCodes;
+ this._distancesTree = staticDistances;
+ this._state = 1;
+ break;
+ case 2:
+ var dTrees = readDynamicTrees(this._bitReader);
+ this._codesTree = dTrees.codesTree;
+ this._distancesTree = dTrees.distancesTree;
+ this._state = 1;
+ break;
+ default:
+ this._throwError('Invalid block type ('+ blockType +')', null, '_decodeItem');
+ }
+ }
+
+ item = {};
+
+ var p = this._codesTree;
+ while (!p.isLeaf) { p = this._bitReader.readBit() ? p.one : p.zero; }
+ if(p.index < 256) {
+ item.itemType = 2;
+ item.symbol = p.index;
+ } else if(p.index > 256) {
+ var lengthCode = p.index;
+ if(lengthCode > 285) this._throwError('Invalid length code', null, '_decodeItem');
+
+ var length = encodedLengthStart[lengthCode - 257];
+ if(encodedLengthAdditionalBits[lengthCode - 257] > 0) {
+ length += this._bitReader.readLSB(encodedLengthAdditionalBits[lengthCode - 257]);
+ }
+
+ p = this._distancesTree;
+ while (!p.isLeaf) { p = this._bitReader.readBit() ? p.one : p.zero; }
+
+ var distanceCode = p.index;
+ var distance = encodedDistanceStart[distanceCode];
+ if (encodedDistanceAdditionalBits[distanceCode] > 0)
+ distance += this._bitReader.readLSB(encodedDistanceAdditionalBits[distanceCode]);
+
+ item.itemType = 3;
+ item.distance = distance;
+ item.length = length;
+ } else {
+ item.itemType = 1;
+ this._state = this._blockFinal ? 2 : 0; // EOB
+ }
+ return item;
+ };
+
+
+
+ // public instance functions
+
+ _inflatingReader.prototype.readByte = function() {
+ var oneByte = this.read(1)[0];
+ return (oneByte === null || oneByte === undefined) ? null : oneByte;
+ };
+
+ _inflatingReader.prototype.read = function(len) {
+ var b = this._buffer; // (since we use this so much...)
+
+ // Keep reading until we get to the right length
+ while (this._bufferPosition+len > b.length) {
+ var item = this._decodeItem();
+ if (item === null) { // EOF
+ len = b.length - this._bufferPosition;
+ break;
+ }
+ switch(item.itemType) {
+ case 0:
+ JSIO.massApply(b.push, b, item.array);
+ break;
+ case 2:
+ b.push(item.symbol);
+ break;
+ case 3:
+ var j = b.length - item.distance;
+ if (item.distance >= item.length)
+ JSIO.massApply(b.push, b, b.slice(j, j+item.length));
+ // sometimes DEFLATE tries some trickery with "look-ahead" compression
+ else {
+ // this is basically just a repetition of the same string, plus some possible cutoff
+ var count = parseInt(item.length / item.distance);
+ var repArr = b.slice(j);
+ // http://stackoverflow.com/questions/202605/repeat-string-javascript/5450113#5450113
+ while (count > 0) {
+ if (count & 1) JSIO.massApply( b.push, b, repArr);
+ if (count >>= 1) JSIO.massApply(repArr.push, repArr, repArr);
+ }
+ // add any remaining cutoff
+ var r;
+ if (r = item.length % item.distance)
+ JSIO.massApply(b.push, b, b.slice(j, j+r));
+ }
+ break;
+ }
+ }
+ var bytes = b.slice(this._bufferPosition, this._bufferPosition+len);
+ this._bufferPosition += len;
+ this.position += len;
+
+ if (this._bufferPosition > 0xC000) {
+ var shift = b.length - 0x8000;
+ if (shift > this._bufferPosition) shift = this._bufferPosition;
+ b.splice(0, shift);
+ this._bufferPosition -= shift;
+ }
+
+ return bytes;
+ };
+
+ // initialization routine - once per type
+ (function(){
+
+ var codes = new Array(288);
+ var codesLengths = new Array(288);
+ var i=0;
+ for ( i = 0; i <= 143; i++) {
+ codes[i] = 0x0030 + i;
+ codesLengths[i] = 8;
+ }
+ for ( i = 144; i <= 255; i++) {
+ codes[i] = 0x0190 + i - 144;
+ codesLengths[i] = 9;
+ }
+ for ( i = 256; i <= 279; i++) {
+ codes[i] = 0x0000 + i - 256;
+ codesLengths[i] = 7;
+ }
+ for ( i = 280; i <= 287; i++) {
+ codes[i] = 0x00C0 + i - 280;
+ codesLengths[i] = 8;
+ }
+ staticCodes = buildTree(codes, codesLengths);
+
+ var distances = new Array(32);
+ var distancesLengths = new Array(32);
+ for ( i = 0; i <= 31; i++) {
+ distances[i] = i;
+ distancesLengths[i] = 5;
+ }
+ staticDistances = buildTree(distances, distancesLengths);
+ })();
+
+
+ JSIO.InflatingReader = _inflatingReader;
})();
@@ -1713,460 +1713,537 @@
// This work is licensed under the GPLv3.
(function(){
- var version = "2.0 2012Feb";
- var typename = "Zipfile";
-
- if (typeof JSIO.BinaryUrlStream !== "function") JSIO.throwError('This extension requires JSIO.BinaryUrlStream.js v2.0', typename);
- if (typeof JSIO.TextDecoder !== "object") JSIO.throwError('This extension requires JSIO.TextDecoder.js v2.0', typename);
- if (typeof JSIO.TextReader !== "function") JSIO.throwError('This extension requires JSIO.TextReader.js v2.0', typename);
- if (typeof JSIO.Crc32 !== "function") JSIO.throwError('This extension requires JSIO.Crc32.js v2.0', typename);
- if (typeof JSIO.InflatingReader !== "function") JSIO.throwError('This extension requires JSIO.InflatingReader.js v2.0', typename);
-
- // =======================================================
- function ZipEntry(zip) {
- this.zipfile = zip;
- this._typename = "ZipEntry";
- this._version = version;
- this._crcCalculator = null;
- }
-
- ZipEntry.prototype._throwError = JSIO.throwError;
-
- // return byte array or string
- ZipEntry.prototype.extract = function(callback, asString) {
- this.contentType = JSIO.guessFileType(this.name);
- asString = asString || ( this.contentType == JSIO.FileType.Text ||
- this.contentType == JSIO.FileType.XML);
- var thisEntry = this;
-
- if (this.compressionMethod !== 0 && this.compressionMethod != 8)
- this._throwError('Unsupported compression method: ' + this.compressionMethod, null, 'extract');
-
- var reader = (asString) ? this.openTextReader(thisEntry.utf8 ? JSIO.TextDecoder.UTF8 : JSIO.TextDecoder.ANSI) : this.openBinaryReader();
-
- // diagnostic purpose only; tag the reader with the entry name
- reader.zipEntryName = thisEntry.name;
-
- if (typeof callback != "function") {
- // synchronous
- var result = reader.readToEnd();
- this.verifyCrc32();
- return result;
- }
-
- // asynchronous
- reader.beginReadToEnd(function(result){
- try {
- thisEntry.verifyCrc32();
- callback(thisEntry, result);
- }
- catch (exc1) {
- callback(thisEntry, exc1);
- }
- });
- return null;
- };
-
-
- // open a ByteReader on the entry, which will read binary
- // content from the compressed stream.
- ZipEntry.prototype.openBinaryReader = function() {
- var reader =
- new JSIO.StreamSegmentReader(this.zipfile.binaryStream,
- this.offset + this.lengthOfHeader,
- this.compressedSize);
- if (this.compressionMethod === 0) {
- this._crcCalculator = new JSIO.Crc32Reader(reader);
- }
- else {
- var inflator = new JSIO.InflatingReader(reader);
- this._crcCalculator = new JSIO.Crc32Reader(inflator);
- }
- // Whether compressed or not, the source ByteReader in each case
- // is wrapped in a second ByteReader object that calculates CRC
- // as it reads. That way, after all reading is complete, the
- // caller can check the calcuated CRC against the expected CRC.
- return this._crcCalculator;
- };
-
- // open a TextReader on the entry, to read text from the
- // compressed stream.
- ZipEntry.prototype.openTextReader = function(decoderKind) {
- var reader = this.openBinaryReader();
- decoderKind = decoderKind || JSIO.TextDecoder.UTF8;
- var d = new decoderKind(reader);
- var textReader = new JSIO.TextReader(d);
- d._parent = textReader; // store a reference, for diagnostic purposes only
- return textReader;
- };
-
- // verify the CRC on the entry.
- // call this after all bytes have been read.
- ZipEntry.prototype.verifyCrc32 = function() {
- var computedCrc = this._crcCalculator.crc32();
- var rc = false; // CRC FAIL
- if (this.crc32 != computedCrc) {
- var msg = "WARNING: CRC check failed: " +
- "entry(" + this.name + ") " +
- "computed(" + JSIO.decimalToHexString(computedCrc,8) + ") " +
- "expected(" + JSIO.decimalToHexString(this.crc32,8) + ") ";
- this.zipfile.status.push(msg);
- } else {
- rc = true; // OK
- if (this.zipfile.verbose>2) {
- this.zipfile.status.push("INFO: CRC check ok: 0x" +
- JSIO.decimalToHexString(this.crc32,8));
- }
- }
- return rc;
- };
-
-
- // ctor
- ZipFile = function(fileUrl, callback, verbosity) {
- if (! (this instanceof arguments.callee) ) JSIO.throwError('You must use new to instantiate this class', typename, 'ctor');
-
- this.verbose = verbosity || 0;
- this.entries = [];
- this.entryNames = [];
- this.status = [];
- this._version = version;
- this._typename = "ZipFile";
- this._throwError = JSIO.throwError;
-
- var thisZipFile = this;
-
- // Could use a back-tracking reader for the central directory, but
- // there's no point, since all the zip data is held in memory anyway.
-
- /* function ReadCentralDirectory(){
- var posn = thisZipFile.binaryStream.length - 64;
- var maxSeekback = Math.Max(s.Length - 0x4000, 10);
- var success = false;
- var nTries = 0;
- do
- {
- thisZipFile.binaryStream.Seek(posn, SeekOrigin.Begin);
- var bytesRead = thisZipFile.binaryStream.findSignature(thisZipFile.Signatures.EndOfCentralDirectory);
- if (bytesRead != -1)
- success = true;
- else
- {
- nTries++;
- // increasingly larger
- posn -= (32 * (nTries + 1) * nTries);
- if (posn < 0) posn = 0; // BOF
- }
- }
- while (!success && posn > maxSeekback);
- if (!success) {
- thisZipFile.status.push("cannot find End of Central Directory");
- return;
- }
- } */
-
-
- function DateFromPackedFormat(packed) {
- if (packed == 0xFFFF || packed === 0) return new Date(1995, 0, 1, 0,0,0,0);
-
- var packedTime = packed & 0x0000ffff;
- var packedDate = ((packed & 0xffff0000) >> 16);
-
- var year = 1980 + ((packedDate & 0xFE00) >> 9);
- var month = ((packedDate & 0x01E0) >> 5) -1;
- var day = packedDate & 0x001F;
-
- var hour = (packedTime & 0xF800) >> 11;
- var minute = (packedTime & 0x07E0) >> 5;
- var second = (packedTime & 0x001F) * 2;
-
- // Validation and error checking.
- // This is not foolproof but will catch most errors.
-
- // I can't believe how many different ways applications
- // can mess up a simple date format.
-
- if (second >= 60) { minute++; second = 0; }
- if (minute >= 60) { hour++; minute = 0; }
- if (hour >= 24) { day++; hour = 0; }
- var success = false;
- var d;
- try {
- d = new Date(year, month, day, hour, minute, second, 0);
- success= true;
- }
- catch (exc1) {
- if (year == 1980 && (month === 0 || day === 0)) {
- try {
- d = new Date(1980, 0, 1, hour, minute, second, 0);
- success= true;
- }
- catch (exc2) {
- try {
- d = new Date(1980, 0, 1, 0, 0, 0, 0);
- success= true;
- }
- catch (exc3) { } // how could this fail??
- }
- }
- else {
- try {
- if (year < 1980) year = 1980;
- if (year > 2030) year = 2030;
- if (month < 1) month = 1;
- if (month > 12) month = 12;
- if (day < 1) day = 1;
- if (day > 31) day = 31;
- if (minute < 0) minute = 0;
- if (minute > 59) minute = 59;
- if (second < 0) second = 0;
- if (second > 59) second = 59;
- d = new Date(year, month-1, day, hour, minute, second, 0);
- success= true;
- }
- catch (exc4){}
- }
- }
- if (!success) this._throwError('Bad date/time value in this ZIP file', null, 'DateFromPackedFormat');
- return d;
- }
-
-
- function ReadZipEntries () {
- // read only once
- if (thisZipFile.entryNames.length === 0){
- var e;
- while ((e = ReadZipEntry()) !== null) {
- thisZipFile.entries.push(e);
- thisZipFile.entryNames.push(e.name);
- }
- }
- }
-
-
- function ReadZipEntry () {
- var offset = thisZipFile.binaryStream.position;
- var sig = thisZipFile.binaryStream.readNumber(4);
- if (sig == ZipFile.Signatures.DirEntry) {
- // after all entries, comes the central directory
- if (thisZipFile.verbose > 0) {
- thisZipFile.status.push("INFO: at offset 0x" +
- JSIO.decimalToHexString(offset) +
- ", found start of Zip Directory.");
- }
- // all done reading
- return null;
- }
- if (sig != ZipFile.Signatures.Entry) {
- thisZipFile.status.push("WARNING: at offset 0x" +
- JSIO.decimalToHexString(offset) +
- ", found unexpected signature: 0x" +
- JSIO.decimalToHexString(sig));
- return null;
- }
-
- var entry = new ZipEntry(thisZipFile);
- entry.offset = offset;
- entry.versionNeeded = thisZipFile.binaryStream.readNumber(2);
- entry.bitField = thisZipFile.binaryStream.readNumber(2);
- entry.compressionMethod = thisZipFile.binaryStream.readNumber(2);
- var timeBlob = thisZipFile.binaryStream.readNumber(4);
- entry.lastModified = DateFromPackedFormat(timeBlob);
- entry.crc32 = thisZipFile.binaryStream.readNumber(4);
- entry.compressedSize = thisZipFile.binaryStream.readNumber(4);
- entry.uncompressedSize = thisZipFile.binaryStream.readNumber(4);
-
- if ((entry.bitField & 0x01) == 0x01){
- thisZipFile.status.push("This zipfile uses Encryption, which is not supported by ZipFile.js.");
- return null;
- }
-
- entry.utf8 = ((entry.bitField & 0x0800) == 0x0800);
-
- if ((entry.bitField & 0x0008) == 0x0008){
- thisZipFile.status.push("This zipfile uses a bit 3 trailing data descriptor, which is not supported by ZipFile.js.");
- return null;
- }
-
- if (entry.compressedSize == 0xFFFFFFFF ||
- entry.uncompressedSize == 0xFFFFFFFF) {
- thisZipFile.status.push("This zipfile uses ZIP64, which is not supported by ZipFile.js");
- return null;
- }
-
- var filenameLength = thisZipFile.binaryStream.readNumber(2);
- var extraFieldLength = thisZipFile.binaryStream.readNumber(2);
-
- thisZipFile.status.push("INFO: filename length= " + filenameLength);
-
- // we've read 30 bytes of metadata so far
- var bytesRead = 30 + filenameLength + extraFieldLength;
-
- if (entry.utf8) {
- thisZipFile.status.push("INFO: before filename, position= 0x" +
- JSIO.decimalToHexString( thisZipFile.binaryStream.position ));
- var binReader =
- new JSIO.StreamSegmentReader(thisZipFile.binaryStream,
- thisZipFile.binaryStream.position,
- filenameLength);
- var utf8Decoder = new JSIO.TextDecoder.UTF8(binReader);
- var textReader = new JSIO.TextReader(utf8Decoder);
- entry.name = textReader.readToEnd();
-
- // advance the filepointer:
- thisZipFile.binaryStream.seek(filenameLength,
- JSIO.SeekOrigin.Current,
- thisZipFile);
-
- thisZipFile.status.push("INFO: after filename, position= 0x" +
- JSIO.decimalToHexString( thisZipFile.binaryStream.position ));
- }
- else {
- entry.name = thisZipFile.binaryStream.readString(filenameLength);
- }
-
- // There are a bunch of things in the "extra" header, thisZipFile we
- // could parse, like timestamps and other things. This class
- // only identifies and separates them.
-
- // More info here: http://www.pkware.com/documents/casestudies/APPNOTE.TXT
-
- var extraPos = 0;
- entry.extra = [];
- while (extraPos < extraFieldLength) {
- var extraBlock = {
- type: thisZipFile.binaryStream.readNumber(2),
- size: thisZipFile.binaryStream.readNumber(2)
- };
- extraBlock.typeDescription = ZipFile.ExtraFieldTypes[extraBlock.type];
- extraBlock.data = thisZipFile.binaryStream.read(extraBlock.size);
- entry.extra.push(extraBlock);
- extraPos += 4 + extraBlock.size;
- }
-
- if (thisZipFile.verbose > 1) {
- thisZipFile.status.push("INFO: at offset 0x" +
- JSIO.decimalToHexString(entry.offset) +
- ", found entry '" + entry.name + "' fnl(" +
- filenameLength + ") efl(" +
- extraFieldLength +")");
- }
-
- if (extraFieldLength > 0) {
- if (thisZipFile.verbose > 0) {
- thisZipFile.status.push("INFO: entry " + entry.name + " has " +
- extraFieldLength + " bytes of " +
- "extra metadata (ID'd but ignored)");
- }
- }
-
- entry.lengthOfHeader = bytesRead;
- entry.totalEntrySize = entry.lengthOfHeader + entry.compressedSize;
-
- // seek past the data without reading it. We will read on Extract()
- if (thisZipFile.verbose > 1) {
- thisZipFile.status.push("INFO: seek 0x" +
- JSIO.decimalToHexString(entry.compressedSize) +
- " (" + entry.compressedSize + ") bytes");
- }
-
- thisZipFile.binaryStream.seek(entry.compressedSize,
- JSIO.SeekOrigin.Current,
- thisZipFile);
-
- return entry;
- }
-
-
- var parseZipFile = function(bfr){
- try {
- if (bfr.req.status == 200) {
- var sig = thisZipFile.binaryStream.readNumber(4);
- if (sig != ZipFile.Signatures.Entry){
- thisZipFile.status.push("WARNING: this file does not appear to be a zip file");
- } else {
- thisZipFile.binaryStream.seek(0, JSIO.SeekOrigin.Begin);
- ReadZipEntries();
- if (thisZipFile.verbose > 0) {
- thisZipFile.status.push("INFO: read " + thisZipFile.entries.length + " entries");
- }
- }
- }
- else {
- thisZipFile.status.push("ERROR: the URL could not be read (" +
- bfr.req.status + " " + bfr.req.statusText + ")");
- }
- callback(thisZipFile);
- }
- catch (exc1)
- {
- thisZipFile.status.push("Exception: " + exc1.message);
- callback(thisZipFile);
- }
- };
-
- this.binaryStream = new JSIO.BinaryUrlStream(fileUrl, parseZipFile);
-
- return this;
- };
-
-
- ZipFile.Signatures = {
- Entry : 0x04034b50,
- EndOfCentralDirectory : 0x06054b50,
- DirEntry : 0x02014b50
- };
-
- ZipFile.Version = version;
-
- ZipFile.EncryptionAlgorithm = {
- None : 0,
- PkzipWeak : 1,
- WinZipAes : 2
- };
-
- ZipFile.ExtraFieldTypes = {};
- ZipFile.ExtraFieldTypes[0x0001] = 'Zip64 Extended Info';
- ZipFile.ExtraFieldTypes[0x0007] = 'AV Info';
- ZipFile.ExtraFieldTypes[0x0008] = 'Extended Language Encoding Data (PFS)';
- ZipFile.ExtraFieldTypes[0x0009] = 'OS/2';
- ZipFile.ExtraFieldTypes[0x000a] = 'NTFS ';
- ZipFile.ExtraFieldTypes[0x000c] = 'OpenVMS';
- ZipFile.ExtraFieldTypes[0x000d] = 'UNIX';
- ZipFile.ExtraFieldTypes[0x000e] = 'File Stream and Fork Descriptors';
- ZipFile.ExtraFieldTypes[0x000f] = 'Patch Descriptor';
- ZipFile.ExtraFieldTypes[0x0014] = 'PKCS#7 Store for X.509 Certificates';
- ZipFile.ExtraFieldTypes[0x0015] = 'X.509 Certificate ID and Signature (Individual File)';
- ZipFile.ExtraFieldTypes[0x0016] = 'X.509 Certificate ID (Central Directory)';
- ZipFile.ExtraFieldTypes[0x0017] = 'Strong Encryption Header';
- ZipFile.ExtraFieldTypes[0x0018] = 'Record Management Controls';
- ZipFile.ExtraFieldTypes[0x0019] = 'PKCS#7 Encryption Recipient Certificate List';
- ZipFile.ExtraFieldTypes[0x0065] = 'IBM S/390 (Z390), AS/400 (I400) attributes (uncompressed)';
- ZipFile.ExtraFieldTypes[0x0066] = 'IBM S/390 (Z390), AS/400 (I400) attributes (compressed)';
- ZipFile.ExtraFieldTypes[0x4690] = 'POSZIP 4690 (reserved) ';
- ZipFile.ExtraFieldTypes[0x07c8] = 'Macintosh';
- ZipFile.ExtraFieldTypes[0x2605] = 'ZipIt Macintosh';
- ZipFile.ExtraFieldTypes[0x2705] = 'ZipIt Macintosh 1.3.5+';
- ZipFile.ExtraFieldTypes[0x2805] = 'ZipIt Macintosh 1.3.5+';
- ZipFile.ExtraFieldTypes[0x334d] = 'Info-ZIP Macintosh';
- ZipFile.ExtraFieldTypes[0x4341] = 'Acorn/SparkFS ';
- ZipFile.ExtraFieldTypes[0x4453] = 'Windows NT security descriptor (binary ACL)';
- ZipFile.ExtraFieldTypes[0x4704] = 'VM/CMS';
- ZipFile.ExtraFieldTypes[0x470f] = 'MVS';
- ZipFile.ExtraFieldTypes[0x4b46] = 'FWKCS MD5';
- ZipFile.ExtraFieldTypes[0x4c41] = 'OS/2 access control list (text ACL)';
- ZipFile.ExtraFieldTypes[0x4d49] = 'Info-ZIP OpenVMS';
- ZipFile.ExtraFieldTypes[0x4f4c] = 'Xceed original location extra field';
- ZipFile.ExtraFieldTypes[0x5356] = 'AOS/VS (ACL)';
- ZipFile.ExtraFieldTypes[0x5455] = 'extended timestamp';
- ZipFile.ExtraFieldTypes[0x554e] = 'Xceed unicode extra field';
- ZipFile.ExtraFieldTypes[0x5855] = 'Info-ZIP UNIX (original, also OS/2, NT, etc)';
- ZipFile.ExtraFieldTypes[0x6375] = 'Info-ZIP Unicode Comment Extra Field';
- ZipFile.ExtraFieldTypes[0x6542] = 'BeOS/BeBox';
- ZipFile.ExtraFieldTypes[0x7075] = 'Info-ZIP Unicode Path Extra Field';
- ZipFile.ExtraFieldTypes[0x756e] = 'ASi UNIX';
- ZipFile.ExtraFieldTypes[0x7855] = 'Info-ZIP UNIX (new)';
- ZipFile.ExtraFieldTypes[0xa220] = 'Microsoft Open Packaging Growth Hint';
- ZipFile.ExtraFieldTypes[0xfd4a] = 'SMS/QDOS';
+ var version = "2.0 2012Feb";
+ var typename = "Zipfile";
+
+ if (typeof JSIO.BinaryUrlStream !== "function") JSIO.throwError('This extension requires JSIO.BinaryUrlStream.js v2.0', typename);
+ if (typeof JSIO.TextDecoder !== "object") JSIO.throwError('This extension requires JSIO.TextDecoder.js v2.0', typename);
+ if (typeof JSIO.TextReader !== "function") JSIO.throwError('This extension requires JSIO.TextReader.js v2.0', typename);
+ if (typeof JSIO.Crc32 !== "function") JSIO.throwError('This extension requires JSIO.Crc32.js v2.0', typename);
+ if (typeof JSIO.InflatingReader !== "function") JSIO.throwError('This extension requires JSIO.InflatingReader.js v2.0', typename);
+
+ // =======================================================
+ function ZipEntry(zip) {
+ this.zipfile = zip;
+ this._typename = "ZipEntry";
+ this._version = version;
+ this._crcCalculator = null;
+ }
+
+ ZipEntry.prototype._throwError = JSIO.throwError;
+
+ // return byte array or string
+ ZipEntry.prototype.extract = function(callback, asString) {
+ this.contentType = JSIO.guessFileType(this.name);
+ asString = asString || ( this.contentType == JSIO.FileType.Text ||
+ this.contentType == JSIO.FileType.XML);
+ var thisEntry = this;
+
+ if (this.compressionMethod !== 0 && this.compressionMethod != 8)
+ this._throwError('Unsupported compression method: ' + this.compressionMethod, null, 'extract');
+
+ var reader = (asString) ? this.openTextReader(thisEntry.utf8 ? JSIO.TextDecoder.UTF8 : JSIO.TextDecoder.ANSI) : this.openBinaryReader();
+
+ // diagnostic purpose only; tag the reader with the entry name
+ reader.zipEntryName = thisEntry.name;
+
+ if (typeof callback != "function") {
+ // synchronous
+ var result = reader.readToEnd();
+ this.verifyCrc32();
+ return result;
+ }
+
+ // asynchronous
+ reader.beginReadToEnd(function(result){
+ try {
+ thisEntry.verifyCrc32();
+ callback(thisEntry, result);
+ }
+ catch (exc1) {
+ this.zipfile.status.push("EXCEPTION: " + exc1.message);
+ callback(thisEntry, exc1);
+ }
+ });
+ return null;
+ };
+
+
+ // open a ByteReader on the entry, which will read binary
+ // content from the compressed stream.
+ ZipEntry.prototype.openBinaryReader = function() {
+ var reader =
+ new JSIO.StreamSegmentReader(this.zipfile.binaryStream,
+ this.offset + this.lengthOfHeader,
+ this.compressedSize);
+ if (this.compressionMethod === 0) {
+ this._crcCalculator = new JSIO.Crc32Reader(reader);
+ }
+ else {
+ var inflator = new JSIO.InflatingReader(reader);
+ this._crcCalculator = new JSIO.Crc32Reader(inflator);
+ }
+ // Whether compressed or not, the source ByteReader in each case
+ // is wrapped in a second ByteReader object that calculates CRC
+ // as it reads. That way, after all reading is complete, the
+ // caller can check the calcuated CRC against the expected CRC.
+ return this._crcCalculator;
+ };
+
+ // open a TextReader on the entry, to read text from the
+ // compressed stream.
+ ZipEntry.prototype.openTextReader = function(decoderKind) {
+ var reader = this.openBinaryReader();
+ decoderKind = decoderKind || JSIO.TextDecoder.UTF8;
+ var d = new decoderKind(reader);
+ var textReader = new JSIO.TextReader(d);
+ d._parent = textReader; // store a reference, for diagnostic purposes only
+ return textReader;
+ };
+
+ // verify the CRC on the entry.
+ // call this after all bytes have been read.
+ ZipEntry.prototype.verifyCrc32 = function() {
+ var computedCrc = this._crcCalculator.crc32();
+ var rc = false; // CRC FAIL
+ if (this.crc32 != computedCrc) {
+ var msg = "WARNING: CRC check failed: " +
+ "entry(" + this.name + ") " +
+ "computed(" + JSIO.decimalToHexString(computedCrc,8) + ") " +
+ "expected(" + JSIO.decimalToHexString(this.crc32,8) + ") ";
+ this.zipfile.status.push(msg);
+ } else {
+ rc = true; // OK
+ if (this.zipfile.verbose>2) {
+ this.zipfile.status.push("INFO: CRC check ok: 0x" +
+ JSIO.decimalToHexString(this.crc32,8));
+ }
+ }
+ return rc;
+ };
+
+
+ // ctor
+ ZipFile = function(fileUrl, callback, verbosity) {
+ if (! (this instanceof arguments.callee) ) JSIO.throwError('You must use new to instantiate this class', typename, 'ctor');
+
+ this.verbose = verbosity || 0;
+ this.entries = [];
+ this.entryNames = [];
+ this.status = [];
+ this._version = version;
+ this._typename = "ZipFile";
+ this._throwError = JSIO.throwError;
+
+ var thisZipFile = this;
+
+ // Could use a back-tracking reader for the central directory, but
+ // there's no point, since all the zip data is held in memory anyway.
+
+ /* function ReadCentralDirectory(){
+ var posn = thisZipFile.binaryStream.length - 64;
+ var maxSeekback = Math.Max(s.Length - 0x4000, 10);
+ var success = false;
+ var nTries = 0;
+ do
+ {
+ thisZipFile.binaryStream.Seek(posn, SeekOrigin.Begin);
+ var bytesRead = thisZipFile.binaryStream.findSignature(thisZipFile.Signatures.EndOfCentralDirectory);
+ if (bytesRead != -1)
+ success = true;
+ else
+ {
+ nTries++;
+ // increasingly larger
+ posn -= (32 * (nTries + 1) * nTries);
+ if (posn < 0) posn = 0; // BOF
+ }
+ }
+ while (!success && posn > maxSeekback);
+ if (!success) {
+ thisZipFile.status.push("cannot find End of Central Directory");
+ return;
+ }
+ } */
+
+
+ function DateFromPackedFormat(packed) {
+ if (packed == 0xFFFF || packed === 0) return new Date(1995, 0, 1, 0,0,0,0);
+
+ var packedTime = packed & 0x0000ffff;
+ var packedDate = ((packed & 0xffff0000) >> 16);
+
+ var year = 1980 + ((packedDate & 0xFE00) >> 9);
+ var month = ((packedDate & 0x01E0) >> 5) -1;
+ var day = packedDate & 0x001F;
+
+ var hour = (packedTime & 0xF800) >> 11;
+ var minute = (packedTime & 0x07E0) >> 5;
+ var second = (packedTime & 0x001F) * 2;
+
+ // Validation and error checking.
+ // This is not foolproof but will catch most errors.
+
+ // I can't believe how many different ways applications
+ // can mess up a simple date format.
+
+ if (second >= 60) { minute++; second = 0; }
+ if (minute >= 60) { hour++; minute = 0; }
+ if (hour >= 24) { day++; hour = 0; }
+ var success = false;
+ var d;
+ try {
+ d = new Date(year, month, day, hour, minute, second, 0);
+ success= true;
+ }
+ catch (exc1) {
+ if (year == 1980 && (month === 0 || day === 0)) {
+ try {
+ d = new Date(1980, 0, 1, hour, minute, second, 0);
+ success= true;
+ }
+ catch (exc2) {
+ try {
+ d = new Date(1980, 0, 1, 0, 0, 0, 0);
+ success= true;
+ }
+ catch (exc3) { } // how could this fail??
+ }
+ }
+ else {
+ try {
+ if (year < 1980) year = 1980;
+ if (year > 2030) year = 2030;
+ if (month < 1) month = 1;
+ if (month > 12) month = 12;
+ if (day < 1) day = 1;
+ if (day > 31) day = 31;
+ if (minute < 0) minute = 0;
+ if (minute > 59) minute = 59;
+ if (second < 0) second = 0;
+ if (second > 59) second = 59;
+ d = new Date(year, month-1, day, hour, minute, second, 0);
+ success= true;
+ }
+ catch (exc4){}
+ }
+ }
+ if (!success) this._throwError('Bad date/time value in this ZIP file', null, 'DateFromPackedFormat');
+ return d;
+ }
+
+
+ function ReadZipEntries () {
+ // read only once
+ if (thisZipFile.entryNames.length === 0){
+ var e;
+ while ((e = ReadZipEntry()) !== null) {
+ thisZipFile.entries.push(e);
+ thisZipFile.entryNames.push(e.name);
+ }
+ }
+ }
+
+
+ function ReadZipEntry () {
+ var offset = thisZipFile.binaryStream.position;
+ var sig = thisZipFile.binaryStream.readNumber(4);
+ if (sig == ZipFile.Signatures.DirEntry) {
+ // after all entries, comes the central directory
+ if (thisZipFile.verbose > 2) {
+ thisZipFile.status.push("INFO: at offset 0x" +
+ JSIO.decimalToHexString(offset) +
+ ", found start of Zip Directory.");
+ }
+ // all done reading
+ return null;
+ }
+ if (sig != ZipFile.Signatures.Entry) {
+ thisZipFile.status.push("WARNING: at offset 0x" +
+ JSIO.decimalToHexString(offset) +
+ ", found unexpected signature: 0x" +
+ JSIO.decimalToHexString(sig));
+ return null;
+ }
+
+ var entry = new ZipEntry(thisZipFile);
+ entry.offset = offset;
+ entry.versionNeeded = thisZipFile.binaryStream.readNumber(2);
+ entry.bitField = thisZipFile.binaryStream.readNumber(2);
+ entry.compressionMethod = thisZipFile.binaryStream.readNumber(2);
+ var timeBlob = thisZipFile.binaryStream.readNumber(4);
+ entry.lastModified = DateFromPackedFormat(timeBlob);
+ entry.crc32 = thisZipFile.binaryStream.readNumber(4);
+ entry.compressedSize = thisZipFile.binaryStream.readNumber(4);
+ entry.uncompressedSize = thisZipFile.binaryStream.readNumber(4);
+
+ if (thisZipFile.verbose>2) {
+ thisZipFile.status.push("INFO: crc32 0x" +
+ JSIO.decimalToHexString(entry.crc32) +
+ " (" + entry.crc32 + ")");
+ thisZipFile.status.push("INFO: compressedSize 0x" +
+ JSIO.decimalToHexString(entry.compressedSize) +
+ " (" + entry.compressedSize + ") bytes");
+ thisZipFile.status.push("INFO: uncompressedSize 0x" +
+ JSIO.decimalToHexString(entry.uncompressedSize) +
+ " (" + entry.uncompressedSize + ") bytes");
+ }
+ if ((entry.bitField & 0x01) == 0x01){
+ thisZipFile.status.push("This zipfile uses Encryption, which is not supported by ZipFile.js.");
+ return null;
+ }
+
+ entry.utf8 = ((entry.bitField & 0x0800) == 0x0800);
+
+ if (entry.compressedSize == 0xFFFFFFFF ||
+ entry.uncompressedSize == 0xFFFFFFFF) {
+ thisZipFile.status.push("This zipfile uses ZIP64, which is not supported by ZipFile.js");
+ return null;
+ }
+
+ var filenameLength = thisZipFile.binaryStream.readNumber(2);
+ var extraFieldLength = thisZipFile.binaryStream.readNumber(2);
+
+ if (thisZipFile.verbose>2) {
+ thisZipFile.status.push("INFO: filename length= " + filenameLength);
+ }
+
+ // we've read 30 bytes of metadata so far
+ var bytesRead = 30 + filenameLength + extraFieldLength;
+
+ if (entry.utf8) {
+ thisZipFile.status.push("INFO: before filename, position= 0x" +
+ JSIO.decimalToHexString( thisZipFile.binaryStream.position ));
+ var binReader =
+ new JSIO.StreamSegmentReader(thisZipFile.binaryStream,
+ thisZipFile.binaryStream.position,
+ filenameLength);
+ var utf8Decoder = new JSIO.TextDecoder.UTF8(binReader);
+ var textReader = new JSIO.TextReader(utf8Decoder);
+ entry.name = textReader.readToEnd();
+
+ // advance the filepointer:
+ thisZipFile.binaryStream.seek(filenameLength,
+ JSIO.SeekOrigin.Current,
+ thisZipFile);
+
+ thisZipFile.status.push("INFO: after filename, position= 0x" +
+ JSIO.decimalToHexString( thisZipFile.binaryStream.position ));
+ }
+ else {
+ entry.name = thisZipFile.binaryStream.readString(filenameLength);
+ }
+
+ // There are a bunch of things in the "extra" header, thisZipFile we
+ // could parse, like timestamps and other things. This class
+ // only identifies and separates them.
+
+ // More info here: http://www.pkware.com/documents/casestudies/APPNOTE.TXT
+
+ var extraPos = 0;
+ entry.extra = [];
+ while (extraPos < extraFieldLength) {
+ var extraBlock = {
+ type: thisZipFile.binaryStream.readNumber(2),
+ size: thisZipFile.binaryStream.readNumber(2)
+ };
+ extraBlock.typeDescription = ZipFile.ExtraFieldTypes[extraBlock.type];
+ extraBlock.data = thisZipFile.binaryStream.read(extraBlock.size);
+ entry.extra.push(extraBlock);
+ extraPos += 4 + extraBlock.size;
+ }
+
+ if (thisZipFile.verbose > 1) {
+ thisZipFile.status.push("INFO: at offset 0x" +
+ JSIO.decimalToHexString(entry.offset) +
+ ", found entry '" + entry.name + "' fnl(" +
+ filenameLength + ") efl(" +
+ extraFieldLength +")");
+ }
+
+ if (extraFieldLength > 0) {
+ if (thisZipFile.verbose > 0) {
+ thisZipFile.status.push("INFO: entry " + entry.name + " has " +
+ extraFieldLength + " bytes of " +
+ "extra metadata (ID'd but ignored)");
+ }
+ }
+
+ entry.lengthOfHeader = bytesRead;
+ if (thisZipFile.verbose>2) {
+ thisZipFile.status.push("INFO: lengthOfHeader 0x" +
+ JSIO.decimalToHexString(entry.lengthOfHeader) +
+ " (" + entry.lengthOfHeader + ")");
+ }
+
+ /*
+ Data descriptor Offset Bytes Description[25]
+ 0 0/4 Optional data descriptor signature = 0x08074b50
+ 0/4 4 CRC-32
+ 4/8 4 Compressed size
+ 8/12 4 Uncompressed size
+ */
+
+ if ((entry.bitField & 0x0008) == 0x0008){
+ thisZipFile.status.push("INFO: This zipfile uses a bit 3 trailing data descriptor");
+ // return null;
+ var currentPosition = thisZipFile.binaryStream.position;
+ var dataDescLength = 0;
+ if (thisZipFile.verbose>2) {
+ thisZipFile.status.push("INFO: currentPosition 0x" +
+ JSIO.decimalToHexString(thisZipFile.binaryStream.position) +
+ " (" + thisZipFile.binaryStream.position + ")");
+ thisZipFile.status.push("INFO: length 0x" +
+ JSIO.decimalToHexString(thisZipFile.binaryStream.length) +
+ " (" + thisZipFile.binaryStream.length + ")");
+ }
+
+ for (var i=0; i<128; i++) { // maximum search backwards from end for signature is 128 bytes
+ thisZipFile.binaryStream.seek(thisZipFile.binaryStream.length-i-4,JSIO.SeekOrigin.Begin);
+ var num = thisZipFile.binaryStream.readNumber(4)
+ if (thisZipFile.verbose>2) {
+ thisZipFile.status.push("INFO: ["+i+"] 0x" +
+ JSIO.decimalToHexString(num) +
+ " (" + num + ")");
+ }
+ if (num == ZipFile.Signatures.DataDescriptor) {
+ sig = num;
+ entry.crc32 = thisZipFile.binaryStream.readNumber(4);
+ entry.compressedSize = thisZipFile.binaryStream.readNumber(4);
+ entry.uncompressedSize = thisZipFile.binaryStream.readNumber(4);
+ dataDescLength = 16;
+ break;
+ }
+ }
+
+ if (thisZipFile.verbose>2) {
+ thisZipFile.status.push("INFO: sig 0x" +
+ JSIO.decimalToHexString(sig) +
+ " (" + sig + ")");
+ thisZipFile.status.push("INFO: crc32 0x" +
+ JSIO.decimalToHexString(entry.crc32) +
+ " (" + entry.crc32 + ")");
+ thisZipFile.status.push("INFO: compressedSize 0x" +
+ JSIO.decimalToHexString(entry.compressedSize) +
+ " (" + entry.compressedSize + ") bytes");
+ thisZipFile.status.push("INFO: uncompressedSize 0x" +
+ JSIO.decimalToHexString(entry.uncompressedSize) +
+ " (" + entry.uncompressedSize + ") bytes");
+ thisZipFile.status.push("INFO: lengthOfHeader 0x" +
+ JSIO.decimalToHexString(entry.lengthOfHeader) +
+ " (" + entry.lengthOfHeader + ")");
+ }
+
+ thisZipFile.binaryStream.position = currentPosition+dataDescLength;
+ }
+
+ entry.totalEntrySize = entry.lengthOfHeader + entry.compressedSize;
+
+ // seek past the data without reading it. We will read on Extract()
+ if (thisZipFile.verbose > 1) {
+ thisZipFile.status.push("INFO: seek 0x" +
+ JSIO.decimalToHexString(entry.compressedSize) +
+ " (" + entry.compressedSize + ") bytes");
+ }
+
+ thisZipFile.binaryStream.seek(entry.compressedSize,
+ JSIO.SeekOrigin.Current,
+ thisZipFile);
+
+ return entry;
+ }
+
+
+ var parseZipFile = function(bfr){
+ try {
+ if (bfr.req.status == 200) {
+ var sig = thisZipFile.binaryStream.readNumber(4);
+ if (sig != ZipFile.Signatures.Entry){
+ thisZipFile.status.push("WARNING: this file does not appear to be a zip file");
+ } else {
+ thisZipFile.binaryStream.seek(0, JSIO.SeekOrigin.Begin);
+ ReadZipEntries();
+ if (thisZipFile.verbose > 0) {
+ thisZipFile.status.push("INFO: read " + thisZipFile.entries.length + " entries");
+ }
+ }
+ }
+ else {
+ thisZipFile.status.push("ERROR: the URL could not be read (" +
+ bfr.req.status + " " + bfr.req.statusText + ")");
+ }
+ callback(thisZipFile);
+ }
+ catch (exc1)
+ {
+ thisZipFile.status.push("EXCEPTION: " + exc1.message);
+ callback(thisZipFile);
+ }
+ };
+
+ this.binaryStream = new JSIO.BinaryUrlStream(fileUrl, parseZipFile);
+
+ return this;
+ };
+
+
+ ZipFile.Signatures = {
+ Entry : 0x04034b50,
+ EndOfCentralDirectory : 0x06054b50,
+ DirEntry : 0x02014b50,
+ DataDescriptor : 0x08074b50
+ };
+
+ ZipFile.Version = version;
+
+ ZipFile.EncryptionAlgorithm = {
+ None : 0,
+ PkzipWeak : 1,
+ WinZipAes : 2
+ };
+
+ ZipFile.ExtraFieldTypes = {};
+ ZipFile.ExtraFieldTypes[0x0001] = 'Zip64 Extended Info';
+ ZipFile.ExtraFieldTypes[0x0007] = 'AV Info';
+ ZipFile.ExtraFieldTypes[0x0008] = 'Extended Language Encoding Data (PFS)';
+ ZipFile.ExtraFieldTypes[0x0009] = 'OS/2';
+ ZipFile.ExtraFieldTypes[0x000a] = 'NTFS ';
+ ZipFile.ExtraFieldTypes[0x000c] = 'OpenVMS';
+ ZipFile.ExtraFieldTypes[0x000d] = 'UNIX';
+ ZipFile.ExtraFieldTypes[0x000e] = 'File Stream and Fork Descriptors';
+ ZipFile.ExtraFieldTypes[0x000f] = 'Patch Descriptor';
+ ZipFile.ExtraFieldTypes[0x0014] = 'PKCS#7 Store for X.509 Certificates';
+ ZipFile.ExtraFieldTypes[0x0015] = 'X.509 Certificate ID and Signature (Individual File)';
+ ZipFile.ExtraFieldTypes[0x0016] = 'X.509 Certificate ID (Central Directory)';
+ ZipFile.ExtraFieldTypes[0x0017] = 'Strong Encryption Header';
+ ZipFile.ExtraFieldTypes[0x0018] = 'Record Management Controls';
+ ZipFile.ExtraFieldTypes[0x0019] = 'PKCS#7 Encryption Recipient Certificate List';
+ ZipFile.ExtraFieldTypes[0x0065] = 'IBM S/390 (Z390), AS/400 (I400) attributes (uncompressed)';
+ ZipFile.ExtraFieldTypes[0x0066] = 'IBM S/390 (Z390), AS/400 (I400) attributes (compressed)';
+ ZipFile.ExtraFieldTypes[0x4690] = 'POSZIP 4690 (reserved) ';
+ ZipFile.ExtraFieldTypes[0x07c8] = 'Macintosh';
+ ZipFile.ExtraFieldTypes[0x2605] = 'ZipIt Macintosh';
+ ZipFile.ExtraFieldTypes[0x2705] = 'ZipIt Macintosh 1.3.5+';
+ ZipFile.ExtraFieldTypes[0x2805] = 'ZipIt Macintosh 1.3.5+';
+ ZipFile.ExtraFieldTypes[0x334d] = 'Info-ZIP Macintosh';
+ ZipFile.ExtraFieldTypes[0x4341] = 'Acorn/SparkFS ';
+ ZipFile.ExtraFieldTypes[0x4453] = 'Windows NT security descriptor (binary ACL)';
+ ZipFile.ExtraFieldTypes[0x4704] = 'VM/CMS';
+ ZipFile.ExtraFieldTypes[0x470f] = 'MVS';
+ ZipFile.ExtraFieldTypes[0x4b46] = 'FWKCS MD5';
+ ZipFile.ExtraFieldTypes[0x4c41] = 'OS/2 access control list (text ACL)';
+ ZipFile.ExtraFieldTypes[0x4d49] = 'Info-ZIP OpenVMS';
+ ZipFile.ExtraFieldTypes[0x4f4c] = 'Xceed original location extra field';
+ ZipFile.ExtraFieldTypes[0x5356] = 'AOS/VS (ACL)';
+ ZipFile.ExtraFieldTypes[0x5455] = 'extended timestamp';
+ ZipFile.ExtraFieldTypes[0x554e] = 'Xceed unicode extra field';
+ ZipFile.ExtraFieldTypes[0x5855] = 'Info-ZIP UNIX (original, also OS/2, NT, etc)';
+ ZipFile.ExtraFieldTypes[0x6375] = 'Info-ZIP Unicode Comment Extra Field';
+ ZipFile.ExtraFieldTypes[0x6542] = 'BeOS/BeBox';
+ ZipFile.ExtraFieldTypes[0x7075] = 'Info-ZIP Unicode Path Extra Field';
+ ZipFile.ExtraFieldTypes[0x756e] = 'ASi UNIX';
+ ZipFile.ExtraFieldTypes[0x7855] = 'Info-ZIP UNIX (new)';
+ ZipFile.ExtraFieldTypes[0xa220] = 'Microsoft Open Packaging Growth Hint';
+ ZipFile.ExtraFieldTypes[0xfd4a] = 'SMS/QDOS';
})();
diff --git a/www/wiki/extensions/Maps/resources/GoogleMaps/geoxml3/geoxml3.js b/www/wiki/extensions/Maps/resources/GoogleMaps/geoxml3/geoxml3.js
index 6cc72d79..83d5929c 100644
--- a/www/wiki/extensions/Maps/resources/GoogleMaps/geoxml3/geoxml3.js
+++ b/www/wiki/extensions/Maps/resources/GoogleMaps/geoxml3/geoxml3.js
@@ -24,17 +24,67 @@
* limitations under the License.
*
*/
-
-if (!String.prototype.trim) {
/**
- * Remove leading and trailing whitespace.
+ * A MultiGeometry object that will allow multiple polylines in a MultiGeometry
+ * containing LineStrings to be treated as a single object
*
- * @augments String
- * @return {String}
+ * @param {MutiGeometryOptions} anonymous object. Available properties:
+ * map: The map on which to attach the MultiGeometry
+ * paths: the individual polylines
+ * polylineOptions: options to use when constructing all the polylines
+ *
+ * @constructor
*/
- String.prototype.trim = function () {
- return this.replace(/^\s+|\s+$/g, '');
- };
+// only if Google Maps API included
+if (!!window.google && !! google.maps) {
+ function MultiGeometry(multiGeometryOptions) {
+ function createPolyline(polylineOptions, mg) {
+ var polyline = new google.maps.Polyline(polylineOptions);
+ google.maps.event.addListener(polyline,'click', function(evt) { google.maps.event.trigger(mg,'click',evt);});
+ google.maps.event.addListener(polyline,'dblclick', function(evt) { google.maps.event.trigger(mg, 'dblclick', evt);});
+ google.maps.event.addListener(polyline,'mousedown', function(evt) { google.maps.event.trigger(mg, 'mousedown', evt);});
+ google.maps.event.addListener(polyline,'mousemove', function(evt) { google.maps.event.trigger(mg, 'mousemove', evt);});
+ google.maps.event.addListener(polyline,'mouseout', function(evt) { google.maps.event.trigger(mg, 'mouseout', evt);});
+ google.maps.event.addListener(polyline,'mouseover', function(evt) { google.maps.event.trigger(mg, 'mouseover', evt);});
+ google.maps.event.addListener(polyline,'mouseup', function(evt) { google.maps.event.trigger(mg, 'mouseup', evt);});
+ google.maps.event.addListener(polyline,'rightclick', function(evt) { google.maps.event.trigger(mg, 'rightclick', evt);});
+ return polyline;
+ }
+ this.setValues(multiGeometryOptions);
+ this.polylines = [];
+
+ for (i=0; i<this.paths.length;i++) {
+ var polylineOptions = multiGeometryOptions;
+ polylineOptions.path = this.paths[i];
+ var polyline = createPolyline(polylineOptions,this);
+ // Bind the polyline properties to the MultiGeometry properties
+ this.polylines.push(polyline);
+ }
+ }
+ MultiGeometry.prototype = new google.maps.MVCObject();
+ MultiGeometry.prototype.changed = function(key) {
+ // alert(key+" changed");
+ if (this.polylines) {
+ for (var i=0; i<this.polylines.length; i++) {
+ this.polylines[i].set(key,this.get(key));
+ }
+ }
+ };
+ MultiGeometry.prototype.setMap = function(map) { this.set('map',map); };
+ MultiGeometry.prototype.getMap = function() { return this.get('map'); };
+}
+
+// Extend the global String object with a method to remove leading and trailing whitespace
+if (!String.prototype.trim) {
+ /**
+ * Remove leading and trailing whitespace.
+ *
+ * @augments String
+ * @return {String}
+ */
+ String.prototype.trim = function () {
+ return this.replace(/^\s+|\s+$/g, '');
+ };
}
/**
@@ -53,977 +103,1049 @@ geoXML3 = window.geoXML3 || {instances: []};
* @param {geoXML3.parserOptions} options
*/
geoXML3.parser = function (options) {
- // Private variables
- var parserOptions = new geoXML3.parserOptions(options);
- var docs = []; // Individual KML documents
- var docsByUrl = {}; // Same docs as an hash by cleanURL
- var kmzMetaData = {}; // Extra files from KMZ data
- var styles = {}; // Global list of styles
- var lastPlacemark;
- var parserName;
- if (!parserOptions.infoWindow && parserOptions.singleInfoWindow)
- parserOptions.infoWindow = new google.maps.InfoWindow();
-
- var parseKmlString = function (kmlString, docSet) {
- // Internal values for the set of documents as a whole
- var internals = {
- parser: this,
- docSet: docSet || [],
- remaining: 1,
- parseOnly: !(parserOptions.afterParse || parserOptions.processStyles)
- };
- thisDoc = new Object();
- thisDoc.internals = internals;
- internals.docSet.push(thisDoc);
- render(geoXML3.xmlParse(kmlString),thisDoc);
- }
-
- var parse = function (urls, docSet) {
- // Process one or more KML documents
- if (!parserName) {
- parserName = 'geoXML3.instances[' + (geoXML3.instances.push(this) - 1) + ']';
- }
-
- if (typeof urls === 'string') {
- // Single KML document
- urls = [urls];
- }
-
- // Internal values for the set of documents as a whole
- var internals = {
- parser: this,
- docSet: docSet || [],
- remaining: urls.length,
- parseOnly: !(parserOptions.afterParse || parserOptions.processStyles)
- };
- var thisDoc, j;
- for (var i = 0; i < urls.length; i++) {
- var baseUrl = cleanURL(defileURL(location.pathname), urls[i]);
- if (docsByUrl[baseUrl]) {
- // Reloading an existing document
- thisDoc = docsByUrl[baseUrl];
- thisDoc.reload = true;
- }
- else {
- thisDoc = new Object();
- thisDoc.baseUrl = baseUrl;
- internals.docSet.push(thisDoc);
- }
- thisDoc.url = urls[i];
- thisDoc.internals = internals;
- fetchDoc(thisDoc.url, thisDoc);
- }
- };
-
- function fetchDoc(url, doc, resFunc) {
- resFunc = resFunc || function (responseXML) { render(responseXML, doc); };
-
- if (typeof ZipFile === 'function' && typeof JSIO === 'object' && typeof JSIO.guessFileType === 'function') { // KMZ support requires these modules loaded
- contentType = JSIO.guessFileType(doc.baseUrl);
- if (contentType == JSIO.FileType.Binary || contentType == JSIO.FileType.Unknown) {
- doc.isCompressed = true;
- doc.baseDir = doc.baseUrl + '/';
- geoXML3.fetchZIP(url, resFunc, doc.internals.parser);
- return;
- }
- }
- doc.isCompressed = false;
- doc.baseDir = defileURL(doc.baseUrl);
- geoXML3.fetchXML(url, resFunc);
- }
-
- var hideDocument = function (doc) {
- if (!doc) doc = docs[0];
- // Hide the map objects associated with a document
- var i;
- if (!!doc.markers) {
- for (i = 0; i < doc.markers.length; i++) {
- if(!!doc.markers[i].infoWindow) doc.markers[i].infoWindow.close();
- doc.markers[i].setVisible(false);
- }
- }
- if (!!doc.ggroundoverlays) {
- for (i = 0; i < doc.ggroundoverlays.length; i++) {
- doc.ggroundoverlays[i].setOpacity(0);
- }
- }
- if (!!doc.gpolylines) {
- for (i=0;i<doc.gpolylines.length;i++) {
- if(!!doc.gpolylines[i].infoWindow) doc.gpolylines[i].infoWindow.close();
- doc.gpolylines[i].setMap(null);
- }
- }
- if (!!doc.gpolygons) {
- for (i=0;i<doc.gpolygons.length;i++) {
- if(!!doc.gpolygons[i].infoWindow) doc.gpolygons[i].infoWindow.close();
- doc.gpolygons[i].setMap(null);
- }
- }
- };
-
- var showDocument = function (doc) {
- if (!doc) doc = docs[0];
- // Show the map objects associated with a document
- var i;
- if (!!doc.markers) {
- for (i = 0; i < doc.markers.length; i++) {
- doc.markers[i].setVisible(true);
- }
- }
- if (!!doc.ggroundoverlays) {
- for (i = 0; i < doc.ggroundoverlays.length; i++) {
- doc.ggroundoverlays[i].setOpacity(doc.ggroundoverlays[i].percentOpacity_);
- }
- }
- if (!!doc.gpolylines) {
- for (i=0;i<doc.gpolylines.length;i++) {
- doc.gpolylines[i].setMap(parserOptions.map);
- }
- }
- if (!!doc.gpolygons) {
- for (i=0;i<doc.gpolygons.length;i++) {
- doc.gpolygons[i].setMap(parserOptions.map);
- }
- }
- };
-
- var defaultStyle = {
- balloon: {
- bgColor: 'ffffffff',
- textColor: 'ff000000',
- text: "<h3>$[name]</h3>\n<div>$[description]</div>\n<div>$[geDirections]</div>",
- displayMode: 'default'
- },
- icon: {
- scale: 1.0,
- dim: {
- x: 0,
- y: 0,
- w: -1,
- h: -1
- },
- hotSpot: {
- x: 0.5,
- y: 0.5,
- xunits: 'fraction',
- yunits: 'fraction'
- }
- },
- line: {
- color: 'ffffffff', // white (KML default)
- colorMode: 'normal',
- width: 1.0
- },
- poly: {
- color: 'ffffffff', // white (KML default)
- colorMode: 'normal',
- fill: true,
- outline: true
- }
- };
-
- var kmlNS = 'http://www.opengis.net/kml/2.2';
- var gxNS = 'http://www.google.com/kml/ext/2.2';
- var nodeValue = geoXML3.nodeValue;
- var getBooleanValue = geoXML3.getBooleanValue;
- var getElementsByTagNameNS = geoXML3.getElementsByTagNameNS;
- var getElementsByTagName = geoXML3.getElementsByTagName;
-
-function processStyleUrl(node) {
- var styleUrlStr = nodeValue(getElementsByTagName(node, 'styleUrl')[0]);
- if (!!styleUrlStr && styleUrlStr.indexOf('#') != -1)
- var styleUrl = styleUrlStr.split('#');
- else var styleUrl = ["",""];
- return styleUrl;
-}
-
- function processStyle(thisNode, baseUrl, styleID, baseDir) {
- var style = (baseUrl === '{inline}') ? clone(defaultStyle) : (styles[baseUrl][styleID] = styles[baseUrl][styleID] || clone(defaultStyle));
-
- var styleNodes = getElementsByTagName(thisNode, 'BalloonStyle');
- if (!!styleNodes && styleNodes.length > 0) {
- style.balloon.bgColor = nodeValue(getElementsByTagName(styleNodes[0], 'bgColor')[0], style.balloon.bgColor);
- style.balloon.textColor = nodeValue(getElementsByTagName(styleNodes[0], 'textColor')[0], style.balloon.textColor);
- style.balloon.text = nodeValue(getElementsByTagName(styleNodes[0], 'text')[0], style.balloon.text);
- style.balloon.displayMode = nodeValue(getElementsByTagName(styleNodes[0], 'displayMode')[0], style.balloon.displayMode);
- }
-
- // style.list = (unsupported; doesn't make sense in Google Maps)
-
- var styleNodes = getElementsByTagName(thisNode, 'IconStyle');
- if (!!styleNodes && styleNodes.length > 0) {
- var icon = style.icon;
-
- icon.scale = parseFloat(nodeValue(getElementsByTagName(styleNodes[0], 'scale')[0], icon.scale));
- // style.icon.heading = (unsupported; not supported in API)
- // style.icon.color = (unsupported; not supported in API)
- // style.icon.colorMode = (unsupported; not supported in API)
-
- styleNodes = getElementsByTagName(thisNode, 'Icon');
- if (!!styleNodes && styleNodes.length > 0) {
- icon.href = nodeValue(getElementsByTagName(styleNodes[0], 'href')[0]);
- icon.url = cleanURL(baseDir, icon.href);
- // Detect images buried in KMZ files (and use a base64 encoded URL)
- if (kmzMetaData[icon.url]) icon.url = kmzMetaData[icon.url].dataUrl;
-
- // Support for icon palettes and exact size dimensions
- icon.dim = {
- x: parseInt(nodeValue(getElementsByTagNameNS(styleNodes[0], gxNS, 'x')[0], icon.dim.x)),
- y: parseInt(nodeValue(getElementsByTagNameNS(styleNodes[0], gxNS, 'y')[0], icon.dim.y)),
- w: parseInt(nodeValue(getElementsByTagNameNS(styleNodes[0], gxNS, 'w')[0], icon.dim.w)),
- h: parseInt(nodeValue(getElementsByTagNameNS(styleNodes[0], gxNS, 'h')[0], icon.dim.h))
- };
-
- styleNodes = getElementsByTagName(styleNodes[0], 'hotSpot')[0];
- if (!!styleNodes && styleNodes.length > 0) {
- icon.hotSpot = {
- x: styleNodes[0].getAttribute('x'),
- y: styleNodes[0].getAttribute('y'),
- xunits: styleNodes[0].getAttribute('xunits'),
- yunits: styleNodes[0].getAttribute('yunits')
- };
- }
-
- // certain occasions where we need the pixel size of the image (like the default settings...)
- // (NOTE: Scale is applied to entire image, not just the section of the icon palette. So,
- // if we need scaling, we'll need the img dimensions no matter what.)
- if ( (icon.dim.w < 0 || icon.dim.h < 0) && (icon.xunits != 'pixels' || icon.yunits == 'fraction') || icon.scale != 1.0) {
- // (hopefully, this will load by the time we need it...)
- icon.img = new Image();
- icon.img.onload = function() {
- if (icon.dim.w < 0 || icon.dim.h < 0) {
- icon.dim.w = this.width;
- icon.dim.h = this.height;
- }
- };
- icon.img.src = icon.url;
-
- // sometimes the file is already cached and it never calls onLoad
- if (icon.img.width > 0) {
- icon.dim.w = icon.img.width;
- icon.dim.h = icon.img.height;
- }
- }
- }
- }
-
- // style.label = (unsupported; may be possible but not with API)
-
- styleNodes = getElementsByTagName(thisNode, 'LineStyle');
- if (!!styleNodes && styleNodes.length > 0) {
- style.line.color = nodeValue(getElementsByTagName(styleNodes[0], 'color')[0], style.line.color);
- style.line.colorMode = nodeValue(getElementsByTagName(styleNodes[0], 'colorMode')[0], style.line.colorMode);
- style.line.width = nodeValue(getElementsByTagName(styleNodes[0], 'width')[0], style.line.width);
- // style.line.outerColor = (unsupported; not supported in API)
- // style.line.outerWidth = (unsupported; not supported in API)
- // style.line.physicalWidth = (unsupported; unnecessary in Google Maps)
- // style.line.labelVisibility = (unsupported; possible to implement)
- }
-
- styleNodes = getElementsByTagName(thisNode, 'PolyStyle');
- if (!!styleNodes && styleNodes.length > 0) {
- style.poly.color = nodeValue( getElementsByTagName(styleNodes[0], 'color')[0], style.poly.color);
- style.poly.colorMode = nodeValue( getElementsByTagName(styleNodes[0], 'colorMode')[0], style.poly.colorMode);
- style.poly.outline = getBooleanValue(getElementsByTagName(styleNodes[0], 'outline')[0], style.poly.outline);
- style.poly.fill = getBooleanValue(getElementsByTagName(styleNodes[0], 'fill')[0], style.poly.fill);
- }
- return style;
- }
-
- // from http://stackoverflow.com/questions/122102/what-is-the-most-efficient-way-to-clone-a-javascript-object
- // http://keithdevens.com/weblog/archive/2007/Jun/07/javascript.clone
- function clone(obj){
- if(obj == null || typeof(obj) != 'object') return obj;
- if (obj.cloneNode) return obj.cloneNode(true);
- var temp = new obj.constructor();
- for(var key in obj) temp[key] = clone(obj[key]);
- return temp;
- }
-
- function processStyleMap(thisNode, baseUrl, styleID, baseDir) {
- var pairs = getElementsByTagName(thisNode, 'Pair');
- var map = new Object();
-
- // add each key to the map
- for (var pr=0;pr<pairs.length;pr++) {
- var pairKey = nodeValue(getElementsByTagName(pairs[pr], 'key')[0]);
- var pairStyle = nodeValue(getElementsByTagName(pairs[pr], 'Style')[0]);
- var pairStyleUrl = processStyleUrl(pairs[pr]);
- var pairStyleBaseUrl = pairStyleUrl[0] ? cleanURL(baseDir, pairStyleUrl[0]) : baseUrl;
- var pairStyleID = pairStyleUrl[1];
-
- if (!!pairStyle) {
- map[pairKey] = processStyle(pairStyle, pairStyleBaseUrl, pairStyleID);
- } else if (!!pairStyleID && !!styles[pairStyleBaseUrl][pairStyleID]) {
- map[pairKey] = clone(styles[pairStyleBaseUrl][pairStyleID]);
- }
- }
- if (!!map["normal"]) {
- styles[baseUrl][styleID] = clone(map["normal"]);
- } else {
- styles[baseUrl][styleID] = clone(defaultStyle);
- }
- if (!!map["highlight"]) {
- processStyleID(map["highlight"]);
- }
- styles[baseUrl][styleID].map = clone(map);
- }
-
- function processPlacemarkCoords(node, tag) {
- var parent = getElementsByTagName(node, tag);
- var coordListA = [];
- for (var i=0; i<parent.length; i++) {
- var coordNodes = getElementsByTagName(parent[i], 'coordinates');
- if (!coordNodes) {
- if (coordListA.length > 0) {
- break;
- } else {
- return [{coordinates: []}];
- }
- }
-
- for (var j=0; j<coordNodes.length;j++) {
- var coords = nodeValue(coordNodes[j]).trim();
- coords = coords.replace(/,\s+/g, ',');
- var path = coords.split(/\s+/g);
- var pathLength = path.length;
- var coordList = [];
- for (var k = 0; k < pathLength; k++) {
- coords = path[k].split(',');
- if (!isNaN(coords[0]) && !isNaN(coords[1])) {
- coordList.push({
- lat: parseFloat(coords[1]),
- lng: parseFloat(coords[0]),
- alt: parseFloat(coords[2])
- });
- }
- }
- coordListA.push({coordinates: coordList});
- }
- }
- return coordListA;
- }
-
- var render = function (responseXML, doc) {
- // Callback for retrieving a KML document: parse the KML and display it on the map
- if (!responseXML) {
- // Error retrieving the data
- geoXML3.log('Unable to retrieve ' + doc.url);
- if (parserOptions.failedParse) parserOptions.failedParse(doc);
- doc.failed = true;
- return;
- } else if (responseXML.parseError && responseXML.parseError.errorCode != 0) {
- // IE parse error
- var err = responseXML.parseError;
- var msg = 'Parse error in line ' + err.line + ', col ' + err.linePos + ' (error code: ' + err.errorCode + ")\n" +
- "\nError Reason: " + err.reason +
- 'Error Line: ' + err.srcText;
-
- geoXML3.log('Unable to retrieve ' + doc.url + ': ' + msg);
- if (parserOptions.failedParse) parserOptions.failedParse(doc);
- doc.failed = true;
- return;
- } else if (responseXML.documentElement && responseXML.documentElement.nodeName == 'parsererror') {
- // Firefox parse error
- geoXML3.log('Unable to retrieve ' + doc.url + ': ' + responseXML.documentElement.childNodes[0].nodeValue);
- if (parserOptions.failedParse) parserOptions.failedParse(doc);
- doc.failed = true;
- return;
- } else if (!doc) {
- throw 'geoXML3 internal error: render called with null document';
- } else { //no errors
- var i;
- doc.placemarks = [];
- doc.groundoverlays = [];
- doc.ggroundoverlays = [];
- doc.networkLinks = [];
- doc.gpolygons = [];
- doc.gpolylines = [];
-
- // Check for dependent KML files
- var nodes = getElementsByTagName(responseXML, 'styleUrl');
- var docSet = doc.internals.docSet;
-
- for (var i = 0; i < nodes.length; i++) {
- var url = nodeValue(nodes[i]).split('#')[0];
- if (!url) continue; // #id (inside doc)
- var rUrl = cleanURL( doc.baseDir, url );
- if (rUrl === doc.baseUrl) continue; // self
- if (docsByUrl[rUrl]) continue; // already loaded
-
- var thisDoc;
- var j = docSet.indexOfObjWithItem('baseUrl', rUrl);
- if (j != -1) {
- // Already listed to be loaded, but probably in the wrong order.
- // Load it right away to immediately resolve dependency.
- thisDoc = docSet[j];
- if (thisDoc.failed) continue; // failed to load last time; don't retry it again
- }
- else {
- // Not listed at all; add it in
- thisDoc = new Object();
- thisDoc.url = rUrl; // url can't be trusted inside KMZ files, since it may .. outside of the archive
- thisDoc.baseUrl = rUrl;
- thisDoc.internals = doc.internals;
-
- doc.internals.docSet.push(thisDoc);
- doc.internals.remaining++;
- }
-
- // render dependent KML first then re-run renderer
- fetchDoc(rUrl, thisDoc, function (thisResXML) {
- render(thisResXML, thisDoc);
- render(responseXML, doc);
- });
-
- // to prevent cross-dependency issues, just load the one
- // file first and re-check the rest later
- return;
- }
-
- // Parse styles
- doc.styles = styles[doc.baseUrl] = styles[doc.baseUrl] || {};
- var styleID, styleNodes;
- nodes = getElementsByTagName(responseXML, 'Style');
- nodeCount = nodes.length;
- for (i = 0; i < nodeCount; i++) {
- thisNode = nodes[i];
- var styleID = thisNode.getAttribute('id');
- if (!!styleID) processStyle(thisNode, doc.baseUrl, styleID, doc.baseDir);
- }
- // Parse StyleMap nodes
- nodes = getElementsByTagName(responseXML, 'StyleMap');
- for (i = 0; i < nodes.length; i++) {
- thisNode = nodes[i];
- var styleID = thisNode.getAttribute('id');
- if (!!styleID) processStyleMap(thisNode, doc.baseUrl, styleID, doc.baseDir);
- }
-
- if (!!parserOptions.processStyles || !parserOptions.createMarker) {
- // Convert parsed styles into GMaps equivalents
- processStyles(doc);
- }
-
- // Parse placemarks
- if (!!doc.reload && !!doc.markers) {
- for (i = 0; i < doc.markers.length; i++) {
- doc.markers[i].active = false;
- }
- }
- var placemark, node, coords, path, marker, poly;
- var placemark, coords, path, pathLength, marker, polygonNodes, coordList;
- var placemarkNodes = getElementsByTagName(responseXML, 'Placemark');
- for (pm = 0; pm < placemarkNodes.length; pm++) {
- // Init the placemark object
- node = placemarkNodes[pm];
- var styleUrl = processStyleUrl(node);
- placemark = {
- name: nodeValue(getElementsByTagName(node, 'name')[0]),
- description: nodeValue(getElementsByTagName(node, 'description')[0]),
- styleUrl: styleUrl.join('#'),
- styleBaseUrl: styleUrl[0] ? cleanURL(doc.baseDir, styleUrl[0]) : doc.baseUrl,
- styleID: styleUrl[1],
- visibility: getBooleanValue(getElementsByTagName(node, 'visibility')[0], true),
- balloonVisibility: getBooleanValue(getElementsByTagNameNS(node, gxNS, 'balloonVisibility')[0], !parserOptions.suppressInfoWindows)
- };
- placemark.style = (styles[placemark.styleBaseUrl] && styles[placemark.styleBaseUrl][placemark.styleID]) || clone(defaultStyle);
- // inline style overrides shared style
- var inlineStyles = getElementsByTagName(node, 'Style');
- if (inlineStyles && (inlineStyles.length > 0)) {
- var style = processStyle(node, '{inline}', '{inline}');
- processStyleID(style);
- if (style) placemark.style = style;
- }
-
- if (/^https?:\/\//.test(placemark.description)) {
- placemark.description = ['<a href="', placemark.description, '">', placemark.description, '</a>'].join('');
- }
-
- // record list of variables for substitution
- placemark.vars = {
- display: {
- name: 'Name',
- description: 'Description',
- address: 'Street Address',
- id: 'ID',
- Snippet: 'Snippet',
- geDirections: 'Directions'
- },
- val: {
- name: placemark.name || '',
- description: placemark.description || '',
- address: nodeValue(getElementsByTagName(node, 'address')[0], ''),
- id: node.getAttribute('id') || '',
- Snippet: nodeValue(getElementsByTagName(node, 'Snippet')[0], '')
- },
- directions: [
- 'f=d',
- 'source=GeoXML3'
- ]
- };
-
- // add extended data to variables
- var extDataNodes = getElementsByTagName(node, 'ExtendedData');
- if (!!extDataNodes && extDataNodes.length > 0) {
- var dataNodes = getElementsByTagName(extDataNodes[0], 'Data');
- for (var d = 0; d < dataNodes.length; d++) {
- var dn = dataNodes[d];
- var name = dn.getAttribute('name');
- if (!name) continue;
- var dName = nodeValue(getElementsByTagName(dn, 'displayName')[0], name);
- var val = nodeValue(getElementsByTagName(dn, 'value')[0]);
-
- placemark.vars.val[name] = val;
- placemark.vars.display[name] = dName;
- }
- }
-
- // process MultiGeometry
- var GeometryNodes = getElementsByTagName(node, 'coordinates');
- var Geometry = null;
- if (!!GeometryNodes && (GeometryNodes.length > 0)) {
- for (var gn=0;gn<GeometryNodes.length;gn++) {
- if (GeometryNodes[gn].parentNode &&
- GeometryNodes[gn].parentNode.nodeName) {
- var GeometryPN = GeometryNodes[gn].parentNode;
- Geometry = GeometryPN.nodeName;
-
- // Extract the coordinates
- // What sort of placemark?
- switch(Geometry) {
- case "Point":
- placemark.Point = processPlacemarkCoords(node, "Point")[0];
- placemark.latlng = new google.maps.LatLng(placemark.Point.coordinates[0].lat, placemark.Point.coordinates[0].lng);
- pathLength = 1;
- break;
- case "LinearRing":
- // Polygon/line
- polygonNodes = getElementsByTagName(node, 'Polygon');
- // Polygon
- if (!placemark.Polygon)
- placemark.Polygon = [{
- outerBoundaryIs: {coordinates: []},
- innerBoundaryIs: [{coordinates: []}]
- }];
- for (var pg=0;pg<polygonNodes.length;pg++) {
- placemark.Polygon[pg] = {
- outerBoundaryIs: {coordinates: []},
- innerBoundaryIs: [{coordinates: []}]
- }
- placemark.Polygon[pg].outerBoundaryIs = processPlacemarkCoords(polygonNodes[pg], "outerBoundaryIs");
- placemark.Polygon[pg].innerBoundaryIs = processPlacemarkCoords(polygonNodes[pg], "innerBoundaryIs");
- }
- coordList = placemark.Polygon[0].outerBoundaryIs;
- break;
-
- case "LineString":
- pathLength = 0;
- placemark.LineString = processPlacemarkCoords(node,"LineString");
- break;
-
- default:
- break;
- }
- }
- }
- }
-
- // call the custom placemark parse function if it is defined
- if (!!parserOptions.pmParseFn) parserOptions.pmParseFn(node, placemark);
- doc.placemarks.push(placemark);
-
- // single marker
- if (placemark.Point) {
- if (!!google.maps) {
- doc.bounds = doc.bounds || new google.maps.LatLngBounds();
- doc.bounds.extend(placemark.latlng);
- }
-
- // Potential user-defined marker handler
- var pointCreateFunc = parserOptions.createMarker || createMarker;
- var found = false;
- if (!parserOptions.createMarker) {
- // Check to see if this marker was created on a previous load of this document
- if (!!doc) {
- doc.markers = doc.markers || [];
- if (doc.reload) {
- for (var j = 0; j < doc.markers.length; j++) {
- if (doc.markers[j].getPosition().equals(placemark.latlng)) {
- found = doc.markers[j].active = true;
- break;
- }
- }
- }
- }
- }
- if (!found) {
- // Call the marker creator
- var marker = pointCreateFunc(placemark, doc);
- if (marker) marker.active = placemark.visibility;
- }
- }
- // polygon/line
- var poly, line;
- if (!!doc) {
- if (placemark.Polygon) doc.gpolygons = doc.gpolygons || [];
- if (placemark.LineString) doc.gpolylines = doc.gpolylines || [];
- }
-
- var polyCreateFunc = parserOptions.createPolygon || createPolygon;
- var lineCreateFunc = parserOptions.createLineString || createPolyline;
- if (placemark.Polygon) {
- poly = polyCreateFunc(placemark,doc);
- if (poly) poly.active = placemark.visibility;
- }
- if (placemark.LineString) {
- line = lineCreateFunc(placemark,doc);
- if (line) line.active = placemark.visibility;
- }
- if (!!google.maps) {
- doc.bounds = doc.bounds || new google.maps.LatLngBounds();
- if (poly) doc.bounds.union(poly.bounds);
- if (line) doc.bounds.union(line.bounds);
- }
-
- } // placemark loop
-
- if (!!doc.reload && !!doc.markers) {
- for (i = doc.markers.length - 1; i >= 0 ; i--) {
- if (!doc.markers[i].active) {
- if (!!doc.markers[i].infoWindow) {
- doc.markers[i].infoWindow.close();
- }
- doc.markers[i].setMap(null);
- doc.markers.splice(i, 1);
- }
- }
- }
-
- // Parse ground overlays
- if (!!doc.reload && !!doc.groundoverlays) {
- for (i = 0; i < doc.groundoverlays.length; i++) {
- doc.groundoverlays[i].active = false;
- }
- }
-
- if (!!doc) {
- doc.groundoverlays = doc.groundoverlays || [];
- }
- // doc.groundoverlays =[];
- var groundOverlay, color, transparency, overlay;
- var groundNodes = getElementsByTagName(responseXML, 'GroundOverlay');
- for (i = 0; i < groundNodes.length; i++) {
- node = groundNodes[i];
-
- // Detect images buried in KMZ files (and use a base64 encoded URL)
- var gnUrl = cleanURL( doc.baseDir, nodeValue(getElementsByTagName(node, 'href')[0]) );
- if (kmzMetaData[gnUrl]) gnUrl = kmzMetaData[gnUrl].dataUrl;
-
- // Init the ground overlay object
- groundOverlay = {
- name: nodeValue(getElementsByTagName(node, 'name')[0]),
- description: nodeValue(getElementsByTagName(node, 'description')[0]),
- icon: { href: gnUrl },
- latLonBox: {
- north: parseFloat(nodeValue(getElementsByTagName(node, 'north')[0])),
- east: parseFloat(nodeValue(getElementsByTagName(node, 'east')[0])),
- south: parseFloat(nodeValue(getElementsByTagName(node, 'south')[0])),
- west: parseFloat(nodeValue(getElementsByTagName(node, 'west')[0]))
- }
- };
- if (!!google.maps) {
- doc.bounds = doc.bounds || new google.maps.LatLngBounds();
- doc.bounds.union(new google.maps.LatLngBounds(
- new google.maps.LatLng(groundOverlay.latLonBox.south, groundOverlay.latLonBox.west),
- new google.maps.LatLng(groundOverlay.latLonBox.north, groundOverlay.latLonBox.east)
- ));
- }
-
- // Opacity is encoded in the color node
- var colorNode = getElementsByTagName(node, 'color');
- if (colorNode && colorNode.length > 0) {
- groundOverlay.opacity = geoXML3.getOpacity(nodeValue(colorNode[0]));
- } else {
- groundOverlay.opacity = 1.0; // KML default
- }
-
- doc.groundoverlays.push(groundOverlay);
- if (!!parserOptions.createOverlay) {
- // User-defined overlay handler
- parserOptions.createOverlay(groundOverlay, doc);
- } else {
- // Check to see if this overlay was created on a previous load of this document
- var found = false;
- if (!!doc) {
- doc.groundoverlays = doc.groundoverlays || [];
- if (doc.reload) {
- overlayBounds = new google.maps.LatLngBounds(
- new google.maps.LatLng(groundOverlay.latLonBox.south, groundOverlay.latLonBox.west),
- new google.maps.LatLng(groundOverlay.latLonBox.north, groundOverlay.latLonBox.east)
- );
- var overlays = doc.groundoverlays;
- for (i = overlays.length; i--;) {
- if ((overlays[i].bounds().equals(overlayBounds)) &&
- (overlays.url_ === groundOverlay.icon.href)) {
- found = overlays[i].active = true;
- break;
- }
- }
- }
- }
-
- if (!found) {
- // Call the built-in overlay creator
- overlay = createOverlay(groundOverlay, doc);
- overlay.active = true;
- }
- }
- if (!!doc.reload && !!doc.groundoverlays && !!doc.groundoverlays.length) {
- var overlays = doc.groundoverlays;
- for (i = overlays.length; i--;) {
- if (!overlays[i].active) {
- overlays[i].remove();
- overlays.splice(i, 1);
- }
- }
- doc.groundoverlays = overlays;
- }
- }
-
- // Parse network links
- var networkLink;
- var docPath = document.location.pathname.split('/');
- docPath = docPath.splice(0, docPath.length - 1).join('/');
- var linkNodes = getElementsByTagName(responseXML, 'NetworkLink');
- for (i = 0; i < linkNodes.length; i++) {
- node = linkNodes[i];
-
- // Init the network link object
- networkLink = {
- name: nodeValue(getElementsByTagName(node, 'name')[0]),
- link: {
- href: nodeValue(getElementsByTagName(node, 'href')[0]),
- refreshMode: nodeValue(getElementsByTagName(node, 'refreshMode')[0])
- }
- };
-
- // Establish the specific refresh mode
- if (!networkLink.link.refreshMode) {
- networkLink.link.refreshMode = 'onChange';
- }
- if (networkLink.link.refreshMode === 'onInterval') {
- networkLink.link.refreshInterval = parseFloat(nodeValue(getElementsByTagName(node, 'refreshInterval')[0]));
- if (isNaN(networkLink.link.refreshInterval)) {
- networkLink.link.refreshInterval = 0;
- }
- } else if (networkLink.link.refreshMode === 'onChange') {
- networkLink.link.viewRefreshMode = nodeValue(getElementsByTagName(node, 'viewRefreshMode')[0]);
- if (!networkLink.link.viewRefreshMode) {
- networkLink.link.viewRefreshMode = 'never';
- }
- if (networkLink.link.viewRefreshMode === 'onStop') {
- networkLink.link.viewRefreshTime = nodeValue(getElementsByTagName(node, 'refreshMode')[0]);
- networkLink.link.viewFormat = nodeValue(getElementsByTagName(node, 'refreshMode')[0]);
- if (!networkLink.link.viewFormat) {
- networkLink.link.viewFormat = 'BBOX=[bboxWest],[bboxSouth],[bboxEast],[bboxNorth]';
- }
- }
- }
-
- if (!/^[\/|http]/.test(networkLink.link.href)) {
- // Fully-qualify the HREF
- networkLink.link.href = docPath + '/' + networkLink.link.href;
- }
-
- // Apply the link
- if ((networkLink.link.refreshMode === 'onInterval') &&
- (networkLink.link.refreshInterval > 0)) {
- // Reload at regular intervals
- setInterval(parserName + '.parse("' + networkLink.link.href + '")',
- 1000 * networkLink.link.refreshInterval);
- } else if (networkLink.link.refreshMode === 'onChange') {
- if (networkLink.link.viewRefreshMode === 'never') {
- // Load the link just once
- doc.internals.parser.parse(networkLink.link.href, doc.internals.docSet);
- } else if (networkLink.link.viewRefreshMode === 'onStop') {
- // Reload when the map view changes
-
- }
- }
- }
- }
-
- if (!!doc.bounds) {
- doc.internals.bounds = doc.internals.bounds || new google.maps.LatLngBounds();
- doc.internals.bounds.union(doc.bounds);
- }
- if (!!doc.markers || !!doc.groundoverlays || !!doc.gpolylines || !!doc.gpolygons) {
- doc.internals.parseOnly = false;
- }
-
- if (!doc.internals.parseOnly) {
- // geoXML3 is not being used only as a real-time parser, so keep the processed documents around
- if (!docsByUrl[doc.baseUrl]) {
- docs.push(doc);
- docsByUrl[doc.baseUrl] = doc;
- }
- else {
- // internal replacement, which keeps the same memory ref loc in docs and docsByUrl
- for (var i in docsByUrl[doc.baseUrl]) {
- docsByUrl[doc.baseUrl][i] = doc[i];
- }
- }
- }
-
- doc.internals.remaining--;
- if (doc.internals.remaining === 0) {
- // We're done processing this set of KML documents
- // Options that get invoked after parsing completes
- if (parserOptions.zoom && !!doc.internals.bounds) {
- parserOptions.map.fitBounds(doc.internals.bounds);
- }
- if (parserOptions.afterParse) {
- parserOptions.afterParse(doc.internals.docSet);
- }
- }
- };
-
- var kmlColor = function (kmlIn, colorMode) {
- var kmlColor = {};
- kmlIn = kmlIn || 'ffffffff'; // white (KML 2.2 default)
-
- var aa = kmlIn.substr(0,2);
- var bb = kmlIn.substr(2,2);
- var gg = kmlIn.substr(4,2);
- var rr = kmlIn.substr(6,2);
-
- kmlColor.opacity = parseInt(aa, 16) / 256;
- kmlColor.color = (colorMode === 'random') ? randomColor(rr, gg, bb) : '#' + rr + gg + bb;
- return kmlColor;
- };
-
- // Implemented per KML 2.2 <ColorStyle> specs
- var randomColor = function(rr, gg, bb) {
- var col = { rr: rr, gg: gg, bb: bb };
- for (var k in col) {
- var v = col[k];
- if (v == null) v = 'ff';
-
- // RGB values are limiters for random numbers (ie: 7f would be a random value between 0 and 7f)
- v = Math.round(Math.random() * parseInt(rr, 16)).toString(16);
- if (v.length === 1) v = '0' + v;
- col[k] = v;
- }
-
- return '#' + col.rr + col.gg + col.bb;
- };
-
- var processStyleID = function (style) {
- var icon = style.icon;
- if (!icon.href) return;
-
- if (icon.img && !icon.img.complete) {
- // we're still waiting on the image loading (probably because we've been blocking since the declaration)
- // so, let's queue this function on the onload stack
- icon.markerBacklog = [];
- icon.img.onload = function() {
- if (icon.dim.w < 0 || icon.dim.h < 0) {
- icon.dim.w = this.width;
- icon.dim.h = this.height;
- }
- processStyleID(style);
-
- // we will undoubtedly get some createMarker queuing, so set this up in advance
- for (var i = 0; i < icon.markerBacklog.length; i++) {
- var p = icon.markerBacklog[i][0];
- var d = icon.markerBacklog[i][1];
- createMarker(p, d);
- if (p.marker) p.marker.active = true;
- }
- delete icon.markerBacklog;
- };
- return;
- }
- else if (icon.dim.w < 0 || icon.dim.h < 0) {
- if (icon.img && icon.img.complete) {
- // sometimes the file is already cached and it never calls onLoad
- icon.dim.w = icon.img.width;
- icon.dim.h = icon.img.height;
- }
- else {
- // settle for a default of 32x32
- icon.dim.whGuess = true;
- icon.dim.w = 32;
- icon.dim.h = 32;
- }
- }
-
- // pre-scaled variables
- var rnd = Math.round;
- var scaled = {
- x: icon.dim.x * icon.scale,
- y: icon.dim.y * icon.scale,
- w: icon.dim.w * icon.scale,
- h: icon.dim.h * icon.scale,
- aX: icon.hotSpot.x * icon.scale,
- aY: icon.hotSpot.y * icon.scale,
- iW: (icon.img ? icon.img.width : icon.dim.w) * icon.scale,
- iH: (icon.img ? icon.img.height : icon.dim.h) * icon.scale
- };
-
- // Figure out the anchor spot
- var aX, aY;
- switch (icon.hotSpot.xunits) {
- case 'fraction': aX = rnd(scaled.aX * icon.dim.w); break;
- case 'insetPixels': aX = rnd(icon.dim.w * icon.scale - scaled.aX); break;
- default: aX = rnd(scaled.aX); break; // already pixels
- }
- aY = rnd( ((icon.hotSpot.yunits === 'fraction') ? icon.dim.h : 1) * scaled.aY ); // insetPixels Y = pixels Y
- var iconAnchor = new google.maps.Point(aX, aY);
-
- // Sizes
- // (NOTE: Scale is applied to entire image, not just the section of the icon palette.)
- var iconSize = icon.dim.whGuess ? null : new google.maps.Size(rnd(scaled.w), rnd(scaled.h));
- var iconScale = icon.scale == 1.0 ? null :
- icon.dim.whGuess ? new google.maps.Size(rnd(scaled.w), rnd(scaled.h))
- : new google.maps.Size(rnd(scaled.iW), rnd(scaled.iH));
- var iconOrigin = new google.maps.Point(rnd(scaled.x), rnd(scaled.y));
-
- // Detect images buried in KMZ files (and use a base64 encoded URL)
- if (kmzMetaData[icon.url]) icon.url = kmzMetaData[icon.url].dataUrl;
-
- // Init the style object with the KML icon
- icon.marker = new google.maps.MarkerImage(
- icon.url, // url
- iconSize, // size
- iconOrigin, // origin
- iconAnchor, // anchor
- iconScale // scaledSize
- );
-
- // Look for a predictable shadow
- var stdRegEx = /\/(red|blue|green|yellow|lightblue|purple|pink|orange)(-dot)?\.png/;
- var shadowSize = new google.maps.Size(59, 32);
- var shadowPoint = new google.maps.Point(16, 32);
- if (stdRegEx.test(icon.href)) {
- // A standard GMap-style marker icon
- icon.shadow = new google.maps.MarkerImage(
- 'http://maps.google.com/mapfiles/ms/micons/msmarker.shadow.png', // url
- shadowSize, // size
- null, // origin
- shadowPoint, // anchor
- shadowSize // scaledSize
- );
- } else if (icon.href.indexOf('-pushpin.png') > -1) {
- // Pushpin marker icon
- icon.shadow = new google.maps.MarkerImage(
- 'http://maps.google.com/mapfiles/ms/micons/pushpin_shadow.png', // url
- shadowSize, // size
- null, // origin
- shadowPoint, // anchor
- shadowSize // scaledSize
- );
- } /* else {
+ // Inherit from Google MVC Object to include event handling
+ google.maps.MVCObject.call(this);
+
+ // Private variables
+ var parserOptions = new geoXML3.parserOptions(options);
+ var docs = []; // Individual KML documents
+ var docsByUrl = {}; // Same docs as an hash by cleanURL
+ var kmzMetaData = {}; // Extra files from KMZ data
+ var styles = {}; // Global list of styles
+ var lastPlacemark;
+ var parserName;
+ if (!parserOptions.infoWindow && parserOptions.singleInfoWindow)
+ parserOptions.infoWindow = new google.maps.InfoWindow();
+
+ var parseKmlString = function (kmlString, docSet) {
+ // Internal values for the set of documents as a whole
+ var internals = {
+ parser: this,
+ docSet: docSet || [],
+ remaining: 1,
+ parseOnly: !(parserOptions.afterParse || parserOptions.processStyles)
+ };
+ thisDoc = new Object();
+ thisDoc.internals = internals;
+ internals.docSet.push(thisDoc);
+ render(geoXML3.xmlParse(kmlString),thisDoc);
+ }
+
+ var parse = function (urls, docSet) {
+ // Process one or more KML documents
+ if (!parserName) {
+ parserName = 'geoXML3.instances[' + (geoXML3.instances.push(this) - 1) + ']';
+ }
+
+ if (typeof urls === 'string') {
+ // Single KML document
+ urls = [urls];
+ }
+
+ // Internal values for the set of documents as a whole
+ var internals = {
+ parser: this,
+ docSet: docSet || [],
+ remaining: urls.length,
+ parseOnly: !(parserOptions.afterParse || parserOptions.processStyles)
+ };
+ var thisDoc, j;
+ for (var i = 0; i < urls.length; i++) {
+ var baseUrl = cleanURL(defileURL(location.pathname), urls[i]);
+ if (docsByUrl[baseUrl]) {
+ // Reloading an existing document
+ thisDoc = docsByUrl[baseUrl];
+ thisDoc.reload = true;
+ }
+ else {
+ thisDoc = new Object();
+ thisDoc.baseUrl = baseUrl;
+ internals.docSet.push(thisDoc);
+ }
+ thisDoc.url = urls[i];
+ thisDoc.internals = internals;
+ fetchDoc(thisDoc.url, thisDoc);
+ }
+ };
+
+ function fetchDoc(url, doc, resFunc) {
+ resFunc = resFunc || function (responseXML) { render(responseXML, doc); };
+
+ if (typeof ZipFile === 'function' && typeof JSIO === 'object' && typeof JSIO.guessFileType === 'function') { // KMZ support requires these modules loaded
+ // if url is a data URI scheme, do not guess type based on extension.
+ if (/^data:[^,]*(kmz)/.test(doc.baseUrl)) {
+ contentType = JSIO.FileType.Binary;
+ } else if (/^data:[^,]*(kml|xml)/.test(doc.baseUrl)) {
+ contentType = JSIO.FileType.XML;
+ } else if (/^data:/.test(doc.baseUrl)) {
+ contentType = JSIO.FileType.Unknown;
+ } else if (parserOptions.forceZip) {
+ contentType = JSIO.FileType.Binary;
+ } else {
+ contentType = JSIO.guessFileType(doc.baseUrl);
+ }
+ if (contentType == JSIO.FileType.Binary || contentType == JSIO.FileType.Unknown) {
+ doc.isCompressed = true;
+ doc.baseDir = doc.baseUrl + '/';
+ geoXML3.fetchZIP(url, resFunc, doc.internals.parser);
+ return;
+ }
+ }
+ doc.isCompressed = false;
+ doc.baseDir = defileURL(doc.baseUrl);
+ geoXML3.fetchXML(url, resFunc);
+ }
+
+ var hideDocument = function (doc) {
+ if (!doc) doc = docs[0];
+ // Hide the map objects associated with a document
+ var i;
+ if (!!doc.markers) {
+ for (i = 0; i < doc.markers.length; i++) {
+ if(!!doc.markers[i].infoWindow) doc.markers[i].infoWindow.close();
+ doc.markers[i].setVisible(false);
+ }
+ }
+ if (!!doc.ggroundoverlays) {
+ for (i = 0; i < doc.ggroundoverlays.length; i++) {
+ doc.ggroundoverlays[i].setOpacity(0);
+ }
+ }
+ if (!!doc.gpolylines) {
+ for (i=0;i<doc.gpolylines.length;i++) {
+ if(!!doc.gpolylines[i].infoWindow) doc.gpolylines[i].infoWindow.close();
+ doc.gpolylines[i].setMap(null);
+ }
+ }
+ if (!!doc.gpolygons) {
+ for (i=0;i<doc.gpolygons.length;i++) {
+ if(!!doc.gpolygons[i].infoWindow) doc.gpolygons[i].infoWindow.close();
+ doc.gpolygons[i].setMap(null);
+ }
+ }
+ };
+
+ var showDocument = function (doc) {
+ if (!doc) doc = docs[0];
+ // Show the map objects associated with a document
+ var i;
+ if (!!doc.markers) {
+ for (i = 0; i < doc.markers.length; i++) {
+ doc.markers[i].setVisible(true);
+ }
+ }
+ if (!!doc.ggroundoverlays) {
+ for (i = 0; i < doc.ggroundoverlays.length; i++) {
+ doc.ggroundoverlays[i].setOpacity(doc.ggroundoverlays[i].percentOpacity_);
+ }
+ }
+ if (!!doc.gpolylines) {
+ for (i=0;i<doc.gpolylines.length;i++) {
+ doc.gpolylines[i].setMap(parserOptions.map);
+ }
+ }
+ if (!!doc.gpolygons) {
+ for (i=0;i<doc.gpolygons.length;i++) {
+ doc.gpolygons[i].setMap(parserOptions.map);
+ }
+ }
+ };
+
+ var defaultStyle = {
+ balloon: {
+ bgColor: 'ffffffff',
+ textColor: 'ff000000',
+ text: "<h3>$[name]</h3>\n<div>$[description]</div>\n<div>$[geDirections]</div>",
+ displayMode: 'default'
+ },
+ icon: {
+ scale: 1.0,
+ dim: {
+ x: 0,
+ y: 0,
+ w: -1,
+ h: -1
+ },
+ hotSpot: {
+ x: 0.5,
+ y: 0.5,
+ xunits: 'fraction',
+ yunits: 'fraction'
+ }
+ },
+ line: {
+ color: 'ffffffff', // white (KML default)
+ colorMode: 'normal',
+ width: 1.0
+ },
+ poly: {
+ color: 'ffffffff', // white (KML default)
+ colorMode: 'normal',
+ fill: true,
+ outline: true
+ }
+ };
+
+ var kmlNS = 'http://www.opengis.net/kml/2.2';
+ var gxNS = 'http://www.google.com/kml/ext/2.2';
+ var nodeValue = geoXML3.nodeValue;
+ var getBooleanValue = geoXML3.getBooleanValue;
+ var getElementsByTagNameNS = geoXML3.getElementsByTagNameNS;
+ var getElementsByTagName = geoXML3.getElementsByTagName;
+
+ function processStyleUrl(node) {
+ var styleUrlStr = nodeValue(getElementsByTagName(node, 'styleUrl')[0]);
+ if (!!styleUrlStr && styleUrlStr.indexOf('#') != -1)
+ var styleUrl = styleUrlStr.split('#');
+ else var styleUrl = ["",""];
+ return styleUrl;
+ }
+
+ function processStyle(thisNode, baseUrl, styleID, baseDir) {
+ var style = (baseUrl === '{inline}') ? clone(defaultStyle) : (styles[baseUrl][styleID] = styles[baseUrl][styleID] || clone(defaultStyle));
+
+ var styleNodes = getElementsByTagName(thisNode, 'BalloonStyle');
+ if (!!styleNodes && styleNodes.length > 0) {
+ style.balloon.bgColor = nodeValue(getElementsByTagName(styleNodes[0], 'bgColor')[0], style.balloon.bgColor);
+ style.balloon.textColor = nodeValue(getElementsByTagName(styleNodes[0], 'textColor')[0], style.balloon.textColor);
+ style.balloon.text = nodeValue(getElementsByTagName(styleNodes[0], 'text')[0], style.balloon.text);
+ style.balloon.displayMode = nodeValue(getElementsByTagName(styleNodes[0], 'displayMode')[0], style.balloon.displayMode);
+ }
+
+ // style.list = (unsupported; doesn't make sense in Google Maps)
+
+ var styleNodes = getElementsByTagName(thisNode, 'IconStyle');
+ if (!!styleNodes && styleNodes.length > 0) {
+ var icon = style.icon;
+
+ icon.scale = parseFloat(nodeValue(getElementsByTagName(styleNodes[0], 'scale')[0], icon.scale));
+ // style.icon.heading = (unsupported; not supported in API)
+ // style.icon.color = (unsupported; not supported in API)
+ // style.icon.colorMode = (unsupported; not supported in API)
+
+ styleNodes = getElementsByTagName(styleNodes[0], 'hotSpot');
+ if (!!styleNodes && styleNodes.length > 0) {
+ icon.hotSpot = {
+ x: styleNodes[0].getAttribute('x'),
+ y: styleNodes[0].getAttribute('y'),
+ xunits: styleNodes[0].getAttribute('xunits'),
+ yunits: styleNodes[0].getAttribute('yunits')
+ };
+ }
+
+ styleNodes = getElementsByTagName(thisNode, 'Icon');
+ if (!!styleNodes && styleNodes.length > 0) {
+ icon.href = nodeValue(getElementsByTagName(styleNodes[0], 'href')[0]);
+ icon.url = cleanURL(baseDir, icon.href);
+ // Detect images buried in KMZ files (and use a base64 encoded URL)
+ if (kmzMetaData[icon.url]) icon.url = kmzMetaData[icon.url].dataUrl;
+
+ // Support for icon palettes and exact size dimensions
+ icon.dim = {
+ x: parseInt(nodeValue(getElementsByTagNameNS(styleNodes[0], gxNS, 'x')[0], icon.dim.x)),
+ y: parseInt(nodeValue(getElementsByTagNameNS(styleNodes[0], gxNS, 'y')[0], icon.dim.y)),
+ w: parseInt(nodeValue(getElementsByTagNameNS(styleNodes[0], gxNS, 'w')[0], icon.dim.w)),
+ h: parseInt(nodeValue(getElementsByTagNameNS(styleNodes[0], gxNS, 'h')[0], icon.dim.h))
+ };
+
+ // certain occasions where we need the pixel size of the image (like the default settings...)
+ // (NOTE: Scale is applied to entire image, not just the section of the icon palette. So,
+ // if we need scaling, we'll need the img dimensions no matter what.)
+ if (true /* (icon.dim.w < 0 || icon.dim.h < 0) && (icon.xunits != 'pixels' || icon.yunits == 'fraction') || icon.scale != 1.0 */) {
+ // (hopefully, this will load by the time we need it...)
+ icon.img = new Image();
+ icon.img.onload = function() {
+ if (icon.dim.w < 0 || icon.dim.h < 0) {
+ icon.dim.w = this.width;
+ icon.dim.h = this.height;
+ } else {
+ icon.dim.th = this.height;
+ }
+ };
+ icon.img.src = icon.url;
+
+ // sometimes the file is already cached and it never calls onLoad
+ if (icon.img.width > 0) {
+ if (icon.dim.w < 0 || icon.dim.h < 0) {
+ icon.dim.w = icon.img.width;
+ icon.dim.h = icon.img.height;
+ } else {
+ icon.dim.th = icon.img.height;
+ }
+ }
+ }
+ }
+ }
+
+ // style.label = (unsupported; may be possible but not with API)
+
+ styleNodes = getElementsByTagName(thisNode, 'LineStyle');
+ if (!!styleNodes && styleNodes.length > 0) {
+ style.line.color = nodeValue(getElementsByTagName(styleNodes[0], 'color')[0], style.line.color);
+ style.line.colorMode = nodeValue(getElementsByTagName(styleNodes[0], 'colorMode')[0], style.line.colorMode);
+ style.line.width = nodeValue(getElementsByTagName(styleNodes[0], 'width')[0], style.line.width);
+ // style.line.outerColor = (unsupported; not supported in API)
+ // style.line.outerWidth = (unsupported; not supported in API)
+ // style.line.physicalWidth = (unsupported; unneccesary in Google Maps)
+ // style.line.labelVisibility = (unsupported; possible to implement)
+ }
+
+ styleNodes = getElementsByTagName(thisNode, 'PolyStyle');
+ if (!!styleNodes && styleNodes.length > 0) {
+ style.poly.color = nodeValue( getElementsByTagName(styleNodes[0], 'color')[0], style.poly.color);
+ style.poly.colorMode = nodeValue( getElementsByTagName(styleNodes[0], 'colorMode')[0], style.poly.colorMode);
+ style.poly.outline = getBooleanValue(getElementsByTagName(styleNodes[0], 'outline')[0], style.poly.outline);
+ style.poly.fill = getBooleanValue(getElementsByTagName(styleNodes[0], 'fill')[0], style.poly.fill);
+ }
+ return style;
+ }
+
+ // from http://stackoverflow.com/questions/122102/what-is-the-most-efficient-way-to-clone-a-javascript-object
+ // http://keithdevens.com/weblog/archive/2007/Jun/07/javascript.clone
+ function clone(obj){
+ if(obj == null || typeof(obj) != 'object') return obj;
+ if (obj.cloneNode) return obj.cloneNode(true);
+ var temp = new obj.constructor();
+ for(var key in obj) temp[key] = clone(obj[key]);
+ return temp;
+ }
+
+ function processStyleMap(thisNode, baseUrl, styleID, baseDir) {
+ var pairs = getElementsByTagName(thisNode, 'Pair');
+ var map = new Object();
+
+ // add each key to the map
+ for (var pr=0;pr<pairs.length;pr++) {
+ var pairKey = nodeValue(getElementsByTagName(pairs[pr], 'key')[0]);
+ var pairStyle = nodeValue(getElementsByTagName(pairs[pr], 'Style')[0]);
+ var pairStyleUrl = processStyleUrl(pairs[pr]);
+ var pairStyleBaseUrl = pairStyleUrl[0] ? cleanURL(baseDir, pairStyleUrl[0]) : baseUrl;
+ var pairStyleID = pairStyleUrl[1];
+
+ if (!!pairStyle) {
+ map[pairKey] = processStyle(pairStyle, pairStyleBaseUrl, pairStyleID);
+ } else if (!!pairStyleID && !!styles[pairStyleBaseUrl][pairStyleID]) {
+ map[pairKey] = clone(styles[pairStyleBaseUrl][pairStyleID]);
+ }
+ }
+ if (!!map["normal"]) {
+ styles[baseUrl][styleID] = clone(map["normal"]);
+ } else {
+ styles[baseUrl][styleID] = clone(defaultStyle);
+ }
+ if (!!map["highlight"] && !!parserOptions.processStyles) {
+ processStyleID(map["highlight"]);
+ }
+ styles[baseUrl][styleID].map = clone(map);
+ }
+
+ function processPlacemarkCoords(node, tag) {
+ var parent = getElementsByTagName(node, tag);
+ var coordListA = [];
+ for (var i=0; i<parent.length; i++) {
+ var coordNodes = getElementsByTagName(parent[i], 'coordinates');
+ if (!coordNodes) {
+ if (coordListA.length > 0) {
+ break;
+ } else {
+ return [{coordinates: []}];
+ }
+ }
+
+ for (var j=0; j<coordNodes.length;j++) {
+ var coords = nodeValue(coordNodes[j]).trim();
+ coords = coords.replace(/,\s+/g, ',');
+ var path = coords.split(/\s+/g);
+ var pathLength = path.length;
+ var coordList = [];
+ for (var k = 0; k < pathLength; k++) {
+ coords = path[k].split(',');
+ if (!isNaN(coords[0]) && !isNaN(coords[1])) {
+ coordList.push({
+ lat: parseFloat(coords[1]),
+ lng: parseFloat(coords[0]),
+ alt: parseFloat(coords[2])
+ });
+ }
+ }
+ coordListA.push({coordinates: coordList});
+ }
+ }
+ return coordListA;
+ }
+
+ var render = function (responseXML, doc) {
+ // Callback for retrieving a KML document: parse the KML and display it on the map
+ if (!responseXML || responseXML == "failed parse") {
+ // Error retrieving the data
+ geoXML3.log('Unable to retrieve ' + doc.url);
+ if (parserOptions.failedParse) parserOptions.failedParse(doc);
+ doc.failed = true;
+ return;
+ } else if (responseXML.parseError && responseXML.parseError.errorCode != 0) {
+ // IE parse error
+ var err = responseXML.parseError;
+ var msg = 'Parse error in line ' + err.line + ', col ' + err.linePos + ' (error code: ' + err.errorCode + ")\n" +
+ "\nError Reason: " + err.reason +
+ 'Error Line: ' + err.srcText;
+
+ geoXML3.log('Unable to retrieve ' + doc.url + ': ' + msg);
+ if (parserOptions.failedParse) parserOptions.failedParse(doc);
+ doc.failed = true;
+ return;
+ } else if (responseXML.documentElement && responseXML.documentElement.nodeName == 'parsererror') {
+ // Firefox parse error
+ geoXML3.log('Unable to retrieve ' + doc.url + ': ' + responseXML.documentElement.childNodes[0].nodeValue);
+ if (parserOptions.failedParse) parserOptions.failedParse(doc);
+ doc.failed = true;
+ return;
+ } else if (!doc) {
+ throw 'geoXML3 internal error: render called with null document';
+ } else { //no errors
+ var i;
+ doc.placemarks = [];
+ doc.groundoverlays = [];
+ doc.ggroundoverlays = [];
+ doc.networkLinks = [];
+ doc.gpolygons = [];
+ doc.gpolylines = [];
+
+ // Check for dependent KML files
+ var nodes = getElementsByTagName(responseXML, 'styleUrl');
+ var docSet = doc.internals.docSet;
+
+ for (var i = 0; i < nodes.length; i++) {
+ var url = nodeValue(nodes[i]).split('#')[0];
+ if (!url) continue; // #id (inside doc)
+ var rUrl = cleanURL( doc.baseDir, url );
+ if (rUrl === doc.baseUrl) continue; // self
+ if (docsByUrl[rUrl]) continue; // already loaded
+
+ var thisDoc;
+ var j = docSet.indexOfObjWithItem('baseUrl', rUrl);
+ if (j != -1) {
+ // Already listed to be loaded, but probably in the wrong order.
+ // Load it right away to immediately resolve dependency.
+ thisDoc = docSet[j];
+ if (thisDoc.failed) continue; // failed to load last time; don't retry it again
+ }
+ else {
+ // Not listed at all; add it in
+ thisDoc = new Object();
+ thisDoc.url = rUrl; // url can't be trusted inside KMZ files, since it may .. outside of the archive
+ thisDoc.baseUrl = rUrl;
+ thisDoc.internals = doc.internals;
+
+ doc.internals.docSet.push(thisDoc);
+ doc.internals.remaining++;
+ }
+
+ // render dependent KML first then re-run renderer
+ fetchDoc(rUrl, thisDoc, function (thisResXML) {
+ render(thisResXML, thisDoc);
+ render(responseXML, doc);
+ });
+
+ // to prevent cross-dependency issues, just load the one
+ // file first and re-check the rest later
+ return;
+ }
+
+ // Parse styles
+ doc.styles = styles[doc.baseUrl] = styles[doc.baseUrl] || {};
+ var styleID, styleNodes;
+ nodes = getElementsByTagName(responseXML, 'Style');
+ nodeCount = nodes.length;
+ for (i = 0; i < nodeCount; i++) {
+ thisNode = nodes[i];
+ var styleID = thisNode.getAttribute('id');
+ if (!!styleID) processStyle(thisNode, doc.baseUrl, styleID, doc.baseDir);
+ }
+ // Parse StyleMap nodes
+ nodes = getElementsByTagName(responseXML, 'StyleMap');
+ for (i = 0; i < nodes.length; i++) {
+ thisNode = nodes[i];
+ var styleID = thisNode.getAttribute('id');
+ if (!!styleID) processStyleMap(thisNode, doc.baseUrl, styleID, doc.baseDir);
+ }
+
+ if (!!parserOptions.processStyles || !parserOptions.createMarker) {
+ // Convert parsed styles into GMaps equivalents
+ processStyles(doc);
+ }
+
+ // Parse placemarks
+ if (!!doc.reload && !!doc.markers) {
+ for (i = 0; i < doc.markers.length; i++) {
+ doc.markers[i].active = false;
+ }
+ }
+ var placemark, node, coords, path, marker, poly;
+ var pathLength, marker, polygonNodes, coordList;
+ var placemarkNodes = getElementsByTagName(responseXML, 'Placemark');
+ for (pm = 0; pm < placemarkNodes.length; pm++) {
+ // Init the placemark object
+ node = placemarkNodes[pm];
+ var styleUrl = processStyleUrl(node);
+ placemark = {
+ name: nodeValue(getElementsByTagName(node, 'name')[0]),
+ description: nodeValue(getElementsByTagName(node, 'description')[0]),
+ styleUrl: styleUrl.join('#'),
+ styleBaseUrl: styleUrl[0] ? cleanURL(doc.baseDir, styleUrl[0]) : doc.baseUrl,
+ styleID: styleUrl[1],
+ visibility: getBooleanValue(getElementsByTagName(node, 'visibility')[0], true),
+ balloonVisibility: getBooleanValue(getElementsByTagNameNS(node, gxNS, 'balloonVisibility')[0], !parserOptions.suppressInfoWindows),
+ id: node.getAttribute('id')
+ };
+ placemark.style = (styles[placemark.styleBaseUrl] && styles[placemark.styleBaseUrl][placemark.styleID]) || clone(defaultStyle);
+ // inline style overrides shared style
+ var inlineStyles = getElementsByTagName(node, 'Style');
+ if (inlineStyles && (inlineStyles.length > 0)) {
+ var style = processStyle(node, '{inline}', '{inline}');
+ processStyleID(style);
+ if (style) placemark.style = style;
+ }
+
+ if (/^https?:\/\//.test(placemark.description)) {
+ placemark.description = ['<a href="', placemark.description, '">', placemark.description, '</a>'].join('');
+ }
+
+ // record list of variables for substitution
+ placemark.vars = {
+ display: {
+ name: 'Name',
+ description: 'Description',
+ address: 'Street Address',
+ id: 'ID',
+ Snippet: 'Snippet',
+ geDirections: 'Directions'
+ },
+ val: {
+ name: placemark.name || '',
+ description: placemark.description || '',
+ address: nodeValue(getElementsByTagName(node, 'address')[0], ''),
+ id: node.getAttribute('id') || '',
+ Snippet: nodeValue(getElementsByTagName(node, 'Snippet')[0], '')
+ },
+ directions: [
+ 'f=d',
+ 'source=GeoXML3'
+ ]
+ };
+
+ // add extended data to variables
+ var extDataNodes = getElementsByTagName(node, 'ExtendedData');
+ if (!!extDataNodes && extDataNodes.length > 0) {
+ var dataNodes = getElementsByTagName(extDataNodes[0], 'Data');
+ for (var d = 0; d < dataNodes.length; d++) {
+ var dn = dataNodes[d];
+ var name = dn.getAttribute('name');
+ if (!name) continue;
+ var dName = nodeValue(getElementsByTagName(dn, 'displayName')[0], name);
+ var val = nodeValue(getElementsByTagName(dn, 'value')[0]);
+
+ placemark.vars.val[name] = val;
+ placemark.vars.display[name] = dName;
+ }
+ }
+
+ // process MultiGeometry
+ var GeometryNodes = getElementsByTagName(node, 'coordinates');
+ var Geometry = null;
+ if (!!GeometryNodes && (GeometryNodes.length > 0)) {
+ for (var gn=0;gn<GeometryNodes.length;gn++) {
+ if (GeometryNodes[gn].parentNode &&
+ GeometryNodes[gn].parentNode.nodeName) {
+ var GeometryPN = GeometryNodes[gn].parentNode;
+ Geometry = GeometryPN.nodeName;
+
+ // Extract the coordinates
+ // What sort of placemark?
+ switch(Geometry) {
+ case "Point":
+ placemark.Point = processPlacemarkCoords(node, "Point")[0];
+ placemark.latlng = new google.maps.LatLng(placemark.Point.coordinates[0].lat, placemark.Point.coordinates[0].lng);
+ pathLength = 1;
+ break;
+ case "LinearRing":
+ // Polygon/line
+ polygonNodes = getElementsByTagName(node, 'Polygon');
+ // Polygon
+ if (!placemark.Polygon)
+ placemark.Polygon = [{
+ outerBoundaryIs: {coordinates: []},
+ innerBoundaryIs: [{coordinates: []}]
+ }];
+ for (var pg=0;pg<polygonNodes.length;pg++) {
+ placemark.Polygon[pg] = {
+ outerBoundaryIs: {coordinates: []},
+ innerBoundaryIs: [{coordinates: []}]
+ }
+ placemark.Polygon[pg].outerBoundaryIs = processPlacemarkCoords(polygonNodes[pg], "outerBoundaryIs");
+ placemark.Polygon[pg].innerBoundaryIs = processPlacemarkCoords(polygonNodes[pg], "innerBoundaryIs");
+ }
+ coordList = placemark.Polygon[0].outerBoundaryIs;
+ break;
+
+ case "LineString":
+ pathLength = 0;
+ placemark.LineString = processPlacemarkCoords(node,"LineString");
+ break;
+
+ default:
+ break;
+ }
+ }
+ }
+ }
+
+ // parse MultiTrack/Track
+ var TrackNodes = getElementsByTagNameNS(node,gxNS,"Track");
+ var coordListA = [];
+ if (TrackNodes.length > 0) {
+ for (var i=0; i<TrackNodes.length; i++) {
+ var coordNodes = getElementsByTagNameNS(TrackNodes[i],gxNS,"coord");
+ var coordList = [];
+ for (var j=0; j<coordNodes.length;j++) {
+ var coords = geoXML3.nodeValue(coordNodes[j]).trim();
+ coords = coords.split(/\s+/g);
+ if (!isNaN(coords[0]) && !isNaN(coords[1])) {
+ coordList.push({
+ lat: parseFloat(coords[1]),
+ lng: parseFloat(coords[0]),
+ alt: parseFloat(coords[2])
+ });
+ }
+ }
+ coordListA.push({coordinates:coordList});
+ }
+ placemark.Track = coordListA;
+ }
+
+ // call the custom placemark parse function if it is defined
+ if (!!parserOptions.pmParseFn) parserOptions.pmParseFn(node, placemark);
+ doc.placemarks.push(placemark);
+
+ // single marker
+ if (placemark.Point) {
+ if (!!google.maps) {
+ doc.bounds = doc.bounds || new google.maps.LatLngBounds();
+ doc.bounds.extend(placemark.latlng);
+ }
+
+ // Potential user-defined marker handler
+ var pointCreateFunc = parserOptions.createMarker || createMarker;
+ var found = false;
+ if (!parserOptions.createMarker) {
+ // Check to see if this marker was created on a previous load of this document
+ if (!!doc) {
+ doc.markers = doc.markers || [];
+ if (doc.reload) {
+ for (var j = 0; j < doc.markers.length; j++) {
+ if ((doc.markers[j].id == placemark.id) ||
+ // if no id, check position
+ (!doc.markers[j].id &&
+ (doc.markers[j].getPosition().equals(placemark.latlng)))) {
+ found = doc.markers[j].active = true;
+ break;
+ }
+ }
+ }
+ }
+ }
+ if (!found) {
+ // Call the marker creator
+ var marker = pointCreateFunc(placemark, doc);
+ if (marker) {
+ marker.active = placemark.visibility;
+ marker.id = placemark.id;
+ }
+ }
+ }
+ // polygon/line
+ var poly, line;
+ if (!!doc) {
+ if (placemark.Polygon) doc.gpolygons = doc.gpolygons || [];
+ if (placemark.LineString) doc.gpolylines = doc.gpolylines || [];
+ if (placemark.Track) doc.gpolylines = doc.gpolylines || [];
+ }
+
+ var polyCreateFunc = parserOptions.createPolygon || createPolygon;
+ var lineCreateFunc = parserOptions.createLineString || createPolyline;
+ if (placemark.Polygon) {
+ poly = polyCreateFunc(placemark,doc);
+ if (poly) poly.active = placemark.visibility;
+ }
+ if (placemark.LineString) {
+ line = lineCreateFunc(placemark,doc);
+ if (line) line.active = placemark.visibility;
+ }
+ if (placemark.Track) { // gx:Track polyline
+ line = lineCreateFunc(placemark,doc);
+ if (line) line.active = placemark.visibility;
+ }
+ if (!!google.maps) {
+ doc.bounds = doc.bounds || new google.maps.LatLngBounds();
+ if (poly) doc.bounds.union(poly.bounds);
+ if (line) doc.bounds.union(line.bounds);
+ }
+
+ } // placemark loop
+
+ if (!!doc.reload && !!doc.markers) {
+ for (i = doc.markers.length - 1; i >= 0 ; i--) {
+ if (!doc.markers[i].active) {
+ if (!!doc.markers[i].infoWindow) {
+ doc.markers[i].infoWindow.close();
+ }
+ doc.markers[i].setMap(null);
+ doc.markers.splice(i, 1);
+ }
+ }
+ }
+
+ var overlayCreateFunc = parserOptions.createOverlay || createOverlay;
+ // Parse ground overlays
+ if (!!doc.reload && !!doc.groundoverlays) {
+ for (i = 0; i < doc.groundoverlays.length; i++) {
+ doc.groundoverlays[i].active = false;
+ }
+ }
+
+ if (!!doc) {
+ doc.groundoverlays = doc.groundoverlays || [];
+ }
+ // doc.groundoverlays =[];
+ var groundOverlay, color, transparency, overlay;
+ var groundNodes = getElementsByTagName(responseXML, 'GroundOverlay');
+ for (i = 0; i < groundNodes.length; i++) {
+ node = groundNodes[i];
+
+ // Detect images buried in KMZ files (and use a base64 encoded URL)
+ var gnUrl = cleanURL( doc.baseDir, nodeValue(getElementsByTagName(node, 'href')[0]) );
+ if (kmzMetaData[gnUrl]) gnUrl = kmzMetaData[gnUrl].dataUrl;
+
+ // Init the ground overlay object
+ groundOverlay = {
+ name: nodeValue(getElementsByTagName(node, 'name')[0]),
+ description: nodeValue(getElementsByTagName(node, 'description')[0]),
+ icon: { href: gnUrl },
+ latLonBox: {
+ north: parseFloat(nodeValue(getElementsByTagName(node, 'north')[0])),
+ east: parseFloat(nodeValue(getElementsByTagName(node, 'east')[0])),
+ south: parseFloat(nodeValue(getElementsByTagName(node, 'south')[0])),
+ west: parseFloat(nodeValue(getElementsByTagName(node, 'west')[0]))
+ },
+ rotation: -1 * parseFloat(nodeValue(getElementsByTagName(node, 'rotation')[0]))
+ };
+ if (!!google.maps) {
+ doc.bounds = doc.bounds || new google.maps.LatLngBounds();
+ doc.bounds.union(new google.maps.LatLngBounds(
+ new google.maps.LatLng(groundOverlay.latLonBox.south, groundOverlay.latLonBox.west),
+ new google.maps.LatLng(groundOverlay.latLonBox.north, groundOverlay.latLonBox.east)
+ ));
+ }
+
+ // Opacity is encoded in the color node
+ var colorNode = getElementsByTagName(node, 'color');
+ if (colorNode && colorNode.length > 0) {
+ groundOverlay.opacity = geoXML3.getOpacity(nodeValue(colorNode[0]));
+ } else {
+ groundOverlay.opacity = 1.0; // KML default
+ }
+
+ doc.groundoverlays.push(groundOverlay);
+ // Check to see if this overlay was created on a previous load of this document
+ var found = false;
+ if (!!doc) {
+ doc.groundoverlays = doc.groundoverlays || [];
+ if (doc.reload) {
+ overlayBounds = new google.maps.LatLngBounds(
+ new google.maps.LatLng(groundOverlay.latLonBox.south, groundOverlay.latLonBox.west),
+ new google.maps.LatLng(groundOverlay.latLonBox.north, groundOverlay.latLonBox.east)
+ );
+ var overlays = doc.groundoverlays;
+ for (i = overlays.length; i--;) {
+ if ((overlays[i].bounds().equals(overlayBounds)) &&
+ (overlays.url_ === groundOverlay.icon.href)) {
+ found = overlays[i].active = true;
+ break;
+ }
+ }
+ }
+
+ if (!found) {
+ overlay = overlayCreateFunc(groundOverlay, doc);
+ overlay.active = true;
+ }
+ }
+ if (!!doc.reload && !!doc.groundoverlays && !!doc.groundoverlays.length) {
+ var overlays = doc.groundoverlays;
+ for (i = overlays.length; i--;) {
+ if (!overlays[i].active) {
+ overlays[i].remove();
+ overlays.splice(i, 1);
+ }
+ }
+ doc.groundoverlays = overlays;
+ }
+ }
+
+ // Parse network links
+ var networkLink;
+ var docPath = document.location.pathname.split('/');
+ docPath = docPath.splice(0, docPath.length - 1).join('/');
+ var linkNodes = getElementsByTagName(responseXML, 'NetworkLink');
+ for (i = 0; i < linkNodes.length; i++) {
+ node = linkNodes[i];
+
+ // Init the network link object
+ networkLink = {
+ name: nodeValue(getElementsByTagName(node, 'name')[0]),
+ link: {
+ href: nodeValue(getElementsByTagName(node, 'href')[0]),
+ refreshMode: nodeValue(getElementsByTagName(node, 'refreshMode')[0])
+ }
+ };
+
+ // Establish the specific refresh mode
+ if (!networkLink.link.refreshMode) {
+ networkLink.link.refreshMode = 'onChange';
+ }
+ if (networkLink.link.refreshMode === 'onInterval') {
+ networkLink.link.refreshInterval = parseFloat(nodeValue(getElementsByTagName(node, 'refreshInterval')[0]));
+ if (isNaN(networkLink.link.refreshInterval)) {
+ networkLink.link.refreshInterval = 0;
+ }
+ } else if (networkLink.link.refreshMode === 'onChange') {
+ networkLink.link.viewRefreshMode = nodeValue(getElementsByTagName(node, 'viewRefreshMode')[0]);
+ if (!networkLink.link.viewRefreshMode) {
+ networkLink.link.viewRefreshMode = 'never';
+ }
+ if (networkLink.link.viewRefreshMode === 'onStop') {
+ networkLink.link.viewRefreshTime = nodeValue(getElementsByTagName(node, 'refreshMode')[0]);
+ networkLink.link.viewFormat = nodeValue(getElementsByTagName(node, 'refreshMode')[0]);
+ if (!networkLink.link.viewFormat) {
+ networkLink.link.viewFormat = 'BBOX=[bboxWest],[bboxSouth],[bboxEast],[bboxNorth]';
+ }
+ }
+ }
+
+ if (!/^[\/|http]/.test(networkLink.link.href)) {
+ // Fully-qualify the HREF
+ networkLink.link.href = docPath + '/' + networkLink.link.href;
+ }
+
+ // Apply the link
+ if ((networkLink.link.refreshMode === 'onInterval') &&
+ (networkLink.link.refreshInterval > 0)) {
+ // Reload at regular intervals
+ setInterval(parserName + '.parse("' + networkLink.link.href + '")',
+ 1000 * networkLink.link.refreshInterval);
+ } else if (networkLink.link.refreshMode === 'onChange') {
+ if (networkLink.link.viewRefreshMode === 'never') {
+ // Load the link just once
+ doc.internals.parser.parse(networkLink.link.href, doc.internals.docSet);
+ } else if (networkLink.link.viewRefreshMode === 'onStop') {
+ // Reload when the map view changes
+
+ }
+ }
+ }
+ }
+
+ if (!!doc.bounds) {
+ doc.internals.bounds = doc.internals.bounds || new google.maps.LatLngBounds();
+ doc.internals.bounds.union(doc.bounds);
+ }
+ if (!!doc.markers || !!doc.groundoverlays || !!doc.gpolylines || !!doc.gpolygons) {
+ doc.internals.parseOnly = false;
+ }
+
+ if (!doc.internals.parseOnly) {
+ // geoXML3 is not being used only as a real-time parser, so keep the processed documents around
+ if (doc.baseUrl){ // handle case from parseKmlString (no doc.baseUrl)
+ if (!docsByUrl[doc.baseUrl]) {
+ docs.push(doc);
+ docsByUrl[doc.baseUrl] = doc;
+ } else {
+ // internal replacement, which keeps the same memory ref loc in docs and docsByUrl
+ for (var i in docsByUrl[doc.baseUrl]) {
+ docsByUrl[doc.baseUrl][i] = doc[i];
+ }
+ }
+ }
+ }
+
+ doc.internals.remaining--;
+ if (doc.internals.remaining === 0) {
+ // We're done processing this set of KML documents
+ // Options that get invoked after parsing completes
+ if (parserOptions.zoom && !!doc.internals.bounds &&
+ !doc.internals.bounds.isEmpty() && !!parserOptions.map) {
+ parserOptions.map.fitBounds(doc.internals.bounds);
+ }
+ if (parserOptions.afterParse) {
+ parserOptions.afterParse(doc.internals.docSet);
+ }
+ google.maps.event.trigger(doc.internals.parser, 'parsed');
+ }
+ };
+
+ var kmlColor = function (kmlIn, colorMode) {
+ var kmlColor = {};
+ kmlIn = kmlIn || 'ffffffff'; // white (KML 2.2 default)
+
+ var aa = kmlIn.substr(0,2);
+ var bb = kmlIn.substr(2,2);
+ var gg = kmlIn.substr(4,2);
+ var rr = kmlIn.substr(6,2);
+
+ kmlColor.opacity = parseInt(aa, 16) / 256;
+ kmlColor.color = (colorMode === 'random') ? randomColor(rr, gg, bb) : '#' + rr + gg + bb;
+ return kmlColor;
+ };
+
+ // Implemented per KML 2.2 <ColorStyle> specs
+ var randomColor = function(rr, gg, bb) {
+ var col = { rr: rr, gg: gg, bb: bb };
+ for (var k in col) {
+ var v = col[k];
+ if (v == null) v = 'ff';
+
+ // RGB values are limiters for random numbers (ie: 7f would be a random value between 0 and 7f)
+ v = Math.round(Math.random() * parseInt(rr, 16)).toString(16);
+ if (v.length === 1) v = '0' + v;
+ col[k] = v;
+ }
+
+ return '#' + col.rr + col.gg + col.bb;
+ };
+
+ var processStyleID = function (style) {
+ var icon = style.icon;
+ if (!icon || !icon.href) return;
+
+ if (icon.img && !icon.img.complete && (icon.dim.w < 0) && (icon.dim.h < 0) ) {
+ // we're still waiting on the image loading (probably because we've been blocking since the declaration)
+ // so, let's queue this function on the onload stack
+ icon.markerBacklog = [];
+ icon.img.onload = function() {
+ if (icon.dim.w < 0 || icon.dim.h < 0) {
+ icon.dim.w = this.width;
+ icon.dim.h = this.height;
+ } else {
+ icon.dim.th = this.height;
+ }
+ processStyleID(style);
+
+ // we will undoubtedly get some createMarker queuing, so set this up in advance
+ for (var i = 0; i < icon.markerBacklog.length; i++) {
+ var p = icon.markerBacklog[i][0];
+ var d = icon.markerBacklog[i][1];
+ createMarker(p, d);
+ if (p.marker) p.marker.active = true;
+ }
+ delete icon.markerBacklog;
+ };
+ return;
+ }
+ else { //if (icon.dim.w < 0 || icon.dim.h < 0) {
+ if (icon.img && icon.img.complete) {
+ // sometimes the file is already cached and it never calls onLoad
+ if (icon.dim.w < 0 || icon.dim.h < 0) {
+ icon.dim.w = icon.img.width;
+ icon.dim.h = icon.img.height;
+ } else {
+ icon.dim.th = icon.img.height;
+ }
+ }
+ else {
+ // settle for a default of 32x32
+ icon.dim.whGuess = true;
+ icon.dim.w = 32;
+ icon.dim.h = 32;
+ icon.dim.th = 32;
+ }
+ }
+
+ // pre-scaled variables
+ var rnd = Math.round;
+ var y = icon.dim.y;
+ if (typeof icon.dim.th !== 'undefined' && icon.dim.th != icon.dim.h) { // palette - reverse kml y for maps
+ y = Math.abs(y - (icon.dim.th - icon.dim.h));
+ }
+
+ var scaled = {
+ x: icon.dim.x * icon.scale,
+ y: y * icon.scale,
+ w: icon.dim.w * icon.scale,
+ h: icon.dim.h * icon.scale,
+ aX:icon.hotSpot.x * icon.scale,
+ aY:icon.hotSpot.y * icon.scale,
+ iW:(icon.img ? icon.img.width : icon.dim.w) * icon.scale,
+ iH:(icon.img ? icon.img.height : icon.dim.h) * icon.scale
+ };
+
+ // Figure out the anchor spot
+ // Origins, anchor positions and coordinates of the marker increase in the X direction to the right and in
+ // the Y direction down.
+ var aX, aY;
+ switch (icon.hotSpot.xunits) {
+ case 'fraction': aX = rnd(scaled.aX * icon.dim.w); break;
+ case 'insetPixels': aX = rnd(icon.dim.w * icon.scale - scaled.aX); break;
+ default: aX = rnd(scaled.aX); break; // already pixels
+ }
+ switch(icon.hotSpot.yunits) {
+ case 'fraction': aY = scaled.h - rnd(icon.dim.h * scaled.aY); break;
+ case 'insetPixels': aY = rnd(scaled.aY); break;
+ default: aY = rnd(icon.dim.h * icon.scale - scaled.aY); break;
+ }
+ var iconAnchor = new google.maps.Point(aX, aY);
+
+ // Sizes
+ // (NOTE: Scale is applied to entire image, not just the section of the icon palette.)
+ var iconSize = icon.dim.whGuess ? null : new google.maps.Size(rnd(scaled.w), rnd(scaled.h));
+ var iconScale = icon.scale == 1.0 ? null :
+ icon.dim.whGuess ? new google.maps.Size(rnd(scaled.w), rnd(scaled.h))
+ : new google.maps.Size(rnd(scaled.iW), rnd(scaled.iH));
+ var iconOrigin = new google.maps.Point(rnd(scaled.x), rnd(scaled.y));
+
+ // Detect images buried in KMZ files (and use a base64 encoded URL)
+ if (kmzMetaData[icon.url]) icon.url = kmzMetaData[icon.url].dataUrl;
+
+ // Init the style object with the KML icon
+ icon.marker = {
+ url: icon.url, // url
+ size: iconSize, // size
+ origin: iconOrigin, // origin
+ anchor: iconAnchor, // anchor
+ scaledSize: iconScale // scaledSize
+ };
+
+ // Look for a predictable shadow
+ var stdRegEx = /\/(red|blue|green|yellow|lightblue|purple|pink|orange)(-dot)?\.png/;
+ var shadowSize = new google.maps.Size(59, 32);
+ var shadowPoint = new google.maps.Point(16, 32);
+ if (stdRegEx.test(icon.href)) {
+ // A standard GMap-style marker icon
+ icon.shadow = {
+ url: 'http://maps.google.com/mapfiles/ms/micons/msmarker.shadow.png', // url
+ size: shadowSize, // size
+ origin: null, // origin
+ anchor: shadowPoint, // anchor
+ scaledSize: shadowSize // scaledSize
+ };
+ } else if (icon.href.indexOf('-pushpin.png') > -1) {
+ // Pushpin marker icon
+ icon.shadow = {
+ url: 'http://maps.google.com/mapfiles/ms/micons/pushpin_shadow.png', // url
+ size: shadowSize, // size
+ origin: null, // origin
+ anchor: shadowPoint, // anchor
+ scaledSize: shadowSize // scaledSize
+ };
+ } /* else {
// Other MyMaps KML standard icon
icon.shadow = new google.maps.MarkerImage(
icon.href.replace('.png', '.shadow.png'), // url
@@ -1033,251 +1155,293 @@ function processStyleUrl(node) {
shadowSize // scaledSize
);
} */
- }
-
- var processStyles = function (doc) {
- for (var styleID in doc.styles) {
- processStyleID(doc.styles[styleID]);
- }
- };
-
- var createMarker = function (placemark, doc) {
- // create a Marker to the map from a placemark KML object
- var icon = placemark.style.icon;
-
- if ( !icon.marker && icon.img ) {
- // yay, single point of failure is holding up multiple markers...
- icon.markerBacklog = icon.markerBacklog || [];
- icon.markerBacklog.push([placemark, doc]);
- return;
- }
-
- // Load basic marker properties
- var markerOptions = geoXML3.combineOptions(parserOptions.markerOptions, {
- map: parserOptions.map,
- position: new google.maps.LatLng(placemark.Point.coordinates[0].lat, placemark.Point.coordinates[0].lng),
- title: placemark.name,
- zIndex: Math.round(placemark.Point.coordinates[0].lat * -100000)<<5,
- icon: icon.marker,
- shadow: icon.shadow,
- flat: !icon.shadow,
- visible: placemark.visibility
- });
-
- // Create the marker on the map
- var marker = new google.maps.Marker(markerOptions);
- if (!!doc) doc.markers.push(marker);
-
- // Set up and create the infowindow if it is not suppressed
- createInfoWindow(placemark, doc, marker);
- placemark.marker = marker;
- return marker;
- };
-
- var createOverlay = function (groundOverlay, doc) {
- // Add a ProjectedOverlay to the map from a groundOverlay KML object
-
- if (!window.ProjectedOverlay) {
- throw 'geoXML3 error: ProjectedOverlay not found while rendering GroundOverlay from KML';
- }
-
- var bounds = new google.maps.LatLngBounds(
- new google.maps.LatLng(groundOverlay.latLonBox.south, groundOverlay.latLonBox.west),
- new google.maps.LatLng(groundOverlay.latLonBox.north, groundOverlay.latLonBox.east)
- );
- var overlayOptions = geoXML3.combineOptions(parserOptions.overlayOptions, {percentOpacity: groundOverlay.opacity*100});
- var overlay = new ProjectedOverlay(parserOptions.map, groundOverlay.icon.href, bounds, overlayOptions);
-
- if (!!doc) {
- doc.ggroundoverlays = doc.ggroundoverlays || [];
- doc.ggroundoverlays.push(overlay);
- }
-
- return overlay;
- };
-
- // Create Polyline
- var createPolyline = function(placemark, doc) {
- var path = [];
- for (var j=0; j<placemark.LineString.length; j++) {
- var coords = placemark.LineString[j].coordinates;
- var bounds = new google.maps.LatLngBounds();
- for (var i=0;i<coords.length;i++) {
- var pt = new google.maps.LatLng(coords[i].lat, coords[i].lng);
- path.push(pt);
- bounds.extend(pt);
- }
- }
- // point to open the infowindow if triggered
- var point = path[Math.floor(path.length/2)];
- // Load basic polyline properties
- var kmlStrokeColor = kmlColor(placemark.style.line.color, placemark.style.line.colorMode);
- var polyOptions = geoXML3.combineOptions(parserOptions.polylineOptions, {
- map: parserOptions.map,
- path: path,
- strokeColor: kmlStrokeColor.color,
- strokeWeight: placemark.style.line.width,
- strokeOpacity: kmlStrokeColor.opacity,
- title: placemark.name,
- visible: placemark.visibility
- });
- var p = new google.maps.Polyline(polyOptions);
- p.bounds = bounds;
-
- // setup and create the infoWindow if it is not suppressed
- createInfoWindow(placemark, doc, p);
- if (!!doc) doc.gpolylines.push(p);
- placemark.polyline = p;
- return p;
- }
-
- // Create Polygon
- var createPolygon = function(placemark, doc) {
- var bounds = new google.maps.LatLngBounds();
- var pathsLength = 0;
- var paths = [];
- for (var polygonPart=0;polygonPart<placemark.Polygon.length;polygonPart++) {
- for (var j=0; j<placemark.Polygon[polygonPart].outerBoundaryIs.length; j++) {
- var coords = placemark.Polygon[polygonPart].outerBoundaryIs[j].coordinates;
- var path = [];
- for (var i=0;i<coords.length;i++) {
- var pt = new google.maps.LatLng(coords[i].lat, coords[i].lng);
- path.push(pt);
- bounds.extend(pt);
- }
- paths.push(path);
- pathsLength += path.length;
- }
- for (var j=0; j<placemark.Polygon[polygonPart].innerBoundaryIs.length; j++) {
- var coords = placemark.Polygon[polygonPart].innerBoundaryIs[j].coordinates;
- var path = [];
- for (var i=0;i<coords.length;i++) {
- var pt = new google.maps.LatLng(coords[i].lat, coords[i].lng);
- path.push(pt);
- bounds.extend(pt);
- }
- paths.push(path);
- pathsLength += path.length;
- }
- }
-
- // Load basic polygon properties
- var kmlStrokeColor = kmlColor(placemark.style.line.color, placemark.style.line.colorMode);
- var kmlFillColor = kmlColor(placemark.style.poly.color, placemark.style.poly.colorMode);
- if (!placemark.style.poly.fill) kmlFillColor.opacity = 0.0;
- var strokeWeight = placemark.style.line.width;
- if (!placemark.style.poly.outline) {
- strokeWeight = 0;
- kmlStrokeColor.opacity = 0.0;
- }
- var polyOptions = geoXML3.combineOptions(parserOptions.polygonOptions, {
- map: parserOptions.map,
- paths: paths,
- title: placemark.name,
- strokeColor: kmlStrokeColor.color,
- strokeWeight: strokeWeight,
- strokeOpacity: kmlStrokeColor.opacity,
- fillColor: kmlFillColor.color,
- fillOpacity: kmlFillColor.opacity,
- visible: placemark.visibility
- });
- var p = new google.maps.Polygon(polyOptions);
- p.bounds = bounds;
-
- createInfoWindow(placemark, doc, p);
- if (!!doc) doc.gpolygons.push(p);
- placemark.polygon = p;
- return p;
- }
-
- var createInfoWindow = function(placemark, doc, gObj) {
- var bStyle = placemark.style.balloon;
- var vars = placemark.vars;
-
- if (!placemark.balloonVisibility || bStyle.displayMode === 'hide') return;
-
- // define geDirections
- if (placemark.latlng) {
- vars.directions.push('sll=' + placemark.latlng.toUrlValue());
-
- var url = 'http://maps.google.com/maps?' + vars.directions.join('&');
- var address = encodeURIComponent( vars.val.address || placemark.latlng.toUrlValue() ).replace(/\%20/g, '+');
-
- vars.val.geDirections = '<a href="' + url + '&daddr=' + address + '" target=_blank>To Here</a> - <a href="' + url + '&saddr=' + address + '" target=_blank>From Here</a>';
- }
- else vars.val.geDirections = '';
-
- // add in the variables
- var iwText = bStyle.text.replace(/\$\[(\w+(\/displayName)?)\]/g, function(txt, n, dn) { return dn ? vars.display[n] : vars.val[n]; });
- var classTxt = 'geoxml3_infowindow geoxml3_style_' + placemark.styleID;
-
- // color styles
- var styleArr = [];
- if (bStyle.bgColor != 'ffffffff') styleArr.push('background: ' + kmlColor(bStyle.bgColor ).color + ';');
- if (bStyle.textColor != 'ff000000') styleArr.push('color: ' + kmlColor(bStyle.textColor).color + ';');
- var styleProp = styleArr.length ? ' style="' + styleArr.join(' ') + '"' : '';
-
- var infoWindowOptions = geoXML3.combineOptions(parserOptions.infoWindowOptions, {
- content: '<div class="' + classTxt + '"' + styleProp + '>' + iwText + '</div>',
- pixelOffset: new google.maps.Size(0, 2)
- });
-
- gObj.infoWindow = parserOptions.infoWindow || new google.maps.InfoWindow(infoWindowOptions);
- gObj.infoWindowOptions = infoWindowOptions;
-
- // Info Window-opening event handler
- google.maps.event.addListener(gObj, 'click', function(e) {
- var iW = this.infoWindow;
- iW.close();
- iW.setOptions(this.infoWindowOptions);
-
- if (e && e.latLng) iW.setPosition(e.latLng);
- else if (this.bounds) iW.setPosition(this.bounds.getCenter());
-
- iW.open(this.map, this.bounds ? null : this);
- });
-
- }
-
- return {
- // Expose some properties and methods
-
- options: parserOptions,
- docs: docs,
- docsByUrl: docsByUrl,
- kmzMetaData: kmzMetaData,
-
- parse: parse,
- parseKmlString: parseKmlString,
- hideDocument: hideDocument,
- showDocument: showDocument,
- processStyles: processStyles,
- createMarker: createMarker,
- createOverlay: createOverlay,
- createPolyline: createPolyline,
- createPolygon: createPolygon
- };
+ }
+
+ var processStyles = function (doc) {
+ for (var styleID in doc.styles) {
+ processStyleID(doc.styles[styleID]);
+ }
+ };
+
+ var createMarker = function (placemark, doc) {
+ // create a Marker to the map from a placemark KML object
+ var icon = placemark.style.icon;
+
+ if ( !icon.marker && icon.img ) {
+ // yay, single point of failure is holding up multiple markers...
+ icon.markerBacklog = icon.markerBacklog || [];
+ icon.markerBacklog.push([placemark, doc]);
+ return;
+ }
+
+ // Load basic marker properties
+ var markerOptions = geoXML3.combineOptions(parserOptions.markerOptions, {
+ map: parserOptions.map,
+ position: new google.maps.LatLng(placemark.Point.coordinates[0].lat, placemark.Point.coordinates[0].lng),
+ title: placemark.name,
+ zIndex: Math.round(placemark.Point.coordinates[0].lat * -100000)<<5,
+ icon: icon.marker,
+ shadow: icon.shadow,
+ flat: !icon.shadow,
+ visible: placemark.visibility
+ });
+
+ // Create the marker on the map
+ var marker = new google.maps.Marker(markerOptions);
+ if (!!doc) doc.markers.push(marker);
+
+ // Set up and create the infowindow if it is not suppressed
+ createInfoWindow(placemark, doc, marker);
+ placemark.marker = marker;
+ return marker;
+ };
+
+ var createOverlay = function (groundOverlay, doc) {
+ // Add a ProjectedOverlay to the map from a groundOverlay KML object
+
+ if (!window.ProjectedOverlay) {
+ throw 'geoXML3 error: ProjectedOverlay not found while rendering GroundOverlay from KML';
+ }
+
+ var bounds = new google.maps.LatLngBounds(
+ new google.maps.LatLng(groundOverlay.latLonBox.south, groundOverlay.latLonBox.west),
+ new google.maps.LatLng(groundOverlay.latLonBox.north, groundOverlay.latLonBox.east)
+ );
+ var overlayOptions = geoXML3.combineOptions(parserOptions.overlayOptions, {
+ percentOpacity: groundOverlay.opacity*100,
+ rotation: groundOverlay.rotation
+ });
+ var overlay = new ProjectedOverlay(parserOptions.map, groundOverlay.icon.href, bounds, overlayOptions);
+
+ if (!!doc) {
+ doc.ggroundoverlays = doc.ggroundoverlays || [];
+ doc.ggroundoverlays.push(overlay);
+ }
+
+ return overlay;
+ };
+
+ // Create Polyline
+ var createPolyline = function(placemark, doc) {
+ var paths = [];
+ var bounds = new google.maps.LatLngBounds();
+ if (placemark.LineString) {
+ for (var j=0; j<placemark.LineString.length; j++) {
+ var path = [];
+ var coords = placemark.LineString[j].coordinates;
+ for (var i=0;i<coords.length;i++) {
+ var pt = new google.maps.LatLng(coords[i].lat, coords[i].lng);
+ path.push(pt);
+ bounds.extend(pt);
+ }
+ paths.push(path);
+ }
+ } else if (placemark.Track) {
+ for (var j=0; j<placemark.Track.length; j++) {
+ var path = [];
+ var coords = placemark.Track[j].coordinates;
+ for (var i=0;i<coords.length;i++) {
+ var pt = new google.maps.LatLng(coords[i].lat, coords[i].lng);
+ path.push(pt);
+ bounds.extend(pt);
+ }
+ paths.push(path);
+ }
+ }
+ // point to open the infowindow if triggered
+ var point = paths[0][Math.floor(path.length/2)];
+ // Load basic polyline properties
+ var kmlStrokeColor = kmlColor(placemark.style.line.color, placemark.style.line.colorMode);
+ var polyOptions = geoXML3.combineOptions(parserOptions.polylineOptions, {
+ map: parserOptions.map,
+ path: path,
+ strokeColor: kmlStrokeColor.color,
+ strokeWeight: placemark.style.line.width,
+ strokeOpacity: kmlStrokeColor.opacity,
+ title: placemark.name,
+ visible: placemark.visibility
+ });
+ if (paths.length > 1) {
+ polyOptions.paths = paths;
+ var p = new MultiGeometry(polyOptions);
+ } else {
+ polyOptions.path = paths[0];
+ var p = new google.maps.Polyline(polyOptions);
+ }
+ p.bounds = bounds;
+
+ // setup and create the infoWindow if it is not suppressed
+ createInfoWindow(placemark, doc, p);
+ if (!!doc) doc.gpolylines.push(p);
+ placemark.polyline = p;
+ return p;
+ }
+
+ // Create Polygon
+ var createPolygon = function(placemark, doc) {
+ var bounds = new google.maps.LatLngBounds();
+ var pathsLength = 0;
+ var paths = [];
+ for (var polygonPart=0;polygonPart<placemark.Polygon.length;polygonPart++) {
+ for (var j=0; j<placemark.Polygon[polygonPart].outerBoundaryIs.length; j++) {
+ var coords = placemark.Polygon[polygonPart].outerBoundaryIs[j].coordinates;
+ var path = [];
+ for (var i=0;i<coords.length;i++) {
+ var pt = new google.maps.LatLng(coords[i].lat, coords[i].lng);
+ path.push(pt);
+ bounds.extend(pt);
+ }
+ paths.push(path);
+ pathsLength += path.length;
+ }
+ for (var j=0; j<placemark.Polygon[polygonPart].innerBoundaryIs.length; j++) {
+ var coords = placemark.Polygon[polygonPart].innerBoundaryIs[j].coordinates;
+ var path = [];
+ for (var i=0;i<coords.length;i++) {
+ var pt = new google.maps.LatLng(coords[i].lat, coords[i].lng);
+ path.push(pt);
+ bounds.extend(pt);
+ }
+ paths.push(path);
+ pathsLength += path.length;
+ }
+ }
+
+ // Load basic polygon properties
+ var kmlStrokeColor = kmlColor(placemark.style.line.color, placemark.style.line.colorMode);
+ var kmlFillColor = kmlColor(placemark.style.poly.color, placemark.style.poly.colorMode);
+ if (!placemark.style.poly.fill) kmlFillColor.opacity = 0.0;
+ var strokeWeight = placemark.style.line.width;
+ if (!placemark.style.poly.outline) {
+ strokeWeight = 0;
+ kmlStrokeColor.opacity = 0.0;
+ }
+ var polyOptions = geoXML3.combineOptions(parserOptions.polygonOptions, {
+ map: parserOptions.map,
+ paths: paths,
+ title: placemark.name,
+ strokeColor: kmlStrokeColor.color,
+ strokeWeight: strokeWeight,
+ strokeOpacity: kmlStrokeColor.opacity,
+ fillColor: kmlFillColor.color,
+ fillOpacity: kmlFillColor.opacity,
+ visible: placemark.visibility
+ });
+ var p = new google.maps.Polygon(polyOptions);
+ p.bounds = bounds;
+
+ createInfoWindow(placemark, doc, p);
+ if (!!doc) doc.gpolygons.push(p);
+ placemark.polygon = p;
+ return p;
+ }
+
+ var createInfoWindow = function(placemark, doc, gObj) {
+ var bStyle = placemark.style.balloon;
+ var vars = placemark.vars;
+
+ if (!placemark.balloonVisibility || bStyle.displayMode === 'hide') return;
+
+ // define geDirections
+ if (placemark.latlng &&
+ (!parserOptions.suppressDirections || !parserOptions.suppressDirections)) {
+ vars.directions.push('sll=' + placemark.latlng.toUrlValue());
+
+ var url = 'http://maps.google.com/maps?' + vars.directions.join('&');
+ var address = encodeURIComponent( vars.val.address || placemark.latlng.toUrlValue() ).replace(/\%20/g, '+');
+
+ vars.val.geDirections = '<a href="' + url + '&daddr=' + address + '" target=_blank>To Here</a> - <a href="' + url + '&saddr=' + address + '" target=_blank>From Here</a>';
+ }
+ else vars.val.geDirections = '';
+
+ // add in the variables
+ var iwText = bStyle.text.replace(/\$\[(\w+(\/displayName)?)\]/g, function(txt, n, dn) { return dn ? vars.display[n] : vars.val[n]; });
+ var classTxt = 'geoxml3_infowindow geoxml3_style_' + placemark.styleID;
+
+ // color styles
+ var styleArr = [];
+ if (bStyle.bgColor != 'ffffffff') styleArr.push('background: ' + kmlColor(bStyle.bgColor ).color + ';');
+ if (bStyle.textColor != 'ff000000') styleArr.push('color: ' + kmlColor(bStyle.textColor).color + ';');
+ var styleProp = styleArr.length ? ' style="' + styleArr.join(' ') + '"' : '';
+
+ var infoWindowOptions = geoXML3.combineOptions(parserOptions.infoWindowOptions, {
+ content: '<div class="' + classTxt + '"' + styleProp + '>' + iwText + '</div>',
+ pixelOffset: new google.maps.Size(0, 2)
+ });
+
+ gObj.infoWindow = parserOptions.infoWindow || new google.maps.InfoWindow(infoWindowOptions);
+ gObj.infoWindowOptions = infoWindowOptions;
+
+ // Info Window-opening event handler
+ google.maps.event.addListener(gObj, 'click', function(e) {
+ var iW = this.infoWindow;
+ iW.close();
+ iW.setOptions(this.infoWindowOptions);
+
+ if (e && e.latLng) iW.setPosition(e.latLng);
+ else if (this.bounds) iW.setPosition(this.bounds.getCenter());
+
+ iW.setContent("<div id='geoxml3_infowindow'>"+iW.getContent()+"</div>");
+ google.maps.event.addListenerOnce(iW, "domready", function() {
+ var node = document.getElementById('geoxml3_infowindow');
+ var imgArray = node.getElementsByTagName('img');
+ for (var i = 0; i < imgArray.length; i++)
+ {
+ var imgUrlIE = imgArray[i].getAttribute("src");
+ var imgUrl = cleanURL(doc.baseDir, imgUrlIE);
+
+ if (kmzMetaData[imgUrl]) {
+ imgArray[i].src = kmzMetaData[imgUrl].dataUrl;
+ } else if (kmzMetaData[imgUrlIE]) {
+ imgArray[i].src = kmzMetaData[imgUrlIE].dataUrl;
+ }
+ }
+ });
+ iW.open(this.map, this.bounds ? null : this);
+ });
+
+ }
+
+ return {
+ // Expose some properties and methods
+
+ options: parserOptions,
+ docs: docs,
+ docsByUrl: docsByUrl,
+ kmzMetaData: kmzMetaData,
+
+ parse: parse,
+ render: render,
+ parseKmlString: parseKmlString,
+ hideDocument: hideDocument,
+ showDocument: showDocument,
+ processStyles: processStyles,
+ createMarker: createMarker,
+ createOverlay: createOverlay,
+ createPolyline: createPolyline,
+ createPolygon: createPolygon
+ };
};
// End of KML Parser
// Helper objects and functions
geoXML3.getOpacity = function (kmlColor) {
- // Extract opacity encoded in a KML color value. Returns a number between 0 and 1.
- if (!!kmlColor &&
- (kmlColor !== '') &&
- (kmlColor.length == 8)) {
- var transparency = parseInt(kmlColor.substr(0, 2), 16);
- return transparency / 255;
- } else {
- return 1;
- }
+ // Extract opacity encoded in a KML color value. Returns a number between 0 and 1.
+ if (!!kmlColor &&
+ (kmlColor !== '') &&
+ (kmlColor.length == 8)) {
+ var transparency = parseInt(kmlColor.substr(0, 2), 16);
+ return transparency / 255;
+ } else {
+ return 1;
+ }
};
// Log a message to the debugging console, if one exists
geoXML3.log = function(msg) {
- if (!!window.console) {
- console.log(msg);
- } else { alert("log:"+msg); }
+ if (!!window.console) {
+ console.log(msg);
+ } else { alert("log:"+msg); }
};
/**
@@ -1290,61 +1454,61 @@ geoXML3.log = function(msg) {
* @property {ProjectedOverlay.options} overlayOptions If the parser is adding ProjectedOverlays to the map itself, any options specified here will be applied to them.
*/
geoXML3.parserOptions = function (overrides) {
- this.map = null,
- /** If true, the parser will automatically move the map to a best-fit of the geodata after parsing of a KML document completes.
- * @type Boolean
- * @default true
- */
- this.zoom = true,
- /**#@+ @type Boolean
- * @default false */
- /** If true, only a single Marker created by the parser will be able to have its InfoWindow open at once (simulating the behavior of GMaps API v2). */
- this.singleInfoWindow = false,
- /** If true, suppresses the rendering of info windows. */
- this.suppressInfoWindows = false,
- /**
- * Control whether to process styles now or later.
- *
- * <p>By default, the parser only processes KML &lt;Style&gt; elements into their GMaps equivalents
- * if it will be creating its own Markers (the createMarker option is null). Setting this option
- * to true will force such processing to happen anyway, useful if you're going to be calling parser.createMarker
- * yourself later. OTOH, leaving this option false removes runtime dependency on the GMaps API, enabling
- * the use of geoXML3 as a standalone KML parser.</p>
- */
- this.processStyles = false,
- /**#@-*/
-
- this.markerOptions = {},
- this.infoWindowOptions = {},
- this.overlayOptions = {},
-
- /**#@+ @event */
- /** This function will be called when parsing of a KML document is complete.
- * @param {geoXML3.parser#docs} doc Parsed KML data. */
- this.afterParse = null,
- /** This function will be called when parsing of a KML document is complete.
- * @param {geoXML3.parser#docs} doc Parsed KML data. */
- this.failedParse = null,
- /**
- * If supplied, this function will be called once for each marker <Placemark> in the KML document, instead of the parser adding its own Marker to the map.
- * @param {geoXML3.parser.render#placemark} placemark Placemark object.
- * @param {geoXML3.parser#docs} doc Parsed KML data.
- */
- this.createMarker = null,
- /**
- * If supplied, this function will be called once for each <GroundOverlay> in the KML document, instead of the parser adding its own ProjectedOverlay to the map.
- * @param {geoXML3.parser.render#groundOverlay} groundOverlay GroundOverlay object.
- * @param {geoXML3.parser#docs} doc Parsed KML data.
- */
- this.createOverlay = null
- /**#@-*/
-
- if (overrides) {
- for (var prop in overrides) {
- if (overrides.hasOwnProperty(prop)) this[prop] = overrides[prop];
- }
- }
- return this;
+ this.map = null,
+ /** If true, the parser will automatically move the map to a best-fit of the geodata after parsing of a KML document completes.
+ * @type Boolean
+ * @default true
+ */
+ this.zoom = true,
+ /**#@+ @type Boolean
+ * @default false */
+ /** If true, only a single Marker created by the parser will be able to have its InfoWindow open at once (simulating the behavior of GMaps API v2). */
+ this.singleInfoWindow = false,
+ /** If true, suppresses the rendering of info windows. */
+ this.suppressInfoWindows = false,
+ /**
+ * Control whether to process styles now or later.
+ *
+ * <p>By default, the parser only processes KML &lt;Style&gt; elements into their GMaps equivalents
+ * if it will be creating its own Markers (the createMarker option is null). Setting this option
+ * to true will force such processing to happen anyway, useful if you're going to be calling parser.createMarker
+ * yourself later. OTOH, leaving this option false removes runtime dependency on the GMaps API, enabling
+ * the use of geoXML3 as a standalone KML parser.</p>
+ */
+ this.processStyles = false,
+ /**#@-*/
+
+ this.markerOptions = {},
+ this.infoWindowOptions = {},
+ this.overlayOptions = {},
+
+ /**#@+ @event */
+ /** This function will be called when parsing of a KML document is complete.
+ * @param {geoXML3.parser#docs} doc Parsed KML data. */
+ this.afterParse = null,
+ /** This function will be called when parsing of a KML document is complete.
+ * @param {geoXML3.parser#docs} doc Parsed KML data. */
+ this.failedParse = null,
+ /**
+ * If supplied, this function will be called once for each marker <Placemark> in the KML document, instead of the parser adding its own Marker to the map.
+ * @param {geoXML3.parser.render#placemark} placemark Placemark object.
+ * @param {geoXML3.parser#docs} doc Parsed KML data.
+ */
+ this.createMarker = null,
+ /**
+ * If supplied, this function will be called once for each <GroundOverlay> in the KML document, instead of the parser adding its own ProjectedOverlay to the map.
+ * @param {geoXML3.parser.render#groundOverlay} groundOverlay GroundOverlay object.
+ * @param {geoXML3.parser#docs} doc Parsed KML data.
+ */
+ this.createOverlay = null
+ /**#@-*/
+
+ if (overrides) {
+ for (var prop in overrides) {
+ if (overrides.hasOwnProperty(prop)) this[prop] = overrides[prop];
+ }
+ }
+ return this;
};
/**
@@ -1356,18 +1520,18 @@ geoXML3.parserOptions = function (overrides) {
* @return {geoXML3.parserOptions} Combined result.
*/
geoXML3.combineOptions = function (overrides, defaults) {
- var result = {};
- if (!!overrides) {
- for (var prop in overrides) {
- if (overrides.hasOwnProperty(prop)) result[prop] = overrides[prop];
- }
- }
- if (!!defaults) {
- for (prop in defaults) {
- if (defaults.hasOwnProperty(prop) && result[prop] === undefined) result[prop] = defaults[prop];
- }
- }
- return result;
+ var result = {};
+ if (!!overrides) {
+ for (var prop in overrides) {
+ if (overrides.hasOwnProperty(prop)) result[prop] = overrides[prop];
+ }
+ }
+ if (!!defaults) {
+ for (prop in defaults) {
+ if (defaults.hasOwnProperty(prop) && result[prop] === undefined) result[prop] = defaults[prop];
+ }
+ }
+ return result;
};
/**
@@ -1394,35 +1558,43 @@ geoXML3.fetchers = [];
* @return {Element|Document} DOM.
*/
geoXML3.xmlParse = function (str) {
- if (!!window.DOMParser) return (new DOMParser()).parseFromString(str, 'text/xml');
- else if (!!window.ActiveXObject) {
- var doc;
-
- // the many versions of IE's DOM parsers
- var AXOs = [
- 'MSXML2.DOMDocument.6.0',
- 'MSXML2.DOMDocument.5.0',
- 'MSXML2.DOMDocument.4.0',
- 'MSXML2.DOMDocument.3.0',
- 'MSXML2.DOMDocument',
- 'Microsoft.XMLDOM',
- 'MSXML.DOMDocument'
- ];
- for (var i = 0; i < AXOs.length; i++) {
- try { doc = new ActiveXObject(AXOs[i]); break; }
- catch(e) { continue; }
- }
- if (!doc) return createElement('div', null);
-
- if (doc.async) doc.async = false;
- doc.loadXML(str);
- return doc;
- }
-
- return createElement('div', null);
+ if ((typeof ActiveXObject != 'undefined') || ("ActiveXObject" in window)) {
+ var doc = new ActiveXObject('Microsoft.XMLDOM');
+ doc.loadXML(str);
+ return doc;
+ }
+
+ if (typeof DOMParser != 'undefined') {
+ return (new DOMParser()).parseFromString(str, 'text/xml');
+ }
+
+ return document.createElement('div', null);
}
/**
+ * Checks for XML parse error.
+ *
+ * @param {xmlDOM} XML DOM.
+ * @return boolean.
+ */
+// from http://stackoverflow.com/questions/11563554/how-do-i-detect-xml-parsing-errors-when-using-javascripts-domparser-in-a-cross
+geoXML3.isParseError = function(parsedDocument) {
+ if ((typeof ActiveXObject != 'undefined') || ("ActiveXObject" in window))
+ return false;
+ // parser and parsererrorNS could be cached on startup for efficiency
+ var p = new DOMParser(),
+ errorneousParse = p.parseFromString('<', 'text/xml'),
+ parsererrorNS = errorneousParse.getElementsByTagName("parsererror")[0].namespaceURI;
+
+ if (parsererrorNS === 'http://www.w3.org/1999/xhtml') {
+ // In PhantomJS the parseerror element doesn't seem to have a special namespace, so we are just guessing here :(
+ return parsedDocument.getElementsByTagName("parsererror").length > 0;
+ }
+
+ return parsedDocument.getElementsByTagNameNS(parsererrorNS, 'parsererror').length > 0;
+};
+
+/**
* Fetches a XML document.
*
* <p>Fetches/parses the given XML URL and passes the parsed document (in a
@@ -1433,62 +1605,87 @@ geoXML3.xmlParse = function (str) {
* @param {Function(Document)} callback Function to call when the document is processed.
*/
geoXML3.fetchXML = function (url, callback) {
- function timeoutHandler() { callback(); };
-
- var xhrFetcher = new Object();
- if (!!geoXML3.fetchers.length) xhrFetcher = geoXML3.fetchers.pop();
- else if (!!window.XMLHttpRequest) xhrFetcher.fetcher = new window.XMLHttpRequest(); // Most browsers
- else if (!!window.ActiveXObject) { // Some IE
- // the many versions of IE's XML fetchers
- var AXOs = [
- 'MSXML2.XMLHTTP.6.0',
- 'MSXML2.XMLHTTP.5.0',
- 'MSXML2.XMLHTTP.4.0',
- 'MSXML2.XMLHTTP.3.0',
- 'MSXML2.XMLHTTP',
- 'Microsoft.XMLHTTP',
- 'MSXML.XMLHTTP'
- ];
- for (var i = 0; i < AXOs.length; i++) {
- try { xhrFetcher.fetcher = new ActiveXObject(AXOs[i]); break; }
- catch(e) { continue; }
- }
- if (!xhrFetcher.fetcher) {
- geoXML3.log('Unable to create XHR object');
- callback(null);
- return null;
- }
- }
-
- if (!!xhrFetcher.fetcher.overrideMimeType) xhrFetcher.fetcher.overrideMimeType('text/xml');
- xhrFetcher.fetcher.open('GET', url, true);
- xhrFetcher.fetcher.onreadystatechange = function () {
- if (xhrFetcher.fetcher.readyState === 4) {
- // Retrieval complete
- if (!!xhrFetcher.xhrtimeout) clearTimeout(xhrFetcher.xhrtimeout);
- if (xhrFetcher.fetcher.status >= 400) {
- geoXML3.log('HTTP error ' + xhrFetcher.fetcher.status + ' retrieving ' + url);
- callback();
- }
- // Returned successfully
- else {
- if (xhrFetcher.fetcher.responseXML) {
- // Sometimes IE will get the data, but won't bother loading it as an XML doc
- var xmlDoc = xhrFetcher.fetcher.responseXML;
- if (xmlDoc && !xmlDoc.documentElement && !xmlDoc.ownerElement) xmlDoc.loadXML(xhrFetcher.fetcher.responseText);
- callback(xmlDoc);
- } else // handle valid xml sent with wrong MIME type
- callback(geoXML3.xmlParse(xhrFetcher.fetcher.responseText));
- }
-
- // We're done with this fetcher object
- geoXML3.fetchers.push(xhrFetcher);
- }
- };
-
- xhrFetcher.xhrtimeout = setTimeout(timeoutHandler, 60000);
- xhrFetcher.fetcher.send(null);
- return null;
+ function timeoutHandler() { callback(); };
+
+ var xhrFetcher = new Object();
+ if (!!geoXML3.fetchers.length) xhrFetcher = geoXML3.fetchers.pop();
+ else if (!!window.XMLHttpRequest) xhrFetcher.fetcher = new window.XMLHttpRequest(); // Most browsers
+ else if (!!window.ActiveXObject) { // Some IE
+ // the many versions of IE's XML fetchers
+ var AXOs = [
+ 'MSXML2.XMLHTTP.6.0',
+ 'MSXML2.XMLHTTP.5.0',
+ 'MSXML2.XMLHTTP.4.0',
+ 'MSXML2.XMLHTTP.3.0',
+ 'MSXML2.XMLHTTP',
+ 'Microsoft.XMLHTTP',
+ 'MSXML.XMLHTTP'
+ ];
+ for (var i = 0; i < AXOs.length; i++) {
+ try { xhrFetcher.fetcher = new ActiveXObject(AXOs[i]); break; }
+ catch(e) { continue; }
+ }
+ if (!xhrFetcher.fetcher) {
+ geoXML3.log('Unable to create XHR object');
+ callback(null);
+ return null;
+ }
+ }
+
+ xhrFetcher.fetcher.open('GET', url, true);
+ if (!!xhrFetcher.fetcher.overrideMimeType) xhrFetcher.fetcher.overrideMimeType('text/xml');
+ xhrFetcher.fetcher.onreadystatechange = function () {
+ if (xhrFetcher.fetcher.readyState === 4) {
+ // Retrieval complete
+ if (!!xhrFetcher.xhrtimeout) clearTimeout(xhrFetcher.xhrtimeout);
+ if (xhrFetcher.fetcher.status >= 400) {
+ geoXML3.log('HTTP error ' + xhrFetcher.fetcher.status + ' retrieving ' + url);
+ callback();
+ }
+ // Returned successfully
+ else {
+ if (xhrFetcher.fetcher.responseXML) {
+ // Sometimes IE will get the data, but won't bother loading it as an XML doc
+ var xml = xhrFetcher.fetcher.responseXML;
+ if (xml && !xml.documentElement && !xml.ownerElement) {
+ xml.loadXML(xhrFetcher.fetcher.responseText);
+ }
+ } else {// handle valid xml sent with wrong MIME type
+ xml=geoXML3.xmlParse(xhrFetcher.fetcher.responseText);
+ }
+ // handle parse errors
+ if (xml.parseError && (xml.parseError.errorCode != 0)) {
+ geoXML3.log("XML parse error "+xml.parseError.errorCode+", "+xml.parseError.reason+"\nLine:"+xml.parseError.line+", Position:"+xml.parseError.linepos+", srcText:"+xml.parseError.srcText);
+ xml = "failed parse"
+ } else if (geoXML3.isParseError(xml)) {
+ geoXML3.log("XML parse error");
+ xml = "failed parse"
+ }
+ callback(xml);
+ }
+ // We're done with this fetcher object
+ geoXML3.fetchers.push(xhrFetcher);
+ }
+ };
+
+ xhrFetcher.xhrtimeout = setTimeout(timeoutHandler, 60000);
+ xhrFetcher.fetcher.send(null);
+ return null;
+};
+
+var IEversion = function() {
+ // http://msdn.microsoft.com/workshop/author/dhtml/overview/browserdetection.asp
+ // Returns the version of Internet Explorer or a -1
+ // (indicating the use of another browser).
+ var rv = -1; // Return value assumes failure
+ if (navigator.appName == 'Microsoft Internet Explorer') {
+ var ua = navigator.userAgent;
+ var re = new RegExp("MSIE ([0-9]{1,}[\.0-9]{0,})");
+ if (re.exec(ua) != null) {
+ rv = parseFloat( RegExp.$1 );
+ }
+ }
+ return rv;
};
/**
@@ -1507,117 +1704,131 @@ geoXML3.fetchXML = function (url, callback) {
* @see http://code.google.com/apis/kml/documentation/kmzarchives.html
*/
geoXML3.fetchZIP = function (url, callback, parser) {
- // Just need a single 'new' declaration with a really long function...
- var zipFile = new ZipFile(url, function (zip) {
- // Retrieval complete
-
- // Check for ERRORs in zip.status
- for (var i = 0; i < zip.status.length; i++) {
- var msg = zip.status[i];
- if (msg.indexOf("ERROR") == 0) {
- geoXML3.log('HTTP/ZIP error retrieving ' + url + ': ' + msg);
- callback();
- return;
- }
- else if (msg.indexOf("WARNING") == 0) { // non-fatal, but still might be useful
- geoXML3.log('HTTP/ZIP warning retrieving ' + url + ': ' + msg);
- }
- }
-
- // Make sure KMZ structure is according to spec (with a single KML file in the root dir)
- var KMLCount = 0;
- var KML;
- for (var i = 0; i < zip.entries.length; i++) {
- var name = zip.entries[i].name;
- if (!/\.kml$/.test(name)) continue;
-
- KMLCount++;
- if (KMLCount == 1) KML = i;
- else {
- geoXML3.log('KMZ warning retrieving ' + url + ': found extra KML "' + name + '" in KMZ; discarding...');
- }
- }
-
- // Returned successfully, but still needs extracting
- var baseUrl = cleanURL(defileURL(url), url) + '/';
- var kmlProcessing = { // this is an object just so it gets passed properly
- timer: null,
- extractLeft: 0,
- timerCalls: 0
- };
- var extractCb = function(entry, entryContent) {
- var mdUrl = cleanURL(baseUrl, entry.name);
- var ext = entry.name.substring(entry.name.lastIndexOf(".") + 1).toLowerCase();
- kmlProcessing.extractLeft--;
-
- if ((typeof entryContent.description == "string") && (entryContent.name == "Error")) {
- geoXML3.log('KMZ error extracting ' + mdUrl + ': ' + entryContent.description);
- callback();
- return;
- }
-
- // MIME types that can be used in KML
- var mime;
- if (ext === 'jpg') ext = 'jpeg';
- if (/^(gif|jpeg|png)$/.test(ext)) mime = 'image/' + ext;
- else if (ext === 'mp3') mime = 'audio/mpeg';
- else if (ext === 'm4a') mime = 'audio/mp4';
- else if (ext === 'm4a') mime = 'audio/MP4-LATM';
- else mime = 'application/octet-stream';
-
- parser.kmzMetaData[mdUrl] = {};
- parser.kmzMetaData[mdUrl].entry = entry;
- // data:image/gif;base64,R0lGODlhEAAOALMA...
- parser.kmzMetaData[mdUrl].dataUrl = 'data:' + mime + ';base64,' + base64Encode(entryContent);
-
- // IE cannot handle GET requests beyond 2071 characters, even if it's an inline image
- if (/msie/i.test(navigator.userAgent) && !/opera/i.test(navigator.userAgent) && parser.kmzMetaData[mdUrl].dataUrl.length > 2071)
- parser.kmzMetaData[mdUrl].dataUrl =
- // this is a simple IE icon; to hint at the problem...
- 'data:image/gif;base64,R0lGODlhDwAQAOMPADBPvSpQ1Dpoyz1p6FhwvU2A6ECP63CM04CWxYCk+V6x+UK++Jao3rvC3fj7+v///yH5BAEKAA8ALAAAAAAPABAAAASC8Mk5mwCAUMlWwcLRHEelLA' +
- 'oGDMgzSsiyGCAhCETDPMh5XQCBwYBrNBIKWmg0MCQHj8MJU5IoroYCY6AAAgrDIbbQDGIK6DR5UPhlNo0JAlSUNAiDgH7eNAxEDWAKCQM2AAFheVxYAA0AIkFOJ1gBcQQaUQKKA5w7LpcEBwkJaKMUEQA7';
- };
- var kmlExtractCb = function(entry, entryContent) {
- if ((typeof entryContent.description == "string") && (entryContent.name == "Error")) {
- geoXML3.log('KMZ error extracting ' + mdUrl + ': ' + entryContent.description);
- callback();
- return;
- }
-
- // check to see if the KML is the last file extracted
- clearTimeout(kmlProcessing.timer);
- if (kmlProcessing.extractLeft <= 1) {
- kmlProcessing.extractLeft--;
- callback(geoXML3.xmlParse(entryContent));
- return;
- }
- else {
- // KML file isn't last yet; it may need to use those files, so wait a bit (100ms)
- kmlProcessing.timerCalls++;
- if (kmlProcessing.timerCalls < 100) {
- kmlProcessing.timer = setTimeout(function() { kmlExtractCb(entry, entryContent); }, 100);
- }
- else {
- geoXML3.log('KMZ warning extracting ' + url + ': entire ZIP has not been extracted after 10 seconds; running through KML, anyway...');
- kmlProcessing.extractLeft--;
- callback(geoXML3.xmlParse(entryContent));
- }
- }
- return;
- };
- for (var i = 0; i < zip.entries.length; i++) {
- var entry = zip.entries[i];
- var ext = entry.name.substring(entry.name.lastIndexOf(".") + 1).toLowerCase();
- if (!/^(gif|jpe?g|png|kml)$/.test(ext)) continue; // not going to bother to extract files we don't support
- if (ext === "kml" && i != KML) continue; // extra KMLs get discarded
- if (!parser && ext != "kml") continue; // cannot store images without a parser object
-
- // extract asynchronously
- kmlProcessing.extractLeft++;
- if (ext === "kml") entry.extract(kmlExtractCb);
- else entry.extract(extractCb);
- }
- });
+ // Just need a single 'new' declaration with a really long function...
+ var zipFile = new ZipFile(url, function (zip) {
+ // Retrieval complete
+
+ // Check for ERRORs in zip.status
+ for (var i = 0; i < zip.status.length; i++) {
+ var msg = zip.status[i];
+ if (msg.indexOf("ERROR") == 0) {
+ geoXML3.log('HTTP/ZIP error retrieving ' + url + ': ' + msg);
+ callback();
+ return;
+ }
+ else if (msg.indexOf("EXCEPTION") == 0) {
+ geoXML3.log('HTTP/ZIP exception retrieving ' + url + ': ' + msg);
+ callback();
+ return;
+ } else if (msg.indexOf("WARNING") == 0) { // non-fatal, but still might be useful
+ geoXML3.log('HTTP/ZIP warning retrieving ' + url + ': ' + msg);
+ } else if (msg.indexOf("INFO") == 0) { // non-fatal, but still might be useful
+ geoXML3.log('HTTP/ZIP info retrieving ' + url + ': ' + msg);
+ }
+ }
+
+ // Make sure KMZ structure is according to spec (with a single KML file in the root dir)
+ var KMLCount = 0;
+ var KML;
+ for (var i = 0; i < zip.entries.length; i++) {
+ var name = zip.entries[i].name;
+ if (!/\.kml$/.test(name)) continue;
+
+ KMLCount++;
+ if (KMLCount == 1) KML = i;
+ else {
+ geoXML3.log('KMZ warning retrieving ' + url + ': found extra KML "' + name + '" in KMZ; discarding...');
+ }
+ }
+
+ // Returned successfully, but still needs extracting
+ var baseUrl = cleanURL(defileURL(url), url) + '/';
+ var kmlProcessing = { // this is an object just so it gets passed properly
+ timer: null,
+ extractLeft: 0,
+ timerCalls: 0
+ };
+ var extractCb = function(entry, entryContent) {
+ var mdUrl = cleanURL(baseUrl, entry.name);
+ var ext = entry.name.substring(entry.name.lastIndexOf(".") + 1).toLowerCase();
+ kmlProcessing.extractLeft--;
+
+ if ((typeof entryContent.description == "string") && (entryContent.name == "Error")) {
+ geoXML3.log('KMZ error extracting ' + mdUrl + ': ' + entryContent.description);
+ callback();
+ return;
+ }
+
+ // MIME types that can be used in KML
+ var mime;
+ if (ext === 'jpg') ext = 'jpeg';
+ if (/^(gif|jpeg|png)$/.test(ext)) mime = 'image/' + ext;
+ else if (ext === 'mp3') mime = 'audio/mpeg';
+ else if (ext === 'm4a') mime = 'audio/mp4';
+ else if (ext === 'm4a') mime = 'audio/MP4-LATM';
+ else mime = 'application/octet-stream';
+
+ parser.kmzMetaData[mdUrl] = {};
+ parser.kmzMetaData[mdUrl].entry = entry;
+ // data:image/gif;base64,R0lGODlhEAAOALMA...
+ parser.kmzMetaData[mdUrl].dataUrl = 'data:' + mime + ';base64,' + base64Encode(entryContent);
+ // IE cannot handle GET requests beyond 2071 characters, even if it's an inline image
+ if (/msie/i.test(navigator.userAgent) && !/opera/i.test(navigator.userAgent))
+ {
+ if (((IEversion() < 8.0) &&
+ (parser.kmzMetaData[mdUrl].dataUrl.length > 2071)) ||
+ ((IEversion < 9.0) &&
+ (parser.kmzMetaData[mdUrl].dataUrl.length > 32767))) {
+ parser.kmzMetaData[mdUrl].dataUrl =
+ // this is a simple IE icon; to hint at the problem...
+ 'data:image/gif;base64,R0lGODlhDwAQAOMPADBPvSpQ1Dpoyz1p6FhwvU2A6ECP63CM04CWxYCk+V6x+UK++Jao3rvC3fj7+v///yH5BAEKAA8ALAAAAAAPABAAAASC8Mk5mwCAUMlWwcLRHEelLA' +
+ 'oGDMgzSsiyGCAhCETDPMh5XQCBwYBrNBIKWmg0MCQHj8MJU5IoroYCY6AAAgrDIbbQDGIK6DR5UPhlNo0JAlSUNAiDgH7eNAxEDWAKCQM2AAFheVxYAA0AIkFOJ1gBcQQaUQKKA5w7LpcEBwkJaKMUEQA7';
+ }
+ }
+ parser.kmzMetaData[internalSrc(entry.name)]=parser.kmzMetaData[mdUrl];
+
+ };
+ var kmlExtractCb = function(entry, entryContent) {
+ if ((typeof entryContent.description == "string") && (entryContent.name == "Error")) {
+ geoXML3.log('KMZ error extracting ' + entry.name + ': ' + entryContent.description);
+ callback();
+ return;
+ }
+
+ // check to see if the KML is the last file extracted
+ clearTimeout(kmlProcessing.timer);
+ if (kmlProcessing.extractLeft <= 1) {
+ kmlProcessing.extractLeft--;
+ callback(geoXML3.xmlParse(entryContent));
+ return;
+ }
+ else {
+ // KML file isn't last yet; it may need to use those files, so wait a bit (100ms)
+ kmlProcessing.timerCalls++;
+ if (kmlProcessing.timerCalls < 100) {
+ kmlProcessing.timer = setTimeout(function() { kmlExtractCb(entry, entryContent); }, 100);
+ }
+ else {
+ geoXML3.log('KMZ warning extracting ' + url + ': entire ZIP has not been extracted after 10 seconds; running through KML, anyway...');
+ kmlProcessing.extractLeft--;
+ callback(geoXML3.xmlParse(entryContent));
+ }
+ }
+ return;
+ };
+ for (var i = 0; i < zip.entries.length; i++) {
+ var entry = zip.entries[i];
+ var ext = entry.name.substring(entry.name.lastIndexOf(".") + 1).toLowerCase();
+ if (!/^(gif|jpe?g|png|kml)$/.test(ext)) continue; // not going to bother to extract files we don't support
+ if (ext === "kml" && i != KML) continue; // extra KMLs get discarded
+ if (!parser && ext != "kml") continue; // cannot store images without a parser object
+
+ // extract asynchronously
+ kmlProcessing.extractLeft++;
+ if (ext === "kml") entry.extract(kmlExtractCb);
+ else entry.extract(extractCb);
+ }
+ }); //,3 for most verbose logging
};
@@ -1629,18 +1840,18 @@ geoXML3.fetchZIP = function (url, callback, parser) {
* @return {String|Null}
*/
geoXML3.nodeValue = function(node, defVal) {
- var retStr="";
- if (!node) {
- return (typeof defVal === 'undefined' || defVal === null) ? null : defVal;
- }
- if(node.nodeType==3||node.nodeType==4||node.nodeType==2){
- retStr+=node.nodeValue;
- }else if(node.nodeType==1||node.nodeType==9||node.nodeType==11){
- for(var i=0;i<node.childNodes.length;++i){
- retStr+=arguments.callee(node.childNodes[i]);
- }
- }
- return retStr;
+ var retStr="";
+ if (!node) {
+ return (typeof defVal === 'undefined' || defVal === null) ? null : defVal;
+ }
+ if(node.nodeType==3||node.nodeType==4||node.nodeType==2){
+ retStr+=node.nodeValue;
+ }else if(node.nodeType==1||node.nodeType==9||node.nodeType==11){
+ for(var i=0;i<node.childNodes.length;++i){
+ retStr+=arguments.callee(node.childNodes[i]);
+ }
+ }
+ return retStr;
};
/**
@@ -1651,12 +1862,12 @@ geoXML3.nodeValue = function(node, defVal) {
* @return {Boolean|Null}
*/
geoXML3.getBooleanValue = function(node, defVal) {
- var nodeContents = geoXML3.nodeValue(node);
- if (nodeContents === null) return defVal || false;
- nodeContents = parseInt(nodeContents);
- if (isNaN(nodeContents)) return true;
- if (nodeContents == 0) return false;
- else return true;
+ var nodeContents = geoXML3.nodeValue(node);
+ if (nodeContents === null) return defVal || false;
+ nodeContents = parseInt(nodeContents);
+ if (isNaN(nodeContents)) return true;
+ if (nodeContents == 0) return false;
+ else return true;
}
/**
@@ -1671,32 +1882,32 @@ geoXML3.getBooleanValue = function(node, defVal) {
* @author Brendan Byrd
*/
geoXML3.getElementsByTagNameNS = function(node, namespace, tagname) {
- if (node && typeof node.getElementsByTagNameNS != 'undefined') return node.getElementsByTagNameNS(namespace, tagname);
- if (!node) return [];
-
- var root = node.documentElement || node.ownerDocument && node.ownerDocument.documentElement;
- if (!root || !root.attributes) return [];
-
- // search for namespace prefix
- for (var i = 0; i < root.attributes.length; i++) {
- var attr = root.attributes[i];
- if (attr.prefix === 'xmlns' && attr.nodeValue === namespace) return node.getElementsByTagName(attr.baseName + ':' + tagname);
- else if (attr.nodeName === 'xmlns' && attr.nodeValue === namespace) {
- // default namespace
- if (typeof node.selectNodes != 'undefined') {
- // Newer IEs have the SelectionNamespace property that can be used with selectNodes
- if (!root.ownerDocument.getProperty('SelectionNamespaces'))
- root.ownerDocument.setProperty('SelectionNamespaces', "xmlns:defaultNS='" + namespace + "'");
- return node.selectNodes('.//defaultNS:' + tagname);
- }
- else {
- // Otherwise, you can still try to tack on the 'xmlns' attribute to root
- root.setAttribute('xmlns:defaultNS', namespace);
- return node.getElementsByTagName('defaultNS:' + tagname);
- }
- }
- }
- return geoXML3.getElementsByTagName(node, tagname); // try the unqualified version
+ if (node && typeof node.getElementsByTagNameNS != 'undefined') return node.getElementsByTagNameNS(namespace, tagname);
+ if (!node) return [];
+
+ var root = node.documentElement || node.ownerDocument && node.ownerDocument.documentElement;
+ if (!root || !root.attributes) return [];
+
+ // search for namespace prefix
+ for (var i = 0; i < root.attributes.length; i++) {
+ var attr = root.attributes[i];
+ if (attr.prefix === 'xmlns' && attr.nodeValue === namespace) return node.getElementsByTagName(attr.baseName + ':' + tagname);
+ else if (attr.nodeName === 'xmlns' && attr.nodeValue === namespace) {
+ // default namespace
+ if (typeof node.selectNodes != 'undefined') {
+ // Newer IEs have the SelectionNamespace property that can be used with selectNodes
+ if (!root.ownerDocument.getProperty('SelectionNamespaces'))
+ root.ownerDocument.setProperty('SelectionNamespaces', "xmlns:defaultNS='" + namespace + "'");
+ return node.selectNodes('.//defaultNS:' + tagname);
+ }
+ else {
+ // Otherwise, you can still try to tack on the 'xmlns' attribute to root
+ root.setAttribute('xmlns:defaultNS', namespace);
+ return node.getElementsByTagName('defaultNS:' + tagname);
+ }
+ }
+ }
+ return geoXML3.getElementsByTagName(node, tagname); // try the unqualified version
};
/**
@@ -1711,9 +1922,9 @@ geoXML3.getElementsByTagNameNS = function(node, namespace, tagname) {
* @author Brendan Byrd
*/
geoXML3.getElementsByTagName = function(node, tagname) {
- if (node && typeof node.getElementsByTagNameNS != 'undefined') return node.getElementsByTagName(tagname); // if it has both functions, it should be accurate
+ if (node && typeof node.getElementsByTagNameNS != 'undefined') return node.getElementsByTagName(tagname); // if it has both functions, it should be accurate
// if (node && typeof node.selectNodes != 'undefined') return node.selectNodes(".//*[local-name()='" + tagname + "']");
- return node.getElementsByTagName(tagname); // hope for the best...
+ return node.getElementsByTagName(tagname); // hope for the best...
}
/**
@@ -1726,21 +1937,30 @@ geoXML3.getElementsByTagName = function(node, tagname) {
* @author Brendan Byrd
*/
var toAbsURL = function (d, s) {
- var p, f, i;
- var h = location.protocol + "://" + location.host;
+ var p, f, i;
+ var h = location.protocol + "://" + location.host;
+
+ if (!s.length) return '';
+ if (/^\w+:/.test(s)) return s;
+ if (s.indexOf('/') == 0) return h + s;
- if (!s.length) return '';
- if (/^\w+:/.test(s)) return s;
- if (s.indexOf('/') == 0) return h + s;
+ p = d.replace(/\/[^\/]*$/, '');
+ f = s.match(/\.\.\//g);
+ if (f) {
+ s = s.substring(f.length * 3);
+ for (i = f.length; i--;) { p = p.substring(0, p.lastIndexOf('/')); }
+ }
- p = d.replace(/\/[^\/]*$/, '');
- f = s.match(/\.\.\//g);
- if (f) {
- s = s.substring(f.length * 3);
- for (i = f.length; i--;) { p = p.substring(0, p.lastIndexOf('/')); }
- }
+ return h + p + '/' + s;
+}
- return h + p + '/' + s;
+var internalSrc = function(src) {
+ //this gets the full url
+ var url = document.location.href;
+ //this removes everything after the last slash in the path
+ url = url.substring(0,url.lastIndexOf("/") + 1);
+ var internalPath= url+src;
+ return internalPath;
}
/**
@@ -1752,9 +1972,9 @@ var toAbsURL = function (d, s) {
* @author Brendan Byrd
*/
var dehostURL = function (s) {
- var h = location.protocol + "://" + location.host;
- h = h.replace(/([\.\\\+\*\?\[\^\]\$\(\)])/g, '\\$1'); // quotemeta
- return s.replace(new RegExp('^' + h, 'i'), '');
+ var h = location.protocol + "://" + location.host;
+ h = h.replace(/([\.\\\+\*\?\[\^\]\$\(\)])/g, '\\$1'); // quotemeta
+ return s.replace(new RegExp('^' + h, 'i'), '');
}
/**
@@ -1782,46 +2002,46 @@ var defileURL = function (s) { return s ? s.substr(0, s.lastIndexOf('/') + 1)
// Some extra Array subs for ease of use
// http://stackoverflow.com/questions/143847/best-way-to-find-an-item-in-a-javascript-array
Array.prototype.hasObject = (
- !Array.indexOf ? function (obj) {
- var l = this.length + 1;
- while (l--) {
- if (this[l - 1] === obj) return true;
- }
- return false;
- } : function (obj) {
- return (this.indexOf(obj) !== -1);
- }
+ !Array.indexOf ? function (obj) {
+ var l = this.length + 1;
+ while (l--) {
+ if (this[l - 1] === obj) return true;
+ }
+ return false;
+ } : function (obj) {
+ return (this.indexOf(obj) !== -1);
+ }
);
Array.prototype.hasItemInObj = function (name, item) {
- var l = this.length + 1;
- while (l--) {
- if (this[l - 1][name] === item) return true;
- }
- return false;
+ var l = this.length + 1;
+ while (l--) {
+ if (this[l - 1][name] === item) return true;
+ }
+ return false;
};
if (!Array.prototype.indexOf) {
- Array.prototype.indexOf = function (obj, fromIndex) {
- if (fromIndex == null) {
- fromIndex = 0;
- } else if (fromIndex < 0) {
- fromIndex = Math.max(0, this.length + fromIndex);
- }
- for (var i = fromIndex, j = this.length; i < j; i++) {
- if (this[i] === obj) return i;
- }
- return -1;
- };
+ Array.prototype.indexOf = function (obj, fromIndex) {
+ if (fromIndex == null) {
+ fromIndex = 0;
+ } else if (fromIndex < 0) {
+ fromIndex = Math.max(0, this.length + fromIndex);
+ }
+ for (var i = fromIndex, j = this.length; i < j; i++) {
+ if (this[i] === obj) return i;
+ }
+ return -1;
+ };
}
Array.prototype.indexOfObjWithItem = function (name, item, fromIndex) {
- if (fromIndex == null) {
- fromIndex = 0;
- } else if (fromIndex < 0) {
- fromIndex = Math.max(0, this.length + fromIndex);
- }
- for (var i = fromIndex, j = this.length; i < j; i++) {
- if (this[i][name] === item) return i;
- }
- return -1;
+ if (fromIndex == null) {
+ fromIndex = 0;
+ } else if (fromIndex < 0) {
+ fromIndex = Math.max(0, this.length + fromIndex);
+ }
+ for (var i = fromIndex, j = this.length; i < j; i++) {
+ if (this[i][name] === item) return i;
+ }
+ return -1;
};
/**
@@ -1833,23 +2053,23 @@ Array.prototype.indexOfObjWithItem = function (name, item, fromIndex) {
* @author Brendan Byrd
*/
var base64Encode = function(input) {
- var keyString = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
- var output = "";
- var chr1, chr2, chr3, enc1, enc2, enc3, enc4;
- var i = 0;
- while (i < input.length) {
- chr1 = input[i++];
- chr2 = input[i++];
- chr3 = input[i++];
- enc1 = chr1 >> 2;
- enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
- enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
- enc4 = chr3 & 63;
-
- if (chr2 == undefined) enc3 = enc4 = 64;
- else if (chr3 == undefined) enc4 = 64;
-
- output = output + keyString.charAt(enc1) + keyString.charAt(enc2) + keyString.charAt(enc3) + keyString.charAt(enc4);
- }
- return output;
+ var keyString = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
+ var output = "";
+ var chr1, chr2, chr3, enc1, enc2, enc3, enc4;
+ var i = 0;
+ while (i < input.length) {
+ chr1 = input[i++];
+ chr2 = input[i++];
+ chr3 = input[i++];
+ enc1 = chr1 >> 2;
+ enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
+ enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
+ enc4 = chr3 & 63;
+
+ if (chr2 == undefined) enc3 = enc4 = 64;
+ else if (chr3 == undefined) enc4 = 64;
+
+ output = output + keyString.charAt(enc1) + keyString.charAt(enc2) + keyString.charAt(enc3) + keyString.charAt(enc4);
+ }
+ return output;
};
diff --git a/www/wiki/extensions/Maps/resources/GoogleMaps/ext.sm.googlemaps3ajax.js b/www/wiki/extensions/Maps/resources/GoogleMaps/googlemaps3ajax.js
index 9f8c658a..aa86319e 100644
--- a/www/wiki/extensions/Maps/resources/GoogleMaps/ext.sm.googlemaps3ajax.js
+++ b/www/wiki/extensions/Maps/resources/GoogleMaps/googlemaps3ajax.js
@@ -17,7 +17,7 @@
if( typeof google === 'undefined' ) {
return;
}
- $( window.maps.googlemapsList ).each( function( index, map ) {
+ $( window.mapsGoogleList ).each( function( index, map ) {
if( !map.options.ajaxquery || !map.options.ajaxcoordproperty ) {
return;
}
diff --git a/www/wiki/extensions/Maps/resources/GoogleMaps/jquery.googlemap.js b/www/wiki/extensions/Maps/resources/GoogleMaps/jquery.googlemap.js
index d0317d16..d76460d5 100644
--- a/www/wiki/extensions/Maps/resources/GoogleMaps/jquery.googlemap.js
+++ b/www/wiki/extensions/Maps/resources/GoogleMaps/jquery.googlemap.js
@@ -233,7 +233,9 @@
var geoXml = new geoXML3.parser({
map:_this.map,
zoom:options.kmlrezoom,
- failedParse:function(){
+ failedParse:function(document){
+ console.log(options.kml);
+ console.log(document);
alert(mw.msg('maps-kml-parsing-failed'));
}
});
@@ -454,7 +456,7 @@
};
this.createMarkerCluster = function() {
- if ( !options.markercluster ) {
+ if ( !options.cluster ) {
return;
}
if (this.markercluster) {
@@ -751,7 +753,7 @@
//Add custom controls
// - Fullscreen
- if(options.enablefullscreen){
+ if(options.fullscreen){
this.map.controls[google.maps.ControlPosition.TOP_RIGHT].push(new FullscreenControl(this.map));
}
};
@@ -886,7 +888,7 @@
//Complete path to OpenLayers WMS layer
- if (!options.markercluster) {
+ if (!options.cluster) {
this.setup();
} else {
mw.loader.using( 'ext.maps.gm3.markercluster', function() {
diff --git a/www/wiki/extensions/Maps/resources/MapSaver.js b/www/wiki/extensions/Maps/resources/MapSaver.js
new file mode 100644
index 00000000..2dd22ac1
--- /dev/null
+++ b/www/wiki/extensions/Maps/resources/MapSaver.js
@@ -0,0 +1,57 @@
+(function(mw) {
+ 'use strict';
+
+ function getUserHasPermission(permission, callback) {
+ mw.user.getRights(
+ function(rights) {
+ callback(rights.includes(permission))
+ }
+ );
+ }
+
+ function ifUserHasPermission(permission, callback) {
+ getUserHasPermission(
+ permission,
+ function(hasPermission) {
+ if (hasPermission) {
+ callback();
+ }
+ }
+ );
+ }
+
+ let MapSaver = function(pageName) {
+ let self = {};
+
+ // parameters.newContent: required string
+ // parameters.summary: required string
+ // parameters.done: required callback function
+ self.save = function(paremeters) {
+ new mw.Api().edit(
+ pageName,
+ function(revision) {
+ let editApiParameters = {
+ text: paremeters.newContent,
+ summary: paremeters.summary,
+ minor: false
+ };
+
+ ifUserHasPermission(
+ "applychangetags",
+ function() {
+ editApiParameters.tags = ['maps-visual-edit'];
+ }
+ );
+
+ return editApiParameters;
+ }
+ ).then(paremeters.done);
+ };
+
+ return self;
+ };
+
+ if (!window.maps) {window.maps = {};}
+
+ window.maps.MapSaver = MapSaver;
+})(window.mediaWiki);
diff --git a/www/wiki/extensions/Maps/resources/editor/css/jquery.miniColors.css b/www/wiki/extensions/Maps/resources/WikitextEditor/css/jquery.miniColors.css
index 664c2fd4..664c2fd4 100644
--- a/www/wiki/extensions/Maps/resources/editor/css/jquery.miniColors.css
+++ b/www/wiki/extensions/Maps/resources/WikitextEditor/css/jquery.miniColors.css
diff --git a/www/wiki/extensions/Maps/resources/editor/css/mapeditor.css b/www/wiki/extensions/Maps/resources/WikitextEditor/css/mapeditor.css
index 9c3fe7c4..9c3fe7c4 100644
--- a/www/wiki/extensions/Maps/resources/editor/css/mapeditor.css
+++ b/www/wiki/extensions/Maps/resources/WikitextEditor/css/mapeditor.css
diff --git a/www/wiki/extensions/Maps/resources/editor/images/circle.gif b/www/wiki/extensions/Maps/resources/WikitextEditor/images/circle.gif
index 599f7f13..599f7f13 100644
--- a/www/wiki/extensions/Maps/resources/editor/images/circle.gif
+++ b/www/wiki/extensions/Maps/resources/WikitextEditor/images/circle.gif
Binary files differ
diff --git a/www/wiki/extensions/Maps/resources/editor/images/gradient.png b/www/wiki/extensions/Maps/resources/WikitextEditor/images/gradient.png
index 486a9f6d..486a9f6d 100644
--- a/www/wiki/extensions/Maps/resources/editor/images/gradient.png
+++ b/www/wiki/extensions/Maps/resources/WikitextEditor/images/gradient.png
Binary files differ
diff --git a/www/wiki/extensions/Maps/resources/editor/images/line.gif b/www/wiki/extensions/Maps/resources/WikitextEditor/images/line.gif
index 9eb19837..9eb19837 100644
--- a/www/wiki/extensions/Maps/resources/editor/images/line.gif
+++ b/www/wiki/extensions/Maps/resources/WikitextEditor/images/line.gif
Binary files differ
diff --git a/www/wiki/extensions/Maps/resources/editor/images/rainbow.png b/www/wiki/extensions/Maps/resources/WikitextEditor/images/rainbow.png
index d16fcd86..d16fcd86 100644
--- a/www/wiki/extensions/Maps/resources/editor/images/rainbow.png
+++ b/www/wiki/extensions/Maps/resources/WikitextEditor/images/rainbow.png
Binary files differ
diff --git a/www/wiki/extensions/Maps/resources/editor/images/trigger.png b/www/wiki/extensions/Maps/resources/WikitextEditor/images/trigger.png
index 20ec282b..20ec282b 100644
--- a/www/wiki/extensions/Maps/resources/editor/images/trigger.png
+++ b/www/wiki/extensions/Maps/resources/WikitextEditor/images/trigger.png
Binary files differ
diff --git a/www/wiki/extensions/Maps/resources/editor/js/README b/www/wiki/extensions/Maps/resources/WikitextEditor/js/README
index 53598d48..53598d48 100644
--- a/www/wiki/extensions/Maps/resources/editor/js/README
+++ b/www/wiki/extensions/Maps/resources/WikitextEditor/js/README
diff --git a/www/wiki/extensions/Maps/resources/editor/js/jquery.miniColors.js b/www/wiki/extensions/Maps/resources/WikitextEditor/js/jquery.miniColors.js
index dfbce542..dfbce542 100644
--- a/www/wiki/extensions/Maps/resources/editor/js/jquery.miniColors.js
+++ b/www/wiki/extensions/Maps/resources/WikitextEditor/js/jquery.miniColors.js
diff --git a/www/wiki/extensions/Maps/resources/editor/js/mapeditor.iefixes.js b/www/wiki/extensions/Maps/resources/WikitextEditor/js/mapeditor.iefixes.js
index a49e42fb..a49e42fb 100644
--- a/www/wiki/extensions/Maps/resources/editor/js/mapeditor.iefixes.js
+++ b/www/wiki/extensions/Maps/resources/WikitextEditor/js/mapeditor.iefixes.js
diff --git a/www/wiki/extensions/Maps/resources/editor/js/mapeditor.js b/www/wiki/extensions/Maps/resources/WikitextEditor/js/mapeditor.js
index 795fcc53..eb2efd61 100644
--- a/www/wiki/extensions/Maps/resources/editor/js/mapeditor.js
+++ b/www/wiki/extensions/Maps/resources/WikitextEditor/js/mapeditor.js
@@ -56,7 +56,7 @@ var mapEditor = {
copycoords: {
values:['on','off']
},
- markercluster: {
+ cluster: {
values:['on','off']
},
searchmarkers:{
diff --git a/www/wiki/extensions/Maps/resources/api.js b/www/wiki/extensions/Maps/resources/api.js
new file mode 100644
index 00000000..5cb70741
--- /dev/null
+++ b/www/wiki/extensions/Maps/resources/api.js
@@ -0,0 +1,44 @@
+(function($, mw) {
+ 'use strict';
+
+ function canEditPage(pageName) {
+ let deferred = $.Deferred();
+
+ new mw.Api().get({
+ action: 'query',
+ format: 'json',
+ titles: pageName,
+ prop: 'info',
+ intestactions: 'edit'
+ }).done(function(response) {
+ // Next level usability in the MW API:
+ let canEdit = response.query.pages[Object.keys(response.query.pages)[0]].actions.hasOwnProperty('edit');
+ deferred.resolve(canEdit);
+ });
+
+ return deferred.promise();
+ }
+
+ function getLatestRevision(pageName) {
+ let deferred = $.Deferred();
+
+ new mw.Api().post({
+ action: 'query',
+ prop: 'revisions',
+ rvlimit: 1,
+ rvprop: [ 'ids', 'content' ],
+ titles: pageName
+ }).done(function(response) {
+ deferred.resolve(response.query.pages[Object.keys(response.query.pages)[0]].revisions[0]);
+ });
+
+ return deferred.promise();
+ }
+
+ if (!window.maps) {window.maps = {};}
+
+ window.maps.api = {
+ canEditPage: canEditPage,
+ getLatestRevision: getLatestRevision
+ };
+})(window.jQuery, window.mediaWiki);
diff --git a/www/wiki/extensions/Maps/resources/geoJsonPage.js b/www/wiki/extensions/Maps/resources/geoJsonPage.js
new file mode 100644
index 00000000..8c6c7c14
--- /dev/null
+++ b/www/wiki/extensions/Maps/resources/geoJsonPage.js
@@ -0,0 +1,96 @@
+(function( $, mw, maps ) {
+
+ function hideLoadingMessage(map, $content) {
+ map.on(
+ 'load',
+ function() {
+ $content.find('div.maps-loading-message').hide();
+ }
+ );
+ }
+
+ function addZoomControl(map) {
+ map.addControl(new L.Control.Zoom());
+ }
+
+ function addTitleLayer(map) {
+ L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
+ attribution: '&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
+ }).addTo(map);
+ }
+
+ function fitContent(map, geoJsonLayer) {
+ map.fitWorld();
+ let bounds = geoJsonLayer.getBounds();
+
+ if (bounds.isValid()) {
+ if (bounds.getNorthEast().equals(bounds.getSouthWest())) {
+ map.setView(
+ bounds.getCenter(),
+ 14
+ );
+ }
+ else {
+ map.fitBounds(bounds);
+ }
+ }
+ }
+
+ function initializeWithEditor(map) {
+ let editor = maps.leaflet.LeafletEditor(
+ map,
+ new maps.MapSaver(mw.config.get('wgPageName'))
+ );
+
+ editor.onSaved(function() {
+ alert(mw.msg('maps-json-editor-changes-saved'));
+ });
+
+ editor.initialize(window.GeoJson);
+
+ fitContent(map, editor.getLayer());
+ }
+
+ function initializePlainMap(map) {
+ fitContent(
+ map,
+ maps.leaflet.GeoJson.newGeoJsonLayer(L, window.GeoJson).addTo(map)
+ );
+ }
+
+ function initializeGeoJsonAndEditorUi(map) {
+ if (mw.config.get('wgCurRevisionId') === mw.config.get('wgRevisionId')) {
+
+ maps.api.canEditPage(mw.config.get('wgPageName')).done(
+ function(canEdit) {
+ if (canEdit) {
+ initializeWithEditor(map);
+ }
+ else {
+ initializePlainMap(map);
+ }
+ }
+ );
+ }
+ else {
+ initializePlainMap(map);
+ }
+ }
+
+ mw.hook( 'wikipage.content' ).add( function ( $content ) {
+ let map = L.map(
+ 'GeoJsonMap',
+ {
+ fullscreenControl: true,
+ fullscreenControlOptions: {position: 'topright'},
+ zoomControl: false
+ }
+ );
+
+ hideLoadingMessage(map, $content);
+ addZoomControl(map);
+ addTitleLayer(map);
+ initializeGeoJsonAndEditorUi(map);
+ } );
+
+})( window.jQuery, window.mediaWiki, window.maps );
diff --git a/www/wiki/extensions/Maps/resources/geojson.new.page.js b/www/wiki/extensions/Maps/resources/geojson.new.page.js
new file mode 100644
index 00000000..30980a7a
--- /dev/null
+++ b/www/wiki/extensions/Maps/resources/geojson.new.page.js
@@ -0,0 +1,38 @@
+$( document ).ready( function() {
+ function getErrorMessageForFailure(failureReason) {
+ if (failureReason === 'assertuserfailed') {
+ return 'Could not create page because your session expired. The page will be reloaded';
+ }
+
+ return 'Failed to create the page: ' + failureReason;
+ }
+
+ $('#maps-geojson-new').click(
+ function() {
+ $(this).prop('disabled', true);
+ $(this).text(mw.msg('maps-geo-json-create-page-creating'));
+
+ new mw.Api().create(
+ mw.config.get('wgPageName'),
+ {
+ summary: mw.msg('maps-geo-json-create-page-summary')
+ },
+ '{"type": "FeatureCollection", "features": []}'
+ ).then(
+ function(editData) {
+ if (editData.result !== 'Success') {
+ console.log(editData);
+ alert('Failed to create the page');
+ }
+
+ location.reload();
+ }
+ ).fail(
+ function(reason) {
+ alert(getErrorMessageForFailure(reason));
+ location.reload();
+ }
+ );
+ }
+ );
+} );
diff --git a/www/wiki/extensions/Maps/resources/leaflet/FeatureBuilder.js b/www/wiki/extensions/Maps/resources/leaflet/FeatureBuilder.js
new file mode 100644
index 00000000..fad0adae
--- /dev/null
+++ b/www/wiki/extensions/Maps/resources/leaflet/FeatureBuilder.js
@@ -0,0 +1,220 @@
+// Builds markers, polygons, etc from the options object (serialized data coming from PHP)
+(function($, mw) {
+ 'use strict';
+
+ /**
+ * Creates a new marker with the provided data and returns it.
+ * @param {Object} properties Contains the fields lat, lon, title, text and icon
+ * @param {Object} options Map options
+ * @return {L.Marker}
+ */
+ function createMarker(properties, options) {
+ let markerOptions = {
+ title:properties.title
+ };
+
+ let marker = L.marker( [properties.lat, properties.lon], markerOptions );
+
+ if (properties.hasOwnProperty('icon') && properties.icon !== '') {
+ marker.setOpacity(0);
+
+ let img = new Image();
+ img.onload = function() {
+ let icon = new L.Icon({
+ iconUrl: properties.icon,
+ iconSize: [ img.width, img.height ],
+ iconAnchor: [ img.width / 2, img.height ],
+ popupAnchor: [ -img.width % 2, -img.height*2/3 ]
+ });
+
+ marker.setIcon(icon);
+ marker.setOpacity(1);
+ };
+ img.src = properties.icon;
+ }
+
+ if( properties.hasOwnProperty('text') && properties.text.length > 0 ) {
+ marker.bindPopup( properties.text );
+ }
+
+ if ( options.copycoords ) {
+ marker.on(
+ 'contextmenu',
+ function( e ) {
+ prompt(mw.msg('maps-copycoords-prompt'), e.latlng.lat + ',' + e.latlng.lng);
+ }
+ );
+ }
+
+ return marker;
+ }
+
+ function newLineFromProperties(properties) {
+ var latlngs = [];
+
+ for (var x = 0; x < properties.pos.length; x++) {
+ latlngs.push([properties.pos[x].lat, properties.pos[x].lon]);
+ }
+
+ let line = L.polyline(
+ latlngs,
+ {
+ color: properties.strokeColor,
+ weight: properties.strokeWeight,
+ opacity: properties.strokeOpacity
+ }
+ );
+
+ // TODO: maybe bind via feature group
+ if ( properties.hasOwnProperty('text') && properties.text.trim().length > 0 ) {
+ line.bindPopup( properties.text );
+ }
+
+ return line;
+ }
+
+ function newPolygonFromProperties(properties) {
+ let polygon = L.polygon(
+ properties.pos.map(function(position) {
+ return [position.lat, position.lon];
+ }),
+ {
+ color: properties.strokeColor,
+ weight:properties.strokeWeight,
+ opacity:properties.strokeOpacity,
+ fillColor:properties.fillColor,
+ fillOpacity:properties.fillOpacity
+ }
+ );
+
+ if( properties.hasOwnProperty('text') && properties.text.trim().length > 0 ) {
+ polygon.bindPopup( properties.text );
+ }
+
+ return polygon;
+ }
+
+ function newCircleFromProperties(properties) {
+ let circle = L.circle(
+ [properties.centre.lat, properties.centre.lon],
+ {
+ radius: properties.radius,
+ color: properties.strokeColor,
+ weight:properties.strokeWeight,
+ opacity:properties.strokeOpacity,
+ fillColor:properties.fillColor,
+ fillOpacity:properties.fillOpacity,
+ }
+ );
+
+ if( properties.hasOwnProperty('text') && properties.text.trim().length > 0 ) {
+ circle.bindPopup( properties.text );
+ }
+
+ return circle;
+ }
+
+ function newRectangleFromProperties(properties) {
+ let rectangle = L.rectangle(
+ [
+ [properties.sw.lat, properties.sw.lon],
+ [properties.ne.lat, properties.ne.lon]
+ ],
+ {
+ color: properties.strokeColor,
+ weight: properties.strokeWeight,
+ opacity: properties.strokeOpacity,
+ fillColor: properties.fillColor,
+ fillOpacity: properties.fillOpacity
+ }
+ );
+
+ if( properties.hasOwnProperty('text') && properties.text.trim().length > 0 ) {
+ rectangle.bindPopup( properties.text );
+ }
+
+ return rectangle;
+ }
+
+ /**
+ * Caution: mutates markerLayer
+ * @param {Object} options
+ * @param {L.LayerGroup} markerLayer
+ * @return {L.GeoJSON}
+ */
+ function newGeoJsonLayer(options, markerLayer) {
+ return L.geoJSON(
+ options.geojson,
+ {
+ style: function (feature) {
+ return maps.leaflet.GeoJson.simpleStyleToLeafletPathOptions(feature.properties);
+ },
+ pointToLayer: function(feature, latlng) {
+ markerLayer.addLayer(
+ createMarker(
+ {
+ lat: latlng.lat,
+ lon: latlng.lng,
+ title: feature.properties.title || '',
+ text: maps.leaflet.GeoJson.popupContentFromProperties(feature.properties),
+ icon: ''
+ },
+ options
+ )
+ );
+ },
+ onEachFeature: function (feature, layer) {
+ if (feature.geometry.type !== 'Point') {
+ let popupContent = maps.leaflet.GeoJson.popupContentFromProperties(feature.properties);
+ if (popupContent !== '') {
+ layer.bindPopup(popupContent);
+ }
+ }
+ }
+ }
+ );
+ }
+
+ function getMarkersAndShapes(options) {
+ let features = L.featureGroup();
+
+ $.each(options.lines, function(index, properties) {
+ features.addLayer(newLineFromProperties(properties));
+ });
+
+ $.each(options.polygons, function(index, properties) {
+ features.addLayer(newPolygonFromProperties(properties));
+ });
+
+ $.each(options.circles, function(index, properties) {
+ features.addLayer(newCircleFromProperties(properties));
+ });
+
+ $.each(options.rectangles, function(index, properties) {
+ features.addLayer(newRectangleFromProperties(properties));
+ });
+
+ let markers = options.cluster ? maps.leaflet.LeafletCluster.newLayer(options) : L.featureGroup();
+
+ features.addLayer(markers);
+ features.markerLayer = markers;
+
+ $.each(options.locations, function(index, properties) {
+ markers.addLayer(createMarker(properties, options));
+ });
+
+ if (options.geojson !== '') {
+ features.addLayer(newGeoJsonLayer(options, markers));
+ }
+
+ return features
+ }
+
+ if (!window.maps) {window.maps = {};}
+ if (!window.maps.leaflet) {window.maps.leaflet = {};}
+
+ window.maps.leaflet.FeatureBuilder = {
+ contentLayerFromOptions: getMarkersAndShapes,
+ createMarker: createMarker
+ };
+})(window.jQuery, window.mediaWiki);
diff --git a/www/wiki/extensions/Maps/resources/leaflet/GeoJson.js b/www/wiki/extensions/Maps/resources/leaflet/GeoJson.js
new file mode 100644
index 00000000..1914738f
--- /dev/null
+++ b/www/wiki/extensions/Maps/resources/leaflet/GeoJson.js
@@ -0,0 +1,74 @@
+(function() {
+ 'use strict';
+
+ let GeoJson = {};
+
+ // https://github.com/Leaflet/Leaflet/blob/f8e09f993292579a1af88261c9b461730f22e4e6/src/layer/GeoJSON.js#L49-L57
+ // https://github.com/mapbox/simplestyle-spec/tree/master/1.1.0
+ // https://leafletjs.com/reference-1.6.0.html#path
+ GeoJson.simpleStyleToLeafletPathOptions = function(featureProperties) {
+ let simpleStyleToLeaflet = {
+ 'stroke': 'color',
+ 'stroke-width': 'weight',
+ 'stroke-opacity': 'opacity',
+ 'fill': 'fillColor',
+ 'fill-opacity': 'fillOpacity',
+ };
+
+ let pathOptions = {};
+
+ for (let [key, value] of Object.entries(simpleStyleToLeaflet)) {
+ if (featureProperties[key]) {
+ pathOptions[value] = featureProperties[key];
+ }
+ }
+
+ return pathOptions;
+ };
+
+ function escapeHTML(unsafeText) {
+ let div = document.createElement('div');
+ div.innerText = unsafeText;
+ return div.innerHTML;
+ }
+
+ GeoJson.popupContentFromProperties = function(properties) {
+ if (!properties.title && !properties.description) {
+ return '';
+ }
+
+ if (!properties.description) {
+ return escapeHTML(properties.title);
+ }
+
+ if (!properties.title) {
+ return escapeHTML(properties.description);
+ }
+
+ // CHANGE: allows html format in description of GeoJSON element
+ return '<strong>' + escapeHTML(properties.title) + '</strong><br><br>'
+ + properties.description;
+ };
+
+ GeoJson.newGeoJsonLayer = function(L, json) {
+ return L.geoJSON(
+ json,
+ {
+ style: function (feature) {
+ return GeoJson.simpleStyleToLeafletPathOptions(feature.properties);
+ },
+ onEachFeature: function (feature, layer) {
+ let popupContent = GeoJson.popupContentFromProperties(feature.properties);
+ if (popupContent !== '') {
+ layer.bindPopup(popupContent);
+ }
+ }
+ }
+ );
+ };
+
+ if (!window.maps) {window.maps = {};}
+ if (!window.maps.leaflet) {window.maps.leaflet = {};}
+
+ window.maps.leaflet.GeoJson = GeoJson;
+})();
diff --git a/www/wiki/extensions/Maps/resources/leaflet/LeafletCluster.js b/www/wiki/extensions/Maps/resources/leaflet/LeafletCluster.js
new file mode 100644
index 00000000..e6ec2566
--- /dev/null
+++ b/www/wiki/extensions/Maps/resources/leaflet/LeafletCluster.js
@@ -0,0 +1,69 @@
+(function($, mw, L) {
+ 'use strict';
+
+ if (!window.maps) {window.maps = {};}
+ if (!window.maps.leaflet) {window.maps.leaflet = {};}
+
+ window.maps.leaflet.LeafletCluster = {
+ newLayer: function(options) {
+ return new L.MarkerClusterGroup({
+ maxClusterRadius: options.clustermaxradius,
+ disableClusteringAtZoom: options.clustermaxzoom + 1,
+ zoomToBoundsOnClick: options.clusterzoomonclick,
+ spiderfyOnMaxZoom: options.clusterspiderfy,
+ iconCreateFunction: function(cluster) {
+ var childCount = cluster.getChildCount();
+
+ var imagePath = mw.config.get( 'egMapsScriptPath' ) + '/resources/leaflet/cluster/';
+
+ var styles = [
+ {
+ iconUrl: imagePath + 'm1.png',
+ iconSize: [53, 52]
+ },
+ {
+ iconUrl: imagePath + 'm2.png',
+ iconSize: [56, 55]
+ },
+ {
+ iconUrl: imagePath + 'm3.png',
+ iconSize: [66, 65]
+ },
+ {
+ iconUrl: imagePath + 'm4.png',
+ iconSize: [78, 77]
+ },
+ {
+ iconUrl: imagePath + 'm5.png',
+ iconSize: [90, 89]
+ }
+ ];
+
+ var index = 0;
+ var dv = childCount;
+ while (dv !== 0) {
+ dv = parseInt(dv / 10, 10);
+ index++;
+ }
+ var index = Math.min(index, styles.length);
+ index = Math.max(0, index - 1);
+ index = Math.min(styles.length - 1, index);
+ var style = styles[index];
+
+ return new L.divIcon({
+ iconSize: style.iconSize,
+ className: '',
+ html: '<img style="' +
+ '" src="' + style.iconUrl + '" />' +
+ '<span style="' +
+ 'position: absolute; font-size: 11px; font-weight: bold; text-align: center; ' +
+ 'top: 0; left: 0; ' +
+ 'line-height: ' + style.iconSize[1] + 'px;' +
+ 'width: ' + style.iconSize[0] + 'px;' +
+ '">' + childCount + '</span>'
+ });
+ }
+ });
+ }
+ };
+})(window.jQuery, window.mediaWiki, window.L);
diff --git a/www/wiki/extensions/Maps/resources/leaflet/LeafletEditor.js b/www/wiki/extensions/Maps/resources/leaflet/LeafletEditor.js
new file mode 100644
index 00000000..8c8e1398
--- /dev/null
+++ b/www/wiki/extensions/Maps/resources/leaflet/LeafletEditor.js
@@ -0,0 +1,256 @@
+(function( $, mw, maps ) {
+
+ function initializeMessages() {
+ let buttons = L.drawLocal.draw.toolbar.buttons;
+
+ buttons.marker = mw.msg('maps-json-editor-button-marker');
+ buttons.polyline = mw.msg('maps-json-editor-button-line');
+ buttons.polygon = mw.msg('maps-json-editor-button-polygon');
+ buttons.rectangle = mw.msg('maps-json-editor-button-rectangle');
+ buttons.circle = mw.msg('maps-json-editor-button-circle');
+
+ let handlers = L.drawLocal.draw.handlers;
+
+ handlers.marker.tooltip.start = mw.msg('maps-json-editor-tooltip-marker');
+ handlers.polyline.tooltip.start = mw.msg('maps-json-editor-tooltip-line');
+ handlers.polygon.tooltip.start = mw.msg('maps-json-editor-tooltip-polygon');
+ handlers.rectangle.tooltip.start = mw.msg('maps-json-editor-tooltip-rectangle');
+ handlers.circle.tooltip.start = mw.msg('maps-json-editor-tooltip-circle');
+
+ let toolbar = L.drawLocal.edit.toolbar;
+
+ toolbar.actions.save.title = mw.msg('maps-json-editor-toolbar-save-title');
+ toolbar.actions.save.text = mw.msg('maps-json-editor-toolbar-save-text');
+ toolbar.actions.cancel.title = mw.msg('maps-json-editor-toolbar-cancel-title');
+ toolbar.actions.cancel.text = mw.msg('maps-json-editor-toolbar-cancel-text');
+ toolbar.actions.clearAll.title = mw.msg('maps-json-editor-toolbar-clear-title');
+ toolbar.actions.clearAll.text = mw.msg('maps-json-editor-toolbar-clear-text');
+
+ toolbar.buttons.edit = mw.msg('maps-json-editor-toolbar-button-edit');
+ toolbar.buttons.editDisabled = mw.msg('maps-json-editor-toolbar-button-edit-disabled');
+ toolbar.buttons.remove = mw.msg('maps-json-editor-toolbar-button-remove');
+ toolbar.buttons.removeDisabled = mw.msg('maps-json-editor-toolbar-button-remove-disabled');
+ }
+
+ let MapEditor = function(map, mapSaver) {
+ let self = {
+ isFirstInitialization: true,
+ unsavedChanges: false
+ };
+
+ self.initialize = function(json) {
+ self.map = map;
+
+ self.geoJsonLayer = self.newGeoJsonLayer(json).addTo(self.map);
+ self.addDrawControls();
+
+ self.firstInitialize();
+ };
+
+ self.firstInitialize = function() {
+ if (!self.isFirstInitialization) {
+ return;
+ }
+
+ self.isFirstInitialization = false;
+
+ self.map.on(
+ L.Draw.Event.CREATED,
+ function (event) {
+ self.geoJsonLayer.addData(event.layer.toGeoJSON());
+ self._showSaveButton();
+ }
+ );
+
+ self.map.on(
+ L.Draw.Event.EDITED,
+ self._showSaveButton
+ );
+
+ self.map.on(
+ L.Draw.Event.DELETED,
+ self._showSaveButton
+ );
+
+ $(window).bind('beforeunload', function() {
+ if (self.unsavedChanges) {
+ return 'The map has unsaved changes. Are you sure you want to leave the page?';
+ }
+ });
+
+ initializeMessages();
+ };
+
+ self.newGeoJsonLayer = function(json) {
+ return L.geoJSON(
+ json,
+ {
+ style: function (feature) {
+ return maps.leaflet.GeoJson.simpleStyleToLeafletPathOptions(feature.properties);
+ },
+ onEachFeature: self._onEditableFeature
+ }
+ );
+ };
+
+ self.newSaveButton = function() {
+ return L.easyButton(
+ '<img src="' + mw.config.get('egMapsScriptPath') + 'resources/leaflet/images/save-solid.svg">',
+ function() {
+ let editSummary = prompt(
+ 'Enter an edit summary for your changes to the map',
+ 'Visual map edit'
+ ); // TODO: i18n
+
+ if (editSummary!== null) {
+ self.saveButton.remove();
+
+ mapSaver.save(
+ {
+ newContent: JSON.stringify(self.geoJsonLayer.toGeoJSON()),
+ summary: editSummary,
+ done: function(response) {
+ if (response.result === 'Success') {
+ self.unsavedChanges = false;
+ self.onSaved();
+ }
+ else {
+ console.log(response);
+ self._showSaveButton();
+ alert(mw.msg('maps-json-editor-edit-failed'));
+ }
+ }
+ }
+ );
+ }
+ },
+ mw.msg('maps-json-editor-toolbar-button-save')
+ );
+ };
+
+ self._showSaveButton = function() {
+ self.unsavedChanges = true;
+
+ if (!self.saveButton) {
+ self.saveButton = self.newSaveButton();
+ }
+
+ self.saveButton.addTo(self.map);
+ };
+
+ function onSizeChange(element, callback) {
+ let currentWidth = element[0].clientWidth;
+ let currentHeight = element[0].clientHeight;
+
+ element.bind('mouseup', function() {
+ if (element[0].clientWidth !== currentWidth || element[0].clientHeight !== currentHeight) {
+ currentWidth = element[0].clientWidth;
+ currentHeight = element[0].clientHeight;
+ callback(element);
+ }
+ });
+ }
+
+ self._onEditableFeature = function(feature, layer) {
+ let titleInput = $('<textarea cols="50" rows="1" />').text(feature.properties.title);
+ let descriptionInput = $('<textarea cols="50" rows="2" />').text(feature.properties.description);
+ let button = $('<button style="width: 100%">').text(mw.msg('maps-json-editor-toolbar-save-text'));
+
+ layer.on("popupopen", function () {
+ let v = titleInput.val();
+ titleInput.focus().val('').val(v);
+ });
+
+ let popup = L.popup({
+ minWidth: 250,
+ maxWidth: 9000,
+ keepInView: true,
+ closeButton: false,
+ autoClose: false,
+ closeOnEscapeKey: false,
+ closeOnClick: false
+ });
+
+ let onSizeChangedHandler = function(element) {
+ popup.update(); element.focus();
+ };
+
+ onSizeChange(titleInput, onSizeChangedHandler);
+ onSizeChange(descriptionInput, onSizeChangedHandler);
+
+ button.click(function() {
+ popup.remove();
+
+ if (titleInput.val() !== titleInput.text() || descriptionInput.val() !== descriptionInput.text()) {
+ feature.properties["title"] = titleInput.val();
+ feature.properties["description"] = descriptionInput.val();
+ self._showSaveButton();
+ }
+ });
+
+ popup.setContent($('<div />').append(titleInput, descriptionInput, button)[0]);
+ layer.bindPopup(popup);
+ };
+
+ self.addDrawControls = function() {
+ // self.map.addControl(L.control.styleEditor({
+ // position: "topleft",
+ // useGrouping: false,
+ // openOnLeafletDraw: false,
+ // }));
+
+ if (!self.drawControl) {
+ self.drawControl = self.newDrawControl();
+ }
+
+ self.drawControl.addTo(self.map);
+ };
+
+ self.newDrawControl = function() {
+ return new L.Control.Draw({
+ edit: {
+ featureGroup: self.geoJsonLayer,
+ poly: {
+ allowIntersection: true
+ }
+ },
+ draw: {
+ polygon: {
+ allowIntersection: true,
+ showArea: true
+ },
+ circlemarker: false, // Do not want this one
+ circle: false // Is not showing properly after save
+ }
+ })
+ };
+
+ self.remove = function() {
+ self.drawControl.remove();
+ self.saveButton.remove();
+ self.geoJsonLayer.remove();
+ };
+
+ self.onSaved = function() {};
+
+ let exports = {};
+
+ exports.initialize = self.initialize;
+ exports.remove = self.remove;
+
+ exports.getLayer = function() {
+ return self.geoJsonLayer;
+ };
+
+ exports.onSaved = function(f) {
+ self.onSaved = f;
+ };
+
+ return exports;
+ };
+
+ if (!maps.leaflet) {maps.leaflet = {};}
+
+ maps.leaflet.LeafletEditor = MapEditor;
+
+})( window.jQuery, window.mediaWiki, window.maps );
diff --git a/www/wiki/extensions/Maps/resources/leaflet/LeafletLoader.js b/www/wiki/extensions/Maps/resources/leaflet/LeafletLoader.js
new file mode 100644
index 00000000..15776cf7
--- /dev/null
+++ b/www/wiki/extensions/Maps/resources/leaflet/LeafletLoader.js
@@ -0,0 +1,15 @@
+window.mapsLeafletList = [];
+
+(function( $, mw ) {
+ mw.hook( 'wikipage.content' ).add( function ( $content ) {
+ $content.find( '.maps-leaflet' ).each( function() {
+ let $this = $( this );
+
+ let jqueryMap = $this.leafletmaps(
+ JSON.parse( $this.find( 'div.mapdata' ).text() )
+ );
+
+ window.mapsLeafletList.push(jqueryMap);
+ } );
+ } );
+})( window.jQuery, window.mediaWiki );
diff --git a/www/wiki/extensions/Maps/resources/leaflet/cluster/m1.png b/www/wiki/extensions/Maps/resources/leaflet/cluster/m1.png
index 3346508f..329ff524 100644
--- a/www/wiki/extensions/Maps/resources/leaflet/cluster/m1.png
+++ b/www/wiki/extensions/Maps/resources/leaflet/cluster/m1.png
Binary files differ
diff --git a/www/wiki/extensions/Maps/resources/leaflet/cluster/m2.png b/www/wiki/extensions/Maps/resources/leaflet/cluster/m2.png
index 64786829..b999cbcf 100644
--- a/www/wiki/extensions/Maps/resources/leaflet/cluster/m2.png
+++ b/www/wiki/extensions/Maps/resources/leaflet/cluster/m2.png
Binary files differ
diff --git a/www/wiki/extensions/Maps/resources/leaflet/cluster/m3.png b/www/wiki/extensions/Maps/resources/leaflet/cluster/m3.png
index 772ed91a..9f30b309 100644
--- a/www/wiki/extensions/Maps/resources/leaflet/cluster/m3.png
+++ b/www/wiki/extensions/Maps/resources/leaflet/cluster/m3.png
Binary files differ
diff --git a/www/wiki/extensions/Maps/resources/leaflet/cluster/m4.png b/www/wiki/extensions/Maps/resources/leaflet/cluster/m4.png
index eb33557a..0d3f8263 100644
--- a/www/wiki/extensions/Maps/resources/leaflet/cluster/m4.png
+++ b/www/wiki/extensions/Maps/resources/leaflet/cluster/m4.png
Binary files differ
diff --git a/www/wiki/extensions/Maps/resources/leaflet/cluster/m5.png b/www/wiki/extensions/Maps/resources/leaflet/cluster/m5.png
index 6ca9c490..61387d2a 100644
--- a/www/wiki/extensions/Maps/resources/leaflet/cluster/m5.png
+++ b/www/wiki/extensions/Maps/resources/leaflet/cluster/m5.png
Binary files differ
diff --git a/www/wiki/extensions/Maps/resources/leaflet/ext.maps.leaflet.js b/www/wiki/extensions/Maps/resources/leaflet/ext.maps.leaflet.js
deleted file mode 100644
index 087d4acf..00000000
--- a/www/wiki/extensions/Maps/resources/leaflet/ext.maps.leaflet.js
+++ /dev/null
@@ -1,4 +0,0 @@
-
-mediaWiki.loader.using( [ 'ext.maps.leaflet' ] ).done( function () {
- ( new maps.services( jQuery( document ) ) ).leaflet();
-} ); \ No newline at end of file
diff --git a/www/wiki/extensions/Maps/resources/leaflet/ext.sm.leafletajax.js b/www/wiki/extensions/Maps/resources/leaflet/ext.sm.leafletajax.js
deleted file mode 100644
index fb9f7341..00000000
--- a/www/wiki/extensions/Maps/resources/leaflet/ext.sm.leafletajax.js
+++ /dev/null
@@ -1,44 +0,0 @@
-/**
- * JavaScript for Leaflet in the Semantic Maps extension.
- * @see https://www.mediawiki.org/wiki/Extension:Semantic_Maps
- *
- * @licence GNU GPL v2+
- * @author Peter Grassberger < petertheone@gmail.com >
- */
-
-
-(function( $, sm ) {
- var ajaxRequest = null;
-
- var mapEvents = ['dragend', 'zoomend'];
-
- $( document ).ready( function() {
- // todo: find a way to remove setTimeout.
- setTimeout( function() {
- $( window.maps.leafletList ).each( function( index, map ) {
- if( !map.options.ajaxquery || !map.options.ajaxcoordproperty ) {
- return;
- }
- map.map.on( mapEvents.join( ' ' ), function() {
- var bounds = map.map.getBounds();
- var query = sm.buildQueryString(
- decodeURIComponent( map.options.ajaxquery.replace( /\+/g, ' ' ) ),
- map.options.ajaxcoordproperty,
- bounds.getNorthEast().lat,
- bounds.getNorthEast().lng,
- bounds.getSouthWest().lat,
- bounds.getSouthWest().lng
- );
-
- if( ajaxRequest !== null ) {
- ajaxRequest.abort();
- }
- ajaxRequest = sm.ajaxUpdateMarker( map, query, map.options.icon ).done( function() {
- map.createMarkerCluster();
- ajaxRequest = null;
- } );
- } );
- } );
- }, 1000 );
- } );
-})( window.jQuery, window.sm );
diff --git a/www/wiki/extensions/Maps/resources/leaflet/images/edit-solid.svg b/www/wiki/extensions/Maps/resources/leaflet/images/edit-solid.svg
new file mode 100644
index 00000000..e757323b
--- /dev/null
+++ b/www/wiki/extensions/Maps/resources/leaflet/images/edit-solid.svg
@@ -0,0 +1 @@
+<svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="edit" class="svg-inline--fa fa-edit fa-w-18" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512"><path fill="currentColor" d="M402.6 83.2l90.2 90.2c3.8 3.8 3.8 10 0 13.8L274.4 405.6l-92.8 10.3c-12.4 1.4-22.9-9.1-21.5-21.5l10.3-92.8L388.8 83.2c3.8-3.8 10-3.8 13.8 0zm162-22.9l-48.8-48.8c-15.2-15.2-39.9-15.2-55.2 0l-35.4 35.4c-3.8 3.8-3.8 10 0 13.8l90.2 90.2c3.8 3.8 10 3.8 13.8 0l35.4-35.4c15.2-15.3 15.2-40 0-55.2zM384 346.2V448H64V128h229.8c3.2 0 6.2-1.3 8.5-3.5l40-40c7.6-7.6 2.2-20.5-8.5-20.5H48C21.5 64 0 85.5 0 112v352c0 26.5 21.5 48 48 48h352c26.5 0 48-21.5 48-48V306.2c0-10.7-12.9-16-20.5-8.5l-40 40c-2.2 2.3-3.5 5.3-3.5 8.5z"></path></svg> \ No newline at end of file
diff --git a/www/wiki/extensions/Maps/resources/leaflet/images/save-solid.svg b/www/wiki/extensions/Maps/resources/leaflet/images/save-solid.svg
new file mode 100644
index 00000000..a27b3f55
--- /dev/null
+++ b/www/wiki/extensions/Maps/resources/leaflet/images/save-solid.svg
@@ -0,0 +1 @@
+<svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="save" class="svg-inline--fa fa-save fa-w-14" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><path fill="currentColor" d="M433.941 129.941l-83.882-83.882A48 48 0 0 0 316.118 32H48C21.49 32 0 53.49 0 80v352c0 26.51 21.49 48 48 48h352c26.51 0 48-21.49 48-48V163.882a48 48 0 0 0-14.059-33.941zM224 416c-35.346 0-64-28.654-64-64 0-35.346 28.654-64 64-64s64 28.654 64 64c0 35.346-28.654 64-64 64zm96-304.52V212c0 6.627-5.373 12-12 12H76c-6.627 0-12-5.373-12-12V108c0-6.627 5.373-12 12-12h228.52c3.183 0 6.235 1.264 8.485 3.515l3.48 3.48A11.996 11.996 0 0 1 320 111.48z"></path></svg> \ No newline at end of file
diff --git a/www/wiki/extensions/Maps/resources/leaflet/jquery.leaflet.js b/www/wiki/extensions/Maps/resources/leaflet/jquery.leaflet.js
index 3483ec3a..5012c766 100644
--- a/www/wiki/extensions/Maps/resources/leaflet/jquery.leaflet.js
+++ b/www/wiki/extensions/Maps/resources/leaflet/jquery.leaflet.js
@@ -4,423 +4,321 @@
*
* @author Pavel Astakhov < pastakhov@yandex.ru >
* @author Peter Grassberger < petertheone@gmail.com >
+ * @author Jeroen De Dauw
*/
-(function ($, mw, L, MQ) {
- $.fn.leafletmaps = function ( options ) {
- var _this = this;
- this.map = null;
- this.options = options;
- this.markers = [];
- this.markercluster = null;
- var apikeys = mw.config.get('egMapsLeafletLayersApiKeys') ;
-
- /**
- * array point of all map elements (markers, lines, polygons, etc.)
- * for map fit
- */
- this.points = [];
-
- /**
- * Creates a new marker with the provided data and returns it.
- * @param {Object} properties Contains the fields lat, lon, title, text and icon
- * @return {L.Marker}
- */
- this.createMarker = function (properties) {
- this.points.push( new L.LatLng(properties.lat, properties.lon) );
-
- var markerOptions = {
- title:properties.title
+(function ($, mw, L, maps, sm) {
+
+ function getMapOptions(options) {
+ let mapOptions = {};
+ if (options.minzoom !== false) mapOptions.minZoom = options.minzoom;
+ if (options.maxzoom !== false) mapOptions.maxZoom = options.maxzoom;
+
+ if (options.fullscreen) {
+ mapOptions.fullscreenControl = true;
+ mapOptions.fullscreenControlOptions= {
+ position: 'topleft'
};
+ }
- var marker = L.marker( [properties.lat, properties.lon], markerOptions );
-
- if (properties.hasOwnProperty('icon') && properties.icon !== '') {
- marker.setOpacity(0);
-
- var img = new Image();
- img.onload = function() {
- var icon = new L.Icon({
- iconUrl: properties.icon,
- iconSize: [ img.width, img.height ],
- iconAnchor: [ img.width / 2, img.height ],
- popupAnchor: [ -img.width % 2, -img.height*2/3 ]
- });
-
- marker.setIcon(icon);
- marker.setOpacity(1);
- };
- img.src = properties.icon;
- }
+ mapOptions.scrollWheelZoom = options.scrollwheelzoom;
- if( properties.hasOwnProperty('text') && properties.text.length > 0 ) {
- marker.bindPopup( properties.text );
- }
+ if (options.static) {
+ mapOptions.scrollWheelZoom = false;
+ mapOptions.doubleClickZoom = false;
+ mapOptions.touchZoom = false;
+ mapOptions.boxZoom = false;
+ mapOptions.tap = false;
+ mapOptions.keyboard = false;
+ mapOptions.zoomControl = false;
+ mapOptions.dragging = false;
+ }
- if ( options.copycoords ) {
- marker.on(
- 'contextmenu',
- function( e ) {
- prompt(mw.msg('maps-copycoords-prompt'), e.latlng.lat + ',' + e.latlng.lng);
- }
- );
- }
-
- return marker;
- };
+ return mapOptions;
+ }
- /**
- * Creates a new marker with the provided data, adds it to the map
- * and returns it.
- * @param {Object} properties Contains the fields lat, lon, title, text and icon
- * @return {L.Marker}
- */
- this.addMarker = function (properties) {
- var marker = this.createMarker(properties);
- if (!this.options.markercluster) {
- marker.addTo( this.map );
- }
- this.markers.push( marker );
- return marker;
- };
+ $.fn.leafletmaps = function ( options ) {
+ let _this = this;
+ _this.options = options; // needed by LeafletAjax.js
- this.removeMarker = function (marker) {
- this.map.removeLayer(marker);
- this.points = [];
- this.markers = this.markers.filter(function(object) {
- return object !== marker;
- });
- };
+ this.setup = function() {
+ this.map = L.map( this.get(0), getMapOptions(options) );
+ this.mapContent = maps.leaflet.FeatureBuilder.contentLayerFromOptions(options).addTo(this.map);
- this.removeMarkers = function () {
- if (this.markercluster) {
- this.map.removeLayer(this.markercluster);
- this.markercluster = null;
- }
- var map = this.map;
- $.each(this.markers, function(index, marker) {
- map.removeLayer(marker);
- });
+ this.hideLoadingMessage();
+ this.addLayersAndOverlays(this.map);
+ this.centerAndZoomMap();
+ this.bindClickTarget();
+ this.applyResizable();
+ this.bindAjaxEvents();
- this.points = [];
- this.markers = [];
+ this.maybeAddEditButton();
};
- this.addLine = function (properties) {
- var options = {
- color: properties.strokeColor,
- weight:properties.strokeWeight,
- opacity:properties.strokeOpacity
- };
+ this.shouldLoadEditorJs = function() {
+ if ( options.geojson === '' || options.GeoJsonSource === null ) {
+ return false;
+ }
- var latlngs = [];
- for (var x = 0; x < properties.pos.length; x++) {
- latlngs.push([properties.pos[x].lat, properties.pos[x].lon]);
- this.points.push( new L.LatLng(properties.pos[x].lat, properties.pos[x].lon) );
+ if (mw.config.get('wgCurRevisionId') !== mw.config.get('wgRevisionId')) {
+ return false;
}
- var line = L.polyline(latlngs, options).addTo(this.map);
+ return true;
+ };
- if( properties.hasOwnProperty('text') && properties.text.trim().length > 0 ) {
- line.bindPopup( properties.text );
+ this.maybeAddEditButton = function() {
+ if ( this.shouldLoadEditorJs() ) {
+ maps.api.canEditPage('GeoJson:' + options.GeoJsonSource).done(
+ function(canEdit) {
+ if (canEdit) {
+ _this.addEditButton();
+ }
+ }
+ );
}
};
- this.addPolygon = function (properties) {
- properties.pos.forEach(function(position) {
- _this.points.push( new L.LatLng(position.lat, position.lon) );
- });
+ this.addEditButton = function() {
+ this.editButton = L.easyButton(
+ '<img src="' + mw.config.get('egMapsScriptPath') + 'resources/leaflet/images/edit-solid.svg">',
+ this.startEditMode,
+ mw.msg('maps-editor-edit-geojson')
+ ).addTo(this.map);
+ };
+
+ this.startEditMode = function() {
+ _this.removeEditButton();
+ _this.mapContent.remove();
- var polygon = L.polygon(
- properties.pos.map(function(position) {
- return [position.lat, position.lon];
- }),
- {
- color: properties.strokeColor,
- weight:properties.strokeWeight,
- opacity:properties.strokeOpacity,
- fillColor:properties.fillColor,
- fillOpacity:properties.fillOpacity
+ maps.api.getLatestRevision('GeoJson:' + options.GeoJsonSource).done(
+ function(revision) {
+ if (revision.revid === options.GeoJsonRevisionId) {
+ _this.initializeEditor(options.geojson);
+ }
+ else {
+ _this.purgePage();
+ _this.initializeEditor(JSON.parse(revision['*']));
+ }
}
);
+ };
- polygon.addTo(this.map);
+ this.initializeEditor = function(geoJson) {
+ let editor = _this.getEditor();
+ editor.initialize(geoJson);
- if( properties.hasOwnProperty('text') && properties.text.trim().length > 0 ) {
- console.log(properties.text);
- polygon.bindPopup( properties.text );
- }
- };
+ editor.onSaved(function() {
+ _this.purgePage();
- this.addCircle = function (properties) {
- var circle = L.circle(
- [properties.centre.lat, properties.centre.lon],
- {
- radius: properties.radius,
- color: properties.strokeColor,
- weight:properties.strokeWeight,
- opacity:properties.strokeOpacity,
- fillColor:properties.fillColor,
- fillOpacity:properties.fillOpacity,
- }
- ).addTo(this.map);
+ editor.remove();
+ options.geojson = editor.getLayer().toGeoJSON();
+ _this.mapContent = maps.leaflet.FeatureBuilder.contentLayerFromOptions(options).addTo(_this.map);
- this.points.push( new L.LatLng(properties.centre.lat, properties.centre.lon) );
+ alert(mw.msg('maps-json-editor-changes-saved'));
+ _this.addEditButton();
+ });
+ };
- if( properties.hasOwnProperty('text') && properties.text.trim().length > 0 ) {
- circle.bindPopup( properties.text );
+ this.getEditor = function() {
+ if (!this.editor) {
+ this.editor = maps.leaflet.LeafletEditor(
+ _this.map,
+ new maps.MapSaver('GeoJson:' + options.GeoJsonSource)
+ );
}
+
+ return this.editor;
};
- this.addRectangle = function (properties) {
- this.points.push( new L.LatLng(properties.sw.lat, properties.sw.lon) );
- this.points.push( new L.LatLng(properties.ne.lat, properties.ne.lon) );
+ this.purgePage = function() {
+ new mw.Api().post({
+ action: 'purge',
+ titles: mw.config.get( 'wgPageName' )
+ }).then(function(response) {
- var options = {
- color: properties.strokeColor,
- weight:properties.strokeWeight,
- opacity:properties.strokeOpacity,
- fillColor:properties.fillColor,
- fillOpacity:properties.fillOpacity
- };
+ });
+ };
- var bounds = [[properties.sw.lat, properties.sw.lon], [properties.ne.lat, properties.ne.lon]];
+ this.removeEditButton = function() {
+ if (this.editButton) {
+ this.editButton.remove();
+ this.editButton = null;
+ }
+ };
- var rectangle = L.rectangle( bounds, options ).addTo(this.map);
+ this.hideLoadingMessage = function() {
+ this.map.on(
+ 'load',
+ function() {
+ $(_this).find('div.maps-loading-message').hide();
+ }
+ );
+ };
- if( properties.hasOwnProperty('text') && properties.text.trim().length > 0 ) {
- rectangle.bindPopup( properties.text );
+ this.applyResizable = function() {
+ if (options.resizable) {
+ _this.resizable();
}
};
- this.createMarkerCluster = function () {
- if ( !options.markercluster ) {
- return;
- }
- var markers = this.markers;
-
- var markercluster = new L.MarkerClusterGroup({
- maxClusterRadius: options.clustermaxradius,
- disableClusteringAtZoom: options.clustermaxzoom + 1,
- zoomToBoundsOnClick: options.clusterzoomonclick,
- spiderfyOnMaxZoom: options.clusterspiderfy,
- iconCreateFunction: function(cluster) {
- var childCount = cluster.getChildCount();
-
- var imagePath = mw.config.get( 'egMapsScriptPath' ) + '/resources/leaflet/cluster/';
-
- var styles = [
- {
- iconUrl: imagePath + 'm1.png',
- iconSize: [53, 52]
- },
- {
- iconUrl: imagePath + 'm2.png',
- iconSize: [56, 55]
- },
- {
- iconUrl: imagePath + 'm3.png',
- iconSize: [66, 65]
- },
- {
- iconUrl: imagePath + 'm4.png',
- iconSize: [78, 77]
- },
- {
- iconUrl: imagePath + 'm5.png',
- iconSize: [90, 89]
- }
- ];
+ // Caution: used by ajaxUpdateMarker
+ this.addMarker = function (properties) {
+ this.mapContent.markerLayer.addLayer(maps.leaflet.FeatureBuilder.createMarker(properties, options));
+ };
- var index = 0;
- var dv = childCount;
- while (dv !== 0) {
- dv = parseInt(dv / 10, 10);
- index++;
- }
- var index = Math.min(index, styles.length);
- index = Math.max(0, index - 1);
- index = Math.min(styles.length - 1, index);
- var style = styles[index];
-
- return new L.divIcon({
- iconSize: style.iconSize,
- className: '',
- html: '<img style="' +
- '" src="' + style.iconUrl + '" />' +
- '<span style="' +
- 'position: absolute; font-size: 11px; font-weight: bold; text-align: center; ' +
- 'top: 0; left: 0; ' +
- 'line-height: ' + style.iconSize[1] + 'px;' +
- 'width: ' + style.iconSize[0] + 'px;' +
- '">' + childCount + '</span>'
- });
- }
- });
- $.each(this.markers, function(index, marker) {
- markercluster.addLayer(marker);
- });
- if (this.markercluster) {
- this.map.removeLayer(this.markercluster);
- this.markercluster = null;
- }
- this.map.addLayer(markercluster);
- this.markercluster = markercluster;
+ // Caution: used by ajaxUpdateMarker
+ this.removeMarkers = function () {
+ this.mapContent.markerLayer.clearLayers();
};
- this.addGeoJson = function(options) {
- if (options.geojson !== '') {
- var geoJson = options.geojson;
- var geoJsonLayer = L.geoJSON( geoJson ).addTo( this.map );
+ this.bindClickTarget = function() {
+ function newClickTargetUrl(latlng) {
+ return options.clicktarget
+ .replace( /%lat%/g, latlng.lat )
+ .replace( /%long%/g, latlng.lng );
+ }
- this.points.push( geoJsonLayer.getBounds().getNorthEast() );
- this.points.push( geoJsonLayer.getBounds().getSouthWest() );
+ if (options.clicktarget !== '') {
+ this.map.on(
+ 'click',
+ function(e) {
+ window.location.href = newClickTargetUrl(e.latlng);
+ }
+ );
}
};
- this.setup = function () {
-
- var mapOptions = {};
- if (options.minzoom !== false ) mapOptions.minZoom = options.minzoom;
- if (options.maxzoom !== false ) mapOptions.maxZoom = options.maxzoom;
+ this.isUserUsesDarkMode = function () {
+ return window.matchMedia( '(prefers-color-scheme: dark)' ).matches;
+ };
- if (options.enablefullscreen) {
- mapOptions.fullscreenControl = true;
- mapOptions.fullscreenControlOptions= {
- position: 'topleft'
- };
- }
-
- mapOptions.scrollWheelZoom = options.scrollwheelzoom;
-
- if (options.static) {
- mapOptions.scrollWheelZoom = false;
- mapOptions.doubleClickZoom = false;
- mapOptions.touchZoom = false;
- mapOptions.boxZoom = false;
- mapOptions.tap = false;
- mapOptions.keyboard = false;
- mapOptions.zoomControl = false;
- mapOptions.dragging = false;
+ this.getLayerNames = function () {
+ if ( this.isUserUsesDarkMode() ) {
+ return mw.config.get('egMapsLeafletLayersDark');
}
- var map = L.map( this.get(0), mapOptions ).fitWorld();
- this.map = map;
+ return options.layers;
+ };
+
+ this.addLayers = function() {
+ let apiKeys = mw.config.get('egMapsLeafletLayersApiKeys');
+ let layers = {};
- var layers = {};
- $.each(options.layers.reverse(), function(index, layerName) {
+ $.each( this.getLayerNames().reverse(), function(index, layerName) {
var options = {} ;
var providerName = layerName.split('.')[0] ;
- if (apikeys.hasOwnProperty(providerName) && apikeys[providerName] !== '') {
- options.apikey = apikeys[providerName] ;
- }
+ if (apiKeys.hasOwnProperty(providerName) && apiKeys[providerName] !== '') {
+ options.apikey = apiKeys[providerName] ;
+ }
if (layerName === 'MapQuestOpen') {
- layers[layerName] = new MQ.TileLayer().addTo(map);
+ layers[layerName] = new window.MQ.TileLayer().addTo(_this.map);
} else {
- layers[layerName] = new L.tileLayer.provider(layerName,options).addTo(map);
+ layers[layerName] = new L.tileLayer.provider(layerName,options).addTo(_this.map);
}
});
- var overlaylayers = {};
- $.each(options.overlaylayers, function(index, overlaylayerName) {
- overlaylayers[overlaylayerName] = new L.tileLayer.provider(overlaylayerName).addTo(_this.map);
+ return layers;
+ };
+
+ this.addOverlays = function() {
+ let overlays = {};
+
+ $.each(options.overlays, function(index, overlayName) {
+ overlays[overlayName] = new L.tileLayer.provider(overlayName).addTo(_this.map);
});
- if (options.layers.length > 1) {
- L.control.layers(layers, overlaylayers).addTo(map);
- }
+ return overlays;
+ };
- if (options.resizable) {
- //TODO: Fix moving map when resized
- _this.resizable();
- }
+ this.addLayersAndOverlays = function() {
+ let layers = this.addLayers();
+ let overlays = this.addOverlays();
- if (!options.locations) {
- options.locations = [];
+ if (options.layers.length > 1 || options.overlays.length > 0) {
+ L.control.layers(layers, overlays).addTo(this.map);
}
+ };
- // Add the markers.
- for (var i = options.locations.length - 1; i >= 0; i--) {
- this.addMarker(options.locations[i]);
+ this.bindAjaxEvents = function() {
+ if ( !options.ajaxquery || !options.ajaxcoordproperty ) {
+ return;
}
- // Add markercluster
- if (options.markercluster) {
- this.createMarkerCluster();
- }
+ let ajaxRequest = null;
- // Add lines
- if (options.lines) {
- for (var i = 0; i < options.lines.length; i++) {
- this.addLine(options.lines[i]);
- }
- }
+ this.map.on( 'dragend zoomend', function() {
+ let bounds = _this.map.getBounds();
- // Add polygons
- if (options.polygons) {
- for (var i = 0; i < options.polygons.length; i++) {
- this.addPolygon(options.polygons[i]);
- }
- }
+ let query = sm.buildQueryString(
+ decodeURIComponent( options.ajaxquery.replace( /\+/g, ' ' ) ),
+ options.ajaxcoordproperty,
+ bounds.getNorthEast().lat,
+ bounds.getNorthEast().lng,
+ bounds.getSouthWest().lat,
+ bounds.getSouthWest().lng
+ );
- // Add circles
- if (options.circles) {
- for (var i = 0; i < options.circles.length; i++) {
- this.addCircle(options.circles[i]);
+ if( ajaxRequest !== null ) {
+ ajaxRequest.abort();
}
+
+ ajaxRequest = sm.ajaxUpdateMarker( _this, query, options.icon ).done( function() {
+ ajaxRequest = null;
+ } );
+ } );
+ };
+
+ this.centerAndZoomMap = function() {
+ this.map.fitWorld();
+ this.fitContent();
+
+ if (options.zoom !== false) {
+ this.map.setZoom(options.zoom);
}
- // Add rectangles
- if (options.rectangles) {
- for (var i = 0; i < options.rectangles.length; i++) {
- this.addRectangle(options.rectangles[i]);
- }
+ if (options.centre !== false) {
+ this.map.setView(
+ new L.LatLng(options.centre.lat, options.centre.lon),
+ this.map.getZoom()
+ );
}
+ };
- this.addGeoJson(options);
-
- // Set map position (centre and zoom)
- var centre;
- if (options.centre === false) {
- switch ( this.points.length ) {
- case 0:
- centre = new L.LatLng(0, 0);
- break;
- case 1:
- centre = this.points[0];
- break;
- default:
- var bounds = new L.LatLngBounds( this.points );
- if (options.zoom === false) {
- map.fitBounds( bounds );
- centre = false;
- } else {
- centre = bounds.getCenter();
- }
- break;
+ this.fitContent = function() {
+ let bounds = this.mapContent.getBounds();
+
+ if (bounds.isValid()) {
+ if (bounds.getNorthEast().equals(bounds.getSouthWest())) {
+ this.map.setView(
+ bounds.getCenter(),
+ options.defzoom
+ );
+ }
+ else {
+ this.map.fitBounds(bounds);
}
- this.points = [];
- } else {
- centre = new L.LatLng(options.centre.lat, options.centre.lon);
- }
- if(centre) {
- map.setView( centre, options.zoom !== false ? options.zoom : options.defzoom );
}
};
this.getDependencies = function ( options ) {
var dependencies = [];
- if (options.layers !== ['MapQuestOpen'] || options.overlaylayers.length > 0) {
- dependencies.push( 'ext.maps.leaflet.providers' );
+
+ if (this.shouldLoadEditorJs()) {
+ dependencies.push( 'ext.maps.leaflet.editor' );
}
- if (options.enablefullscreen) {
+
+ if (options.fullscreen) {
dependencies.push( 'ext.maps.leaflet.fullscreen' );
}
+
if (options.resizable) {
dependencies.push( 'ext.maps.resizable' );
}
- if (options.markercluster) {
+
+ if (options.cluster) {
dependencies.push( 'ext.maps.leaflet.markercluster' );
}
+
return dependencies;
};
@@ -429,6 +327,5 @@
} );
return this;
-
};
-})(jQuery, window.mediaWiki, L, window.MQ);
+})(window.jQuery, window.mediaWiki, window.L, window.maps, window.sm);
diff --git a/www/wiki/extensions/Maps/resources/leaflet/leaflet.editable/Leaflet.Editable.js b/www/wiki/extensions/Maps/resources/leaflet/leaflet.editable/Leaflet.Editable.js
deleted file mode 100644
index e259a14c..00000000
--- a/www/wiki/extensions/Maps/resources/leaflet/leaflet.editable/Leaflet.Editable.js
+++ /dev/null
@@ -1,1945 +0,0 @@
-'use strict';
-(function (factory, window) {// 1.2 alpha
- /*globals define, module, require*/
-
- // define an AMD module that relies on 'leaflet'
- if (typeof define === 'function' && define.amd) {
- define(['leaflet'], factory);
-
-
- // define a Common JS module that relies on 'leaflet'
- } else if (typeof exports === 'object') {
- module.exports = factory(require('leaflet'));
- }
-
- // attach your plugin to the global 'L' variable
- if(typeof window !== 'undefined' && window.L){
- factory(window.L);
- }
-
-}(function (L) {
- // 🍂miniclass CancelableEvent (Event objects)
- // 🍂method cancel()
- // Cancel any subsequent action.
-
- // 🍂miniclass VertexEvent (Event objects)
- // 🍂property vertex: VertexMarker
- // The vertex that fires the event.
-
- // 🍂miniclass ShapeEvent (Event objects)
- // 🍂property shape: Array
- // The shape (LatLngs array) subject of the action.
-
- // 🍂miniclass CancelableVertexEvent (Event objects)
- // 🍂inherits VertexEvent
- // 🍂inherits CancelableEvent
-
- // 🍂miniclass CancelableShapeEvent (Event objects)
- // 🍂inherits ShapeEvent
- // 🍂inherits CancelableEvent
-
- // 🍂miniclass LayerEvent (Event objects)
- // 🍂property layer: object
- // The Layer (Marker, Polyline…) subject of the action.
-
- // 🍂namespace Editable; 🍂class Editable; 🍂aka L.Editable
- // Main edition handler. By default, it is attached to the map
- // as `map.editTools` property.
- // Leaflet.Editable is made to be fully extendable. You have three ways to customize
- // the behaviour: using options, listening to events, or extending.
- L.Editable = L.Evented.extend({
-
- statics: {
- FORWARD: 1,
- BACKWARD: -1
- },
-
- options: {
-
- // You can pass them when creating a map using the `editOptions` key.
- // 🍂option zIndex: int = 1000
- // The default zIndex of the editing tools.
- zIndex: 1000,
-
- // 🍂option polygonClass: class = L.Polygon
- // Class to be used when creating a new Polygon.
- polygonClass: L.Polygon,
-
- // 🍂option polylineClass: class = L.Polyline
- // Class to be used when creating a new Polyline.
- polylineClass: L.Polyline,
-
- // 🍂option markerClass: class = L.Marker
- // Class to be used when creating a new Marker.
- markerClass: L.Marker,
-
- // 🍂option rectangleClass: class = L.Rectangle
- // Class to be used when creating a new Rectangle.
- rectangleClass: L.Rectangle,
-
- // 🍂option circleClass: class = L.Circle
- // Class to be used when creating a new Circle.
- circleClass: L.Circle,
-
- // 🍂option drawingCSSClass: string = 'leaflet-editable-drawing'
- // CSS class to be added to the map container while drawing.
- drawingCSSClass: 'leaflet-editable-drawing',
-
- // 🍂option drawingCursor: const = 'crosshair'
- // Cursor mode set to the map while drawing.
- drawingCursor: 'crosshair',
-
- // 🍂option editLayer: Layer = new L.LayerGroup()
- // Layer used to store edit tools (vertex, line guide…).
- editLayer: undefined,
-
- // 🍂option featuresLayer: Layer = new L.LayerGroup()
- // Default layer used to store drawn features (Marker, Polyline…).
- featuresLayer: undefined,
-
- // 🍂option polylineEditorClass: class = PolylineEditor
- // Class to be used as Polyline editor.
- polylineEditorClass: undefined,
-
- // 🍂option polygonEditorClass: class = PolygonEditor
- // Class to be used as Polygon editor.
- polygonEditorClass: undefined,
-
- // 🍂option markerEditorClass: class = MarkerEditor
- // Class to be used as Marker editor.
- markerEditorClass: undefined,
-
- // 🍂option rectangleEditorClass: class = RectangleEditor
- // Class to be used as Rectangle editor.
- rectangleEditorClass: undefined,
-
- // 🍂option circleEditorClass: class = CircleEditor
- // Class to be used as Circle editor.
- circleEditorClass: undefined,
-
- // 🍂option lineGuideOptions: hash = {}
- // Options to be passed to the line guides.
- lineGuideOptions: {},
-
- // 🍂option skipMiddleMarkers: boolean = false
- // Set this to true if you don't want middle markers.
- skipMiddleMarkers: false
-
- },
-
- initialize: function (map, options) {
- L.setOptions(this, options);
- this._lastZIndex = this.options.zIndex;
- this.map = map;
- this.editLayer = this.createEditLayer();
- this.featuresLayer = this.createFeaturesLayer();
- this.forwardLineGuide = this.createLineGuide();
- this.backwardLineGuide = this.createLineGuide();
- },
-
- fireAndForward: function (type, e) {
- e = e || {};
- e.editTools = this;
- this.fire(type, e);
- this.map.fire(type, e);
- },
-
- createLineGuide: function () {
- var options = L.extend({dashArray: '5,10', weight: 1, interactive: false}, this.options.lineGuideOptions);
- return L.polyline([], options);
- },
-
- createVertexIcon: function (options) {
- return L.Browser.mobile && L.Browser.touch ? new L.Editable.TouchVertexIcon(options) : new L.Editable.VertexIcon(options);
- },
-
- createEditLayer: function () {
- return this.options.editLayer || new L.LayerGroup().addTo(this.map);
- },
-
- createFeaturesLayer: function () {
- return this.options.featuresLayer || new L.LayerGroup().addTo(this.map);
- },
-
- moveForwardLineGuide: function (latlng) {
- if (this.forwardLineGuide._latlngs.length) {
- this.forwardLineGuide._latlngs[1] = latlng;
- this.forwardLineGuide._bounds.extend(latlng);
- this.forwardLineGuide.redraw();
- }
- },
-
- moveBackwardLineGuide: function (latlng) {
- if (this.backwardLineGuide._latlngs.length) {
- this.backwardLineGuide._latlngs[1] = latlng;
- this.backwardLineGuide._bounds.extend(latlng);
- this.backwardLineGuide.redraw();
- }
- },
-
- anchorForwardLineGuide: function (latlng) {
- this.forwardLineGuide._latlngs[0] = latlng;
- this.forwardLineGuide._bounds.extend(latlng);
- this.forwardLineGuide.redraw();
- },
-
- anchorBackwardLineGuide: function (latlng) {
- this.backwardLineGuide._latlngs[0] = latlng;
- this.backwardLineGuide._bounds.extend(latlng);
- this.backwardLineGuide.redraw();
- },
-
- attachForwardLineGuide: function () {
- this.editLayer.addLayer(this.forwardLineGuide);
- },
-
- attachBackwardLineGuide: function () {
- this.editLayer.addLayer(this.backwardLineGuide);
- },
-
- detachForwardLineGuide: function () {
- this.forwardLineGuide.setLatLngs([]);
- this.editLayer.removeLayer(this.forwardLineGuide);
- },
-
- detachBackwardLineGuide: function () {
- this.backwardLineGuide.setLatLngs([]);
- this.editLayer.removeLayer(this.backwardLineGuide);
- },
-
- blockEvents: function () {
- // Hack: force map not to listen to other layers events while drawing.
- if (!this._oldTargets) {
- this._oldTargets = this.map._targets;
- this.map._targets = {};
- }
- },
-
- unblockEvents: function () {
- if (this._oldTargets) {
- // Reset, but keep targets created while drawing.
- this.map._targets = L.extend(this.map._targets, this._oldTargets);
- delete this._oldTargets;
- }
- },
-
- registerForDrawing: function (editor) {
- if (this._drawingEditor) this.unregisterForDrawing(this._drawingEditor);
- this.blockEvents();
- editor.reset(); // Make sure editor tools still receive events.
- this._drawingEditor = editor;
- this.map.on('mousemove touchmove', editor.onDrawingMouseMove, editor);
- this.map.on('mousedown', this.onMousedown, this);
- this.map.on('mouseup', this.onMouseup, this);
- L.DomUtil.addClass(this.map._container, this.options.drawingCSSClass);
- this.defaultMapCursor = this.map._container.style.cursor;
- this.map._container.style.cursor = this.options.drawingCursor;
- },
-
- unregisterForDrawing: function (editor) {
- this.unblockEvents();
- L.DomUtil.removeClass(this.map._container, this.options.drawingCSSClass);
- this.map._container.style.cursor = this.defaultMapCursor;
- editor = editor || this._drawingEditor;
- if (!editor) return;
- this.map.off('mousemove touchmove', editor.onDrawingMouseMove, editor);
- this.map.off('mousedown', this.onMousedown, this);
- this.map.off('mouseup', this.onMouseup, this);
- if (editor !== this._drawingEditor) return;
- delete this._drawingEditor;
- if (editor._drawing) editor.cancelDrawing();
- },
-
- onMousedown: function (e) {
- this._mouseDown = e;
- this._drawingEditor.onDrawingMouseDown(e);
- },
-
- onMouseup: function (e) {
- if (this._mouseDown) {
- var editor = this._drawingEditor,
- mouseDown = this._mouseDown;
- this._mouseDown = null;
- editor.onDrawingMouseUp(e);
- if (this._drawingEditor !== editor) return; // onDrawingMouseUp may call unregisterFromDrawing.
- var origin = L.point(mouseDown.originalEvent.clientX, mouseDown.originalEvent.clientY);
- var distance = L.point(e.originalEvent.clientX, e.originalEvent.clientY).distanceTo(origin);
- if (Math.abs(distance) < 9 * (window.devicePixelRatio || 1)) this._drawingEditor.onDrawingClick(e);
- }
- },
-
- // 🍂section Public methods
- // You will generally access them by the `map.editTools`
- // instance:
- //
- // `map.editTools.startPolyline();`
-
- // 🍂method drawing(): boolean
- // Return true if any drawing action is ongoing.
- drawing: function () {
- return this._drawingEditor && this._drawingEditor.drawing();
- },
-
- // 🍂method stopDrawing()
- // When you need to stop any ongoing drawing, without needing to know which editor is active.
- stopDrawing: function () {
- this.unregisterForDrawing();
- },
-
- // 🍂method commitDrawing()
- // When you need to commit any ongoing drawing, without needing to know which editor is active.
- commitDrawing: function (e) {
- if (!this._drawingEditor) return;
- this._drawingEditor.commitDrawing(e);
- },
-
- connectCreatedToMap: function (layer) {
- return this.featuresLayer.addLayer(layer);
- },
-
- // 🍂method startPolyline(latlng: L.LatLng, options: hash): L.Polyline
- // Start drawing a Polyline. If `latlng` is given, a first point will be added. In any case, continuing on user click.
- // If `options` is given, it will be passed to the Polyline class constructor.
- startPolyline: function (latlng, options) {
- var line = this.createPolyline([], options);
- line.enableEdit(this.map).newShape(latlng);
- return line;
- },
-
- // 🍂method startPolygon(latlng: L.LatLng, options: hash): L.Polygon
- // Start drawing a Polygon. If `latlng` is given, a first point will be added. In any case, continuing on user click.
- // If `options` is given, it will be passed to the Polygon class constructor.
- startPolygon: function (latlng, options) {
- var polygon = this.createPolygon([], options);
- polygon.enableEdit(this.map).newShape(latlng);
- return polygon;
- },
-
- // 🍂method startMarker(latlng: L.LatLng, options: hash): L.Marker
- // Start adding a Marker. If `latlng` is given, the Marker will be shown first at this point.
- // In any case, it will follow the user mouse, and will have a final `latlng` on next click (or touch).
- // If `options` is given, it will be passed to the Marker class constructor.
- startMarker: function (latlng, options) {
- latlng = latlng || this.map.getCenter().clone();
- var marker = this.createMarker(latlng, options);
- marker.enableEdit(this.map).startDrawing();
- return marker;
- },
-
- // 🍂method startRectangle(latlng: L.LatLng, options: hash): L.Rectangle
- // Start drawing a Rectangle. If `latlng` is given, the Rectangle anchor will be added. In any case, continuing on user drag.
- // If `options` is given, it will be passed to the Rectangle class constructor.
- startRectangle: function(latlng, options) {
- var corner = latlng || L.latLng([0, 0]);
- var bounds = new L.LatLngBounds(corner, corner);
- var rectangle = this.createRectangle(bounds, options);
- rectangle.enableEdit(this.map).startDrawing();
- return rectangle;
- },
-
- // 🍂method startCircle(latlng: L.LatLng, options: hash): L.Circle
- // Start drawing a Circle. If `latlng` is given, the Circle anchor will be added. In any case, continuing on user drag.
- // If `options` is given, it will be passed to the Circle class constructor.
- startCircle: function (latlng, options) {
- latlng = latlng || this.map.getCenter().clone();
- var circle = this.createCircle(latlng, options);
- circle.enableEdit(this.map).startDrawing();
- return circle;
- },
-
- startHole: function (editor, latlng) {
- editor.newHole(latlng);
- },
-
- createLayer: function (klass, latlngs, options) {
- options = L.Util.extend({editOptions: {editTools: this}}, options);
- var layer = new klass(latlngs, options);
- // 🍂namespace Editable
- // 🍂event editable:created: LayerEvent
- // Fired when a new feature (Marker, Polyline…) is created.
- this.fireAndForward('editable:created', {layer: layer});
- return layer;
- },
-
- createPolyline: function (latlngs, options) {
- return this.createLayer(options && options.polylineClass || this.options.polylineClass, latlngs, options);
- },
-
- createPolygon: function (latlngs, options) {
- return this.createLayer(options && options.polygonClass || this.options.polygonClass, latlngs, options);
- },
-
- createMarker: function (latlng, options) {
- return this.createLayer(options && options.markerClass || this.options.markerClass, latlng, options);
- },
-
- createRectangle: function (bounds, options) {
- return this.createLayer(options && options.rectangleClass || this.options.rectangleClass, bounds, options);
- },
-
- createCircle: function (latlng, options) {
- return this.createLayer(options && options.circleClass || this.options.circleClass, latlng, options);
- }
-
- });
-
- L.extend(L.Editable, {
-
- makeCancellable: function (e) {
- e.cancel = function () {
- e._cancelled = true;
- };
- }
-
- });
-
- // 🍂namespace Map; 🍂class Map
- // Leaflet.Editable add options and events to the `L.Map` object.
- // See `Editable` events for the list of events fired on the Map.
- // 🍂example
- //
- // ```js
- // var map = L.map('map', {
- // editable: true,
- // editOptions: {
- // …
- // }
- // });
- // ```
- // 🍂section Editable Map Options
- L.Map.mergeOptions({
-
- // 🍂namespace Map
- // 🍂section Map Options
- // 🍂option editToolsClass: class = L.Editable
- // Class to be used as vertex, for path editing.
- editToolsClass: L.Editable,
-
- // 🍂option editable: boolean = false
- // Whether to create a L.Editable instance at map init.
- editable: false,
-
- // 🍂option editOptions: hash = {}
- // Options to pass to L.Editable when instantiating.
- editOptions: {}
-
- });
-
- L.Map.addInitHook(function () {
-
- this.whenReady(function () {
- if (this.options.editable) {
- this.editTools = new this.options.editToolsClass(this, this.options.editOptions);
- }
- });
-
- });
-
- L.Editable.VertexIcon = L.DivIcon.extend({
-
- options: {
- iconSize: new L.Point(8, 8)
- }
-
- });
-
- L.Editable.TouchVertexIcon = L.Editable.VertexIcon.extend({
-
- options: {
- iconSize: new L.Point(20, 20)
- }
-
- });
-
-
- // 🍂namespace Editable; 🍂class VertexMarker; Handler for dragging path vertices.
- L.Editable.VertexMarker = L.Marker.extend({
-
- options: {
- draggable: true,
- className: 'leaflet-div-icon leaflet-vertex-icon'
- },
-
-
- // 🍂section Public methods
- // The marker used to handle path vertex. You will usually interact with a `VertexMarker`
- // instance when listening for events like `editable:vertex:ctrlclick`.
-
- initialize: function (latlng, latlngs, editor, options) {
- // We don't use this._latlng, because on drag Leaflet replace it while
- // we want to keep reference.
- this.latlng = latlng;
- this.latlngs = latlngs;
- this.editor = editor;
- L.Marker.prototype.initialize.call(this, latlng, options);
- this.options.icon = this.editor.tools.createVertexIcon({className: this.options.className});
- this.latlng.__vertex = this;
- this.editor.editLayer.addLayer(this);
- this.setZIndexOffset(editor.tools._lastZIndex + 1);
- },
-
- onAdd: function (map) {
- L.Marker.prototype.onAdd.call(this, map);
- this.on('drag', this.onDrag);
- this.on('dragstart', this.onDragStart);
- this.on('dragend', this.onDragEnd);
- this.on('mouseup', this.onMouseup);
- this.on('click', this.onClick);
- this.on('contextmenu', this.onContextMenu);
- this.on('mousedown touchstart', this.onMouseDown);
- this.on('mouseover', this.onMouseOver);
- this.on('mouseout', this.onMouseOut);
- this.addMiddleMarkers();
- },
-
- onRemove: function (map) {
- if (this.middleMarker) this.middleMarker.delete();
- delete this.latlng.__vertex;
- this.off('drag', this.onDrag);
- this.off('dragstart', this.onDragStart);
- this.off('dragend', this.onDragEnd);
- this.off('mouseup', this.onMouseup);
- this.off('click', this.onClick);
- this.off('contextmenu', this.onContextMenu);
- this.off('mousedown touchstart', this.onMouseDown);
- this.off('mouseover', this.onMouseOver);
- this.off('mouseout', this.onMouseOut);
- L.Marker.prototype.onRemove.call(this, map);
- },
-
- onDrag: function (e) {
- e.vertex = this;
- this.editor.onVertexMarkerDrag(e);
- var iconPos = L.DomUtil.getPosition(this._icon),
- latlng = this._map.layerPointToLatLng(iconPos);
- this.latlng.update(latlng);
- this._latlng = this.latlng; // Push back to Leaflet our reference.
- this.editor.refresh();
- if (this.middleMarker) this.middleMarker.updateLatLng();
- var next = this.getNext();
- if (next && next.middleMarker) next.middleMarker.updateLatLng();
- },
-
- onDragStart: function (e) {
- e.vertex = this;
- this.editor.onVertexMarkerDragStart(e);
- },
-
- onDragEnd: function (e) {
- e.vertex = this;
- this.editor.onVertexMarkerDragEnd(e);
- },
-
- onClick: function (e) {
- e.vertex = this;
- this.editor.onVertexMarkerClick(e);
- },
-
- onMouseup: function (e) {
- L.DomEvent.stop(e);
- e.vertex = this;
- this.editor.map.fire('mouseup', e);
- },
-
- onContextMenu: function (e) {
- e.vertex = this;
- this.editor.onVertexMarkerContextMenu(e);
- },
-
- onMouseDown: function (e) {
- e.vertex = this;
- this.editor.onVertexMarkerMouseDown(e);
- },
-
- onMouseOver: function (e) {
- e.vertex = this;
- this.editor.onVertexMarkerMouseOver(e);
- },
-
- onMouseOut: function (e) {
- e.vertex = this;
- this.editor.onVertexMarkerMouseOut(e);
- },
-
- // 🍂method delete()
- // Delete a vertex and the related LatLng.
- delete: function () {
- var next = this.getNext(); // Compute before changing latlng
- this.latlngs.splice(this.getIndex(), 1);
- this.editor.editLayer.removeLayer(this);
- this.editor.onVertexDeleted({latlng: this.latlng, vertex: this});
- if (!this.latlngs.length) this.editor.deleteShape(this.latlngs);
- if (next) next.resetMiddleMarker();
- this.editor.refresh();
- },
-
- // 🍂method getIndex(): int
- // Get the index of the current vertex among others of the same LatLngs group.
- getIndex: function () {
- return this.latlngs.indexOf(this.latlng);
- },
-
- // 🍂method getLastIndex(): int
- // Get last vertex index of the LatLngs group of the current vertex.
- getLastIndex: function () {
- return this.latlngs.length - 1;
- },
-
- // 🍂method getPrevious(): VertexMarker
- // Get the previous VertexMarker in the same LatLngs group.
- getPrevious: function () {
- if (this.latlngs.length < 2) return;
- var index = this.getIndex(),
- previousIndex = index - 1;
- if (index === 0 && this.editor.CLOSED) previousIndex = this.getLastIndex();
- var previous = this.latlngs[previousIndex];
- if (previous) return previous.__vertex;
- },
-
- // 🍂method getNext(): VertexMarker
- // Get the next VertexMarker in the same LatLngs group.
- getNext: function () {
- if (this.latlngs.length < 2) return;
- var index = this.getIndex(),
- nextIndex = index + 1;
- if (index === this.getLastIndex() && this.editor.CLOSED) nextIndex = 0;
- var next = this.latlngs[nextIndex];
- if (next) return next.__vertex;
- },
-
- addMiddleMarker: function (previous) {
- if (!this.editor.hasMiddleMarkers()) return;
- previous = previous || this.getPrevious();
- if (previous && !this.middleMarker) this.middleMarker = this.editor.addMiddleMarker(previous, this, this.latlngs, this.editor);
- },
-
- addMiddleMarkers: function () {
- if (!this.editor.hasMiddleMarkers()) return;
- var previous = this.getPrevious();
- if (previous) this.addMiddleMarker(previous);
- var next = this.getNext();
- if (next) next.resetMiddleMarker();
- },
-
- resetMiddleMarker: function () {
- if (this.middleMarker) this.middleMarker.delete();
- this.addMiddleMarker();
- },
-
- // 🍂method split()
- // Split the vertex LatLngs group at its index, if possible.
- split: function () {
- if (!this.editor.splitShape) return; // Only for PolylineEditor
- this.editor.splitShape(this.latlngs, this.getIndex());
- },
-
- // 🍂method continue()
- // Continue the vertex LatLngs from this vertex. Only active for first and last vertices of a Polyline.
- continue: function () {
- if (!this.editor.continueBackward) return; // Only for PolylineEditor
- var index = this.getIndex();
- if (index === 0) this.editor.continueBackward(this.latlngs);
- else if (index === this.getLastIndex()) this.editor.continueForward(this.latlngs);
- }
-
- });
-
- L.Editable.mergeOptions({
-
- // 🍂namespace Editable
- // 🍂option vertexMarkerClass: class = VertexMarker
- // Class to be used as vertex, for path editing.
- vertexMarkerClass: L.Editable.VertexMarker
-
- });
-
- L.Editable.MiddleMarker = L.Marker.extend({
-
- options: {
- opacity: 0.5,
- className: 'leaflet-div-icon leaflet-middle-icon',
- draggable: true
- },
-
- initialize: function (left, right, latlngs, editor, options) {
- this.left = left;
- this.right = right;
- this.editor = editor;
- this.latlngs = latlngs;
- L.Marker.prototype.initialize.call(this, this.computeLatLng(), options);
- this._opacity = this.options.opacity;
- this.options.icon = this.editor.tools.createVertexIcon({className: this.options.className});
- this.editor.editLayer.addLayer(this);
- this.setVisibility();
- },
-
- setVisibility: function () {
- var leftPoint = this._map.latLngToContainerPoint(this.left.latlng),
- rightPoint = this._map.latLngToContainerPoint(this.right.latlng),
- size = L.point(this.options.icon.options.iconSize);
- if (leftPoint.distanceTo(rightPoint) < size.x * 3) this.hide();
- else this.show();
- },
-
- show: function () {
- this.setOpacity(this._opacity);
- },
-
- hide: function () {
- this.setOpacity(0);
- },
-
- updateLatLng: function () {
- this.setLatLng(this.computeLatLng());
- this.setVisibility();
- },
-
- computeLatLng: function () {
- var leftPoint = this.editor.map.latLngToContainerPoint(this.left.latlng),
- rightPoint = this.editor.map.latLngToContainerPoint(this.right.latlng),
- y = (leftPoint.y + rightPoint.y) / 2,
- x = (leftPoint.x + rightPoint.x) / 2;
- return this.editor.map.containerPointToLatLng([x, y]);
- },
-
- onAdd: function (map) {
- L.Marker.prototype.onAdd.call(this, map);
- L.DomEvent.on(this._icon, 'mousedown touchstart', this.onMouseDown, this);
- map.on('zoomend', this.setVisibility, this);
- },
-
- onRemove: function (map) {
- delete this.right.middleMarker;
- L.DomEvent.off(this._icon, 'mousedown touchstart', this.onMouseDown, this);
- map.off('zoomend', this.setVisibility, this);
- L.Marker.prototype.onRemove.call(this, map);
- },
-
- onMouseDown: function (e) {
- var iconPos = L.DomUtil.getPosition(this._icon),
- latlng = this.editor.map.layerPointToLatLng(iconPos);
- e = {
- originalEvent: e,
- latlng: latlng
- };
- if (this.options.opacity === 0) return;
- L.Editable.makeCancellable(e);
- this.editor.onMiddleMarkerMouseDown(e);
- if (e._cancelled) return;
- this.latlngs.splice(this.index(), 0, e.latlng);
- this.editor.refresh();
- var icon = this._icon;
- var marker = this.editor.addVertexMarker(e.latlng, this.latlngs);
- this.editor.onNewVertex(marker);
- /* Hack to workaround browser not firing touchend when element is no more on DOM */
- var parent = marker._icon.parentNode;
- parent.removeChild(marker._icon);
- marker._icon = icon;
- parent.appendChild(marker._icon);
- marker._initIcon();
- marker._initInteraction();
- marker.setOpacity(1);
- /* End hack */
- // Transfer ongoing dragging to real marker
- L.Draggable._dragging = false;
- marker.dragging._draggable._onDown(e.originalEvent);
- this.delete();
- },
-
- delete: function () {
- this.editor.editLayer.removeLayer(this);
- },
-
- index: function () {
- return this.latlngs.indexOf(this.right.latlng);
- }
-
- });
-
- L.Editable.mergeOptions({
-
- // 🍂namespace Editable
- // 🍂option middleMarkerClass: class = VertexMarker
- // Class to be used as middle vertex, pulled by the user to create a new point in the middle of a path.
- middleMarkerClass: L.Editable.MiddleMarker
-
- });
-
- // 🍂namespace Editable; 🍂class BaseEditor; 🍂aka L.Editable.BaseEditor
- // When editing a feature (Marker, Polyline…), an editor is attached to it. This
- // editor basically knows how to handle the edition.
- L.Editable.BaseEditor = L.Handler.extend({
-
- initialize: function (map, feature, options) {
- L.setOptions(this, options);
- this.map = map;
- this.feature = feature;
- this.feature.editor = this;
- this.editLayer = new L.LayerGroup();
- this.tools = this.options.editTools || map.editTools;
- },
-
- // 🍂method enable(): this
- // Set up the drawing tools for the feature to be editable.
- addHooks: function () {
- if (this.isConnected()) this.onFeatureAdd();
- else this.feature.once('add', this.onFeatureAdd, this);
- this.onEnable();
- this.feature.on(this._getEvents(), this);
- },
-
- // 🍂method disable(): this
- // Remove the drawing tools for the feature.
- removeHooks: function () {
- this.feature.off(this._getEvents(), this);
- if (this.feature.dragging) this.feature.dragging.disable();
- this.editLayer.clearLayers();
- this.tools.editLayer.removeLayer(this.editLayer);
- this.onDisable();
- if (this._drawing) this.cancelDrawing();
- },
-
- // 🍂method drawing(): boolean
- // Return true if any drawing action is ongoing with this editor.
- drawing: function () {
- return !!this._drawing;
- },
-
- reset: function () {},
-
- onFeatureAdd: function () {
- this.tools.editLayer.addLayer(this.editLayer);
- if (this.feature.dragging) this.feature.dragging.enable();
- },
-
- hasMiddleMarkers: function () {
- return !this.options.skipMiddleMarkers && !this.tools.options.skipMiddleMarkers;
- },
-
- fireAndForward: function (type, e) {
- e = e || {};
- e.layer = this.feature;
- this.feature.fire(type, e);
- this.tools.fireAndForward(type, e);
- },
-
- onEnable: function () {
- // 🍂namespace Editable
- // 🍂event editable:enable: Event
- // Fired when an existing feature is ready to be edited.
- this.fireAndForward('editable:enable');
- },
-
- onDisable: function () {
- // 🍂namespace Editable
- // 🍂event editable:disable: Event
- // Fired when an existing feature is not ready anymore to be edited.
- this.fireAndForward('editable:disable');
- },
-
- onEditing: function () {
- // 🍂namespace Editable
- // 🍂event editable:editing: Event
- // Fired as soon as any change is made to the feature geometry.
- this.fireAndForward('editable:editing');
- },
-
- onStartDrawing: function () {
- // 🍂namespace Editable
- // 🍂section Drawing events
- // 🍂event editable:drawing:start: Event
- // Fired when a feature is to be drawn.
- this.fireAndForward('editable:drawing:start');
- },
-
- onEndDrawing: function () {
- // 🍂namespace Editable
- // 🍂section Drawing events
- // 🍂event editable:drawing:end: Event
- // Fired when a feature is not drawn anymore.
- this.fireAndForward('editable:drawing:end');
- },
-
- onCancelDrawing: function () {
- // 🍂namespace Editable
- // 🍂section Drawing events
- // 🍂event editable:drawing:cancel: Event
- // Fired when user cancel drawing while a feature is being drawn.
- this.fireAndForward('editable:drawing:cancel');
- },
-
- onCommitDrawing: function (e) {
- // 🍂namespace Editable
- // 🍂section Drawing events
- // 🍂event editable:drawing:commit: Event
- // Fired when user finish drawing a feature.
- this.fireAndForward('editable:drawing:commit', e);
- },
-
- onDrawingMouseDown: function (e) {
- // 🍂namespace Editable
- // 🍂section Drawing events
- // 🍂event editable:drawing:mousedown: Event
- // Fired when user `mousedown` while drawing.
- this.fireAndForward('editable:drawing:mousedown', e);
- },
-
- onDrawingMouseUp: function (e) {
- // 🍂namespace Editable
- // 🍂section Drawing events
- // 🍂event editable:drawing:mouseup: Event
- // Fired when user `mouseup` while drawing.
- this.fireAndForward('editable:drawing:mouseup', e);
- },
-
- startDrawing: function () {
- if (!this._drawing) this._drawing = L.Editable.FORWARD;
- this.tools.registerForDrawing(this);
- this.onStartDrawing();
- },
-
- commitDrawing: function (e) {
- this.onCommitDrawing(e);
- this.endDrawing();
- },
-
- cancelDrawing: function () {
- // If called during a vertex drag, the vertex will be removed before
- // the mouseup fires on it. This is a workaround. Maybe better fix is
- // To have L.Draggable reset it's status on disable (Leaflet side).
- L.Draggable._dragging = false;
- this.onCancelDrawing();
- this.endDrawing();
- },
-
- endDrawing: function () {
- this._drawing = false;
- this.tools.unregisterForDrawing(this);
- this.onEndDrawing();
- },
-
- onDrawingClick: function (e) {
- if (!this.drawing()) return;
- L.Editable.makeCancellable(e);
- // 🍂namespace Editable
- // 🍂section Drawing events
- // 🍂event editable:drawing:click: CancelableEvent
- // Fired when user `click` while drawing, before any internal action is being processed.
- this.fireAndForward('editable:drawing:click', e);
- if (e._cancelled) return;
- if (!this.isConnected()) this.connect(e);
- this.processDrawingClick(e);
- },
-
- isConnected: function () {
- return this.map.hasLayer(this.feature);
- },
-
- connect: function () {
- this.tools.connectCreatedToMap(this.feature);
- this.tools.editLayer.addLayer(this.editLayer);
- },
-
- onMove: function (e) {
- // 🍂namespace Editable
- // 🍂section Drawing events
- // 🍂event editable:drawing:move: Event
- // Fired when `move` mouse while drawing, while dragging a marker, and while dragging a vertex.
- this.fireAndForward('editable:drawing:move', e);
- },
-
- onDrawingMouseMove: function (e) {
- this.onMove(e);
- },
-
- _getEvents: function () {
- return {
- dragstart: this.onDragStart,
- drag: this.onDrag,
- dragend: this.onDragEnd,
- remove: this.disable
- };
- },
-
- onDragStart: function (e) {
- this.onEditing();
- // 🍂namespace Editable
- // 🍂event editable:dragstart: Event
- // Fired before a path feature is dragged.
- this.fireAndForward('editable:dragstart', e);
- },
-
- onDrag: function (e) {
- this.onMove(e);
- // 🍂namespace Editable
- // 🍂event editable:drag: Event
- // Fired when a path feature is being dragged.
- this.fireAndForward('editable:drag', e);
- },
-
- onDragEnd: function (e) {
- // 🍂namespace Editable
- // 🍂event editable:dragend: Event
- // Fired after a path feature has been dragged.
- this.fireAndForward('editable:dragend', e);
- }
-
- });
-
- // 🍂namespace Editable; 🍂class MarkerEditor; 🍂aka L.Editable.MarkerEditor
- // 🍂inherits BaseEditor
- // Editor for Marker.
- L.Editable.MarkerEditor = L.Editable.BaseEditor.extend({
-
- onDrawingMouseMove: function (e) {
- L.Editable.BaseEditor.prototype.onDrawingMouseMove.call(this, e);
- if (this._drawing) this.feature.setLatLng(e.latlng);
- },
-
- processDrawingClick: function (e) {
- // 🍂namespace Editable
- // 🍂section Drawing events
- // 🍂event editable:drawing:clicked: Event
- // Fired when user `click` while drawing, after all internal actions.
- this.fireAndForward('editable:drawing:clicked', e);
- this.commitDrawing(e);
- },
-
- connect: function (e) {
- // On touch, the latlng has not been updated because there is
- // no mousemove.
- if (e) this.feature._latlng = e.latlng;
- L.Editable.BaseEditor.prototype.connect.call(this, e);
- }
-
- });
-
- // 🍂namespace Editable; 🍂class PathEditor; 🍂aka L.Editable.PathEditor
- // 🍂inherits BaseEditor
- // Base class for all path editors.
- L.Editable.PathEditor = L.Editable.BaseEditor.extend({
-
- CLOSED: false,
- MIN_VERTEX: 2,
-
- addHooks: function () {
- L.Editable.BaseEditor.prototype.addHooks.call(this);
- if (this.feature) this.initVertexMarkers();
- return this;
- },
-
- initVertexMarkers: function (latlngs) {
- if (!this.enabled()) return;
- latlngs = latlngs || this.getLatLngs();
- if (isFlat(latlngs)) this.addVertexMarkers(latlngs);
- else for (var i = 0; i < latlngs.length; i++) this.initVertexMarkers(latlngs[i]);
- },
-
- getLatLngs: function () {
- return this.feature.getLatLngs();
- },
-
- // 🍂method reset()
- // Rebuild edit elements (Vertex, MiddleMarker, etc.).
- reset: function () {
- this.editLayer.clearLayers();
- this.initVertexMarkers();
- },
-
- addVertexMarker: function (latlng, latlngs) {
- return new this.tools.options.vertexMarkerClass(latlng, latlngs, this);
- },
-
- onNewVertex: function (vertex) {
- // 🍂namespace Editable
- // 🍂section Vertex events
- // 🍂event editable:vertex:new: VertexEvent
- // Fired when a new vertex is created.
- this.fireAndForward('editable:vertex:new', {latlng: vertex.latlng, vertex: vertex});
- },
-
- addVertexMarkers: function (latlngs) {
- for (var i = 0; i < latlngs.length; i++) {
- this.addVertexMarker(latlngs[i], latlngs);
- }
- },
-
- refreshVertexMarkers: function (latlngs) {
- latlngs = latlngs || this.getDefaultLatLngs();
- for (var i = 0; i < latlngs.length; i++) {
- latlngs[i].__vertex.update();
- }
- },
-
- addMiddleMarker: function (left, right, latlngs) {
- return new this.tools.options.middleMarkerClass(left, right, latlngs, this);
- },
-
- onVertexMarkerClick: function (e) {
- L.Editable.makeCancellable(e);
- // 🍂namespace Editable
- // 🍂section Vertex events
- // 🍂event editable:vertex:click: CancelableVertexEvent
- // Fired when a `click` is issued on a vertex, before any internal action is being processed.
- this.fireAndForward('editable:vertex:click', e);
- if (e._cancelled) return;
- if (this.tools.drawing() && this.tools._drawingEditor !== this) return;
- var index = e.vertex.getIndex(), commit;
- if (e.originalEvent.ctrlKey) {
- this.onVertexMarkerCtrlClick(e);
- } else if (e.originalEvent.altKey) {
- this.onVertexMarkerAltClick(e);
- } else if (e.originalEvent.shiftKey) {
- this.onVertexMarkerShiftClick(e);
- } else if (e.originalEvent.metaKey) {
- this.onVertexMarkerMetaKeyClick(e);
- } else if (index === e.vertex.getLastIndex() && this._drawing === L.Editable.FORWARD) {
- if (index >= this.MIN_VERTEX - 1) commit = true;
- } else if (index === 0 && this._drawing === L.Editable.BACKWARD && this._drawnLatLngs.length >= this.MIN_VERTEX) {
- commit = true;
- } else if (index === 0 && this._drawing === L.Editable.FORWARD && this._drawnLatLngs.length >= this.MIN_VERTEX && this.CLOSED) {
- commit = true; // Allow to close on first point also for polygons
- } else {
- this.onVertexRawMarkerClick(e);
- }
- // 🍂namespace Editable
- // 🍂section Vertex events
- // 🍂event editable:vertex:clicked: VertexEvent
- // Fired when a `click` is issued on a vertex, after all internal actions.
- this.fireAndForward('editable:vertex:clicked', e);
- if (commit) this.commitDrawing(e);
- },
-
- onVertexRawMarkerClick: function (e) {
- // 🍂namespace Editable
- // 🍂section Vertex events
- // 🍂event editable:vertex:rawclick: CancelableVertexEvent
- // Fired when a `click` is issued on a vertex without any special key and without being in drawing mode.
- this.fireAndForward('editable:vertex:rawclick', e);
- if (e._cancelled) return;
- if (!this.vertexCanBeDeleted(e.vertex)) return;
- e.vertex.delete();
- },
-
- vertexCanBeDeleted: function (vertex) {
- return vertex.latlngs.length > this.MIN_VERTEX;
- },
-
- onVertexDeleted: function (e) {
- // 🍂namespace Editable
- // 🍂section Vertex events
- // 🍂event editable:vertex:deleted: VertexEvent
- // Fired after a vertex has been deleted by user.
- this.fireAndForward('editable:vertex:deleted', e);
- },
-
- onVertexMarkerCtrlClick: function (e) {
- // 🍂namespace Editable
- // 🍂section Vertex events
- // 🍂event editable:vertex:ctrlclick: VertexEvent
- // Fired when a `click` with `ctrlKey` is issued on a vertex.
- this.fireAndForward('editable:vertex:ctrlclick', e);
- },
-
- onVertexMarkerShiftClick: function (e) {
- // 🍂namespace Editable
- // 🍂section Vertex events
- // 🍂event editable:vertex:shiftclick: VertexEvent
- // Fired when a `click` with `shiftKey` is issued on a vertex.
- this.fireAndForward('editable:vertex:shiftclick', e);
- },
-
- onVertexMarkerMetaKeyClick: function (e) {
- // 🍂namespace Editable
- // 🍂section Vertex events
- // 🍂event editable:vertex:metakeyclick: VertexEvent
- // Fired when a `click` with `metaKey` is issued on a vertex.
- this.fireAndForward('editable:vertex:metakeyclick', e);
- },
-
- onVertexMarkerAltClick: function (e) {
- // 🍂namespace Editable
- // 🍂section Vertex events
- // 🍂event editable:vertex:altclick: VertexEvent
- // Fired when a `click` with `altKey` is issued on a vertex.
- this.fireAndForward('editable:vertex:altclick', e);
- },
-
- onVertexMarkerContextMenu: function (e) {
- // 🍂namespace Editable
- // 🍂section Vertex events
- // 🍂event editable:vertex:contextmenu: VertexEvent
- // Fired when a `contextmenu` is issued on a vertex.
- this.fireAndForward('editable:vertex:contextmenu', e);
- },
-
- onVertexMarkerMouseDown: function (e) {
- // 🍂namespace Editable
- // 🍂section Vertex events
- // 🍂event editable:vertex:mousedown: VertexEvent
- // Fired when user `mousedown` a vertex.
- this.fireAndForward('editable:vertex:mousedown', e);
- },
-
- onVertexMarkerMouseOver: function (e) {
- // 🍂namespace Editable
- // 🍂section Vertex events
- // 🍂event editable:vertex:mouseover: VertexEvent
- // Fired when a user's mouse enters the vertex
- this.fireAndForward('editable:vertex:mouseover', e);
- },
-
- onVertexMarkerMouseOut: function (e) {
- // 🍂namespace Editable
- // 🍂section Vertex events
- // 🍂event editable:vertex:mouseout: VertexEvent
- // Fired when a user's mouse leaves the vertex
- this.fireAndForward('editable:vertex:mouseout', e);
- },
-
- onMiddleMarkerMouseDown: function (e) {
- // 🍂namespace Editable
- // 🍂section MiddleMarker events
- // 🍂event editable:middlemarker:mousedown: VertexEvent
- // Fired when user `mousedown` a middle marker.
- this.fireAndForward('editable:middlemarker:mousedown', e);
- },
-
- onVertexMarkerDrag: function (e) {
- this.onMove(e);
- if (this.feature._bounds) this.extendBounds(e);
- // 🍂namespace Editable
- // 🍂section Vertex events
- // 🍂event editable:vertex:drag: VertexEvent
- // Fired when a vertex is dragged by user.
- this.fireAndForward('editable:vertex:drag', e);
- },
-
- onVertexMarkerDragStart: function (e) {
- // 🍂namespace Editable
- // 🍂section Vertex events
- // 🍂event editable:vertex:dragstart: VertexEvent
- // Fired before a vertex is dragged by user.
- this.fireAndForward('editable:vertex:dragstart', e);
- },
-
- onVertexMarkerDragEnd: function (e) {
- // 🍂namespace Editable
- // 🍂section Vertex events
- // 🍂event editable:vertex:dragend: VertexEvent
- // Fired after a vertex is dragged by user.
- this.fireAndForward('editable:vertex:dragend', e);
- },
-
- setDrawnLatLngs: function (latlngs) {
- this._drawnLatLngs = latlngs || this.getDefaultLatLngs();
- },
-
- startDrawing: function () {
- if (!this._drawnLatLngs) this.setDrawnLatLngs();
- L.Editable.BaseEditor.prototype.startDrawing.call(this);
- },
-
- startDrawingForward: function () {
- this.startDrawing();
- },
-
- endDrawing: function () {
- this.tools.detachForwardLineGuide();
- this.tools.detachBackwardLineGuide();
- if (this._drawnLatLngs && this._drawnLatLngs.length < this.MIN_VERTEX) this.deleteShape(this._drawnLatLngs);
- L.Editable.BaseEditor.prototype.endDrawing.call(this);
- delete this._drawnLatLngs;
- },
-
- addLatLng: function (latlng) {
- if (this._drawing === L.Editable.FORWARD) this._drawnLatLngs.push(latlng);
- else this._drawnLatLngs.unshift(latlng);
- this.feature._bounds.extend(latlng);
- var vertex = this.addVertexMarker(latlng, this._drawnLatLngs);
- this.onNewVertex(vertex);
- this.refresh();
- },
-
- newPointForward: function (latlng) {
- this.addLatLng(latlng);
- this.tools.attachForwardLineGuide();
- this.tools.anchorForwardLineGuide(latlng);
- },
-
- newPointBackward: function (latlng) {
- this.addLatLng(latlng);
- this.tools.anchorBackwardLineGuide(latlng);
- },
-
- // 🍂namespace PathEditor
- // 🍂method push()
- // Programmatically add a point while drawing.
- push: function (latlng) {
- if (!latlng) return console.error('L.Editable.PathEditor.push expect a valid latlng as parameter');
- if (this._drawing === L.Editable.FORWARD) this.newPointForward(latlng);
- else this.newPointBackward(latlng);
- },
-
- removeLatLng: function (latlng) {
- latlng.__vertex.delete();
- this.refresh();
- },
-
- // 🍂method pop(): L.LatLng or null
- // Programmatically remove last point (if any) while drawing.
- pop: function () {
- if (this._drawnLatLngs.length <= 1) return;
- var latlng;
- if (this._drawing === L.Editable.FORWARD) latlng = this._drawnLatLngs[this._drawnLatLngs.length - 1];
- else latlng = this._drawnLatLngs[0];
- this.removeLatLng(latlng);
- if (this._drawing === L.Editable.FORWARD) this.tools.anchorForwardLineGuide(this._drawnLatLngs[this._drawnLatLngs.length - 1]);
- else this.tools.anchorForwardLineGuide(this._drawnLatLngs[0]);
- return latlng;
- },
-
- processDrawingClick: function (e) {
- if (e.vertex && e.vertex.editor === this) return;
- if (this._drawing === L.Editable.FORWARD) this.newPointForward(e.latlng);
- else this.newPointBackward(e.latlng);
- this.fireAndForward('editable:drawing:clicked', e);
- },
-
- onDrawingMouseMove: function (e) {
- L.Editable.BaseEditor.prototype.onDrawingMouseMove.call(this, e);
- if (this._drawing) {
- this.tools.moveForwardLineGuide(e.latlng);
- this.tools.moveBackwardLineGuide(e.latlng);
- }
- },
-
- refresh: function () {
- this.feature.redraw();
- this.onEditing();
- },
-
- // 🍂namespace PathEditor
- // 🍂method newShape(latlng?: L.LatLng)
- // Add a new shape (Polyline, Polygon) in a multi, and setup up drawing tools to draw it;
- // if optional `latlng` is given, start a path at this point.
- newShape: function (latlng) {
- var shape = this.addNewEmptyShape();
- if (!shape) return;
- this.setDrawnLatLngs(shape[0] || shape); // Polygon or polyline
- this.startDrawingForward();
- // 🍂namespace Editable
- // 🍂section Shape events
- // 🍂event editable:shape:new: ShapeEvent
- // Fired when a new shape is created in a multi (Polygon or Polyline).
- this.fireAndForward('editable:shape:new', {shape: shape});
- if (latlng) this.newPointForward(latlng);
- },
-
- deleteShape: function (shape, latlngs) {
- var e = {shape: shape};
- L.Editable.makeCancellable(e);
- // 🍂namespace Editable
- // 🍂section Shape events
- // 🍂event editable:shape:delete: CancelableShapeEvent
- // Fired before a new shape is deleted in a multi (Polygon or Polyline).
- this.fireAndForward('editable:shape:delete', e);
- if (e._cancelled) return;
- shape = this._deleteShape(shape, latlngs);
- if (this.ensureNotFlat) this.ensureNotFlat(); // Polygon.
- this.feature.setLatLngs(this.getLatLngs()); // Force bounds reset.
- this.refresh();
- this.reset();
- // 🍂namespace Editable
- // 🍂section Shape events
- // 🍂event editable:shape:deleted: ShapeEvent
- // Fired after a new shape is deleted in a multi (Polygon or Polyline).
- this.fireAndForward('editable:shape:deleted', {shape: shape});
- return shape;
- },
-
- _deleteShape: function (shape, latlngs) {
- latlngs = latlngs || this.getLatLngs();
- if (!latlngs.length) return;
- var self = this,
- inplaceDelete = function (latlngs, shape) {
- // Called when deleting a flat latlngs
- shape = latlngs.splice(0, Number.MAX_VALUE);
- return shape;
- },
- spliceDelete = function (latlngs, shape) {
- // Called when removing a latlngs inside an array
- latlngs.splice(latlngs.indexOf(shape), 1);
- if (!latlngs.length) self._deleteShape(latlngs);
- return shape;
- };
- if (latlngs === shape) return inplaceDelete(latlngs, shape);
- for (var i = 0; i < latlngs.length; i++) {
- if (latlngs[i] === shape) return spliceDelete(latlngs, shape);
- else if (latlngs[i].indexOf(shape) !== -1) return spliceDelete(latlngs[i], shape);
- }
- },
-
- // 🍂namespace PathEditor
- // 🍂method deleteShapeAt(latlng: L.LatLng): Array
- // Remove a path shape at the given `latlng`.
- deleteShapeAt: function (latlng) {
- var shape = this.feature.shapeAt(latlng);
- if (shape) return this.deleteShape(shape);
- },
-
- // 🍂method appendShape(shape: Array)
- // Append a new shape to the Polygon or Polyline.
- appendShape: function (shape) {
- this.insertShape(shape);
- },
-
- // 🍂method prependShape(shape: Array)
- // Prepend a new shape to the Polygon or Polyline.
- prependShape: function (shape) {
- this.insertShape(shape, 0);
- },
-
- // 🍂method insertShape(shape: Array, index: int)
- // Insert a new shape to the Polygon or Polyline at given index (default is to append).
- insertShape: function (shape, index) {
- this.ensureMulti();
- shape = this.formatShape(shape);
- if (typeof index === 'undefined') index = this.feature._latlngs.length;
- this.feature._latlngs.splice(index, 0, shape);
- this.feature.redraw();
- if (this._enabled) this.reset();
- },
-
- extendBounds: function (e) {
- this.feature._bounds.extend(e.vertex.latlng);
- },
-
- onDragStart: function (e) {
- this.editLayer.clearLayers();
- L.Editable.BaseEditor.prototype.onDragStart.call(this, e);
- },
-
- onDragEnd: function (e) {
- this.initVertexMarkers();
- L.Editable.BaseEditor.prototype.onDragEnd.call(this, e);
- }
-
- });
-
- // 🍂namespace Editable; 🍂class PolylineEditor; 🍂aka L.Editable.PolylineEditor
- // 🍂inherits PathEditor
- L.Editable.PolylineEditor = L.Editable.PathEditor.extend({
-
- startDrawingBackward: function () {
- this._drawing = L.Editable.BACKWARD;
- this.startDrawing();
- },
-
- // 🍂method continueBackward(latlngs?: Array)
- // Set up drawing tools to continue the line backward.
- continueBackward: function (latlngs) {
- if (this.drawing()) return;
- latlngs = latlngs || this.getDefaultLatLngs();
- this.setDrawnLatLngs(latlngs);
- if (latlngs.length > 0) {
- this.tools.attachBackwardLineGuide();
- this.tools.anchorBackwardLineGuide(latlngs[0]);
- }
- this.startDrawingBackward();
- },
-
- // 🍂method continueForward(latlngs?: Array)
- // Set up drawing tools to continue the line forward.
- continueForward: function (latlngs) {
- if (this.drawing()) return;
- latlngs = latlngs || this.getDefaultLatLngs();
- this.setDrawnLatLngs(latlngs);
- if (latlngs.length > 0) {
- this.tools.attachForwardLineGuide();
- this.tools.anchorForwardLineGuide(latlngs[latlngs.length - 1]);
- }
- this.startDrawingForward();
- },
-
- getDefaultLatLngs: function (latlngs) {
- latlngs = latlngs || this.feature._latlngs;
- if (!latlngs.length || latlngs[0] instanceof L.LatLng) return latlngs;
- else return this.getDefaultLatLngs(latlngs[0]);
- },
-
- ensureMulti: function () {
- if (this.feature._latlngs.length && isFlat(this.feature._latlngs)) {
- this.feature._latlngs = [this.feature._latlngs];
- }
- },
-
- addNewEmptyShape: function () {
- if (this.feature._latlngs.length) {
- var shape = [];
- this.appendShape(shape);
- return shape;
- } else {
- return this.feature._latlngs;
- }
- },
-
- formatShape: function (shape) {
- if (isFlat(shape)) return shape;
- else if (shape[0]) return this.formatShape(shape[0]);
- },
-
- // 🍂method splitShape(latlngs?: Array, index: int)
- // Split the given `latlngs` shape at index `index` and integrate new shape in instance `latlngs`.
- splitShape: function (shape, index) {
- if (!index || index >= shape.length - 1) return;
- this.ensureMulti();
- var shapeIndex = this.feature._latlngs.indexOf(shape);
- if (shapeIndex === -1) return;
- var first = shape.slice(0, index + 1),
- second = shape.slice(index);
- // We deal with reference, we don't want twice the same latlng around.
- second[0] = L.latLng(second[0].lat, second[0].lng, second[0].alt);
- this.feature._latlngs.splice(shapeIndex, 1, first, second);
- this.refresh();
- this.reset();
- }
-
- });
-
- // 🍂namespace Editable; 🍂class PolygonEditor; 🍂aka L.Editable.PolygonEditor
- // 🍂inherits PathEditor
- L.Editable.PolygonEditor = L.Editable.PathEditor.extend({
-
- CLOSED: true,
- MIN_VERTEX: 3,
-
- newPointForward: function (latlng) {
- L.Editable.PathEditor.prototype.newPointForward.call(this, latlng);
- if (!this.tools.backwardLineGuide._latlngs.length) this.tools.anchorBackwardLineGuide(latlng);
- if (this._drawnLatLngs.length === 2) this.tools.attachBackwardLineGuide();
- },
-
- addNewEmptyHole: function (latlng) {
- this.ensureNotFlat();
- var latlngs = this.feature.shapeAt(latlng);
- if (!latlngs) return;
- var holes = [];
- latlngs.push(holes);
- return holes;
- },
-
- // 🍂method newHole(latlng?: L.LatLng, index: int)
- // Set up drawing tools for creating a new hole on the Polygon. If the `latlng` param is given, a first point is created.
- newHole: function (latlng) {
- var holes = this.addNewEmptyHole(latlng);
- if (!holes) return;
- this.setDrawnLatLngs(holes);
- this.startDrawingForward();
- if (latlng) this.newPointForward(latlng);
- },
-
- addNewEmptyShape: function () {
- if (this.feature._latlngs.length && this.feature._latlngs[0].length) {
- var shape = [];
- this.appendShape(shape);
- return shape;
- } else {
- return this.feature._latlngs;
- }
- },
-
- ensureMulti: function () {
- if (this.feature._latlngs.length && isFlat(this.feature._latlngs[0])) {
- this.feature._latlngs = [this.feature._latlngs];
- }
- },
-
- ensureNotFlat: function () {
- if (!this.feature._latlngs.length || isFlat(this.feature._latlngs)) this.feature._latlngs = [this.feature._latlngs];
- },
-
- vertexCanBeDeleted: function (vertex) {
- var parent = this.feature.parentShape(vertex.latlngs),
- idx = L.Util.indexOf(parent, vertex.latlngs);
- if (idx > 0) return true; // Holes can be totally deleted without removing the layer itself.
- return L.Editable.PathEditor.prototype.vertexCanBeDeleted.call(this, vertex);
- },
-
- getDefaultLatLngs: function () {
- if (!this.feature._latlngs.length) this.feature._latlngs.push([]);
- return this.feature._latlngs[0];
- },
-
- formatShape: function (shape) {
- // [[1, 2], [3, 4]] => must be nested
- // [] => must be nested
- // [[]] => is already nested
- if (isFlat(shape) && (!shape[0] || shape[0].length !== 0)) return [shape];
- else return shape;
- }
-
- });
-
- // 🍂namespace Editable; 🍂class RectangleEditor; 🍂aka L.Editable.RectangleEditor
- // 🍂inherits PathEditor
- L.Editable.RectangleEditor = L.Editable.PathEditor.extend({
-
- CLOSED: true,
- MIN_VERTEX: 4,
-
- options: {
- skipMiddleMarkers: true
- },
-
- extendBounds: function (e) {
- var index = e.vertex.getIndex(),
- next = e.vertex.getNext(),
- previous = e.vertex.getPrevious(),
- oppositeIndex = (index + 2) % 4,
- opposite = e.vertex.latlngs[oppositeIndex],
- bounds = new L.LatLngBounds(e.latlng, opposite);
- // Update latlngs by hand to preserve order.
- previous.latlng.update([e.latlng.lat, opposite.lng]);
- next.latlng.update([opposite.lat, e.latlng.lng]);
- this.updateBounds(bounds);
- this.refreshVertexMarkers();
- },
-
- onDrawingMouseDown: function (e) {
- L.Editable.PathEditor.prototype.onDrawingMouseDown.call(this, e);
- this.connect();
- var latlngs = this.getDefaultLatLngs();
- // L.Polygon._convertLatLngs removes last latlng if it equals first point,
- // which is the case here as all latlngs are [0, 0]
- if (latlngs.length === 3) latlngs.push(e.latlng);
- var bounds = new L.LatLngBounds(e.latlng, e.latlng);
- this.updateBounds(bounds);
- this.updateLatLngs(bounds);
- this.refresh();
- this.reset();
- // Stop dragging map.
- // L.Draggable has two workflows:
- // - mousedown => mousemove => mouseup
- // - touchstart => touchmove => touchend
- // Problem: L.Map.Tap does not allow us to listen to touchstart, so we only
- // can deal with mousedown, but then when in a touch device, we are dealing with
- // simulated events (actually simulated by L.Map.Tap), which are no more taken
- // into account by L.Draggable.
- // Ref.: https://github.com/Leaflet/Leaflet.Editable/issues/103
- e.originalEvent._simulated = false;
- this.map.dragging._draggable._onUp(e.originalEvent);
- // Now transfer ongoing drag action to the bottom right corner.
- // Should we refine which corner will handle the drag according to
- // drag direction?
- latlngs[3].__vertex.dragging._draggable._onDown(e.originalEvent);
- },
-
- onDrawingMouseUp: function (e) {
- this.commitDrawing(e);
- e.originalEvent._simulated = false;
- L.Editable.PathEditor.prototype.onDrawingMouseUp.call(this, e);
- },
-
- onDrawingMouseMove: function (e) {
- e.originalEvent._simulated = false;
- L.Editable.PathEditor.prototype.onDrawingMouseMove.call(this, e);
- },
-
-
- getDefaultLatLngs: function (latlngs) {
- return latlngs || this.feature._latlngs[0];
- },
-
- updateBounds: function (bounds) {
- this.feature._bounds = bounds;
- },
-
- updateLatLngs: function (bounds) {
- var latlngs = this.getDefaultLatLngs(),
- newLatlngs = this.feature._boundsToLatLngs(bounds);
- // Keep references.
- for (var i = 0; i < latlngs.length; i++) {
- latlngs[i].update(newLatlngs[i]);
- }
- }
-
- });
-
- // 🍂namespace Editable; 🍂class CircleEditor; 🍂aka L.Editable.CircleEditor
- // 🍂inherits PathEditor
- L.Editable.CircleEditor = L.Editable.PathEditor.extend({
-
- MIN_VERTEX: 2,
-
- options: {
- skipMiddleMarkers: true
- },
-
- initialize: function (map, feature, options) {
- L.Editable.PathEditor.prototype.initialize.call(this, map, feature, options);
- this._resizeLatLng = this.computeResizeLatLng();
- },
-
- computeResizeLatLng: function () {
- // While circle is not added to the map, _radius is not set.
- var delta = (this.feature._radius || this.feature._mRadius) * Math.cos(Math.PI / 4),
- point = this.map.project(this.feature._latlng);
- return this.map.unproject([point.x + delta, point.y - delta]);
- },
-
- updateResizeLatLng: function () {
- this._resizeLatLng.update(this.computeResizeLatLng());
- this._resizeLatLng.__vertex.update();
- },
-
- getLatLngs: function () {
- return [this.feature._latlng, this._resizeLatLng];
- },
-
- getDefaultLatLngs: function () {
- return this.getLatLngs();
- },
-
- onVertexMarkerDrag: function (e) {
- if (e.vertex.getIndex() === 1) this.resize(e);
- else this.updateResizeLatLng(e);
- L.Editable.PathEditor.prototype.onVertexMarkerDrag.call(this, e);
- },
-
- resize: function (e) {
- var radius = this.feature._latlng.distanceTo(e.latlng);
- this.feature.setRadius(radius);
- },
-
- onDrawingMouseDown: function (e) {
- L.Editable.PathEditor.prototype.onDrawingMouseDown.call(this, e);
- this._resizeLatLng.update(e.latlng);
- this.feature._latlng.update(e.latlng);
- this.connect();
- // Stop dragging map.
- e.originalEvent._simulated = false;
- this.map.dragging._draggable._onUp(e.originalEvent);
- // Now transfer ongoing drag action to the radius handler.
- this._resizeLatLng.__vertex.dragging._draggable._onDown(e.originalEvent);
- },
-
- onDrawingMouseUp: function (e) {
- this.commitDrawing(e);
- e.originalEvent._simulated = false;
- L.Editable.PathEditor.prototype.onDrawingMouseUp.call(this, e);
- },
-
- onDrawingMouseMove: function (e) {
- e.originalEvent._simulated = false;
- L.Editable.PathEditor.prototype.onDrawingMouseMove.call(this, e);
- },
-
- onDrag: function (e) {
- L.Editable.PathEditor.prototype.onDrag.call(this, e);
- this.feature.dragging.updateLatLng(this._resizeLatLng);
- }
-
- });
-
- // 🍂namespace Editable; 🍂class EditableMixin
- // `EditableMixin` is included to `L.Polyline`, `L.Polygon`, `L.Rectangle`, `L.Circle`
- // and `L.Marker`. It adds some methods to them.
- // *When editing is enabled, the editor is accessible on the instance with the
- // `editor` property.*
- var EditableMixin = {
-
- createEditor: function (map) {
- map = map || this._map;
- var tools = (this.options.editOptions || {}).editTools || map.editTools;
- if (!tools) throw Error('Unable to detect Editable instance.');
- var Klass = this.options.editorClass || this.getEditorClass(tools);
- return new Klass(map, this, this.options.editOptions);
- },
-
- // 🍂method enableEdit(map?: L.Map): this.editor
- // Enable editing, by creating an editor if not existing, and then calling `enable` on it.
- enableEdit: function (map) {
- if (!this.editor) this.createEditor(map);
- this.editor.enable();
- return this.editor;
- },
-
- // 🍂method editEnabled(): boolean
- // Return true if current instance has an editor attached, and this editor is enabled.
- editEnabled: function () {
- return this.editor && this.editor.enabled();
- },
-
- // 🍂method disableEdit()
- // Disable editing, also remove the editor property reference.
- disableEdit: function () {
- if (this.editor) {
- this.editor.disable();
- delete this.editor;
- }
- },
-
- // 🍂method toggleEdit()
- // Enable or disable editing, according to current status.
- toggleEdit: function () {
- if (this.editEnabled()) this.disableEdit();
- else this.enableEdit();
- },
-
- _onEditableAdd: function () {
- if (this.editor) this.enableEdit();
- }
-
- };
-
- var PolylineMixin = {
-
- getEditorClass: function (tools) {
- return (tools && tools.options.polylineEditorClass) ? tools.options.polylineEditorClass : L.Editable.PolylineEditor;
- },
-
- shapeAt: function (latlng, latlngs) {
- // We can have those cases:
- // - latlngs are just a flat array of latlngs, use this
- // - latlngs is an array of arrays of latlngs, loop over
- var shape = null;
- latlngs = latlngs || this._latlngs;
- if (!latlngs.length) return shape;
- else if (isFlat(latlngs) && this.isInLatLngs(latlng, latlngs)) shape = latlngs;
- else for (var i = 0; i < latlngs.length; i++) if (this.isInLatLngs(latlng, latlngs[i])) return latlngs[i];
- return shape;
- },
-
- isInLatLngs: function (l, latlngs) {
- if (!latlngs) return false;
- var i, k, len, part = [], p,
- w = this._clickTolerance();
- this._projectLatlngs(latlngs, part, this._pxBounds);
- part = part[0];
- p = this._map.latLngToLayerPoint(l);
-
- if (!this._pxBounds.contains(p)) { return false; }
- for (i = 1, len = part.length, k = 0; i < len; k = i++) {
-
- if (L.LineUtil.pointToSegmentDistance(p, part[k], part[i]) <= w) {
- return true;
- }
- }
- return false;
- }
-
- };
-
- var PolygonMixin = {
-
- getEditorClass: function (tools) {
- return (tools && tools.options.polygonEditorClass) ? tools.options.polygonEditorClass : L.Editable.PolygonEditor;
- },
-
- shapeAt: function (latlng, latlngs) {
- // We can have those cases:
- // - latlngs are just a flat array of latlngs, use this
- // - latlngs is an array of arrays of latlngs, this is a simple polygon (maybe with holes), use the first
- // - latlngs is an array of arrays of arrays, this is a multi, loop over
- var shape = null;
- latlngs = latlngs || this._latlngs;
- if (!latlngs.length) return shape;
- else if (isFlat(latlngs) && this.isInLatLngs(latlng, latlngs)) shape = latlngs;
- else if (isFlat(latlngs[0]) && this.isInLatLngs(latlng, latlngs[0])) shape = latlngs;
- else for (var i = 0; i < latlngs.length; i++) if (this.isInLatLngs(latlng, latlngs[i][0])) return latlngs[i];
- return shape;
- },
-
- isInLatLngs: function (l, latlngs) {
- var inside = false, l1, l2, j, k, len2;
-
- for (j = 0, len2 = latlngs.length, k = len2 - 1; j < len2; k = j++) {
- l1 = latlngs[j];
- l2 = latlngs[k];
-
- if (((l1.lat > l.lat) !== (l2.lat > l.lat)) &&
- (l.lng < (l2.lng - l1.lng) * (l.lat - l1.lat) / (l2.lat - l1.lat) + l1.lng)) {
- inside = !inside;
- }
- }
-
- return inside;
- },
-
- parentShape: function (shape, latlngs) {
- latlngs = latlngs || this._latlngs;
- if (!latlngs) return;
- var idx = L.Util.indexOf(latlngs, shape);
- if (idx !== -1) return latlngs;
- for (var i = 0; i < latlngs.length; i++) {
- idx = L.Util.indexOf(latlngs[i], shape);
- if (idx !== -1) return latlngs[i];
- }
- }
-
- };
-
-
- var MarkerMixin = {
-
- getEditorClass: function (tools) {
- return (tools && tools.options.markerEditorClass) ? tools.options.markerEditorClass : L.Editable.MarkerEditor;
- }
-
- };
-
- var RectangleMixin = {
-
- getEditorClass: function (tools) {
- return (tools && tools.options.rectangleEditorClass) ? tools.options.rectangleEditorClass : L.Editable.RectangleEditor;
- }
-
- };
-
- var CircleMixin = {
-
- getEditorClass: function (tools) {
- return (tools && tools.options.circleEditorClass) ? tools.options.circleEditorClass : L.Editable.CircleEditor;
- }
-
- };
-
- var keepEditable = function () {
- // Make sure you can remove/readd an editable layer.
- this.on('add', this._onEditableAdd);
- };
-
- var isFlat = L.LineUtil.isFlat || L.LineUtil._flat || L.Polyline._flat; // <=> 1.1 compat.
-
-
- if (L.Polyline) {
- L.Polyline.include(EditableMixin);
- L.Polyline.include(PolylineMixin);
- L.Polyline.addInitHook(keepEditable);
- }
- if (L.Polygon) {
- L.Polygon.include(EditableMixin);
- L.Polygon.include(PolygonMixin);
- }
- if (L.Marker) {
- L.Marker.include(EditableMixin);
- L.Marker.include(MarkerMixin);
- L.Marker.addInitHook(keepEditable);
- }
- if (L.Rectangle) {
- L.Rectangle.include(EditableMixin);
- L.Rectangle.include(RectangleMixin);
- }
- if (L.Circle) {
- L.Circle.include(EditableMixin);
- L.Circle.include(CircleMixin);
- }
-
- L.LatLng.prototype.update = function (latlng) {
- latlng = L.latLng(latlng);
- this.lat = latlng.lat;
- this.lng = latlng.lng;
- }
-
-}, window));
diff --git a/www/wiki/extensions/Maps/resources/leaflet/leaflet.editor.js b/www/wiki/extensions/Maps/resources/leaflet/leaflet.editor.js
deleted file mode 100644
index 6aadad18..00000000
--- a/www/wiki/extensions/Maps/resources/leaflet/leaflet.editor.js
+++ /dev/null
@@ -1,15 +0,0 @@
-(function( $, mw ) {
-
- $( document ).ready( function() {
- var map = L.map('GeoJsonMap'/*, {editable: true}*/);
-
- L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
- attribution: '&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
- }).addTo(map);
-
- var geoJsonLayer = L.geoJSON(GeoJson).addTo(map);
-
- map.fitBounds(geoJsonLayer.getBounds());
- } );
-
-})( window.jQuery, mediaWiki );
diff --git a/www/wiki/extensions/Maps/resources/leaflet/leaflet.fullscreen/.jshintrc b/www/wiki/extensions/Maps/resources/leaflet/leaflet.fullscreen/.jshintrc
deleted file mode 100644
index b7636119..00000000
--- a/www/wiki/extensions/Maps/resources/leaflet/leaflet.fullscreen/.jshintrc
+++ /dev/null
@@ -1,12 +0,0 @@
-{
- "browser": true,
- "curly": true,
- "eqeqeq": true,
- "undef": true,
- "quotmark": "single",
- "trailing": true,
- "globals": {
- "L": true,
- "jQuery": true
- }
-} \ No newline at end of file
diff --git a/www/wiki/extensions/Maps/resources/leaflet/leaflet.fullscreen/Control.FullScreen.css b/www/wiki/extensions/Maps/resources/leaflet/leaflet.fullscreen/Control.FullScreen.css
deleted file mode 100644
index c93b1bf9..00000000
--- a/www/wiki/extensions/Maps/resources/leaflet/leaflet.fullscreen/Control.FullScreen.css
+++ /dev/null
@@ -1,4 +0,0 @@
-.leaflet-control-zoom-fullscreen { background-image: url(icon-fullscreen.png); }
-.leaflet-retina .leaflet-control-zoom-fullscreen { background-image: url(icon-fullscreen-2x.png); background-size: 26px 26px; }
-.leaflet-container:-webkit-full-screen { width: 100% !important; height: 100% !important; z-index: 99999; }
-.leaflet-pseudo-fullscreen { position: fixed !important; width: 100% !important; height: 100% !important; top: 0px !important; left: 0px !important; z-index: 99999; } \ No newline at end of file
diff --git a/www/wiki/extensions/Maps/resources/leaflet/leaflet.fullscreen/Control.FullScreen.js b/www/wiki/extensions/Maps/resources/leaflet/leaflet.fullscreen/Control.FullScreen.js
deleted file mode 100644
index f1cd7ccc..00000000
--- a/www/wiki/extensions/Maps/resources/leaflet/leaflet.fullscreen/Control.FullScreen.js
+++ /dev/null
@@ -1,164 +0,0 @@
-(function() {
-
-L.Control.FullScreen = L.Control.extend({
- options: {
- position: 'topleft',
- title: 'Full Screen',
- forceSeparateButton: false,
- forcePseudoFullscreen: false
- },
-
- onAdd: function (map) {
- var className = 'leaflet-control-zoom-fullscreen', container;
-
- if (map.zoomControl && !this.options.forceSeparateButton) {
- container = map.zoomControl._container;
- } else {
- container = L.DomUtil.create('div', 'leaflet-bar');
- }
-
- this._createButton(this.options.title, className, container, this.toggleFullScreen, this);
-
- return container;
- },
-
- _createButton: function (title, className, container, fn, context) {
- var link = L.DomUtil.create('a', className, container);
- link.href = '#';
- link.title = title;
-
- L.DomEvent
- .addListener(link, 'click', L.DomEvent.stopPropagation)
- .addListener(link, 'click', L.DomEvent.preventDefault)
- .addListener(link, 'click', fn, context);
-
- L.DomEvent
- .addListener(container, fullScreenApi.fullScreenEventName, L.DomEvent.stopPropagation)
- .addListener(container, fullScreenApi.fullScreenEventName, L.DomEvent.preventDefault)
- .addListener(container, fullScreenApi.fullScreenEventName, this._handleEscKey, context);
-
- L.DomEvent
- .addListener(document, fullScreenApi.fullScreenEventName, L.DomEvent.stopPropagation)
- .addListener(document, fullScreenApi.fullScreenEventName, L.DomEvent.preventDefault)
- .addListener(document, fullScreenApi.fullScreenEventName, this._handleEscKey, context);
-
- return link;
- },
-
- toggleFullScreen: function () {
- var map = this._map;
- map._exitFired = false;
- if (map._isFullscreen) {
- if (fullScreenApi.supportsFullScreen && !this.options.forcePseudoFullscreen) {
- fullScreenApi.cancelFullScreen(map._container);
- } else {
- L.DomUtil.removeClass(map._container, 'leaflet-pseudo-fullscreen');
- }
- map.invalidateSize();
- map.fire('exitFullscreen');
- map._exitFired = true;
- map._isFullscreen = false;
- }
- else {
- if (fullScreenApi.supportsFullScreen && !this.options.forcePseudoFullscreen) {
- fullScreenApi.requestFullScreen(map._container);
- } else {
- L.DomUtil.addClass(map._container, 'leaflet-pseudo-fullscreen');
- }
- map.invalidateSize();
- map.fire('enterFullscreen');
- map._isFullscreen = true;
- }
- },
-
- _handleEscKey: function () {
- var map = this._map;
- if (!fullScreenApi.isFullScreen(map) && !map._exitFired) {
- map.fire('exitFullscreen');
- map._exitFired = true;
- map._isFullscreen = false;
- }
- }
-});
-
-L.Map.addInitHook(function () {
- if (this.options.fullscreenControl) {
- this.fullscreenControl = L.control.fullscreen(this.options.fullscreenControlOptions);
- this.addControl(this.fullscreenControl);
- }
-});
-
-L.control.fullscreen = function (options) {
- return new L.Control.FullScreen(options);
-};
-
-/*
-Native FullScreen JavaScript API
--------------
-Assumes Mozilla naming conventions instead of W3C for now
-
-source : http://johndyer.name/native-fullscreen-javascript-api-plus-jquery-plugin/
-
-*/
-
- var
- fullScreenApi = {
- supportsFullScreen: false,
- isFullScreen: function() { return false; },
- requestFullScreen: function() {},
- cancelFullScreen: function() {},
- fullScreenEventName: '',
- prefix: ''
- },
- browserPrefixes = 'webkit moz o ms khtml'.split(' ');
-
- // check for native support
- if (typeof document.exitFullscreen !== 'undefined') {
- fullScreenApi.supportsFullScreen = true;
- } else {
- // check for fullscreen support by vendor prefix
- for (var i = 0, il = browserPrefixes.length; i < il; i++ ) {
- fullScreenApi.prefix = browserPrefixes[i];
- if (typeof document[fullScreenApi.prefix + 'CancelFullScreen' ] !== 'undefined' ) {
- fullScreenApi.supportsFullScreen = true;
- break;
- }
- }
- }
-
- // update methods to do something useful
- if (fullScreenApi.supportsFullScreen) {
- fullScreenApi.fullScreenEventName = fullScreenApi.prefix + 'fullscreenchange';
- fullScreenApi.isFullScreen = function() {
- switch (this.prefix) {
- case '':
- return document.fullScreen;
- case 'webkit':
- return document.webkitIsFullScreen;
- default:
- return document[this.prefix + 'FullScreen'];
- }
- };
- fullScreenApi.requestFullScreen = function(el) {
- return (this.prefix === '') ? el.requestFullscreen() : el[this.prefix + 'RequestFullScreen']();
- };
- fullScreenApi.cancelFullScreen = function(el) {
- return (this.prefix === '') ? document.exitFullscreen() : document[this.prefix + 'CancelFullScreen']();
- };
- }
-
- // jQuery plugin
- if (typeof jQuery !== 'undefined') {
- jQuery.fn.requestFullScreen = function() {
- return this.each(function() {
- var el = jQuery(this);
- if (fullScreenApi.supportsFullScreen) {
- fullScreenApi.requestFullScreen(el);
- }
- });
- };
- }
-
- // export api
- window.fullScreenApi = fullScreenApi;
-})();
diff --git a/www/wiki/extensions/Maps/resources/leaflet/leaflet.fullscreen/LICENSE b/www/wiki/extensions/Maps/resources/leaflet/leaflet.fullscreen/LICENSE
deleted file mode 100644
index 07ddddcc..00000000
--- a/www/wiki/extensions/Maps/resources/leaflet/leaflet.fullscreen/LICENSE
+++ /dev/null
@@ -1,22 +0,0 @@
-Copyright (c) 2013, Bruno Bergot
-All rights reserved.
-
-Redistribution and use in source and binary forms, with or without modification, are
-permitted provided that the following conditions are met:
-
- 1. Redistributions of source code must retain the above copyright notice, this list of
- conditions and the following disclaimer.
-
- 2. Redistributions in binary form must reproduce the above copyright notice, this list
- of conditions and the following disclaimer in the documentation and/or other materials
- provided with the distribution.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
-EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
-MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
-COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
-EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
-HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
-TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
-SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/www/wiki/extensions/Maps/resources/leaflet/leaflet.fullscreen/README.md b/www/wiki/extensions/Maps/resources/leaflet/leaflet.fullscreen/README.md
deleted file mode 100644
index a111801f..00000000
--- a/www/wiki/extensions/Maps/resources/leaflet/leaflet.fullscreen/README.md
+++ /dev/null
@@ -1,68 +0,0 @@
-Leaflet.Control.FullScreen
-============
-
-What ?
-------
-
-Simple plugin for Leaflet that adds fullscreen button to your maps.
-
-Inspired by http://elidupuis.github.com/leaflet.zoomfs/
-
-Use the native javascript fullscreen API http://johndyer.name/native-fullscreen-javascript-api-plus-jquery-plugin/
-
-Released under the MIT License http://opensource.org/licenses/mit-license.php
-
-How ?
-------
-
-Include Control.FullScreen.js and Control.FullScreen.css in your page:
-
-``` html
- <link rel="stylesheet" href="Control.FullScreen.css" />
- <script src="Control.FullScreen.js"></script>
-```
-
-Add the fullscreen control to the map:
-
-``` js
-var map = new L.Map('map', {
- fullscreenControl: true,
- fullscreenControlOptions: {
- position: 'topleft'
- }
-});
-```
-
-If your map have a zoomControl the fullscreen button will be added at the bottom of this one.
-
-If your map doesn't have a zoomContron the fullscreen button will be added to topleft corner of the map (same as the zoomcontrol).
-
-__Events and options__:
-
-``` js
-// create a fullscreen button and add it to the map
-L.control.fullscreen({
- position: 'topleft', // change the position of the button can be topleft, topright, bottomright or bottomleft, defaut topleft
- title: 'Show me the fullscreen !', // change the title of the button, default Full Screen
- forceSeparateButton: true, // force seperate button to detach from zoom buttons, default false
- forcePseudoFullscreen: true // force use of pseudo full screen even if full screen API is available, default false
-}).addTo(map);
-
-// events are fired when entering or exiting fullscreen.
-map.on('enterFullscreen', function(){
- console.log('entered fullscreen');
-});
-
-map.on('exitFullscreen', function(){
- console.log('exited fullscreen');
-});
-```
-
-Where ?
-------
-
-Source code : https://github.com/brunob/leaflet.fullscreen
-
-Downloads : https://github.com/brunob/leaflet.fullscreen/releases
-
-Demo : http://brunob.github.com/leaflet.fullscreen/
diff --git a/www/wiki/extensions/Maps/resources/leaflet/leaflet.fullscreen/bower.json b/www/wiki/extensions/Maps/resources/leaflet/leaflet.fullscreen/bower.json
deleted file mode 100644
index 6de9eee8..00000000
--- a/www/wiki/extensions/Maps/resources/leaflet/leaflet.fullscreen/bower.json
+++ /dev/null
@@ -1,30 +0,0 @@
-{
- "name": "leaflet.fullscreen",
- "version": "1.1.4",
- "homepage": "https://github.com/brunob/leaflet.fullscreen",
- "authors": [
- "brunob <brunobergot@gmail.com>"
- ],
- "description": "Leaflet.Control.FullScreen for Leaflet",
- "main": [
- "Control.FullScreen.js",
- "Control.FullScreen.css",
- "icon-fullscreen.png",
- "icon-fullscreen-2x.png"
- ],
- "keywords": [
- "leaflet",
- "plugins",
- "maps",
- "fullscreen"
- ],
- "license": "MIT",
- "ignore": [
- "**/.*",
- "node_modules",
- "bower_components",
- "test",
- "tests",
- "index.html"
- ]
-}
diff --git a/www/wiki/extensions/Maps/resources/leaflet/leaflet.fullscreen/icon-fullscreen-2x.png b/www/wiki/extensions/Maps/resources/leaflet/leaflet.fullscreen/icon-fullscreen-2x.png
deleted file mode 100644
index 7320d953..00000000
--- a/www/wiki/extensions/Maps/resources/leaflet/leaflet.fullscreen/icon-fullscreen-2x.png
+++ /dev/null
Binary files differ
diff --git a/www/wiki/extensions/Maps/resources/leaflet/leaflet.fullscreen/icon-fullscreen.png b/www/wiki/extensions/Maps/resources/leaflet/leaflet.fullscreen/icon-fullscreen.png
deleted file mode 100644
index 17478145..00000000
--- a/www/wiki/extensions/Maps/resources/leaflet/leaflet.fullscreen/icon-fullscreen.png
+++ /dev/null
Binary files differ
diff --git a/www/wiki/extensions/Maps/resources/leaflet/leaflet.fullscreen/index.html b/www/wiki/extensions/Maps/resources/leaflet/leaflet.fullscreen/index.html
deleted file mode 100644
index 87b345c4..00000000
--- a/www/wiki/extensions/Maps/resources/leaflet/leaflet.fullscreen/index.html
+++ /dev/null
@@ -1,48 +0,0 @@
-<!DOCTYPE html>
-<html>
-<head>
- <meta charset='utf-8'>
- <title>Leaflet.Control.FullScreen Demo</title>
- <link rel="stylesheet" href="http://cdn.leafletjs.com/leaflet-0.7/leaflet.css" />
- <style type="text/css">
- #map { width: 700px; height: 433px; }
- .leaflet-control-zoom-fullscreen { background-image: url(icon-fullscreen.png); }
- /* on selector per rule as explained here : http://www.sitepoint.com/html5-full-screen-api/ */
- #map:-webkit-full-screen { width: 100% !important; height: 100% !important; z-index: 99999; }
- #map:-moz-full-screen { width: 100% !important; height: 100% !important; z-index: 99999; }
- #map:full-screen { width: 100% !important; height: 100% !important; z-index: 99999; }
- .leaflet-pseudo-fullscreen { position: fixed !important; width: 100% !important; height: 100% !important; top: 0px !important; left: 0px !important; z-index: 99999; }
- </style>
- <script src="http://cdn.leafletjs.com/leaflet-0.7/leaflet.js"></script>
- <script src="Control.FullScreen.js"></script>
-</head>
-<body>
-
- <div id="map"></div>
-
- <script>
- var base = new L.TileLayer('http://{s}.www.toolserver.org/tiles/bw-mapnik/{z}/{x}/{y}.png', {
- maxZoom: 18,
- attribution: '&copy; <a href="http://openstreetmap.org">OpenStreetMap</a> contributors, <a href="http://creativecommons.org/licenses/by-sa/2.0/">CC-BY-SA</a>'
- });
-
- var map = new L.Map('map', {
- layers: [base],
- center: new L.LatLng(48.5, -4.5),
- zoom: 5,
- fullscreenControl: true,
- fullscreenControlOptions: { // optional
- title:"Show me the fullscreen !"
- }
- });
-
- // detect fullscreen toggling
- map.on('enterFullscreen', function(){
- if(window.console) window.console.log('enterFullscreen');
- });
- map.on('exitFullscreen', function(){
- if(window.console) window.console.log('exitFullscreen');
- });
- </script>
-</body>
-</html>
diff --git a/www/wiki/extensions/Maps/resources/leaflet/leaflet.markercluster/leaflet.markercluster.js b/www/wiki/extensions/Maps/resources/leaflet/leaflet.markercluster/leaflet.markercluster.js
deleted file mode 100644
index ed22dc07..00000000
--- a/www/wiki/extensions/Maps/resources/leaflet/leaflet.markercluster/leaflet.markercluster.js
+++ /dev/null
@@ -1,3 +0,0 @@
-!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t((e.Leaflet=e.Leaflet||{},e.Leaflet.markercluster=e.Leaflet.markercluster||{}))}(this,function(e){"use strict";var t=L.MarkerClusterGroup=L.FeatureGroup.extend({options:{maxClusterRadius:80,iconCreateFunction:null,clusterPane:L.Marker.prototype.options.pane,spiderfyOnMaxZoom:!0,showCoverageOnHover:!0,zoomToBoundsOnClick:!0,singleMarkerMode:!1,disableClusteringAtZoom:null,removeOutsideVisibleBounds:!0,animate:!0,animateAddingMarkers:!1,spiderfyDistanceMultiplier:1,spiderLegPolylineOptions:{weight:1.5,color:"#222",opacity:.5},chunkedLoading:!1,chunkInterval:200,chunkDelay:50,chunkProgress:null,polygonOptions:{}},initialize:function(e){L.Util.setOptions(this,e),this.options.iconCreateFunction||(this.options.iconCreateFunction=this._defaultIconCreateFunction),this._featureGroup=L.featureGroup(),this._featureGroup.addEventParent(this),this._nonPointGroup=L.featureGroup(),this._nonPointGroup.addEventParent(this),this._inZoomAnimation=0,this._needsClustering=[],this._needsRemoving=[],this._currentShownBounds=null,this._queue=[],this._childMarkerEventHandlers={dragstart:this._childMarkerDragStart,move:this._childMarkerMoved,dragend:this._childMarkerDragEnd};var t=L.DomUtil.TRANSITION&&this.options.animate;L.extend(this,t?this._withAnimation:this._noAnimation),this._markerCluster=t?L.MarkerCluster:L.MarkerClusterNonAnimated},addLayer:function(e){if(e instanceof L.LayerGroup)return this.addLayers([e]);if(!e.getLatLng)return this._nonPointGroup.addLayer(e),this.fire("layeradd",{layer:e}),this;if(!this._map)return this._needsClustering.push(e),this.fire("layeradd",{layer:e}),this;if(this.hasLayer(e))return this;this._unspiderfy&&this._unspiderfy(),this._addLayer(e,this._maxZoom),this.fire("layeradd",{layer:e}),this._topClusterLevel._recalculateBounds(),this._refreshClustersIcons();var t=e,i=this._zoom;if(e.__parent)for(;t.__parent._zoom>=i;)t=t.__parent;return this._currentShownBounds.contains(t.getLatLng())&&(this.options.animateAddingMarkers?this._animationAddLayer(e,t):this._animationAddLayerNonAnimated(e,t)),this},removeLayer:function(e){return e instanceof L.LayerGroup?this.removeLayers([e]):e.getLatLng?this._map?e.__parent?(this._unspiderfy&&(this._unspiderfy(),this._unspiderfyLayer(e)),this._removeLayer(e,!0),this.fire("layerremove",{layer:e}),this._topClusterLevel._recalculateBounds(),this._refreshClustersIcons(),e.off(this._childMarkerEventHandlers,this),this._featureGroup.hasLayer(e)&&(this._featureGroup.removeLayer(e),e.clusterShow&&e.clusterShow()),this):this:(!this._arraySplice(this._needsClustering,e)&&this.hasLayer(e)&&this._needsRemoving.push({layer:e,latlng:e._latlng}),this.fire("layerremove",{layer:e}),this):(this._nonPointGroup.removeLayer(e),this.fire("layerremove",{layer:e}),this)},addLayers:function(e,t){if(!L.Util.isArray(e))return this.addLayer(e);var i,n=this._featureGroup,r=this._nonPointGroup,s=this.options.chunkedLoading,o=this.options.chunkInterval,a=this.options.chunkProgress,h=e.length,l=0,u=!0;if(this._map){var _=(new Date).getTime(),d=L.bind(function(){for(var c=(new Date).getTime();h>l;l++){if(s&&0===l%200){var p=(new Date).getTime()-c;if(p>o)break}if(i=e[l],i instanceof L.LayerGroup)u&&(e=e.slice(),u=!1),this._extractNonGroupLayers(i,e),h=e.length;else if(i.getLatLng){if(!this.hasLayer(i)&&(this._addLayer(i,this._maxZoom),t||this.fire("layeradd",{layer:i}),i.__parent&&2===i.__parent.getChildCount())){var f=i.__parent.getAllChildMarkers(),m=f[0]===i?f[1]:f[0];n.removeLayer(m)}}else r.addLayer(i),t||this.fire("layeradd",{layer:i})}a&&a(l,h,(new Date).getTime()-_),l===h?(this._topClusterLevel._recalculateBounds(),this._refreshClustersIcons(),this._topClusterLevel._recursivelyAddChildrenToMap(null,this._zoom,this._currentShownBounds)):setTimeout(d,this.options.chunkDelay)},this);d()}else for(var c=this._needsClustering;h>l;l++)i=e[l],i instanceof L.LayerGroup?(u&&(e=e.slice(),u=!1),this._extractNonGroupLayers(i,e),h=e.length):i.getLatLng?this.hasLayer(i)||c.push(i):r.addLayer(i);return this},removeLayers:function(e){var t,i,n=e.length,r=this._featureGroup,s=this._nonPointGroup,o=!0;if(!this._map){for(t=0;n>t;t++)i=e[t],i instanceof L.LayerGroup?(o&&(e=e.slice(),o=!1),this._extractNonGroupLayers(i,e),n=e.length):(this._arraySplice(this._needsClustering,i),s.removeLayer(i),this.hasLayer(i)&&this._needsRemoving.push({layer:i,latlng:i._latlng}),this.fire("layerremove",{layer:i}));return this}if(this._unspiderfy){this._unspiderfy();var a=e.slice(),h=n;for(t=0;h>t;t++)i=a[t],i instanceof L.LayerGroup?(this._extractNonGroupLayers(i,a),h=a.length):this._unspiderfyLayer(i)}for(t=0;n>t;t++)i=e[t],i instanceof L.LayerGroup?(o&&(e=e.slice(),o=!1),this._extractNonGroupLayers(i,e),n=e.length):i.__parent?(this._removeLayer(i,!0,!0),this.fire("layerremove",{layer:i}),r.hasLayer(i)&&(r.removeLayer(i),i.clusterShow&&i.clusterShow())):(s.removeLayer(i),this.fire("layerremove",{layer:i}));return this._topClusterLevel._recalculateBounds(),this._refreshClustersIcons(),this._topClusterLevel._recursivelyAddChildrenToMap(null,this._zoom,this._currentShownBounds),this},clearLayers:function(){return this._map||(this._needsClustering=[],delete this._gridClusters,delete this._gridUnclustered),this._noanimationUnspiderfy&&this._noanimationUnspiderfy(),this._featureGroup.clearLayers(),this._nonPointGroup.clearLayers(),this.eachLayer(function(e){e.off(this._childMarkerEventHandlers,this),delete e.__parent},this),this._map&&this._generateInitialClusters(),this},getBounds:function(){var e=new L.LatLngBounds;this._topClusterLevel&&e.extend(this._topClusterLevel._bounds);for(var t=this._needsClustering.length-1;t>=0;t--)e.extend(this._needsClustering[t].getLatLng());return e.extend(this._nonPointGroup.getBounds()),e},eachLayer:function(e,t){var i,n,r,s=this._needsClustering.slice(),o=this._needsRemoving;for(this._topClusterLevel&&this._topClusterLevel.getAllChildMarkers(s),n=s.length-1;n>=0;n--){for(i=!0,r=o.length-1;r>=0;r--)if(o[r].layer===s[n]){i=!1;break}i&&e.call(t,s[n])}this._nonPointGroup.eachLayer(e,t)},getLayers:function(){var e=[];return this.eachLayer(function(t){e.push(t)}),e},getLayer:function(e){var t=null;return e=parseInt(e,10),this.eachLayer(function(i){L.stamp(i)===e&&(t=i)}),t},hasLayer:function(e){if(!e)return!1;var t,i=this._needsClustering;for(t=i.length-1;t>=0;t--)if(i[t]===e)return!0;for(i=this._needsRemoving,t=i.length-1;t>=0;t--)if(i[t].layer===e)return!1;return!(!e.__parent||e.__parent._group!==this)||this._nonPointGroup.hasLayer(e)},zoomToShowLayer:function(e,t){"function"!=typeof t&&(t=function(){});var i=function(){!e._icon&&!e.__parent._icon||this._inZoomAnimation||(this._map.off("moveend",i,this),this.off("animationend",i,this),e._icon?t():e.__parent._icon&&(this.once("spiderfied",t,this),e.__parent.spiderfy()))};e._icon&&this._map.getBounds().contains(e.getLatLng())?t():e.__parent._zoom<Math.round(this._map._zoom)?(this._map.on("moveend",i,this),this._map.panTo(e.getLatLng())):(this._map.on("moveend",i,this),this.on("animationend",i,this),e.__parent.zoomToBounds())},onAdd:function(e){this._map=e;var t,i,n;if(!isFinite(this._map.getMaxZoom()))throw"Map has no maxZoom specified";for(this._featureGroup.addTo(e),this._nonPointGroup.addTo(e),this._gridClusters||this._generateInitialClusters(),this._maxLat=e.options.crs.projection.MAX_LATITUDE,t=0,i=this._needsRemoving.length;i>t;t++)n=this._needsRemoving[t],n.newlatlng=n.layer._latlng,n.layer._latlng=n.latlng;for(t=0,i=this._needsRemoving.length;i>t;t++)n=this._needsRemoving[t],this._removeLayer(n.layer,!0),n.layer._latlng=n.newlatlng;this._needsRemoving=[],this._zoom=Math.round(this._map._zoom),this._currentShownBounds=this._getExpandedVisibleBounds(),this._map.on("zoomend",this._zoomEnd,this),this._map.on("moveend",this._moveEnd,this),this._spiderfierOnAdd&&this._spiderfierOnAdd(),this._bindEvents(),i=this._needsClustering,this._needsClustering=[],this.addLayers(i,!0)},onRemove:function(e){e.off("zoomend",this._zoomEnd,this),e.off("moveend",this._moveEnd,this),this._unbindEvents(),this._map._mapPane.className=this._map._mapPane.className.replace(" leaflet-cluster-anim",""),this._spiderfierOnRemove&&this._spiderfierOnRemove(),delete this._maxLat,this._hideCoverage(),this._featureGroup.remove(),this._nonPointGroup.remove(),this._featureGroup.clearLayers(),this._map=null},getVisibleParent:function(e){for(var t=e;t&&!t._icon;)t=t.__parent;return t||null},_arraySplice:function(e,t){for(var i=e.length-1;i>=0;i--)if(e[i]===t)return e.splice(i,1),!0},_removeFromGridUnclustered:function(e,t){for(var i=this._map,n=this._gridUnclustered,r=Math.floor(this._map.getMinZoom());t>=r&&n[t].removeObject(e,i.project(e.getLatLng(),t));t--);},_childMarkerDragStart:function(e){e.target.__dragStart=e.target._latlng},_childMarkerMoved:function(e){if(!this._ignoreMove&&!e.target.__dragStart){var t=e.target._popup&&e.target._popup.isOpen();this._moveChild(e.target,e.oldLatLng,e.latlng),t&&e.target.openPopup()}},_moveChild:function(e,t,i){e._latlng=t,this.removeLayer(e),e._latlng=i,this.addLayer(e)},_childMarkerDragEnd:function(e){e.target.__dragStart&&this._moveChild(e.target,e.target.__dragStart,e.target._latlng),delete e.target.__dragStart},_removeLayer:function(e,t,i){var n=this._gridClusters,r=this._gridUnclustered,s=this._featureGroup,o=this._map,a=Math.floor(this._map.getMinZoom());t&&this._removeFromGridUnclustered(e,this._maxZoom);var h,l=e.__parent,u=l._markers;for(this._arraySplice(u,e);l&&(l._childCount--,l._boundsNeedUpdate=!0,!(l._zoom<a));)t&&l._childCount<=1?(h=l._markers[0]===e?l._markers[1]:l._markers[0],n[l._zoom].removeObject(l,o.project(l._cLatLng,l._zoom)),r[l._zoom].addObject(h,o.project(h.getLatLng(),l._zoom)),this._arraySplice(l.__parent._childClusters,l),l.__parent._markers.push(h),h.__parent=l.__parent,l._icon&&(s.removeLayer(l),i||s.addLayer(h))):l._iconNeedsUpdate=!0,l=l.__parent;delete e.__parent},_isOrIsParent:function(e,t){for(;t;){if(e===t)return!0;t=t.parentNode}return!1},fire:function(e,t,i){if(t&&t.layer instanceof L.MarkerCluster){if(t.originalEvent&&this._isOrIsParent(t.layer._icon,t.originalEvent.relatedTarget))return;e="cluster"+e}L.FeatureGroup.prototype.fire.call(this,e,t,i)},listens:function(e,t){return L.FeatureGroup.prototype.listens.call(this,e,t)||L.FeatureGroup.prototype.listens.call(this,"cluster"+e,t)},_defaultIconCreateFunction:function(e){var t=e.getChildCount(),i=" marker-cluster-";return i+=10>t?"small":100>t?"medium":"large",new L.DivIcon({html:"<div><span>"+t+"</span></div>",className:"marker-cluster"+i,iconSize:new L.Point(40,40)})},_bindEvents:function(){var e=this._map,t=this.options.spiderfyOnMaxZoom,i=this.options.showCoverageOnHover,n=this.options.zoomToBoundsOnClick;(t||n)&&this.on("clusterclick",this._zoomOrSpiderfy,this),i&&(this.on("clustermouseover",this._showCoverage,this),this.on("clustermouseout",this._hideCoverage,this),e.on("zoomend",this._hideCoverage,this))},_zoomOrSpiderfy:function(e){for(var t=e.layer,i=t;1===i._childClusters.length;)i=i._childClusters[0];i._zoom===this._maxZoom&&i._childCount===t._childCount&&this.options.spiderfyOnMaxZoom?t.spiderfy():this.options.zoomToBoundsOnClick&&t.zoomToBounds(),e.originalEvent&&13===e.originalEvent.keyCode&&this._map._container.focus()},_showCoverage:function(e){var t=this._map;this._inZoomAnimation||(this._shownPolygon&&t.removeLayer(this._shownPolygon),e.layer.getChildCount()>2&&e.layer!==this._spiderfied&&(this._shownPolygon=new L.Polygon(e.layer.getConvexHull(),this.options.polygonOptions),t.addLayer(this._shownPolygon)))},_hideCoverage:function(){this._shownPolygon&&(this._map.removeLayer(this._shownPolygon),this._shownPolygon=null)},_unbindEvents:function(){var e=this.options.spiderfyOnMaxZoom,t=this.options.showCoverageOnHover,i=this.options.zoomToBoundsOnClick,n=this._map;(e||i)&&this.off("clusterclick",this._zoomOrSpiderfy,this),t&&(this.off("clustermouseover",this._showCoverage,this),this.off("clustermouseout",this._hideCoverage,this),n.off("zoomend",this._hideCoverage,this))},_zoomEnd:function(){this._map&&(this._mergeSplitClusters(),this._zoom=Math.round(this._map._zoom),this._currentShownBounds=this._getExpandedVisibleBounds())},_moveEnd:function(){if(!this._inZoomAnimation){var e=this._getExpandedVisibleBounds();this._topClusterLevel._recursivelyRemoveChildrenFromMap(this._currentShownBounds,Math.floor(this._map.getMinZoom()),this._zoom,e),this._topClusterLevel._recursivelyAddChildrenToMap(null,Math.round(this._map._zoom),e),this._currentShownBounds=e}},_generateInitialClusters:function(){var e=Math.ceil(this._map.getMaxZoom()),t=Math.floor(this._map.getMinZoom()),i=this.options.maxClusterRadius,n=i;"function"!=typeof i&&(n=function(){return i}),null!==this.options.disableClusteringAtZoom&&(e=this.options.disableClusteringAtZoom-1),this._maxZoom=e,this._gridClusters={},this._gridUnclustered={};for(var r=e;r>=t;r--)this._gridClusters[r]=new L.DistanceGrid(n(r)),this._gridUnclustered[r]=new L.DistanceGrid(n(r));this._topClusterLevel=new this._markerCluster(this,t-1)},_addLayer:function(e,t){var i,n,r=this._gridClusters,s=this._gridUnclustered,o=Math.floor(this._map.getMinZoom());for(this.options.singleMarkerMode&&this._overrideMarkerIcon(e),e.on(this._childMarkerEventHandlers,this);t>=o;t--){i=this._map.project(e.getLatLng(),t);var a=r[t].getNearObject(i);if(a)return a._addChild(e),e.__parent=a,void 0;if(a=s[t].getNearObject(i)){var h=a.__parent;h&&this._removeLayer(a,!1);var l=new this._markerCluster(this,t,a,e);r[t].addObject(l,this._map.project(l._cLatLng,t)),a.__parent=l,e.__parent=l;var u=l;for(n=t-1;n>h._zoom;n--)u=new this._markerCluster(this,n,u),r[n].addObject(u,this._map.project(a.getLatLng(),n));return h._addChild(u),this._removeFromGridUnclustered(a,t),void 0}s[t].addObject(e,i)}this._topClusterLevel._addChild(e),e.__parent=this._topClusterLevel},_refreshClustersIcons:function(){this._featureGroup.eachLayer(function(e){e instanceof L.MarkerCluster&&e._iconNeedsUpdate&&e._updateIcon()})},_enqueue:function(e){this._queue.push(e),this._queueTimeout||(this._queueTimeout=setTimeout(L.bind(this._processQueue,this),300))},_processQueue:function(){for(var e=0;e<this._queue.length;e++)this._queue[e].call(this);this._queue.length=0,clearTimeout(this._queueTimeout),this._queueTimeout=null},_mergeSplitClusters:function(){var e=Math.round(this._map._zoom);this._processQueue(),this._zoom<e&&this._currentShownBounds.intersects(this._getExpandedVisibleBounds())?(this._animationStart(),this._topClusterLevel._recursivelyRemoveChildrenFromMap(this._currentShownBounds,Math.floor(this._map.getMinZoom()),this._zoom,this._getExpandedVisibleBounds()),this._animationZoomIn(this._zoom,e)):this._zoom>e?(this._animationStart(),this._animationZoomOut(this._zoom,e)):this._moveEnd()},_getExpandedVisibleBounds:function(){return this.options.removeOutsideVisibleBounds?L.Browser.mobile?this._checkBoundsMaxLat(this._map.getBounds()):this._checkBoundsMaxLat(this._map.getBounds().pad(1)):this._mapBoundsInfinite},_checkBoundsMaxLat:function(e){var t=this._maxLat;return void 0!==t&&(e.getNorth()>=t&&(e._northEast.lat=1/0),e.getSouth()<=-t&&(e._southWest.lat=-1/0)),e},_animationAddLayerNonAnimated:function(e,t){if(t===e)this._featureGroup.addLayer(e);else if(2===t._childCount){t._addToMap();var i=t.getAllChildMarkers();this._featureGroup.removeLayer(i[0]),this._featureGroup.removeLayer(i[1])}else t._updateIcon()},_extractNonGroupLayers:function(e,t){var i,n=e.getLayers(),r=0;for(t=t||[];r<n.length;r++)i=n[r],i instanceof L.LayerGroup?this._extractNonGroupLayers(i,t):t.push(i);return t},_overrideMarkerIcon:function(e){var t=e.options.icon=this.options.iconCreateFunction({getChildCount:function(){return 1},getAllChildMarkers:function(){return[e]}});return t}});L.MarkerClusterGroup.include({_mapBoundsInfinite:new L.LatLngBounds(new L.LatLng(-1/0,-1/0),new L.LatLng(1/0,1/0))}),L.MarkerClusterGroup.include({_noAnimation:{_animationStart:function(){},_animationZoomIn:function(e,t){this._topClusterLevel._recursivelyRemoveChildrenFromMap(this._currentShownBounds,Math.floor(this._map.getMinZoom()),e),this._topClusterLevel._recursivelyAddChildrenToMap(null,t,this._getExpandedVisibleBounds()),this.fire("animationend")},_animationZoomOut:function(e,t){this._topClusterLevel._recursivelyRemoveChildrenFromMap(this._currentShownBounds,Math.floor(this._map.getMinZoom()),e),this._topClusterLevel._recursivelyAddChildrenToMap(null,t,this._getExpandedVisibleBounds()),this.fire("animationend")},_animationAddLayer:function(e,t){this._animationAddLayerNonAnimated(e,t)}},_withAnimation:{_animationStart:function(){this._map._mapPane.className+=" leaflet-cluster-anim",this._inZoomAnimation++},_animationZoomIn:function(e,t){var i,n=this._getExpandedVisibleBounds(),r=this._featureGroup,s=Math.floor(this._map.getMinZoom());this._ignoreMove=!0,this._topClusterLevel._recursively(n,e,s,function(s){var o,a=s._latlng,h=s._markers;for(n.contains(a)||(a=null),s._isSingleParent()&&e+1===t?(r.removeLayer(s),s._recursivelyAddChildrenToMap(null,t,n)):(s.clusterHide(),s._recursivelyAddChildrenToMap(a,t,n)),i=h.length-1;i>=0;i--)o=h[i],n.contains(o._latlng)||r.removeLayer(o)}),this._forceLayout(),this._topClusterLevel._recursivelyBecomeVisible(n,t),r.eachLayer(function(e){e instanceof L.MarkerCluster||!e._icon||e.clusterShow()}),this._topClusterLevel._recursively(n,e,t,function(e){e._recursivelyRestoreChildPositions(t)}),this._ignoreMove=!1,this._enqueue(function(){this._topClusterLevel._recursively(n,e,s,function(e){r.removeLayer(e),e.clusterShow()}),this._animationEnd()})},_animationZoomOut:function(e,t){this._animationZoomOutSingle(this._topClusterLevel,e-1,t),this._topClusterLevel._recursivelyAddChildrenToMap(null,t,this._getExpandedVisibleBounds()),this._topClusterLevel._recursivelyRemoveChildrenFromMap(this._currentShownBounds,Math.floor(this._map.getMinZoom()),e,this._getExpandedVisibleBounds())},_animationAddLayer:function(e,t){var i=this,n=this._featureGroup;n.addLayer(e),t!==e&&(t._childCount>2?(t._updateIcon(),this._forceLayout(),this._animationStart(),e._setPos(this._map.latLngToLayerPoint(t.getLatLng())),e.clusterHide(),this._enqueue(function(){n.removeLayer(e),e.clusterShow(),i._animationEnd()})):(this._forceLayout(),i._animationStart(),i._animationZoomOutSingle(t,this._map.getMaxZoom(),this._zoom)))}},_animationZoomOutSingle:function(e,t,i){var n=this._getExpandedVisibleBounds(),r=Math.floor(this._map.getMinZoom());e._recursivelyAnimateChildrenInAndAddSelfToMap(n,r,t+1,i);var s=this;this._forceLayout(),e._recursivelyBecomeVisible(n,i),this._enqueue(function(){if(1===e._childCount){var o=e._markers[0];this._ignoreMove=!0,o.setLatLng(o.getLatLng()),this._ignoreMove=!1,o.clusterShow&&o.clusterShow()}else e._recursively(n,i,r,function(e){e._recursivelyRemoveChildrenFromMap(n,r,t+1)});s._animationEnd()})},_animationEnd:function(){this._map&&(this._map._mapPane.className=this._map._mapPane.className.replace(" leaflet-cluster-anim","")),this._inZoomAnimation--,this.fire("animationend")},_forceLayout:function(){L.Util.falseFn(document.body.offsetWidth)}}),L.markerClusterGroup=function(e){return new L.MarkerClusterGroup(e)};var i=L.MarkerCluster=L.Marker.extend({options:L.Icon.prototype.options,initialize:function(e,t,i,n){L.Marker.prototype.initialize.call(this,i?i._cLatLng||i.getLatLng():new L.LatLng(0,0),{icon:this,pane:e.options.clusterPane}),this._group=e,this._zoom=t,this._markers=[],this._childClusters=[],this._childCount=0,this._iconNeedsUpdate=!0,this._boundsNeedUpdate=!0,this._bounds=new L.LatLngBounds,i&&this._addChild(i),n&&this._addChild(n)},getAllChildMarkers:function(e){e=e||[];for(var t=this._childClusters.length-1;t>=0;t--)this._childClusters[t].getAllChildMarkers(e);for(var i=this._markers.length-1;i>=0;i--)e.push(this._markers[i]);return e},getChildCount:function(){return this._childCount},zoomToBounds:function(e){for(var t,i=this._childClusters.slice(),n=this._group._map,r=n.getBoundsZoom(this._bounds),s=this._zoom+1,o=n.getZoom();i.length>0&&r>s;){s++;var a=[];for(t=0;t<i.length;t++)a=a.concat(i[t]._childClusters);i=a}r>s?this._group._map.setView(this._latlng,s):o>=r?this._group._map.setView(this._latlng,o+1):this._group._map.fitBounds(this._bounds,e)},getBounds:function(){var e=new L.LatLngBounds;return e.extend(this._bounds),e},_updateIcon:function(){this._iconNeedsUpdate=!0,this._icon&&this.setIcon(this)},createIcon:function(){return this._iconNeedsUpdate&&(this._iconObj=this._group.options.iconCreateFunction(this),this._iconNeedsUpdate=!1),this._iconObj.createIcon()},createShadow:function(){return this._iconObj.createShadow()},_addChild:function(e,t){this._iconNeedsUpdate=!0,this._boundsNeedUpdate=!0,this._setClusterCenter(e),e instanceof L.MarkerCluster?(t||(this._childClusters.push(e),e.__parent=this),this._childCount+=e._childCount):(t||this._markers.push(e),this._childCount++),this.__parent&&this.__parent._addChild(e,!0)},_setClusterCenter:function(e){this._cLatLng||(this._cLatLng=e._cLatLng||e._latlng)},_resetBounds:function(){var e=this._bounds;e._southWest&&(e._southWest.lat=1/0,e._southWest.lng=1/0),e._northEast&&(e._northEast.lat=-1/0,e._northEast.lng=-1/0)},_recalculateBounds:function(){var e,t,i,n,r=this._markers,s=this._childClusters,o=0,a=0,h=this._childCount;if(0!==h){for(this._resetBounds(),e=0;e<r.length;e++)i=r[e]._latlng,this._bounds.extend(i),o+=i.lat,a+=i.lng;for(e=0;e<s.length;e++)t=s[e],t._boundsNeedUpdate&&t._recalculateBounds(),this._bounds.extend(t._bounds),i=t._wLatLng,n=t._childCount,o+=i.lat*n,a+=i.lng*n;this._latlng=this._wLatLng=new L.LatLng(o/h,a/h),this._boundsNeedUpdate=!1}},_addToMap:function(e){e&&(this._backupLatlng=this._latlng,this.setLatLng(e)),this._group._featureGroup.addLayer(this)},_recursivelyAnimateChildrenIn:function(e,t,i){this._recursively(e,this._group._map.getMinZoom(),i-1,function(e){var i,n,r=e._markers;for(i=r.length-1;i>=0;i--)n=r[i],n._icon&&(n._setPos(t),n.clusterHide())},function(e){var i,n,r=e._childClusters;for(i=r.length-1;i>=0;i--)n=r[i],n._icon&&(n._setPos(t),n.clusterHide())})},_recursivelyAnimateChildrenInAndAddSelfToMap:function(e,t,i,n){this._recursively(e,n,t,function(r){r._recursivelyAnimateChildrenIn(e,r._group._map.latLngToLayerPoint(r.getLatLng()).round(),i),r._isSingleParent()&&i-1===n?(r.clusterShow(),r._recursivelyRemoveChildrenFromMap(e,t,i)):r.clusterHide(),r._addToMap()})},_recursivelyBecomeVisible:function(e,t){this._recursively(e,this._group._map.getMinZoom(),t,null,function(e){e.clusterShow()})},_recursivelyAddChildrenToMap:function(e,t,i){this._recursively(i,this._group._map.getMinZoom()-1,t,function(n){if(t!==n._zoom)for(var r=n._markers.length-1;r>=0;r--){var s=n._markers[r];i.contains(s._latlng)&&(e&&(s._backupLatlng=s.getLatLng(),s.setLatLng(e),s.clusterHide&&s.clusterHide()),n._group._featureGroup.addLayer(s))}},function(t){t._addToMap(e)})},_recursivelyRestoreChildPositions:function(e){for(var t=this._markers.length-1;t>=0;t--){var i=this._markers[t];i._backupLatlng&&(i.setLatLng(i._backupLatlng),delete i._backupLatlng)}if(e-1===this._zoom)for(var n=this._childClusters.length-1;n>=0;n--)this._childClusters[n]._restorePosition();else for(var r=this._childClusters.length-1;r>=0;r--)this._childClusters[r]._recursivelyRestoreChildPositions(e)},_restorePosition:function(){this._backupLatlng&&(this.setLatLng(this._backupLatlng),delete this._backupLatlng)},_recursivelyRemoveChildrenFromMap:function(e,t,i,n){var r,s;this._recursively(e,t-1,i-1,function(e){for(s=e._markers.length-1;s>=0;s--)r=e._markers[s],n&&n.contains(r._latlng)||(e._group._featureGroup.removeLayer(r),r.clusterShow&&r.clusterShow())},function(e){for(s=e._childClusters.length-1;s>=0;s--)r=e._childClusters[s],n&&n.contains(r._latlng)||(e._group._featureGroup.removeLayer(r),r.clusterShow&&r.clusterShow())})},_recursively:function(e,t,i,n,r){var s,o,a=this._childClusters,h=this._zoom;if(h>=t&&(n&&n(this),r&&h===i&&r(this)),t>h||i>h)for(s=a.length-1;s>=0;s--)o=a[s],e.intersects(o._bounds)&&o._recursively(e,t,i,n,r)},_isSingleParent:function(){return this._childClusters.length>0&&this._childClusters[0]._childCount===this._childCount}});L.Marker.include({clusterHide:function(){return this.options.opacityWhenUnclustered=this.options.opacity||1,this.setOpacity(0)},clusterShow:function(){var e=this.setOpacity(this.options.opacity||this.options.opacityWhenUnclustered);return delete this.options.opacityWhenUnclustered,e}}),L.DistanceGrid=function(e){this._cellSize=e,this._sqCellSize=e*e,this._grid={},this._objectPoint={}},L.DistanceGrid.prototype={addObject:function(e,t){var i=this._getCoord(t.x),n=this._getCoord(t.y),r=this._grid,s=r[n]=r[n]||{},o=s[i]=s[i]||[],a=L.Util.stamp(e);this._objectPoint[a]=t,o.push(e)},updateObject:function(e,t){this.removeObject(e),this.addObject(e,t)},removeObject:function(e,t){var i,n,r=this._getCoord(t.x),s=this._getCoord(t.y),o=this._grid,a=o[s]=o[s]||{},h=a[r]=a[r]||[];for(delete this._objectPoint[L.Util.stamp(e)],i=0,n=h.length;n>i;i++)if(h[i]===e)return h.splice(i,1),1===n&&delete a[r],!0},eachObject:function(e,t){var i,n,r,s,o,a,h,l=this._grid;for(i in l){o=l[i];for(n in o)for(a=o[n],r=0,s=a.length;s>r;r++)h=e.call(t,a[r]),h&&(r--,s--)}},getNearObject:function(e){var t,i,n,r,s,o,a,h,l=this._getCoord(e.x),u=this._getCoord(e.y),_=this._objectPoint,d=this._sqCellSize,c=null;for(t=u-1;u+1>=t;t++)if(r=this._grid[t])for(i=l-1;l+1>=i;i++)if(s=r[i])for(n=0,o=s.length;o>n;n++)a=s[n],h=this._sqDist(_[L.Util.stamp(a)],e),(d>h||d>=h&&null===c)&&(d=h,c=a);return c},_getCoord:function(e){var t=Math.floor(e/this._cellSize);return isFinite(t)?t:e},_sqDist:function(e,t){var i=t.x-e.x,n=t.y-e.y;return i*i+n*n}},function(){L.QuickHull={getDistant:function(e,t){var i=t[1].lat-t[0].lat,n=t[0].lng-t[1].lng;return n*(e.lat-t[0].lat)+i*(e.lng-t[0].lng)},findMostDistantPointFromBaseLine:function(e,t){var i,n,r,s=0,o=null,a=[];for(i=t.length-1;i>=0;i--)n=t[i],r=this.getDistant(n,e),r>0&&(a.push(n),r>s&&(s=r,o=n));return{maxPoint:o,newPoints:a}},buildConvexHull:function(e,t){var i=[],n=this.findMostDistantPointFromBaseLine(e,t);return n.maxPoint?(i=i.concat(this.buildConvexHull([e[0],n.maxPoint],n.newPoints)),i=i.concat(this.buildConvexHull([n.maxPoint,e[1]],n.newPoints))):[e[0]]},getConvexHull:function(e){var t,i=!1,n=!1,r=!1,s=!1,o=null,a=null,h=null,l=null,u=null,_=null;for(t=e.length-1;t>=0;t--){var d=e[t];(i===!1||d.lat>i)&&(o=d,i=d.lat),(n===!1||d.lat<n)&&(a=d,n=d.lat),(r===!1||d.lng>r)&&(h=d,r=d.lng),(s===!1||d.lng<s)&&(l=d,s=d.lng)}n!==i?(_=a,u=o):(_=l,u=h);var c=[].concat(this.buildConvexHull([_,u],e),this.buildConvexHull([u,_],e));return c}}}(),L.MarkerCluster.include({getConvexHull:function(){var e,t,i=this.getAllChildMarkers(),n=[];for(t=i.length-1;t>=0;t--)e=i[t].getLatLng(),n.push(e);return L.QuickHull.getConvexHull(n)}}),L.MarkerCluster.include({_2PI:2*Math.PI,_circleFootSeparation:25,_circleStartAngle:0,_spiralFootSeparation:28,_spiralLengthStart:11,_spiralLengthFactor:5,_circleSpiralSwitchover:9,spiderfy:function(){if(this._group._spiderfied!==this&&!this._group._inZoomAnimation){var e,t=this.getAllChildMarkers(),i=this._group,n=i._map,r=n.latLngToLayerPoint(this._latlng);this._group._unspiderfy(),this._group._spiderfied=this,t.length>=this._circleSpiralSwitchover?e=this._generatePointsSpiral(t.length,r):(r.y+=10,e=this._generatePointsCircle(t.length,r)),this._animationSpiderfy(t,e)}},unspiderfy:function(e){this._group._inZoomAnimation||(this._animationUnspiderfy(e),this._group._spiderfied=null)},_generatePointsCircle:function(e,t){var i,n,r=this._group.options.spiderfyDistanceMultiplier*this._circleFootSeparation*(2+e),s=r/this._2PI,o=this._2PI/e,a=[];for(s=Math.max(s,35),a.length=e,i=0;e>i;i++)n=this._circleStartAngle+i*o,a[i]=new L.Point(t.x+s*Math.cos(n),t.y+s*Math.sin(n))._round();return a},_generatePointsSpiral:function(e,t){var i,n=this._group.options.spiderfyDistanceMultiplier,r=n*this._spiralLengthStart,s=n*this._spiralFootSeparation,o=n*this._spiralLengthFactor*this._2PI,a=0,h=[];for(h.length=e,i=e;i>=0;i--)e>i&&(h[i]=new L.Point(t.x+r*Math.cos(a),t.y+r*Math.sin(a))._round()),a+=s/r+5e-4*i,r+=o/a;return h},_noanimationUnspiderfy:function(){var e,t,i=this._group,n=i._map,r=i._featureGroup,s=this.getAllChildMarkers();for(i._ignoreMove=!0,this.setOpacity(1),t=s.length-1;t>=0;t--)e=s[t],r.removeLayer(e),e._preSpiderfyLatlng&&(e.setLatLng(e._preSpiderfyLatlng),delete e._preSpiderfyLatlng),e.setZIndexOffset&&e.setZIndexOffset(0),e._spiderLeg&&(n.removeLayer(e._spiderLeg),delete e._spiderLeg);i.fire("unspiderfied",{cluster:this,markers:s}),i._ignoreMove=!1,i._spiderfied=null}}),L.MarkerClusterNonAnimated=L.MarkerCluster.extend({_animationSpiderfy:function(e,t){var i,n,r,s,o=this._group,a=o._map,h=o._featureGroup,l=this._group.options.spiderLegPolylineOptions;for(o._ignoreMove=!0,i=0;i<e.length;i++)s=a.layerPointToLatLng(t[i]),n=e[i],r=new L.Polyline([this._latlng,s],l),a.addLayer(r),n._spiderLeg=r,n._preSpiderfyLatlng=n._latlng,n.setLatLng(s),n.setZIndexOffset&&n.setZIndexOffset(1e6),h.addLayer(n);this.setOpacity(.3),o._ignoreMove=!1,o.fire("spiderfied",{cluster:this,markers:e})},_animationUnspiderfy:function(){this._noanimationUnspiderfy()}}),L.MarkerCluster.include({_animationSpiderfy:function(e,t){var i,n,r,s,o,a,h=this,l=this._group,u=l._map,_=l._featureGroup,d=this._latlng,c=u.latLngToLayerPoint(d),p=L.Path.SVG,f=L.extend({},this._group.options.spiderLegPolylineOptions),m=f.opacity;for(void 0===m&&(m=L.MarkerClusterGroup.prototype.options.spiderLegPolylineOptions.opacity),p?(f.opacity=0,f.className=(f.className||"")+" leaflet-cluster-spider-leg"):f.opacity=m,l._ignoreMove=!0,i=0;i<e.length;i++)n=e[i],a=u.layerPointToLatLng(t[i]),r=new L.Polyline([d,a],f),u.addLayer(r),n._spiderLeg=r,p&&(s=r._path,o=s.getTotalLength()+.1,s.style.strokeDasharray=o,s.style.strokeDashoffset=o),n.setZIndexOffset&&n.setZIndexOffset(1e6),n.clusterHide&&n.clusterHide(),_.addLayer(n),n._setPos&&n._setPos(c);for(l._forceLayout(),l._animationStart(),i=e.length-1;i>=0;i--)a=u.layerPointToLatLng(t[i]),n=e[i],n._preSpiderfyLatlng=n._latlng,n.setLatLng(a),n.clusterShow&&n.clusterShow(),p&&(r=n._spiderLeg,s=r._path,s.style.strokeDashoffset=0,r.setStyle({opacity:m}));this.setOpacity(.3),l._ignoreMove=!1,setTimeout(function(){l._animationEnd(),l.fire("spiderfied",{cluster:h,markers:e})},200)},_animationUnspiderfy:function(e){var t,i,n,r,s,o,a=this,h=this._group,l=h._map,u=h._featureGroup,_=e?l._latLngToNewLayerPoint(this._latlng,e.zoom,e.center):l.latLngToLayerPoint(this._latlng),d=this.getAllChildMarkers(),c=L.Path.SVG;for(h._ignoreMove=!0,h._animationStart(),this.setOpacity(1),i=d.length-1;i>=0;i--)t=d[i],t._preSpiderfyLatlng&&(t.closePopup(),t.setLatLng(t._preSpiderfyLatlng),delete t._preSpiderfyLatlng,o=!0,t._setPos&&(t._setPos(_),o=!1),t.clusterHide&&(t.clusterHide(),o=!1),o&&u.removeLayer(t),c&&(n=t._spiderLeg,r=n._path,s=r.getTotalLength()+.1,r.style.strokeDashoffset=s,n.setStyle({opacity:0})));h._ignoreMove=!1,setTimeout(function(){var e=0;for(i=d.length-1;i>=0;i--)t=d[i],t._spiderLeg&&e++;for(i=d.length-1;i>=0;i--)t=d[i],t._spiderLeg&&(t.clusterShow&&t.clusterShow(),t.setZIndexOffset&&t.setZIndexOffset(0),e>1&&u.removeLayer(t),l.removeLayer(t._spiderLeg),delete t._spiderLeg);h._animationEnd(),h.fire("unspiderfied",{cluster:a,markers:d})},200)}}),L.MarkerClusterGroup.include({_spiderfied:null,unspiderfy:function(){this._unspiderfy.apply(this,arguments)},_spiderfierOnAdd:function(){this._map.on("click",this._unspiderfyWrapper,this),this._map.options.zoomAnimation&&this._map.on("zoomstart",this._unspiderfyZoomStart,this),this._map.on("zoomend",this._noanimationUnspiderfy,this),L.Browser.touch||this._map.getRenderer(this)},_spiderfierOnRemove:function(){this._map.off("click",this._unspiderfyWrapper,this),this._map.off("zoomstart",this._unspiderfyZoomStart,this),this._map.off("zoomanim",this._unspiderfyZoomAnim,this),this._map.off("zoomend",this._noanimationUnspiderfy,this),this._noanimationUnspiderfy()
-},_unspiderfyZoomStart:function(){this._map&&this._map.on("zoomanim",this._unspiderfyZoomAnim,this)},_unspiderfyZoomAnim:function(e){L.DomUtil.hasClass(this._map._mapPane,"leaflet-touching")||(this._map.off("zoomanim",this._unspiderfyZoomAnim,this),this._unspiderfy(e))},_unspiderfyWrapper:function(){this._unspiderfy()},_unspiderfy:function(e){this._spiderfied&&this._spiderfied.unspiderfy(e)},_noanimationUnspiderfy:function(){this._spiderfied&&this._spiderfied._noanimationUnspiderfy()},_unspiderfyLayer:function(e){e._spiderLeg&&(this._featureGroup.removeLayer(e),e.clusterShow&&e.clusterShow(),e.setZIndexOffset&&e.setZIndexOffset(0),this._map.removeLayer(e._spiderLeg),delete e._spiderLeg)}}),L.MarkerClusterGroup.include({refreshClusters:function(e){return e?e instanceof L.MarkerClusterGroup?e=e._topClusterLevel.getAllChildMarkers():e instanceof L.LayerGroup?e=e._layers:e instanceof L.MarkerCluster?e=e.getAllChildMarkers():e instanceof L.Marker&&(e=[e]):e=this._topClusterLevel.getAllChildMarkers(),this._flagParentsIconsNeedUpdate(e),this._refreshClustersIcons(),this.options.singleMarkerMode&&this._refreshSingleMarkerModeMarkers(e),this},_flagParentsIconsNeedUpdate:function(e){var t,i;for(t in e)for(i=e[t].__parent;i;)i._iconNeedsUpdate=!0,i=i.__parent},_refreshSingleMarkerModeMarkers:function(e){var t,i;for(t in e)i=e[t],this.hasLayer(i)&&i.setIcon(this._overrideMarkerIcon(i))}}),L.Marker.include({refreshIconOptions:function(e,t){var i=this.options.icon;return L.setOptions(i,e),this.setIcon(i),t&&this.__parent&&this.__parent._group.refreshClusters(this),this}}),e.MarkerClusterGroup=t,e.MarkerCluster=i});
-//# sourceMappingURL=leaflet.markercluster.js.map \ No newline at end of file
diff --git a/www/wiki/extensions/Maps/resources/leaflet/leaflet/images/marker-icon-2x.png b/www/wiki/extensions/Maps/resources/leaflet/leaflet/images/marker-icon-2x.png
deleted file mode 100644
index 1c26e9fc..00000000
--- a/www/wiki/extensions/Maps/resources/leaflet/leaflet/images/marker-icon-2x.png
+++ /dev/null
Binary files differ
diff --git a/www/wiki/extensions/Maps/resources/leaflet/leaflet/images/marker-icon.png b/www/wiki/extensions/Maps/resources/leaflet/leaflet/images/marker-icon.png
deleted file mode 100644
index 3e64e06d..00000000
--- a/www/wiki/extensions/Maps/resources/leaflet/leaflet/images/marker-icon.png
+++ /dev/null
Binary files differ
diff --git a/www/wiki/extensions/Maps/resources/leaflet/leaflet/leaflet.js b/www/wiki/extensions/Maps/resources/leaflet/leaflet/leaflet.js
deleted file mode 100644
index 3b628aba..00000000
--- a/www/wiki/extensions/Maps/resources/leaflet/leaflet/leaflet.js
+++ /dev/null
@@ -1,5 +0,0 @@
-/* @preserve
- * Leaflet 1.3.4+Detached: 0e566b2ad5e696ba9f79a9d48a7e51c8f4892441.0e566b2, a JS library for interactive maps. http://leafletjs.com
- * (c) 2010-2018 Vladimir Agafonkin, (c) 2010-2011 CloudMade
- */
-!function(t,i){"object"==typeof exports&&"undefined"!=typeof module?i(exports):"function"==typeof define&&define.amd?define(["exports"],i):i(t.L={})}(this,function(t){"use strict";function i(t){var i,e,n,o;for(e=1,n=arguments.length;e<n;e++){o=arguments[e];for(i in o)t[i]=o[i]}return t}function e(t,i){var e=Array.prototype.slice;if(t.bind)return t.bind.apply(t,e.call(arguments,1));var n=e.call(arguments,2);return function(){return t.apply(i,n.length?n.concat(e.call(arguments)):arguments)}}function n(t){return t._leaflet_id=t._leaflet_id||++ei,t._leaflet_id}function o(t,i,e){var n,o,s,r;return r=function(){n=!1,o&&(s.apply(e,o),o=!1)},s=function(){n?o=arguments:(t.apply(e,arguments),setTimeout(r,i),n=!0)}}function s(t,i,e){var n=i[1],o=i[0],s=n-o;return t===n&&e?t:((t-o)%s+s)%s+o}function r(){return!1}function a(t,i){var e=Math.pow(10,void 0===i?6:i);return Math.round(t*e)/e}function h(t){return t.trim?t.trim():t.replace(/^\s+|\s+$/g,"")}function u(t){return h(t).split(/\s+/)}function l(t,i){t.hasOwnProperty("options")||(t.options=t.options?ii(t.options):{});for(var e in i)t.options[e]=i[e];return t.options}function c(t,i,e){var n=[];for(var o in t)n.push(encodeURIComponent(e?o.toUpperCase():o)+"="+encodeURIComponent(t[o]));return(i&&-1!==i.indexOf("?")?"&":"?")+n.join("&")}function _(t,i){return t.replace(ni,function(t,e){var n=i[e];if(void 0===n)throw new Error("No value provided for variable "+t);return"function"==typeof n&&(n=n(i)),n})}function d(t,i){for(var e=0;e<t.length;e++)if(t[e]===i)return e;return-1}function p(t){return window["webkit"+t]||window["moz"+t]||window["ms"+t]}function m(t){var i=+new Date,e=Math.max(0,16-(i-ri));return ri=i+e,window.setTimeout(t,e)}function f(t,i,n){if(!n||ai!==m)return ai.call(window,e(t,i));t.call(i)}function g(t){t&&hi.call(window,t)}function v(){}function y(t){if("undefined"!=typeof L&&L&&L.Mixin){t=oi(t)?t:[t];for(var i=0;i<t.length;i++)t[i]===L.Mixin.Events&&console.warn("Deprecated include of L.Mixin.Events: this property will be removed in future releases, please inherit from L.Evented instead.",(new Error).stack)}}function x(t,i,e){this.x=e?Math.round(t):t,this.y=e?Math.round(i):i}function w(t,i,e){return t instanceof x?t:oi(t)?new x(t[0],t[1]):void 0===t||null===t?t:"object"==typeof t&&"x"in t&&"y"in t?new x(t.x,t.y):new x(t,i,e)}function P(t,i){if(t)for(var e=i?[t,i]:t,n=0,o=e.length;n<o;n++)this.extend(e[n])}function b(t,i){return!t||t instanceof P?t:new P(t,i)}function T(t,i){if(t)for(var e=i?[t,i]:t,n=0,o=e.length;n<o;n++)this.extend(e[n])}function z(t,i){return t instanceof T?t:new T(t,i)}function M(t,i,e){if(isNaN(t)||isNaN(i))throw new Error("Invalid LatLng object: ("+t+", "+i+")");this.lat=+t,this.lng=+i,void 0!==e&&(this.alt=+e)}function C(t,i,e){return t instanceof M?t:oi(t)&&"object"!=typeof t[0]?3===t.length?new M(t[0],t[1],t[2]):2===t.length?new M(t[0],t[1]):null:void 0===t||null===t?t:"object"==typeof t&&"lat"in t?new M(t.lat,"lng"in t?t.lng:t.lon,t.alt):void 0===i?null:new M(t,i,e)}function S(t,i,e,n){if(oi(t))return this._a=t[0],this._b=t[1],this._c=t[2],void(this._d=t[3]);this._a=t,this._b=i,this._c=e,this._d=n}function Z(t,i,e,n){return new S(t,i,e,n)}function E(t){return document.createElementNS("http://www.w3.org/2000/svg",t)}function k(t,i){var e,n,o,s,r,a,h="";for(e=0,o=t.length;e<o;e++){for(n=0,s=(r=t[e]).length;n<s;n++)a=r[n],h+=(n?"L":"M")+a.x+" "+a.y;h+=i?Ji?"z":"x":""}return h||"M0 0"}function A(t){return navigator.userAgent.toLowerCase().indexOf(t)>=0}function B(t,i,e,n){return"touchstart"===i?O(t,e,n):"touchmove"===i?W(t,e,n):"touchend"===i&&H(t,e,n),this}function I(t,i,e){var n=t["_leaflet_"+i+e];return"touchstart"===i?t.removeEventListener(te,n,!1):"touchmove"===i?t.removeEventListener(ie,n,!1):"touchend"===i&&(t.removeEventListener(ee,n,!1),t.removeEventListener(ne,n,!1)),this}function O(t,i,n){var o=e(function(t){if("mouse"!==t.pointerType&&t.MSPOINTER_TYPE_MOUSE&&t.pointerType!==t.MSPOINTER_TYPE_MOUSE){if(!(oe.indexOf(t.target.tagName)<0))return;Pt(t)}j(t,i)});t["_leaflet_touchstart"+n]=o,t.addEventListener(te,o,!1),re||(document.documentElement.addEventListener(te,R,!0),document.documentElement.addEventListener(ie,N,!0),document.documentElement.addEventListener(ee,D,!0),document.documentElement.addEventListener(ne,D,!0),re=!0)}function R(t){se[t.pointerId]=t,ae++}function N(t){se[t.pointerId]&&(se[t.pointerId]=t)}function D(t){delete se[t.pointerId],ae--}function j(t,i){t.touches=[];for(var e in se)t.touches.push(se[e]);t.changedTouches=[t],i(t)}function W(t,i,e){var n=function(t){(t.pointerType!==t.MSPOINTER_TYPE_MOUSE&&"mouse"!==t.pointerType||0!==t.buttons)&&j(t,i)};t["_leaflet_touchmove"+e]=n,t.addEventListener(ie,n,!1)}function H(t,i,e){var n=function(t){j(t,i)};t["_leaflet_touchend"+e]=n,t.addEventListener(ee,n,!1),t.addEventListener(ne,n,!1)}function F(t,i,e){function n(t){var i;if(Vi){if(!bi||"mouse"===t.pointerType)return;i=ae}else i=t.touches.length;if(!(i>1)){var e=Date.now(),n=e-(s||e);r=t.touches?t.touches[0]:t,a=n>0&&n<=h,s=e}}function o(t){if(a&&!r.cancelBubble){if(Vi){if(!bi||"mouse"===t.pointerType)return;var e,n,o={};for(n in r)e=r[n],o[n]=e&&e.bind?e.bind(r):e;r=o}r.type="dblclick",i(r),s=null}}var s,r,a=!1,h=250;return t[le+he+e]=n,t[le+ue+e]=o,t[le+"dblclick"+e]=i,t.addEventListener(he,n,!1),t.addEventListener(ue,o,!1),t.addEventListener("dblclick",i,!1),this}function U(t,i){var e=t[le+he+i],n=t[le+ue+i],o=t[le+"dblclick"+i];return t.removeEventListener(he,e,!1),t.removeEventListener(ue,n,!1),bi||t.removeEventListener("dblclick",o,!1),this}function V(t){return"string"==typeof t?document.getElementById(t):t}function q(t,i){var e=t.style[i]||t.currentStyle&&t.currentStyle[i];if((!e||"auto"===e)&&document.defaultView){var n=document.defaultView.getComputedStyle(t,null);e=n?n[i]:null}return"auto"===e?null:e}function G(t,i,e){var n=document.createElement(t);return n.className=i||"",e&&e.appendChild(n),n}function K(t){var i=t.parentNode;i&&i.removeChild(t)}function Y(t){for(;t.firstChild;)t.removeChild(t.firstChild)}function X(t){var i=t.parentNode;i.lastChild!==t&&i.appendChild(t)}function J(t){var i=t.parentNode;i.firstChild!==t&&i.insertBefore(t,i.firstChild)}function $(t,i){if(void 0!==t.classList)return t.classList.contains(i);var e=et(t);return e.length>0&&new RegExp("(^|\\s)"+i+"(\\s|$)").test(e)}function Q(t,i){if(void 0!==t.classList)for(var e=u(i),n=0,o=e.length;n<o;n++)t.classList.add(e[n]);else if(!$(t,i)){var s=et(t);it(t,(s?s+" ":"")+i)}}function tt(t,i){void 0!==t.classList?t.classList.remove(i):it(t,h((" "+et(t)+" ").replace(" "+i+" "," ")))}function it(t,i){void 0===t.className.baseVal?t.className=i:t.className.baseVal=i}function et(t){return void 0===t.className.baseVal?t.className:t.className.baseVal}function nt(t,i){"opacity"in t.style?t.style.opacity=i:"filter"in t.style&&ot(t,i)}function ot(t,i){var e=!1,n="DXImageTransform.Microsoft.Alpha";try{e=t.filters.item(n)}catch(t){if(1===i)return}i=Math.round(100*i),e?(e.Enabled=100!==i,e.Opacity=i):t.style.filter+=" progid:"+n+"(opacity="+i+")"}function st(t){for(var i=document.documentElement.style,e=0;e<t.length;e++)if(t[e]in i)return t[e];return!1}function rt(t,i,e){var n=i||new x(0,0);t.style[ce]=(Ri?"translate("+n.x+"px,"+n.y+"px)":"translate3d("+n.x+"px,"+n.y+"px,0)")+(e?" scale("+e+")":"")}function at(t,i){t._leaflet_pos=i,ji?rt(t,i):(t.style.left=i.x+"px",t.style.top=i.y+"px")}function ht(t){return t._leaflet_pos||new x(0,0)}function ut(){mt(window,"dragstart",Pt)}function lt(){ft(window,"dragstart",Pt)}function ct(t){for(;-1===t.tabIndex;)t=t.parentNode;t.style&&(_t(),me=t,fe=t.style.outline,t.style.outline="none",mt(window,"keydown",_t))}function _t(){me&&(me.style.outline=fe,me=void 0,fe=void 0,ft(window,"keydown",_t))}function dt(t){do{t=t.parentNode}while(!(t.offsetWidth&&t.offsetHeight||t===document.body));return t}function pt(t){var i=t.getBoundingClientRect();return{x:i.width/t.offsetWidth||1,y:i.height/t.offsetHeight||1,boundingClientRect:i}}function mt(t,i,e,n){if("object"==typeof i)for(var o in i)gt(t,o,i[o],e);else for(var s=0,r=(i=u(i)).length;s<r;s++)gt(t,i[s],e,n);return this}function ft(t,i,e,n){if("object"==typeof i)for(var o in i)vt(t,o,i[o],e);else if(i)for(var s=0,r=(i=u(i)).length;s<r;s++)vt(t,i[s],e,n);else{for(var a in t[ye])vt(t,a,t[ye][a]);delete t[ye]}return this}function gt(t,i,e,o){var s=i+n(e)+(o?"_"+n(o):"");if(t[ye]&&t[ye][s])return this;var r=function(i){return e.call(o||t,i||window.event)},a=r;Vi&&0===i.indexOf("touch")?B(t,i,r,s):!qi||"dblclick"!==i||!F||Vi&&Ei?"addEventListener"in t?"mousewheel"===i?t.addEventListener("onwheel"in t?"wheel":"mousewheel",r,!1):"mouseenter"===i||"mouseleave"===i?(r=function(i){i=i||window.event,Ct(t,i)&&a(i)},t.addEventListener("mouseenter"===i?"mouseover":"mouseout",r,!1)):("click"===i&&zi&&(r=function(t){St(t,a)}),t.addEventListener(i,r,!1)):"attachEvent"in t&&t.attachEvent("on"+i,r):F(t,r,s),t[ye]=t[ye]||{},t[ye][s]=r}function vt(t,i,e,o){var s=i+n(e)+(o?"_"+n(o):""),r=t[ye]&&t[ye][s];if(!r)return this;Vi&&0===i.indexOf("touch")?I(t,i,s):!qi||"dblclick"!==i||!U||Vi&&Ei?"removeEventListener"in t?"mousewheel"===i?t.removeEventListener("onwheel"in t?"wheel":"mousewheel",r,!1):t.removeEventListener("mouseenter"===i?"mouseover":"mouseleave"===i?"mouseout":i,r,!1):"detachEvent"in t&&t.detachEvent("on"+i,r):U(t,s),t[ye][s]=null}function yt(t){return t.stopPropagation?t.stopPropagation():t.originalEvent?t.originalEvent._stopped=!0:t.cancelBubble=!0,Mt(t),this}function xt(t){return gt(t,"mousewheel",yt),this}function wt(t){return mt(t,"mousedown touchstart dblclick",yt),gt(t,"click",zt),this}function Pt(t){return t.preventDefault?t.preventDefault():t.returnValue=!1,this}function Lt(t){return Pt(t),yt(t),this}function bt(t,i){if(!i)return new x(t.clientX,t.clientY);var e=pt(i),n=e.boundingClientRect;return new x((t.clientX-n.left)/e.x-i.clientLeft,(t.clientY-n.top)/e.y-i.clientTop)}function Tt(t){return bi?t.wheelDeltaY/2:t.deltaY&&0===t.deltaMode?-t.deltaY/xe:t.deltaY&&1===t.deltaMode?20*-t.deltaY:t.deltaY&&2===t.deltaMode?60*-t.deltaY:t.deltaX||t.deltaZ?0:t.wheelDelta?(t.wheelDeltaY||t.wheelDelta)/2:t.detail&&Math.abs(t.detail)<32765?20*-t.detail:t.detail?t.detail/-32765*60:0}function zt(t){we[t.type]=!0}function Mt(t){var i=we[t.type];return we[t.type]=!1,i}function Ct(t,i){var e=i.relatedTarget;if(!e)return!0;try{for(;e&&e!==t;)e=e.parentNode}catch(t){return!1}return e!==t}function St(t,i){var e=t.timeStamp||t.originalEvent&&t.originalEvent.timeStamp,n=ge&&e-ge;n&&n>100&&n<500||t.target._simulatedClick&&!t._simulated?Lt(t):(ge=e,i(t))}function Zt(t,i){if(!i||!t.length)return t.slice();var e=i*i;return t=Bt(t,e),t=kt(t,e)}function Et(t,i,e){return Math.sqrt(Dt(t,i,e,!0))}function kt(t,i){var e=t.length,n=new(typeof Uint8Array!=void 0+""?Uint8Array:Array)(e);n[0]=n[e-1]=1,At(t,n,i,0,e-1);var o,s=[];for(o=0;o<e;o++)n[o]&&s.push(t[o]);return s}function At(t,i,e,n,o){var s,r,a,h=0;for(r=n+1;r<=o-1;r++)(a=Dt(t[r],t[n],t[o],!0))>h&&(s=r,h=a);h>e&&(i[s]=1,At(t,i,e,n,s),At(t,i,e,s,o))}function Bt(t,i){for(var e=[t[0]],n=1,o=0,s=t.length;n<s;n++)Nt(t[n],t[o])>i&&(e.push(t[n]),o=n);return o<s-1&&e.push(t[s-1]),e}function It(t,i,e,n,o){var s,r,a,h=n?ke:Rt(t,e),u=Rt(i,e);for(ke=u;;){if(!(h|u))return[t,i];if(h&u)return!1;a=Rt(r=Ot(t,i,s=h||u,e,o),e),s===h?(t=r,h=a):(