From e3880a1c86acaa3bbd05786ad2f5c586e6511a58 Mon Sep 17 00:00:00 2001 From: Yaco Date: Tue, 19 Oct 2021 20:24:11 -0300 Subject: updates Maps to 7.13.0 --- www/wiki/extensions/Maps/.travis.yml | 12 +- www/wiki/extensions/Maps/DefaultSettings.php | 9 +- www/wiki/extensions/Maps/INSTALL.md | 36 +- www/wiki/extensions/Maps/README.md | 11 +- www/wiki/extensions/Maps/RELEASE-NOTES.md | 130 + www/wiki/extensions/Maps/composer.json | 5 +- www/wiki/extensions/Maps/extension.json | 220 +- www/wiki/extensions/Maps/i18n/ar.json | 44 +- www/wiki/extensions/Maps/i18n/ast.json | 41 +- www/wiki/extensions/Maps/i18n/be-tarask.json | 2 +- www/wiki/extensions/Maps/i18n/bn.json | 7 +- www/wiki/extensions/Maps/i18n/de.json | 3 +- www/wiki/extensions/Maps/i18n/el.json | 3 +- www/wiki/extensions/Maps/i18n/en.json | 54 +- www/wiki/extensions/Maps/i18n/es.json | 29 +- www/wiki/extensions/Maps/i18n/fi.json | 9 +- www/wiki/extensions/Maps/i18n/fr.json | 250 +- www/wiki/extensions/Maps/i18n/gl.json | 6 +- www/wiki/extensions/Maps/i18n/he.json | 7 +- www/wiki/extensions/Maps/i18n/hr.json | 3 +- www/wiki/extensions/Maps/i18n/ia.json | 2 +- www/wiki/extensions/Maps/i18n/it.json | 23 +- www/wiki/extensions/Maps/i18n/ja.json | 3 +- www/wiki/extensions/Maps/i18n/ko.json | 11 +- www/wiki/extensions/Maps/i18n/lb.json | 8 +- www/wiki/extensions/Maps/i18n/lt.json | 41 +- www/wiki/extensions/Maps/i18n/mk.json | 58 +- www/wiki/extensions/Maps/i18n/nb.json | 48 +- www/wiki/extensions/Maps/i18n/nl.json | 3 +- www/wiki/extensions/Maps/i18n/pl.json | 45 +- www/wiki/extensions/Maps/i18n/pt-br.json | 39 +- www/wiki/extensions/Maps/i18n/pt.json | 7 +- www/wiki/extensions/Maps/i18n/qqq.json | 29 +- www/wiki/extensions/Maps/i18n/roa-tara.json | 40 +- www/wiki/extensions/Maps/i18n/ru.json | 41 +- www/wiki/extensions/Maps/i18n/sh.json | 69 + www/wiki/extensions/Maps/i18n/sv.json | 45 +- www/wiki/extensions/Maps/i18n/tt-cyrl.json | 73 + www/wiki/extensions/Maps/i18n/uk.json | 3 +- www/wiki/extensions/Maps/i18n/vi.json | 3 +- www/wiki/extensions/Maps/i18n/zh-hans.json | 10 +- www/wiki/extensions/Maps/i18n/zh-hant.json | 46 +- .../resources/GoogleMaps/ext.maps.googlemaps3.js | 31 +- .../resources/GoogleMaps/ext.sm.googlemaps3ajax.js | 48 - .../GoogleMaps/geoxml3/ProjectedOverlay.js | 186 +- .../Maps/resources/GoogleMaps/geoxml3/README | 2 +- .../GoogleMaps/geoxml3/ZipFile.complete.js | 3939 +++--- .../Maps/resources/GoogleMaps/geoxml3/geoxml3.js | 3398 ++--- .../Maps/resources/GoogleMaps/googlemaps3ajax.js | 48 + .../Maps/resources/GoogleMaps/jquery.googlemap.js | 10 +- www/wiki/extensions/Maps/resources/MapSaver.js | 57 + .../WikitextEditor/css/jquery.miniColors.css | 70 + .../resources/WikitextEditor/css/mapeditor.css | 79 + .../resources/WikitextEditor/images/circle.gif | Bin 0 -> 78 bytes .../resources/WikitextEditor/images/gradient.png | Bin 0 -> 6548 bytes .../Maps/resources/WikitextEditor/images/line.gif | Bin 0 -> 1104 bytes .../resources/WikitextEditor/images/rainbow.png | Bin 0 -> 1665 bytes .../resources/WikitextEditor/images/trigger.png | Bin 0 -> 538 bytes .../Maps/resources/WikitextEditor/js/README | 3 + .../WikitextEditor/js/jquery.miniColors.js | 567 + .../WikitextEditor/js/mapeditor.iefixes.js | 28 + .../Maps/resources/WikitextEditor/js/mapeditor.js | 919 ++ www/wiki/extensions/Maps/resources/api.js | 44 + .../resources/editor/css/jquery.miniColors.css | 70 - .../Maps/resources/editor/css/mapeditor.css | 79 - .../Maps/resources/editor/images/circle.gif | Bin 78 -> 0 bytes .../Maps/resources/editor/images/gradient.png | Bin 6548 -> 0 bytes .../Maps/resources/editor/images/line.gif | Bin 1104 -> 0 bytes .../Maps/resources/editor/images/rainbow.png | Bin 1665 -> 0 bytes .../Maps/resources/editor/images/trigger.png | Bin 538 -> 0 bytes .../extensions/Maps/resources/editor/js/README | 3 - .../Maps/resources/editor/js/jquery.miniColors.js | 567 - .../Maps/resources/editor/js/mapeditor.iefixes.js | 28 - .../Maps/resources/editor/js/mapeditor.js | 919 -- www/wiki/extensions/Maps/resources/geoJsonPage.js | 96 + .../extensions/Maps/resources/geojson.new.page.js | 38 + .../Maps/resources/leaflet/FeatureBuilder.js | 220 + .../extensions/Maps/resources/leaflet/GeoJson.js | 74 + .../Maps/resources/leaflet/LeafletCluster.js | 69 + .../Maps/resources/leaflet/LeafletEditor.js | 256 + .../Maps/resources/leaflet/LeafletLoader.js | 15 + .../Maps/resources/leaflet/cluster/m1.png | Bin 3288 -> 3003 bytes .../Maps/resources/leaflet/cluster/m2.png | Bin 3707 -> 3259 bytes .../Maps/resources/leaflet/cluster/m3.png | Bin 5605 -> 3956 bytes .../Maps/resources/leaflet/cluster/m4.png | Bin 6515 -> 5705 bytes .../Maps/resources/leaflet/cluster/m5.png | Bin 7249 -> 6839 bytes .../Maps/resources/leaflet/ext.maps.leaflet.js | 4 - .../Maps/resources/leaflet/ext.sm.leafletajax.js | 44 - .../Maps/resources/leaflet/images/edit-solid.svg | 1 + .../Maps/resources/leaflet/images/save-solid.svg | 1 + .../Maps/resources/leaflet/jquery.leaflet.js | 567 +- .../leaflet/leaflet-providers/leaflet-providers.js | 774 - .../leaflet/leaflet.editable/Leaflet.Editable.js | 1945 --- .../Maps/resources/leaflet/leaflet.editor.js | 15 - .../resources/leaflet/leaflet.fullscreen/.jshintrc | 12 - .../leaflet.fullscreen/Control.FullScreen.css | 4 - .../leaflet.fullscreen/Control.FullScreen.js | 164 - .../resources/leaflet/leaflet.fullscreen/LICENSE | 22 - .../resources/leaflet/leaflet.fullscreen/README.md | 68 - .../leaflet/leaflet.fullscreen/bower.json | 30 - .../leaflet.fullscreen/icon-fullscreen-2x.png | Bin 228 -> 0 bytes .../leaflet/leaflet.fullscreen/icon-fullscreen.png | Bin 153 -> 0 bytes .../leaflet/leaflet.fullscreen/index.html | 48 - .../leaflet/leaflet.fullscreen/package.json | 25 - .../leaflet.markercluster/MarkerCluster.css | 14 - .../leaflet.markercluster/leaflet.markercluster.js | 3 - .../resources/leaflet/leaflet/images/layers-2x.png | Bin 1259 -> 0 bytes .../resources/leaflet/leaflet/images/layers.png | Bin 696 -> 0 bytes .../leaflet/leaflet/images/marker-icon-2x.png | Bin 4230 -> 0 bytes .../leaflet/leaflet/images/marker-icon.png | Bin 1870 -> 0 bytes .../leaflet/leaflet/images/marker-shadow.png | Bin 618 -> 0 bytes .../Maps/resources/leaflet/leaflet/leaflet.css | 635 - .../Maps/resources/leaflet/leaflet/leaflet.js | 5 - .../lib/leaflet-providers/leaflet-providers.js | 774 + .../lib/leaflet.EasyButton/easy-button.css | 56 + .../lib/leaflet.EasyButton/easy-button.js | 377 + .../css/Leaflet.StyleEditor.min.css | 306 + .../lib/leaflet.StyleEditor/img/control.svg | 114 + .../resources/lib/leaflet.StyleEditor/img/icon.png | Bin 0 -> 4546 bytes .../javascript/Leaflet.StyleEditor.min.js | 2 + .../lib/leaflet.draw/images/spritesheet-2x.png | Bin 0 -> 3581 bytes .../lib/leaflet.draw/images/spritesheet.svg | 156 + .../lib/leaflet.draw/leaflet.draw-src.css | 325 + .../resources/lib/leaflet.draw/leaflet.draw-src.js | 4774 +++++++ .../lib/leaflet.draw/leaflet.draw-src.map | 1 + .../resources/lib/leaflet.draw/leaflet.draw.css | 10 + .../resources/lib/leaflet.draw/leaflet.draw.js | 10 + .../lib/leaflet.fullscreen/Control.FullScreen.css | 8 + .../lib/leaflet.fullscreen/Control.FullScreen.js | 228 + .../lib/leaflet.fullscreen/icon-fullscreen-2x.png | Bin 0 -> 215 bytes .../lib/leaflet.fullscreen/icon-fullscreen.png | Bin 0 -> 139 bytes .../resources/lib/leaflet.fullscreen/package.json | 25 + .../lib/leaflet.markercluster/MarkerCluster.css | 14 + .../leaflet.markercluster/leaflet.markercluster.js | 2689 ++++ .../resources/lib/leaflet/images/layers-2x.png | Bin 0 -> 1259 bytes .../Maps/resources/lib/leaflet/images/layers.png | Bin 0 -> 696 bytes .../lib/leaflet/images/marker-icon-2x.png | Bin 0 -> 2586 bytes .../resources/lib/leaflet/images/marker-icon.png | Bin 0 -> 1466 bytes .../resources/lib/leaflet/images/marker-shadow.png | Bin 0 -> 618 bytes .../Maps/resources/lib/leaflet/leaflet.css | 640 + .../Maps/resources/lib/leaflet/leaflet.js | 14079 +++++++++++++++++++ www/wiki/extensions/Maps/resources/maps.common.js | 4 - .../extensions/Maps/resources/maps.services.js | 86 - www/wiki/extensions/Maps/resources/semanticMaps.js | 76 + www/wiki/extensions/Maps/resources/sm.common.js | 76 - .../Maps/src/DataAccess/GeoJsonFetcher.php | 93 + .../Maps/src/DataAccess/GeoJsonFetcherResult.php | 31 + .../Maps/src/DataAccess/JsonFileParser.php | 79 - www/wiki/extensions/Maps/src/GoogleMapsService.php | 71 +- www/wiki/extensions/Maps/src/LeafletService.php | 66 +- www/wiki/extensions/Maps/src/MappingService.php | 6 +- www/wiki/extensions/Maps/src/MapsFactory.php | 34 + www/wiki/extensions/Maps/src/MapsFunctions.php | 32 +- www/wiki/extensions/Maps/src/MapsSetup.php | 87 +- .../Maps/src/MediaWiki/Content/GeoJsonContent.php | 57 +- .../MediaWiki/Content/GeoJsonContentHandler.php | 6 +- .../extensions/Maps/src/MediaWiki/MapsHooks.php | 61 + .../MediaWiki/ParserHooks/DisplayMapFunction.php | 117 +- .../MediaWiki/ParserHooks/DisplayMapRenderer.php | 7 +- .../ParserHooks/FindDestinationFunction.php | 4 +- .../src/MediaWiki/ParserHooks/MapsDocFunction.php | 19 +- .../src/MediaWiki/Specials/SpecialMapEditor.php | 3 +- .../Maps/src/Presentation/GeoJsonMapPageUi.php | 83 + .../Maps/src/Presentation/GeoJsonNewPageUi.php | 27 + .../Maps/src/Presentation/MapHtmlBuilder.php | 10 +- .../Maps/src/Presentation/MapsDistanceParser.php | 2 +- .../Maps/src/Presentation/OutputFacade.php | 62 + .../src/SemanticMW/ResultPrinters/MapPrinter.php | 17 +- .../Integration/DataAccess/GeoJsonFetcherTest.php | 119 + .../MediaWiki/ParserHooks/CoordinatesTest.php | 8 +- .../MediaWiki/ParserHooks/ParserHookTest.php | 13 +- .../tests/Integration/Parser/DisplayMapTest.php | 39 +- .../Maps/tests/Integration/ResourceModulesTest.php | 56 + .../Integration/parsers/JsonFileParserTest.php | 120 - .../tests/Unit/MediaWiki/GeoJsonContentTest.php | 22 + .../Maps/tests/js/leaflet/GeoJsonTest.js | 112 + 176 files changed, 33689 insertions(+), 10376 deletions(-) create mode 100644 www/wiki/extensions/Maps/i18n/sh.json create mode 100644 www/wiki/extensions/Maps/i18n/tt-cyrl.json delete mode 100644 www/wiki/extensions/Maps/resources/GoogleMaps/ext.sm.googlemaps3ajax.js create mode 100644 www/wiki/extensions/Maps/resources/GoogleMaps/googlemaps3ajax.js create mode 100644 www/wiki/extensions/Maps/resources/MapSaver.js create mode 100644 www/wiki/extensions/Maps/resources/WikitextEditor/css/jquery.miniColors.css create mode 100644 www/wiki/extensions/Maps/resources/WikitextEditor/css/mapeditor.css create mode 100644 www/wiki/extensions/Maps/resources/WikitextEditor/images/circle.gif create mode 100644 www/wiki/extensions/Maps/resources/WikitextEditor/images/gradient.png create mode 100644 www/wiki/extensions/Maps/resources/WikitextEditor/images/line.gif create mode 100644 www/wiki/extensions/Maps/resources/WikitextEditor/images/rainbow.png create mode 100644 www/wiki/extensions/Maps/resources/WikitextEditor/images/trigger.png create mode 100644 www/wiki/extensions/Maps/resources/WikitextEditor/js/README create mode 100644 www/wiki/extensions/Maps/resources/WikitextEditor/js/jquery.miniColors.js create mode 100644 www/wiki/extensions/Maps/resources/WikitextEditor/js/mapeditor.iefixes.js create mode 100644 www/wiki/extensions/Maps/resources/WikitextEditor/js/mapeditor.js create mode 100644 www/wiki/extensions/Maps/resources/api.js delete mode 100644 www/wiki/extensions/Maps/resources/editor/css/jquery.miniColors.css delete mode 100644 www/wiki/extensions/Maps/resources/editor/css/mapeditor.css delete mode 100644 www/wiki/extensions/Maps/resources/editor/images/circle.gif delete mode 100644 www/wiki/extensions/Maps/resources/editor/images/gradient.png delete mode 100644 www/wiki/extensions/Maps/resources/editor/images/line.gif delete mode 100644 www/wiki/extensions/Maps/resources/editor/images/rainbow.png delete mode 100644 www/wiki/extensions/Maps/resources/editor/images/trigger.png delete mode 100644 www/wiki/extensions/Maps/resources/editor/js/README delete mode 100644 www/wiki/extensions/Maps/resources/editor/js/jquery.miniColors.js delete mode 100644 www/wiki/extensions/Maps/resources/editor/js/mapeditor.iefixes.js delete mode 100644 www/wiki/extensions/Maps/resources/editor/js/mapeditor.js create mode 100644 www/wiki/extensions/Maps/resources/geoJsonPage.js create mode 100644 www/wiki/extensions/Maps/resources/geojson.new.page.js create mode 100644 www/wiki/extensions/Maps/resources/leaflet/FeatureBuilder.js create mode 100644 www/wiki/extensions/Maps/resources/leaflet/GeoJson.js create mode 100644 www/wiki/extensions/Maps/resources/leaflet/LeafletCluster.js create mode 100644 www/wiki/extensions/Maps/resources/leaflet/LeafletEditor.js create mode 100644 www/wiki/extensions/Maps/resources/leaflet/LeafletLoader.js delete mode 100644 www/wiki/extensions/Maps/resources/leaflet/ext.maps.leaflet.js delete mode 100644 www/wiki/extensions/Maps/resources/leaflet/ext.sm.leafletajax.js create mode 100644 www/wiki/extensions/Maps/resources/leaflet/images/edit-solid.svg create mode 100644 www/wiki/extensions/Maps/resources/leaflet/images/save-solid.svg delete mode 100644 www/wiki/extensions/Maps/resources/leaflet/leaflet-providers/leaflet-providers.js delete mode 100644 www/wiki/extensions/Maps/resources/leaflet/leaflet.editable/Leaflet.Editable.js delete mode 100644 www/wiki/extensions/Maps/resources/leaflet/leaflet.editor.js delete mode 100644 www/wiki/extensions/Maps/resources/leaflet/leaflet.fullscreen/.jshintrc delete mode 100644 www/wiki/extensions/Maps/resources/leaflet/leaflet.fullscreen/Control.FullScreen.css delete mode 100644 www/wiki/extensions/Maps/resources/leaflet/leaflet.fullscreen/Control.FullScreen.js delete mode 100644 www/wiki/extensions/Maps/resources/leaflet/leaflet.fullscreen/LICENSE delete mode 100644 www/wiki/extensions/Maps/resources/leaflet/leaflet.fullscreen/README.md delete mode 100644 www/wiki/extensions/Maps/resources/leaflet/leaflet.fullscreen/bower.json delete mode 100644 www/wiki/extensions/Maps/resources/leaflet/leaflet.fullscreen/icon-fullscreen-2x.png delete mode 100644 www/wiki/extensions/Maps/resources/leaflet/leaflet.fullscreen/icon-fullscreen.png delete mode 100644 www/wiki/extensions/Maps/resources/leaflet/leaflet.fullscreen/index.html delete mode 100644 www/wiki/extensions/Maps/resources/leaflet/leaflet.fullscreen/package.json delete mode 100644 www/wiki/extensions/Maps/resources/leaflet/leaflet.markercluster/MarkerCluster.css delete mode 100644 www/wiki/extensions/Maps/resources/leaflet/leaflet.markercluster/leaflet.markercluster.js delete mode 100644 www/wiki/extensions/Maps/resources/leaflet/leaflet/images/layers-2x.png delete mode 100644 www/wiki/extensions/Maps/resources/leaflet/leaflet/images/layers.png delete mode 100644 www/wiki/extensions/Maps/resources/leaflet/leaflet/images/marker-icon-2x.png delete mode 100644 www/wiki/extensions/Maps/resources/leaflet/leaflet/images/marker-icon.png delete mode 100644 www/wiki/extensions/Maps/resources/leaflet/leaflet/images/marker-shadow.png delete mode 100644 www/wiki/extensions/Maps/resources/leaflet/leaflet/leaflet.css delete mode 100644 www/wiki/extensions/Maps/resources/leaflet/leaflet/leaflet.js create mode 100644 www/wiki/extensions/Maps/resources/lib/leaflet-providers/leaflet-providers.js create mode 100644 www/wiki/extensions/Maps/resources/lib/leaflet.EasyButton/easy-button.css create mode 100644 www/wiki/extensions/Maps/resources/lib/leaflet.EasyButton/easy-button.js create mode 100644 www/wiki/extensions/Maps/resources/lib/leaflet.StyleEditor/css/Leaflet.StyleEditor.min.css create mode 100644 www/wiki/extensions/Maps/resources/lib/leaflet.StyleEditor/img/control.svg create mode 100644 www/wiki/extensions/Maps/resources/lib/leaflet.StyleEditor/img/icon.png create mode 100644 www/wiki/extensions/Maps/resources/lib/leaflet.StyleEditor/javascript/Leaflet.StyleEditor.min.js create mode 100644 www/wiki/extensions/Maps/resources/lib/leaflet.draw/images/spritesheet-2x.png create mode 100644 www/wiki/extensions/Maps/resources/lib/leaflet.draw/images/spritesheet.svg create mode 100644 www/wiki/extensions/Maps/resources/lib/leaflet.draw/leaflet.draw-src.css create mode 100644 www/wiki/extensions/Maps/resources/lib/leaflet.draw/leaflet.draw-src.js create mode 100644 www/wiki/extensions/Maps/resources/lib/leaflet.draw/leaflet.draw-src.map create mode 100644 www/wiki/extensions/Maps/resources/lib/leaflet.draw/leaflet.draw.css create mode 100644 www/wiki/extensions/Maps/resources/lib/leaflet.draw/leaflet.draw.js create mode 100644 www/wiki/extensions/Maps/resources/lib/leaflet.fullscreen/Control.FullScreen.css create mode 100644 www/wiki/extensions/Maps/resources/lib/leaflet.fullscreen/Control.FullScreen.js create mode 100644 www/wiki/extensions/Maps/resources/lib/leaflet.fullscreen/icon-fullscreen-2x.png create mode 100644 www/wiki/extensions/Maps/resources/lib/leaflet.fullscreen/icon-fullscreen.png create mode 100644 www/wiki/extensions/Maps/resources/lib/leaflet.fullscreen/package.json create mode 100644 www/wiki/extensions/Maps/resources/lib/leaflet.markercluster/MarkerCluster.css create mode 100644 www/wiki/extensions/Maps/resources/lib/leaflet.markercluster/leaflet.markercluster.js create mode 100644 www/wiki/extensions/Maps/resources/lib/leaflet/images/layers-2x.png create mode 100644 www/wiki/extensions/Maps/resources/lib/leaflet/images/layers.png create mode 100644 www/wiki/extensions/Maps/resources/lib/leaflet/images/marker-icon-2x.png create mode 100644 www/wiki/extensions/Maps/resources/lib/leaflet/images/marker-icon.png create mode 100644 www/wiki/extensions/Maps/resources/lib/leaflet/images/marker-shadow.png create mode 100644 www/wiki/extensions/Maps/resources/lib/leaflet/leaflet.css create mode 100644 www/wiki/extensions/Maps/resources/lib/leaflet/leaflet.js delete mode 100644 www/wiki/extensions/Maps/resources/maps.common.js delete mode 100644 www/wiki/extensions/Maps/resources/maps.services.js create mode 100644 www/wiki/extensions/Maps/resources/semanticMaps.js delete mode 100644 www/wiki/extensions/Maps/resources/sm.common.js create mode 100644 www/wiki/extensions/Maps/src/DataAccess/GeoJsonFetcher.php create mode 100644 www/wiki/extensions/Maps/src/DataAccess/GeoJsonFetcherResult.php delete mode 100644 www/wiki/extensions/Maps/src/DataAccess/JsonFileParser.php create mode 100644 www/wiki/extensions/Maps/src/Presentation/GeoJsonMapPageUi.php create mode 100644 www/wiki/extensions/Maps/src/Presentation/GeoJsonNewPageUi.php create mode 100644 www/wiki/extensions/Maps/src/Presentation/OutputFacade.php create mode 100644 www/wiki/extensions/Maps/tests/Integration/DataAccess/GeoJsonFetcherTest.php create mode 100644 www/wiki/extensions/Maps/tests/Integration/ResourceModulesTest.php delete mode 100644 www/wiki/extensions/Maps/tests/Integration/parsers/JsonFileParserTest.php create mode 100644 www/wiki/extensions/Maps/tests/Unit/MediaWiki/GeoJsonContentTest.php create mode 100644 www/wiki/extensions/Maps/tests/js/leaflet/GeoJsonTest.js (limited to 'www') 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 @@ '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,26 +18,40 @@ minimum requirements are indicated in bold. For a detailed list of changes, see - - - - - + + + + + - + - - - + + + - + - + + + + + + + + + + + + + + + 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                              [![Build Status](https://secure.travis-ci.org/JeroenDeDauw/Maps.png?branch=master)](http://travis-ci.org/JeroenDeDauw/Maps) * Code quality                       [![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 - */ -(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/ext.sm.googlemaps3ajax.js b/www/wiki/extensions/Maps/resources/GoogleMaps/ext.sm.googlemaps3ajax.js deleted file mode 100644 index 9f8c658a..00000000 --- a/www/wiki/extensions/Maps/resources/GoogleMaps/ext.sm.googlemaps3ajax.js +++ /dev/null @@ -1,48 +0,0 @@ -/** - * JavaScript for Google Maps v3 maps 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', 'zoom_changed']; - - $( document ).ready( function() { - // todo: find a way to remove setTimeout. - setTimeout( function() { - if( typeof google === 'undefined' ) { - return; - } - $( window.maps.googlemapsList ).each( function( index, map ) { - if( !map.options.ajaxquery || !map.options.ajaxcoordproperty ) { - return; - } - $( mapEvents ).each( function( index, event ) { - google.maps.event.addListener( map.map, event, 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; - } ); - } ); - } ); - } ); - }, 500 ); - } ); -})( window.jQuery, window.sm ); 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 = '' ; - 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 = '' ; + 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" ), "&") - .replace(new RegExp( "<", "g" ), "<") - .replace(new RegExp( ">", "g" ), ">") - .replace(new RegExp( "\x13", "g" ), "
") - .replace(new RegExp( "\x10", "g" ), "
"); - }; - - 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" ), "&") + .replace(new RegExp( "<", "g" ), "<") + .replace(new RegExp( ">", "g" ), ">") + .replace(new RegExp( "\x13", "g" ), "
") + .replace(new RegExp( "\x10", "g" ), "
"); + }; + + 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= 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 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= 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 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 = - "\r\n"+ - "\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 = + "\r\n"+ + "\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= 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= 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>>8); - } - } else { - for (var n2=0; n2>>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>>8); + } + } else { + for (var n2=0; n2>>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 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[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 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 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[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 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$[name]\n
$[description]
\n
$[geDirections]
", - 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 0) { - break; - } else { - return [{coordinates: []}]; - } - } - - for (var j=0; j 0)) { - var style = processStyle(node, '{inline}', '{inline}'); - processStyleID(style); - if (style) placemark.style = style; - } - - if (/^https?:\/\//.test(placemark.description)) { - placemark.description = ['', placemark.description, ''].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= 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 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$[name]\n
$[description]
\n
$[geDirections]
", + 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 0) { + break; + } else { + return [{coordinates: []}]; + } + } + + for (var j=0; j 0)) { + var style = processStyle(node, '{inline}', '{inline}'); + processStyleID(style); + if (style) placemark.style = style; + } + + if (/^https?:\/\//.test(placemark.description)) { + placemark.description = ['', placemark.description, ''].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 0) { + for (var i=0; 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 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; jTo Here - From Here'; - } - 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: '
' + iwText + '
', - 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 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;polygonPartTo Here - From Here'; + } + 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: '
' + iwText + '
', + 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("
"+iW.getContent()+"
"); + 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. - * - *

By default, the parser only processes KML <Style> 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.

- */ - 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 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 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. + * + *

By default, the parser only processes KML <Style> 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.

+ */ + 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 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 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,34 +1558,42 @@ 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. * @@ -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> 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/googlemaps3ajax.js b/www/wiki/extensions/Maps/resources/GoogleMaps/googlemaps3ajax.js new file mode 100644 index 00000000..aa86319e --- /dev/null +++ b/www/wiki/extensions/Maps/resources/GoogleMaps/googlemaps3ajax.js @@ -0,0 +1,48 @@ +/** + * JavaScript for Google Maps v3 maps 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', 'zoom_changed']; + + $( document ).ready( function() { + // todo: find a way to remove setTimeout. + setTimeout( function() { + if( typeof google === 'undefined' ) { + return; + } + $( window.mapsGoogleList ).each( function( index, map ) { + if( !map.options.ajaxquery || !map.options.ajaxcoordproperty ) { + return; + } + $( mapEvents ).each( function( index, event ) { + google.maps.event.addListener( map.map, event, 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; + } ); + } ); + } ); + } ); + }, 500 ); + } ); +})( window.jQuery, window.sm ); 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/WikitextEditor/css/jquery.miniColors.css b/www/wiki/extensions/Maps/resources/WikitextEditor/css/jquery.miniColors.css new file mode 100644 index 00000000..664c2fd4 --- /dev/null +++ b/www/wiki/extensions/Maps/resources/WikitextEditor/css/jquery.miniColors.css @@ -0,0 +1,70 @@ +.miniColors-trigger { + height: 22px; + width: 22px; + /* @embed */ + background: url(../images/trigger.png) center no-repeat; + vertical-align: middle; + margin: 0 .25em; + display: inline-block; + outline: none; +} + +.miniColors-selector { + position: absolute; + width: 175px; + height: 150px; + background: #FFF; + border: solid 1px #BBB; + -moz-box-shadow: 0 0 6px rgba(0, 0, 0, .25); + -webkit-box-shadow: 0 0 6px rgba(0, 0, 0, .25); + box-shadow: 0 0 6px rgba(0, 0, 0, .25); + -moz-border-radius: 5px; + -webkit-border-radius: 5px; + border-radius: 5px; + padding: 5px; + z-index: 999999; +} + +.miniColors-selector.black { + background: #000; + border-color: #000; +} + +.miniColors-colors { + position: absolute; + top: 5px; + left: 5px; + width: 150px; + height: 150px; + /* @embed */ + background: url(../images/gradient.png) center no-repeat; + cursor: crosshair; +} + +.miniColors-hues { + position: absolute; + top: 5px; + left: 160px; + width: 20px; + height: 150px; + /* @embed */ + background: url(../images/rainbow.png) center no-repeat; + cursor: crosshair; +} + +.miniColors-colorPicker { + position: absolute; + width: 11px; + height: 11px; + /* @embed */ + background: url(../images/circle.gif) center no-repeat; +} + +.miniColors-huePicker { + position: absolute; + left: -3px; + width: 26px; + height: 3px; + /* @embed */ + background: url(../images/line.gif) center no-repeat; +} \ No newline at end of file diff --git a/www/wiki/extensions/Maps/resources/WikitextEditor/css/mapeditor.css b/www/wiki/extensions/Maps/resources/WikitextEditor/css/mapeditor.css new file mode 100644 index 00000000..9c3fe7c4 --- /dev/null +++ b/www/wiki/extensions/Maps/resources/WikitextEditor/css/mapeditor.css @@ -0,0 +1,79 @@ +#code-output-container, #code-input-container,#code-input { + height: 100% !important; + width: 100%; +} + +#code-output-container { + overflow: visible; +} + +textarea#code-input, textarea#code-output { + resize: none; +} + +#code-output { + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + border: none; + padding: 10px; +} + +#map-canvas { + width: 100%; + height: 500px; +} + +#map-canvas,#code-input { + display: block; +} + +.mapeditor-dialog-form fieldset label{ + display: block; +} + +.mapeditor-dialog-form fieldset input{ + width: 200px; +} + +.mapeditor-dialog-form fieldset input[type="checkbox"]{ + width: auto; +} + +.mapeditor-dialog-form fieldset input[name="strokeColor"],.mapeditor-dialog-form fieldset input[name="fillColor"]{ + width: 100px; +} + +.mapeditor-dialog-form .ui-slider{ + margin: 5px 2px; + width: 200px; +} + +.link-title-switcher { + margin: 5px; +} + +#map-parameter-form{ + text-align: center; +} + +.mapeditor-controls{ + padding: 5px; +} + +.mapeditor-control-element{ + background-color:white; + border: 1px solid #717B87; + cursor: pointer; + text-align: center; + float: left; + padding: 2px; +} + +.mapeditor-control-text{ + font-family:"Arial","sans-serif"; + font-size: 12px; + padding-left: 4px; + padding-right: 4px; + font-weight: bold; +} \ No newline at end of file diff --git a/www/wiki/extensions/Maps/resources/WikitextEditor/images/circle.gif b/www/wiki/extensions/Maps/resources/WikitextEditor/images/circle.gif new file mode 100644 index 00000000..599f7f13 Binary files /dev/null and b/www/wiki/extensions/Maps/resources/WikitextEditor/images/circle.gif differ diff --git a/www/wiki/extensions/Maps/resources/WikitextEditor/images/gradient.png b/www/wiki/extensions/Maps/resources/WikitextEditor/images/gradient.png new file mode 100644 index 00000000..486a9f6d Binary files /dev/null and b/www/wiki/extensions/Maps/resources/WikitextEditor/images/gradient.png differ diff --git a/www/wiki/extensions/Maps/resources/WikitextEditor/images/line.gif b/www/wiki/extensions/Maps/resources/WikitextEditor/images/line.gif new file mode 100644 index 00000000..9eb19837 Binary files /dev/null and b/www/wiki/extensions/Maps/resources/WikitextEditor/images/line.gif differ diff --git a/www/wiki/extensions/Maps/resources/WikitextEditor/images/rainbow.png b/www/wiki/extensions/Maps/resources/WikitextEditor/images/rainbow.png new file mode 100644 index 00000000..d16fcd86 Binary files /dev/null and b/www/wiki/extensions/Maps/resources/WikitextEditor/images/rainbow.png differ diff --git a/www/wiki/extensions/Maps/resources/WikitextEditor/images/trigger.png b/www/wiki/extensions/Maps/resources/WikitextEditor/images/trigger.png new file mode 100644 index 00000000..20ec282b Binary files /dev/null and b/www/wiki/extensions/Maps/resources/WikitextEditor/images/trigger.png differ diff --git a/www/wiki/extensions/Maps/resources/WikitextEditor/js/README b/www/wiki/extensions/Maps/resources/WikitextEditor/js/README new file mode 100644 index 00000000..53598d48 --- /dev/null +++ b/www/wiki/extensions/Maps/resources/WikitextEditor/js/README @@ -0,0 +1,3 @@ +== jquery.miniColors.js == +Is dual licensed under the MIT / GPLv2 licenses +https://github.com/claviska/jquery-miniColors \ No newline at end of file diff --git a/www/wiki/extensions/Maps/resources/WikitextEditor/js/jquery.miniColors.js b/www/wiki/extensions/Maps/resources/WikitextEditor/js/jquery.miniColors.js new file mode 100644 index 00000000..dfbce542 --- /dev/null +++ b/www/wiki/extensions/Maps/resources/WikitextEditor/js/jquery.miniColors.js @@ -0,0 +1,567 @@ +/* + * jQuery miniColors: A small color selector + * + * Copyright 2011 Cory LaViska for A Beautiful Site, LLC. (http://abeautifulsite.net/) + * + * Dual licensed under the MIT or GPL Version 2 licenses + * +*/ +if(jQuery) (function($) { + + $.extend($.fn, { + + miniColors: function(o, data) { + + var create = function(input, o, data) { + // + // Creates a new instance of the miniColors selector + // + + // Determine initial color (defaults to white) + var color = expandHex(input.val()); + if( !color ) color = 'ffffff'; + var hsb = hex2hsb(color); + + // Create trigger + var trigger = $(''); + trigger.insertAfter(input); + + // Set input data and update attributes + input + .addClass('miniColors') + .data('original-maxlength', input.attr('maxlength') || null) + .data('original-autocomplete', input.attr('autocomplete') || null) + .data('letterCase', 'uppercase') + .data('trigger', trigger) + .data('hsb', hsb) + .data('change', o.change ? o.change : null) + .attr('maxlength', 7) + .attr('autocomplete', 'off') + .val('#' + convertCase(color, o.letterCase)); + + // Handle options + if( o.readonly ) input.prop('readonly', true); + if( o.disabled ) disable(input); + + // Show selector when trigger is clicked + trigger.bind('click.miniColors', function(event) { + event.preventDefault(); + if( input.val() === '' ) input.val('#'); + show(input); + + }); + + // Show selector when input receives focus + input.bind('focus.miniColors', function(event) { + if( input.val() === '' ) input.val('#'); + show(input); + }); + + // Hide on blur + input.bind('blur.miniColors', function(event) { + var hex = expandHex(input.val()); + input.val( hex ? '#' + convertCase(hex, input.data('letterCase')) : '' ); + }); + + // Hide when tabbing out of the input + input.bind('keydown.miniColors', function(event) { + if( event.keyCode === 9 ) hide(input); + }); + + // Update when color is typed in + input.bind('keyup.miniColors', function(event) { + setColorFromInput(input); + }); + + // Handle pasting + input.bind('paste.miniColors', function(event) { + // Short pause to wait for paste to complete + setTimeout( function() { + setColorFromInput(input); + }, 5); + }); + + }; + + var destroy = function(input) { + // + // Destroys an active instance of the miniColors selector + // + + hide(); + input = $(input); + + // Restore to original state + input.data('trigger').remove(); + input + .attr('autocomplete', input.data('original-autocomplete')) + .attr('maxlength', input.data('original-maxlength')) + .removeData() + .removeClass('miniColors') + .unbind('.miniColors'); + $(document).unbind('.miniColors'); + }; + + var enable = function(input) { + // + // Enables the input control and the selector + // + input + .prop('disabled', false) + .data('trigger') + .css('opacity', 1); + }; + + var disable = function(input) { + // + // Disables the input control and the selector + // + hide(input); + input + .prop('disabled', true) + .data('trigger') + .css('opacity', 0.5); + }; + + var show = function(input) { + // + // Shows the miniColors selector + // + if( input.prop('disabled') ) return false; + + // Hide all other instances + hide(); + + // Generate the selector + var selector = $('
'); + selector + .append('
') + .append('
') + .css({ + top: input.is(':visible') ? input.offset().top + input.outerHeight() : input.data('trigger').offset().top + input.data('trigger').outerHeight(), + left: input.is(':visible') ? input.offset().left : input.data('trigger').offset().left, + display: 'none' + }) + .addClass( input.attr('class') ); + + // Set background for colors + var hsb = input.data('hsb'); + selector + .find('.miniColors-colors') + .css('backgroundColor', '#' + hsb2hex({ h: hsb.h, s: 100, b: 100 })); + + // Set colorPicker position + var colorPosition = input.data('colorPosition'); + if( !colorPosition ) colorPosition = getColorPositionFromHSB(hsb); + selector.find('.miniColors-colorPicker') + .css('top', colorPosition.y + 'px') + .css('left', colorPosition.x + 'px'); + + // Set huePicker position + var huePosition = input.data('huePosition'); + if( !huePosition ) huePosition = getHuePositionFromHSB(hsb); + selector.find('.miniColors-huePicker').css('top', huePosition.y + 'px'); + + // Set input data + input + .data('selector', selector) + .data('huePicker', selector.find('.miniColors-huePicker')) + .data('colorPicker', selector.find('.miniColors-colorPicker')) + .data('mousebutton', 0); + + $('BODY').append(selector); + selector.fadeIn(100); + + // Prevent text selection in IE + selector.bind('selectstart', function() { return false; }); + + $(document).bind('mousedown.miniColors touchstart.miniColors', function(event) { + + input.data('mousebutton', 1); + + if( $(event.target).parents().andSelf().hasClass('miniColors-colors') ) { + event.preventDefault(); + input.data('moving', 'colors'); + moveColor(input, event); + } + + if( $(event.target).parents().andSelf().hasClass('miniColors-hues') ) { + event.preventDefault(); + input.data('moving', 'hues'); + moveHue(input, event); + } + + if( $(event.target).parents().andSelf().hasClass('miniColors-selector') ) { + event.preventDefault(); + return; + } + + if( $(event.target).parents().andSelf().hasClass('miniColors') ) return; + + hide(input); + }); + + $(document) + .bind('mouseup.miniColors touchend.miniColors', function(event) { + event.preventDefault(); + input.data('mousebutton', 0).removeData('moving'); + }) + .bind('mousemove.miniColors touchmove.miniColors', function(event) { + event.preventDefault(); + if( input.data('mousebutton') === 1 ) { + if( input.data('moving') === 'colors' ) moveColor(input, event); + if( input.data('moving') === 'hues' ) moveHue(input, event); + } + }); + + }; + + var hide = function(input) { + + // + // Hides one or more miniColors selectors + // + + // Hide all other instances if input isn't specified + if( !input ) input = '.miniColors'; + + $(input).each( function() { + var selector = $(this).data('selector'); + $(this).removeData('selector'); + $(selector).fadeOut(100, function() { + $(this).remove(); + }); + }); + + $(document).unbind('.miniColors'); + + }; + + var moveColor = function(input, event) { + + var colorPicker = input.data('colorPicker'); + + colorPicker.hide(); + + var position = { + x: event.pageX, + y: event.pageY + }; + + // Touch support + if( event.originalEvent.changedTouches ) { + position.x = event.originalEvent.changedTouches[0].pageX; + position.y = event.originalEvent.changedTouches[0].pageY; + } + position.x = position.x - input.data('selector').find('.miniColors-colors').offset().left - 5; + position.y = position.y - input.data('selector').find('.miniColors-colors').offset().top - 5; + if( position.x <= -5 ) position.x = -5; + if( position.x >= 144 ) position.x = 144; + if( position.y <= -5 ) position.y = -5; + if( position.y >= 144 ) position.y = 144; + + input.data('colorPosition', position); + colorPicker.css('left', position.x).css('top', position.y).show(); + + // Calculate saturation + var s = Math.round((position.x + 5) * 0.67); + if( s < 0 ) s = 0; + if( s > 100 ) s = 100; + + // Calculate brightness + var b = 100 - Math.round((position.y + 5) * 0.67); + if( b < 0 ) b = 0; + if( b > 100 ) b = 100; + + // Update HSB values + var hsb = input.data('hsb'); + hsb.s = s; + hsb.b = b; + + // Set color + setColor(input, hsb, true); + }; + + var moveHue = function(input, event) { + + var huePicker = input.data('huePicker'); + + huePicker.hide(); + + var position = { + y: event.pageY + }; + + // Touch support + if( event.originalEvent.changedTouches ) { + position.y = event.originalEvent.changedTouches[0].pageY; + } + + position.y = position.y - input.data('selector').find('.miniColors-colors').offset().top - 1; + if( position.y <= -1 ) position.y = -1; + if( position.y >= 149 ) position.y = 149; + input.data('huePosition', position); + huePicker.css('top', position.y).show(); + + // Calculate hue + var h = Math.round((150 - position.y - 1) * 2.4); + if( h < 0 ) h = 0; + if( h > 360 ) h = 360; + + // Update HSB values + var hsb = input.data('hsb'); + hsb.h = h; + + // Set color + setColor(input, hsb, true); + + }; + + var setColor = function(input, hsb, updateInput) { + input.data('hsb', hsb); + var hex = hsb2hex(hsb); + if( updateInput ) input.val( '#' + convertCase(hex, input.data('letterCase')) ); + input.data('trigger').css('backgroundColor', '#' + hex); + if( input.data('selector') ) input.data('selector').find('.miniColors-colors').css('backgroundColor', '#' + hsb2hex({ h: hsb.h, s: 100, b: 100 })); + + // Fire change callback + if( input.data('change') ) { + if( hex === input.data('lastChange') ) return; + input.data('change').call(input.get(0), '#' + hex, hsb2rgb(hsb)); + input.data('lastChange', hex); + } + + }; + + var setColorFromInput = function(input) { + + input.val('#' + cleanHex(input.val())); + var hex = expandHex(input.val()); + if( !hex ) return false; + + // Get HSB equivalent + var hsb = hex2hsb(hex); + + // If color is the same, no change required + var currentHSB = input.data('hsb'); + if( hsb.h === currentHSB.h && hsb.s === currentHSB.s && hsb.b === currentHSB.b ) return true; + + // Set colorPicker position + var colorPosition = getColorPositionFromHSB(hsb); + var colorPicker = $(input.data('colorPicker')); + colorPicker.css('top', colorPosition.y + 'px').css('left', colorPosition.x + 'px'); + input.data('colorPosition', colorPosition); + + // Set huePosition position + var huePosition = getHuePositionFromHSB(hsb); + var huePicker = $(input.data('huePicker')); + huePicker.css('top', huePosition.y + 'px'); + input.data('huePosition', huePosition); + + setColor(input, hsb); + + return true; + + }; + + var convertCase = function(string, letterCase) { + if( letterCase === 'lowercase' ) return string.toLowerCase(); + if( letterCase === 'uppercase' ) return string.toUpperCase(); + return string; + }; + + var getColorPositionFromHSB = function(hsb) { + var x = Math.ceil(hsb.s / 0.67); + if( x < 0 ) x = 0; + if( x > 150 ) x = 150; + var y = 150 - Math.ceil(hsb.b / 0.67); + if( y < 0 ) y = 0; + if( y > 150 ) y = 150; + return { x: x - 5, y: y - 5 }; + }; + + var getHuePositionFromHSB = function(hsb) { + var y = 150 - (hsb.h / 2.4); + if( y < 0 ) h = 0; + if( y > 150 ) h = 150; + return { y: y - 1 }; + }; + + var cleanHex = function(hex) { + return hex.replace(/[^A-F0-9]/ig, ''); + }; + + var expandHex = function(hex) { + hex = cleanHex(hex); + if( !hex ) return null; + if( hex.length === 3 ) hex = hex[0] + hex[0] + hex[1] + hex[1] + hex[2] + hex[2]; + return hex.length === 6 ? hex : null; + }; + + var hsb2rgb = function(hsb) { + var rgb = {}; + var h = Math.round(hsb.h); + var s = Math.round(hsb.s*255/100); + var v = Math.round(hsb.b*255/100); + if(s === 0) { + rgb.r = rgb.g = rgb.b = v; + } else { + var t1 = v; + var t2 = (255 - s) * v / 255; + var t3 = (t1 - t2) * (h % 60) / 60; + if( h === 360 ) h = 0; + if( h < 60 ) { rgb.r = t1; rgb.b = t2; rgb.g = t2 + t3; } + else if( h < 120 ) {rgb.g = t1; rgb.b = t2; rgb.r = t1 - t3; } + else if( h < 180 ) {rgb.g = t1; rgb.r = t2; rgb.b = t2 + t3; } + else if( h < 240 ) {rgb.b = t1; rgb.r = t2; rgb.g = t1 - t3; } + else if( h < 300 ) {rgb.b = t1; rgb.g = t2; rgb.r = t2 + t3; } + else if( h < 360 ) {rgb.r = t1; rgb.g = t2; rgb.b = t1 - t3; } + else { rgb.r = 0; rgb.g = 0; rgb.b = 0; } + } + return { + r: Math.round(rgb.r), + g: Math.round(rgb.g), + b: Math.round(rgb.b) + }; + }; + + var rgb2hex = function(rgb) { + var hex = [ + rgb.r.toString(16), + rgb.g.toString(16), + rgb.b.toString(16) + ]; + $.each(hex, function(nr, val) { + if (val.length === 1) hex[nr] = '0' + val; + }); + return hex.join(''); + }; + + var hex2rgb = function(hex) { + hex = parseInt(((hex.indexOf('#') > -1) ? hex.substring(1) : hex), 16); + + return { + r: hex >> 16, + g: (hex & 0x00FF00) >> 8, + b: (hex & 0x0000FF) + }; + }; + + var rgb2hsb = function(rgb) { + var hsb = { h: 0, s: 0, b: 0 }; + var min = Math.min(rgb.r, rgb.g, rgb.b); + var max = Math.max(rgb.r, rgb.g, rgb.b); + var delta = max - min; + hsb.b = max; + hsb.s = max !== 0 ? 255 * delta / max : 0; + if( hsb.s !== 0 ) { + if( rgb.r === max ) { + hsb.h = (rgb.g - rgb.b) / delta; + } else if( rgb.g === max ) { + hsb.h = 2 + (rgb.b - rgb.r) / delta; + } else { + hsb.h = 4 + (rgb.r - rgb.g) / delta; + } + } else { + hsb.h = -1; + } + hsb.h *= 60; + if( hsb.h < 0 ) { + hsb.h += 360; + } + hsb.s *= 100/255; + hsb.b *= 100/255; + return hsb; + }; + + var hex2hsb = function(hex) { + var hsb = rgb2hsb(hex2rgb(hex)); + // Zero out hue marker for black, white, and grays (saturation === 0) + if( hsb.s === 0 ) hsb.h = 360; + return hsb; + }; + + var hsb2hex = function(hsb) { + return rgb2hex(hsb2rgb(hsb)); + }; + + + // Handle calls to $([selector]).miniColors() + switch(o) { + + case 'readonly': + + $(this).each( function() { + if( !$(this).hasClass('miniColors') ) return; + $(this).prop('readonly', data); + }); + + return $(this); + + case 'disabled': + + $(this).each( function() { + if( !$(this).hasClass('miniColors') ) return; + if( data ) { + disable($(this)); + } else { + enable($(this)); + } + }); + + return $(this); + + case 'value': + + // Getter + if( data === undefined ) { + if( !$(this).hasClass('miniColors') ) return; + var input = $(this), + hex = expandHex(input.val()); + return hex ? '#' + convertCase(hex, input.data('letterCase')) : null; + } + + // Setter + $(this).each( function() { + if( !$(this).hasClass('miniColors') ) return; + $(this).val(data); + setColorFromInput($(this)); + }); + + return $(this); + + case 'destroy': + + $(this).each( function() { + if( !$(this).hasClass('miniColors') ) return; + destroy($(this)); + }); + + return $(this); + + default: + + if( !o ) o = {}; + + $(this).each( function() { + + // Must be called on an input element + if( $(this)[0].tagName.toLowerCase() !== 'input' ) return; + + // If a trigger is present, the control was already created + if( $(this).data('trigger') ) return; + + // Create the control + create($(this), o, data); + + }); + + return $(this); + + } + + } + + }); + +})(jQuery); \ No newline at end of file diff --git a/www/wiki/extensions/Maps/resources/WikitextEditor/js/mapeditor.iefixes.js b/www/wiki/extensions/Maps/resources/WikitextEditor/js/mapeditor.iefixes.js new file mode 100644 index 00000000..a49e42fb --- /dev/null +++ b/www/wiki/extensions/Maps/resources/WikitextEditor/js/mapeditor.iefixes.js @@ -0,0 +1,28 @@ +if (!Array.prototype.indexOf) +{ + Array.prototype.indexOf = function(elt /*, from*/) + { + var len = this.length >>> 0; + + var from = Number(arguments[1]) || 0; + from = (from < 0) + ? Math.ceil(from) + : Math.floor(from); + if (from < 0) + from += len; + + for (; from < len; from++) + { + if (from in this && + this[from] === elt) + return from; + } + return -1; + }; +} + +if(typeof String.prototype.trim !== 'function') { + String.prototype.trim = function() { + return this.replace(/^\s+|\s+$/g, ''); + } +} \ No newline at end of file diff --git a/www/wiki/extensions/Maps/resources/WikitextEditor/js/mapeditor.js b/www/wiki/extensions/Maps/resources/WikitextEditor/js/mapeditor.js new file mode 100644 index 00000000..eb2efd61 --- /dev/null +++ b/www/wiki/extensions/Maps/resources/WikitextEditor/js/mapeditor.js @@ -0,0 +1,919 @@ +var mapEditor = { + __map:null, + __drawingManager:null, + __mapObjects:[], + __controlsDiv:null, + __options:{ + canvas:'map-canvas', + onRightClick:function(){} + }, + __mapObjectOptions: { + marker:{ + draggable:true + }, + polyline:{ + strokeWeight:2, + strokeOpacity:1, + strokeColor:'#FF0000', + editable:true + }, + circle:{ + strokeWeight:2, + strokeOpacity:1, + strokeColor:'#FF0000', + fillColor:'#FF0000', + fillOpacity:0.5, + editable:true + }, + rectangle:{ + strokeWeight:2, + strokeOpacity:1, + strokeColor:'#FF0000', + fillColor:'#FF0000', + fillOpacity:0.5, + editable:true + }, + polygon:{ + strokeWeight:2, + strokeOpacity:1, + strokeColor:'#FF0000', + fillColor:'#FF0000', + fillOpacity:0.5, + editable:true + }, + imageoverlay:{ + strokeWeight:1, + strokeOpacity:0.5, + strokeColor:'#000', + fillOpacity:0.0, + editable:true + } + }, + __mapParameters:{ + mappingservice: { + values:mw.config.get( 'egMapsAvailableServices' ) + }, + copycoords: { + values:['on','off'] + }, + cluster: { + values:['on','off'] + }, + searchmarkers:{ + values:['title','all'] + }, + 'static':{ + values:['on','off'] + }, + maxzoom: { + values:[] + }, + minzoom: { + values:[] + }, + zoom:{ + values:[] + }, + centre:{ + values:[] + }, + title:{ + values:[] + }, + label:{ + values:[] + }, + icon:{ + values:[] + }, + type:{ + values:[] + }, + types:{ + values:[] + }, + layers:{ + values:[] + }, + controls:{ + values:[] + }, + zoomstyle:{ + values:['default','small','large'] + }, + typestyle:{ + values:[] + }, + autoinfowindows:{ + values:['on','off'] + }, + kml:{ + values:[] + }, + gkml:{ + values:[] + }, + resizable:{ + values:[] + }, + tilt:{ + values:[] + }, + kmlrezoom:{ + values:['on','off'] + }, + poi:{ + values:['on','off'] + }, + visitedicon:{ + values:[] + } + }, + __addMapObject:function(o){ + var idx = this.__mapObjects.indexOf(o); + if(idx === -1){ + this.__mapObjects.push(o); + o.overlay.setMap(this.__map); + } + }, + __removeMapObject:function(o){ + var idx = this.__mapObjects.indexOf(o); + if(idx !== -1){ + this.__mapObjects[idx].overlay.setMap(null); + this.__mapObjects.splice(idx,1); + } + }, + __createOrUpdateImageOverlay:function(e,imageUrl){ + //remove old image overlay if exists + this.__removeMapObject(e.imageoverlay); + + //set to new type so it doesn't collide with rectangle + e.type = 'imageoverlaybounds'; + + //add map objects + this.__addMapObject(e); + var image = new google.maps.GroundOverlay(imageUrl, e.overlay.getBounds()); + var imageOverlay = { + overlay:image, + type:'imageoverlay' + }; + this.__addMapObject(imageOverlay); + e.imageoverlay = imageOverlay; + imageOverlay.metadata = e.metadata; + + //register right click listener if not already done + if(e.rightClickListener !== true){ + google.maps.event.addListener(e.overlay, 'rightclick', function () { + mapEditor.__options.onRightClick(e); + }); + e.rightClickListener = true; + } + + //register bounds change listener if not already done + if(e.boundsChangedListener !== true){ + google.maps.event.addListener(e.overlay, 'bounds_changed', function () { + //destroy and recreate imageoverlay (due to no e.imageoverlay.setBounds() method) + mapEditor.__createOrUpdateImageOverlay(e, e.imageoverlay.overlay.getUrl()); + }); + e.boundsChangedListener = true; + } + + return imageOverlay; + }, + __initControls:function(){ + //Create root controls element + var controlDiv = this.__controlsDiv = document.createElement('div'); + controlDiv.setAttribute('class','mapeditor-controls'); + controlDiv.index = 1; + + this.__map.controls[google.maps.ControlPosition.BOTTOM_CENTER].push(controlDiv); + }, + __initMap:function(){ + var myOptions = { + center:new google.maps.LatLng(0, 0), + zoom:1, + mapTypeId:google.maps.MapTypeId.ROADMAP + }; + + this.__map = new google.maps.Map(document.getElementById(this.__options.canvas),myOptions); + + //noinspection JSUnresolvedVariable + var drawingManager = this.__drawingManager = new google.maps.drawing.DrawingManager({ + drawingMode:null, + drawingControl:true, + drawingControlOptions:{ + position:google.maps.ControlPosition.TOP_CENTER + }, + markerOptions:this.__mapObjectOptions.marker, + circleOptions:this.__mapObjectOptions.circle, + rectangleOptions:this.__mapObjectOptions.rectangle, + polygonOptions:this.__mapObjectOptions.polygon, + polylineOptions:this.__mapObjectOptions.polyline + }); + drawingManager.setMap(this.__map); + + google.maps.event.addListener(drawingManager, 'overlaycomplete', function (e) { + mapEditor.__addMapObject(e); + mapEditor.__registerRightClickListener(e); + google.maps.event.trigger(e.overlay, 'rightclick'); + }); + }, + __readMapObjectOptionsFromMetadata:function(e){ + var options = $.extend({},this.__mapObjectOptions[e.type]); + for(var key in e.metadata){ + var data = e.metadata[key]; + if(data.value.trim() !== ''){ + options[data.name] = data.value; + } + } + try{ + e.overlay.setOptions(options); + return true; + }catch(e){ + return false; + } + }, + __writeMetaDataToMapObject:function(e,metadata){ + e.metadata = this.__arrayToObject(metadata); + }, + __convertPositionalParametersToMetaData:function(positionalArray){ + var positionalNames = [ + 'title', + 'text', + 'strokeColor', + 'strokeOpacity', + 'strokeWeight', + 'fillColor', + 'fillOpacity', + 'showOnHover' + ]; + + if(positionalArray !== undefined && positionalArray.length > 0){ + if(positionalArray[0].trim().indexOf('link:') === 0){ + positionalNames.splice(0,1); + positionalNames[0] = 'link'; + } + } + + for(var x = 0; x < positionalArray.length; x++){ + positionalArray[x] = { + name:positionalNames[x], + value:positionalArray[x].trim() + }; + } + return this.__arrayToObject(positionalArray); + }, + __arrayToObject:function(arr){ + var o = {}; + for (var i = 0; i < arr.length; ++i){ + o[i] = arr[i]; + } + return o; + }, + __registerRightClickListener:function(e){ + google.maps.event.addListener(e.overlay, 'rightclick', function () { + mapEditor.__options.onRightClick(e); + }); + }, + __generateWikiCode:function( separators ){ + var code = separators.codeStart; + + var markers = ''; + var circles = ''; + var polygons = ''; + var lines = ''; + var rectangles = ''; + var imageoverlays = ''; + + for(var x = 0; x < this.__mapObjects.length; x++){ + var mapObject = this.__mapObjects[x].overlay; + var mapObjectType = this.__mapObjects[x].type; + var mapObjectMeta = this.__mapObjects[x].metadata; + + var metadata = ''; + if(mapObjectMeta !== undefined){ + var delimiterPosition = ''; + for(var key in mapObjectMeta){ + var data = mapObjectMeta[key]; + delimiterPosition += delimiterPosition.length > 0 ? ' ~' : '~'; + if(data.value !== ''){ + if(data.name === 'link' && data.value.indexOf('link:') === -1){ + data.value = 'link:'+data.value; + } + if(!(mapObjectType === 'imageoverlay' && data.name === 'image')){ + metadata += delimiterPosition+data.value; + delimiterPosition = ''; + } + } + } + } + + var serializedData = mapObject+metadata; + if (mapObjectType === 'marker') { + markers += markers === '' ? serializedData : '; '+serializedData; + } else if (mapObjectType === 'circle') { + circles += circles === '' ? serializedData : '; '+serializedData; + } else if (mapObjectType === 'polygon') { + polygons += polygons === '' ? serializedData : '; '+serializedData; + } else if (mapObjectType === 'polyline') { + lines += lines === '' ? serializedData : '; '+serializedData; + } else if (mapObjectType === 'rectangle') { + rectangles += rectangles === '' ? serializedData : '; '+serializedData; + }else if(mapObjectType === 'imageoverlay'){ + imageoverlays += imageoverlays === '' ? serializedData : '; '+serializedData; + } + } + + + code += markers !== '' ? markers : ''; + code += circles !== '' ? separators.separator+'circles='+circles : ''; + code += polygons !== '' ? separators.separator+'polygons='+polygons : ''; + code += lines !== '' ? separators.separator+'lines='+lines : ''; + code += rectangles !== '' ? separators.separator+'rectangles='+rectangles : ''; + code += imageoverlays !== '' ? separators.separator+'imageoverlays='+imageoverlays : ''; + + //add map parameters + for(var param in this.__mapParameters){ + var value = this.__mapParameters[param].value; + if(value === undefined || value === ''){ + continue; + } + code += '\n|'+param+'='+value; + } + + code += separators.codeEnd; + return code; + }, + __importWikiCode:function(rawData){ + var syntaxPattern = /^\{\{#display_map:[\s\S]*\}\}[\s\n]*$/i; + if(rawData.match(syntaxPattern) === null){ //NO MATCH + return false; + }else{ + try{ + var patterns = { + marker: /^\{\{#display_map:\s*(.*)/i, + polyline: /\|\s*lines=(.*)/i, + circle:/\|\s*circles=(.*)/i, + polygon:/\|\s*polygons=(.*)/i, + rectangle:/\|\s*rectangles=(.*)/i, + imageoverlay:/\|\s*imageoverlays=(.*)/i, + parameter:/\|\s*(.*)=(.*)/i + }; + var mapObjects = []; + rawData = rawData.split('\n'); + for(var j = 0; j < rawData.length; j++){ + for (var key in patterns){ + var match = rawData[j].match(patterns[key]); + if(match !== null && match[1].trim().length !== 0){ + var isMapObject = false; + if(key !== 'parameter'){ + var data = match[1].split(';'); + for(var i = 0; i < data.length; i++){ + + var metadata = data[i].split('~'); + metadata.splice(0,1); + if(metadata.length > 0){ + data[i] = data[i].substring(0,data[i].indexOf('~')); + } + metadata = this.__convertPositionalParametersToMetaData(metadata); + + var options = this.__mapObjectOptions[key]; + var mapObject = null; + if (key === 'marker') { + var position = data[i].split(','); + //noinspection JSValidateTypes + options = $.extend({ + position: new google.maps.LatLng(position[0],position[1]) + },options); + mapObject = new google.maps.Marker(options); + } else if (key === 'circle') { + var parts = data[i].split(':'); + var radius = parts[1]; + var position = parts[0].split(','); + //noinspection JSValidateTypes + options = $.extend({ + center: new google.maps.LatLng(position[0],position[1]), + radius: parseFloat(radius) + },options); + mapObject = new google.maps.Circle(options); + } else if (key === 'polygon') { + var paths = data[i].split(':'); + for(var x = 0; x < paths.length; x++){ + var position = paths[x].split(','); + paths[x] = new google.maps.LatLng(position[0],position[1]); + } + paths = new google.maps.MVCArray(paths); + //noinspection JSValidateTypes + options = $.extend({ + paths: paths + },options); + mapObject = new google.maps.Polygon(options); + } else if (key === 'polyline') { + var paths = data[i].split(':'); + for(var x = 0; x < paths.length; x++){ + var position = paths[x].split(','); + paths[x] = new google.maps.LatLng(position[0],position[1]); + } + paths = new google.maps.MVCArray(paths); + //noinspection JSValidateTypes + options = $.extend({ + path: paths + },options); + mapObject = new google.maps.Polyline(options); + } else if (key === 'rectangle') { + var parts = data[i].split(':'); + var ne = parts[0].split(','); + var sw = parts[1].split(','); + sw = new google.maps.LatLng(sw[0],sw[1]); + ne = new google.maps.LatLng(ne[0],ne[1]); + //noinspection JSValidateTypes + options = $.extend({ + bounds: new google.maps.LatLngBounds(sw,ne) + },options); + mapObject = new google.maps.Rectangle(options); + }else if (key === 'imageoverlay'){ + var parts = data[i].split(':'); + var ne = parts[0].split(','); + var sw = parts[1].split(','); + var imageUrl = parts[2]; + sw = new google.maps.LatLng(sw[0],sw[1]); + ne = new google.maps.LatLng(ne[0],ne[1]); + + options = $.extend({ + bounds: new google.maps.LatLngBounds(sw,ne) + },options); + var rectangle = new google.maps.Rectangle(options); + + //add image url as metadata entry + metadata.image = { + name:'image',value:imageUrl + }; + + mapObject = { + type:'imageoverlaybounds', + overlay:rectangle, + metadata:metadata + }; + + this.__createOrUpdateImageOverlay(mapObject,imageUrl); + this.__readMapObjectOptionsFromMetadata(mapObject); + + } + if(mapObject !== null){ + //imageoverlay needs special handling + if(key !== 'imageoverlay' ){ + mapObject = { + type:key, + overlay:mapObject, + metadata:metadata + }; + + this.__registerRightClickListener(mapObject); + this.__addMapObject(mapObject); + this.__readMapObjectOptionsFromMetadata(mapObject); + } + + isMapObject = true; + + } + } + }else if(!isMapObject){ + //handle global map parameters + if(this.__mapParameters[match[1]] === undefined){ + this.__mapParameters[match[1]] = {}; + } + this.__mapParameters[match[1]].value = match[2]; + } + } + } + } + }catch(e){ + console.log('An error occurred when parsing data'); + return false; + } + return true; + } + }, + addControlButton:function (text, onclick){ + // Set CSS for the control border + var controlUI = $('
'); + $(controlUI).click(function(){ + onclick.call(this); + }).appendTo(this.__controlsDiv); + + // Set CSS for the control interior + var controlText = $('') + .text(text).appendTo(controlUI); + }, + setup:function(o){ + //extend options + $.extend(this.__options,o); + + //Override tostring methods for wiki code generation + google.maps.LatLng.prototype.toString = function(){ + return this.lat()+','+this.lng(); + }; + + google.maps.Rectangle.prototype.toString = function(){ + var bounds = this.getBounds(); + var ne = bounds.getNorthEast(); + var sw = bounds.getSouthWest(); + return ne+':'+sw; + }; + + google.maps.Marker.prototype.toString = function(){ + var position = this.getPosition(); + return position.lat()+','+position.lng(); + }; + + google.maps.Circle.prototype.toString = function(){ + var center = this.getCenter(); + var radius = this.getRadius(); + return center.lat()+','+center.lng()+':'+radius; + }; + + google.maps.Polygon.prototype.toString = function(){ + var polygons = ''; + this.getPath().forEach(function(e){ + polygons += ':'+e; + }); + return polygons.substr(1); + }; + + google.maps.Polyline.prototype.toString = function(){ + var lines = ''; + this.getPath().forEach(function(e){ + lines += ':'+e; + }); + return lines.substr(1); + }; + + google.maps.GroundOverlay.prototype.toString = function(){ + var bounds = this.getBounds(); + var sw = bounds.getSouthWest(); + var ne = bounds.getNorthEast(); + return [ne,sw,this.getUrl()].join(':'); + }; + + //initialize rest + this.__initMap(); + this.__initControls(); + } +}; + +$(document).ready(function(){ + + + function openDialog(e){ + if(e.metadata !== undefined){ + for(var key in e.metadata){ + var data = e.metadata[key]; + $(this).find('form input[name="'+data.name+'"]').val(data.value); + } + } + var i18nButtons = {}; + i18nButtons[mw.msg('mapeditor-done-button')] = function () { + var form = $(this).find('form'); + form.find('.miniColors').each(function(){ + if($(this).val() === '#'){ + $(this).val(''); + } + }); + mapEditor.__writeMetaDataToMapObject(e,form.serializeArray()); + $(this).dialog("close"); + if(!mapEditor.__readMapObjectOptionsFromMetadata(e)){ + alert(mw.msg('mapeditor-parser-error')); + } + }; + i18nButtons[mw.msg('mapeditor-remove-button')] = function () { + mapEditor.__removeMapObject(e); + $(this).dialog("close"); + }; + + this.dialog({ + modal:true, + buttons:i18nButtons, + open:function(){ + if(e.metadata !== undefined){ + var isText = true; + for(var key in e.metadata){ + var data = e.metadata[key]; + if(data.name === 'link' && data.value.length > 0){ + isText = false; + break; + } + } + //depending on existing metadata, + //show either form with title/text fields or just link field + if(isText){ + $(this).find('input[value="text"]').trigger('click'); + }else{ + $(this).find('input[value="link"]').trigger('click'); + } + }else{ + //default trigger click on text radio button + $(this).find('input[value="text"]').trigger('click'); + } + + + }, + beforeClose:function(){ + //reset the form + var form = $(this).find('form'); + form[0].reset(); + form.find('.opacity-data-holder').text(mw.msg('mapeditor-none-text')); + form.find('.ui-slider').slider('value',-1); + form.find('.miniColors').miniColors('value','#fff').val(''); + } + }); + } + + function openImageOverlayDialog(e,callback){ + + var form = $('#imageoverlay-form'); + + var i18nButtons = {}; + i18nButtons[mw.msg('mapeditor-done-button')] = function(){ + var imageUrl = $(this).find('input[name="image"]').val(); + mapEditor.__createOrUpdateImageOverlay(e,imageUrl); + + var metadata = form.find('form').serializeArray(); + mapEditor.__writeMetaDataToMapObject(e,metadata); + mapEditor.__writeMetaDataToMapObject(e.imageoverlay,metadata); + + form.dialog("close"); + }; + + i18nButtons[mw.msg('mapeditor-remove-button')] = function () { + mapEditor.__removeMapObject(e.imageoverlay); + e.imageoverlay = undefined; + form.dialog("close"); + }; + + form.dialog({ + modal:true, + buttons:i18nButtons, + open:function(){ + //restore data from previous edits + if(e.metadata !== undefined){ + var isText = true; + for(var key in e.metadata){ + var data = e.metadata[key]; + if(data.name === 'link' && data.value.length > 0){ + isText = false; + } + form.find('form input[name="'+data.name+'"]').val(data.value); + } + //depending on existing metadata, + //show either form with title/text fields or just link field + if(isText){ + form.find('input[value="text"]').trigger('click'); + }else{ + form.find('input[value="link"]').trigger('click'); + } + }else{ + //default trigger click on text radio button + form.find('input[value="text"]').trigger('click'); + } + }, + beforeClose:function(){ + mapEditor.__drawingManager.setMap(mapEditor.__map); //re-enable standard drawing manager + if(e.imageoverlay === undefined){ + mapEditor.__removeMapObject(e); + } + + //reset the form + var formElement = form.find('form'); + formElement[0].reset(); + + if(callback !== undefined && typeof(callback) === 'function'){ + callback(); + } + + } + }); + } + + mapEditor.setup({ + onRightClick:function(e){ + if (e.type === 'marker') { + openDialog.call($('#marker-form'),e); + } else if (e.type === 'circle') { + openDialog.call($('#fillable-form'),e); + } else if (e.type === 'polygon') { + openDialog.call($('#polygon-form'),e); + } else if (e.type === 'polyline') { + openDialog.call($('#strokable-form'),e); + } else if (e.type === 'rectangle') { + openDialog.call($('#fillable-form'),e); + } else if (e.type === 'imageoverlaybounds') { + openImageOverlayDialog(e); + } + } + }); + + //add custom controls + if( $('#map-canvas').attr('context') != 'forminput' ) { //for Special:MapEditor + var editorMarkers = { + 'codeStart' : '{{#display_map: ', + 'separator' : '\n|', + 'codeEnd' : '\n}}\n' + }; + mapEditor.addControlButton(mw.msg('mapeditor-export-button'),function(){ + var code = mapEditor.__generateWikiCode( editorMarkers ); + if(navigator.appName == 'Microsoft Internet Explorer'){ + //if IE replace /n with /r/n so it is displayed properly + code = code.split('\n').join('\r\n'); + } + $('#code-output').text(code); + $('#code-output-container').dialog({ + modal:true, + width:'80%', + open:function(){ + $('#code-output').focus(); + } + }); + }); + + mapEditor.addControlButton(mw.msg('mapeditor-import-button'), function(){ + var i18nButtons = {}; + i18nButtons[mw.msg('mapeditor-import-button2')] = function () { + var data = $('#code-input').val(); + if(mapEditor.__importWikiCode(data)){ + $(this).dialog('close'); + }else{ + alert('Could not parse input! make sure the input has the same structure as exported wiki code'); + } + }; + $('#code-input-container').dialog({ + modal:true, + width:'80%', + buttons: i18nButtons + }); + }); + } else { //for form input + var forminputMarkers = { + 'codeStart' : '', + 'separator' : '', + 'codeEnd' : '' + }; + mapEditor.addControlButton(mw.msg('mapeditor-export-button'),function(){ + var code = mapEditor.__generateWikiCode( forminputMarkers ); + if(navigator.appName == 'Microsoft Internet Explorer'){ + //if IE replace /n with /r/n so it is displayed properly + code = code.split('\n').join('\r\n'); + } + $('#map-polygon').text(code); + }); + } + + mapEditor.addControlButton(mw.msg('mapeditor-mapparam-button'), function(){ + var i18nButtons = {}; + i18nButtons[mw.msg('mapeditor-done-button')] = function(){ + var data = $(this).find('form input').not('select').serializeArray(); + for(var x = 0; x < data.length; x++){ + mapEditor.__mapParameters[data[x].name].value = data[x].value; + } + $(this).dialog('close'); + }; + $('#map-parameter-form').dialog({ + modal:true, + width: 500, + buttons:i18nButtons + }); + }); + + mapEditor.addControlButton(mw.msg('mapeditor-clear-button'), function(){ + while(mapEditor.__mapObjects.length > 0){ + var mapObj = mapEditor.__mapObjects.pop(); + mapObj.overlay.setMap(null); + } + for(var param in mapEditor.__mapParameters){ + mapEditor.__mapParameters[param].value = undefined; + } + }); + + mapEditor.addControlButton(mw.msg('mapeditor-imageoverlay-button'), function(){ + var button = $(this); + if(button.data('clicked') === true){ + return; //already clicked, disregard this click + }else{ + button.data('clicked',true); + } + + mapEditor.__drawingManager.setMap(null); //disable current drawing manager + + var drawingManager = new google.maps.drawing.DrawingManager({ + drawingMode:google.maps.drawing.OverlayType.RECTANGLE, + drawingControl:false, + rectangleOptions:mapEditor.__mapObjectOptions.imageoverlay + }); + drawingManager.setMap(mapEditor.__map); + + google.maps.event.addListener(drawingManager, 'overlaycomplete', function (e) { + mapEditor.__addMapObject(e); //add if it doesn't already exist. + + drawingManager.setMap(null); + + openImageOverlayDialog(e,function(){ + //re-enable button + button.data('clicked',false); + }); + + }); + + }); + + + + //init map parameters + var formselect = $('#map-parameter-form select[name="key"]'); + formselect.on('change',function(){ + var option = $(this); + var key = option.val(); + var value = mapEditor.__mapParameters[key].value; + if(value === undefined){ + value = ''; + } + + var parent = option.parent(); + var input = $(''); + var removeButton = $('[x]'); + var option2 = option.clone(true); + var container = $('
'); + + option2.find('option[value="'+key+'"]').remove(); + option.attr('disabled',true); + removeButton.on('click',function(){ + var removedKey = $(this).prevAll('select').val(); + var activeSelect = $(this).parent().parent().find('select').not('select[disabled="disabled"]'); + var option = $(''); + activeSelect.children().first().after(option); + $(this).parent().remove(); + mapEditor.__mapParameters[key].value = undefined; + return false; + }); + + parent.append(input); + parent.append(removeButton); + parent.parent().append(container); + container.append(option2); + + input.autocomplete({ + source: mapEditor.__mapParameters[key].values, + minLength: 0, + autoFocus: true + }); + input.autocomplete('search',''); + }); + + for(var parameter in mapEditor.__mapParameters){ + var option = $(''); + formselect.append(option); + } + + //hide link input initially + $('input[name="link"]').attr('disabled',true).hide().prev().hide(); + + //init text/link switcher + $('input[name="switch"]').on('click',function(){ + if($(this).val() === 'link'){ + $(this).parent().next().find('input[name="title"],input[name="text"]').attr('disabled',true).hide().prev().hide(); + $(this).parent().next().find('input[name="link"]').attr('disabled',false).show().prev().show(); + }else{ + $(this).parent().next().find('input[name="title"],input[name="text"]').attr('disabled',false).show().prev().show(); + $(this).parent().next().find('input[name="link"]').attr('disabled',true).hide().prev().hide(); + } + }); + + //init enter keypress to press done on dialog. + $('.mapeditor-dialog').on('keypress',function(e){ + if(e.keyCode === 13){ + $(this).dialog('option','buttons').Done.call(this); + } + }); + + //init sliders + $('input[name="strokeOpacity"],input[name="fillOpacity"]').each(function(){ + var input = $(this); + var dataHolder = $(''+mw.msg('mapeditor-none-text')+''); + dataHolder.css({fontSize: 12}); + var slider = $('
').slider({ + min: -1, + slide: function(event, ui){ + if(ui.value === -1){ + input.val(''); + dataHolder.text(mw.msg('mapeditor-none-text')); + }else{ + input.val( ui.value/100 ); + dataHolder.text(ui.value+'%'); + } + } + }); + input.before(dataHolder); + input.after(slider); + }); + + //init color pickers + $('input[name="strokeColor"],input[name="fillColor"]').miniColors().miniColors('value','').val(''); +}); 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/editor/css/jquery.miniColors.css b/www/wiki/extensions/Maps/resources/editor/css/jquery.miniColors.css deleted file mode 100644 index 664c2fd4..00000000 --- a/www/wiki/extensions/Maps/resources/editor/css/jquery.miniColors.css +++ /dev/null @@ -1,70 +0,0 @@ -.miniColors-trigger { - height: 22px; - width: 22px; - /* @embed */ - background: url(../images/trigger.png) center no-repeat; - vertical-align: middle; - margin: 0 .25em; - display: inline-block; - outline: none; -} - -.miniColors-selector { - position: absolute; - width: 175px; - height: 150px; - background: #FFF; - border: solid 1px #BBB; - -moz-box-shadow: 0 0 6px rgba(0, 0, 0, .25); - -webkit-box-shadow: 0 0 6px rgba(0, 0, 0, .25); - box-shadow: 0 0 6px rgba(0, 0, 0, .25); - -moz-border-radius: 5px; - -webkit-border-radius: 5px; - border-radius: 5px; - padding: 5px; - z-index: 999999; -} - -.miniColors-selector.black { - background: #000; - border-color: #000; -} - -.miniColors-colors { - position: absolute; - top: 5px; - left: 5px; - width: 150px; - height: 150px; - /* @embed */ - background: url(../images/gradient.png) center no-repeat; - cursor: crosshair; -} - -.miniColors-hues { - position: absolute; - top: 5px; - left: 160px; - width: 20px; - height: 150px; - /* @embed */ - background: url(../images/rainbow.png) center no-repeat; - cursor: crosshair; -} - -.miniColors-colorPicker { - position: absolute; - width: 11px; - height: 11px; - /* @embed */ - background: url(../images/circle.gif) center no-repeat; -} - -.miniColors-huePicker { - position: absolute; - left: -3px; - width: 26px; - height: 3px; - /* @embed */ - background: url(../images/line.gif) center no-repeat; -} \ No newline at end of file diff --git a/www/wiki/extensions/Maps/resources/editor/css/mapeditor.css b/www/wiki/extensions/Maps/resources/editor/css/mapeditor.css deleted file mode 100644 index 9c3fe7c4..00000000 --- a/www/wiki/extensions/Maps/resources/editor/css/mapeditor.css +++ /dev/null @@ -1,79 +0,0 @@ -#code-output-container, #code-input-container,#code-input { - height: 100% !important; - width: 100%; -} - -#code-output-container { - overflow: visible; -} - -textarea#code-input, textarea#code-output { - resize: none; -} - -#code-output { - -webkit-box-sizing: border-box; - -moz-box-sizing: border-box; - box-sizing: border-box; - border: none; - padding: 10px; -} - -#map-canvas { - width: 100%; - height: 500px; -} - -#map-canvas,#code-input { - display: block; -} - -.mapeditor-dialog-form fieldset label{ - display: block; -} - -.mapeditor-dialog-form fieldset input{ - width: 200px; -} - -.mapeditor-dialog-form fieldset input[type="checkbox"]{ - width: auto; -} - -.mapeditor-dialog-form fieldset input[name="strokeColor"],.mapeditor-dialog-form fieldset input[name="fillColor"]{ - width: 100px; -} - -.mapeditor-dialog-form .ui-slider{ - margin: 5px 2px; - width: 200px; -} - -.link-title-switcher { - margin: 5px; -} - -#map-parameter-form{ - text-align: center; -} - -.mapeditor-controls{ - padding: 5px; -} - -.mapeditor-control-element{ - background-color:white; - border: 1px solid #717B87; - cursor: pointer; - text-align: center; - float: left; - padding: 2px; -} - -.mapeditor-control-text{ - font-family:"Arial","sans-serif"; - font-size: 12px; - padding-left: 4px; - padding-right: 4px; - font-weight: bold; -} \ No newline at end of file diff --git a/www/wiki/extensions/Maps/resources/editor/images/circle.gif b/www/wiki/extensions/Maps/resources/editor/images/circle.gif deleted file mode 100644 index 599f7f13..00000000 Binary files a/www/wiki/extensions/Maps/resources/editor/images/circle.gif and /dev/null differ diff --git a/www/wiki/extensions/Maps/resources/editor/images/gradient.png b/www/wiki/extensions/Maps/resources/editor/images/gradient.png deleted file mode 100644 index 486a9f6d..00000000 Binary files a/www/wiki/extensions/Maps/resources/editor/images/gradient.png and /dev/null differ diff --git a/www/wiki/extensions/Maps/resources/editor/images/line.gif b/www/wiki/extensions/Maps/resources/editor/images/line.gif deleted file mode 100644 index 9eb19837..00000000 Binary files a/www/wiki/extensions/Maps/resources/editor/images/line.gif and /dev/null differ diff --git a/www/wiki/extensions/Maps/resources/editor/images/rainbow.png b/www/wiki/extensions/Maps/resources/editor/images/rainbow.png deleted file mode 100644 index d16fcd86..00000000 Binary files a/www/wiki/extensions/Maps/resources/editor/images/rainbow.png and /dev/null differ diff --git a/www/wiki/extensions/Maps/resources/editor/images/trigger.png b/www/wiki/extensions/Maps/resources/editor/images/trigger.png deleted file mode 100644 index 20ec282b..00000000 Binary files a/www/wiki/extensions/Maps/resources/editor/images/trigger.png and /dev/null differ diff --git a/www/wiki/extensions/Maps/resources/editor/js/README b/www/wiki/extensions/Maps/resources/editor/js/README deleted file mode 100644 index 53598d48..00000000 --- a/www/wiki/extensions/Maps/resources/editor/js/README +++ /dev/null @@ -1,3 +0,0 @@ -== jquery.miniColors.js == -Is dual licensed under the MIT / GPLv2 licenses -https://github.com/claviska/jquery-miniColors \ No newline at end of file diff --git a/www/wiki/extensions/Maps/resources/editor/js/jquery.miniColors.js b/www/wiki/extensions/Maps/resources/editor/js/jquery.miniColors.js deleted file mode 100644 index dfbce542..00000000 --- a/www/wiki/extensions/Maps/resources/editor/js/jquery.miniColors.js +++ /dev/null @@ -1,567 +0,0 @@ -/* - * jQuery miniColors: A small color selector - * - * Copyright 2011 Cory LaViska for A Beautiful Site, LLC. (http://abeautifulsite.net/) - * - * Dual licensed under the MIT or GPL Version 2 licenses - * -*/ -if(jQuery) (function($) { - - $.extend($.fn, { - - miniColors: function(o, data) { - - var create = function(input, o, data) { - // - // Creates a new instance of the miniColors selector - // - - // Determine initial color (defaults to white) - var color = expandHex(input.val()); - if( !color ) color = 'ffffff'; - var hsb = hex2hsb(color); - - // Create trigger - var trigger = $(''); - trigger.insertAfter(input); - - // Set input data and update attributes - input - .addClass('miniColors') - .data('original-maxlength', input.attr('maxlength') || null) - .data('original-autocomplete', input.attr('autocomplete') || null) - .data('letterCase', 'uppercase') - .data('trigger', trigger) - .data('hsb', hsb) - .data('change', o.change ? o.change : null) - .attr('maxlength', 7) - .attr('autocomplete', 'off') - .val('#' + convertCase(color, o.letterCase)); - - // Handle options - if( o.readonly ) input.prop('readonly', true); - if( o.disabled ) disable(input); - - // Show selector when trigger is clicked - trigger.bind('click.miniColors', function(event) { - event.preventDefault(); - if( input.val() === '' ) input.val('#'); - show(input); - - }); - - // Show selector when input receives focus - input.bind('focus.miniColors', function(event) { - if( input.val() === '' ) input.val('#'); - show(input); - }); - - // Hide on blur - input.bind('blur.miniColors', function(event) { - var hex = expandHex(input.val()); - input.val( hex ? '#' + convertCase(hex, input.data('letterCase')) : '' ); - }); - - // Hide when tabbing out of the input - input.bind('keydown.miniColors', function(event) { - if( event.keyCode === 9 ) hide(input); - }); - - // Update when color is typed in - input.bind('keyup.miniColors', function(event) { - setColorFromInput(input); - }); - - // Handle pasting - input.bind('paste.miniColors', function(event) { - // Short pause to wait for paste to complete - setTimeout( function() { - setColorFromInput(input); - }, 5); - }); - - }; - - var destroy = function(input) { - // - // Destroys an active instance of the miniColors selector - // - - hide(); - input = $(input); - - // Restore to original state - input.data('trigger').remove(); - input - .attr('autocomplete', input.data('original-autocomplete')) - .attr('maxlength', input.data('original-maxlength')) - .removeData() - .removeClass('miniColors') - .unbind('.miniColors'); - $(document).unbind('.miniColors'); - }; - - var enable = function(input) { - // - // Enables the input control and the selector - // - input - .prop('disabled', false) - .data('trigger') - .css('opacity', 1); - }; - - var disable = function(input) { - // - // Disables the input control and the selector - // - hide(input); - input - .prop('disabled', true) - .data('trigger') - .css('opacity', 0.5); - }; - - var show = function(input) { - // - // Shows the miniColors selector - // - if( input.prop('disabled') ) return false; - - // Hide all other instances - hide(); - - // Generate the selector - var selector = $('
'); - selector - .append('
') - .append('
') - .css({ - top: input.is(':visible') ? input.offset().top + input.outerHeight() : input.data('trigger').offset().top + input.data('trigger').outerHeight(), - left: input.is(':visible') ? input.offset().left : input.data('trigger').offset().left, - display: 'none' - }) - .addClass( input.attr('class') ); - - // Set background for colors - var hsb = input.data('hsb'); - selector - .find('.miniColors-colors') - .css('backgroundColor', '#' + hsb2hex({ h: hsb.h, s: 100, b: 100 })); - - // Set colorPicker position - var colorPosition = input.data('colorPosition'); - if( !colorPosition ) colorPosition = getColorPositionFromHSB(hsb); - selector.find('.miniColors-colorPicker') - .css('top', colorPosition.y + 'px') - .css('left', colorPosition.x + 'px'); - - // Set huePicker position - var huePosition = input.data('huePosition'); - if( !huePosition ) huePosition = getHuePositionFromHSB(hsb); - selector.find('.miniColors-huePicker').css('top', huePosition.y + 'px'); - - // Set input data - input - .data('selector', selector) - .data('huePicker', selector.find('.miniColors-huePicker')) - .data('colorPicker', selector.find('.miniColors-colorPicker')) - .data('mousebutton', 0); - - $('BODY').append(selector); - selector.fadeIn(100); - - // Prevent text selection in IE - selector.bind('selectstart', function() { return false; }); - - $(document).bind('mousedown.miniColors touchstart.miniColors', function(event) { - - input.data('mousebutton', 1); - - if( $(event.target).parents().andSelf().hasClass('miniColors-colors') ) { - event.preventDefault(); - input.data('moving', 'colors'); - moveColor(input, event); - } - - if( $(event.target).parents().andSelf().hasClass('miniColors-hues') ) { - event.preventDefault(); - input.data('moving', 'hues'); - moveHue(input, event); - } - - if( $(event.target).parents().andSelf().hasClass('miniColors-selector') ) { - event.preventDefault(); - return; - } - - if( $(event.target).parents().andSelf().hasClass('miniColors') ) return; - - hide(input); - }); - - $(document) - .bind('mouseup.miniColors touchend.miniColors', function(event) { - event.preventDefault(); - input.data('mousebutton', 0).removeData('moving'); - }) - .bind('mousemove.miniColors touchmove.miniColors', function(event) { - event.preventDefault(); - if( input.data('mousebutton') === 1 ) { - if( input.data('moving') === 'colors' ) moveColor(input, event); - if( input.data('moving') === 'hues' ) moveHue(input, event); - } - }); - - }; - - var hide = function(input) { - - // - // Hides one or more miniColors selectors - // - - // Hide all other instances if input isn't specified - if( !input ) input = '.miniColors'; - - $(input).each( function() { - var selector = $(this).data('selector'); - $(this).removeData('selector'); - $(selector).fadeOut(100, function() { - $(this).remove(); - }); - }); - - $(document).unbind('.miniColors'); - - }; - - var moveColor = function(input, event) { - - var colorPicker = input.data('colorPicker'); - - colorPicker.hide(); - - var position = { - x: event.pageX, - y: event.pageY - }; - - // Touch support - if( event.originalEvent.changedTouches ) { - position.x = event.originalEvent.changedTouches[0].pageX; - position.y = event.originalEvent.changedTouches[0].pageY; - } - position.x = position.x - input.data('selector').find('.miniColors-colors').offset().left - 5; - position.y = position.y - input.data('selector').find('.miniColors-colors').offset().top - 5; - if( position.x <= -5 ) position.x = -5; - if( position.x >= 144 ) position.x = 144; - if( position.y <= -5 ) position.y = -5; - if( position.y >= 144 ) position.y = 144; - - input.data('colorPosition', position); - colorPicker.css('left', position.x).css('top', position.y).show(); - - // Calculate saturation - var s = Math.round((position.x + 5) * 0.67); - if( s < 0 ) s = 0; - if( s > 100 ) s = 100; - - // Calculate brightness - var b = 100 - Math.round((position.y + 5) * 0.67); - if( b < 0 ) b = 0; - if( b > 100 ) b = 100; - - // Update HSB values - var hsb = input.data('hsb'); - hsb.s = s; - hsb.b = b; - - // Set color - setColor(input, hsb, true); - }; - - var moveHue = function(input, event) { - - var huePicker = input.data('huePicker'); - - huePicker.hide(); - - var position = { - y: event.pageY - }; - - // Touch support - if( event.originalEvent.changedTouches ) { - position.y = event.originalEvent.changedTouches[0].pageY; - } - - position.y = position.y - input.data('selector').find('.miniColors-colors').offset().top - 1; - if( position.y <= -1 ) position.y = -1; - if( position.y >= 149 ) position.y = 149; - input.data('huePosition', position); - huePicker.css('top', position.y).show(); - - // Calculate hue - var h = Math.round((150 - position.y - 1) * 2.4); - if( h < 0 ) h = 0; - if( h > 360 ) h = 360; - - // Update HSB values - var hsb = input.data('hsb'); - hsb.h = h; - - // Set color - setColor(input, hsb, true); - - }; - - var setColor = function(input, hsb, updateInput) { - input.data('hsb', hsb); - var hex = hsb2hex(hsb); - if( updateInput ) input.val( '#' + convertCase(hex, input.data('letterCase')) ); - input.data('trigger').css('backgroundColor', '#' + hex); - if( input.data('selector') ) input.data('selector').find('.miniColors-colors').css('backgroundColor', '#' + hsb2hex({ h: hsb.h, s: 100, b: 100 })); - - // Fire change callback - if( input.data('change') ) { - if( hex === input.data('lastChange') ) return; - input.data('change').call(input.get(0), '#' + hex, hsb2rgb(hsb)); - input.data('lastChange', hex); - } - - }; - - var setColorFromInput = function(input) { - - input.val('#' + cleanHex(input.val())); - var hex = expandHex(input.val()); - if( !hex ) return false; - - // Get HSB equivalent - var hsb = hex2hsb(hex); - - // If color is the same, no change required - var currentHSB = input.data('hsb'); - if( hsb.h === currentHSB.h && hsb.s === currentHSB.s && hsb.b === currentHSB.b ) return true; - - // Set colorPicker position - var colorPosition = getColorPositionFromHSB(hsb); - var colorPicker = $(input.data('colorPicker')); - colorPicker.css('top', colorPosition.y + 'px').css('left', colorPosition.x + 'px'); - input.data('colorPosition', colorPosition); - - // Set huePosition position - var huePosition = getHuePositionFromHSB(hsb); - var huePicker = $(input.data('huePicker')); - huePicker.css('top', huePosition.y + 'px'); - input.data('huePosition', huePosition); - - setColor(input, hsb); - - return true; - - }; - - var convertCase = function(string, letterCase) { - if( letterCase === 'lowercase' ) return string.toLowerCase(); - if( letterCase === 'uppercase' ) return string.toUpperCase(); - return string; - }; - - var getColorPositionFromHSB = function(hsb) { - var x = Math.ceil(hsb.s / 0.67); - if( x < 0 ) x = 0; - if( x > 150 ) x = 150; - var y = 150 - Math.ceil(hsb.b / 0.67); - if( y < 0 ) y = 0; - if( y > 150 ) y = 150; - return { x: x - 5, y: y - 5 }; - }; - - var getHuePositionFromHSB = function(hsb) { - var y = 150 - (hsb.h / 2.4); - if( y < 0 ) h = 0; - if( y > 150 ) h = 150; - return { y: y - 1 }; - }; - - var cleanHex = function(hex) { - return hex.replace(/[^A-F0-9]/ig, ''); - }; - - var expandHex = function(hex) { - hex = cleanHex(hex); - if( !hex ) return null; - if( hex.length === 3 ) hex = hex[0] + hex[0] + hex[1] + hex[1] + hex[2] + hex[2]; - return hex.length === 6 ? hex : null; - }; - - var hsb2rgb = function(hsb) { - var rgb = {}; - var h = Math.round(hsb.h); - var s = Math.round(hsb.s*255/100); - var v = Math.round(hsb.b*255/100); - if(s === 0) { - rgb.r = rgb.g = rgb.b = v; - } else { - var t1 = v; - var t2 = (255 - s) * v / 255; - var t3 = (t1 - t2) * (h % 60) / 60; - if( h === 360 ) h = 0; - if( h < 60 ) { rgb.r = t1; rgb.b = t2; rgb.g = t2 + t3; } - else if( h < 120 ) {rgb.g = t1; rgb.b = t2; rgb.r = t1 - t3; } - else if( h < 180 ) {rgb.g = t1; rgb.r = t2; rgb.b = t2 + t3; } - else if( h < 240 ) {rgb.b = t1; rgb.r = t2; rgb.g = t1 - t3; } - else if( h < 300 ) {rgb.b = t1; rgb.g = t2; rgb.r = t2 + t3; } - else if( h < 360 ) {rgb.r = t1; rgb.g = t2; rgb.b = t1 - t3; } - else { rgb.r = 0; rgb.g = 0; rgb.b = 0; } - } - return { - r: Math.round(rgb.r), - g: Math.round(rgb.g), - b: Math.round(rgb.b) - }; - }; - - var rgb2hex = function(rgb) { - var hex = [ - rgb.r.toString(16), - rgb.g.toString(16), - rgb.b.toString(16) - ]; - $.each(hex, function(nr, val) { - if (val.length === 1) hex[nr] = '0' + val; - }); - return hex.join(''); - }; - - var hex2rgb = function(hex) { - hex = parseInt(((hex.indexOf('#') > -1) ? hex.substring(1) : hex), 16); - - return { - r: hex >> 16, - g: (hex & 0x00FF00) >> 8, - b: (hex & 0x0000FF) - }; - }; - - var rgb2hsb = function(rgb) { - var hsb = { h: 0, s: 0, b: 0 }; - var min = Math.min(rgb.r, rgb.g, rgb.b); - var max = Math.max(rgb.r, rgb.g, rgb.b); - var delta = max - min; - hsb.b = max; - hsb.s = max !== 0 ? 255 * delta / max : 0; - if( hsb.s !== 0 ) { - if( rgb.r === max ) { - hsb.h = (rgb.g - rgb.b) / delta; - } else if( rgb.g === max ) { - hsb.h = 2 + (rgb.b - rgb.r) / delta; - } else { - hsb.h = 4 + (rgb.r - rgb.g) / delta; - } - } else { - hsb.h = -1; - } - hsb.h *= 60; - if( hsb.h < 0 ) { - hsb.h += 360; - } - hsb.s *= 100/255; - hsb.b *= 100/255; - return hsb; - }; - - var hex2hsb = function(hex) { - var hsb = rgb2hsb(hex2rgb(hex)); - // Zero out hue marker for black, white, and grays (saturation === 0) - if( hsb.s === 0 ) hsb.h = 360; - return hsb; - }; - - var hsb2hex = function(hsb) { - return rgb2hex(hsb2rgb(hsb)); - }; - - - // Handle calls to $([selector]).miniColors() - switch(o) { - - case 'readonly': - - $(this).each( function() { - if( !$(this).hasClass('miniColors') ) return; - $(this).prop('readonly', data); - }); - - return $(this); - - case 'disabled': - - $(this).each( function() { - if( !$(this).hasClass('miniColors') ) return; - if( data ) { - disable($(this)); - } else { - enable($(this)); - } - }); - - return $(this); - - case 'value': - - // Getter - if( data === undefined ) { - if( !$(this).hasClass('miniColors') ) return; - var input = $(this), - hex = expandHex(input.val()); - return hex ? '#' + convertCase(hex, input.data('letterCase')) : null; - } - - // Setter - $(this).each( function() { - if( !$(this).hasClass('miniColors') ) return; - $(this).val(data); - setColorFromInput($(this)); - }); - - return $(this); - - case 'destroy': - - $(this).each( function() { - if( !$(this).hasClass('miniColors') ) return; - destroy($(this)); - }); - - return $(this); - - default: - - if( !o ) o = {}; - - $(this).each( function() { - - // Must be called on an input element - if( $(this)[0].tagName.toLowerCase() !== 'input' ) return; - - // If a trigger is present, the control was already created - if( $(this).data('trigger') ) return; - - // Create the control - create($(this), o, data); - - }); - - return $(this); - - } - - } - - }); - -})(jQuery); \ No newline at end of file diff --git a/www/wiki/extensions/Maps/resources/editor/js/mapeditor.iefixes.js b/www/wiki/extensions/Maps/resources/editor/js/mapeditor.iefixes.js deleted file mode 100644 index a49e42fb..00000000 --- a/www/wiki/extensions/Maps/resources/editor/js/mapeditor.iefixes.js +++ /dev/null @@ -1,28 +0,0 @@ -if (!Array.prototype.indexOf) -{ - Array.prototype.indexOf = function(elt /*, from*/) - { - var len = this.length >>> 0; - - var from = Number(arguments[1]) || 0; - from = (from < 0) - ? Math.ceil(from) - : Math.floor(from); - if (from < 0) - from += len; - - for (; from < len; from++) - { - if (from in this && - this[from] === elt) - return from; - } - return -1; - }; -} - -if(typeof String.prototype.trim !== 'function') { - String.prototype.trim = function() { - return this.replace(/^\s+|\s+$/g, ''); - } -} \ No newline at end of file diff --git a/www/wiki/extensions/Maps/resources/editor/js/mapeditor.js b/www/wiki/extensions/Maps/resources/editor/js/mapeditor.js deleted file mode 100644 index 795fcc53..00000000 --- a/www/wiki/extensions/Maps/resources/editor/js/mapeditor.js +++ /dev/null @@ -1,919 +0,0 @@ -var mapEditor = { - __map:null, - __drawingManager:null, - __mapObjects:[], - __controlsDiv:null, - __options:{ - canvas:'map-canvas', - onRightClick:function(){} - }, - __mapObjectOptions: { - marker:{ - draggable:true - }, - polyline:{ - strokeWeight:2, - strokeOpacity:1, - strokeColor:'#FF0000', - editable:true - }, - circle:{ - strokeWeight:2, - strokeOpacity:1, - strokeColor:'#FF0000', - fillColor:'#FF0000', - fillOpacity:0.5, - editable:true - }, - rectangle:{ - strokeWeight:2, - strokeOpacity:1, - strokeColor:'#FF0000', - fillColor:'#FF0000', - fillOpacity:0.5, - editable:true - }, - polygon:{ - strokeWeight:2, - strokeOpacity:1, - strokeColor:'#FF0000', - fillColor:'#FF0000', - fillOpacity:0.5, - editable:true - }, - imageoverlay:{ - strokeWeight:1, - strokeOpacity:0.5, - strokeColor:'#000', - fillOpacity:0.0, - editable:true - } - }, - __mapParameters:{ - mappingservice: { - values:mw.config.get( 'egMapsAvailableServices' ) - }, - copycoords: { - values:['on','off'] - }, - markercluster: { - values:['on','off'] - }, - searchmarkers:{ - values:['title','all'] - }, - 'static':{ - values:['on','off'] - }, - maxzoom: { - values:[] - }, - minzoom: { - values:[] - }, - zoom:{ - values:[] - }, - centre:{ - values:[] - }, - title:{ - values:[] - }, - label:{ - values:[] - }, - icon:{ - values:[] - }, - type:{ - values:[] - }, - types:{ - values:[] - }, - layers:{ - values:[] - }, - controls:{ - values:[] - }, - zoomstyle:{ - values:['default','small','large'] - }, - typestyle:{ - values:[] - }, - autoinfowindows:{ - values:['on','off'] - }, - kml:{ - values:[] - }, - gkml:{ - values:[] - }, - resizable:{ - values:[] - }, - tilt:{ - values:[] - }, - kmlrezoom:{ - values:['on','off'] - }, - poi:{ - values:['on','off'] - }, - visitedicon:{ - values:[] - } - }, - __addMapObject:function(o){ - var idx = this.__mapObjects.indexOf(o); - if(idx === -1){ - this.__mapObjects.push(o); - o.overlay.setMap(this.__map); - } - }, - __removeMapObject:function(o){ - var idx = this.__mapObjects.indexOf(o); - if(idx !== -1){ - this.__mapObjects[idx].overlay.setMap(null); - this.__mapObjects.splice(idx,1); - } - }, - __createOrUpdateImageOverlay:function(e,imageUrl){ - //remove old image overlay if exists - this.__removeMapObject(e.imageoverlay); - - //set to new type so it doesn't collide with rectangle - e.type = 'imageoverlaybounds'; - - //add map objects - this.__addMapObject(e); - var image = new google.maps.GroundOverlay(imageUrl, e.overlay.getBounds()); - var imageOverlay = { - overlay:image, - type:'imageoverlay' - }; - this.__addMapObject(imageOverlay); - e.imageoverlay = imageOverlay; - imageOverlay.metadata = e.metadata; - - //register right click listener if not already done - if(e.rightClickListener !== true){ - google.maps.event.addListener(e.overlay, 'rightclick', function () { - mapEditor.__options.onRightClick(e); - }); - e.rightClickListener = true; - } - - //register bounds change listener if not already done - if(e.boundsChangedListener !== true){ - google.maps.event.addListener(e.overlay, 'bounds_changed', function () { - //destroy and recreate imageoverlay (due to no e.imageoverlay.setBounds() method) - mapEditor.__createOrUpdateImageOverlay(e, e.imageoverlay.overlay.getUrl()); - }); - e.boundsChangedListener = true; - } - - return imageOverlay; - }, - __initControls:function(){ - //Create root controls element - var controlDiv = this.__controlsDiv = document.createElement('div'); - controlDiv.setAttribute('class','mapeditor-controls'); - controlDiv.index = 1; - - this.__map.controls[google.maps.ControlPosition.BOTTOM_CENTER].push(controlDiv); - }, - __initMap:function(){ - var myOptions = { - center:new google.maps.LatLng(0, 0), - zoom:1, - mapTypeId:google.maps.MapTypeId.ROADMAP - }; - - this.__map = new google.maps.Map(document.getElementById(this.__options.canvas),myOptions); - - //noinspection JSUnresolvedVariable - var drawingManager = this.__drawingManager = new google.maps.drawing.DrawingManager({ - drawingMode:null, - drawingControl:true, - drawingControlOptions:{ - position:google.maps.ControlPosition.TOP_CENTER - }, - markerOptions:this.__mapObjectOptions.marker, - circleOptions:this.__mapObjectOptions.circle, - rectangleOptions:this.__mapObjectOptions.rectangle, - polygonOptions:this.__mapObjectOptions.polygon, - polylineOptions:this.__mapObjectOptions.polyline - }); - drawingManager.setMap(this.__map); - - google.maps.event.addListener(drawingManager, 'overlaycomplete', function (e) { - mapEditor.__addMapObject(e); - mapEditor.__registerRightClickListener(e); - google.maps.event.trigger(e.overlay, 'rightclick'); - }); - }, - __readMapObjectOptionsFromMetadata:function(e){ - var options = $.extend({},this.__mapObjectOptions[e.type]); - for(var key in e.metadata){ - var data = e.metadata[key]; - if(data.value.trim() !== ''){ - options[data.name] = data.value; - } - } - try{ - e.overlay.setOptions(options); - return true; - }catch(e){ - return false; - } - }, - __writeMetaDataToMapObject:function(e,metadata){ - e.metadata = this.__arrayToObject(metadata); - }, - __convertPositionalParametersToMetaData:function(positionalArray){ - var positionalNames = [ - 'title', - 'text', - 'strokeColor', - 'strokeOpacity', - 'strokeWeight', - 'fillColor', - 'fillOpacity', - 'showOnHover' - ]; - - if(positionalArray !== undefined && positionalArray.length > 0){ - if(positionalArray[0].trim().indexOf('link:') === 0){ - positionalNames.splice(0,1); - positionalNames[0] = 'link'; - } - } - - for(var x = 0; x < positionalArray.length; x++){ - positionalArray[x] = { - name:positionalNames[x], - value:positionalArray[x].trim() - }; - } - return this.__arrayToObject(positionalArray); - }, - __arrayToObject:function(arr){ - var o = {}; - for (var i = 0; i < arr.length; ++i){ - o[i] = arr[i]; - } - return o; - }, - __registerRightClickListener:function(e){ - google.maps.event.addListener(e.overlay, 'rightclick', function () { - mapEditor.__options.onRightClick(e); - }); - }, - __generateWikiCode:function( separators ){ - var code = separators.codeStart; - - var markers = ''; - var circles = ''; - var polygons = ''; - var lines = ''; - var rectangles = ''; - var imageoverlays = ''; - - for(var x = 0; x < this.__mapObjects.length; x++){ - var mapObject = this.__mapObjects[x].overlay; - var mapObjectType = this.__mapObjects[x].type; - var mapObjectMeta = this.__mapObjects[x].metadata; - - var metadata = ''; - if(mapObjectMeta !== undefined){ - var delimiterPosition = ''; - for(var key in mapObjectMeta){ - var data = mapObjectMeta[key]; - delimiterPosition += delimiterPosition.length > 0 ? ' ~' : '~'; - if(data.value !== ''){ - if(data.name === 'link' && data.value.indexOf('link:') === -1){ - data.value = 'link:'+data.value; - } - if(!(mapObjectType === 'imageoverlay' && data.name === 'image')){ - metadata += delimiterPosition+data.value; - delimiterPosition = ''; - } - } - } - } - - var serializedData = mapObject+metadata; - if (mapObjectType === 'marker') { - markers += markers === '' ? serializedData : '; '+serializedData; - } else if (mapObjectType === 'circle') { - circles += circles === '' ? serializedData : '; '+serializedData; - } else if (mapObjectType === 'polygon') { - polygons += polygons === '' ? serializedData : '; '+serializedData; - } else if (mapObjectType === 'polyline') { - lines += lines === '' ? serializedData : '; '+serializedData; - } else if (mapObjectType === 'rectangle') { - rectangles += rectangles === '' ? serializedData : '; '+serializedData; - }else if(mapObjectType === 'imageoverlay'){ - imageoverlays += imageoverlays === '' ? serializedData : '; '+serializedData; - } - } - - - code += markers !== '' ? markers : ''; - code += circles !== '' ? separators.separator+'circles='+circles : ''; - code += polygons !== '' ? separators.separator+'polygons='+polygons : ''; - code += lines !== '' ? separators.separator+'lines='+lines : ''; - code += rectangles !== '' ? separators.separator+'rectangles='+rectangles : ''; - code += imageoverlays !== '' ? separators.separator+'imageoverlays='+imageoverlays : ''; - - //add map parameters - for(var param in this.__mapParameters){ - var value = this.__mapParameters[param].value; - if(value === undefined || value === ''){ - continue; - } - code += '\n|'+param+'='+value; - } - - code += separators.codeEnd; - return code; - }, - __importWikiCode:function(rawData){ - var syntaxPattern = /^\{\{#display_map:[\s\S]*\}\}[\s\n]*$/i; - if(rawData.match(syntaxPattern) === null){ //NO MATCH - return false; - }else{ - try{ - var patterns = { - marker: /^\{\{#display_map:\s*(.*)/i, - polyline: /\|\s*lines=(.*)/i, - circle:/\|\s*circles=(.*)/i, - polygon:/\|\s*polygons=(.*)/i, - rectangle:/\|\s*rectangles=(.*)/i, - imageoverlay:/\|\s*imageoverlays=(.*)/i, - parameter:/\|\s*(.*)=(.*)/i - }; - var mapObjects = []; - rawData = rawData.split('\n'); - for(var j = 0; j < rawData.length; j++){ - for (var key in patterns){ - var match = rawData[j].match(patterns[key]); - if(match !== null && match[1].trim().length !== 0){ - var isMapObject = false; - if(key !== 'parameter'){ - var data = match[1].split(';'); - for(var i = 0; i < data.length; i++){ - - var metadata = data[i].split('~'); - metadata.splice(0,1); - if(metadata.length > 0){ - data[i] = data[i].substring(0,data[i].indexOf('~')); - } - metadata = this.__convertPositionalParametersToMetaData(metadata); - - var options = this.__mapObjectOptions[key]; - var mapObject = null; - if (key === 'marker') { - var position = data[i].split(','); - //noinspection JSValidateTypes - options = $.extend({ - position: new google.maps.LatLng(position[0],position[1]) - },options); - mapObject = new google.maps.Marker(options); - } else if (key === 'circle') { - var parts = data[i].split(':'); - var radius = parts[1]; - var position = parts[0].split(','); - //noinspection JSValidateTypes - options = $.extend({ - center: new google.maps.LatLng(position[0],position[1]), - radius: parseFloat(radius) - },options); - mapObject = new google.maps.Circle(options); - } else if (key === 'polygon') { - var paths = data[i].split(':'); - for(var x = 0; x < paths.length; x++){ - var position = paths[x].split(','); - paths[x] = new google.maps.LatLng(position[0],position[1]); - } - paths = new google.maps.MVCArray(paths); - //noinspection JSValidateTypes - options = $.extend({ - paths: paths - },options); - mapObject = new google.maps.Polygon(options); - } else if (key === 'polyline') { - var paths = data[i].split(':'); - for(var x = 0; x < paths.length; x++){ - var position = paths[x].split(','); - paths[x] = new google.maps.LatLng(position[0],position[1]); - } - paths = new google.maps.MVCArray(paths); - //noinspection JSValidateTypes - options = $.extend({ - path: paths - },options); - mapObject = new google.maps.Polyline(options); - } else if (key === 'rectangle') { - var parts = data[i].split(':'); - var ne = parts[0].split(','); - var sw = parts[1].split(','); - sw = new google.maps.LatLng(sw[0],sw[1]); - ne = new google.maps.LatLng(ne[0],ne[1]); - //noinspection JSValidateTypes - options = $.extend({ - bounds: new google.maps.LatLngBounds(sw,ne) - },options); - mapObject = new google.maps.Rectangle(options); - }else if (key === 'imageoverlay'){ - var parts = data[i].split(':'); - var ne = parts[0].split(','); - var sw = parts[1].split(','); - var imageUrl = parts[2]; - sw = new google.maps.LatLng(sw[0],sw[1]); - ne = new google.maps.LatLng(ne[0],ne[1]); - - options = $.extend({ - bounds: new google.maps.LatLngBounds(sw,ne) - },options); - var rectangle = new google.maps.Rectangle(options); - - //add image url as metadata entry - metadata.image = { - name:'image',value:imageUrl - }; - - mapObject = { - type:'imageoverlaybounds', - overlay:rectangle, - metadata:metadata - }; - - this.__createOrUpdateImageOverlay(mapObject,imageUrl); - this.__readMapObjectOptionsFromMetadata(mapObject); - - } - if(mapObject !== null){ - //imageoverlay needs special handling - if(key !== 'imageoverlay' ){ - mapObject = { - type:key, - overlay:mapObject, - metadata:metadata - }; - - this.__registerRightClickListener(mapObject); - this.__addMapObject(mapObject); - this.__readMapObjectOptionsFromMetadata(mapObject); - } - - isMapObject = true; - - } - } - }else if(!isMapObject){ - //handle global map parameters - if(this.__mapParameters[match[1]] === undefined){ - this.__mapParameters[match[1]] = {}; - } - this.__mapParameters[match[1]].value = match[2]; - } - } - } - } - }catch(e){ - console.log('An error occurred when parsing data'); - return false; - } - return true; - } - }, - addControlButton:function (text, onclick){ - // Set CSS for the control border - var controlUI = $('
'); - $(controlUI).click(function(){ - onclick.call(this); - }).appendTo(this.__controlsDiv); - - // Set CSS for the control interior - var controlText = $('') - .text(text).appendTo(controlUI); - }, - setup:function(o){ - //extend options - $.extend(this.__options,o); - - //Override tostring methods for wiki code generation - google.maps.LatLng.prototype.toString = function(){ - return this.lat()+','+this.lng(); - }; - - google.maps.Rectangle.prototype.toString = function(){ - var bounds = this.getBounds(); - var ne = bounds.getNorthEast(); - var sw = bounds.getSouthWest(); - return ne+':'+sw; - }; - - google.maps.Marker.prototype.toString = function(){ - var position = this.getPosition(); - return position.lat()+','+position.lng(); - }; - - google.maps.Circle.prototype.toString = function(){ - var center = this.getCenter(); - var radius = this.getRadius(); - return center.lat()+','+center.lng()+':'+radius; - }; - - google.maps.Polygon.prototype.toString = function(){ - var polygons = ''; - this.getPath().forEach(function(e){ - polygons += ':'+e; - }); - return polygons.substr(1); - }; - - google.maps.Polyline.prototype.toString = function(){ - var lines = ''; - this.getPath().forEach(function(e){ - lines += ':'+e; - }); - return lines.substr(1); - }; - - google.maps.GroundOverlay.prototype.toString = function(){ - var bounds = this.getBounds(); - var sw = bounds.getSouthWest(); - var ne = bounds.getNorthEast(); - return [ne,sw,this.getUrl()].join(':'); - }; - - //initialize rest - this.__initMap(); - this.__initControls(); - } -}; - -$(document).ready(function(){ - - - function openDialog(e){ - if(e.metadata !== undefined){ - for(var key in e.metadata){ - var data = e.metadata[key]; - $(this).find('form input[name="'+data.name+'"]').val(data.value); - } - } - var i18nButtons = {}; - i18nButtons[mw.msg('mapeditor-done-button')] = function () { - var form = $(this).find('form'); - form.find('.miniColors').each(function(){ - if($(this).val() === '#'){ - $(this).val(''); - } - }); - mapEditor.__writeMetaDataToMapObject(e,form.serializeArray()); - $(this).dialog("close"); - if(!mapEditor.__readMapObjectOptionsFromMetadata(e)){ - alert(mw.msg('mapeditor-parser-error')); - } - }; - i18nButtons[mw.msg('mapeditor-remove-button')] = function () { - mapEditor.__removeMapObject(e); - $(this).dialog("close"); - }; - - this.dialog({ - modal:true, - buttons:i18nButtons, - open:function(){ - if(e.metadata !== undefined){ - var isText = true; - for(var key in e.metadata){ - var data = e.metadata[key]; - if(data.name === 'link' && data.value.length > 0){ - isText = false; - break; - } - } - //depending on existing metadata, - //show either form with title/text fields or just link field - if(isText){ - $(this).find('input[value="text"]').trigger('click'); - }else{ - $(this).find('input[value="link"]').trigger('click'); - } - }else{ - //default trigger click on text radio button - $(this).find('input[value="text"]').trigger('click'); - } - - - }, - beforeClose:function(){ - //reset the form - var form = $(this).find('form'); - form[0].reset(); - form.find('.opacity-data-holder').text(mw.msg('mapeditor-none-text')); - form.find('.ui-slider').slider('value',-1); - form.find('.miniColors').miniColors('value','#fff').val(''); - } - }); - } - - function openImageOverlayDialog(e,callback){ - - var form = $('#imageoverlay-form'); - - var i18nButtons = {}; - i18nButtons[mw.msg('mapeditor-done-button')] = function(){ - var imageUrl = $(this).find('input[name="image"]').val(); - mapEditor.__createOrUpdateImageOverlay(e,imageUrl); - - var metadata = form.find('form').serializeArray(); - mapEditor.__writeMetaDataToMapObject(e,metadata); - mapEditor.__writeMetaDataToMapObject(e.imageoverlay,metadata); - - form.dialog("close"); - }; - - i18nButtons[mw.msg('mapeditor-remove-button')] = function () { - mapEditor.__removeMapObject(e.imageoverlay); - e.imageoverlay = undefined; - form.dialog("close"); - }; - - form.dialog({ - modal:true, - buttons:i18nButtons, - open:function(){ - //restore data from previous edits - if(e.metadata !== undefined){ - var isText = true; - for(var key in e.metadata){ - var data = e.metadata[key]; - if(data.name === 'link' && data.value.length > 0){ - isText = false; - } - form.find('form input[name="'+data.name+'"]').val(data.value); - } - //depending on existing metadata, - //show either form with title/text fields or just link field - if(isText){ - form.find('input[value="text"]').trigger('click'); - }else{ - form.find('input[value="link"]').trigger('click'); - } - }else{ - //default trigger click on text radio button - form.find('input[value="text"]').trigger('click'); - } - }, - beforeClose:function(){ - mapEditor.__drawingManager.setMap(mapEditor.__map); //re-enable standard drawing manager - if(e.imageoverlay === undefined){ - mapEditor.__removeMapObject(e); - } - - //reset the form - var formElement = form.find('form'); - formElement[0].reset(); - - if(callback !== undefined && typeof(callback) === 'function'){ - callback(); - } - - } - }); - } - - mapEditor.setup({ - onRightClick:function(e){ - if (e.type === 'marker') { - openDialog.call($('#marker-form'),e); - } else if (e.type === 'circle') { - openDialog.call($('#fillable-form'),e); - } else if (e.type === 'polygon') { - openDialog.call($('#polygon-form'),e); - } else if (e.type === 'polyline') { - openDialog.call($('#strokable-form'),e); - } else if (e.type === 'rectangle') { - openDialog.call($('#fillable-form'),e); - } else if (e.type === 'imageoverlaybounds') { - openImageOverlayDialog(e); - } - } - }); - - //add custom controls - if( $('#map-canvas').attr('context') != 'forminput' ) { //for Special:MapEditor - var editorMarkers = { - 'codeStart' : '{{#display_map: ', - 'separator' : '\n|', - 'codeEnd' : '\n}}\n' - }; - mapEditor.addControlButton(mw.msg('mapeditor-export-button'),function(){ - var code = mapEditor.__generateWikiCode( editorMarkers ); - if(navigator.appName == 'Microsoft Internet Explorer'){ - //if IE replace /n with /r/n so it is displayed properly - code = code.split('\n').join('\r\n'); - } - $('#code-output').text(code); - $('#code-output-container').dialog({ - modal:true, - width:'80%', - open:function(){ - $('#code-output').focus(); - } - }); - }); - - mapEditor.addControlButton(mw.msg('mapeditor-import-button'), function(){ - var i18nButtons = {}; - i18nButtons[mw.msg('mapeditor-import-button2')] = function () { - var data = $('#code-input').val(); - if(mapEditor.__importWikiCode(data)){ - $(this).dialog('close'); - }else{ - alert('Could not parse input! make sure the input has the same structure as exported wiki code'); - } - }; - $('#code-input-container').dialog({ - modal:true, - width:'80%', - buttons: i18nButtons - }); - }); - } else { //for form input - var forminputMarkers = { - 'codeStart' : '', - 'separator' : '', - 'codeEnd' : '' - }; - mapEditor.addControlButton(mw.msg('mapeditor-export-button'),function(){ - var code = mapEditor.__generateWikiCode( forminputMarkers ); - if(navigator.appName == 'Microsoft Internet Explorer'){ - //if IE replace /n with /r/n so it is displayed properly - code = code.split('\n').join('\r\n'); - } - $('#map-polygon').text(code); - }); - } - - mapEditor.addControlButton(mw.msg('mapeditor-mapparam-button'), function(){ - var i18nButtons = {}; - i18nButtons[mw.msg('mapeditor-done-button')] = function(){ - var data = $(this).find('form input').not('select').serializeArray(); - for(var x = 0; x < data.length; x++){ - mapEditor.__mapParameters[data[x].name].value = data[x].value; - } - $(this).dialog('close'); - }; - $('#map-parameter-form').dialog({ - modal:true, - width: 500, - buttons:i18nButtons - }); - }); - - mapEditor.addControlButton(mw.msg('mapeditor-clear-button'), function(){ - while(mapEditor.__mapObjects.length > 0){ - var mapObj = mapEditor.__mapObjects.pop(); - mapObj.overlay.setMap(null); - } - for(var param in mapEditor.__mapParameters){ - mapEditor.__mapParameters[param].value = undefined; - } - }); - - mapEditor.addControlButton(mw.msg('mapeditor-imageoverlay-button'), function(){ - var button = $(this); - if(button.data('clicked') === true){ - return; //already clicked, disregard this click - }else{ - button.data('clicked',true); - } - - mapEditor.__drawingManager.setMap(null); //disable current drawing manager - - var drawingManager = new google.maps.drawing.DrawingManager({ - drawingMode:google.maps.drawing.OverlayType.RECTANGLE, - drawingControl:false, - rectangleOptions:mapEditor.__mapObjectOptions.imageoverlay - }); - drawingManager.setMap(mapEditor.__map); - - google.maps.event.addListener(drawingManager, 'overlaycomplete', function (e) { - mapEditor.__addMapObject(e); //add if it doesn't already exist. - - drawingManager.setMap(null); - - openImageOverlayDialog(e,function(){ - //re-enable button - button.data('clicked',false); - }); - - }); - - }); - - - - //init map parameters - var formselect = $('#map-parameter-form select[name="key"]'); - formselect.on('change',function(){ - var option = $(this); - var key = option.val(); - var value = mapEditor.__mapParameters[key].value; - if(value === undefined){ - value = ''; - } - - var parent = option.parent(); - var input = $(''); - var removeButton = $('[x]'); - var option2 = option.clone(true); - var container = $('
'); - - option2.find('option[value="'+key+'"]').remove(); - option.attr('disabled',true); - removeButton.on('click',function(){ - var removedKey = $(this).prevAll('select').val(); - var activeSelect = $(this).parent().parent().find('select').not('select[disabled="disabled"]'); - var option = $(''); - activeSelect.children().first().after(option); - $(this).parent().remove(); - mapEditor.__mapParameters[key].value = undefined; - return false; - }); - - parent.append(input); - parent.append(removeButton); - parent.parent().append(container); - container.append(option2); - - input.autocomplete({ - source: mapEditor.__mapParameters[key].values, - minLength: 0, - autoFocus: true - }); - input.autocomplete('search',''); - }); - - for(var parameter in mapEditor.__mapParameters){ - var option = $(''); - formselect.append(option); - } - - //hide link input initially - $('input[name="link"]').attr('disabled',true).hide().prev().hide(); - - //init text/link switcher - $('input[name="switch"]').on('click',function(){ - if($(this).val() === 'link'){ - $(this).parent().next().find('input[name="title"],input[name="text"]').attr('disabled',true).hide().prev().hide(); - $(this).parent().next().find('input[name="link"]').attr('disabled',false).show().prev().show(); - }else{ - $(this).parent().next().find('input[name="title"],input[name="text"]').attr('disabled',false).show().prev().show(); - $(this).parent().next().find('input[name="link"]').attr('disabled',true).hide().prev().hide(); - } - }); - - //init enter keypress to press done on dialog. - $('.mapeditor-dialog').on('keypress',function(e){ - if(e.keyCode === 13){ - $(this).dialog('option','buttons').Done.call(this); - } - }); - - //init sliders - $('input[name="strokeOpacity"],input[name="fillOpacity"]').each(function(){ - var input = $(this); - var dataHolder = $(''+mw.msg('mapeditor-none-text')+''); - dataHolder.css({fontSize: 12}); - var slider = $('
').slider({ - min: -1, - slide: function(event, ui){ - if(ui.value === -1){ - input.val(''); - dataHolder.text(mw.msg('mapeditor-none-text')); - }else{ - input.val( ui.value/100 ); - dataHolder.text(ui.value+'%'); - } - } - }); - input.before(dataHolder); - input.after(slider); - }); - - //init color pickers - $('input[name="strokeColor"],input[name="fillColor"]').miniColors().miniColors('value','').val(''); -}); 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: '© OpenStreetMap 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 '' + escapeHTML(properties.title) + '

' + + 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: '' + + '' + childCount + '' + }); + } + }); + } + }; +})(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( + '', + 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 = $('
MapsPHPMediaWikiSemantic
MediaWiki
Release statusMaps
 
PHP
 
MediaWiki
 
Semantic MediaWiki
(optional)
Release status
 
7.4.x7.14.x 7.1 - 7.4+1.31 - 1.33+3.0+Planned Q3 20191.31 - 1.34+3.0 - 3.1Planned Q4 2019
7.3.x7.13.x 7.1 - 7.41.31 - 1.331.31 - 1.34 3.0 - 3.1 Stable release
7.12.x
-
7.10.x
7.1 - 7.41.31 - 1.343.0 - 3.1Obsolete release, no support
7.9.x
-
7.3.x
7.1 - 7.41.31 - 1.333.0 - 3.1Obsolete release, no support
7.2.x 7.1 - 7.3