From 4edb516e88595e0014baada81157a7b3a74b06c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1t=C3=A9=20Szab=C3=B3?= Date: Wed, 9 Aug 2017 14:22:58 +0200 Subject: [PATCH] SUS-1784: Upgrade Maps extension to 4.3.0 (#3) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * SUS-1784: Apply Maps 4.3.0 upgrade commit 34a6ca46b4e7d1b044154538c1642c08a4b251cd Merge: ca32e11c 5178cb66 Author: Jeroen De Dauw Date: Sat Jun 10 06:51:25 2017 +0200 Merge pull request #346 from JeroenDeDauw/rel430 4.3.0 release commit 5178cb661a8530c87485306d38078dad2e5b3282 Author: Jeroen De Dauw Date: Sat Jun 10 06:42:45 2017 +0200 4.3.0 release commit ca32e11cf3c1147f945dd3fe6569881e61573ab5 Merge: 71251854 99db4999 Author: Jeroen De Dauw Date: Sat Jun 10 01:31:08 2017 +0200 Merge pull request #345 from JeroenDeDauw/fileftehcer4 Allow installation together with FileFetcher 4.x commit 99db4999b190283e93fe0696741e1ab52b5cc282 Author: Jeroen De Dauw Date: Sat Jun 10 00:34:54 2017 +0200 Allow installation together with FileFetcher 4.x commit 71251854c8cb2bee3d7349c0b367ff862d9006bf Merge: 96b28f55 444cbadd Author: Karsten Hoffmeyer Date: Fri Jun 9 18:42:33 2017 +0200 Merge pull request #344 from JeroenDeDauw/shortarray Convert to PHP 5.4+ short array syntax (Semantic Maps) commit 444cbadd414d5883677bce559116e6ea7923c181 Author: kghbln Date: Fri Jun 9 13:56:58 2017 +0000 Convert to PHP 5.4+ short array syntax (Semantic Maps) commit 96b28f55a7bce66e254d53802e6bddc7e35fc567 Merge: 929f29f0 61a0e354 Author: Jeroen De Dauw Date: Fri Jun 2 21:15:28 2017 +0200 Merge pull request #341 from JeroenDeDauw/assertsame use assertSame instead of equals commit 929f29f0cac741e86c0a7cdbe3520d59f2c11bc8 Merge: 9feb3869 569f4977 Author: Jeroen De Dauw Date: Fri Jun 2 21:06:46 2017 +0200 Merge pull request #340 from JeroenDeDauw/serviceclutter Removed clutter from mapping services commit 61a0e3543ab40c1e6e95f9c8a68f230142b5b112 Author: Jeroen De Dauw Date: Fri Jun 2 21:06:12 2017 +0200 use assertSame instead of equals commit 569f49774c31db5ebddd391b233226e53800a297 Author: Jeroen De Dauw Date: Fri Jun 2 21:01:06 2017 +0200 Removed clutter from mapping services commit 9feb3869331b143a51bfbe4278ad7a97493b5d0e Merge: 30257ad4 c2828ca4 Author: Jeroen De Dauw Date: Fri Jun 2 20:56:03 2017 +0200 Merge pull request #339 from JeroenDeDauw/element Cleanup in element.php commit 30257ad489734aacd33b4bbdefdadc628ef4f7f0 Merge: faf7a7dc 5bc01aee Author: Jeroen De Dauw Date: Fri Jun 2 20:53:29 2017 +0200 Merge pull request #338 from JeroenDeDauw/rmtestclutter Removed clutter in parserhook tests commit c2828ca478862831fec8868f985b599e2937a620 Author: Jeroen De Dauw Date: Fri Jun 2 20:49:02 2017 +0200 Cleanup in element.php commit 5bc01aeeb42707b15d991e7bbb65ed82935f654d Author: Jeroen De Dauw Date: Fri Jun 2 20:37:59 2017 +0200 Removed clutter in parserhook tests commit faf7a7dcbe1b2822d797e21d11104b34da5ab9c5 Author: Jeroen De Dauw Date: Fri Jun 2 20:31:30 2017 +0200 Fix incorrect type hint commit e24317a97bea2fc37f6290280da2509dbf8e7db2 Merge: 2d412a53 12064e21 Author: Jeroen De Dauw Date: Fri Jun 2 19:19:22 2017 +0200 Merge pull request #337 from JeroenDeDauw/smwversions Update SMW versions to test against commit 12064e217d17182d08fe6f358a7b693f95693398 Author: Jeroen De Dauw Date: Fri Jun 2 19:03:41 2017 +0200 Update SMW versions to test against commit 2d412a53538b03f1496444178a67aac54d2bfe18 Merge: a3055ad2 d712ed0d Author: Jeroen De Dauw Date: Fri Jun 2 18:53:50 2017 +0200 Merge pull request #336 from JeroenDeDauw/bump43 Bump to 4.3 and raise min PHP and MW commit d712ed0dfdba212b251578b34d8aadeb319587a0 Author: Jeroen De Dauw Date: Fri Jun 2 18:31:17 2017 +0200 Bump to 4.3 and raise min PHP and MW commit a3055ad218a7891575b6474c1a68bc782ba660bd Author: translatewiki.net Date: Mon May 29 08:02:44 2017 +0200 Localisation updates from https://translatewiki.net. commit 1c989cc9dc452db4e10e9cb5c5774c3f4ac84fbb Author: Siebrand Mazeland Date: Sun May 21 09:56:03 2017 +0200 Localisation updates from https://translatewiki.net. commit e69d9c07417df74b7749257caa47fe17ef62c921 Author: translatewiki.net Date: Sat May 20 11:02:26 2017 +0200 Localisation updates from https://translatewiki.net. commit d985439e4c72e033c74064781ac7b20aa3ed06e5 Author: Jeroen De Dauw Date: Sat May 20 01:24:04 2017 +0200 Update RELEASE-NOTES.md commit 9f4a15bd5644246924e4e4402c3603a6d6b238ed Author: Jeroen De Dauw Date: Fri May 19 11:34:30 2017 +0200 Update RELEASE-NOTES.md [skip ci] commit 786b68421e2f67f2890e75479f2e772ed0f6f06f Merge: d357ec39 69dab62a Author: Jeroen De Dauw Date: Thu May 18 05:49:25 2017 +0200 Merge pull request #333 from JeroenDeDauw/moarcs CS changes in query handling code commit d357ec39b37c8fe3752558014d6f76facfc20886 Author: Jeroen De Dauw Date: Thu May 18 05:47:53 2017 +0200 Update rel notes commit 69dab62ac9e9853c0f701df437185d2c477cd4ab Author: Jeroen De Dauw Date: Thu May 18 05:46:05 2017 +0200 CS changes in query handling code commit 2017c03e635f76a247f9f3cce77a1082d5f88fb7 Merge: 97d2aa23 d0b7a80d Author: Jeroen De Dauw Date: Thu May 18 05:39:21 2017 +0200 Merge pull request #332 from JeroenDeDauw/parsermawgic Poke the parser in a slightly different way commit 97d2aa2311fc8037f8c8844d39b9e53657eedf5f Author: Jeroen De Dauw Date: Thu May 18 05:25:01 2017 +0200 Update Maps.php commit d0b7a80d438964190639c81ee86efc5126b2c9a1 Author: Jeroen De Dauw Date: Thu May 18 05:23:11 2017 +0200 Poke the parser in a slightly different way [inset "I have no idea what I am doing" meme here] I think this fixes https://github.com/JeroenDeDauw/Maps/issues/248 There are still issues though... This breaks: {{#ask: [[Category:Locations]] |?Has coordinates |?Has location type |?Located in |format=googlemaps |template=Location Popup |showtitle=off }} And if you remove showtitle, it works commit 66bda1378f8e7a8da8ebc29e49e9aceeffbb14db Merge: cf36a53f f0c10eab Author: Jeroen De Dauw Date: Thu May 18 05:22:37 2017 +0200 Merge pull request #331 from JeroenDeDauw/csqh CS changes in QueryHandler commit f0c10eab09b478798bf4a6161c4f72e91484ba9d Author: Jeroen De Dauw Date: Thu May 18 04:46:45 2017 +0200 CS changes in QueryHandler commit cf36a53f50a029e1711b1357f48eea02424a75b5 Author: Jeroen De Dauw Date: Wed May 17 02:10:32 2017 +0200 Update README.md commit 95326a8640ffb0ecb8f186d23a3ee2155bd3a026 Author: Jeroen De Dauw Date: Mon May 15 00:24:26 2017 +0200 Update INSTALL commit b682c791dc4c7462a380c97e0155580b6615e132 Merge: e1e17130 69b53fd9 Author: Jeroen De Dauw Date: Mon May 15 00:21:37 2017 +0200 Merge pull request #329 from JeroenDeDauw/rmui Remove unused import commit e1e171309ec151e3ab178b5a35dd2ef5140aea26 Author: Jeroen De Dauw Date: Mon May 15 00:20:51 2017 +0200 4.2.0 release commit 69b53fd989b2670ee2a13ec6535ffd718aa38d1a Author: Jeroen De Dauw Date: Mon May 15 00:19:27 2017 +0200 Remove unused import commit 30ae9595fa802c4d65c98f9a011a66dbc229b8d6 Author: Jeroen De Dauw Date: Mon May 15 00:15:56 2017 +0200 Update Maps.php commit a4bdfdd1c4efcbb7acbd0fcf41655ed1ac3aede4 Merge: 20088616 fe81acd4 Author: Jeroen De Dauw Date: Sun May 14 01:52:15 2017 +0200 Merge pull request #328 from JeroenDeDauw/dvgeo Allow installation together with DV Geo 2.x commit fe81acd4db75eabeed70535f74c36f00e5ee68ef Author: Jeroen De Dauw Date: Sun May 14 01:42:21 2017 +0200 Allow installation together with DV Geo 2.x commit 200886160d9f7d64e3a20731dfe7f5541e1d00cf Merge: c27b902d 18c677c7 Author: Jeroen De Dauw Date: Fri May 12 20:03:41 2017 +0200 Merge pull request #307 from JeroenDeDauw/travis-php71 Added PHP 7.1 to TravisCI build commit c27b902dd8d2e438c87d545f502de24e1acc4e5e Author: Jeroen De Dauw Date: Thu May 11 19:33:56 2017 +0200 Update RELEASE-NOTES.md commit 21da25f513a2a7b4f64698fe4b7a9587fe0192f6 Merge: 0526af3a 700a552d Author: Jeroen De Dauw Date: Thu May 11 19:33:14 2017 +0200 Merge pull request #326 from JeroenDeDauw/leaflet103 Upgrade Leaflet to 1.0.3 commit 700a552d0294940a0daeaf2fbd8a50f20632d392 Author: Jeroen De Dauw Date: Wed May 10 21:52:14 2017 +0200 Upgrade Leaflet to 1.0.3 commit 0526af3a7974306e5ca99927fc4892db90a7d934 Author: Jeroen De Dauw Date: Wed May 10 20:55:05 2017 +0200 Update README.md [skip ci] commit 6be68493c40576441e36659f919003c86b2f8b34 Author: Jeroen De Dauw Date: Wed May 10 20:54:24 2017 +0200 Bumped to 4.2.x-dev commit 657013122d4dad546c1b4a28d93a36382d71a0cf Author: Jeroen De Dauw Date: Wed May 10 20:53:56 2017 +0200 Update RELEASE-NOTES.md commit 29876d68c509ee3e8a4cc4668c23b6f1b30b7208 Merge: 62067373 bb2c4a04 Author: Jeroen De Dauw Date: Wed May 10 20:30:44 2017 +0200 Merge pull request #323 from JeroenDeDauw/NominatimGeocoder Catch file fecthing errors in NominatimGeocoder commit bb2c4a0412714103d8f3754b9e492c4cd1e5f8cb Author: Jeroen De Dauw Date: Wed May 10 20:22:17 2017 +0200 Catch file fecthing errors in NominatimGeocoder Fixes https://github.com/JeroenDeDauw/Maps/issues/322 commit 62067373dc6a4422429aa0db3e193b58a111e9eb Author: translatewiki.net Date: Thu May 4 07:54:49 2017 +0200 Localisation updates from https://translatewiki.net. commit d99c51c80f5abce5bb248933a476f3c4d693e007 Author: translatewiki.net Date: Tue May 2 08:06:24 2017 +0200 Localisation updates from https://translatewiki.net. commit 26932ef50f697d1f028c6937c53601079e1444d3 Author: translatewiki.net Date: Thu Apr 27 08:22:42 2017 +0200 Localisation updates from https://translatewiki.net. commit c1976a1f628d05e17d9ed14cee38f5f489e5570d Author: translatewiki.net Date: Thu Apr 20 07:39:06 2017 +0200 Localisation updates from https://translatewiki.net. commit e471be661567914552eb71147a802ac87f349df8 Author: translatewiki.net Date: Mon Apr 17 09:39:14 2017 +0200 Localisation updates from https://translatewiki.net. commit 36e790385fa364b4edfc58f378e616ba2624157e Author: Jeroen De Dauw Date: Fri Apr 14 02:07:50 2017 +0200 4.1.0 release commit 7b1709c01277544cd97873fbda0cbebc41336ae9 Author: Jeroen De Dauw Date: Fri Apr 14 02:05:43 2017 +0200 Style tweak in INSTALL docs commit 79380ee0968f23c8158d4009eb5944a544f4a9fc Author: Jeroen De Dauw Date: Fri Apr 14 02:04:50 2017 +0200 Updated install docs commit 67bb17fea399ab3243c86b31c05e4610c7522762 Author: Jeroen De Dauw Date: Fri Apr 14 02:01:33 2017 +0200 Removed no longer needed compat code commit 70f9cf27ea3c1a586f12bf359cae4c0031baab80 Author: Jeroen De Dauw Date: Fri Apr 14 01:59:44 2017 +0200 Fixed AreaDescription::getQueryString Fixes https://github.com/JeroenDeDauw/Maps/issues/314 commit 0c179aae3b0a2edbfba7d50b1b36b954a419c4f7 Author: Jeroen De Dauw Date: Fri Apr 14 01:43:35 2017 +0200 Removed dead code from SMW descriptions commit abf8df10f58d3ec7ef385cd6a9218d7afdb585c2 Author: Jeroen De Dauw Date: Fri Apr 14 01:41:28 2017 +0200 Bumped to 4.1.0-dev commit 998a6908261168f4b9edb92917b859a12e78e67b Author: Jeroen De Dauw Date: Fri Apr 14 01:39:43 2017 +0200 Added AreaDescriptionTest commit 484b1aa6c6e5e321080a5ed13c74738e9ad2ecf6 Author: Jeroen De Dauw Date: Fri Apr 14 01:02:34 2017 +0200 Moved description classes to src/ commit 584df3dd196553bcb1a42100cc651f309c39bd6e Author: Jeroen De Dauw Date: Fri Apr 14 00:55:46 2017 +0200 Moved test file commit a922fe4ec32d2edea07d0392af95fef2551bdcc3 Author: Jeroen De Dauw Date: Fri Apr 14 00:52:25 2017 +0200 Removed dead code in SMAreaValueDescription commit 554ce18533538128e8fa2f25afffeaa29cf5c4d5 Author: Jeroen De Dauw Date: Fri Apr 14 00:52:07 2017 +0200 CS improvements commit 29eae5e38b934ebf9022c5783d95b4fbad646126 Author: Jeroen De Dauw Date: Fri Apr 14 00:43:19 2017 +0200 Minor CS changes commit dc68f612b65d9d7f8aabbefb993f14f5ec3e1d70 Author: Jeroen De Dauw Date: Fri Apr 14 00:07:48 2017 +0200 Update RELEASE-NOTES.md commit de2c4d72d273e0d7b0b31331d4ff7fc18a126f72 Author: Jeroen De Dauw Date: Fri Apr 14 00:05:56 2017 +0200 Update en.json commit 13cff4b505b028956e602359cae03fe85011b16f Author: translatewiki.net Date: Mon Apr 3 07:35:59 2017 +0200 Localisation updates from https://translatewiki.net. commit 3393b25276617f46b6200cfad8b5ae4a1bf53a4c Author: translatewiki.net Date: Thu Mar 30 07:54:41 2017 +0200 Localisation updates from https://translatewiki.net. commit db836a83eacf7aea02a5c0f309829c1a25f2f1c2 Author: Jeroen De Dauw Date: Thu Mar 30 07:22:28 2017 +0200 Use Compose cache on TravisCI commit 6ddec454c2a1cdd27afabb18ec89bf3418908486 Author: Jeroen De Dauw Date: Wed Mar 15 13:45:39 2017 +0100 Updated release notes commit aa3de9af042c146e765d5316316ab1a54cf74996 Merge: 37b72ce2 82db7bc2 Author: Jeroen De Dauw Date: Wed Mar 15 13:43:43 2017 +0100 Merge pull request #310 from JeroenDeDauw/ca Stop using class aliases commit 37b72ce2d331afc1312fa7a1704f206982b9fb09 Merge: 2a751f5a 08daae65 Author: Jeroen De Dauw Date: Wed Mar 15 12:44:18 2017 +0100 Merge pull request #309 from JeroenDeDauw/rmcomment Remove silly comment commit 82db7bc24ad0efa5ba45506536429d30edf54d2f Author: Jeroen De Dauw Date: Wed Mar 15 12:40:42 2017 +0100 Stop using class aliases commit 08daae65c1a3db6bc105612778555e5ea461683b Author: Jeroen De Dauw Date: Wed Mar 15 12:36:20 2017 +0100 Remove silly comment commit 2a751f5ad5d205a1d54b85ed2de79b430573c0dc Author: translatewiki.net Date: Mon Mar 6 09:00:37 2017 +0100 Localisation updates from https://translatewiki.net. commit e75a23c6612768abd3c256668a36dc29d431a1a7 Merge: 5ad5775b 6678b84c Author: Jeroen De Dauw Date: Sun Mar 5 03:01:08 2017 +0100 Merge pull request #308 from JeroenDeDauw/rm-dc-stub Removed dead code commit 6678b84c43c5d0b7192b883f3931b79eb56a70c3 Author: jeroendedauw Date: Sun Mar 5 02:41:21 2017 +0100 Removed dead code Found this to be unused while hunting for a bug in it... Presumably fixes https://github.com/JeroenDeDauw/Maps/issues/303 commit 5ad5775bdd9dd28e3b12c447cc214921d3e78e39 Author: jeroendedauw Date: Sun Mar 5 01:43:08 2017 +0100 4.0.5 release commit aae228f1288202c0bba856e3bd98e0a960597438 Merge: 639afc53 f3d08fb2 Author: Jeroen De Dauw Date: Sun Mar 5 01:04:27 2017 +0100 Merge pull request #306 from JeroenDeDauw/fix-mapdoc-message Fixed parameter type i18n in #mapsdoc commit 18c677c75021fcd837cea623be7016c1f53f02ed Author: jeroendedauw Date: Sun Mar 5 00:53:34 2017 +0100 Added PHP 7.1 to TravisCI build commit f3d08fb2a04a84be854268c08fdec1faecb2c14a Author: jeroendedauw Date: Sun Mar 5 00:51:28 2017 +0100 Fixed parameter type i18n in #mapsdoc Fixes https://github.com/JeroenDeDauw/Validator/issues/18 commit 639afc531117509c9b45f51472b79a8acbd4ef45 Author: translatewiki.net Date: Thu Feb 23 09:09:40 2017 +0100 Localisation updates from https://translatewiki.net. commit 771f1f44d8afda294f28b89101a1ba91ed5b6a6a Author: translatewiki.net Date: Mon Feb 20 08:42:51 2017 +0100 Localisation updates from https://translatewiki.net. commit 476d9e3ddbdb8a7669ef09ea9a708a8679ee9490 Author: translatewiki.net Date: Mon Feb 13 09:03:59 2017 +0100 Localisation updates from https://translatewiki.net. commit 843df913d492f1a12299b86fffe38cc278043728 Author: Niklas Laxström Date: Wed Jan 11 19:35:04 2017 +0100 Localisation updates from https://translatewiki.net. commit cc9eb40be1a39fd35bb7323f61e44166177704ff Author: Jeroen De Dauw Date: Mon Jan 9 10:25:29 2017 +0100 4.0.4 release commit 2c3dcc5c1edc6d53c0bb328359d87151678b9eb3 Author: Jeroen De Dauw Date: Mon Jan 9 10:25:07 2017 +0100 Update RELEASE-NOTES.md commit ac0a90c12ce8ab43b8efc082087c3a3fe29c97d0 Author: Jeroen De Dauw Date: Mon Jan 9 10:24:55 2017 +0100 Update RELEASE-NOTES.md commit 6b79ab1f5b386a921843b27b1a84883d00c47cdb Merge: 3f791533 5ac675b2 Author: Jeroen De Dauw Date: Mon Jan 9 10:22:41 2017 +0100 Merge pull request #301 from somescout/master Maps_GoogleGeocoder - address being double urlencoded commit 5ac675b2d96ebff2cb605d50c14216e8e305ac7d Author: Абрамов Виталий Date: Mon Jan 9 15:30:36 2017 +0700 Maps_GoogleGeocoder - address being double urlencoded commit 3f791533f2618ac3158c741b2c1794fd7d5afbb2 Merge: 294899c5 b4d0d736 Author: Jeroen De Dauw Date: Sun Jan 8 10:17:48 2017 +0100 Merge pull request #300 from JeroenDeDauw/php7-compat-split-line Improved PHP 7 compatibility commit b4d0d73639a14b0977e59b81ad347af123b4db3f Author: Jeroen De Dauw Date: Sun Jan 8 10:14:18 2017 +0100 Improved PHP 7 compatibility As suggested by @aklapper in https://github.com/JeroenDeDauw/Maps/issues/298 commit 294899c58364cfb3621e05bc466009457fc0a94c Author: Niklas Laxström Date: Mon Jan 2 13:22:17 2017 +0100 Localisation updates from https://translatewiki.net. commit 1e3cd5cea89ea4ff4e47f971b464a72b4965e107 Author: Niklas Laxström Date: Thu Dec 29 08:38:26 2016 +0100 Localisation updates from https://translatewiki.net. commit 5b7846fffcfbe80e159e17893103851752b8b4fd Author: Niklas Laxström Date: Thu Dec 22 07:46:58 2016 +0100 Localisation updates from https://translatewiki.net. commit 2ee5490f07e6ce70f5b8b3116b99121b184e36f8 Merge: e044a990 e4f4b9a3 Author: Jeroen De Dauw Date: Tue Dec 6 14:22:56 2016 +0100 Merge pull request #295 from JeroenDeDauw/rel403 4.0.3 release commit e4f4b9a3d04727d7ffd23c5a82a455d99f6d34b7 Author: jeroendedauw Date: Tue Dec 6 14:18:25 2016 +0100 4.0.3 release commit e044a9902e3a63d16990207d90f22b3f0ad07bb1 Author: Siebrand Mazeland Date: Tue Dec 6 13:12:44 2016 +0100 Localisation updates from https://translatewiki.net. commit 7cdad8c39ab447c093bb74bee6dfa95ce5371397 Merge: d644dd78 c67414e5 Author: Jeroen De Dauw Date: Tue Dec 6 03:49:34 2016 +0100 Merge pull request #293 from JeroenDeDauw/fix-path Fixed JS paths for SM files commit c67414e5b32f500977e27abf0c11032c4509e667 Author: jeroendedauw Date: Tue Dec 6 03:44:39 2016 +0100 Fixed JS paths for SM files Broken in the last patch release due to moving of SM init code and use of __DIR__ commit d644dd789fd0bca0dcaa9462c08e1932edfdd9c4 Merge: 8491daa9 2aeaad58 Author: Jeroen De Dauw Date: Sun Dec 4 14:46:13 2016 +0100 Merge pull request #291 from JeroenDeDauw/fix-qp-geoservice Fixed geoservice usage for center parameter in the map printer commit 2aeaad580c95478b201439f2c064f7c602494c13 Author: jeroendedauw Date: Sun Dec 4 13:20:13 2016 +0100 Fixed geoservice usage for center parameter in the map printer Another step towards fully fixing https://github.com/JeroenDeDauw/Maps/issues/285 commit 8491daa9a0e9cc1efe8de644d8dedce495571ad2 Author: jeroendedauw Date: Sun Dec 4 12:30:30 2016 +0100 Reduced field visibility commit d59f0bd7b8c5cca069267bc844142b5ceed4fe06 Merge: 76f9d5cf 5a7928db Author: Jeroen De Dauw Date: Sun Dec 4 12:28:05 2016 +0100 Merge pull request #290 from JeroenDeDauw/zzzz Cleaned up some codes commit 76f9d5cfa987626e56b7acd6c74e36cea4de2761 Merge: 264305ee 87821564 Author: Jeroen De Dauw Date: Sun Dec 4 11:55:57 2016 +0100 Merge pull request #289 from JeroenDeDauw/fix-displaymap-geoservice Mostly fixed the display_map geoservice parameter commit 5a7928db80ede6e105782289216124d3b4db12fa Author: jeroendedauw Date: Sun Dec 4 11:55:46 2016 +0100 Removed a pile of bullshit interfaces commit eddc079f3f5a5b15e0acd8c36bf4f54755111b97 Author: jeroendedauw Date: Sun Dec 4 11:53:28 2016 +0100 Ditched iMappingService interface commit c767186559e08ae73f7943baea35a427eed7d5b6 Author: jeroendedauw Date: Sun Dec 4 11:50:46 2016 +0100 Ditched some staticz commit 8782156426c239b76f2b5df7471fe8587af397d0 Author: jeroendedauw Date: Sun Dec 4 11:41:43 2016 +0100 Mostly fixed the display_map geoservice parameter It is now used for the main parameter. Still needs fixing for the center parameter commit 264305eeafc554e0cb2771f3ba74f34511a0afc5 Author: jeroendedauw Date: Sun Dec 4 10:44:54 2016 +0100 4.0.2 release commit a7b0df3183e3c501446dfe1bbdef3ac178684f6f Merge: d0cc4239 8f2894fa Author: Jeroen De Dauw Date: Sun Dec 4 10:49:06 2016 +0100 Merge pull request #287 from JeroenDeDauw/mv-sm-init Moved Semantic Maps class into dedicated file commit 8f2894fabe1ebfc932af7aecba9686cf998f6b3c Author: jeroendedauw Date: Sun Dec 4 10:41:49 2016 +0100 Moved Semantic Maps class into dedicated file Probably adresses https://github.com/JeroenDeDauw/Maps/issues/286 commit d0cc4239829a2b5a1d7e28504de7c01c07b28045 Author: jeroendedauw Date: Sun Dec 4 10:37:21 2016 +0100 Updated test instructions commit 969b84883d650bc3700e7ac6ef093ff5f4e6697d Author: Siebrand Mazeland Date: Mon Nov 28 10:15:22 2016 +0100 Localisation updates from https://translatewiki.net. commit 7cff3d2a2ad6d14f699af35686e7292d5408e5ac Author: Niklas Laxström Date: Thu Nov 24 08:34:41 2016 +0100 Localisation updates from https://translatewiki.net. commit 2b6d36b1d0673f9d6da88e07a5e7abdf453344c2 Author: Jeroen De Dauw Date: Fri Nov 18 19:43:37 2016 +0100 Update README.md [skip ci] commit a0b036706186bc627d47d3f800265f7ecb1ba333 Author: Jeroen De Dauw Date: Fri Nov 18 19:42:54 2016 +0100 Update README.md [skip ci] commit cba2cbbd23d9e5faf189b51b437d12428032c59e Author: Jeroen De Dauw Date: Fri Nov 18 19:41:51 2016 +0100 Update README.md [skip ci] commit 3f84930a7edfdac0d123d4b04c8f2eb14480d6df Author: Jeroen De Dauw Date: Fri Nov 18 19:41:01 2016 +0100 Update README.md commit 8b837a546ceb0a4950bdeda4c6b1d74a4db1a849 Author: Jeroen De Dauw Date: Fri Nov 18 19:37:52 2016 +0100 Update RELEASE-NOTES.md commit 13328a69adc15c17e1a9a2cedac5c8cae4112ab1 Author: Jeroen De Dauw Date: Fri Nov 18 19:36:20 2016 +0100 Update INSTALL.md commit a5ef49d4f9a3013a9c93211e2f2f24242791d610 Author: Jeroen De Dauw Date: Fri Nov 18 19:34:52 2016 +0100 Update INSTALL.md commit 9f59f5a91c67b2ed05e3f3c729512de4dcde15f0 Merge: a8ffaa87 06a3247b Author: Jeroen De Dauw Date: Fri Nov 18 19:21:38 2016 +0100 Merge pull request #284 from JeroenDeDauw/rel401 4.0.1 release commit 06a3247b954385ea352bccea533fdf0296c351b6 Author: jeroendedauw Date: Fri Nov 18 19:05:18 2016 +0100 4.0.1 release commit a8ffaa877d1d21f26ab117af3d0fe39ed48f6465 Author: Jeroen De Dauw Date: Fri Nov 18 18:56:39 2016 +0100 Update INSTALL.md commit 93f7c5764443a142c4ff542ac016dcb7357b2840 Author: Jeroen De Dauw Date: Fri Nov 18 18:53:47 2016 +0100 Update INSTALL.md commit 80fea252c7147e010dc15b60b2b2f4ee4e3d5c8d Author: Siebrand Mazeland Date: Thu Nov 17 10:06:14 2016 +0100 Localisation updates from https://translatewiki.net. commit 1077d83984580c71342c50c68206899318fada01 Merge: 8344d3e7 3a19158c Author: Jeroen De Dauw Date: Wed Nov 16 16:04:22 2016 +0100 Merge pull request #283 from JeroenDeDauw/geocoderz Fixed serious fail in the geocode parser hook commit 3a19158c987ffb04aea87a84bd28c08c66b9c8e8 Author: jeroendedauw Date: Wed Nov 16 15:49:59 2016 +0100 Fixed serious fail in the geocode parser hook commit 8344d3e75840fd80c27e42edf99233234e5668bc Author: Jeroen De Dauw Date: Wed Nov 16 15:43:34 2016 +0100 Update composer.json commit 1c4c5b4fa77bfff8f409c72f6964c7070de1ef7f Merge: 06e56bf4 badd5062 Author: Jeroen De Dauw Date: Wed Nov 16 15:35:09 2016 +0100 Merge pull request #282 from JeroenDeDauw/rmdc Removed dead code commit badd50621e84585f0fd6c2637507631e47089a05 Author: jeroendedauw Date: Wed Nov 16 15:25:06 2016 +0100 Removed dead code commit 06e56bf49d5fea5f03e543c32801494bfcce7ae3 Author: Jeroen De Dauw Date: Wed Nov 16 08:10:29 2016 +0100 Update RELEASE-NOTES.md [skip ci] commit e4365938b6142760ce8b80e2d155118598d5fead Merge: 805a9012 479a6e7c Author: Jeroen De Dauw Date: Wed Nov 16 08:09:58 2016 +0100 Merge pull request #280 from JeroenDeDauw/rel400 4.0.0 release commit 479a6e7c7ead39353642900d11df776861d800d7 Author: jeroendedauw Date: Wed Nov 16 08:06:10 2016 +0100 4.0.0 release commit 805a9012fee4ee8ee84163b4679e4c8b49c230a3 Author: Jeroen De Dauw Date: Tue Nov 15 04:38:12 2016 +0100 Update Maps_Settings.php commit 8b07a7e31f17a220b56ce7287f18cccb9e6f3436 Author: Jeroen De Dauw Date: Tue Nov 15 04:35:11 2016 +0100 Update Maps_Settings.php commit 86a23baee4720fb4bf890e01d099b94081565427 Merge: 5d166235 c4b1312f Author: Jeroen De Dauw Date: Wed Nov 9 05:36:37 2016 +0100 Merge pull request #278 from JeroenDeDauw/rel400rc1 4.0.0-RC1 release commit 5d16623557a0743c38462eee443316bc810d0471 Author: Jeroen De Dauw Date: Wed Nov 9 05:27:47 2016 +0100 Update README.md [skip ci] commit c4b1312ffb3498930ceb6a99e0782ea5e26dd5b4 Author: jeroendedauw Date: Wed Nov 9 05:22:51 2016 +0100 4.0.0-RC1 release commit 81a4cb3d953c9e181623cf0568d15d68b19d5f92 Merge: b5dc0d83 febd2900 Author: Jeroen De Dauw Date: Mon Nov 7 03:07:20 2016 +0100 Merge pull request #277 from JeroenDeDauw/cat Update release notes and disable tracking category commit febd290013070d61d2b458dc9f21c807181eafb3 Author: jeroendedauw Date: Mon Nov 7 02:51:37 2016 +0100 Update release notes and disable tracking category commit b5dc0d83b737d14e7c31a4715668c168002c26d6 Author: Siebrand Mazeland Date: Fri Nov 4 12:47:42 2016 +0100 Localisation updates from https://translatewiki.net. commit 7db71ef9807cb6ed63e2a241b28e67dc32d7ee20 Author: Niklas Laxström Date: Thu Nov 3 08:02:59 2016 +0100 Localisation updates from https://translatewiki.net. commit f95bc8a3761862b7a253982044ed2dccd5c38314 Author: Niklas Laxström Date: Tue Nov 1 08:29:16 2016 +0100 Localisation updates from https://translatewiki.net. commit 88090ec3fd90b624bb20514f0ca0cd884a81db49 Merge: e68a3882 411ef3f6 Author: Jeroen De Dauw Date: Tue Oct 18 19:57:30 2016 +0200 Merge pull request #273 from JeroenDeDauw/rmfi Remove dead code commit e68a3882fd73c5ac24f78e95aeb86c1a2d1b8816 Author: Niklas Laxström Date: Mon Oct 17 20:51:22 2016 +0200 Localisation updates from https://translatewiki.net. commit 31530e64e6dbc983b3896c3556af45df5c63e5d6 Merge: 70ab19ac d81d354a Author: Jeroen De Dauw Date: Sat Oct 15 04:13:19 2016 +0200 Merge pull request #274 from JeroenDeDauw/i18nfluff Update en.json commit d81d354a1805afc430a4b26c3299c546980fa0c5 Author: Karsten Hoffmeyer Date: Fri Oct 14 19:16:41 2016 +0200 Update en.json * Remove an obsolete message * Add a missing message (leftover from https://github.com/JeroenDeDauw/Maps/issues/178) commit 411ef3f617494cd4306bac6016f088d612886f6e Author: jeroendedauw Date: Fri Oct 14 14:15:11 2016 +0200 Remove dead code commit 70ab19ac93a5e9004edb755172be9f379dc8f661 Merge: 95eb3695 38b5f195 Author: Jeroen De Dauw Date: Fri Oct 14 08:49:59 2016 +0200 Merge pull request #271 from JeroenDeDauw/default Changed default mapping service to Leaflet commit 95eb3695d2837cb43b134e2fb20f05e0abd0defc Merge: b02a0e5e 523825d6 Author: Jeroen De Dauw Date: Fri Oct 14 08:49:53 2016 +0200 Merge pull request #270 from JeroenDeDauw/maps-par-maxclusterradius Added missing maps-par-maxclusterradius message commit b02a0e5e35e673d24469bdaba9a2c81bc095e2bd Author: yaronkoren Date: Fri Oct 14 02:49:18 2016 -0400 Remove SF form input (#266) * Update en.json * Delete FormInputsSetup.php * Delete SM_FormInput.php * Delete jquery.mapforminput.js * Update README.md * Update Maps.php * Update Maps_Settings.php * Update RELEASE-NOTES.md * Update INSTALL.md * Update composer.json commit 7b49281ffb7672ca093686d5f2240900ab6175be Author: Niklas Laxström Date: Thu Oct 13 08:31:01 2016 +0200 Localisation updates from https://translatewiki.net. commit 50157c21f5ee794345b2f7be652455844ca00d2d Merge: 8dcca6d7 545264cb Author: Jeroen De Dauw Date: Thu Oct 13 03:26:46 2016 +0200 Merge pull request #269 from JeroenDeDauw/egGoogleJsApiKey Added compat for old egGoogleJsApiKey setting commit 8dcca6d71ce0a356d33a2c94a4f50bd2621e4878 Author: Jeroen De Dauw Date: Thu Oct 13 03:25:41 2016 +0200 Debugging by having someone else run it on their production server is such fun commit 0699122982903058787aac18a75a83f4d479229e Merge: 0bb42bed 66afc59d Author: Jeroen De Dauw Date: Wed Oct 12 11:38:14 2016 +0200 Merge pull request #272 from JeroenDeDauw/rmub Removed SMW compat upper bound from composer.json commit 66afc59d4c3f19c5b8eaceb9b9579e6b01781957 Author: jeroendedauw Date: Wed Oct 12 08:46:07 2016 +0200 Removed SMW compat upper bound from composer.json Since apparently it prevents installation together with SMW dev-master commit 38b5f195c64bdba655c665d768f60c379a5aa1ce Author: jeroendedauw Date: Wed Oct 12 06:11:41 2016 +0200 Changed default mapping service to Leaflet Fixes https://github.com/JeroenDeDauw/Maps/issues/184 commit 523825d6e56b8b78a7f77b4756a0c43bcbd496c8 Author: jeroendedauw Date: Wed Oct 12 06:10:22 2016 +0200 Added missing maps-par-maxclusterradius message Fixes https://github.com/JeroenDeDauw/Maps/issues/254 commit 545264cb54308910af951cb505ba358b42f6339d Author: jeroendedauw Date: Wed Oct 12 06:04:14 2016 +0200 Added compat for old egGoogleJsApiKey setting commit 0bb42bed1f2405bf6144c78f0d2b536e515c04d4 Merge: 33b48534 4a74e1f7 Author: Jeroen De Dauw Date: Wed Oct 12 06:00:30 2016 +0200 Merge pull request #267 from JeroenDeDauw/rmmvmsg Remove moved messages commit 4a74e1f70b7eb6ad4d846275f06fc8f1312e9ee1 Author: jeroendedauw Date: Wed Oct 12 03:12:22 2016 +0200 Remove moved messages commit 33b485345aa14ac14ded3bc09ff0147ae99e13c7 Merge: 10288ab1 8c2f64bf Author: Jeroen De Dauw Date: Tue Oct 11 04:03:38 2016 +0200 Merge pull request #265 from JeroenDeDauw/issuetemplate Create ISSUE_TEMPLATE.md commit 10288ab1a41d757e76695b07572e75d325d402bd Author: yaronkoren Date: Mon Oct 10 22:01:25 2016 -0400 Reverted my deletions Oops - I didn't mean to modify the master version; I was trying to create a patch. commit 246136e11e4561b2b9923d7fcd45e77886f4f4b8 Author: yaronkoren Date: Mon Oct 10 21:54:42 2016 -0400 Update en.json commit 8c2f64bfa6a38218d1ae232f0c2bad5495a4cb94 Author: Karsten Hoffmeyer Date: Mon Oct 10 17:32:39 2016 +0200 Create ISSUE_TEMPLATE.md Remind people of basic information to be provided commit f2f99d0fe09a6840451c17d2d3d1740c42b5dcd5 Merge: 7a80291a 6dee417d Author: Karsten Hoffmeyer Date: Mon Oct 10 10:56:04 2016 +0200 Merge pull request #261 from JeroenDeDauw/addmessages Add missing messages commit 7a80291af93cdc28c92491d216a0f07045f9d040 Merge: 9692ac00 bb6fc238 Author: Jeroen De Dauw Date: Mon Oct 10 10:33:03 2016 +0200 Merge pull request #264 from JeroenDeDauw/mvinternationalization Moved i18n registration out of wgExtensionFunctions commit bb6fc2380a19ce399b1cce262c20efa5b85027e6 Author: jeroendedauw Date: Mon Oct 10 01:34:57 2016 +0200 Moved i18n registration out of wgExtensionFunctions commit 9692ac00e39fb08f5a045e67ed1deb5085644de9 Author: jeroendedauw Date: Sun Oct 9 02:59:13 2016 +0200 Updated rel notes commit 38546245c5a910bdb06db0d18841ad8b0fede02d Merge: 13befa67 f5068a89 Author: Jeroen De Dauw Date: Sun Oct 9 02:56:26 2016 +0200 Merge pull request #260 from JeroenDeDauw/initialization Reordered initialization code commit f5068a891c4ee42867e6d8db9ad84ceb4cfb5738 Author: jeroendedauw Date: Sun Oct 9 02:10:43 2016 +0200 Reordered initialization code This adds settings to disable Maps and to disable its SMW integration. It also removes everything but global declarations from code immediately executed, so should allow for inclusion of Maps via Composer even without MediaWiki being loaded. commit 6dee417d7feb38abf5273ef77acba0dc0120296f Author: jeroendedauw Date: Sun Oct 9 02:51:09 2016 +0200 Add missing messages Fixes https://github.com/JeroenDeDauw/Maps/issues/178 commit 13befa671009b1da281574a962040eb66332f690 Merge: e72d44c5 68a216dc Author: Jeroen De Dauw Date: Fri Oct 7 19:04:34 2016 +0200 Merge pull request #252 from JeroenDeDauw/mergemessages Merged SM messages into the main messages file commit e72d44c549fb769552941db0d7ea5f781f8099a4 Merge: b2a41f1f c52434af Author: Jeroen De Dauw Date: Fri Oct 7 03:41:19 2016 +0200 Merge pull request #257 from JeroenDeDauw/cs Protected -> private commit c52434af6dae0cf5918e60d9cf4c0f3f24728d01 Author: jeroendedauw Date: Fri Oct 7 03:32:29 2016 +0200 Protected -> private commit b2a41f1f46d69465e07f8ec33a9e75435cc154ea Author: jeroendedauw Date: Fri Oct 7 03:01:54 2016 +0200 Require SMW 2.1 as min commit 74e7da718be1daa5a125852d00cb24281f08039d Merge: 42493490 06fa9925 Author: Jeroen De Dauw Date: Fri Oct 7 02:59:34 2016 +0200 Merge pull request #251 from JeroenDeDauw/uc Declare SMW compat and SM replacement in composer.json commit 42493490b4eacfaddab43c19f67533d34de75947 Merge: 3f0e4eab 42b211a3 Author: Jeroen De Dauw Date: Fri Oct 7 02:59:13 2016 +0200 Merge pull request #253 from JeroenDeDauw/rmalias Stop using deprecated classs aliases commit 3f0e4eabce86caef2e1d119638534a3a58cb0cbf Merge: 3e1dded2 ecfd8cbb Author: Jeroen De Dauw Date: Fri Oct 7 02:59:03 2016 +0200 Merge pull request #255 from JeroenDeDauw/since Removed or updated @since tags in SM code commit 3e1dded205d5e51e59e45e051a66a2287629a84c Merge: deffb41c 26008027 Author: Jeroen De Dauw Date: Fri Oct 7 02:58:53 2016 +0200 Merge pull request #256 from JeroenDeDauw/fqn Do not use SMW class alias commit 26008027d7cf6e211b25e601bf08b3fb9f553075 Author: jeroendedauw Date: Fri Oct 7 02:51:10 2016 +0200 Do not use SMW class alias commit ecfd8cbb7f18a57c4b41d9f2abf5f9608264af36 Author: jeroendedauw Date: Fri Oct 7 02:44:03 2016 +0200 Removed or updated @since tags in SM code commit 42b211a375b8b63bebc959b8485b75eccfe0db25 Author: jeroendedauw Date: Fri Oct 7 02:29:58 2016 +0200 Stop using deprecated classs aliases commit 68a216dccc626fa42b0ac776353fd42cfed7584b Author: jeroendedauw Date: Fri Oct 7 02:26:19 2016 +0200 Merged SM messages into the main messages file commit 06fa9925066465999e15315e386fc18e57693ef4 Author: jeroendedauw Date: Fri Oct 7 02:18:51 2016 +0200 Declare SMW compat and SM replacement in composer.json commit deffb41cb7886274fc1c73c9a1a3100184b67d55 Author: Niklas Laxström Date: Thu Oct 6 08:11:24 2016 +0200 Localisation updates from https://translatewiki.net. commit eab9d5f51eddf8bc171c1f3ca96496bdeca179ec Merge: f04bbc46 eb39744b Author: Jeroen De Dauw Date: Mon Oct 3 15:22:11 2016 +0200 Merge pull request #235 from JeroenDeDauw/merge-sm Merged in Semantic Maps extension commit f04bbc461dc8369843bf5f9163fef2cd02e3962b Author: Niklas Laxström Date: Tue Sep 27 14:26:01 2016 +0200 Localisation updates from https://translatewiki.net. commit eb39744b2692cb820f317e618846bf1fa8ba27e5 Author: jeroendedauw Date: Wed Sep 21 06:34:52 2016 +0200 Merged in SM settings file commit 2dd49e2c9959b61ab335436c7e1fcb89028a0786 Author: jeroendedauw Date: Wed Sep 21 06:06:24 2016 +0200 Updated documentation to reflect SM merge commit 99c6664bc62bf560d7418396145f0bf484c7eb9f Author: jeroendedauw Date: Wed Sep 21 02:55:51 2016 +0200 Install SMW for some of the builds on TravisCI This to run the SemanticMaps tests commit 29b3816ef6603f14f6a72a043555913e19af7ba4 Merge: 9eb44548 9a35aad5 Author: jeroendedauw Date: Wed Sep 21 02:41:04 2016 +0200 Merge branch 'master' into merge-sm commit 9a35aad579d0e2f733bce4a62281390172ff8680 Merge: 684184c4 7470a259 Author: Jeroen De Dauw Date: Mon Sep 19 17:17:35 2016 +0200 Merge pull request #215 from JeroenDeDauw/leaflet-layers Add Leaflet layers commit 9eb445489c2838dae659bac54d545b22f401b063 Author: jeroendedauw Date: Sun Sep 18 22:41:25 2016 +0200 WIP - Merged in Semantic Maps extension commit 8259c65b9ef8544a87642508b1c6c630a8a65f20 Merge: 684184c4 b230b92e Author: jeroendedauw Date: Sun Sep 18 22:16:54 2016 +0200 Add 'SemanticMaps/' from commit 'b230b92e8b03b1df4f7ac4ddb9b07007d21b184b' git-subtree-dir: SemanticMaps git-subtree-mainline: 684184c44ca935c75f11007e87e79f27b242cf5f git-subtree-split: b230b92e8b03b1df4f7ac4ddb9b07007d21b184b commit b230b92e8b03b1df4f7ac4ddb9b07007d21b184b Merge: 563e6538 24ee3cab Author: Jeroen De Dauw Date: Sun Sep 18 22:14:22 2016 +0200 Merge pull request #112 from SemanticMediaWiki/qpregistration Simplify QueryPrinter registration commit 563e6538d17419949be02b26ec2917d509e9d960 Merge: b4c044ca eadc826c Author: Jeroen De Dauw Date: Sun Sep 18 22:12:40 2016 +0200 Merge pull request #117 from SemanticMediaWiki/rel340real Actual 3.4.0 release :) commit eadc826c16746164a035aaeeb060f20ca4e9acee Author: jeroendedauw Date: Sun Sep 18 22:12:02 2016 +0200 Actual 3.4.0 release :) commit b4c044ca5ba065a6573f44e3e5365777a46b4d2f Merge: 436b798f 7a6f4b79 Author: Peter Grassberger Date: Sun Sep 18 10:55:15 2016 +0200 Merge pull request #116 from SemanticMediaWiki/rmdefault Remove property default commit 436b798f81f765af58365a994665dc92d34e89d0 Merge: 297039d8 57a83807 Author: Peter Grassberger Date: Sun Sep 18 10:54:52 2016 +0200 Merge pull request #115 from SemanticMediaWiki/rel340 3.4.0 release commit 57a83807af7fa885039a49c949726caf1d2abdb7 Author: jeroendedauw Date: Sat Sep 17 00:55:02 2016 +0200 3.4.0 release commit 7a6f4b795ee92ca1fb205bbdeffe7d669e32a645 Author: jeroendedauw Date: Sat Sep 17 00:55:31 2016 +0200 Remove property default We never make any assumptions about userspace property names in SMW or extensions commit 297039d8da5e74e1bd549bbc624d354ba3f1072e Author: Jeroen De Dauw Date: Wed Sep 14 21:31:53 2016 +0200 Update RELEASE-NOTES.md commit 24ee3cabb6a1920e6e9377d2f2812b7341cffa9f Author: jeroendedauw Date: Tue Sep 13 20:42:20 2016 +0200 Simplify QueryPrinter registration commit 60d54991864d3749675553841c2576f3d6191bb9 Merge: aae7d7c5 19d0e241 Author: Jeroen De Dauw Date: Wed Sep 14 19:00:57 2016 +0200 Merge pull request #106 from SemanticMediaWiki/PeterTheOne-patch-1 fix error in ajax compoundquery detection commit 684184c44ca935c75f11007e87e79f27b242cf5f Merge: 43a6dbc0 91370b6e Author: Jeroen De Dauw Date: Wed Sep 14 18:59:33 2016 +0200 Merge pull request #234 from JeroenDeDauw/ol-fix-centre fix openlayers centre bug commit 91370b6ee1c12186f1d03df706511ab6e416efa8 Author: PeterTheOne Date: Wed Sep 14 16:07:03 2016 +0200 remove unused code commit 19d0e241331f14fc55a65fe276850915ed6c024a Author: PeterTheOne Date: Wed Sep 14 15:27:19 2016 +0200 improve compoundsquery detection commit aae7d7c5f75f9df6483fde601cd8f2fe00ff232e Merge: 7f1c03cb 88fbf092 Author: Jeroen De Dauw Date: Wed Sep 14 15:05:11 2016 +0200 Merge pull request #113 from SemanticMediaWiki/fixcap Fixed method call capitalization commit c7f2c5db584bbdba2fe8e56e56a2136e83b43ac9 Author: PeterTheOne Date: Wed Sep 14 14:51:01 2016 +0200 fix openlayers centre bug commit 88fbf09294483cf9184fb56945968728a8f17d12 Author: jeroendedauw Date: Tue Sep 13 20:50:45 2016 +0200 Fixed method call capitalization commit 7f1c03cb8bcad41d9017212703dac019cf0de31d Merge: 8f63ceec 10ddf55b Author: Jeroen De Dauw Date: Tue Sep 13 17:30:02 2016 +0200 Merge pull request #110 from SemanticMediaWiki/mvsettings Undo move of settings file commit 8f63ceecdfd52d2ccb43f9fe510dc9a476512861 Merge: 9346476b 8e228e33 Author: Jeroen De Dauw Date: Tue Sep 13 17:04:27 2016 +0200 Merge pull request #111 from SemanticMediaWiki/record Added record support change to release notes commit 10ddf55b9258e1a664768b1d10c52400a7eccb24 Author: jeroendedauw Date: Tue Sep 13 16:33:21 2016 +0200 Undo move of settings file To avoid user confusion. If this is the last release of SM anyway, as per https://github.com/JeroenDeDauw/Maps/issues/227, then lets not have one version where its different. commit 9346476b7b9ffd1c7430a0af2b232575980f4b25 Merge: 8f6bcf80 d25086bb Author: Jeroen De Dauw Date: Tue Sep 13 16:55:28 2016 +0200 Merge pull request #109 from SemanticMediaWiki/mvqi Moved and simplified query printer initialization code commit 8e228e33385b3f610227764c9a7b67f286cbad26 Author: jeroendedauw Date: Tue Sep 13 16:40:16 2016 +0200 Added record support change to release notes commit 43a6dbc085edf8e186d011b1850d42f25185ac10 Merge: 298fc2e6 359f9899 Author: Jeroen De Dauw Date: Tue Sep 13 16:24:40 2016 +0200 Merge pull request #232 from JeroenDeDauw/rmacu Remove not needed $wgAutoloadClasses usage commit 298fc2e67d2c39ea1d6df926360f55da153a50c2 Merge: 75671780 7e2169a4 Author: Jeroen De Dauw Date: Tue Sep 13 16:24:15 2016 +0200 Merge pull request #230 from JeroenDeDauw/suchunit Allow running the unit tests without MediaWiki commit d25086bb120ffef0f44120b817c8af771044fc63 Author: jeroendedauw Date: Tue Sep 13 16:21:24 2016 +0200 Moved and simplified query printer initialization code commit 8f6bcf80793af2ddfb03ff6e8cacc0862cecb661 Merge: d9205a1a af89ab5f Author: Jeroen De Dauw Date: Tue Sep 13 16:20:44 2016 +0200 Merge pull request #108 from SemanticMediaWiki/rmalc Remove remaining $wgAutoloadClasses occurances commit 7470a259d0b79de04ce4c7c8961b7aa93defb6e6 Author: PeterTheOne Date: Tue Sep 13 16:15:18 2016 +0200 rename addLayerDependencies to addHtmlDependencies commit 359f989969c126e24777895e160c1abf551d7be3 Author: jeroendedauw Date: Tue Sep 13 16:10:14 2016 +0200 Remove not needed $wgAutoloadClasses usage commit af89ab5f29e7dcc34ce5f5bf21f00a1a037fcf79 Author: jeroendedauw Date: Tue Sep 13 16:09:25 2016 +0200 Remove remaining $wgAutoloadClasses occurances commit 7e2169a4ac7a1556855d6ea89135a22f626746f3 Author: jeroendedauw Date: Tue Sep 13 15:26:14 2016 +0200 Allow running the unit tests without MediaWiki Fixes https://github.com/JeroenDeDauw/Maps/issues/229 commit 75671780899afc84ee0b375d7dff6886b02f2c57 Merge: 632d9551 4e66d221 Author: Jeroen De Dauw Date: Tue Sep 13 16:02:49 2016 +0200 Merge pull request #231 from JeroenDeDauw/rename Corrected name of geocoding adapter commit f372909ace3d682a423e3283e0c35eab7c1b331c Author: PeterTheOne Date: Tue Sep 13 15:30:17 2016 +0200 change layers settings to array of booleans commit 6b885720ffd10dbfe65ef254e601887257786878 Author: PeterTheOne Date: Tue Sep 13 15:21:27 2016 +0200 cleanup loading of layer dependencies commit 4e66d22109afe324f17fc2d4a5bcd2031bd7faaf Author: jeroendedauw Date: Tue Sep 13 14:40:21 2016 +0200 Corrected name of geocoding adapter Since it most certainly is not a decorator... commit 632d95511ec4db0ca16f6b75fccddce352688f1d Merge: 978b928b 58999587 Author: Jeroen De Dauw Date: Tue Sep 13 14:35:52 2016 +0200 Merge pull request #214 from JeroenDeDauw/mawiki Refactor new geocoder and its test commit 58999587eaa615123eec032f8c2eb1f74d294abc Author: Jeroen De Dauw Date: Tue Sep 13 14:34:36 2016 +0200 Update composer.json commit 58efc265d3afc4a0236e484e70014a46325b24cf Author: PeterTheOne Date: Tue Sep 13 14:28:43 2016 +0200 move addLayerDependencies to MappingService commit 51607cc606514f85436ae7a7ac7a5e64184bbd45 Author: PeterTheOne Date: Mon Sep 12 17:26:44 2016 +0200 load leaflet layer dependencies in renderMap function commit 4fb9d35b39c9f30fd7a510e87f1ffca715297c28 Author: jeroendedauw Date: Thu Aug 18 01:16:43 2016 +0200 Refactor new geocoder and its test This includes adding of support of usage of the new interface and proper dependency injection in the geocoding registration code. commit 978b928b7279006f4a95a7f146127976d4dd930d Merge: dc4df042 34f64017 Author: Peter Grassberger Date: Wed Sep 7 13:37:12 2016 +0200 Merge pull request #226 from JeroenDeDauw/rel381 3.8.1 release commit 34f640173fbac49cf3a058c1e97f3434dd40f6e6 Author: jeroendedauw Date: Wed Sep 7 09:01:04 2016 +0200 3.8.1 release commit dc4df042537a5107232fdaf240848306758a2827 Merge: 7cc5828e bb6aaa04 Author: Jeroen De Dauw Date: Wed Sep 7 08:57:45 2016 +0200 Merge pull request #225 from JeroenDeDauw/prevent-cluster-creation prevent cluster creation when option not set commit bb6aaa04b4fbb72123ca93b18e4a1f1b2ce8d866 Author: PeterTheOne Date: Tue Sep 6 14:35:58 2016 +0200 prevent cluster creation when option not set commit c8c84ee6d9d3688efe1965147965266ad03d878c Author: Peter Grassberger Date: Tue Sep 6 13:57:46 2016 +0200 fix error in ajax compoundquery detection commit d9205a1a16c3b18f3fdb4420d3ef078d8e82f9cc Author: Niklas Laxström Date: Mon Sep 5 07:39:53 2016 +0200 Localisation updates from https://translatewiki.net. commit 7cc5828e09831eb2a829eb5b2a9f3051c6fd74b8 Author: Niklas Laxström Date: Mon Sep 5 07:39:31 2016 +0200 Localisation updates from https://translatewiki.net. commit 116ae5a0c02570c2907b25203ef7bd7e7576f712 Merge: cebfe96a ff15f61d Author: Jeroen De Dauw Date: Sat Sep 3 05:12:23 2016 +0200 Merge pull request #105 from SemanticMediaWiki/simplify-initialization Simplified initialization commit ff15f61d5cf8de785859e1a1b40fb3dafbb19b35 Author: jeroendedauw Date: Fri Sep 2 03:56:34 2016 +0200 Simplified initialization No longer spread out over so many files Now using anon functions, and breaking out of static/global scope commit a219002e956cd418056f07323ffd33ca1bf2b4d7 Author: Jeroen De Dauw Date: Thu Sep 1 05:28:56 2016 +0200 Update README.md [skip ci] commit 50c479a4779ef5e09e521b01056267bca2eca0dc Author: Jeroen De Dauw Date: Wed Aug 31 02:53:53 2016 +0200 Update RELEASE-NOTES.md [skip ci] commit 9e5ec6f19dcb0e69aa05eab6e47b3332057ffc6a Author: Jeroen De Dauw Date: Wed Aug 31 02:42:56 2016 +0200 Update RELEASE-NOTES.md [skip ci] commit 5f628adc770064588c9aa911e5b76b509d5843ea Author: Jeroen De Dauw Date: Wed Aug 31 02:42:36 2016 +0200 Update RELEASE-NOTES.md [skip ci] commit ec6c666ec68977e0d90e43d4ec3e49c150187e9f Author: Jeroen De Dauw Date: Wed Aug 31 02:41:30 2016 +0200 Update RELEASE-NOTES.md [skip ci] commit eab27e4b79fa4924548e7545b7735cf356a5dd1a Author: Jeroen De Dauw Date: Wed Aug 31 02:41:14 2016 +0200 Update RELEASE-NOTES.md [skip ci] commit 9dfa52c54ee03ec28857d0fafe581d4593a8aa76 Author: Jeroen De Dauw Date: Wed Aug 31 02:33:36 2016 +0200 Update RELEASE-NOTES.md commit cebfe96a1dd30081f4a9808cdc808f64990853f0 Merge: fac4b2b4 d49658e2 Author: Jeroen De Dauw Date: Sat Aug 27 02:16:43 2016 +0200 Merge pull request #103 from SemanticMediaWiki/ajax-compoundquery add ajax compoundquery support commit d49658e2421b38a47fab1f6eb7d1f2a27ab87286 Author: Peter Grassberger Date: Fri Aug 26 00:30:54 2016 +0200 rename compoundQuery to isCompoundQuery commit fac4b2b479095e578ce1deb0b598197365c89fc5 Merge: 74accb4c a0e44e36 Author: Jeroen De Dauw Date: Thu Aug 25 22:44:51 2016 +0200 Merge pull request #104 from SemanticMediaWiki/ajax-icon1 ajax set icon #92 commit a0e44e366f62a9252a7181c4f64cb148b7435bbf Author: PeterTheOne Date: Thu Aug 25 18:41:23 2016 +0200 ajax set icon #92 (only works with absolute path) commit 7c9435554a9339f2007f42bd13d972b9ba034ebd Author: PeterTheOne Date: Thu Aug 25 18:04:49 2016 +0200 add ajax compoundquery support fix #74 commit df0997cd5b36428530c156a955cfe29d9017797a Author: PeterTheOne Date: Thu Aug 25 17:30:47 2016 +0200 fix LayerApiKeys settings commit a1c5564efd027f3f35df951469c320b793741561 Author: PeterTheOne Date: Thu Aug 25 17:21:57 2016 +0200 disable mapbox support because of missing api key integration commit 2c4c2cfa35d9981fdc7ae1b89b190bb244c671ba Author: PeterTheOne Date: Thu Aug 25 17:20:51 2016 +0200 add missing i18n commit 74accb4c99af5be3d71daa03fd551eac7fae7cfc Merge: 53e42e87 02a78f7f Author: Jeroen De Dauw Date: Wed Aug 24 18:48:53 2016 +0200 Merge pull request #99 from SemanticMediaWiki/PeterTheOne-patch-3 Update RELEASE-NOTES commit 02a78f7f2c83c5a9266827f1f0f542eaba559622 Merge: 8fe15b7e 85906b4e Author: Jeroen De Dauw Date: Wed Aug 24 18:48:34 2016 +0200 Merge pull request #100 from SemanticMediaWiki/relnotez Update RELEASE-NOTES.md commit 53e42e87707936f248989e1b9a462f53378e9aaf Merge: d7206556 1508474d Author: Jeroen De Dauw Date: Wed Aug 24 18:48:13 2016 +0200 Merge pull request #102 from SemanticMediaWiki/record Fix query with record type coordinates commit d7206556cf510fe1298166dffa602df83e908db9 Author: Jeroen De Dauw Date: Wed Aug 24 16:27:52 2016 +0200 Update composer.json commit a8e86d1e86b6c4d1f312194e60ff0722b99ec966 Merge: 6daf8e8f ed2409fd Author: Jeroen De Dauw Date: Wed Aug 24 16:20:06 2016 +0200 Merge pull request #220 from JeroenDeDauw/rel380 3.8.0 release commit ed2409fd351c2f727da55ee8c6ff441f1fc05d57 Author: jeroendedauw Date: Wed Aug 24 15:59:45 2016 +0200 3.8.0 release commit 6daf8e8f85fc6a8b972bb7ec18dffa0178a1a99c Merge: f56a47ff 48cb6330 Author: Jeroen De Dauw Date: Wed Aug 24 15:56:45 2016 +0200 Merge pull request #219 from JeroenDeDauw/compat Updated compat docs for 3.8 release commit 48cb6330510ede73dda79fc1876425a63ebe60d6 Author: jeroendedauw Date: Wed Aug 24 15:19:20 2016 +0200 Updated compat docs for 3.8 release commit f56a47ff1181fd9e8a8a03282133394b63b5a4e3 Merge: 05407569 e7418639 Author: Jeroen De Dauw Date: Wed Aug 24 15:33:26 2016 +0200 Merge pull request #218 from JeroenDeDauw/config Added required config notes to INSTALL commit e741863998529ee121bfa79288a2c17f5a508180 Author: jeroendedauw Date: Wed Aug 24 15:15:23 2016 +0200 Added required config notes to INSTALL commit 0540756967384d45bda2743336d4cb5c793a1332 Merge: f763ce56 e1d76213 Author: Jeroen De Dauw Date: Wed Aug 24 15:08:17 2016 +0200 Merge pull request #212 from JeroenDeDauw/relnotez Update RELEASE-NOTES.md commit 1508474d25b01ba7da01a9ea00715c782004509f Author: mwjames Date: Wed Aug 24 01:50:46 2016 +0000 Fix query with record type coordinates ### Setup and configuration - SMW version: any SMW version - MW version: 1.27+ - DB (MySQL etc.): MariaDB - SM: master ### Issue http://sandbox.semantic-mediawiki.org/wiki/CoordinatesAndRecordValue commit e97633ba0f828db16aac8c5674ff4351cf69064a Author: Niklas Laxström Date: Mon Aug 22 07:56:36 2016 +0200 Localisation updates from https://translatewiki.net. commit f763ce567dedc119a3a05bb54c18378bf10dbe8e Author: Niklas Laxström Date: Mon Aug 22 07:56:26 2016 +0200 Localisation updates from https://translatewiki.net. commit 70c20c03cd5e18f16532c29b9016573538a0a555 Author: PeterTheOne Date: Fri Aug 19 13:18:01 2016 +0200 fix loading of dependencies commit 853b5797aae1d7041324d2312655a0df35d797c6 Merge: cb259727 5a35f3fc Author: PeterTheOne Date: Fri Aug 19 12:54:41 2016 +0200 Merge branch 'master' into leaflet-layers commit 5a35f3fc7e654b848013a6b4cd4c39bb34598937 Author: Niklas Laxström Date: Thu Aug 18 08:17:43 2016 +0200 Localisation updates from https://translatewiki.net. commit 74f5b7b97ca889a1342d8b04667052d56a484e48 Merge: 94ac26d5 5ed260c0 Author: Jeroen De Dauw Date: Wed Aug 17 20:16:26 2016 +0200 Merge pull request #213 from JeroenDeDauw/mawdiawiki Moved src/Maps/ to src/ commit 5ed260c0e7a481e0c1b2fcb43fd6e43b8d5f99f4 Author: jeroendedauw Date: Wed Aug 17 20:08:44 2016 +0200 Moved src/Maps/ to src/ commit cb2597278900234cbba454729d1dd86aa919a07c Author: PeterTheOne Date: Tue Aug 16 20:43:40 2016 +0200 add leaflet layers commit 6f133d6a016aef701b6a160ea4f34f0feed0bd4e Merge: a4929396 dd94fd8c Author: Jeroen De Dauw Date: Tue Aug 16 17:40:16 2016 +0200 Merge pull request #101 from SemanticMediaWiki/render-openlayers fix openlayers not loading bug commit dd94fd8cffe5b4c4266854e8696502cc558c7bcd Author: PeterTheOne Date: Tue Aug 16 17:30:53 2016 +0200 fix openlayers not loading bug commit 85906b4e422017b1bf2846cbeeac680b5d5dd244 Author: Jeroen De Dauw Date: Tue Aug 16 13:27:39 2016 +0200 Update RELEASE-NOTES.md TODO: * Elaborate `Dynamic loading of markers` section, either including a brief example or linking to one on SMW wiki or sandbox * Go over merged changes since last rel, as this list was definitely not complete yet commit a4929396008d597e2891dd0398726912ceea7ea2 Merge: f6bc5c3c 71ac7664 Author: Jeroen De Dauw Date: Tue Aug 16 13:20:22 2016 +0200 Merge pull request #95 from SemanticMediaWiki/PeterTheOne-patch-1 replace static api url commit e1d7621368aa704b9d959881810c11305d7abba5 Author: Jeroen De Dauw Date: Tue Aug 16 13:09:05 2016 +0200 Update RELEASE-NOTES.md TODO: * `Added Leaflet fullscreen control`: add small inline example (foo=bar) * `Added OSM Nominatim Geocoder`: add small inline example (foo=bar) * Google Maps API stuff should be mentioned in relevant install docs commit 94ac26d5105e24a0d2f42693c90198f6c5486782 Merge: 09a82db9 42da87d6 Author: Jeroen De Dauw Date: Tue Aug 16 13:03:54 2016 +0200 Merge pull request #209 from JeroenDeDauw/PeterTheOne-patch-1 Update RELEASE-NOTES commit 09a82db9b1c69591596556586c9dbe0482d12408 Merge: 32aae940 7f6f5608 Author: Jeroen De Dauw Date: Tue Aug 16 13:01:25 2016 +0200 Merge pull request #207 from JeroenDeDauw/leaflet-getdep leaflet: add getDependencies function commit 32aae940d4649b53eeaeb858697c3aae11c1f7c5 Author: Niklas Laxström Date: Mon Aug 15 08:33:12 2016 +0200 Localisation updates from https://translatewiki.net. commit 75e4b822e2b7ce627d57d4b783d3ef565c1dcb8d Author: Niklas Laxström Date: Thu Aug 11 09:17:53 2016 +0200 Localisation updates from https://translatewiki.net. commit 8fe15b7ea51f77ef0c96d0edcf94a34de4699a5c Author: Peter Grassberger Date: Tue Aug 9 12:17:34 2016 +0200 Update RELEASE-NOTES commit 42da87d6246952a56acc0af6342a28532f979608 Author: Peter Grassberger Date: Tue Aug 9 12:05:02 2016 +0200 Update RELEASE-NOTES commit 7f6f5608df42231e56985fce2fe12f99ddae3212 Author: PeterTheOne Date: Tue Aug 9 11:22:50 2016 +0200 leaflet: add getDependencies function commit f6bc5c3c561939d8c1a2915c3e1cc67fa37bd04b Merge: 87cbbf30 f8161593 Author: Jeroen De Dauw Date: Mon Aug 8 17:52:17 2016 +0200 Merge pull request #96 from SemanticMediaWiki/ajax-leaflet-cluster Make leaflet cluster work with ajax commit 87cbbf309a5f2d52d6cb09fff3eb0654a67a2639 Merge: 2bf17f4e 1bd32b20 Author: Jeroen De Dauw Date: Mon Aug 8 16:42:36 2016 +0200 Merge pull request #97 from SemanticMediaWiki/fix-forminputs-path Fix ext.sm.forminputs path commit d018a66b0013f3584c507299954a99909099aa65 Merge: 99868097 48735d28 Author: Jeroen De Dauw Date: Mon Aug 8 16:38:40 2016 +0200 Merge pull request #190 from JeroenDeDauw/nominatim-geocoder add nominatim geocoder commit 99868097234b6bb696037851c79b7b328ffd6f25 Merge: c0b4ce87 3c7f7c63 Author: Jeroen De Dauw Date: Mon Aug 8 16:38:05 2016 +0200 Merge pull request #203 from JeroenDeDauw/leaflet-fullscreen-load fix leaflet fullscreen plugin loading commit c0b4ce87b55c9b36fbd42f692a76da52c88f0415 Merge: 1a0b0d56 c6438b01 Author: Jeroen De Dauw Date: Mon Aug 8 16:35:59 2016 +0200 Merge pull request #206 from JeroenDeDauw/leaflet-1.0.0-rc3 update to leaflet 1.0.0-rc3 commit c6438b0169d484fcb9808e57448ca90eee86ff02 Author: PeterTheOne Date: Mon Aug 8 15:57:17 2016 +0200 update to leaflet 1.0.0-rc3 fix #204 commit f81615933a8e54303fbca9a0d29cdb7b24a460c3 Author: PeterTheOne Date: Mon Aug 8 15:48:06 2016 +0200 increase ajax init timeout commit 3c7f7c63a6b9c807df48265d625bf2af48704938 Merge: 08cef44b 1a0b0d56 Author: PeterTheOne Date: Mon Aug 8 15:47:01 2016 +0200 Merge branch 'master' of git://github.com/JeroenDeDauw/Maps.git into leaflet-fullscreen-load # Conflicts: # includes/services/Leaflet/jquery.leaflet.js commit 18dc249a1b636d34af9a0c809a0e36a774b5794f Author: PeterTheOne Date: Mon Aug 8 15:22:06 2016 +0200 ajaxquery and ajaxcoordproperty both need to be set. commit 1bd32b20f7fd7acecbf87b859c7ba6637004dab0 Author: PeterTheOne Date: Mon Aug 8 15:17:18 2016 +0200 fix style commit 6cd7458edd1b0004cb17afc1999176e4e2d936c3 Author: PeterTheOne Date: Mon Aug 8 15:16:36 2016 +0200 fix ext.sm.forminputs path fix #84 commit 1a0b0d56eeef3ad85d48b23943161fcddc95191c Merge: 2eb83420 0d8d2585 Author: Jeroen De Dauw Date: Mon Aug 8 15:06:35 2016 +0200 Merge pull request #194 from JeroenDeDauw/leaflet-cluster add leaflet.cluster commit 2eb8342083ea6c55049d7916a3ddce969893d69a Merge: a762a092 0df42067 Author: Jeroen De Dauw Date: Mon Aug 8 14:47:32 2016 +0200 Merge pull request #205 from JeroenDeDauw/gm3-sensor remove gm3 sensor url parameter commit 0df4206771acbb5573acb1c9… * SUS-1784: Update .gitattributes and .gitignore * SUS-1784: add i18n shim, disable hardcoded exception * SUS-1784: Depend on MW 1.19-compatible fork of Validator * SUS-1784: Work around AMD issue in leaflet.js * SUS-1784: Rework dependency handling for display_map --- .gitattributes | 2 + .gitignore | 1 + .gitreview | 5 - .../before_script.sh => .travis.install.sh | 3 +- .travis.yml | 37 +- INSTALL.md | 251 +- ISSUE_TEMPLATE.md | 16 + Maps.hooks.php | 170 +- Maps.i18n.alias.php | 148 +- Maps.i18n.magic.php | 598 +- Maps.i18n.namespaces.php | 18 - Maps.i18n.php | 39 +- Maps.php | 234 +- Maps.resources.php | 79 +- Maps_Settings.php | 223 +- README.md | 80 +- RELEASE-NOTES.md | 186 +- SemanticMaps/SemanticMaps.hooks.php | 113 + SemanticMaps/src/SM_PolygonHandler.php | 118 + SemanticMaps/src/ext.sm.common.js | 76 + .../src/queryprinters/SM_KMLPrinter.php | 174 + .../src/queryprinters/SM_MapPrinter.php | 415 + .../src/queryprinters/SM_QueryHandler.php | 594 + .../GoogleMaps3/ext.sm.googlemaps3ajax.js | 48 + .../services/Leaflet/ext.sm.leafletajax.js | 44 + .../OpenLayers/ext.sm.openlayersajax.js | 41 + build/travis/after_success.sh | 14 - build/travis/script.sh | 14 - composer.json | 41 +- i18n/ast.json | 59 +- i18n/be-tarask.json | 19 +- i18n/bn.json | 91 +- i18n/br.json | 26 +- i18n/cs.json | 31 +- i18n/de.json | 98 +- i18n/el.json | 31 +- i18n/en.json | 62 +- i18n/es.json | 55 +- i18n/fa.json | 30 +- i18n/fi.json | 27 +- i18n/fr.json | 71 +- i18n/frp.json | 17 +- i18n/fy.json | 3 +- i18n/gl.json | 57 +- i18n/he.json | 50 +- i18n/hsb.json | 30 +- i18n/hu.json | 25 +- i18n/ia.json | 30 +- i18n/id.json | 20 +- i18n/it.json | 53 +- i18n/ja.json | 32 +- i18n/ko.json | 66 +- i18n/ksh.json | 2 +- i18n/lb.json | 32 +- i18n/lt.json | 90 +- i18n/mk.json | 71 +- i18n/ms.json | 8 +- i18n/nb.json | 65 +- i18n/nl.json | 57 +- i18n/pa.json | 1 - i18n/pl.json | 43 +- i18n/pms.json | 26 +- i18n/pt-br.json | 47 +- i18n/pt.json | 51 +- i18n/qqq.json | 285 +- i18n/ro.json | 5 +- i18n/ru.json | 49 +- i18n/si.json | 15 +- i18n/sv.json | 45 +- i18n/tl.json | 33 +- i18n/tr.json | 27 +- i18n/uk.json | 65 +- i18n/vi.json | 14 +- i18n/zh-hans.json | 108 +- i18n/zh-hant.json | 36 +- includes/Element.php | 34 +- includes/Geocoder.php | 6 +- includes/Geocoders.php | 255 +- includes/Maps_BaseFillableElement.php | 6 +- includes/Maps_BaseStrokableElement.php | 6 +- includes/Maps_DisplayMapRenderer.php | 313 +- includes/Maps_DistanceParser.php | 6 +- includes/Maps_GeoFunctions.php | 8 +- includes/Maps_KMLFormatter.php | 10 +- includes/Maps_Layer.php | 339 - includes/Maps_LayerGroup.php | 254 - includes/Maps_LayerPage.php | 179 - includes/Maps_LayerTypes.php | 161 - includes/Maps_Layers.php | 229 - includes/Maps_Mapper.php | 136 +- includes/Maps_MappingService.php | 83 +- includes/Maps_MappingServices.php | 27 +- includes/api/ApiGeocode.php | 40 +- includes/criteria/CriterionIsNonNumeric.php | 46 - includes/criteria/CriterionMapLayer.php | 74 - includes/editor/MapEditorHTML.php | 2 +- includes/ext.maps.common.js | 7 +- includes/ext.maps.layers.css | 70 - .../geocoders/Maps_GeocoderusGeocoder.php | 6 +- includes/geocoders/Maps_GeonamesGeocoder.php | 6 +- includes/geocoders/Maps_GoogleGeocoder.php | 22 +- .../geocoders/Maps_OldGeocoderAdapter.php | 45 + includes/iMappingService.php | 125 - includes/images/m1.png | Bin 0 -> 3003 bytes includes/images/m2.png | Bin 0 -> 3259 bytes includes/images/m3.png | Bin 0 -> 3956 bytes includes/images/m4.png | Bin 0 -> 5705 bytes includes/images/m5.png | Bin 0 -> 6839 bytes includes/layers/Maps_ImageLayer.php | 198 - .../Maps_ParamLayerDefinition.php | 43 - .../Maps_ParamSwitchIfGreaterThan.php | 49 - includes/parserhooks/Maps_Coordinates.php | 21 +- includes/parserhooks/Maps_DisplayMap.php | 139 +- includes/parserhooks/Maps_Distance.php | 16 +- includes/parserhooks/Maps_Finddestination.php | 46 +- includes/parserhooks/Maps_Geocode.php | 68 +- includes/parserhooks/Maps_Geodistance.php | 37 +- includes/parserhooks/Maps_LayerDefinition.php | 426 - includes/parserhooks/Maps_MapsDoc.php | 36 +- includes/parsers/CircleParser.php | 14 +- includes/parsers/LineParser.php | 2 +- includes/parsers/LocationParser.php | 62 +- includes/parsers/RectangleParser.php | 10 +- includes/properties/iBubbleMapElement.php | 24 - includes/properties/iFillableMapElement.php | 24 - includes/properties/iHoverableMapElement.php | 13 - includes/properties/iLinkableMapElement.php | 13 - includes/properties/iStrokableMapElement.php | 33 - includes/services/GoogleMaps3/GoogleMaps3.php | 102 +- .../services/GoogleMaps3/Maps_GoogleMaps3.php | 208 +- .../GoogleMaps3/ext.maps.googlemaps3.js | 5 +- .../GoogleMaps3/gm3-util-library/README | 6 +- .../gm3-util-library/markerclusterer.js | 243 +- .../services/GoogleMaps3/jquery.googlemap.js | 73 +- includes/services/Leaflet/Leaflet.php | 85 +- includes/services/Leaflet/Maps_Leaflet.php | 87 +- includes/services/Leaflet/ext.maps.leaflet.js | 3 +- includes/services/Leaflet/jquery.leaflet.js | 188 +- .../Leaflet/leaflet-providers/.eslintrc | 39 + .../Leaflet/leaflet-providers/.gitignore | 1 + .../Leaflet/leaflet-providers/.mversionrc | 8 + .../Leaflet/leaflet-providers/.travis.yml | 8 + .../Leaflet/leaflet-providers/CHANGELOG.md | 46 + .../Leaflet/leaflet-providers/CONTRIBUTING.md | 10 + .../Leaflet/leaflet-providers/README.md | 63 + .../Leaflet/leaflet-providers/bower.json | 25 + .../leaflet-providers/css/gh-fork-ribbon.css | 127 + .../css/gh-fork-ribbon.ie.css | 68 + .../Leaflet/leaflet-providers/index.html | 94 + .../leaflet-providers/leaflet-providers.js | 639 + .../Leaflet/leaflet-providers/license.md | 9 + .../Leaflet/leaflet-providers/package.json | 45 + .../preview/https-support.js | 57 + .../leaflet-providers/preview/index.html | 90 + .../leaflet-providers/preview/layer-bounds.js | 63 + .../leaflet-providers/preview/preview.js | 227 + .../leaflet-providers/preview/shared.js | 63 + .../preview/test-bounds.html | 87 + .../preview/test-https-support.html | 79 + .../leaflet-providers/tests/index.html | 44 + .../Leaflet/leaflet-providers/tests/test.js | 99 + .../Leaflet/leaflet.fullscreen/.jshintrc | 12 + .../leaflet.fullscreen/Control.FullScreen.css | 4 + .../leaflet.fullscreen/Control.FullScreen.js | 164 + .../Leaflet/leaflet.fullscreen/LICENSE | 22 + .../Leaflet/leaflet.fullscreen/README.md | 68 + .../Leaflet/leaflet.fullscreen/bower.json | 30 + .../leaflet.fullscreen/icon-fullscreen-2x.png | Bin 0 -> 228 bytes .../leaflet.fullscreen/icon-fullscreen.png | Bin 0 -> 153 bytes .../Leaflet/leaflet.fullscreen/index.html | 48 + .../Leaflet/leaflet.fullscreen/package.json | 25 + .../MarkerCluster.Default.css | 60 + .../leaflet.markercluster/MarkerCluster.css | 14 + .../leaflet.markercluster-src.js | 2632 +++ .../leaflet.markercluster.js | 6 + .../Leaflet/leaflet/images/layers-2x.png | Bin 0 -> 1259 bytes .../Leaflet/leaflet/images/layers.png | Bin 956 -> 696 bytes .../Leaflet/leaflet/images/marker-icon-2x.png | Bin 0 -> 2586 bytes .../Leaflet/leaflet/images/marker-icon.png | Bin 1493 -> 1466 bytes .../Leaflet/leaflet/images/marker-shadow.png | Bin 684 -> 618 bytes .../Leaflet/leaflet/images/zoom-in.png | Bin 87 -> 0 bytes .../Leaflet/leaflet/images/zoom-out.png | Bin 87 -> 0 bytes includes/services/Leaflet/leaflet/leaflet.css | 585 +- .../services/Leaflet/leaflet/leaflet.ie.css | 44 - includes/services/Leaflet/leaflet/leaflet.js | 13583 +++++++++++++++- .../services/OpenLayers/Maps_OpenLayers.php | 132 +- .../OpenLayers/Maps_ParamOLLayers.php | 170 - .../services/OpenLayers/OSM/OpenStreetMap.js | 32 - includes/services/OpenLayers/OpenLayers.php | 42 +- .../OpenLayers/ext.maps.openlayers.js | 3 +- .../services/OpenLayers/jquery.openlayers.js | 161 +- includes/specials/SpecialMapEditor.php | 6 +- phpunit.xml.dist | 8 +- schema/MapsLayers-postgres.sql | 10 - schema/MapsLayers.sql | 6 - src/{Maps => }/Elements/BaseElement.php | 4 +- src/{Maps => }/Elements/Circle.php | 12 +- src/{Maps => }/Elements/ImageOverlay.php | 4 - src/{Maps => }/Elements/Line.php | 10 +- src/{Maps => }/Elements/Location.php | 73 +- src/{Maps => }/Elements/Polygon.php | 8 +- src/{Maps => }/Elements/Rectangle.php | 12 +- src/{Maps => }/Elements/WmsOverlay.php | 6 +- src/Geocoders/Geocoder.php | 24 + src/Geocoders/InMemoryGeocoder.php | 37 + src/Geocoders/NominatimGeocoder.php | 63 + src/Semantic/DataValues/CoordinateValue.php | 259 + src/Semantic/DataValues/GeoPolygonValue.php | 109 + .../ValueDescriptions/AreaDescription.php | 160 + .../CoordinateDescription.php | 71 + src/SemanticMaps.php | 138 + .../MapsDistanceParserTest.php | 46 +- tests/Integration/MapsMapperTest.php | 47 + .../ValueDescriptions/AreaDescriptionTest.php | 83 + .../parserhooks/CoordinatesTest.php | 84 +- .../parserhooks/DisplayMapTest.php | 37 +- .../parserhooks/DistanceTest.php | 68 +- .../parserhooks/FinddestinationTest.php | 55 +- .../parserhooks/GeocodeTest.php | 39 +- .../parserhooks/GeodistanceTest.php | 41 +- .../parserhooks/MapsDocTest.php | 27 +- .../parserhooks/ParserHookTest.php | 25 +- .../parsers/CircleParserTest.php | 17 +- .../parsers/DistanceParserTest.php | 69 + .../parsers/LineParserTest.php | 71 +- .../parsers/LocationParserTest.php | 104 +- .../parsers/RectlangleParserTest.php | 15 +- .../parsers/WmsOverlayParserTest.php | 46 +- tests/{phpunit => Unit}/ElementTest.php | 16 +- .../Elements}/BaseElementTest.php | 16 +- .../elements => Unit/Elements}/CircleTest.php | 22 +- .../Elements}/ImageOverlayTest.php | 4 +- .../elements => Unit/Elements}/LineTest.php | 28 +- .../Elements}/LocationTest.php | 12 +- .../Elements}/PolygonTest.php | 4 +- .../Elements}/RectangleTest.php | 18 +- .../Geocoders/MapsOldGeocoderAdapterTest.php | 52 + .../Unit/Geocoders/NominatimGeocoderTest.php | 60 + .../DataValues/CoordinateValueTest.php | 112 + tests/bootstrap.php | 18 + tests/phpunit/parsers/DistanceParserTest.php | 59 - 241 files changed, 26869 insertions(+), 5983 deletions(-) delete mode 100644 .gitreview rename build/travis/before_script.sh => .travis.install.sh (92%) create mode 100644 ISSUE_TEMPLATE.md delete mode 100644 Maps.i18n.namespaces.php create mode 100644 SemanticMaps/SemanticMaps.hooks.php create mode 100644 SemanticMaps/src/SM_PolygonHandler.php create mode 100644 SemanticMaps/src/ext.sm.common.js create mode 100644 SemanticMaps/src/queryprinters/SM_KMLPrinter.php create mode 100644 SemanticMaps/src/queryprinters/SM_MapPrinter.php create mode 100644 SemanticMaps/src/queryprinters/SM_QueryHandler.php create mode 100644 SemanticMaps/src/services/GoogleMaps3/ext.sm.googlemaps3ajax.js create mode 100644 SemanticMaps/src/services/Leaflet/ext.sm.leafletajax.js create mode 100644 SemanticMaps/src/services/OpenLayers/ext.sm.openlayersajax.js delete mode 100644 build/travis/after_success.sh delete mode 100644 build/travis/script.sh delete mode 100644 includes/Maps_Layer.php delete mode 100644 includes/Maps_LayerGroup.php delete mode 100644 includes/Maps_LayerPage.php delete mode 100644 includes/Maps_LayerTypes.php delete mode 100644 includes/Maps_Layers.php delete mode 100644 includes/criteria/CriterionIsNonNumeric.php delete mode 100644 includes/criteria/CriterionMapLayer.php delete mode 100644 includes/ext.maps.layers.css create mode 100644 includes/geocoders/Maps_OldGeocoderAdapter.php delete mode 100644 includes/iMappingService.php create mode 100644 includes/images/m1.png create mode 100644 includes/images/m2.png create mode 100644 includes/images/m3.png create mode 100644 includes/images/m4.png create mode 100644 includes/images/m5.png delete mode 100644 includes/layers/Maps_ImageLayer.php delete mode 100644 includes/manipulations/Maps_ParamLayerDefinition.php delete mode 100644 includes/manipulations/Maps_ParamSwitchIfGreaterThan.php delete mode 100644 includes/parserhooks/Maps_LayerDefinition.php delete mode 100644 includes/properties/iBubbleMapElement.php delete mode 100644 includes/properties/iFillableMapElement.php delete mode 100644 includes/properties/iHoverableMapElement.php delete mode 100644 includes/properties/iLinkableMapElement.php delete mode 100644 includes/properties/iStrokableMapElement.php create mode 100644 includes/services/Leaflet/leaflet-providers/.eslintrc create mode 100644 includes/services/Leaflet/leaflet-providers/.gitignore create mode 100644 includes/services/Leaflet/leaflet-providers/.mversionrc create mode 100644 includes/services/Leaflet/leaflet-providers/.travis.yml create mode 100644 includes/services/Leaflet/leaflet-providers/CHANGELOG.md create mode 100644 includes/services/Leaflet/leaflet-providers/CONTRIBUTING.md create mode 100644 includes/services/Leaflet/leaflet-providers/README.md create mode 100644 includes/services/Leaflet/leaflet-providers/bower.json create mode 100644 includes/services/Leaflet/leaflet-providers/css/gh-fork-ribbon.css create mode 100644 includes/services/Leaflet/leaflet-providers/css/gh-fork-ribbon.ie.css create mode 100644 includes/services/Leaflet/leaflet-providers/index.html create mode 100644 includes/services/Leaflet/leaflet-providers/leaflet-providers.js create mode 100644 includes/services/Leaflet/leaflet-providers/license.md create mode 100644 includes/services/Leaflet/leaflet-providers/package.json create mode 100644 includes/services/Leaflet/leaflet-providers/preview/https-support.js create mode 100644 includes/services/Leaflet/leaflet-providers/preview/index.html create mode 100644 includes/services/Leaflet/leaflet-providers/preview/layer-bounds.js create mode 100644 includes/services/Leaflet/leaflet-providers/preview/preview.js create mode 100644 includes/services/Leaflet/leaflet-providers/preview/shared.js create mode 100644 includes/services/Leaflet/leaflet-providers/preview/test-bounds.html create mode 100644 includes/services/Leaflet/leaflet-providers/preview/test-https-support.html create mode 100644 includes/services/Leaflet/leaflet-providers/tests/index.html create mode 100644 includes/services/Leaflet/leaflet-providers/tests/test.js create mode 100644 includes/services/Leaflet/leaflet.fullscreen/.jshintrc create mode 100644 includes/services/Leaflet/leaflet.fullscreen/Control.FullScreen.css create mode 100644 includes/services/Leaflet/leaflet.fullscreen/Control.FullScreen.js create mode 100644 includes/services/Leaflet/leaflet.fullscreen/LICENSE create mode 100644 includes/services/Leaflet/leaflet.fullscreen/README.md create mode 100644 includes/services/Leaflet/leaflet.fullscreen/bower.json create mode 100644 includes/services/Leaflet/leaflet.fullscreen/icon-fullscreen-2x.png create mode 100644 includes/services/Leaflet/leaflet.fullscreen/icon-fullscreen.png create mode 100644 includes/services/Leaflet/leaflet.fullscreen/index.html create mode 100644 includes/services/Leaflet/leaflet.fullscreen/package.json create mode 100644 includes/services/Leaflet/leaflet.markercluster/MarkerCluster.Default.css create mode 100644 includes/services/Leaflet/leaflet.markercluster/MarkerCluster.css create mode 100644 includes/services/Leaflet/leaflet.markercluster/leaflet.markercluster-src.js create mode 100644 includes/services/Leaflet/leaflet.markercluster/leaflet.markercluster.js create mode 100644 includes/services/Leaflet/leaflet/images/layers-2x.png create mode 100644 includes/services/Leaflet/leaflet/images/marker-icon-2x.png delete mode 100644 includes/services/Leaflet/leaflet/images/zoom-in.png delete mode 100644 includes/services/Leaflet/leaflet/images/zoom-out.png delete mode 100644 includes/services/Leaflet/leaflet/leaflet.ie.css delete mode 100644 includes/services/OpenLayers/Maps_ParamOLLayers.php delete mode 100644 schema/MapsLayers-postgres.sql delete mode 100644 schema/MapsLayers.sql rename src/{Maps => }/Elements/BaseElement.php (95%) rename src/{Maps => }/Elements/Circle.php (94%) rename src/{Maps => }/Elements/ImageOverlay.php (96%) rename src/{Maps => }/Elements/Line.php (89%) rename src/{Maps => }/Elements/Location.php (83%) rename src/{Maps => }/Elements/Polygon.php (88%) rename src/{Maps => }/Elements/Rectangle.php (97%) rename src/{Maps => }/Elements/WmsOverlay.php (95%) create mode 100644 src/Geocoders/Geocoder.php create mode 100644 src/Geocoders/InMemoryGeocoder.php create mode 100644 src/Geocoders/NominatimGeocoder.php create mode 100644 src/Semantic/DataValues/CoordinateValue.php create mode 100644 src/Semantic/DataValues/GeoPolygonValue.php create mode 100644 src/Semantic/ValueDescriptions/AreaDescription.php create mode 100644 src/Semantic/ValueDescriptions/CoordinateDescription.php create mode 100644 src/SemanticMaps.php rename tests/{phpunit => Integration}/MapsDistanceParserTest.php (88%) create mode 100644 tests/Integration/MapsMapperTest.php create mode 100644 tests/Integration/Semantic/ValueDescriptions/AreaDescriptionTest.php rename tests/{phpunit => Integration}/parserhooks/CoordinatesTest.php (71%) rename tests/{phpunit => Integration}/parserhooks/DisplayMapTest.php (61%) rename tests/{phpunit => Integration}/parserhooks/DistanceTest.php (62%) rename tests/{phpunit => Integration}/parserhooks/FinddestinationTest.php (66%) rename tests/{phpunit => Integration}/parserhooks/GeocodeTest.php (53%) rename tests/{phpunit => Integration}/parserhooks/GeodistanceTest.php (72%) rename tests/{phpunit => Integration}/parserhooks/MapsDocTest.php (52%) rename tests/{phpunit => Integration}/parserhooks/ParserHookTest.php (89%) rename tests/{phpunit => Integration}/parsers/CircleParserTest.php (66%) create mode 100644 tests/Integration/parsers/DistanceParserTest.php rename tests/{phpunit => Integration}/parsers/LineParserTest.php (66%) rename tests/{phpunit => Integration}/parsers/LocationParserTest.php (56%) rename tests/{phpunit => Integration}/parsers/RectlangleParserTest.php (72%) rename tests/{phpunit => Integration}/parsers/WmsOverlayParserTest.php (65%) rename tests/{phpunit => Unit}/ElementTest.php (76%) rename tests/{phpunit/elements => Unit/Elements}/BaseElementTest.php (77%) rename tests/{phpunit/elements => Unit/Elements}/CircleTest.php (67%) rename tests/{phpunit/elements => Unit/Elements}/ImageOverlayTest.php (94%) rename tests/{phpunit/elements => Unit/Elements}/LineTest.php (67%) rename tests/{phpunit/elements => Unit/Elements}/LocationTest.php (71%) rename tests/{phpunit/elements => Unit/Elements}/PolygonTest.php (96%) rename tests/{phpunit/elements => Unit/Elements}/RectangleTest.php (80%) create mode 100644 tests/Unit/Geocoders/MapsOldGeocoderAdapterTest.php create mode 100644 tests/Unit/Geocoders/NominatimGeocoderTest.php create mode 100644 tests/Unit/Semantic/DataValues/CoordinateValueTest.php create mode 100644 tests/bootstrap.php delete mode 100644 tests/phpunit/parsers/DistanceParserTest.php diff --git a/.gitattributes b/.gitattributes index ad3a3d2cc..139b09a3e 100644 --- a/.gitattributes +++ b/.gitattributes @@ -2,9 +2,11 @@ .gitignore export-ignore .gitreview export-ignore .scrutinizer.yml export-ignore +.travis.install.sh export-ignore .travis.yml export-ignore build/ export-ignore INSTALL.md export-ignore +ISSUE_TEMPLATE.md export-ignore phpunit.xml-dist export-ignore README.md export-ignore RELEASE-NOTES.md export-ignore diff --git a/.gitignore b/.gitignore index 59d96612e..f822b659e 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,7 @@ !.* .idea/ +*.iml vendor/ extensions/ diff --git a/.gitreview b/.gitreview deleted file mode 100644 index a4240ea1c..000000000 --- a/.gitreview +++ /dev/null @@ -1,5 +0,0 @@ -[gerrit] -host=gerrit.wikimedia.org -port=29418 -project=mediawiki/extensions/Maps.git -defaultbranch=master diff --git a/build/travis/before_script.sh b/.travis.install.sh similarity index 92% rename from build/travis/before_script.sh rename to .travis.install.sh index 308438b4e..5c4a744a3 100644 --- a/build/travis/before_script.sh +++ b/.travis.install.sh @@ -29,7 +29,8 @@ cp -r $originalDirectory Maps cd Maps composer install --prefer-source -composer require 'phpunit/phpunit=3.7.*' --prefer-source + +[[ ! -z $SMW ]] && composer require "mediawiki/semantic-media-wiki=$SMW" --prefer-source cd ../.. diff --git a/.travis.yml b/.travis.yml index 63b599a69..217c92c5a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,27 +1,32 @@ language: php -env: - - THENEEDFORTHIS=FAIL +sudo: false matrix: include: - - env: DBTYPE=mysql; MW=master - php: 5.6 - - env: DBTYPE=mysql; MW=master + - env: DBTYPE=mysql; MW=1.27.3 php: 7 - - env: DBTYPE=sqlite; MW=1.21.0 - php: 5.3 - - env: DBTYPE=mysql; MW=1.23.0 - php: 5.5 - - env: DBTYPE=sqlite; MW=master; TYPE=coverage + - env: DBTYPE=mysql; MW=master; SMW=2.5.2 + php: 7.1 + - env: DBTYPE=sqlite; MW=1.28.2; SMW=2.1.3 php: 5.6 - exclude: - - env: THENEEDFORTHIS=FAIL + - env: DBTYPE=mysql; MW=1.27.3; SMW=2.4.6 + php: 5.6 + - env: DBTYPE=sqlite; MW=master; TYPE=coverage + php: 7.1 -install: travis_retry composer self-update +install: + - travis_retry composer self-update + - bash .travis.install.sh -before_script: bash ./build/travis/before_script.sh +script: ../phase3/tests/phpunit/phpunit.php -c ../phase3/extensions/Maps/phpunit.xml.dist -script: bash ./build/travis/script.sh +after_success: + - if [[ "$TYPE" != "coverage" ]]; then exit 0; fi + - ../phase3/tests/phpunit/phpunit.php -c ../phase3/extensions/Maps/phpunit.xml.dist --coverage-clover coverage.clover + - wget https://scrutinizer-ci.com/ocular.phar + - php ocular.phar code-coverage:upload --format=php-clover coverage.clover -after_success: bash ./build/travis/after_success.sh +cache: + directories: + - $HOME/.composer/cache diff --git a/INSTALL.md b/INSTALL.md index 2101f3ad3..d8363da47 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -2,78 +2,124 @@ These are the installation and configuration instructions for the [Maps extension](README.md). -## Versions +**Table of contents** + +* [Download and installation](INSTALL.md#download-and-installation) +* [Configuration](INSTALL.md#configuration) +* [Platform compatibility and release status](INSTALL.md#platform-compatibility-and-release-status) + +## Download and installation + +Go to the root directory of your MediaWiki installation. + +If you have previously installed Composer skip to step 2. + +To install Composer, just download http://getcomposer.org/composer.phar into your +current directory. + + wget http://getcomposer.org/composer.phar + +#### Step 2 + +Now using Composer, install Maps + + php composer.phar require mediawiki/maps "*" + +#### Verify installation success + +As final step, you can verify Maps got installed by looking at the Special:Version page on your wiki +and verifying the Maps extension is listed. + +If you want to use the Semantic MediaWiki integration, you will also need to install Semantic MediaWiki. + +## Configuration + +At present, minimal configuration is needed to get Maps running. Configuration is done like in most +MediaWiki extensions, by placing some simple snippets of PHP code at the bottom of MediaWiki's +LocalSettings.php. + +As of June 2016, Google requires you to provide an API key when you where not already using their +maps API. This means that you will either need to configure this key, or use another of the +supported mapping services. + +### Required configuration for Google Maps + +$GLOBALS['egMapsGMaps3ApiKey'] = 'your-api-key'; + +### Not using Google Maps by default + +For OpenLayers: + +$GLOBALS['egMapsDefaultService'] = 'openlayers'; + +For Leaflet: + +$GLOBALS['egMapsDefaultService'] = 'leaflet'; + +You might also want to fully disable Google Maps by placing a copy of the `egMapsAvailableServices` +setting in LocalSettings, and removing the `googlemaps3` line. + +See the [Maps settings file](Maps_Settings.php) for all available configuration options. + +### Platform compatibility and release status + +The PHP and MediaWiki version ranges listed are those in which Maps is known to work. It might also +work with more recent versions of PHP and MediaWiki, though this is not guaranteed. Increases of +minimum requirements are indicated in bold. For a detailed list of changes, see the [release notes](RELEASE-NOTES.md). - - - + + + + - - - - + + + + + - + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + - - - - + + + + + - - - - + + + + + - - - - - + + + + + +
StatusRelease dateGit branchPHPMediaWikiSemantic MediaWikiRelease status
Maps 3.6.xDevelopment versionFuturemasterMaps 4.4.x5.6 - 7.1+1.27 - 1.29+TBD - 2.5+In development
Maps 3.5.xMaps 4.3.x5.6 - 7.11.27 - 1.292.1 - 2.5 Stable release2016-04-013.5.0
Maps 3.4.1Obsolete release2016-01-303.4.1
Maps 3.4Obsolete release2015-07-253.4.0
Maps 3.3Obsolete release2015-06-293.3.0
Maps 3.2Obsolete release2014-09-123.2.0
Maps 3.1Obsolete release2014-06-303.1.0Maps 4.2.x5.5 - 7.11.23 - 1.292.1 - 2.5Legacy release
Maps 3.0.1Obsolete release2014-03-273.0.1Maps 4.1.x5.5 - 7.11.23 - 1.282.1 - 2.5Legacy release
Maps 2.0.xObsolete release2012-12-132.0.xMaps 4.0.x5.5 - 7.01.23 - 1.282.1 - 2.4Legacy release
Maps 1.0.5Obsolete release2011-11-301.0.5
Maps 3.8.x5.5 - 7.01.23 - 1.27Not applicableLegacy release
-### Platform compatibility +Older versions (no longer supported): @@ -84,44 +130,65 @@ These are the installation and configuration instructions for the [Maps extensio - - + + + + + + + + + + + + + + + + - + - - + + - + - + + + + + + + + - + - + - + - - + + @@ -134,60 +201,6 @@ These are the installation and configuration instructions for the [Maps extensio
Validator
3.5.x5.3.2 - 7.xMaps 3.7.x5.5 - 7.01.23 - 1.27RequiredHandled by Composer
Maps 3.6.x5.5 - 7.01.23 - 1.27RequiredHandled by Composer
Maps 3.5.x5.3.2 - 7.0 1.18 - 1.27 Required2.x (handled by Composer)Handled by Composer
3.4.x5.3.2 - 7.xMaps 3.4.x5.3.2 - 7.0 1.18 - 1.27 Required2.x (handled by Composer)Handled by Composer
Maps 3.3.x 5.3.2 - 5.6.x 1.18 - 1.25 Required2.x (handled by Composer)Handled by Composer
Maps 3.2.x5.3.2 - 5.6.x1.18 - 1.24RequiredHandled by Composer
Maps 3.1.x & 3.2.xMaps 3.1.x 5.3.2 - 5.6.x 1.18 - 1.24 Required2.x (handled by Composer)Handled by Composer
Maps 3.0.x 5.3.2 - 5.6.x 1.18 - 1.23 Required1.x (handled by Composer)Handled by Composer
Maps 2.0.x5.3.2 - 5.5.x1.18 - 1.235.3.2 - 5.5.x1.18 - 1.23 Not supported 0.5.1
-When installing Maps 2.x, see the installation instructions that come bundled with it. Also -make use of Validator 0.5.x. More recent versions of Validator will not work. - ### Database support All current versions of Maps have full support for all databases that can be used with MediaWiki. - -## Download and installation - -The recommended way to download and install Maps is with [Composer](http://getcomposer.org) using -[MediaWiki 1.22 built-in support for Composer](https://www.mediawiki.org/wiki/Composer). MediaWiki -versions prior to 1.22 can use Composer via the -[Extension Installer](https://github.com/JeroenDeDauw/ExtensionInstaller/blob/master/README.md) -extension. - -#### Step 1 - -If you have MediaWiki 1.22 or later, go to the root directory of your MediaWiki installation, -and go to step 2. You do not need to install any extensions to support composer. - -For MediaWiki 1.21.x and earlier you need to install the -[Extension Installer](https://github.com/JeroenDeDauw/ExtensionInstaller/blob/master/README.md) extension. - -Once you are done installing the Extension Installer, go to its directory so composer.phar -is installed in the right place. - - cd extensions/ExtensionInstaller - -#### Step 2 - -If you have previously installed Composer skip to step 3. - -To install Composer, just download http://getcomposer.org/composer.phar into your -current directory. - - wget http://getcomposer.org/composer.phar - -#### Step 3 - -Now using Composer, install Maps - - php composer.phar require mediawiki/maps "*" - -#### Verify installation success - -As final step, you can verify Maps got installed by looking at the Special:Version page on your wiki and verifying the -Maps extension is listed. - -#### Custom image layers support (experimental) - -For support of the experimental custom image layers feature you have to run the MediaWiki update script. - - php maintenance/update.php - -## Configuration - -See the [Maps settings file](Maps_Settings.php) for the available configuration options. diff --git a/ISSUE_TEMPLATE.md b/ISSUE_TEMPLATE.md new file mode 100644 index 000000000..12cd946c7 --- /dev/null +++ b/ISSUE_TEMPLATE.md @@ -0,0 +1,16 @@ +### Setup + +- MW version: +- DB (MySQL etc.): +- PHP version: +- Maps version: +- SMW version (if applicable): + +### Issue + +Detailed description of the issue and a [stack trace](https://www.semantic-mediawiki.org/wiki/Help:Identifying_bugs) if applicable: + +``` +``` + +Steps to reproduce the observation (recommendation is to use the [sandbox](http://sandbox.semantic-mediawiki.org)): diff --git a/Maps.hooks.php b/Maps.hooks.php index d64f2277c..75843cd0c 100644 --- a/Maps.hooks.php +++ b/Maps.hooks.php @@ -7,17 +7,8 @@ * * @licence GNU GPL v2+ * @author Jeroen De Dauw < jeroendedauw@gmail.com > - * @author Daniel Werner */ final class MapsHooks { - /** - * Helper flag indicating whether the page has been purged. - * @var bool - * - * TODO: Figure out a better way to do this, not requiring this flag and make sure it works with - * later MW versions (purging mechanism got changed somewhat around 1.18). - */ - static $purgedBeforeStore = false; /** * Adds a link to Admin Links page. @@ -44,30 +35,14 @@ public static function addToAdminLinks( ALTree $admin_links_tree ) { return true; } - /** - * Intercept pages in the Layer namespace to handle them correctly. - * - * @param $title: Title - * @param $article: Article or null - * - * @return boolean - */ - public static function onArticleFromTitle( Title &$title, /* Article */ &$article ) { - if ( $title->getNamespace() == Maps_NS_LAYER ) { - $article = new MapsLayerPage( $title ); - } - - return true; - } - /** * Adds global JavaScript variables. * * @since 1.0 - * @see http://www.mediawiki.org/wiki/Manual:Hooks/MakeGlobalVariablesScript - * @param array &$vars Variables to be added into the output - * @param OutputPage $outputPage OutputPage instance calling the hook - * @return boolean true in all cases + * @see http://www.mediawiki.org/wiki/Manual:Hooks/MakeGlobalVariablesScript + * @param array &$vars Variables to be added into the output + * @param OutputPage $outputPage OutputPage instance calling the hook + * @return boolean true in all cases */ public static function onMakeGlobalVariablesScript( array &$vars, OutputPage $outputPage ) { global $egMapsGlobalJSVars; @@ -81,138 +56,27 @@ public static function onMakeGlobalVariablesScript( array &$vars, OutputPage $ou } /** - * @since 0.7 - * - * @param array $list + * Wikia change + * Load library dependencies of display_map in the after article wikitext + * Registering them as head items will cause Oasis logic to put them after RL ", 'bing' => "", 'ol-wms' => "", - 'google' => "", - ); + 'google' => "", + ]; - // Leaflet + // Leaflet // Integer. The default zoom of a map. This value will only be used when the // user does not provide one. $GLOBALS['egMapsLeafletZoom'] = 14; + // String. The default layer for Leaflet. This value will only be + // used when the user does not provide one. + $GLOBALS['egMapsLeafletLayer'] = 'OpenStreetMap'; + + $GLOBALS['egMapsLeafletOverlayLayers'] = [ + + ]; + + // The definitions for the layers that should be available for the user. + $GLOBALS['egMapsLeafletAvailableLayers'] = [ + 'OpenStreetMap' => true, + 'OpenStreetMap.DE' => true, + 'OpenStreetMap.BlackAndWhite' => true, + 'OpenStreetMap.HOT' => true, + 'Thunderforest.OpenCycleMap' => true, + 'Thunderforest.Transport' => true, + 'Thunderforest.Landscape' => true, + 'Hydda.Full' => true, + //'MapBox' => false, // todo: implement setting api key + 'Stamen.Toner' => true, + 'Stamen.Terrain' => true, + 'Stamen.Watercolor' => true, + 'Esri.WorldStreetMap' => true, + 'Esri.DeLorme' => true, + 'Esri.WorldTopoMap' => true, + 'Esri.WorldImagery' => true, + 'Esri.WorldTerrain' => true, + 'Esri.WorldShadedRelief' => true, + 'Esri.WorldPhysical' => true, + 'Esri.OceanBasemap' => true, + 'Esri.NatGeoWorldMap' => true, + 'Esri.WorldGrayCanvas' => true, + 'MapQuestOpen' => true, + ]; + + $GLOBALS['egMapsLeafletAvailableOverlayLayers'] = [ + 'OpenSeaMap' => true, + 'OpenWeatherMap.Clouds' => true, + 'OpenWeatherMap.CloudsClassic' => true, + 'OpenWeatherMap.Precipitation' => true, + 'OpenWeatherMap.PrecipitationClassic' => true, + 'OpenWeatherMap.Rain' => true, + 'OpenWeatherMap.RainClassic' => true, + 'OpenWeatherMap.Pressure' => true, + 'OpenWeatherMap.PressureContour' => true, + 'OpenWeatherMap.Wind' => true, + 'OpenWeatherMap.Temperature' => true, + 'OpenWeatherMap.Snow' => true, + ]; + + $GLOBALS['egMapsLeafletLayersApiKeys'] = [ + 'MapBox' => '', + 'MapQuestOpen' => '', + ]; + + // Layer dependencies + $GLOBALS['egMapsLeafletLayerDependencies'] = [ + 'MapQuestOpen' => 'https://open.mapquestapi.com/sdk/leaflet/v2.2/mq-map.js?key=', + ]; + + + $GLOBALS['egMapsGlobalJSVars'] = []; -$GLOBALS['egMapsGlobalJSVars'] = array(); diff --git a/README.md b/README.md index a17175a88..7af63a75d 100644 --- a/README.md +++ b/README.md @@ -1,29 +1,43 @@ # Maps -Maps is a [MediaWiki](https://www.mediawiki.org) extension to work with and visualise geographical +Maps is a [MediaWiki](https://www.mediawiki.org) extension to work with and visualize geographical information. Features: * Powerful `#display_map` parser hook for embedding highly customizable dynamic maps into wiki pages. -* Support for multiple mapping services: Google Maps, [OpenLayers](http://www.openlayers.org/), -[OpenStreetMap](www.openstreetmap.org/) and [Leaflet](http://leafletjs.com/). +* Support for multiple mapping services: [Leaflet](http://leafletjs.com/), Google Maps, [OpenLayers](http://www.openlayers.org/) and [OpenStreetMap](www.openstreetmap.org/). +* Integration with [Semantic MediaWiki](https://www.semantic-mediawiki.org) via a [coordinate datatype](https://www.semantic-mediawiki.org/wiki/Help:Type_Geographic_coordinate) + * Query your stored coordinates and visualize them on dynamic maps, as tables or as lists + * Export your coordinates as KML or RDF + * Combine coordinates with other structured data stored in your wiki * Coordinate formatting and format conversion via the `#coordinates` parser function. * Geocoding via several supported services with the `#geocode` parser function. * Geospatial operations * Calculating the distance between two points with `#geodistance` * Finding a destination given a starting point, bearing and distance with `#finddestination` * Distance formatting and format conversion via the `#distance` parser function. -* Visual map editor (Special:MapEditor) to edit `#display_map` wikitext. -* Structured data support provided by the [Semantic Maps extension] -(https://www.mediawiki.org/wiki/Extension:Semantic_Maps). +* Visual map editor (Special:MapEditor) to edit `#display_map` wikitext (requires Google Maps). -View the [release notes](RELEASE-NOTES.md) for recent changes to Maps. +## User manual -### User manual +### For administrators * [Installation and configuration](INSTALL.md) -* [Usage instructions and examples](https://www.semantic-mediawiki.org/wiki/Maps) +* [Release notes](RELEASE-NOTES.md) - detailed list of changes per release +* [Platform compatibility](INSTALL.md#platform-compatibility-and-release-status) - overview of PHP and MediaWiki support per release + +### For wiki users + +* [Usage instructions](https://www.semantic-mediawiki.org/wiki/Maps) +* [Usage examples](https://www.semantic-mediawiki.org/wiki/Category:Maps_examples) +* [Semantic usage examples](https://www.semantic-mediawiki.org/wiki/Semantic_Maps_examples) + +### Getting support + +* Ask a question on [the mailing list](https://semantic-mediawiki.org/wiki/Mailing_list) +* Ask a question on the #mediawiki IRC channel on Freenode. +* File an issue on [our issue tracker](https://github.com/JeroenDeDauw/Maps/issues) (technical issues only) ## Project status @@ -41,47 +55,27 @@ On [Packagist](https://packagist.org/packages/mediawiki/maps): ## Contributing -Feel free to fork the [code on GitHub](https://github.com/JeroenDeDauw/Maps) and to submit pull -requests. - -You can run the PHPUnit tests by changing into the `tests/phpunit` directory of your MediaWiki -install and running - - php phpunit.php -c ../../extensions/Maps/ - -## Credits to other projects - -### jQuery - -This extension uses code from the jQuery library. -jQuery is dual licensed under the -[MIT](http://www.opensource.org/licenses/mit-license.php) -and -[GPL](http://www.opensource.org/licenses/gpl-license.php) -licenses. - -### OpenLayers +* [File an issue](https://github.com/JeroenDeDauw/Maps/issues) +* [Submit a pull request](https://github.com/JeroenDeDauw/Maps/pulls) ([tasks for newcomers](https://github.com/JeroenDeDauw/Maps/issues?q=is%3Aissue+is%3Aopen+label%3Anewcomer)) -This extension includes code from the OpenLayers application. -OpenLayers is an open-source product released under a -[BSD-style license](http://svn.openlayers.org/trunk/openlayers/license.txt). +### Running the tests -### geoxml3 +As setup, run `composer install` inside of the Maps root directory. -This extension includes a copy of the geoxml3 KML processor. -geoxml3 is released under the -[Apache License 2.0 license](http://www.apache.org/licenses/LICENSE-2.0). +You can run the MediaWiki independent tests by changing into the Maps root directory and running -### google-maps-utility-library-v3 + phpunit + +This is possible without having a MediaWiki installation or webserver. A clone of the Maps code suffices. -This extension includes code from the google-maps-utility-library-v3 (googleearth.js). -It is released under the -[Apache License 2.0 license](http://www.apache.org/licenses/LICENSE-2.0). +To run the tests with MediaWiki, change into `tests/phpunit` of your MediaWiki installation and run -### OpenStreetMap.js + php phpunit.php --wiki wiki -c ../../extensions/Maps/phpunit.xml.dist + +Where you either update `wiki` to match your wikis name, or drop the parameter. The above command +works without modification if you are using the [MediaWiki Vagrant](https://www.mediawiki.org/wiki/MediaWiki-Vagrant). -This extension includes the OpenStreetMap.js file which can be found -[here](http://www.openstreetmap.org/openlayers/OpenStreetMap.js). +Beware that due to severe technical debt, some tests access the network. ## Links diff --git a/RELEASE-NOTES.md b/RELEASE-NOTES.md index 768c446ee..5a3c137ce 100644 --- a/RELEASE-NOTES.md +++ b/RELEASE-NOTES.md @@ -1,6 +1,158 @@ -These are the release notes for the [Maps extension](../README.md). +These are the release notes for the [Maps extension](README.md). For an overview of the different releases and which versions of PHP and MediaWiki they support, see the [platform compatibility tables](INSTALL.md#platform-compatibility-and-release-status). +## Maps 4.3.0 + +Released on June 10th, 2017. + +* Dropped support for MediaWiki older than 1.27 +* Dropped support for PHP older than 5.6 +* Fixed compatibility conflict with the GitHub MediaWiki extension + +## Maps 4.2.1 + +Released on May 20th, 2017. + +* Fixed issue occurring when using the `template` parameter in the Google Maps result format more than once on a page + +## Maps 4.2.0 + +Released on May 15th, 2017. + +* Fixed bug in Nomatim geocoder that caused page loading to fail when Nomatim is down +* Fixed bug in Nomatim geocoder that caused page loading to fail when Nomatim returned an invalid response +* Updated Leaflet from 1.0.0-rc to 1.0.3 + +## Maps 4.1.0 + +Released on April 14th, 2017. + +* Fixed rendering of area query values (they now work properly in SMW "further result" links) +* Fixed type warning in `SMMapPrinter::getMapHTML` +* Added missing geographical polygon type i18n messages + +## Maps 4.0.5 + +Released on March 5th, 2017. + +* Fixed i18n issue in the `mapsdoc` parser hook + +## Maps 4.0.4 + +Released on January 9th, 2017. + +* Fixed encoding of special characters in the Google geocoder (by somescout) +* Improved PHP 7 compatibility (by Andre Klapper) + +## Maps 4.0.3 + +Released on December 6th, 2016. + +* Fixed regression introduced in 4.0.2 that caused the JavaScript to not be loaded in some cases +* The `display_map` parser hook now correctly uses its `geoservice` parameter +* The `center` parameter for the map result format now takes into account the `geoservice` parameter + +## Maps 4.0.2 + +Released on December 4th, 2016. + +* Fixed fatal error caused by double loading of initialization code on some platforms + +## Maps 4.0.1 + +Released on November 19th, 2016. + +* The `geocode` parser hook now correctly uses its `geoservice` and `allowcoordinates` parameters + +## Maps 4.0 + +Released on November 16th, 2016. Also see the [Maps 4.0 blog post](https://www.entropywins.wtf/blog/2016/11/09/maps-4-0-0-rc1-released/) + +### Highlight: Integrated Semantic MediaWiki support + +Merged in most of the features of the Semantic Maps extension. These are enabled automatically when SMW is installed. + +* Added a [coordinate datatype](https://www.semantic-mediawiki.org/wiki/Help:Type_Geographic_coordinate) +* Added a [result format](https://www.semantic-mediawiki.org/wiki/Help:Result_formats) for each mapping service +* Added a KML result format +* Added [distance query](https://www.semantic-mediawiki.org/wiki/Semantic_Maps_examples/Distance_query) support + +Semantic Maps is discontinued as the features will now be maintained in Maps. The Semantic Maps form input +has been moved into the [Page Forms](https://www.mediawiki.org/wiki/Extension:Page_Forms) extension. + +### Breaking changes + +* The default mapping service was changed from Google Maps to Leaflet (can be changed via the `egMapsDefaultService` setting) +* The Maps tracking category is now disabled by default (can be enabled using the `egMapsEnableCategory` setting) + +### Other changes + +* Added `egMapsDisableExtension` setting that allows disabling the extension even when it is installed +* The `egGoogleJsApiKey` setting from Maps 2.x will now be used as Google API key when `egMapsGMaps3ApiKey` is not set +* Various missing messages where added + +## Maps 3.8.2 + +Released on September 22nd, 2016. + +* Fixed incorrect centering of OpenLayers maps (by Peter Grassberger) + +## Maps 3.8.1 + +Released on September 7th, 2016. + +* Fixed bug that caused clustering to always be enabled for Leaflet (by Peter Grassberger) + +## Maps 3.8 + +Released on August 24rd, 2016. + +Due to changes to Google Maps, an API key now needs to be set. See the +[installation configuration instructions](https://github.com/JeroenDeDauw/Maps/blob/master/INSTALL.md#configuration). + +* Added Google Maps API key `egMapsGMaps3ApiKey` setting (by Peter Grassberger) +* Added Google Maps API version number `egMapsGMaps3ApiVersion` setting (by Peter Grassberger) +* Added [Leaflet marker clustering](https://www.semantic-mediawiki.org/wiki/Maps_examples/Leaflet_marker_clustering) (by Peter Grassberger) + * `markercluster`: Enables clustering, multiple markers are merged into one marker. + * `clustermaxzoom`: The maximum zoom level where clusters may exist. + * `clusterzoomonclick`: Whether clicking on a cluster zooms into it. + * `clustermaxradius`: The maximum radius that a cluster will cover. + * `clusterspiderfy`: At the lowest zoom level markers are separated so you can see them all. +* Added [Leaflet fullscreen control](https://www.semantic-mediawiki.org/wiki/Maps_examples/Leaflet_fullscreen_control) (by Peter Grassberger) +* Added [OSM Nominatim Geocoder](https://www.semantic-mediawiki.org/wiki/Maps_examples/Geocode) (by Peter Grassberger) +* Upgraded Leaflet library to its latest version (1.0.0-r3) (by Peter Grassberger) +* Made removal of marker clusters more robust (by Peter Grassberger) +* Unified system messages for several services (by Karsten Hoffmeyer) + +## Maps 3.7 + +Released on June 21st, 2016. + +* Added [rotate control support](https://www.semantic-mediawiki.org/wiki/Maps_examples/Google_Maps_with_rotate_control) for Google Maps (by Peter Grassberger) +* Changed coordinate display on OpenLayers maps from long-lat to lat-long (by Peter Grassberger) +* Upgraded google marker cluster library to its latest version (2.1.2) (by Peter Grassberger) +* Upgraded Leaflet library to its latest version (0.7.7) (by Peter Grassberger) +* Added missing system messages (by Karsten Hoffmeyer) +* Internal code enhancements (by Peter Grassberger) +* Removed broken custom map layer functionality. You no longer need to run update.php for full installation. + +## Maps 3.6 + +Released on May 26th, 2016. + +* Dropped support for MediaWiki older than 1.23 +* Dropped support for PHP older than 5.5 +* Added cluster properties for Google Maps (by Peter Grassberger) + * `clustergridsize`: The grid size of a cluster in pixels + * `clustermaxzoom`: The maximum zoom level that a marker can be part of a cluster + * `clusterzoomonclick`: Whether the default behaviour of clicking on a cluster is to zoom into it. + * `clusteraveragecenter`: Whether the center of each cluster should be the average of all markers in the cluster. + * `clusterminsize`: The minimum number of markers required to form a cluster. +* Fixed missing marker cluster images for Google Maps (by Peter Grassberger) +* Fixed duplicate markers in OpenLayers maps (by Peter Grassberger) +* Fixed URL support in the icon parameter (by Peter Grassberger) +* Various minor MediaWiki compatibility enhancements (by Karsten Hoffmeyer, Siebrand Mazeland and FlorianSW) + ## Maps 3.5 Released on April 2nd, 2016. @@ -60,7 +212,7 @@ Released on January 19th, 2015. Released on January 13th, 2015. * Fixed `geocode` right -* Fixed coordinate precision issue after breaking changes in DataValues Geo +* Fixed coordinate precision issue after breaking changes in DataValues Geo ## Maps 3.2 @@ -289,11 +441,11 @@ MediaWiki unit testing support. #### New features #### * Tag support for these parser hooks (which previously only had parser function support): -** Coordinates -** Distance -** Finddestination -** Geocode -** Geodistance + * Coordinates + * Distance + * Finddestination + * Geocode + * Geodistance * Thumbs and photos parameters for the OSM service. #### Bug fixes #### @@ -348,7 +500,7 @@ with all the globals previously needed for this. * Moved marker JavaScript creation for display_points to the mapping service class for all features. * Moved default zoom level access method to the mapping service class for all features. * Improved the way marker data is turned into JavaScript variables. -* Improved coordinate recognition regexes. +* Improved coordinate recognition regexes. #### Bug fixes #### @@ -579,8 +731,8 @@ for the current version. Changes in 0.4.2 discussed on the authors blog: -* [Maps and Semantic Maps 0.4.2 released](http://www.bn2vs.com/blog/2009/11/16/maps-and-semantic-maps-0-4-2/) -* [New in Maps 0.4.2](http://www.bn2vs.com/blog/2009/11/12/new-in-maps-0-4-2/) +* [Maps and Semantic Maps 0.4.2 released](https://www.entropywins.wtf/blog/2009/11/16/maps-and-semantic-maps-0-4-2/) +* [New in Maps 0.4.2](https://www.entropywins.wtf/blog/2009/11/12/new-in-maps-0-4-2/) #### New features #### @@ -622,7 +774,7 @@ where not separated by a comma. Changes in 0.4 discussed on the authors blog: -* [Finally! Maps and Semantic Maps 0.4!](http://www.bn2vs.com/blog/2009/11/03/finally-maps-and-semantic-maps-0-4/) +* [Finally! Maps and Semantic Maps 0.4!](https://www.entropywins.wtf/blog/2009/11/03/finally-maps-and-semantic-maps-0-4/) #### New features #### @@ -661,7 +813,7 @@ for large contents. Changes in 0.3.4 discussed on the authors blog: -* [Maps and Semantic Maps 0.3.4 released](http://www.bn2vs.com/blog/2009/09/12/maps-and-semantic-maps-0-3-4-released/) +* [Maps and Semantic Maps 0.3.4 released](https://www.entropywins.wtf/blog/2009/09/12/maps-and-semantic-maps-0-3-4-released/) ####New features#### @@ -691,7 +843,7 @@ any value (ie |coordinates=|) Changes in 0.3.3 discussed on the authors blog: -* [Maps and Semantic Maps 0.3.3](http://www.bn2vs.com/blog/2009/08/25/maps-and-semantic-maps-0-3-3/) +* [Maps and Semantic Maps 0.3.3](https://www.entropywins.wtf/blog/2009/08/25/maps-and-semantic-maps-0-3-3/) ####New features#### @@ -741,10 +893,10 @@ since adding the path is impossible in the declaration. Changes in 0.3 discussed on the authors blog: -* [Final changes for Maps and SM 0.3](http://www.bn2vs.com/blog/2009/08/13/final-changes-for-maps-and-sm-0-3/) -* [New features in Maps and SM 0.3](http://www.bn2vs.com/blog/2009/08/07/new-features-in-maps-and-sm-0-3/) -* [Structural changes for Maps and SM 0.3](http://www.bn2vs.com/blog/2009/08/05/structural-changes-for-maps-and-sm-0-3/) - +* [Final changes for Maps and SM 0.3](https://www.entropywins.wtf/blog/2009/08/13/final-changes-for-maps-and-sm-0-3/) +* [New features in Maps and SM 0.3](https://www.entropywins.wtf/blog/2009/08/07/new-features-in-maps-and-sm-0-3/) +* [Structural changes for Maps and SM 0.3](https://www.entropywins.wtf/blog/2009/08/05/structural-changes-for-maps-and-sm-0-3/) + ####New features#### * Multi location parser functions. Two completely new parser functions have been added that diff --git a/SemanticMaps/SemanticMaps.hooks.php b/SemanticMaps/SemanticMaps.hooks.php new file mode 100644 index 000000000..9da8a89bd --- /dev/null +++ b/SemanticMaps/SemanticMaps.hooks.php @@ -0,0 +1,113 @@ + + */ +final class SemanticMapsHooks { + + /** + * Adds a link to Admin Links page. + * + * @since 0.7 + * + * @param ALTree $admin_links_tree + * + * @return boolean + */ + public static function addToAdminLinks( ALTree &$admin_links_tree ) { + $displaying_data_section = $admin_links_tree->getSection( wfMessage( 'smw_adminlinks_displayingdata' )->text() ); + + // Escape if SMW hasn't added links. + if ( is_null( $displaying_data_section ) ) { + return true; + } + + $smw_docu_row = $displaying_data_section->getRow( 'smw' ); + + $sm_docu_label = wfMessage( 'adminlinks_documentation', 'Semantic Maps' )->text(); + $smw_docu_row->addItem( AlItem::newFromExternalLink( 'http://mapping.referata.com/wiki/Semantic_Maps', $sm_docu_label ) ); + + return true; + } + + /** + * Adds support for the geographical coordinates and shapes data type to Semantic MediaWiki. + * + * @since 2.0 + * + * @return boolean + */ + public static function initGeoDataTypes() { + DataTypeRegistry::getInstance()->registerDatatype( + '_geo', + CoordinateValue::class, + SMWDataItem::TYPE_GEO + ); + + DataTypeRegistry::getInstance()->registerDatatype( + '_gpo', + GeoPolygonValue::class, + SMWDataItem::TYPE_BLOB + ); + + return true; + } + + /** + * Set the default format to 'map' when the requested properties are + * of type geographic coordinates. + * + * TODO: have a setting to turn this off and have it off by default for #show + * + * @since 1.0 + * + * @param $format Mixed: The format (string), or false when not set yet + * @param array $printRequests The print requests made + * @param array $params The parameters for the query printer + * + * @return boolean + */ + public static function addGeoCoordsDefaultFormat( &$format, array $printRequests, array $params ) { + // Only set the format when not set yet. This allows other extensions to override the Semantic Maps behavior. + if ( $format === false ) { + // Only apply when there is more then one print request. + // This way requests comming from #show are ignored. + if ( count( $printRequests ) > 1 ) { + $allValid = true; + $hasCoords = false; + + // Loop through the print requests to determine their types. + foreach( $printRequests as /* SMWPrintRequest */ $printRequest ) { + // Skip the first request, as it's the object. + if ( $printRequest->getMode() == SMWPrintRequest::PRINT_THIS ) { + continue; + } + + $typeId = $printRequest->getTypeID(); + + if ( $typeId == '_geo' ) { + $hasCoords = true; + } + else { + $allValid = false; + break; + } + } + + // If they are all coordinates, set the result format to 'map'. + if ( $allValid && $hasCoords ) { + $format = 'map'; + } + } + + } + + return true; + } + +} diff --git a/SemanticMaps/src/SM_PolygonHandler.php b/SemanticMaps/src/SM_PolygonHandler.php new file mode 100644 index 000000000..3aeffc3e1 --- /dev/null +++ b/SemanticMaps/src/SM_PolygonHandler.php @@ -0,0 +1,118 @@ + 'LocationValidator', + 'lines' => 'LineValidator', + 'polygons' => 'PolygonValidator', + 'circles' => 'CircleValidator', + 'rectangles' => 'RectangleValidator' + ]; + + /** + * Array of classes of different Geographic shapes. + * + * @var array + */ + private $geoClasses = [ + 'locations' => 'MapsLocation', + 'lines' => 'MapsLine', + 'polygons' => 'MapsPolygon', + 'circles' => 'MapsCircle', + 'rectangles' => 'MapsRectanlge' + ]; + + /** + * NOTE: These need to be changed as Manipulations are deprecated. + * Array of classes for param handling of different Geographic shapes. + * + * @var array + */ + private $paramClasses = [ + 'locations' => 'MapsParamLocation', + 'lines' => 'MapsParamLine', + 'polygons' => 'MapsParamPolygon', + 'circles' => 'MapsParamCircle', + 'rectangles' => 'MapsParamRectangle' + ]; + + /** + * @param string $text + */ + public function __construct( $text ) { + $this->text = $text; + } + + public function getGeoType() { + $parts = explode( '=', $this->text ); + return current( $parts ); + } + + public function getValidationErrors() { + $this->validateText(); + return $this->errors; + } + + private function validateText() { + $parts = explode( '=', $this->text ); + if( array_key_exists( $parts[0], $this->validatorClasses ) ) { + $validatorClass = new $this->validatorClasses[ $parts[0] ]( '~' ); + if ( !$validatorClass->doValidation( $parts[1] ) ) + $this->errors[] = wfMessage( 'semanticmaps-shapes-improperformat', $this->text )->escaped(); + } else { + $this->errors[] = wfMessage( 'semanticmaps-shapes-missingshape', $parts[0] )->escaped(); + } + } + + /** + * Gets an object of the model class the string represents. + * + * @since 2.1 + */ + public function shapeFromText() { + $parts = explode( '~' , $this->text ); + $shape = explode( '=', array_shift( $parts ) ); + if( array_key_exists( $shape[0] , $this->geoClasses ) ) { + $geoClass = new $this->geoClasses[ $shape[0] ]( explode( ':' , $shape[1] ) ); + + return $geoClass; + } else { + return false; + } + } +} diff --git a/SemanticMaps/src/ext.sm.common.js b/SemanticMaps/src/ext.sm.common.js new file mode 100644 index 000000000..19148c562 --- /dev/null +++ b/SemanticMaps/src/ext.sm.common.js @@ -0,0 +1,76 @@ +/** + * JavaScript the Semantic Maps extension. + * @see https://www.mediawiki.org/wiki/Extension:Semantic_Maps + * + * @licence GNU GPL v2++ + * @author Peter Grassberger < petertheone@gmail.com > + */ +window.sm = new ( function( $, mw ) { + + this.buildQueryString = function( query, ajaxcoordproperty, top, right, bottom, left ) { + var isCompoundQuery = query.indexOf('|') > -1; + var query = query.split('|'); + $.each( query, function ( index ) { + query[index] += ' [[' + ajaxcoordproperty + '::+]] '; + query[index] += '[[' + ajaxcoordproperty + '::>' + bottom + '°, ' + left + '°]] '; + query[index] += '[[' + ajaxcoordproperty + '::<' + top + '°, ' + right + '°]]'; + if (!isCompoundQuery) { + query[index] += '|?' + ajaxcoordproperty; + } else { + query[index] += ';?' + ajaxcoordproperty; + } + } ); + return query.join(' | '); + }; + + /** + * Detects semicolons `;` not in square brackets `[]`. + * + * @param string + * @returns {boolean} + */ + this.hasCompoundQuerySemicolon = function( string ) { + return /;(?![^[]*])/g.test( string ); + }; + + this.sendQuery = function( query ) { + var action = this.hasCompoundQuerySemicolon( query ) ? 'compoundquery' : 'ask'; + return $.ajax( { + method: 'GET', + url: mw.util.wikiScript( 'api' ), + data: { + 'action': action, + 'query': query, + 'format': 'json' + }, + dataType: 'json' + } ); + }; + + this.ajaxUpdateMarker = function( map, query, icon ) { + return this.sendQuery(query).done( function( data ) { + if ( !data.hasOwnProperty( 'query' ) || + !data.query.hasOwnProperty( 'results' )) { + return; + } + // todo: don't remove and recreate all markers.. + // only add new ones. + map.removeMarkers(); + for ( var property in data.query.results ) { + if ( data.query.results.hasOwnProperty( property ) ) { + var location = data.query.results[property]; + var coordinates = location.printouts[map.options.ajaxcoordproperty][0]; + var markerOptions = { + lat: coordinates.lat, + lon: coordinates.lon, + title: location.fulltext, + text: '' + location.fulltext + '', + icon: icon + }; + map.addMarker( markerOptions ); + } + } + } ); + }; + +} )( jQuery, mediaWiki ); diff --git a/SemanticMaps/src/queryprinters/SM_KMLPrinter.php b/SemanticMaps/src/queryprinters/SM_KMLPrinter.php new file mode 100644 index 000000000..f47271e4c --- /dev/null +++ b/SemanticMaps/src/queryprinters/SM_KMLPrinter.php @@ -0,0 +1,174 @@ + + */ +class SMKMLPrinter extends FileExportPrinter { + + /** + * Handler of the print request. + * + * @param SMWQueryResult $res + * @param $outputmode + * + * @return array + */ + public function getResultText( SMWQueryResult $res, $outputmode ) { + if ( $outputmode == SMW_OUTPUT_FILE ) { + return $this->getKML( $res, $outputmode ); + } + else { + return $this->getKMLLink( $res, $outputmode ); + } + } + + /** + * @see SMWResultPrinter::handleParameters + * + * @param array $params + * @param $outputmode + */ + protected function handleParameters( array $params, $outputmode ) { + $this->params = $params; + } + + /** + * @see SMWResultPrinter::getParamDefinitions + * + * @param ParamDefinition[] $definitions + * + * @return array of ParamDefinition|array + */ + public function getParamDefinitions( array $definitions ) { + global $egMapsDefaultLabel, $egMapsDefaultTitle; + + $params = parent::getParamDefinitions( $definitions ); + + $params['text'] = [ + 'message' => 'semanticmaps-kml-text', + 'default' => $egMapsDefaultLabel, + ]; + + $params['title'] = [ + 'message' => 'semanticmaps-kml-title', + 'default' => $egMapsDefaultTitle, + ]; + + $params['linkabsolute'] = [ + 'message' => 'semanticmaps-kml-linkabsolute', + 'type' => 'boolean', + 'default' => true, + ]; + + $params['pagelinktext'] = [ + 'message' => 'semanticmaps-kml-pagelinktext', + 'default' => wfMessage( 'semanticmaps-default-kml-pagelink' )->text(), + ]; + + return $params; + } + + /** + * Returns the KML for the query result. + * + * @param SMWQueryResult $res + * @param integer $outputmode + * + * @return string + */ + private function getKML( SMWQueryResult $res, $outputmode ) { + $queryHandler = new SMQueryHandler( $res, $outputmode, $this->params['linkabsolute'] ); + $queryHandler->setText( $this->params['text'] ); + $queryHandler->setTitle( $this->params['title'] ); + $queryHandler->setSubjectSeparator( '' ); + $queryHandler->setPageLinkText( $this->params['pagelinktext'] ); + + $formatter = new MapsKMLFormatter( $this->params ); + + $shapes = $queryHandler->getShapes(); + $formatter->addPlacemarks( $shapes['locations'] ); + + return $formatter->getKML(); + } + + /** + * Returns a link (HTML) pointing to a query that returns the actual KML file. + * + * @param SMWQueryResult $res + * @param integer $outputmode + * + * @return string + */ + private function getKMLLink( SMWQueryResult $res, $outputmode ) { + $searchLabel = $this->getSearchLabel( $outputmode ); + $link = $res->getQueryLink( $searchLabel ? $searchLabel : wfMessage( 'semanticmaps-kml-link' )->inContentLanguage()->text() ); + $link->setParameter( 'kml', 'format' ); + $link->setParameter( $this->params['linkabsolute'] ? 'yes' : 'no', 'linkabsolute' ); + $link->setParameter( $this->params['pagelinktext'], 'pagelinktext' ); + + if ( $this->params['title'] !== '' ) { + $link->setParameter( $this->params['title'], 'title' ); + } + + if ( $this->params['text'] !== '' ) { + $link->setParameter( $this->params['text'], 'text' ); + } + + // Fix for offset-error in getQueryLink() + // (getQueryLink by default sets offset to point to the next + // result set, fix by setting it to 0 if now explicitly set) + if ( array_key_exists( 'offset', $this->params ) ) { + $link->setParameter( $this->params['offset'], 'offset' ); + } else { + $link->setParameter( 0, 'offset' ); + } + + if ( array_key_exists( 'limit', $this->params ) ) { + $link->setParameter( $this->params['limit'], 'limit' ); + } else { // Use a reasonable default limit. + $link->setParameter( 20, 'limit' ); + } + + $this->isHTML = ( $outputmode == SMW_OUTPUT_HTML ); + + return $link->getText( $outputmode, $this->mLinker ); + } + + /** + * @see SMWIExportPrinter::getMimeType + * + * @param SMWQueryResult $queryResult + * + * @return string + */ + public function getMimeType( SMWQueryResult $queryResult ) { + return 'application/vnd.google-earth.kml+xml'; + } + + /** + * @see SMWIExportPrinter::getFileName + * + * @param SMWQueryResult $queryResult + * + * @return string|boolean + */ + public function getFileName( SMWQueryResult $queryResult ) { + return 'kml.kml'; + } + + /** + * @see SMWResultPrinter::getName() + */ + public final function getName() { + return wfMessage( 'semanticmaps-kml' )->text(); + } +} diff --git a/SemanticMaps/src/queryprinters/SM_MapPrinter.php b/SemanticMaps/src/queryprinters/SM_MapPrinter.php new file mode 100644 index 000000000..a32a61c12 --- /dev/null +++ b/SemanticMaps/src/queryprinters/SM_MapPrinter.php @@ -0,0 +1,415 @@ + + * @author Peter Grassberger < petertheone@gmail.com > + */ +class SMMapPrinter extends SMW\ResultPrinter { + + private static $services = []; + + /** + * @var LocationParser + */ + private $locationParser; + + /** + * @since 3.4 + * FIXME: this is a temporary hack that should be replaced when SMW allows for dependency + * injection in query printers. + * + * @param MapsMappingService $service + */ + public static function registerService( MapsMappingService $service ) { + self::$services[$service->getName()] = $service; + } + + public static function registerDefaultService( $serviceName ) { + self::$services['map'] = self::$services[$serviceName]; + } + + /** + * @var MapsMappingService + */ + private $service; + + /** + * @var string|boolean + */ + private $fatalErrorMsg = false; + + /** + * @param string $format + * @param bool $inline + */ + public function __construct( $format, $inline = true ) { + $this->service = self::$services[$format]; + + parent::__construct( $format, $inline ); + } + + /** + * Returns an array containing the parameter info. + * + * @return array + */ + private function getParameterInfo() { + global $smgQPShowTitle, $smgQPTemplate, $smgQPHideNamespace; + + $params = ParamDefinition::getCleanDefinitions( MapsMapper::getCommonParameters() ); + + $this->service->addParameterInfo( $params ); + + $params['staticlocations'] = [ + 'type' => 'mapslocation', // FIXME: geoservice is not used + 'aliases' => [ 'locations', 'points' ], + 'default' => [], + 'islist' => true, + 'delimiter' => ';', + 'message' => 'semanticmaps-par-staticlocations', + ]; + + $params['showtitle'] = [ + 'type' => 'boolean', + 'aliases' => 'show title', + 'default' => $smgQPShowTitle, + ]; + + $params['hidenamespace'] = [ + 'type' => 'boolean', + 'aliases' => 'hide namespace', + 'default' => $smgQPHideNamespace, + ]; + + $params['template'] = [ + 'default' => $smgQPTemplate, + ]; + + $params['userparam'] = [ + 'default' => '', + ]; + + $params['activeicon'] = [ + 'type' => 'string', + 'default' => '', + ]; + + $params['pagelabel'] = [ + 'type' => 'boolean', + 'default' => false, + ]; + + $params['ajaxcoordproperty'] = [ + 'default' => '', + ]; + + $params['ajaxquery'] = [ + 'default' => '', + 'type' => 'string' + ]; + + // Messages: + // semanticmaps-par-staticlocations, semanticmaps-par-showtitle, semanticmaps-par-hidenamespace, + // semanticmaps-par-template, semanticmaps-par-userparam, semanticmaps-par-activeicon, + // semanticmaps-par-pagelabel, semanticmaps-par-ajaxcoordproperty semanticmaps-par-ajaxquery + foreach ( $params as $name => &$data ) { + if ( is_array( $data ) && !array_key_exists( 'message', $data ) ) { + $data['message'] = 'semanticmaps-par-' . $name; + } + } + + return $params; + } + + /** + * Builds up and returns the HTML for the map, with the queried coordinate data on it. + * + * @param SMWQueryResult $res + * @param $outputmode + * + * @return string + */ + public final function getResultText( SMWQueryResult $res, $outputmode ) { + if ( $this->fatalErrorMsg !== false ) { + return $this->fatalErrorMsg; + } + + $this->addTrackingCategoryIfNeeded(); + + $params = $this->params; + + $this->initializeLocationParser( $params ); + + $queryHandler = new SMQueryHandler( $res, $outputmode ); + $queryHandler->setLinkStyle( $params['link'] ); + $queryHandler->setHeaderStyle($params['headers']); + $queryHandler->setShowSubject( $params['showtitle'] ); + $queryHandler->setTemplate( $params['template'] ); + $queryHandler->setUserParam( $params['userparam'] ); + $queryHandler->setHideNamespace( $params['hidenamespace'] ); + $queryHandler->setActiveIcon( $params['activeicon'] ); + + $this->handleMarkerData( $params, $queryHandler ); + + $params['ajaxquery'] = urlencode( $params['ajaxquery'] ); + + $locationAmount = count( $params['locations'] ); + + if ( $locationAmount > 0 ) { + // We can only take care of the zoom defaulting here, + // as not all locations are available in whats passed to Validator. + if ( $this->fullParams['zoom']->wasSetToDefault() && $locationAmount > 1 ) { + $params['zoom'] = false; + } + + $mapName = $this->service->getMapId(); + + SMWOutputs::requireHeadItem( + $mapName, + $this->service->getDependencyHtml() . + $configVars = Skin::makeVariablesScript( $this->service->getConfigVariables() ) + ); + + foreach ( $this->service->getResourceModules() as $resourceModule ) { + SMWOutputs::requireResource( $resourceModule ); + } + + if ( array_key_exists( 'source', $params ) ) { + unset( $params['source'] ); + } + + return $this->getMapHTML( $params, $mapName ); + } + else { + return $params['default']; + } + } + + private function addTrackingCategoryIfNeeded() { + /** + * @var Parser $wgParser + */ + global $wgParser; + + if ( $GLOBALS['egMapsEnableCategory'] && $wgParser->getOutput() !== null ) { + $wgParser->addTrackingCategory( 'maps-tracking-category' ); + } + } + + private function initializeLocationParser( array $params ) { + $this->locationParser = new LocationParser( new ValueParserOptions( [ + 'geoService' => $params['geoservice'] + ] ) ); + } + + /** + * Converts the data in the coordinates parameter to JSON-ready objects. + * These get stored in the locations parameter, and the coordinates on gets deleted. + * + * @param array &$params + * @param SMQueryHandler $queryHandler + */ + private function handleMarkerData( array &$params, SMQueryHandler $queryHandler ) { + $params['centre'] = $this->getCenter( $params['centre'] ); + + $iconUrl = MapsMapper::getFileUrl( $params['icon'] ); + $visitedIconUrl = MapsMapper::getFileUrl( $params['visitedicon'] ); + + $params['locations'] = $this->getJsonForStaticLocations( + $params['staticlocations'], + $params, + $iconUrl, + $visitedIconUrl + ); + + unset( $params['staticlocations'] ); + + $this->addShapeData( $queryHandler->getShapes(), $params, $iconUrl, $visitedIconUrl ); + + if ( $params['format'] === 'openlayers' ) { + $params['layers'] = MapsDisplayMapRenderer::evilOpenLayersHack( $params['layers'] ); + } + } + + private function getCenter( $coordinatesOrAddress ) { + if ( $coordinatesOrAddress === false ) { + return false; + } + + try { + // FIXME: a Location makes no sense here, since the non-coordinate data is not used + $location = $this->locationParser->stringParse( $coordinatesOrAddress ); + } + catch ( \Exception $ex ) { + // TODO: somehow report this to the user + return false; + } + + return $location->getJSONObject(); + } + + /** + * Returns the HTML to display the map. + * + * @param array $params + * @param string $mapName + * + * @return string + */ + private function getMapHTML( array $params, $mapName ) { + return Html::rawElement( + 'div', + [ + 'id' => $mapName, + 'style' => "width: {$params['width']}; height: {$params['height']}; background-color: #cccccc; overflow: hidden;", + 'class' => 'maps-map maps-' . $this->service->getName() + ], + wfMessage( 'maps-loading-map' )->inContentLanguage()->escaped() . + Html::element( + 'div', + [ 'style' => 'display:none', 'class' => 'mapdata' ], + FormatJson::encode( $params ) + ) + ); + } + + private function getJsonForStaticLocations( array $staticLocations, array $params, $iconUrl, $visitedIconUrl ) { + /** + * @var Parser $wgParser + */ + global $wgParser; + + $parser = version_compare( $GLOBALS['wgVersion'], '1.18', '<' ) ? $wgParser : clone $wgParser; + + $locationsJson = []; + + foreach ( $staticLocations as $location ) { + $locationsJson[] = $this->getJsonForStaticLocation( + $location, + $params, + $iconUrl, + $visitedIconUrl, + $parser + ); + } + + return $locationsJson; + } + + private function getJsonForStaticLocation( Location $location, array $params, $iconUrl, $visitedIconUrl, Parser $parser ) { + $jsonObj = $location->getJSONObject( $params['title'], $params['label'], $iconUrl, '', '', $visitedIconUrl ); + + $jsonObj['title'] = $parser->parse( $jsonObj['title'], $parser->getTitle(), new ParserOptions() )->getText(); + $jsonObj['text'] = $parser->parse( $jsonObj['text'], $parser->getTitle(), new ParserOptions() )->getText(); + + $hasTitleAndtext = $jsonObj['title'] !== '' && $jsonObj['text'] !== ''; + $jsonObj['text'] = ( $hasTitleAndtext ? '' . $jsonObj['title'] . '
' : $jsonObj['title'] ) . $jsonObj['text']; + $jsonObj['title'] = strip_tags( $jsonObj['title'] ); + + if ( $params['pagelabel'] ) { + $jsonObj['inlineLabel'] = Linker::link( Title::newFromText( $jsonObj['title'] ) ); + } + + return $jsonObj; + } + + /** + * @param Element[] $queryShapes + * @param array $params + * @param string $iconUrl + * @param string $visitedIconUrl + */ + private function addShapeData( array $queryShapes, array &$params, $iconUrl, $visitedIconUrl ) { + $params['locations'] = array_merge( + $params['locations'], + $this->getJsonForLocations( + $queryShapes['locations'], + $params, + $iconUrl, + $visitedIconUrl + ) + ); + + $params['lines'] = $this->getElementJsonArray( $queryShapes['lines'], $params ); + $params['polygons'] = $this->getElementJsonArray( $queryShapes['polygons'], $params ); + } + + /** + * @param Location[] $locations + * @param array $params + * @param string $iconUrl + * @param string $visitedIconUrl + * + * @return array + */ + private function getJsonForLocations( array $locations, array $params, $iconUrl, $visitedIconUrl ) { + $locationsJson = []; + + foreach ( $locations as $location ) { + $jsonObj = $location->getJSONObject( $params['title'], $params['label'], $iconUrl, '', '', $visitedIconUrl ); + + $jsonObj['title'] = strip_tags( $jsonObj['title'] ); + + $locationsJson[] = $jsonObj; + } + + return $locationsJson; + } + + /** + * @param BaseElement[] $elements + * @param array $params + * + * @return array + */ + private function getElementJsonArray( array $elements, array $params ) { + $elementsJson = []; + + foreach ( $elements as $element ) { + $jsonObj = $element->getJSONObject( $params['title'], $params['label'] ); + $elementsJson[] = $jsonObj; + } + + return $elementsJson; + } + + /** + * Returns the internationalized name of the mapping service. + * + * @return string + */ + public final function getName() { + return wfMessage( 'maps_' . $this->service->getName() )->text(); + } + + /** + * Returns a list of parameter information, for usage by Special:Ask and others. + * + * @return array + */ + public function getParameters() { + $params = parent::getParameters(); + $paramInfo = $this->getParameterInfo(); + + // Do not display this as an option, as the format already determines it + // TODO: this can probably be done cleaner with some changes in Maps + unset( $paramInfo['mappingservice'] ); + + $params = array_merge( $params, $paramInfo ); + + return $params; + } +} diff --git a/SemanticMaps/src/queryprinters/SM_QueryHandler.php b/SemanticMaps/src/queryprinters/SM_QueryHandler.php new file mode 100644 index 000000000..eedba15fe --- /dev/null +++ b/SemanticMaps/src/queryprinters/SM_QueryHandler.php @@ -0,0 +1,594 @@ + + */ +class SMQueryHandler { + + private $queryResult; + private $outputMode; + + /** + * @var array + */ + private $geoShapes = [ + 'lines' => [], + 'locations' => [], + 'polygons' => [] + ]; + + /** + * The template to use for the text, or false if there is none. + * + * @var string|boolean false + */ + private $template = false; + + /** + * The global icon. + * + * @var string + */ + public $icon = ''; + + /** + * The global text. + * + * @var string + */ + public $text = ''; + + /** + * The global title. + * + * @var string + */ + public $title = ''; + + /** + * Make a separate link to the title or not? + * + * @var boolean + */ + public $titleLinkSeparate = false; + + /** + * Should link targets be made absolute (instead of relative)? + * + * @var boolean + */ + private $linkAbsolute; + + /** + * The text used for the link to the page (if it's created). $1 will be replaced by the page name. + * + * @var string + */ + private $pageLinkText = '$1'; + + /** + * A separator to use between the subject and properties in the text field. + * + * @var string + */ + private $subjectSeparator = '
'; + + /** + * Make the subject in the text bold or not? + * + * @var boolean + */ + private $boldSubject = true; + + /** + * Show the subject in the text or not? + * + * @var boolean + */ + private $showSubject = true; + + /** + * Hide the namespace or not. + * + * @var boolean + */ + private $hideNamespace = false; + + + /** + * Defines which article names in the result are hyperlinked, all normally is the default + * none, subject, all + */ + private $linkStyle = 'all'; + + /* + * Show headers (with links), show headers (just text) or hide them. show is default + * show, plain, hide + */ + private $headerStyle = 'show'; + + /** + * Marker icon to show when marker equals active page + * + * @var string|null + */ + private $activeIcon = null; + + /** + * @var string + */ + private $userParam = ''; + + /** + * @param SMWQueryResult $queryResult + * @param integer $outputMode + * @param boolean $linkAbsolute + */ + public function __construct( SMWQueryResult $queryResult, $outputMode, $linkAbsolute = false ) { + $this->queryResult = $queryResult; + $this->outputMode = $outputMode; + $this->linkAbsolute = $linkAbsolute; + } + + /** + * Sets the template. + * + * @param string $template + */ + public function setTemplate( $template ) { + $this->template = $template === '' ? false : $template; + } + + /** + * @param string $userParam + */ + public function setUserParam( $userParam ) { + $this->userParam = $userParam; + } + + /** + * Sets the global icon. + * + * @param string $icon + */ + public function setIcon( $icon ) { + $this->icon = $icon; + } + + /** + * Sets the global title. + * + * @param string $title + */ + public function setTitle( $title ) { + $this->title = $title; + } + + /** + * Sets the global text. + * + * @param string $text + */ + public function setText( $text ) { + $this->text = $text; + } + + /** + * Sets the subject separator. + * + * @param string $subjectSeparator + */ + public function setSubjectSeparator( $subjectSeparator ) { + $this->subjectSeparator = $subjectSeparator; + } + + /** + * Sets if the subject should be made bold in the text. + * + * @param string $boldSubject + */ + public function setBoldSubject( $boldSubject ) { + $this->boldSubject = $boldSubject; + } + + /** + * Sets if the subject should shown in the text. + * + * @param string $showSubject + */ + public function setShowSubject( $showSubject ) { + $this->showSubject = $showSubject; + } + + /** + * Sets the text for the link to the page when separate from the title. + * + * @param string $text + */ + public function setPageLinkText( $text ) { + $this->pageLinkText = $text; + } + + /** + * + * @param boolean $link + */ + public function setLinkStyle ( $link ) { + $this->linkStyle = $link; + } + + /** + * + * @param boolean $headers + */ + public function setHeaderStyle ( $headers ) { + $this->headerStyle = $headers; + } + + /** + * @return array + */ + public function getShapes() { + $this->findShapes(); + return $this->geoShapes; + } + + /** + * @since 2.0 + */ + private function findShapes() { + while ( ( $row = $this->queryResult->getNext() ) !== false ) { + $this->handleResultRow( $row ); + } + } + + /** + * Returns the locations found in the provided result row. + * + * @param SMWResultArray[] $row + */ + private function handleResultRow( array $row ) { + $locations = []; + $properties = []; + + $title = ''; + $text = ''; + + // Loop through all fields of the record. + foreach ( $row as $i => $resultArray ) { + $printRequest = $resultArray->getPrintRequest(); + + // Loop through all the parts of the field value. + while ( ( $dataValue = $resultArray->getNextDataValue() ) !== false ) { + if ( $dataValue->getTypeID() == '_wpg' && $i == 0 ) { + list( $title, $text ) = $this->handleResultSubject( $dataValue ); + } + else if ( $dataValue->getTypeID() == '_str' && $i == 0 ) { + $title = $dataValue->getLongText( $this->outputMode, null ); + $text = $dataValue->getLongText( $this->outputMode, smwfGetLinker() ); + } + else if ( $dataValue->getTypeID() == '_gpo' ) { + $dataItem = $dataValue->getDataItem(); + $polyHandler = new PolygonHandler ( $dataItem->getString() ); + $this->geoShapes[ $polyHandler->getGeoType() ][] = $polyHandler->shapeFromText(); + } else if ( strpos( $dataValue->getTypeID(), '_rec' ) !== false ) { + foreach ( $dataValue->getDataItems() as $dataItem ) { + if ( $dataItem instanceof \SMWDIGeoCoord ) { + $location = Location::newFromLatLon( $dataItem->getLatitude(), $dataItem->getLongitude() ); + $locations[] = $location; + } + } + } + else if ( $dataValue->getTypeID() != '_geo' && $i != 0 && !$this->isHeadersHide() ) { + $properties[] = $this->handleResultProperty( $dataValue, $printRequest ); + } + else if ( $printRequest->getMode() == SMWPrintRequest::PRINT_PROP && $printRequest->getTypeID() == '_geo' || $dataValue->getTypeID() == '_geo' ) { + $dataItem = $dataValue->getDataItem(); + + $location = Location::newFromLatLon( $dataItem->getLatitude(), $dataItem->getLongitude() ); + + $locations[] = $location; + } + } + } + + if ( $properties !== [] && $text !== '' ) { + $text .= $this->subjectSeparator; + } + + $icon = $this->getLocationIcon( $row ); + + $this->geoShapes['locations'] = array_merge( + $this->geoShapes['locations'], + $this->buildLocationsList( + $locations, + $text, + $icon, + $properties, + Title::newFromText( $title ) + ) + ); + } + + /** + * Handles a SMWWikiPageValue subject value. + * Gets the plain text title and creates the HTML text with headers and the like. + * + * @param SMWWikiPageValue $object + * + * @return array with title and text + */ + private function handleResultSubject( SMWWikiPageValue $object ) { + $title = $object->getLongText( $this->outputMode, null ); + $text = ''; + + if ( $this->showSubject ) { + if( !$this->showArticleLink()){ + $text = $this->hideNamespace ? $object->getText() : $object->getTitle()->getFullText(); + }else if ( !$this->titleLinkSeparate && $this->linkAbsolute ) { + $text = Html::element( + 'a', + [ 'href' => $object->getTitle()->getFullUrl() ], + $this->hideNamespace ? $object->getText() : $object->getTitle()->getFullText() + ); + } + else { + if($this->hideNamespace){ + $text = $object->getShortHTMLText(smwfGetLinker()); + }else{ + $text = $object->getLongHTMLText( smwfGetLinker() ); + } + } + + if ( $this->boldSubject ) { + $text = '' . $text . ''; + } + + if ( $this->titleLinkSeparate ) { + $txt = $object->getTitle()->getText(); + + if ( $this->pageLinkText !== '' ) { + $txt = str_replace( '$1', $txt, $this->pageLinkText ); + } + $text .= Html::element( + 'a', + [ 'href' => $object->getTitle()->getFullUrl() ], + $txt + ); + } + } + + return [ $title, $text ]; + } + + private function showArticleLink() { + return $this->linkStyle !== 'none'; + } + + private function hasTemplate() { + return is_string( $this->template ); + } + + /** + * Handles a single property (SMWPrintRequest) to be displayed for a record (SMWDataValue). + * + * @param SMWDataValue $object + * @param SMWPrintRequest $printRequest + * + * @return string + */ + private function handleResultProperty( SMWDataValue $object, SMWPrintRequest $printRequest ) { + if ( $this->hasTemplate() ) { + if ( $object instanceof SMWWikiPageValue ) { + return $object->getTitle()->getPrefixedText(); + } + + return $object->getLongText( SMW_OUTPUT_WIKI, null ); + } + + if ( $this->linkAbsolute ) { + $titleText = $printRequest->getText( null ); + $t = Title::newFromText($titleText , SMW_NS_PROPERTY ); + + if ($this->isHeadersShow() && $t instanceof Title && $t->exists() ) { + $propertyName = $propertyName = Html::element( + 'a', + [ 'href' => $t->getFullUrl() ], + $printRequest->getHTMLText( null ) + ); + } + else { + $propertyName = $titleText; + } + } + else { + if($this->isHeadersShow()){ + $propertyName = $printRequest->getHTMLText( smwfGetLinker() ); + }else if($this->isHeadersPlain()){ + $propertyName = $printRequest->getText(null); + } + } + + if ( $this->linkAbsolute ) { + $hasPage = $object->getTypeID() == '_wpg'; + + if ( $hasPage ) { + $t = Title::newFromText( $object->getLongText( $this->outputMode, null ), NS_MAIN ); + $hasPage = $t !== null && $t->exists(); + } + + if ( $hasPage ) { + $propertyValue = Html::element( + 'a', + [ 'href' => $t->getFullUrl() ], + $object->getLongText( $this->outputMode, null ) + ); + } + else { + $propertyValue = $object->getLongText( $this->outputMode, null ); + } + } + else { + $propertyValue = $object->getLongText( $this->outputMode, smwfGetLinker() ); + } + + return $propertyName . ( $propertyName === '' ? '' : ': ' ) . $propertyValue; + } + + + private function isHeadersShow() { + return $this->headerStyle === 'show'; + } + + private function isHeadersHide() { + return $this->headerStyle === 'hide'; + } + + private function isHeadersPlain() { + return $this->headerStyle === 'plain'; + } + + /** + * Builds a set of locations with the provided title, text and icon. + * + * @param Location[] $locations + * @param string $text + * @param string $icon + * @param array $properties + * @param Title|null $title + * + * @return Location[] + */ + private function buildLocationsList( array $locations, $text, $icon, array $properties, Title $title = null ) { + if ( !$this->hasTemplate() ) { + $text .= implode( '
', $properties ); + } + + $titleOutput = $this->getTitleOutput( $title ); + + foreach ( $locations as &$location ) { + if ( $this->hasTemplate() ) { + $segments = array_merge( + [ + $this->template, + 'title=' . $titleOutput, + 'latitude=' . $location->getCoordinates()->getLatitude(), + 'longitude=' . $location->getCoordinates()->getLongitude(), + 'userparam=' . $this->userParam + ], + $properties + ); + + $text .= $this->getParser()->recursiveTagParse( + '{{' . implode( '|', $segments ) . '}}' + ); + } + + $location->setTitle( $titleOutput ); + $location->setText( $text ); + $location->setIcon( $icon ); + } + + return $locations; + } + + /** + * @return \Parser + */ + private function getParser() { + return $GLOBALS['wgParser']; + } + + private function getTitleOutput( Title $title = null ) { + if ( $title === null ) { + return ''; + } + + return $this->hideNamespace ? $title->getText() : $title->getFullText(); + } + + /** + * Get the icon for a row. + * + * @param array $row + * + * @return string + */ + private function getLocationIcon( array $row ) { + $icon = ''; + $legendLabels = []; + + //Check for activeicon parameter + + if ( $this->shouldGetActiveIconUrlFor( $row[0]->getResultSubject()->getTitle() ) ){ + $icon = MapsMapper::getFileUrl( $this->activeIcon ); + } + + // Look for display_options field, which can be set by Semantic Compound Queries + // the location of this field changed in SMW 1.5 + $display_location = method_exists( $row[0], 'getResultSubject' ) ? $row[0]->getResultSubject() : $row[0]; + + if ( property_exists( $display_location, 'display_options' ) && is_array( $display_location->display_options ) ) { + $display_options = $display_location->display_options; + if ( array_key_exists( 'icon', $display_options ) ) { + $icon = $display_options['icon']; + + // This is somewhat of a hack - if a legend label has been set, we're getting it for every point, instead of just once per icon + if ( array_key_exists( 'legend label', $display_options ) ) { + + $legend_label = $display_options['legend label']; + + if ( ! array_key_exists( $icon, $legendLabels ) ) { + $legendLabels[$icon] = $legend_label; + } + } + } + } // Icon can be set even for regular, non-compound queries If it is, though, we have to translate the name into a URL here + elseif ( $this->icon !== '' ) { + $icon = MapsMapper::getFileUrl( $this->icon ); + } + + return $icon; + } + + private function shouldGetActiveIconUrlFor( Title $title ) { + global $wgTitle; + + return isset( $this->activeIcon ) && is_object( $wgTitle ) + && $wgTitle->equals( $title ); + } + + /** + * @param boolean $hideNamespace + */ + public function setHideNamespace( $hideNamespace ) { + $this->hideNamespace = $hideNamespace; + } + + /** + * @return boolean + */ + public function getHideNamespace() { + return $this->hideNamespace; + } + + /** + * @param string $activeIcon + */ + public function setActiveIcon( $activeIcon ) { + $this->activeIcon = $activeIcon; + } + + /** + * @return string + */ + public function getActiveIcon() { + return $this->activeIcon; + } + +} diff --git a/SemanticMaps/src/services/GoogleMaps3/ext.sm.googlemaps3ajax.js b/SemanticMaps/src/services/GoogleMaps3/ext.sm.googlemaps3ajax.js new file mode 100644 index 000000000..aa568abd2 --- /dev/null +++ b/SemanticMaps/src/services/GoogleMaps3/ext.sm.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.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/SemanticMaps/src/services/Leaflet/ext.sm.leafletajax.js b/SemanticMaps/src/services/Leaflet/ext.sm.leafletajax.js new file mode 100644 index 000000000..ebb671b9a --- /dev/null +++ b/SemanticMaps/src/services/Leaflet/ext.sm.leafletajax.js @@ -0,0 +1,44 @@ +/** + * JavaScript for Leaflet in the Semantic Maps extension. + * @see https://www.mediawiki.org/wiki/Extension:Semantic_Maps + * + * @licence GNU GPL v2+ + * @author Peter Grassberger < petertheone@gmail.com > + */ + + +(function( $, sm ) { + var ajaxRequest = null; + + var mapEvents = ['dragend', 'zoomend']; + + $( document ).ready( function() { + // todo: find a way to remove setTimeout. + setTimeout(function() { + $( window.maps.leafletList ).each( function( index, map ) { + if (!map.options.ajaxquery || !map.options.ajaxcoordproperty) { + return; + } + map.map.on( mapEvents.join( ' ' ), function() { + var bounds = map.map.getBounds(); + var query = sm.buildQueryString( + decodeURIComponent(map.options.ajaxquery.replace(/\+/g, ' ')), + map.options.ajaxcoordproperty, + bounds.getNorthEast().lat, + bounds.getNorthEast().lng, + bounds.getSouthWest().lat, + bounds.getSouthWest().lng + ); + + if ( ajaxRequest !== null ) { + ajaxRequest.abort(); + } + ajaxRequest = sm.ajaxUpdateMarker( map, query, map.options.icon ).done( function () { + map.createMarkerCluster(); + ajaxRequest = null; + } ); + } ); + } ); + }, 1000 ); + } ); +})( window.jQuery, window.sm ); diff --git a/SemanticMaps/src/services/OpenLayers/ext.sm.openlayersajax.js b/SemanticMaps/src/services/OpenLayers/ext.sm.openlayersajax.js new file mode 100644 index 000000000..f4813af3e --- /dev/null +++ b/SemanticMaps/src/services/OpenLayers/ext.sm.openlayersajax.js @@ -0,0 +1,41 @@ +/** + * JavaScript for OpenLayers 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; + + $( document ).ready( function() { + // todo: find a way to remove setTimeout. + setTimeout(function() { + $( window.maps.openlayersList ).each( function( index, map ) { + if (!map.options.ajaxquery || !map.options.ajaxcoordproperty) { + return; + } + map.map.events.register( 'moveend', map.map, function () { + var bounds = map.map.getExtent().transform(map.map.projection, map.map.displayProjection); + var query = sm.buildQueryString( + decodeURIComponent(map.options.ajaxquery.replace(/\+/g, ' ')), + map.options.ajaxcoordproperty, + bounds.top, + bounds.right, + bounds.bottom, + bounds.left + ); + + if ( ajaxRequest !== null ) { + ajaxRequest.abort(); + } + ajaxRequest = sm.ajaxUpdateMarker( map, query, map.options.icon ).done( function () { + ajaxRequest = null; + } ); + } ); + } ); + }, 500 ); + } ); +})( window.jQuery, window.sm ); diff --git a/build/travis/after_success.sh b/build/travis/after_success.sh deleted file mode 100644 index 62e3bd6c9..000000000 --- a/build/travis/after_success.sh +++ /dev/null @@ -1,14 +0,0 @@ -#! /bin/bash - -set -x - -originalDirectory=$(pwd) - -if [ "$TYPE" == "coverage" ] -then - wget https://scrutinizer-ci.com/ocular.phar - du -hs $originalDirectory/build/coverage.clover - ls -lap $originalDirectory - ls -lap $originalDirectory/build - php ocular.phar code-coverage:upload --format=php-clover $originalDirectory/build/coverage.clover -fi \ No newline at end of file diff --git a/build/travis/script.sh b/build/travis/script.sh deleted file mode 100644 index 67f3b4bca..000000000 --- a/build/travis/script.sh +++ /dev/null @@ -1,14 +0,0 @@ -#! /bin/bash - -set -x - -originalDirectory=$(pwd) - -cd ../phase3/tests/phpunit - -if [ "$TYPE" == "coverage" ] -then - php phpunit.php --group Maps -c ../../extensions/Maps/phpunit.xml.dist --coverage-clover $originalDirectory/build/coverage.clover -else - php phpunit.php --group Maps -c ../../extensions/Maps/phpunit.xml.dist -fi \ No newline at end of file diff --git a/composer.json b/composer.json index 084b8ea40..424e8cde1 100644 --- a/composer.json +++ b/composer.json @@ -6,9 +6,13 @@ "MediaWiki", "Semantic MediaWiki", "Maps", + "Semantic Maps", "Google Maps", "OpenLayers", "OSM", + "Leaflet", + "Geocode", + "Geocoding", "OpenStreetMap" ], "homepage": "https://github.com/JeroenDeDauw/Maps", @@ -27,32 +31,55 @@ "source": "https://github.com/JeroenDeDauw/Maps" }, "require": { - "php": ">=5.3.2", + "php": ">=5.6", "composer/installers": "^1.0.1", - "mediawiki/validator": "^2.0.2", - "data-values/geo": "~1.0" + "wikia/validator": "^2.2", + "data-values/geo": "~2.0", + "jeroen/file-fetcher": "~3.1|~4.0", + "jeroen/simple-cache": "~2.0" + }, + "require-dev": { + "data-values/common": "~0.3.1", + "phpunit/phpunit": "~3.7" }, "autoload": { "files" : [ "Maps.php" ], "psr-4": { - "Maps\\": "src/Maps/" + "Maps\\": "src/" }, "classmap": [ "includes/", - "Maps.hooks.php" + "Maps.hooks.php", + "SemanticMaps/src/", + "SemanticMaps/SemanticMaps.hooks.php" + ] + }, + "autoload-dev": { + "psr-4": { + "Maps\\Tests\\TestDoubles\\": "tests/TestDoubles/" + }, + "classmap": [ + "tests/Unit/Elements/", + "tests/Integration/parserhooks/ParserHookTest.php" ] }, "extra": { "branch-alias": { - "dev-master": "3.5.x-dev" + "dev-master": "4.3.x-dev" } }, + "replace": { + "mediawiki/semantic-maps": "*" + }, + "suggest": { + "mediawiki/semantic-media-wiki": "Add, edit, aggregate and visualize structured coordinate data stored with Semantic MediaWiki" + }, "config": { "process-timeout": 0 }, "scripts":{ - "phpunit": "php ../../tests/phpunit/phpunit.php -c phpunit.xml.dist" + "ci": "php ../../tests/phpunit/phpunit.php -c phpunit.xml.dist" } } diff --git a/i18n/ast.json b/i18n/ast.json index 184f0ee12..b58792fe8 100644 --- a/i18n/ast.json +++ b/i18n/ast.json @@ -39,7 +39,6 @@ "maps-layer-type-supported-by": "Esta triba de capa pue emplegase {{PLURAL:$2|namái col serviciu de mapes $1|con estos servicios de mapes: $1}}.", "maps-coordinates-description": "Asociador del analizador pa dar formatu a les coordenaes, dende y a cualesquiera de los formatos sofitaos.", "maps-displaymap-description": "Amosar mapes xeográficos ensin dengún marcador definíu na wiki nellos.", - "maps-displaypoint-description": "Amosar mapes xeográficos con unu o más marcadores definíos na wiki nellos.", "maps-distance-description": "Convertir una distancia usando cierta unidá sofitada al equivalente utilizando otra unidá.", "maps-finddestination-description": "Alcontrar un destín dende un puntu de partida (que pue tar en cualesquiera de los formatos compatibles), una orientación inicial y una distancia.", "maps-geocode-description": "Activa la xeocodificación de direiciones; n'otres pallabres, tresformar llugares lleíbles por humanos en conxuntos de coordenaes. Hai sofitu pa dellos servicios de xeocodificación, que nun tienen de confundise con servicios de mapes.", @@ -93,7 +92,6 @@ "maps-displaymap-par-wmsoverlay": "Usar una superposición WMS", "maps-fullscreen-button": "Conmutar pantalla completa", "maps-fullscreen-button-tooltip": "Ver el mapa como pantalla completa o incrustáu.", - "maps-googlemaps3-par-enable-fullscreen": "Activar el botón de pantalla completa", "validation-error-invalid-location": "El parámetru $1 tien de ser un llugar válidu.", "validation-error-invalid-locations": "El parámetru $1 tien de ser un o más llugares válidos.", "validation-error-invalid-width": "El parámetru $1 tien de ser un anchor válidu.", @@ -119,6 +117,11 @@ "maps_map_cannot_be_displayed": "Nun se pue amosar el mapa.", "maps-geocoder-not-available": "La carauterística de xeocodificación de mapes nun ta disponible. Nun se pue xeocodificar la to situación.", "maps_leaflet": "Leaflet", + "maps-leaflet-par-defzoom": "Permite configurar el nivel predetermináu d'ampliación del mapa.", + "maps-leaflet-par-layer": "La capa que va apaecer cuando se cargue'l 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_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.", @@ -129,9 +132,11 @@ "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-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.", + "maps-par-markercluster": "Permite fusionar múltiples marcadores cercanos nun solu marcador", "maps-googlemaps3-incompatbrowser": "El to navegador nun ye compatible con Google Maps v3.", "maps-googlemaps3-par-imageoverlays": "Permite amestar una imaxe p'amosala nel llugar especificáu del mapa.", - "maps-googlemaps3-par-markercluster": "Permite fusionar múltiples marcadores cercanos nun solu marcador", "maps-googlemaps3-par-type": "El tipu de mapa a amosar inicialmente.", "maps-googlemaps3-par-types": "Los tipos de mapa que tarán disponibles al traviés del control de tipu.", "maps-googlemaps3-par-layers": "Capes especiales a cargar nel mapa.", @@ -139,17 +144,20 @@ "maps-googlemaps3-par-zoomstyle": "El estilu del control de zoom.", "maps-googlemaps3-par-typestyle": "El estilo del control de tipu.", "maps-googlemaps3-par-autoinfowindows": "Abrir automáticamente toles ventanes d'información dempués de que se cargue la páxina.", - "maps-googlemaps3-par-kml": "Ficheros KML a cargar nel mapa.", "maps-googlemaps3-par-gkml": "Ficheros KML agospiaos por Google a cargar nel mapa.", "maps-googlemaps3-par-fusiontables": "IDs de les tables de Google Fusion que tienen de cargase nel mapa.", "maps-googlemaps3-par-tilt": "Inclinación del mapa al usar Google Maps.", "maps-googlemaps3-par-kmlrezoom": "Axustar el nivel de zoom dempués de que carguen les capes KML.", "maps-googlemaps3-par-poi": "Amosar puntos d'interés.", + "maps-googlemaps3-par-clustergridsize": "La midida de rexa d'un grupu en píxeles.", + "maps-par-clustermaxzoom": "El nivel máximu d'ampliación nel que pueden esistir grupos.", + "maps-par-clusterzoomonclick": "Si'l comportamientu predetermináu al facer click nun grupu ye amplialu.", + "maps-par-maxclusterradius": "El radiu máximu que cubrirá un cluster.", + "maps-googlemaps3-par-clusteraveragecenter": "Si'l centru de cada grupu tien de ser la media de tolos marcadores del grupu.", + "maps-googlemaps3-par-clusterminsize": "El númberu mínimu de marcadores que tán nun grupu primero que se despinten los marcadores y s'amuese un recuentu.", "maps-openlayers-par-controls": "Controles a poner nel mapa.", "maps-openlayers-par-layers": "Les capes que tarán disponibles nel selector de capes. La primera capa s'amosará al cargar el mapa.", "maps-openlayers-par-overlays": "Capes superpuestes que tarán disponibles nel selector de capes. Eses capes veránse enriba d'una capa normal, de mou asemeyáu a un marcador.", - "maps-osm-par-thumbs": "Amosar miniatures", - "maps-osm-par-photos": "Amosar les fotos", "mapeditor": "Editor de mapes", "specialpages-group-maps": "Mapes", "mapeditor-parser-error": "Hebo un error al analizar los metadatos. Inorando la entrada d'usuariu.", @@ -185,5 +193,42 @@ "mapeditor-imageoverlay-button": "Amestar superposición d'imaxe", "mapeditor-form-field-image": "Imaxe", "mapeditor-imageoverlay-title": "Detalles de superposición d'imaxe", - "mapeditor-form-field-visitedicon": "Iconu visitáu" + "mapeditor-form-field-visitedicon": "Iconu visitáu", + "semanticmaps-unrecognizeddistance": "El valor $1 nun ye una distancia válida.", + "semanticmaps-kml-link": "Ver el ficheru KML", + "semanticmaps-default-kml-pagelink": "Ver la páxina \"$1\"", + "semanticmaps-latitude": "Llatitú: $1", + "semanticmaps-longitude": "Llonxitú: $1", + "semanticmaps-altitude": "Altitú: $1", + "semanticmaps-forminput-locations": "Llugares", + "semanticmaps-par-staticlocations": "Llista de llugares p'amestar al mapa xunto colos datos consultaos. Como con display_points, pues amestar un títulu, una descripción y un iconu pa cada llugar usando'l signu \"~\" como separador.", + "semanticmaps-par-showtitle": "Amosar o non un títulu na ventana d'información del marcador. De vezu, desactivalo ye útil cuando s'utiliza una plantía pa dar formatu al conteníu de la ventana d'información.", + "semanticmaps-par-hidenamespace": "Amosar el títulu del espaciu de nomes na ventana d'información del marcador.", + "semanticmaps-par-centre": "El centru del mapa. Cuando nun se proporciona, el mapa escoyerá automáticamente'l meyor centru p'amosar tolos marcadores del mapa.", + "semanticmaps-par-template": "Una plantía que s'utiliza pa dar formatu al conteníu de la ventana d'información.", + "semanticmaps-par-geocodecontrol": "Amosar el control de xeocodificación.", + "semanticmaps-par-activeicon": "Iconu a amosar en llugar del marcador predetermináu, cuando la páxina activa ye igual al resultáu de la consulta", + "semanticmaps-par-pagelabel": "Cuando se pon a \"si\", tolos marcadores tendrán una \"inlineLabel\" con un enllaz a la páxina que contien les coordenaes del marcador", + "semanticmaps-par-ajaxcoordproperty": "Nome de la propiedá de coordenaes que s'usa pa construir la consulta ajax.", + "semanticmaps-par-ajaxquery": "Una segunda consulta que s'unvia con ajax para recuperar otres coordenaes más.", + "semanticmaps-par-userparam": "Un valor pasáu en cada llamada de plantía,si s'usa una plantía", + "semanticmaps-kml-text": "El testu asociáu con cada páxina. Sustituyíu poles otres propiedaes consultaes, si esisten.", + "semanticmaps-kml-title": "El títulu predetermináu pa los resultaos", + "semanticmaps-kml-linkabsolute": "Si los títulos tienen de ser absolutos (lo contrario de relativos)", + "semanticmaps-kml-pagelinktext": "El testu a usar pa los enllaces a la páxina, onde \"$1\" se sustituye pol títulu de la páxina", + "semanticmaps-shapes-improperformat": "Formatu incorreutu de $1. Por favor, consulta la documentación sobre formatos", + "semanticmaps-shapes-missingshape": "Nun s'alcontraron formes pa $1. Por favor, consulta la documentación de les formes disponibles", + "validator-type-mapscircle": "Círculu xeográficu", + "validator-type-mapscircle-list": "Llista de círculos", + "validator-type-mapsimageoverlay": "Superposición d'imaxen", + "validator-type-mapsimageoverlay-list": "Lista de superposiciones d'imaxen", + "validator-type-mapsline": "Llinia xeográfica", + "validator-type-mapsline-list": "Llista de llinies", + "validator-type-mapslocation": "Allugamientu xeográficu", + "validator-type-mapslocation-list": "Llista d'allugamientos", + "validator-type-mapsrectangle": "Rectángulu xeográficu", + "validator-type-mapsrectangle-list": "Llista de rectángulos", + "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" } diff --git a/i18n/be-tarask.json b/i18n/be-tarask.json index 3da66ff08..60eb49c56 100644 --- a/i18n/be-tarask.json +++ b/i18n/be-tarask.json @@ -5,7 +5,8 @@ "Jim-by", "Wizardist", "Red Winged Duck", - "Macofe" + "Macofe", + "Renessaince" ] }, "maps-desc": "Забясьпечвае магчымасьць убудаваньня дынамічных мапаў у вікі-старонкі, геаграфічнага кадаваньня адрасоў і іншыя геаграфічныя апэрацыі", @@ -30,7 +31,6 @@ "maps-layer-type-supported-by": "Гэты тып слою можа быць выкарыстаны толькі з {{PLURAL:$2|1=сэрвісам мапаў $1|сэрвісамі мапаў: $1}}.", "maps-coordinates-description": "Перахопнік парсэру для фарматаваньня каардынатаў, з і ў любыя фарматы, якія падтрымліваюцца.", "maps-displaymap-description": "Паказвае геаграфічныя мапы без аніякіх вікі-пазнакаў на іх.", - "maps-displaypoint-description": "Паказвае геаграфічныя мапы з адной ці болей вікі-пазнакамі на іх.", "maps-distance-description": "Канвэртуе адлегласьць выкарыстоўваючы адзінкі, якія падтрымліваюцца, ў іх эквівалент, выкарыстоўваючы іншыя адзінкі.", "maps-finddestination-description": "Знаходзіць пункт прызначэньня з пададзенага пачатковага пункту (можа быць у любым фармаце, якія падтрымліваюцца), пачатковага напрамку і адлегласьці.", "maps-geocode-description": "Уключае геаграфічную кадыроўку адрасу, іншымі словамі, пераўтварае чытальнае для чалавека знаходжаньне ў набор каардынатаў. Тут ёсьць падтрымка для некалькіх сэрвісаў геаграфічнай кадыроўкі, якія не павінны быць блытаныя з сэрвісамі мапаў.", @@ -102,6 +102,7 @@ "maps-par-zoom": "Маштаб мапы. Для мапаў з пазначэньнямі маштаб будзе такім, пры якім яшчэ будуць паказвацца ўсе пазначэньні.", "maps-par-width": "Дазваляе наладжваць шырыню мапы. Па змоўчваньні піксэлі выкарыстоўваюцца як адзінкі вымярэньня, але Вы можаце непасрэдна вызначыць адну з гэтых адзінак вымярэньня: px, ex, em, %.", "maps-par-height": "Дазваляе наладжваць вышыню мапы. Па змоўчваньні піксэлі выкарыстоўваюцца як адзінкі вымярэньня, але Вы можаце непасрэдна вызначыць адну з гэтых адзінак вымярэньня: px, ex, em, %.", + "maps-par-kml": "KML-файлы для загрузкі ў мапу.", "maps-googlemaps3-incompatbrowser": "Ваш браўзэр не сумяшчальны з Google Maps v3.", "maps-googlemaps3-par-type": "Тып мапы, які будзе паказвацца ў пачатку.", "maps-googlemaps3-par-types": "Тыпы мапаў, якія будуць даступныя праз элемэнт кіраваньня тыпамі.", @@ -110,7 +111,6 @@ "maps-googlemaps3-par-zoomstyle": "Стыль элемэнта кіраваньня маштабам.", "maps-googlemaps3-par-typestyle": "Стыль элемэнта кіраваньня тыпамі.", "maps-googlemaps3-par-autoinfowindows": "Аўтаматычна адкрываць ўсе інфармацыйныя вокны, пасьля таго, як старонка была загружаная.", - "maps-googlemaps3-par-kml": "KML-файлы для загрузкі ў мапу.", "maps-googlemaps3-par-gkml": "Файлы KML разьмешчаныя на Google для загрузкі на мапу.", "maps-googlemaps3-par-fusiontables": "Ідэнтыфікатары табліцаў Google Fusion, якія павінны быць загружаныя ў мапу.", "maps-googlemaps3-par-tilt": "Вугал нахілу мапы, падчас выкарыстаньня Google Maps.", @@ -118,8 +118,6 @@ "maps-googlemaps3-par-poi": "Паказаць выбітныя мясьціны.", "maps-openlayers-par-controls": "Элемэнты кіраваньня, якія будуць разьмешчаныя на мапе.", "maps-openlayers-par-layers": "Слаі, якія будуць даступныя падчас выбару слаёў. Першы слой будзе паказаны пасьля загрузкі мапы.", - "maps-osm-par-thumbs": "Паказваць мініятуры", - "maps-osm-par-photos": "Паказваць выявы", "mapeditor": "Рэдактар мап", "specialpages-group-maps": "Мапы", "mapeditor-parser-error": "У час разбору мэтазьвестак адбылася памылка. Ігнаруем уведзеныя зьвесткі.", @@ -155,5 +153,14 @@ "mapeditor-imageoverlay-button": "Накласьці выяву", "mapeditor-form-field-image": "Выява", "mapeditor-imageoverlay-title": "Накладаньне выявы", - "mapeditor-form-field-visitedicon": "Значак для наведанага" + "mapeditor-form-field-visitedicon": "Значак для наведанага", + "semanticmaps-unrecognizeddistance": "Значэньне $1 — няслушная адлегласьць.", + "semanticmaps-kml-link": "Паказаць KML-файл", + "semanticmaps-default-kml-pagelink": "Паказаць старонку $1", + "semanticmaps-forminput-locations": "Месцы", + "semanticmaps-par-staticlocations": "Сьпіс месцазнаходжаньняў для даданьня на мапу разам з запытанымі зьвесткамі. Напрыклад, разам з «display_points», Вы можаце дадаць назву, апісаньне і мініятуру для месцазнаходжаньня з дапамогай сымбаля «~» у якасьці разьдзяляльніка.", + "semanticmaps-par-showtitle": "Паказваць назву ў акне інфармацыі пра маркер ці не. Адключэньне гэтай функцыі часта карыснае падчас выкарыстаньня шаблёну для фарматаваньня зьместу акна інфармацыі.", + "semanticmaps-par-centre": "Цэнтар мапы. Калі ён не пададзены, мапа будзе аўтаматычна выбіраць аптымальны цэнтар для паказу ўсіх маркераў.", + "semanticmaps-par-template": "Шаблён для фарматаваньня зьместу акна інфармацыі.", + "semanticmaps-par-geocodecontrol": "Паказаць элемэнты кіраваньня геаграфічным кадаваньнем." } diff --git a/i18n/bn.json b/i18n/bn.json index 32c2b6697..793e43228 100644 --- a/i18n/bn.json +++ b/i18n/bn.json @@ -2,29 +2,110 @@ "@metadata": { "authors": [ "Tauhid16", - "Wikitanvir" + "Wikitanvir", + "Aftabuzzaman", + "আজিজ" ] }, + "right-geocode": "জিওকোড", + "action-geocode": "এই উইকিতে জিওকোডিং করুন", "maps_map": "মানচিত্র", "maps-loading-map": "মানচিত্র লোড করা হচ্ছে...", "maps-load-failed": "মানচিত্র লোড করা সম্ভব হয়নি!", + "maps-markers": "চিহ্নিতকারী", + "maps-copycoords-prompt": "CTRL+C, ENTER", + "maps-searchmarkers-text": "ছাকনি চিহ্নিতকারী", "maps-others": "অন্যান্য", - "maps-ns-layer": "পর্ব", - "maps-ns-layer-talk": "পর্ব আলোচনা", + "maps-kml-parsing-failed": "একটি অথবা আরও বেশি KML ফাইল পদান্বয় ব্যর্থ। সাধারণত এটি আহরণ ব্যর্থতা বা ত্রুটিপূর্ণ XML-এর কারণে ঘটে।", + "maps-ns-layer": "স্তর", + "maps-ns-layer-talk": "স্তর আলোচনা", "maps-layer-property": "সম্পত্তি", "maps-layer-value": "মান", "maps-layer-errors": "ত্রুটি", + "maps-layerdef-invalid": "অবৈধ {{PLURAL:$1|সংজ্ঞা}}", + "maps-layerdef-invalid-fatal": "ধ্বংসাত্মক অবৈধ সংজ্ঞা", + "maps-layerdef-wrong-namespace": "স্তর সজ্ঞা শুধুমাত্র \"$1\" নামস্থান পাতাসমূহে বৈধ", + "maps-layerdef-equal-layer-name": "স্তরের নামসমূহ একই স্তর পৃষ্ঠার মধ্যে অনন্য হওয়া আবশ্যক। \"$1\" ইতিমধ্যে আরেকটি স্তর দ্বারা ব্যবহৃত হয়েছে।", + "maps-layerpage-usage": "\"$1\" স্তর ব্যবহৃত মানচিত্র সহ পৃষ্ঠাসমূহ", + "maps-layerpage-nousage": "এই মুহূর্তে কোন পৃষ্ঠায় এই স্তরটি ব্যবহার হয়নি।", + "maps-error-invalid-layertype": "সেখানে \"$1\" ধরনের কোন স্তর নেই। শুধু {{PLURAL:$3|এই ধরনের}} স্তর সমর্থিত হবে: $2", + "maps-error-no-layertype": "আপনাকে স্তরের ধরন নির্দিষ্ট করতে হবে। শুধু {{PLURAL:$2|এই ধরনের}} স্তর সমর্থিত: $1", + "validation-error-invalid-layer": "\"$1\" পরামিতি অবশ্যই বৈধ স্তর হতে হবে।", + "validation-error-invalid-layers": "\"$1\" পরামিতি অবশ্যই এক বা একাধিক বৈধ স্তর হতে হবে।", + "validation-error-no-non-numeric": "\"$1\" পরামিতি অবশ্যই অ-সাংখ্যিক স্ট্রিং হতে হবে।", + "validation-error-no-non-numerics": "\"$1\" পরামিতি অবশ্যই এক বা একাধিক অ-সাংখ্যিক স্ট্রিং হতে হবে।", "maps-layer-of-type": "\"$1\" ধরণের লেয়ার", + "maps-layer-of-type-and-name": "\"$2\" স্তরটি \"$1\" ধরণের", + "maps-finddestination-par-location": "প্রারম্ভিক অবস্থান", + "maps-finddestination-par-distance": "ভ্রমণ করার ব্যবধান", "validation-error-invalid-location": "স্থিতিমাপ \"$1\" একটি বৈধ অবস্থান হতে হবে।", + "validation-error-invalid-locations": "\"$1\" পরামিতি অবশ্যই এক বা একাধিক বৈধ অবস্থান হতে হবে।", + "validation-error-invalid-width": "\"$1\" পরামিতি অবশ্যই বৈধ প্রস্থ বিশিষ্ঠ হতে হবে।", + "validation-error-invalid-height": "\"$1\" পরামিতি অবশ্যই বৈধ উচ্চতা বিশিষ্ঠ হতে হবে।", + "validation-error-invalid-distance": "\"$1\" পরামিতি অবশ্যই বৈধ দূরত্ব বিশিষ্ঠ হতে হবে।", + "validation-error-invalid-distances": "\"$1\" পরামিতি অবশ্যই এক বা একাধিক বৈধ দূরত্ব বিশিষ্ঠ হতে হবে।", + "validation-error-invalid-image": "\"$1\" পরামিতি অবশ্যই বৈধ চিত্র যুক্ত হতে হবে।", + "validation-error-invalid-images": "\"$1\" পরামিতি অবশ্যই এক বা একাধিক বৈধ চিত্র যুক্ত হতে হবে।", + "validation-error-invalid-goverlay": "\"$1\" পরামিতি অবশ্যই বৈধ আচ্ছদন হতে হবে।", + "validation-error-invalid-goverlays": "\"$1\" পরামিতি অবশ্যই এক বা একাধিক বৈধ আচ্ছদন হতে হবে।", "maps-abb-north": "উ", "maps-abb-east": "পূ", "maps-abb-south": "দ", "maps-abb-west": "প", "maps-latitude": "অক্ষাংশ:", "maps-longitude": "দ্রাঘিমাংশ:", + "maps_coordinates_missing": "মানচিত্রের জন্য স্থানাঙ্ক প্রদান করা হয়নি।", "maps_map_cannot_be_displayed": "এই মানচিত্রটি প্রদর্শন করা সম্ভব নয়।", + "maps-geocoder-not-available": "মানচিত্রে জিওকোডিং বৈশিষ্টটি উপলব্ধ নয়। আপনার অবস্থান জিওকোডেড করা যাবে না।", + "maps_leaflet": "প্রচারপত্র", "maps_click_to_activate": "মানচিত্র চালু করতে ক্লিক করুন", "maps_centred_on": "মানচিত্র $1, $2-এ কেন্দ্রীভূত।", - "maps-osm-par-thumbs": "থাম্ব দেখাও", - "maps-osm-par-photos": "ছবি দেখাও" + "maps-googlemaps3-par-poi": "আগ্রহের বিষয়সমূহ দেখাও।", + "mapeditor": "মানচিত্র সম্পাদক", + "specialpages-group-maps": "মানচিত্র", + "mapeditor-parser-error": "মেটাডেটা বিশ্লেষণ করার সময় একটি ত্রুটি ঘটেছে। ব্যবহারকারীর ইনপুট উপেক্ষা করা হয়েছে।", + "mapeditor-none-text": "কিছু নয়", + "mapeditor-done-button": "সম্পন্ন", + "mapeditor-remove-button": "সরান", + "mapeditor-import-button2": "আমদানি", + "mapeditor-export-button": "উইকি কোডে রপ্তানি করুন", + "mapeditor-import-button": "উইকি কোড থেকে আমদানি করুন", + "mapeditor-select-button": "এই বহুভুজ নির্বাচন করুন", + "mapeditor-mapparam-button": "মানচিত্রের পরামিতিগুলো সম্পাদনা করুন", + "mapeditor-clear-button": "মানচিত্র সাফ করুন", + "mapeditor-code-title": "উইকি কোড", + "mapeditor-import-title": "উইকি কোড আমদানি করুন", + "mapeditor-form-title": "তথ্য সংশোধন করুন", + "mapeditor-link-title-switcher-link-text": "সংযোগ", + "mapeditor-form-field-title": "শিরোনাম", + "mapeditor-form-field-text": "লেখা", + "mapeditor-form-field-link": "সংযোগ", + "mapeditor-form-field-icon": "আইকন", + "mapeditor-form-field-group": "দল", + "mapeditor-form-field-strokecolor": "রেখার রং", + "mapeditor-form-field-strokeopacity": "রেখার অস্পষ্টতা", + "mapeditor-form-field-strokeweight": "রেখার প্রস্থ", + "mapeditor-mapparam-title": "মানচিত্রের পরামিতিগুলো সম্পাদনা করুন", + "mapeditor-mapparam-defoption": "-পরামিতি বাছাই করুন-", + "mapeditor-imageoverlay-button": "চিত্র আচ্ছাদন যোগ করুন", + "mapeditor-form-field-image": "চিত্র", + "mapeditor-imageoverlay-title": "চিত্র আচ্ছদনের খুঁটিনাটি", + "semanticmaps-kml-link": "কেএমএল ফাইল দেখাও", + "semanticmaps-default-kml-pagelink": "$1 পাতা প্রদর্শন করো", + "semanticmaps-latitude": "অক্ষাংশ: $1", + "semanticmaps-longitude": "দ্রাঘিমাংশ: $1", + "semanticmaps-altitude": "উচ্চতা: $1", + "semanticmaps-forminput-locations": "অবস্থান", + "validator-type-mapscircle": "ভৌগলিক বৃত্ত", + "validator-type-mapscircle-list": "বৃত্তের তালিকা", + "validator-type-mapsimageoverlay": "চিত্র প্রতিস্থাপক", + "validator-type-mapsimageoverlay-list": "চিত্র প্রতিস্থাপকের তালিকা", + "validator-type-mapsline": "ভৌগলিক রেখা", + "validator-type-mapsline-list": "রেখার তালিকা", + "validator-type-mapslocation": "ভৌগলিক অবস্থান", + "validator-type-mapslocation-list": "অবস্থানগুলির তালিকা", + "validator-type-mapsrectangle": "ভৌগলিক আয়তক্ষেত্র", + "validator-type-mapsrectangle-list": "আয়তক্ষেত্রের তালিকা", + "validator-type-mapspolygon": "ভৌগলিক বহুভুজ", + "validator-type-mapspolygon-list": "ভৌগলিক বহুভুজের তালিকা" } diff --git a/i18n/br.json b/i18n/br.json index accde547a..3035ad900 100644 --- a/i18n/br.json +++ b/i18n/br.json @@ -47,7 +47,6 @@ "maps-displaymap-par-rectangles": "Hirgarrezennoù da ziskwel", "maps-displaymap-par-static": "Lakaat ar gartenn da vezañ statek", "maps-fullscreen-button": "Gweredekaat ar skramm leun", - "maps-googlemaps3-par-enable-fullscreen": "Gweredekaat ar bouton skramm leun", "validation-error-invalid-location": "Rankout a ra an arventenn $1 bezañ evit ul lec'hiadur reizh.", "validation-error-invalid-locations": "Rankout a ra an arventenn $1 bezañ evit ul lec'hiadur reizh, da nebeutañ.", "validation-error-invalid-width": "Rankout a ra an arventenn $1 bezañ evit ul ledander reizh.", @@ -72,8 +71,11 @@ "maps_unrecognized_coords_for": "N'eo ket bet anavezet an {{PLURAL:$2|daveenn|daveennoù}} da-heul ha {{PLURAL:$2|n'eo|n'int}} ket bet lakaet war ar gartenn :\n$1", "maps_map_cannot_be_displayed": "N'hall ket ar gartenn bezañ diskwelet.", "maps-geocoder-not-available": "N'haller ket ober gant arc'hwel geokodañ ar c'hartennoù. N'haller ket geokodañ ho lec'hiadur.", + "maps_leaflet": "Plegfollenn", "maps_click_to_activate": "Klikañ evit gweredekaat ar gartenn", "maps_centred_on": "Kartenn kreizet war $1, $2.", + "maps-par-enable-fullscreen": "Gweredekaat ar bouton skramm leun", + "maps-par-kml": "Restroù KML da gargañ war ar gartenn.", "maps-googlemaps3-incompatbrowser": "N'eo ket kenglotus ho merdeer gant Google Maps v3.", "maps-googlemaps3-par-type": "Ar seurt kartenn da ziskouez da gentañ.", "maps-googlemaps3-par-layers": "Gwiskadoù arbennik da gargañ war ar gartenn.", @@ -81,11 +83,8 @@ "maps-googlemaps3-par-zoomstyle": "Stil kontroll ar zoum.", "maps-googlemaps3-par-typestyle": "Stil kontroll ar seurt.", "maps-googlemaps3-par-autoinfowindows": "Digeriñ an holl brenestroù diouzhtu goude bezañ karget ar bajenn.", - "maps-googlemaps3-par-kml": "Restroù KML da gargañ war ar gartenn.", "maps-googlemaps3-par-poi": "Diskouez al lec'hioù dudius.", "maps-openlayers-par-controls": "Ar c'hontrolloù da lakaat war ar gartenn.", - "maps-osm-par-thumbs": "Diskouez ar munudoù", - "maps-osm-par-photos": "Diskouez ar skeudennoù", "mapeditor": "Aozer kartennoù", "specialpages-group-maps": "Kartennoù", "mapeditor-none-text": "Hini ebet", @@ -109,5 +108,22 @@ "mapeditor-mapparam-title": "Kemmañ arventennoù ar gartenn", "mapeditor-mapparam-defoption": "-Diuzañ un arventenn-", "mapeditor-form-field-image": "Skeudenn", - "mapeditor-form-field-visitedicon": "Arlun gweladennet" + "mapeditor-form-field-visitedicon": "Arlun gweladennet", + "semanticmaps-unrecognizeddistance": "An talvoud $1 n'eo ket un hed reizh anezhañ.", + "semanticmaps-kml-link": "Gwelet ar restr KML", + "semanticmaps-default-kml-pagelink": "Gwelet ar pennad $1", + "semanticmaps-latitude": "Ledred : $1", + "semanticmaps-longitude": "Hedred : $1", + "semanticmaps-altitude": "Uhelder : $1", + "semanticmaps-forminput-locations": "Lec'hiadurioù", + "semanticmaps-par-template": "Ur patrom d'ober gantañ da furmadiñ boued ar prenestr titouriñ.", + "semanticmaps-kml-title": "Titl dre ziouer evit an disoc'hoù", + "validator-type-mapscircle": "Kelc'h douaroniel", + "validator-type-mapscircle-list": "Roll kelc'hioù", + "validator-type-mapsline": "Linenn zouaroniel", + "validator-type-mapsline-list": "Roll linennoù", + "validator-type-mapslocation": "Lec'hiadur douaroniel", + "validator-type-mapslocation-list": "Roll lec'hiadurioù", + "validator-type-mapsrectangle": "Higarrezenn zouaroniel", + "validator-type-mapsrectangle-list": "Roll hirgarrezennoù" } diff --git a/i18n/cs.json b/i18n/cs.json index 861dc705e..29bc084ea 100644 --- a/i18n/cs.json +++ b/i18n/cs.json @@ -2,7 +2,9 @@ "@metadata": { "authors": [ "Mormegil", - "Vks" + "Vks", + "Utar", + "XenoPheX" ] }, "right-geocode": "Geokódování", @@ -40,8 +42,6 @@ "maps_map_cannot_be_displayed": "Nelze zobrazit mapu.", "maps_click_to_activate": "Mapu aktivujete kliknutím", "maps-googlemaps3-par-poi": "Zobrazit body zájmu.", - "maps-osm-par-thumbs": "Zobrazit náhledy", - "maps-osm-par-photos": "Zobrazit fotografie", "mapeditor": "Editor map", "specialpages-group-maps": "Mapy", "mapeditor-none-text": "Nic", @@ -69,5 +69,28 @@ "mapeditor-form-field-fillopcaity": "Krytí výplně", "mapeditor-form-field-showonhover": "Zobrazit pouze pod myší", "mapeditor-form-field-image": "Obrázek", - "mapeditor-form-field-visitedicon": "Navštívená ikona" + "mapeditor-form-field-visitedicon": "Navštívená ikona", + "semanticmaps-desc": "Poskytuje možnost zobrazit a upravovat data souřadnic uložená rozšířením Semantic MediaWiki ([https://mapping.referata.com/wiki/Examples demos]).", + "semanticmaps-unrecognizeddistance": "Hodnota $1 není platná vzdálenost.", + "semanticmaps-kml-link": "Zobrazit soubor KML", + "semanticmaps-default-kml-pagelink": "Zobrazit stránku $1", + "semanticmaps-loading-forminput": "Načítání mapy ze vstupu…", + "semanticmaps_lookupcoordinates": "Vyhledat souřadnice", + "semanticmaps_enteraddresshere": "Sem zadejte adresu", + "semanticmaps-updatemap": "Aktualizovat mapu", + "semanticmaps-forminput-remove": "Odebrat", + "semanticmaps-forminput-add": "Přidat", + "semanticmaps-latitude": "Z. šířka: $1", + "semanticmaps-longitude": "Z. délka: $1", + "semanticmaps-altitude": "Nadm. výška: $1", + "semanticmaps-forminput-locations": "Místa", + "semanticmaps-par-staticlocations": "Seznam míst, která se přidají do mapy spolu s dotazovanými daty. Podobně jako u display_points můžete každé místo doplnit o titulek, popis a ikonu, za použití tildy „~“ jako oddělovače.", + "semanticmaps-par-showtitle": "Zobrazovat název v info okně značky či ne. Vypnutí je často užitečné, pokud je obsah informačního okna formátován pomocí šablony.", + "semanticmaps-par-centre": "Střed mapy. Není-li specifikován, mapa automaticky vybere optimální střed tak, aby byly zobrazeny všechny značky na ní.", + "semanticmaps-par-template": "Šablona formátování obsahu informačního okna", + "semanticmaps-par-geocodecontrol": "Zobrazit ovladač geocodingu.", + "semanticmaps-kml-text": "Text je přidružený ke každé stránce. Je přepsán dodatečnými dotazovanými vlastnostmi, jsou-li nějaké.", + "semanticmaps-kml-title": "Výchozí titulek pro výsledky", + "semanticmaps-kml-linkabsolute": "Mají být odkazy absolutní či ne (tj. relativní)", + "semanticmaps-kml-pagelinktext": "Text, který bude použit pro odkazy na stránku, ve kterém bude $1 nahrazeno názvem stránky" } diff --git a/i18n/de.json b/i18n/de.json index 1c044e55a..4ba987932 100644 --- a/i18n/de.json +++ b/i18n/de.json @@ -7,12 +7,13 @@ "Kghbln", "Metalhead64", "Purodha", - "The Evil IP address" + "The Evil IP address", + "Umherirrender" ] }, "maps-desc": "Ermöglicht das Einbinden dynamischer Karten, die Georeferenzierung von Adressen und andere geographische Operationen", "right-geocode": "Georeferenzieren", - "action-geocode": "Geocoding auf diesem Wiki auszuführen", + "action-geocode": "Georeferenzierungen auf diesem Wiki auszuführen", "maps_map": "Karte", "maps-tracking-category": "Seiten mit einer von der Maps-Erweiterung gerenderten Karte", "maps-loading-map": "Die Karte wird geladen …", @@ -21,22 +22,22 @@ "maps-copycoords-prompt": "STRG+C, ENTER", "maps-searchmarkers-text": "Markierungen filtern", "maps-others": "andere", - "maps-kml-parsing-failed": "Das Parsen einer oder mehrerer KML-Dateien ist fehlgeschlagen, normalerweise aufgrund eines Abfragefehlers oder aufgrund von fehlerhaftem XML.", + "maps-kml-parsing-failed": "Das Parsen einer oder mehrerer KML-Dateien ist fehlgeschlagen. Dies geschieht normalerweise aufgrund eines Abfragefehlers oder aufgrund von fehlerhaftem XML.", "maps-ns-layer": "Ebene", "maps-ns-layer-talk": "Ebene Diskussion", "maps-layer-property": "Attribut", "maps-layer-value": "Wert", "maps-layer-errors": "Fehler", "maps-layerdef-invalid": "Ungültige {{PLURAL:$1|Definition|Definitionen}}", - "maps-layerdef-invalid-fatal": "Schwerwiegende ungültige Definition", - "maps-layerdef-wrong-namespace": "Ebenendefinitionen sind nur gültig auf Seiten des Namensraums „$1“", - "maps-layerdef-equal-layer-name": "Ebenennamen müssen innerhalb der gleichen Ebenenseite einmalig sein. „$1“ wird bereits von einer anderen Ebene verwendet.", - "maps-layerpage-usage": "Wikiseiten mit Karten die die Ebene „$1“ verwenden", - "maps-layerpage-nousage": "Keine Seite verwendet diese Ebene momentan.", + "maps-layerdef-invalid-fatal": "Die Definition ist ungültig und führt zu einem schwerwiegenden Fehler.", + "maps-layerdef-wrong-namespace": "Ebenendefinitionen sind nur auf Seiten des Namensraums „$1“ gültig", + "maps-layerdef-equal-layer-name": "Ebenennamen müssen auf der gleichen Ebenenseite einmalig sein. „$1“ wird bereits von einer anderen Ebene verwendet.", + "maps-layerpage-usage": "Seiten mit Karten, die die Ebene „$1“ verwenden", + "maps-layerpage-nousage": "Keine Seite verwendet momentan diese Ebene.", "maps-error-invalid-layertype": "Es gibt keine Ebenen des Typs „$1“. Nur {{PLURAL:$3|dieser Typ wird|diese Typen werden}} unterstützt: $2", "maps-error-no-layertype": "Der Ebenentyp muss angegeben werden. Nur {{PLURAL:$2|dieser Typ wird|diese Typen werden}} unterstützt: $1", - "validation-error-invalid-layer": "Parameter „$1“ muss einer gültigen Ebene entsprechen.", - "validation-error-invalid-layers": "Parameter „$1“ muss einer oder mehreren gültigen Ebenen entsprechen.", + "validation-error-invalid-layer": "Der Parameter „$1“ muss einer gültigen Ebene entsprechen.", + "validation-error-invalid-layers": "Der Parameter „$1“ muss einer oder mehreren gültigen Ebenen entsprechen.", "validation-error-no-non-numeric": "Der Parameter „$1“ muss eine nicht-numerische Zeichenfolge sein.", "validation-error-no-non-numerics": "Der Parameter „$1“ muss eine oder mehrere nicht-numerische Zeichenfolgen sein.", "maps-layer-of-type": "Ebene des Typs „$1“", @@ -44,7 +45,6 @@ "maps-layer-type-supported-by": "Dieser Ebenentyp kann {{PLURAL:$2|nur mit dem Kartografiedienst $1 genutzt werden|mit diesen Kartografiediensten genutzt werden: $1}}.", "maps-coordinates-description": "Parserhook zur Koordinatenformatierung aus und in alle unterstützte Formate.", "maps-displaymap-description": "Geographische Karten ohne jegliche im Wiki definierte Markierungen anzeigen.", - "maps-displaypoint-description": "Geographische Karten zusammen mit einer oder mehreren im Wiki definierter Markierungen anzeigen.", "maps-distance-description": "Konvertiere die Entfernung unter Verwendung einer der unterstützten Einheiten in ihr Äquivalent einer anderen unterstützen Einheit.", "maps-finddestination-description": "Ein Ziel unter Angabe des Ausgangspunkts (kann in jedwedem unterstützten Format angegeben sein), der Peilung sowie der Entfernung ermitteln.", "maps-geocode-description": "Aktiviert das Georeferenzieren von Adressen, also deren Umwandlung in Koordinaten. Mehrere Georeferenzierungsdienste werden unterstützt, was allerdings nicht mit den Kartografiediensten zu verwechseln ist.", @@ -78,7 +78,7 @@ "maps-geodistance-par-unit": "Die Ausgabeeinheit für die Entfernung.", "maps-geodistance-par-decimals": "Die bei der Ergebnisausgabe zu verwendende Höchstzahl an Nachkommastellen.", "maps-geodistance-par-mappingservice": "Der Kartografiedienst der für alle Adressen genutzt werden soll.", - "maps-geodistance-par-geoservice": "Der Kartografiedienst mit dem diese Parserfunktion genutzt wird.\nDies kann Auswirkungen auf die Standardwerte des Georeferenzierungsdiensts haben.", + "maps-geodistance-par-geoservice": "Der Kartografiedienst, der von dieser Parserfunktion genutzt wird.\nDies kann Auswirkungen auf die Standardeinstellungswerte des Georeferenzierungsdiensts haben", "maps-displaymap-par-mappingservice": "Der Kartografiedienst, der zur Generierung der Karte genutzt werden soll", "maps-displaymap-par-coordinates": "Die Postion auf welche die Karte zunächst zentriert werden soll", "maps-displaymap-par-visitedicon": "Der Dateiname des Symbols, das anstelle der ursprünglichen Markierung angezeigt werden soll, sobald die Originalmarkierungen angeklickt wurden", @@ -94,11 +94,10 @@ "maps-displaymap-par-minzoom": "Die minimale Anzeigestufe", "maps-displaymap-par-polygons": "Anzuzeigende Vielecke", "maps-displaymap-par-rectangles": "Anzuzeigende Rechtecke", - "maps-displaymap-par-static": "Macht die Karte statisch", - "maps-displaymap-par-wmsoverlay": "Eine WMS-Überlagerung verwenden", + "maps-displaymap-par-static": "Die Karte statisch machen", + "maps-displaymap-par-wmsoverlay": "Eine Web-Map-Service-Ebene verwenden", "maps-fullscreen-button": "Auf Vollbild umschalten", "maps-fullscreen-button-tooltip": "Die Karte als Vollbild oder eingebettet darstellen.", - "maps-googlemaps3-par-enable-fullscreen": "Vollbildbutton aktivieren", "validation-error-invalid-location": "Parameter $1 muss einem gültigen Standort entsprechen.", "validation-error-invalid-locations": "Parameter $1 muss einem oder mehreren gültigen Standorten entsprechen.", "validation-error-invalid-width": "Parameter $1 muss einer gültigen Breite entsprechen.", @@ -125,23 +124,27 @@ "maps-geocoder-not-available": "Die Funktion zum Georeferenzierung von Karten ist nicht verfügbar. Der Standort kann nicht georeferenziert werden.", "maps_googlemaps3": "Karte (Google Maps v3)", "maps_leaflet": "Karte (Leaflet)", - "maps-leaflet-par-zoom": "Erlaubt das Festlegen der Vergrößerungsstufe der Karte.", - "maps-leaflet-par-defzoom": "Erlaubt das Festlegen der Standardvergrößerungsstufe der Karte.", - "maps-leaflet-par-resizable": "Erlaubt die Anpassung der Kartengröße durch das Ziehen an der rechten unteren Ecke.", + "maps-leaflet-par-defzoom": "Erlaubt das Festlegen der Standardvergrößerungsstufe der Karte", + "maps-leaflet-par-layer": "Die Ebene, die angezeigt wird, wenn die Karte lädt.", + "maps-leaflet-par-overlaylayers": "Die Überlagerungsebenen, die angezeigt werden, wenn die Karte lädt.", + "maps-leaflet-par-maxclusterradius": "Der maximale Radius, den ein Cluster ab der Mittelmarkierung abdeckt (in Pixeln).", + "maps-leaflet-par-clusterspiderfy": "Bei Klicken auf einen Cluster in einer niedrigen Vergrößerungsstufe wird dieser netzförmig expandiert, so dass alle enthaltenen Markierungen eingesehen werden können.", "maps_openlayers": "Karte (OpenLayers)", "maps_click_to_activate": "Klicken, um die Karte zu aktivieren.", "maps_centred_on": "Karte ist auf $1, $2 zentriert.", "maps-par-mappingservice": "Ermöglicht das Festlegen des Kartografiedienstes, der zum Erstellen der Karte verwendet werden soll", "maps-par-resizable": "Die Karte durch Ziehen von der unteren rechten Ecke größenveränderbar machen können", - "maps-par-searchmarkers": "Erlaubt die Suche nach speziellen Markierungen über ein in die Karte eingebettetes Feld.", + "maps-par-searchmarkers": "Erlaubt die Suche nach speziellen Markierungen über ein in die Karte eingebettetes Feld", "maps-par-geoservice": "Der für Umwandlung von Adressen in Koordinaten zu verwendende Georeferenzierungsdienst.", "maps-par-zoom": "Die Zoomstufe für die Karte. Karten mit Kennzeichnungen werden standardmäßig auf die Stufe gezoomt in der diese noch alle gemeinsam angezeigt werden können.", "maps-par-width": "Die Kartenbreite, die genutzt werden soll. Standardmäßig wird Pixel (px) als Einheit angenommen. Jedoch kann auch eine der folgenden Einheiten angegeben werden: ex, em und %", "maps-par-height": "Die Kartenhöhe, die genutzt werden soll. Standardmäßig wird Pixel (px) als Einheit angenommen. Jedoch kann auch eine der folgenden Einheiten angegeben werden: ex, em und %", - "maps-par-centre": "Der Standort, an dem die Karte zentriert werden soll", + "maps-par-centre": "Die Koordinaten des Standorts (bpsw. 50.0093,8.2564), an dem die Karte zentriert werden soll", + "maps-par-enable-fullscreen": "Vollbildschaltfläche aktivieren", + "maps-par-kml": "Die auf die Karte zu ladenden KML-Dateien.", + "maps-par-markercluster": "Erlaubt das Zusammenführen mehrerer benachbarter Markierungen zu einer Markierung", "maps-googlemaps3-incompatbrowser": "Der Browser ist nicht mit Google Maps v3 kompatibel.", - "maps-googlemaps3-par-imageoverlays": "Erlaubt das Hinzufügen eines Bildes, das auf dem angegebenen Ort auf der Karte angezeigt wird.", - "maps-googlemaps3-par-markercluster": "Erlaubt das Zusammenführen mehrerer benachbarter Markierungen in eine Markierung", + "maps-googlemaps3-par-imageoverlays": "Erlaubt das Hinzufügen eines Bildes, das am angegebenen Ort auf der Karte angezeigt wird", "maps-googlemaps3-par-type": "Die zunächst anzuzeigende Kartenart.", "maps-googlemaps3-par-types": "Die Kartenarten, die über die Steuerung zu den Kartenarten verfügbar gemacht werden sollen.", "maps-googlemaps3-par-layers": "Die auf die Karte zu ladenden Sonderebenen.", @@ -149,17 +152,20 @@ "maps-googlemaps3-par-zoomstyle": "Der Stil der Zoomsteuerung.", "maps-googlemaps3-par-typestyle": "Der Stil der Steuerung zu den Kartenarten.", "maps-googlemaps3-par-autoinfowindows": "Die Informationsfenster nach dem Laden der Seite automatisch öffnen oder nicht", - "maps-googlemaps3-par-kml": "Die auf die Karte zu ladenden KML-Dateien.", "maps-googlemaps3-par-gkml": "Die von Google gehosteten KML-Dateien, die auf die Karte geladen werden sollen", "maps-googlemaps3-par-fusiontables": "Die Kennungen der Google Fusion Tables, die auf die Karte geladen werden sollen", "maps-googlemaps3-par-tilt": "Die Neigung der Karte bei der Nutzung von Google Maps.", "maps-googlemaps3-par-kmlrezoom": "Die Karte erneut zoomen, nachdem die KML-Ebenen geladen wurden.", "maps-googlemaps3-par-poi": "Die Sehenswürdigkeiten anzeigen", + "maps-googlemaps3-par-clustergridsize": "Die Rastergröße eines Clusters in Pixeln", + "maps-par-clustermaxzoom": "Die maximale Vergrößerungsstufe, in der Cluster vorhanden sein können.", + "maps-par-clusterzoomonclick": "Ob das Standardverhalten beim Klicken auf einen Cluster dessen Vergrößerung ist.", + "maps-par-maxclusterradius": "Der maximale Radius, den ein Cluster abdeckt.", + "maps-googlemaps3-par-clusteraveragecenter": "Ob die Mitte jedes Clusters der Durchschnitt aller Markierungen des Clusters sein soll", + "maps-googlemaps3-par-clusterminsize": "Die Mindestzahl der Markierungen, die ein Cluster enthalten soll, bevor die Markierungen versteckt werden und ein Zähler angezeigt wird", "maps-openlayers-par-controls": "Die auf der Karte anzuzeigenden Steuerelemente.", "maps-openlayers-par-layers": "Die Ebenen, die über die Ebenenauswahl verfügbar sein sollen. Die erste Ebene wird während des Ladens der Karten angezeigt.", - "maps-openlayers-par-overlays": "Überlagerungsebenen, die in der Ebenenauswahl zur Verfügung stehen. Diese Ebenen werden bei einer normalen Ebene oben angezeigt, in der Art einer Markierung.", - "maps-osm-par-thumbs": "Miniaturansichten anzeigen", - "maps-osm-par-photos": "Fotografien anzeigen", + "maps-openlayers-par-overlays": "Die Überlagerungsebenen, die in der Ebenenauswahl zur Verfügung stehen. Sie werden auf einer normalen Ebene nach Art einer Markierung angezeigt", "mapeditor": "Karteneditor", "specialpages-group-maps": "Karten", "mapeditor-parser-error": "Ein Fehler ist beim Parsen der Metadaten aufgetreten. Ignoriere Benutzereingaben.", @@ -195,5 +201,43 @@ "mapeditor-imageoverlay-button": "Bildeinblendung hinzufügen", "mapeditor-form-field-image": "Bild", "mapeditor-imageoverlay-title": "Einzelheiten zur Bildeinblendung", - "mapeditor-form-field-visitedicon": "Symbol sofern besucht" + "mapeditor-form-field-visitedicon": "Symbol sofern besucht", + "semanticmaps-unrecognizeddistance": "Der Wert $1 ist keine gültige Distanz.", + "semanticmaps-kml-link": "KML-Datei ansehen", + "semanticmaps-kml": "Export (KML)", + "semanticmaps-default-kml-pagelink": "Artikel $1 ansehen", + "semanticmaps-latitude": "Breitengrad: $1", + "semanticmaps-longitude": "Längengrad: $1", + "semanticmaps-altitude": "Höhe: $1", + "semanticmaps-forminput-locations": "Standort", + "semanticmaps-par-staticlocations": "Die Listen von Standorten, die zusammen mit den abgefragten Daten, der Karte hinzugefügt werden sollen. Analog zu den Anzeigepunkten können je Standort Titel, Beschreibung und Symbol, unter Verwendung einer Tilde „~“ als Trennzeichen, hinzugefügt werden.", + "semanticmaps-par-showtitle": "Den Titel im Informationsfenster der Kennzeichnung anzeigen oder nicht. Diese Option zu deaktivieren ist oftmals dann nützlich, sofern eine Vorlage zur Formatierung des Informationsfensterinhalts verwendet wird.", + "semanticmaps-par-hidenamespace": "Den Namen des Namensraums im Informationsfenster der Kennzeichnung anzeigen", + "semanticmaps-par-centre": "Das Zentrum der Karte. Sofern nicht angegeben wird automatisch das optimale Zentrum zur Darstellung aller Kennzeichnungen auf der Karte gewählt.", + "semanticmaps-par-template": "Die zur Formatierung des Informationsfensterinhalts zu verwendende Vorlage.", + "semanticmaps-par-geocodecontrol": "Die Steuerungsseite zum Geokodieren anzeigen.", + "semanticmaps-par-activeicon": "Das Symbol, das anstelle der Standardmarkierung angezeigt wird, sofern die aktive Seite dem Abfrageergebnis entspricht.", + "semanticmaps-par-pagelabel": "Sofern mit „yes“ (ja) festgelegt, verfügen alle Markierungen über eine eingebettete Anzeige mit einem Link zur entsprechenden Seite sowie den entsprechenden Koordinaten.", + "semanticmaps-par-ajaxcoordproperty": "Name des Koordinatenattributs, das zur Erstellung der Ajax-Abfrage verwendet wird.", + "semanticmaps-par-ajaxquery": "Eine zweite Abfrage, die über Ajax gesendet wird, um zusätzliche Koordinaten abzurufen.", + "semanticmaps-par-userparam": "Ein in jedem Vorlagenaufruf zu übergebender Wert, falls eine Vorlage verwendet wird.", + "semanticmaps-kml-text": "Der Text, der zu jeder Seite angezeigt wird. Wird im Fall zusätzlich abgefragter Attribute ersetzt.", + "semanticmaps-kml-title": "Der Standardtitel für die Ergebnisse", + "semanticmaps-kml-linkabsolute": "Die Links sollen absolut sein (anstatt relativ)", + "semanticmaps-kml-pagelinktext": "Der Text, der für die Links zur Seite genutzt werden soll. $1 wird dabei durch den Namen der Seite ersetzt.", + "semanticmaps-shapes-improperformat": "$1 ist falsch formatiert. Siehe hierzu die Dokumentation bezüglich Formatierungen.", + "semanticmaps-shapes-missingshape": "Für $1 wurden keine Formen gefunden. Siehe hierzu die Dokumentation bezüglich verfügbarer Formen.", + "validator-type-mapscircle": "Geografischer Kreis", + "validator-type-mapscircle-list": "Liste der Kreise", + "validator-type-mapsimageoverlay": "Bildüberlagerung", + "validator-type-mapsimageoverlay-list": "Liste der Bildüberlagerungen", + "validator-type-mapsline": "Geografische Linie", + "validator-type-mapsline-list": "Liste der Linien", + "validator-type-mapslocation": "Geografischer Standort", + "validator-type-mapslocation-list": "Liste der Standorte", + "validator-type-mapsrectangle": "Geografisches Rechteck", + "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" } diff --git a/i18n/el.json b/i18n/el.json index 9aba2d1a8..6fb0a06ee 100644 --- a/i18n/el.json +++ b/i18n/el.json @@ -33,7 +33,6 @@ "maps-layer-of-type-and-name": "Στρώση «$2» τύπου «$1»", "maps-layer-type-supported-by": "Αυτός ο τύπος στρώσης μπορεί να χρησιμοποιηθεί {{PLURAL:$2|μόνο με την υπηρεσία|με αυτές τις υπηρεσίες}} χαρτογράφησης: $1.", "maps-displaymap-description": "Προβολή γεωγραφικών χαρτών χωρίς σημάδια ορισμένα μέσω wiki.", - "maps-displaypoint-description": "Προβολή γεωγραφικών χαρτών με ένα ή περισσότερα σημάδια ορισμένα μέσω wiki.", "maps-coordinates-par-location": "Οι συντεταγμένες που θέλετε να μορφοποιήσετε.", "maps-coordinates-par-format": "Η μορφή προορισμού για τις συντεταγμένες.", "maps-coordinates-par-directional": "Δηλώνει εάν οι συντεταγμένες πρέπει να εκφραστούν κατευθυντικά ή όχι.", @@ -65,7 +64,6 @@ "maps-displaymap-par-wmsoverlay": "Χρήση μιας υπέρθεσης WMS", "maps-fullscreen-button": "Εναλλαγή πλήρους οθόνης", "maps-fullscreen-button-tooltip": "Προβολή του χάρτη σε πλήρη οθόνη ή ενσωματωμένου.", - "maps-googlemaps3-par-enable-fullscreen": "Ενεργοποίηση κουμπιού πλήρους οθόνης", "validation-error-invalid-location": "Η παράμετρος «$1» πρέπει να είναι μια έγκυρη τοποθεσία.", "validation-error-invalid-locations": "Η παράμετρος «$1» πρέπει να είναι μία ή περισσότερες έγκυρες τοποθεσίες.", "validation-error-invalid-width": "Η παράμετρος «$1» πρέπει να είναι ένα έγκυρο πλάτος.", @@ -96,9 +94,11 @@ "maps-par-searchmarkers": "Επιτρέπει την αναζήτηση για συγκεκριμένα σημάδια μέσω πεδίου ενσωματωμένου στο χάρτη.", "maps-par-geoservice": "Η υπηρεσία γεωκωδικοποίησης που να χρησιμοποιείται για να μεταφράζει μεταξύ διευθύνσεων και συντεταγμένων.", "maps-par-centre": "Η τοποθεσία στην οποία ο χάρτης θα πρέπει να είναι κεντραρισμένος", + "maps-par-enable-fullscreen": "Ενεργοποίηση κουμπιού πλήρους οθόνης", + "maps-par-kml": "Αρχεία KML για φόρτωση στο χάρτη.", + "maps-par-markercluster": "Επιτρέπει τη συγχώνευση πολλαπλών κοντινών σημαδιών σε ένα σημάδι", "maps-googlemaps3-incompatbrowser": "Το πρόγραμμα περιήγησης δεν είναι συμβατό με την έκδοση 3 των Χαρτών Google.", "maps-googlemaps3-par-imageoverlays": "Επιτρέπει την προσθήκη μιας εικόνας προς εμφάνιση στην καθοριζόμενη θέση στο χάρτη.", - "maps-googlemaps3-par-markercluster": "Επιτρέπει τη συγχώνευση πολλαπλών κοντινών σημαδιών σε ένα σημάδι", "maps-googlemaps3-par-type": "Τύπος χάρτη που να εμφανίζεται αρχικά.", "maps-googlemaps3-par-types": "Τύποι χάρτη που θα είναι διαθέσιμοι μέσω της ρύθμισης τύπου.", "maps-googlemaps3-par-layers": "Ειδικές στρώσεις για φόρτωση επάνω στο χάρτη.", @@ -106,13 +106,10 @@ "maps-googlemaps3-par-zoomstyle": "Στυλ του στοιχείου ελέγχου του ζουμ.", "maps-googlemaps3-par-typestyle": "Στυλ του στοιχείου ελέγχου του τύπου.", "maps-googlemaps3-par-autoinfowindows": "Αυτόματο άνοιγμα όλων των παραθύρων πληροφοριών μετά τη φόρτωση της σελίδας.", - "maps-googlemaps3-par-kml": "Αρχεία KML για φόρτωση στο χάρτη.", "maps-googlemaps3-par-gkml": "Αρχεία KML που φιλοξενούνται στη Google για φόρτωση στο χάρτη.", "maps-googlemaps3-par-poi": "Εμφάνιση σημείων ενδιαφέροντος.", "maps-openlayers-par-controls": "Στοιχεία ελέγχου προς τοποθέτηση στο χάρτη.", "maps-openlayers-par-layers": "Οι στρώσεις που θα είναι διαθέσιμες στον επιλογέα στρώσεων. Η πρώτη στρώση θα εμφανίζεται κατά τη φόρτωση του χάρτη.", - "maps-osm-par-thumbs": "Εμφάνιση μικρογραφιών", - "maps-osm-par-photos": "Εμφάνιση φωτογραφιών", "mapeditor": "Πρόγραμμα επεξεργασίας χαρτών", "specialpages-group-maps": "Χάρτες", "mapeditor-none-text": "Κανένα", @@ -146,5 +143,25 @@ "mapeditor-imageoverlay-button": "Προσθήκη υπέρθεσης εικόνας", "mapeditor-form-field-image": "Εικόνα", "mapeditor-imageoverlay-title": "Λεπτομέρειες υπέρθεσης εικόνας", - "mapeditor-form-field-visitedicon": "Επισκεφθέν εικονίδιο" + "mapeditor-form-field-visitedicon": "Επισκεφθέν εικονίδιο", + "semanticmaps-unrecognizeddistance": "Η τιμή $1 δεν είναι έγκυρη απόσταση.", + "semanticmaps-kml-link": "Προβολή του αρχείου KML", + "semanticmaps-default-kml-pagelink": "Προβολή σελίδας $1", + "semanticmaps-latitude": "Γεωγραφικό πλάτος: $1", + "semanticmaps-longitude": "Γεωγραφικό μήκος: $1", + "semanticmaps-altitude": "Υψόμετρο: $1", + "semanticmaps-forminput-locations": "Τοποθεσίες", + "semanticmaps-par-staticlocations": "Μια λίστα με τοποθεσίες για προσθήκη στο χάρτη μαζί με τα ερωτηθέντα δεδομένα. Όπως και με τα display_points, μπορείτε να προσθέσετε τίτλο, περιγραφή και εικονίδιο ανά τοποθεσία χρησιμοποιώντας την περισπωμένη «~» ως διαχωριστικό.", + "semanticmaps-par-showtitle": "Εμφάνιση ή μη του τίτλου στο παράθυρο πληροφοριών δείκτη. Η απενεργοποίησή του είναι συχνά χρήσιμη όταν χρησιμοποιείται πρότυπο για τη μορφοποίηση του περιεχομένου του παραθύρου πληροφοριών.", + "semanticmaps-par-hidenamespace": "Εμφάνιση τίτλου ονοματοχώρου στο παράθυρο πληροφοριών δείκτη", + "semanticmaps-par-centre": "Το κέντρο του χάρτη. Όταν δεν παρέχεται, ο χάρτης θα επιλέξει αυτόματα το βέλτιστο κέντρο για την προβολή όλων των δεικτών επάνω στο χάρτη.", + "semanticmaps-par-template": "Πρότυπο για να το χρησιμοποιήσετε για τη μορφοποίηση των περιεχομένων του παραθύρου πληροφοριών.", + "semanticmaps-par-geocodecontrol": "Εμφάνιση στοιχείου ελέγχου γεωκωδικοποίησης.", + "semanticmaps-par-activeicon": "Εικονίδιο που θα εμφανίζεται αντί του προεπιλεγμένου δείκτη, όταν η ενεργή σελίδα ισούται με το αποτέλεσμα του ερωτήματος", + "semanticmaps-kml-text": "Το κείμενο που σχετίζεται με κάθε σελίδα. Παρακάμπτεται από τις πρόσθετες ερωτηθέντες ιδιότητες αν υπάρχουν.", + "semanticmaps-kml-title": "Προεπιλεγμένος τίτλος για αποτελέσματα", + "semanticmaps-kml-linkabsolute": "Να είναι οι σύνδεσμοι απόλυτοι (ως αντιπαράθεση με τους σχετικούς)", + "semanticmaps-kml-pagelinktext": "Το κείμενο που θα χρησιμοποιείται για τους συνδέσμους προς τη σελίδα, στο οποίο το $1 θα αντικαθίσταται από τον τίτλο της σελίδας", + "semanticmaps-shapes-improperformat": "Εσφαλμένη μορφοποίηση του $1, ανατρέξτε στην τεκμηρίωση περί μορφοποίησης", + "semanticmaps-shapes-missingshape": "Δεν βρέθηκαν σχήματα για το $1, ανατρέξτε στην τεκμηρίωση για διαθέσιμα σχήματα" } diff --git a/i18n/en.json b/i18n/en.json index 0e6669db3..4e54c6553 100644 --- a/i18n/en.json +++ b/i18n/en.json @@ -39,7 +39,6 @@ "maps-layer-type-supported-by": "This layer type can {{PLURAL:$2|only be used with the $1 mapping service|be used with these mapping services: $1}}.", "maps-coordinates-description": "Parser hook to format coordinates, from and to any of the supported formats.", "maps-displaymap-description": "Display geographical maps without any wiki-defined markers on them.", - "maps-displaypoint-description": "Display geographical maps with one or more wiki-defined markers on them.", "maps-distance-description": "Convert a distance using a certain supported unit to its equivalent using another unit.", "maps-finddestination-description": "Find a destination given a starting point (that can be in any of the supported formats), an initial bearing and a distance.", "maps-geocode-description": "Enables the geocoding of addresses, in other words, turning human readable locations into sets of coordinates. There is support for several geocoding services, which should not be confused with mapping services.", @@ -93,7 +92,6 @@ "maps-displaymap-par-wmsoverlay": "Use a WMS overlay", "maps-fullscreen-button": "Toggle fullscreen", "maps-fullscreen-button-tooltip": "View the map as fullscreen or embedded.", - "maps-googlemaps3-par-enable-fullscreen": "Enable fullscreen button", "validation-error-invalid-location": "Parameter \"$1\" must be a valid location.", "validation-error-invalid-locations": "Parameter \"$1\" must be one or more valid locations.", "validation-error-invalid-width": "Parameter \"$1\" must be a valid width.", @@ -120,11 +118,12 @@ "maps-geocoder-not-available": "The geocoding feature of Maps is not available. Your location cannot be geocoded.", "maps_googlemaps3": "Google Maps v3", "maps_leaflet": "Leaflet", - "maps-leaflet-par-zoom": "Allows setting the zoom level of the map.", "maps-leaflet-par-defzoom": "Allows setting the default zoom level of the map.", - "maps-leaflet-par-resizable": "Allows making the map resizable by dragging at its lower right corner.", + "maps-leaflet-par-layer": "The layer that 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_openlayers": "OpenLayers", - "maps_osm": "OpenStreetMap", "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.", @@ -135,9 +134,11 @@ "maps-par-width": "Allows setting the width of the map. By default pixels will be assumed as unit, but you can explicitly specify one of these units: px, ex, em, %.", "maps-par-height": "Allows setting the height of the map. By default pixels will be assumed as unit, but you can explicitly specify one of these units: px, ex, em, %.", "maps-par-centre": "The location on which the map should be centered", + "maps-par-enable-fullscreen": "Enable fullscreen button", + "maps-par-kml": "KML files to load onto the map.", + "maps-par-markercluster": "Allows merging of multiple nearby markers into one marker", "maps-googlemaps3-incompatbrowser": "Your browser is not compatible with Google Maps v3.", "maps-googlemaps3-par-imageoverlays": "Allows adding an image to be shown on the specified location on the map.", - "maps-googlemaps3-par-markercluster": "Allows merging of multiple nearby markers into one marker", "maps-googlemaps3-par-type": "The map type to initially show.", "maps-googlemaps3-par-types": "The map types that will be available via the type control.", "maps-googlemaps3-par-layers": "Special layers to load onto the map.", @@ -145,17 +146,20 @@ "maps-googlemaps3-par-zoomstyle": "The style of the zoom control.", "maps-googlemaps3-par-typestyle": "The style of the type control.", "maps-googlemaps3-par-autoinfowindows": "Automatically open all info windows after the page has loaded.", - "maps-googlemaps3-par-kml": "KML files to load onto the map.", "maps-googlemaps3-par-gkml": "KML files hosted by Google to load onto the map.", "maps-googlemaps3-par-fusiontables": "IDs of Google Fusion Tables which should be loaded onto the map.", "maps-googlemaps3-par-tilt": "Tilt for the Map when using Google Maps.", "maps-googlemaps3-par-kmlrezoom": "Rezoom the map after the KML layers have been loaded.", "maps-googlemaps3-par-poi": "Show points of interest.", + "maps-googlemaps3-par-clustergridsize": "The grid size of a cluster in pixels.", + "maps-par-clustermaxzoom": "The maximum zoom level where clusters may exist.", + "maps-par-clusterzoomonclick": "Whether the default behaviour of clicking on a cluster is to zoom into it.", + "maps-par-maxclusterradius": "The maximum radius that a cluster will cover.", + "maps-googlemaps3-par-clusteraveragecenter": "Whether the center of each cluster should be the average of all markers in the cluster.", + "maps-googlemaps3-par-clusterminsize": "The minimum number of markers to be in a cluster before the markers are hidden and a count is shown.", "maps-openlayers-par-controls": "The controls to place on the map.", "maps-openlayers-par-layers": "The layers that will be available in the layer selector. The first layer will be shown when the map loads.", "maps-openlayers-par-overlays": "Overlay layers that will be available in the layer selector. These layers will be displayed on top of a normal layer, kind of like a marker.", - "maps-osm-par-thumbs": "Show thumbs", - "maps-osm-par-photos": "Show photos", "mapeditor": "Map editor", "specialpages-group-maps": "Maps", "mapeditor-parser-error": "An error occurred when parsing metadata. Ignoring user input.", @@ -191,5 +195,43 @@ "mapeditor-imageoverlay-button": "Add image overlay", "mapeditor-form-field-image": "Image", "mapeditor-imageoverlay-title": "Image overlay details", - "mapeditor-form-field-visitedicon": "Visited icon" + "mapeditor-form-field-visitedicon": "Visited icon", + "semanticmaps-unrecognizeddistance": "The value $1 is not a valid distance.", + "semanticmaps-kml-link": "View the KML file", + "semanticmaps-kml": "KML", + "semanticmaps-default-kml-pagelink": "View page $1", + "semanticmaps-latitude": "Latitude: $1", + "semanticmaps-longitude": "Longitude: $1", + "semanticmaps-altitude": "Altitude: $1", + "semanticmaps-forminput-locations": "Locations", + "semanticmaps-par-staticlocations": "A list of locations to add to the map together with the queried data. Like with display_points, you can add a title, description and icon per location using the tilde \"~\" as separator.", + "semanticmaps-par-showtitle": "Show a title in the marker info window or not. Disabling this is often useful when using a template to format the info window content.", + "semanticmaps-par-hidenamespace": "Show the namespace title in the marker info window", + "semanticmaps-par-centre": "The center of the map. When not provided, the map will automatically pick the optimal center to display all markers on the map.", + "semanticmaps-par-template": "A template to use to format the info window contents.", + "semanticmaps-par-geocodecontrol": "Show the geocoding control.", + "semanticmaps-par-activeicon": "Icon to be displayed instead of default marker, when active page is equal to query result", + "semanticmaps-par-pagelabel": "When set to \"yes\", all markers will have an \"inlineLabel\" with a link to the page containing the coordinates for the marker", + "semanticmaps-par-ajaxcoordproperty": "Name of the coordinate property which is used to build the ajax query.", + "semanticmaps-par-ajaxquery": "A second query that is sent via ajax to fetch additional coordinates.", + "semanticmaps-par-userparam": "A value passed into each template call, if a template is used", + "semanticmaps-kml-text": "The text associated with each page. Overridden by the additional queried properties if any.", + "semanticmaps-kml-title": "The default title for results", + "semanticmaps-kml-linkabsolute": "Should links be absolute (as opposed to relative)", + "semanticmaps-kml-pagelinktext": "The text to use for the links to the page, in which $1 will be replaced by the page title", + "semanticmaps-shapes-improperformat": "Improper formatting of $1. Please see documentation for formatting", + "semanticmaps-shapes-missingshape": "No shapes found for $1. Please see documentation for available shapes", + "validator-type-mapscircle": "Geographical circle", + "validator-type-mapscircle-list": "List of circles", + "validator-type-mapsimageoverlay": "Image overlay", + "validator-type-mapsimageoverlay-list": "List of image overlays", + "validator-type-mapsline": "Geographical line", + "validator-type-mapsline-list": "List of lines", + "validator-type-mapslocation": "Geographical location", + "validator-type-mapslocation-list": "List of locations", + "validator-type-mapsrectangle": "Geographical rectangle", + "validator-type-mapsrectangle-list": "List of rectangles", + "validator-type-mapspolygon": "Geographical polygon", + "validator-type-mapspolygon-list": "List of geographical polygons", + "validator-type-wmsoverlay": "Web Map Service overlay" } diff --git a/i18n/es.json b/i18n/es.json index 69eafc693..1fe791ed8 100644 --- a/i18n/es.json +++ b/i18n/es.json @@ -19,7 +19,12 @@ "Macofe", "Themasterriot", "Mor", - "Rafael.minuesa" + "Rafael.minuesa", + "Indiralena", + "Lemondoge", + "Rubentl134", + "AlvaroMolina", + "Dgstranz" ] }, "maps-desc": "Habilita la inserción de mapas dinámicos en páginas wikis, la geocodificación de direcciones y otras operaciones geográficas", @@ -33,6 +38,7 @@ "maps-copycoords-prompt": "Ctrl+C, Intro", "maps-searchmarkers-text": "Marcadores de filtro", "maps-others": "otros", + "maps-kml-parsing-failed": "Falló el análisis de uno o más archivos KML. Por lo general, esto se debe a fallos de recuperación o a XML con formato incorrecto.", "maps-ns-layer": "Capa", "maps-ns-layer-talk": "Discusión de capa", "maps-layer-property": "Propiedad", @@ -41,6 +47,7 @@ "maps-layerdef-invalid": "{{PLURAL:$1|Definición no válida|Definiciones no válidas}}", "maps-layerdef-invalid-fatal": "Definición no válida fatal", "maps-layerdef-wrong-namespace": "Las definiciones de capas solo son válidas en el espacio de nombres «$1».", + "maps-layerdef-equal-layer-name": "Los nombres de las capas deben ser únicos dentro de la misma página de capas. Otra capa ya está utilizando «$1».", "maps-layerpage-usage": "Páginas con mapas que usan la capa «$1»", "maps-layerpage-nousage": "No hay páginas que usen esta capa en este momento.", "maps-error-invalid-layertype": "No hay capas de tipo \"$1\". Sólo {{PLURAL:$3|este tipo es compatible|estos tipos son compatibles}} :$2", @@ -54,7 +61,6 @@ "maps-layer-type-supported-by": "Este tipo de capa solo puede ser utilizado con {{PLURAL:$2|el servicio de mapas $1|estos servicios de mapas: $1 }}.", "maps-coordinates-description": "Marcador del analizador para formatear las coordenadas, desde y hacia cualquiera de los formatos compatibles.", "maps-displaymap-description": "Mostrar mapas geográficos sin ningún marcador definido por el wiki sobre ellos.", - "maps-displaypoint-description": "Mostrar mapas geográficos con uno o más marcadores wiki en ellos.", "maps-distance-description": "Convertir una distancia usando un cierta unidad soportada a su equivalente utilizando otra unidad.", "maps-finddestination-description": "Encontrar un destino dado un punto de partida (que puede estar en cualquiera de los formatos compatibles), un orientación inicial y una distancia.", "maps-geocode-description": "Permite la geocodificación de direcciones, en otras palabras, transformando las ubicaciones legibles por humanos en conjuntos de coordenadas. No hay soporte para varios servicios de geocodificación, que no deben confundirse con servicios de mapas.", @@ -108,7 +114,6 @@ "maps-displaymap-par-wmsoverlay": "Utilizar una superposición WMS", "maps-fullscreen-button": "Activar o desactivar pantalla completa", "maps-fullscreen-button-tooltip": "Ver el mapa en toda la pantalla o incrustado.", - "maps-googlemaps3-par-enable-fullscreen": "Activar el botón «Pantalla completa»", "validation-error-invalid-location": "El parámetro \"$1\" deber ser una ubicación válida.", "validation-error-invalid-locations": "El parámetro \"$1\" debe ser una o más ubicaciones válidas.", "validation-error-invalid-width": "El parámetro \"$1\" debe ser un ancho válido.", @@ -133,19 +138,23 @@ "maps_unrecognized_coords_for": "{{PLURAL:$2|La coordenada siguiente no se reconoce y se ha|Las coordenadas siguientes no se reconocen y se han}} omitido del mapa:\n$1", "maps_map_cannot_be_displayed": "No se puede mostrar el mapa.", "maps-geocoder-not-available": "La funcionalidad de geocodificación de Maps no está disponible. Su ubicación no puede ser geocodificada.", + "maps-leaflet-par-defzoom": "Permite establecer el nivel de ampliación predeterminado del mapa.", + "maps-leaflet-par-layer": "La capa que aparecerá cuando se cargue el mapa.", "maps_click_to_activate": "Haz clic para activar el mapa", "maps_centred_on": "Mapa centrado en $1, $2.", "maps-par-mappingservice": "Permite configurar el servicio de cartografía que se utilizará para generar el mapa.", - "maps-par-resizable": "Hace que se puedan alteras las dimensiones del mapa arrastrando su esquina inferior derecha.", + "maps-par-resizable": "Hace que se puedan alterar las dimensiones del mapa arrastrando su esquina inferior derecha.", "maps-par-searchmarkers": "Permite buscar marcadores específicos a través de un campo incrustado en el mapa.", "maps-par-geoservice": "El servicio de geocodificación a utilizar para realizar traducciones entre las direcciones y las coordenadas.", "maps-par-zoom": "El nivel de ampliación del mapa. Para mapas con marcadores el valor predeterminado será el máximo que aun muestre todos los marcadores.", "maps-par-width": "Permite establecer la anchura del mapa. De forma predeterminada se emplearán los pixeles como unidad, pero puede especificar explícitamente una de estas unidades: px, ex, em, %.", "maps-par-height": "Permite establecer la altura del mapa. De forma predeterminada se usarán los pixeles como unidad, pero puede especificar explícitamente una de estas unidades: px, ex, em, %.", "maps-par-centre": "La ubicación en la que el mapa debe quedar centrado", + "maps-par-enable-fullscreen": "Activar el botón «Pantalla completa»", + "maps-par-kml": "Archivos KML para cargar en el mapa.", + "maps-par-markercluster": "Permite fusionar múltiples marcadores cercanos en un marcador", "maps-googlemaps3-incompatbrowser": "Su navegador no es compatible con Google Maps v2.", "maps-googlemaps3-par-imageoverlays": "Permite añadir una imagen que mostrar en la ubicación especificada en el mapa.", - "maps-googlemaps3-par-markercluster": "Permite fusionar múltiples marcadores cercanos en un marcador", "maps-googlemaps3-par-type": "El tipo de mapa que mostrar inicialmente.", "maps-googlemaps3-par-types": "Los tipos de mapa que estarán disponibles a través del control de tipo.", "maps-googlemaps3-par-layers": "Capas especiales para cargar en el mapa.", @@ -153,17 +162,16 @@ "maps-googlemaps3-par-zoomstyle": "El estilo del control de zoom.", "maps-googlemaps3-par-typestyle": "El estilo del control de tipo.", "maps-googlemaps3-par-autoinfowindows": "Abrir automáticamente todas las ventanas de información después de que la página se haya cargado.", - "maps-googlemaps3-par-kml": "Archivos KML para cargar en el mapa.", "maps-googlemaps3-par-gkml": "Archivos KML alojados por Google para cargar en el mapa.", "maps-googlemaps3-par-fusiontables": "Identificadores de las tablas de Google Fusion que se deben cargar en el mapa.", "maps-googlemaps3-par-tilt": "Inclinación del mapa al utilizar Google Maps.", "maps-googlemaps3-par-kmlrezoom": "Ajustar el nivel de zoom del mapa después de que se hayan cargado las capas KML.", "maps-googlemaps3-par-poi": "Mostrar puntos de interés.", + "maps-googlemaps3-par-clustergridsize": "La medida de verja de un grupo en píxeles.", + "maps-googlemaps3-par-clusterminsize": "El número mínimo de marcadores que están en un grupo antes de que los marcadores se oculten y un recuento se muestre", "maps-openlayers-par-controls": "Los controles a colocar en el mapa.", "maps-openlayers-par-layers": "Las capas que estarán disponibles en el selector de capas. La primera capa se mostrará cuando se cargue el mapa.", "maps-openlayers-par-overlays": "Capas superposicionadas que estarán disponibles en el selector de capas. Estas capas serán mostradas sobre la parte superior de una capa normal, como si fuera una especie de marcador.", - "maps-osm-par-thumbs": "Mostrar las miniaturas", - "maps-osm-par-photos": "Mostrar las fotos", "mapeditor": "Editor de mapas", "specialpages-group-maps": "Mapas", "mapeditor-parser-error": "Se ha producido un error al analizar los metadatos. Ignorando la entrada del usuario.", @@ -199,5 +207,34 @@ "mapeditor-imageoverlay-button": "Agregar imagen superpuesta", "mapeditor-form-field-image": "Imagen", "mapeditor-imageoverlay-title": "Detalles de la imagen superpuesta", - "mapeditor-form-field-visitedicon": "Icono visitado" + "mapeditor-form-field-visitedicon": "Icono visitado", + "semanticmaps-unrecognizeddistance": "El valor $1 no es una distancia válida.", + "semanticmaps-kml-link": "Ver el archivo KML", + "semanticmaps-default-kml-pagelink": "Ver página $1", + "semanticmaps-latitude": "Latitud: $1", + "semanticmaps-longitude": "Longitud: $1", + "semanticmaps-altitude": "Altitud: $1", + "semanticmaps-forminput-locations": "Ubicaciones", + "semanticmaps-par-staticlocations": "Una lista de localizaciones para añadir al mapa junto a los datos consultados. De forma similar a display_points, puede añadir un título, una descripción o un icono por localización usando el signo \"~\" como separador.", + "semanticmaps-par-showtitle": "Mostrar o no mostrar un título en la ventana de información del marcador. La desactivación de esto es frecuentemente útil al utilizar una plantilla para dar formato al contenido de la ventana de información.", + "semanticmaps-par-hidenamespace": "Mostrar el título del espacio de nombres en la ventana de información del marcador.", + "semanticmaps-par-centre": "El centro del mapa. Cuando no se proporciona, el mapa escogerá automáticamente el mejor centro para mostrar todos los marcadores en el mapa.", + "semanticmaps-par-template": "Una plantilla a usar para dar formato al contenido de la ventana de información.", + "semanticmaps-par-geocodecontrol": "Mostrar el control de geocodificación.", + "semanticmaps-par-ajaxquery": "Una segunda consulta que se envía mediante AJAX para obtener coordenadas adicionales.", + "semanticmaps-par-userparam": "Un valor pasado en cada llamada de plantilla,si una plantilla es usada", + "semanticmaps-kml-text": "El texto asociado a cada página. Es substituído por las propiedades recuperadas adicionales, si existen.", + "semanticmaps-kml-title": "El título predeterminado de los resultados", + "semanticmaps-kml-linkabsolute": "Los enlaces deberían ser absolutos (lo opuesto de relativos)", + "semanticmaps-kml-pagelinktext": "El texto a usar para los enlaces a la página, en las que $1 será substituído por el título de la página", + "semanticmaps-shapes-improperformat": "Formateo incorrecto de $1, por favor consulta la documentación sobre formateo", + "semanticmaps-shapes-missingshape": "No se ha encontrado ninguna forma para $1, por favor consulta la documentación sobre formas disponibles", + "validator-type-mapscircle": "Círculo geográfico", + "validator-type-mapscircle-list": "Lista de círculos", + "validator-type-mapsline": "Línea geográfica", + "validator-type-mapsline-list": "Lista de líneas", + "validator-type-mapslocation": "Ubicación geográfica", + "validator-type-mapslocation-list": "Lista de ubicaciones", + "validator-type-mapsrectangle": "Rectángulo geográfico", + "validator-type-mapsrectangle-list": "Lista de rectángulos" } diff --git a/i18n/fa.json b/i18n/fa.json index 6cacaed6a..77c79b73e 100644 --- a/i18n/fa.json +++ b/i18n/fa.json @@ -42,7 +42,6 @@ "maps-layer-type-supported-by": "این نوع لایه می‌تواند {{PLURAL:$2|فقط با سرویس نقشه‌برداری $1 استفاده شود|با این سرویس‌های نقشه‌برداری استفاده شود: $1}}.", "maps-coordinates-description": "قلاب تجزیه کننده برای فرمت مختصات، از و به هر فرمت پشتیبانی شده.", "maps-displaymap-description": "نمایش نقشه‌های جغرافیایی بدون هر نشانگر تعریف شده‌ٔ ویکی بر روی آنها.", - "maps-displaypoint-description": "نمایش نقشه‌های جغرافیایی بدون هر نشانگر تعریف شده‌ٔ ویکی بر روی آنها.", "maps-distance-description": "تبدیل یک فاصله با استفاده از یک واحد مشخص پشتیبانی شده برابر با استفاده از واحد دیگری.", "maps-finddestination-description": "پیدا کردن یک مقصد که یک نقطهٔ شروع داده (که می‌تواند در هر فرمت پشتیبانی شده‌ای باشد)، وضع اولیه و یک فاصله.", "maps-geocode-description": "فعل کردن آدرس‌های جئوکدینگ، به عبارت دیگر تبدیل مکان‌های انسانی قابل خواندن به مجموعهٔ مختصات. پشتیبانی برای چندین سرویس های جئوکدینگ، که نباید با سرویس‌های نقشه برداری اشتباه شود، وجود دارد.", @@ -96,7 +95,6 @@ "maps-displaymap-par-wmsoverlay": "استفاده از یک پوشش وی‌ام‌اس", "maps-fullscreen-button": "ضامن تمام صفحه", "maps-fullscreen-button-tooltip": "مشاهدهٔ‌ نقشه به عنوان تمام صفحه یا تعبیه شده.", - "maps-googlemaps3-par-enable-fullscreen": "فعا کردن دکمهٔ تمام صفحه", "validation-error-invalid-location": "پارامتر $1 باید یک مکان معتبر باشد.", "validation-error-invalid-locations": "پارامتر $1 باید یک مکان یا مکان‌های بیشتر معتبری باشد.", "validation-error-invalid-width": "پارامتر $1 باید یک عرض معتبر باشد.", @@ -130,6 +128,8 @@ "maps-par-width": "اجازهٔ تنظیم عرض نقشه. با پیکسل‌های پیش‌فرض، به عنوان واحد فرض خواهد شد، اما شما می‌توانید یکی از این واحدها را به طور واضح مشخص کنید: پی‌ایکس، ای‌ایکس، ای‌ام، ٪.", "maps-par-height": "اجازهٔ تنظیم ارتفاع نقشه. با پیکسل‌های پیش‌فرض، به عنوان واحد فرض خواهد شد، اما شما می‌توانید یکی از این واحدها را به طور واضح مشخص کنید: پی‌ایکس، ای‌ایکس، ای‌ام، ٪.", "maps-par-centre": "مکانی که در نقشه باید مرکز باشد", + "maps-par-enable-fullscreen": "فعا کردن دکمهٔ تمام صفحه", + "maps-par-kml": "پوشه‌های کا‌ام‌ال برای بارگذاری بر روی نقشه.", "maps-googlemaps3-incompatbrowser": "مرورگر شما با نقشه‌های گوگل وی۳ سازگار نیست.", "maps-googlemaps3-par-type": "نوع نقشه برای ابتدا نشان دادن.", "maps-googlemaps3-par-types": "انواع نقشه‌ای که از طریق کنترل نوع در دسترس خواهد بود.", @@ -138,7 +138,6 @@ "maps-googlemaps3-par-zoomstyle": "سبک کنترل زوم.", "maps-googlemaps3-par-typestyle": "سبک کنترل نوع.", "maps-googlemaps3-par-autoinfowindows": "به طور خودکار باز شدن همهٔ صفحات اطلاعات، پس از بارگذاری صفحات.", - "maps-googlemaps3-par-kml": "پوشه‌های کا‌ام‌ال برای بارگذاری بر روی نقشه.", "maps-googlemaps3-par-gkml": "پوشه‌های کا‌ام‌ال توسط گوگل برای بارگذاری بر روی نقشه میزبانی شده‌.", "maps-googlemaps3-par-fusiontables": "شناسه‌های جداول گوگل فوژن که باید بر روی نقشه بارگذاری شود.", "maps-googlemaps3-par-tilt": "نوسان برای نقشه هنگامی که استفاده از نقشه‌های گوگل.", @@ -147,8 +146,6 @@ "maps-openlayers-par-controls": "کنترل‌ها برای قرار دادن بر روی نقشه.", "maps-openlayers-par-layers": "لایه‌هایی که در انتخاب کننده‌ٔ لایه در دسترس خواهند بود. اولین لایه هنگامی نمایش داده خواهد شد که نقشه بازگداری می‌شود.", "maps-openlayers-par-overlays": "لایه‌های پوششی در انتخاب کنندهٔ لایه در دسترس خواهد بود. این لایه‌ها در بالای یک لایهٔ عادی، نمایش داده خواهد شد، نوعی مانند یک علامت.", - "maps-osm-par-thumbs": "نشان دادن جای شست", - "maps-osm-par-photos": "نمایش عکس‌ها", "mapeditor": "تدوین‌گر نقشه", "specialpages-group-maps": "نقشه‌ها", "mapeditor-parser-error": "خطاای هنگام تجزیه‌ٔ فراداده اتفاق افتاد. نادیده گرفتن ورودی کاربر.", @@ -184,5 +181,26 @@ "mapeditor-imageoverlay-button": "افزودن روی هم قرار دادن عکس", "mapeditor-form-field-image": "تصویر", "mapeditor-imageoverlay-title": "جزئیات روی هم قرار دادن عکس", - "mapeditor-form-field-visitedicon": "نمادهای بازدید شده" + "mapeditor-form-field-visitedicon": "نمادهای بازدید شده", + "semanticmaps-unrecognizeddistance": "مقدار $1 یک فاصلهٔ معتبر نیست.", + "semanticmaps-kml-link": "مشاهدهٔ پوشهٔ کا‌ام‌ال", + "semanticmaps-default-kml-pagelink": "مشاهده صفحهٔ $1", + "semanticmaps-latitude": "عرض جغرافیایی: $1", + "semanticmaps-longitude": "طول جغرافیایی: $1", + "semanticmaps-altitude": "ارتفاع: $1", + "semanticmaps-forminput-locations": "مکان‌ها", + "semanticmaps-par-staticlocations": "فهرست مکان‌های برای افزودن به نقشه با اطلاعات سوال شده. مانند با display_points، شما می توانید یک عنوان، توصیف و نماد در هر مکان با استفاده از عنوان جدا کننده \"~\".", + "semanticmaps-par-showtitle": "نمایش یک عنوان در پنجره اطلاعات نشانگر یا نمایش ندادن آن.غیرفعال کردن این اغلب مفید است هنگام استفاده از الگو برای فرمت کردن محتوای پنجره اطلاعات.", + "semanticmaps-par-hidenamespace": "نمایش عنوان فضای نام در پنجره اطلاعات نشانگر", + "semanticmaps-par-centre": "مرکز نقشه. هنگامی که ارائه نشد،نقشه به طور خودکار مرکز مطلوب را برای نمایش همه نشانگرها در نقشه انتخاب می‌کند.", + "semanticmaps-par-template": "الگو برای استفاده از فرمت محتویات پنجره اطلاعات.", + "semanticmaps-par-geocodecontrol": "نشان دادن کنترل جئوکدینگ.", + "semanticmaps-par-activeicon": "نماد به جای نشانگر پیش‌فرض نمایش داده می‌شود هنگامی که صفحه فعال برابر با نتیجه پرس‌و‌جوی است", + "semanticmaps-par-pagelabel": "هنگامی که به «بله» تنظیم می‌شود، همه نشانگرها، یک «inlineLabel» با یک پیوند به صفحه دارند که شامل مختصات برای نشانگر است", + "semanticmaps-kml-text": "متن با هر صفحه مرتبط است. اگر هر خواص پرس‌وجو اضافی وجود داشته باشد، به‌وسیله آن خذف لغو شده.", + "semanticmaps-kml-title": "عنوان پیش‌زمینه برای نتایج", + "semanticmaps-kml-linkabsolute": "پیوندها باید (به عنوان مخالف به نسبی) مطلق باشند", + "semanticmaps-kml-pagelinktext": "متن برای استفاده پیوندها به صفحه، که در آن توسط عنوان صفحه $1 جایگزین خواهد شد", + "semanticmaps-shapes-improperformat": "شکل‌بندی نادرست $1. لطفاً مستندات را برای شکل‌بندی مشاهده کنید", + "semanticmaps-shapes-missingshape": "هیچ شکلی برای$1 پیدا نشد. لطفاً مستندات را برای شکل‌های دردسترس مشاهده کنید" } diff --git a/i18n/fi.json b/i18n/fi.json index 6d6e5a009..1298307b3 100644 --- a/i18n/fi.json +++ b/i18n/fi.json @@ -71,7 +71,6 @@ "maps-displaymap-par-static": "Onko kartan on oltava staattinen", "maps-fullscreen-button": "Koko näytön tila", "maps-fullscreen-button-tooltip": "Näytä kartta koko näytön tilassa tai upotettuna.", - "maps-googlemaps3-par-enable-fullscreen": "Koko näytön tila -painike", "validation-error-invalid-location": "Parametrin $1 on oltava sallittu sijainti.", "validation-error-invalid-locations": "Parametrin $1 on oltava yksi tai useampi sallittu sijainti.", "validation-error-invalid-width": "Parametrin $1 on oltava sallittu leveys.", @@ -102,6 +101,8 @@ "maps-par-width": "Mahdollistaa kartan leveyden asettamisen. Oletusyksikkönä on pikseli, mutta voit erikseen määrittää jonkin seuraavista yksiköistä: px, ex, em, %.", "maps-par-height": "Mahdollistaa kartan korkeuden asettamisen. Oletusyksikkönä on pikseli, mutta voit erikseen määrittää jonkin seuraavista yksiköistä: px, ex, em, %.", "maps-par-centre": "Sijainti, johon kartta keskitetään", + "maps-par-enable-fullscreen": "Koko näytön tila -painike", + "maps-par-kml": "Kartalle ladattavat KML-tiedostot.", "maps-googlemaps3-incompatbrowser": "Selaimesi ei ole yhteensopiva Google Maps v3:n kanssa.", "maps-googlemaps3-par-type": "Ensimmäiseksi näytettävä karttatyyppi.", "maps-googlemaps3-par-types": "Karttatasovalitsimen käyttämät karttatyypit.", @@ -110,14 +111,11 @@ "maps-googlemaps3-par-zoomstyle": "Loitonnusohjaimen tyyli.", "maps-googlemaps3-par-typestyle": "Karttatasovalitsimen tyyli.", "maps-googlemaps3-par-autoinfowindows": "Avaa automaattisesti kaikki tietoikkunat sen jälkeen, kun sivu on ladattu.", - "maps-googlemaps3-par-kml": "Kartalle ladattavat KML-tiedostot.", "maps-googlemaps3-par-fusiontables": "Google Fusion Tables -tunnukset, joka ladataan karttaan.", "maps-googlemaps3-par-tilt": "Kartan kallistus, kun käytössä on Google Maps.", "maps-googlemaps3-par-kmlrezoom": "Valitse paras loitonnustaso uudelleen sen jälkeen, kun KML-tasot on ladattu.", "maps-openlayers-par-controls": "Kartalle sijoitettavat ohjaimet.", "maps-openlayers-par-layers": "Tasot, jotka näytetään karttatasojen valinnassa. Kartan oletustaso on listan ensimmäinen taso.", - "maps-osm-par-thumbs": "Näytä pienoiskuvat", - "maps-osm-par-photos": "Näytä valokuvat", "mapeditor": "Kartan muokkausohjelma", "specialpages-group-maps": "Kartat", "mapeditor-parser-error": "Metadatan jäsennys epäonnistui. Käyttäjän antamat tiedot ohitetaan.", @@ -145,5 +143,24 @@ "mapeditor-form-field-showonhover": "Näytä työkaluvihjeenä", "mapeditor-mapparam-title": "Kartan parametrien muokkaus", "mapeditor-mapparam-defoption": "-Valitse parametri-", - "mapeditor-form-field-image": "Kuva" + "mapeditor-form-field-image": "Kuva", + "semanticmaps-unrecognizeddistance": "Arvoa $1 ei ole sallittu etäisyys.", + "semanticmaps-kml-link": "Näytä KLM-tiedosto", + "semanticmaps-default-kml-pagelink": "Näytä sivu $1", + "semanticmaps-latitude": "Leveyspiiri: $1", + "semanticmaps-longitude": "Pituuspiiri: $1", + "semanticmaps-altitude": "Korkeus: $1", + "semanticmaps-forminput-locations": "Sijainnit", + "semanticmaps-par-staticlocations": "Sijaintien luettelo voidaan lisätä karttaan kyselydatan lisäksi. Kuten display_points-parametrissa voit lisätä sijaintikohtaisen otsikon, kuvauksen ja kuvakkeen; erottimena on \"~\".", + "semanticmaps-par-showtitle": "Näyttää kohdemerkin tietoikkunan otsikon tai ei. Käytöstä poisto on usein hyödyllistä, jos tietoikkunan sisältö muotoillaan mallineella.", + "semanticmaps-par-hidenamespace": "Näytä nimiavaruuden otsikko kohdemerkin tietoikkunassa", + "semanticmaps-par-centre": "Kartan keskus. Jos sitä ei määritetä, kartta laskee automaattisesti optimaalisen keskuksen kartalla olevien kohdemerkkien perusteella.", + "semanticmaps-par-template": "Tietoikkunan sisällön muotoilussa käytettävä malline.", + "semanticmaps-par-geocodecontrol": "Näytä geokoodausohjaimet.", + "semanticmaps-kml-text": "Kuhunkin sivuun liittyvä teksti. Jos kyselyllä on lisäominaisuuksia, ne syrjäyttävät tämän.", + "semanticmaps-kml-title": "Tulossivun oletusotsikko", + "semanticmaps-kml-linkabsolute": "Ovatko linkit absoluuttisia (eivätkä suhteellisia)", + "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." } diff --git a/i18n/fr.json b/i18n/fr.json index 0d3c90072..ed434b056 100644 --- a/i18n/fr.json +++ b/i18n/fr.json @@ -17,7 +17,12 @@ "Wyz", "Weft", "Jonathan1", - "Lbayle" + "Lbayle", + "Yasten", + "Trial", + "Wladek92", + "Urhixidur", + "Grondin" ] }, "maps-desc": "Permet d’afficher des cartes dynamiques dans les pages du wiki, des adresses géo-codées et d'autres opérations géographiques.", @@ -52,11 +57,10 @@ "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 ces services de cartographie : $1}}.", - "maps-coordinates-description": "Crochet de l'analyseur pour formater les coordonnées, depuis et vers n'importe quel format supporté.", + "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 sur eux.", - "maps-displaypoint-description": "Affiche les cartes géographiques avec un ou plusieurs marqueurs wiki sur eux.", "maps-distance-description": "Convertit une distance d'une certaine unité prise en charge à son équivalent en utilisant une autre unité.", - "maps-finddestination-description": "Trouver une destination avec un point de départ donné (qui peuvent être dans n'importe lequel des formats supportés), une orientation initiale et une distance.", + "maps-finddestination-description": "Trouver une destination avec un point de départ donné (qui peut être dans n’importe lequel des formats pris en charge), une orientation initiale et 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 confondu 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.", @@ -108,7 +112,6 @@ "maps-displaymap-par-wmsoverlay": "Utiliser une superposition WMS", "maps-fullscreen-button": "Basculer en plein écran", "maps-fullscreen-button-tooltip": "Visualiser la carte en plein écran ou incorporé.", - "maps-googlemaps3-par-enable-fullscreen": "Activer le bouton plein écran", "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.", @@ -134,9 +137,11 @@ "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_leaflet": "Dépliant", - "maps-leaflet-par-zoom": "Permet de définir le niveau de zoom de la carte.", "maps-leaflet-par-defzoom": "Permet de définir le niveau de zoom par défaut de la carte.", - "maps-leaflet-par-resizable": "Permet de rendre la carte redimensionnable en tirant sur son coin inférieur droit.", + "maps-leaflet-par-layer": "La couche qui sera affichée pendant que la carte se charge.", + "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_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.", @@ -147,9 +152,11 @@ "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-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-par-markercluster": "Autoriser la fusion de plusieurs repères à proximité en un seul repère", "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.", @@ -157,17 +164,20 @@ "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-kml": "Fichiers KML à charger sur la carte.", "maps-googlemaps3-par-gkml": "Les fichiers KML hébergés par Google à charger sur la carte.", "maps-googlemaps3-par-fusiontables": "ID des tables de Google Fusion qui devrait être chargées sur la carte.", "maps-googlemaps3-par-tilt": "Inclinaison de la carte lors de l'utilisation de Google Maps.", "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-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-openlayers-par-controls": "Les contrôles à placer sur la carte.", "maps-openlayers-par-layers": "Les couches qui seront disponibles dans le sélecteur de couche. La première couche sera affichée lors du chargement de la carte.", "maps-openlayers-par-overlays": "Superposer les couches qui seront disponibles dans le sélecteur de couches. Ces couches seront affichées par dessus la couche normale, comme une sorte de marqueur.", - "maps-osm-par-thumbs": "Afficher des miniatures", - "maps-osm-par-photos": "Afficher des photos", "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.", @@ -203,5 +213,42 @@ "mapeditor-imageoverlay-button": "Ajouter la couverture d'image", "mapeditor-form-field-image": "Image", "mapeditor-imageoverlay-title": "Détails de la couverture d'image", - "mapeditor-form-field-visitedicon": "Icône visité" + "mapeditor-form-field-visitedicon": "Icône visité", + "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-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-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-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-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-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-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", + "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-mapsline": "Ligne géographique", + "validator-type-mapsline-list": "Liste des lignes", + "validator-type-mapslocation": "Emplacement géographique", + "validator-type-mapslocation-list": "Liste des emplacements", + "validator-type-mapsrectangle": "Rectangle géographique", + "validator-type-mapsrectangle-list": "Liste des 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" } diff --git a/i18n/frp.json b/i18n/frp.json index 4332dd3d3..18efd662f 100644 --- a/i18n/frp.json +++ b/i18n/frp.json @@ -41,8 +41,6 @@ "maps_centred_on": "Mapa centrâ dessus $1, $2.", "maps-googlemaps3-par-poi": "Montrar los pouents d’entèrèt.", "maps-openlayers-par-controls": "Los contrôlos a placiér sur la mapa.", - "maps-osm-par-thumbs": "Fâre vêre des figures", - "maps-osm-par-photos": "Fâre vêre des fotôs", "mapeditor": "Changior de mapa", "specialpages-group-maps": "Mapes", "mapeditor-none-text": "Nion", @@ -76,5 +74,18 @@ "mapeditor-imageoverlay-button": "Apondre la cuvèrta d’émâge", "mapeditor-form-field-image": "Émâge", "mapeditor-imageoverlay-title": "Dètalys de la cuvèrta d’émâge", - "mapeditor-form-field-visitedicon": "Icôna visitâ" + "mapeditor-form-field-visitedicon": "Icôna visitâ", + "semanticmaps-unrecognizeddistance": "La valor $1 est pas una distance valida.", + "semanticmaps-kml-link": "Vêre lo fichiér KML", + "semanticmaps-default-kml-pagelink": "Vêre la pâge $1", + "semanticmaps-loading-forminput": "Chargement du formulèro d’entrâ de la mapa...", + "semanticmaps_lookupcoordinates": "Èstimar les coordonâs", + "semanticmaps_enteraddresshere": "Buchiéd l’adrèce ique", + "semanticmaps-updatemap": "Misa a jorn de la mapa", + "semanticmaps-forminput-remove": "Enlevar", + "semanticmaps-forminput-add": "Apondre", + "semanticmaps-latitude": "Latituda : $1", + "semanticmaps-longitude": "Longituda : $1", + "semanticmaps-altitude": "Hôtior : $1", + "semanticmaps-forminput-locations": "Emplacements" } diff --git a/i18n/fy.json b/i18n/fy.json index c98005bea..0ac2a2c79 100644 --- a/i18n/fy.json +++ b/i18n/fy.json @@ -1,7 +1,8 @@ { "@metadata": { "authors": [ - "Robin0van0der0vliet" + "Robin0van0der0vliet", + "Robin van der Vliet" ] }, "maps-layer-property": "Eigenskip", diff --git a/i18n/gl.json b/i18n/gl.json index d91f8c3b0..b7ed21b2e 100644 --- a/i18n/gl.json +++ b/i18n/gl.json @@ -8,8 +8,8 @@ ] }, "maps-desc": "Permite incorporar mapas dinámicos, enderezos xeocodificados e outras operacións xeográficas nas páxinas do wiki", - "right-geocode": "Xeocódigo", - "action-geocode": "Xeocodificar nesta wiki", + "right-geocode": "Xeocodificar", + "action-geocode": "xeocodificar neste wiki", "maps_map": "Mapa", "maps-tracking-category": "Páxinas cun mapa renderizado coa extensión Maps", "maps-loading-map": "Cargando o mapa...", @@ -41,7 +41,6 @@ "maps-layer-type-supported-by": "Este tipo de capa só se pode empregar {{PLURAL:$2|co servizo de mapas $1|con estes servizos de mapas: $1}}.", "maps-coordinates-description": "Asociador do analizador para dar formato ás coordenadas, desde e cara a calquera formato soportado.", "maps-displaymap-description": "Mostrar os mapas xeográficos sen marcadores wiki sobre eles.", - "maps-displaypoint-description": "Mostrar os mapas xeográficos con, polo menos, un ou máis marcadores wiki sobre eles.", "maps-distance-description": "Converter unha distancia nunha certa unidade soportada na súa equivalente noutra unidade.", "maps-finddestination-description": "Atopar un destino a partir dun punto de partida (que pode ser en calquera dos formatos soportados), unha orientación inicial e unha distancia.", "maps-geocode-description": "Permite a xeocodificación de enderezos; noutras palabras, transformar as localizacións lexibles por humanos en conxuntos de coordenadas. Hai soporte para diversos servizos de xeocodificación, que non se deben confundir cos servizos de cartografía.", @@ -95,7 +94,6 @@ "maps-displaymap-par-wmsoverlay": "Utilizar unha sobreposición WMS", "maps-fullscreen-button": "Activar ou desactivar a pantalla completa", "maps-fullscreen-button-tooltip": "Mostrar o mapa en pantalla completa ou incrustado.", - "maps-googlemaps3-par-enable-fullscreen": "Activar o botón de pantalla completa", "validation-error-invalid-location": "O parámetro \"$1\" debe ser unha localización válida.", "validation-error-invalid-locations": "O parámetro \"$1\" debe ser unha ou máis localizacións válidas.", "validation-error-invalid-width": "O parámetro \"$1\" debe ser un largo válido.", @@ -121,9 +119,9 @@ "maps_map_cannot_be_displayed": "O mapa non se pode mostrar.", "maps-geocoder-not-available": "A funcionalidade de xeocodificación de mapas non está dispoñible; non se pode xeocodificar a súa situación.", "maps_leaflet": "Folleto", - "maps-leaflet-par-zoom": "Permite definir o nivel de zoom do mapa.", "maps-leaflet-par-defzoom": "Permite definir o nivel de zoom por defecto do mapa.", - "maps-leaflet-par-resizable": "Permite que o mapa sexa redimensionable tirando da súa esquina inferior dereita.", + "maps-leaflet-par-layer": "A capa que se mostrará cando se cargue o mapa.", + "maps-leaflet-par-overlaylayers": "As capas de transparencia que se mostrarán cando se cargue o mapa.", "maps_click_to_activate": "Prema para activar o mapa", "maps_centred_on": "Mapa centrado en $1, $2.", "maps-par-mappingservice": "Permite configurar o servizo de mapas que se empregará para xerar o mapa.", @@ -134,9 +132,11 @@ "maps-par-width": "Permite definir o largo do mapa. Por defecto, os píxeles asúmense como unidade, pero tamén pode especificar unha destas unidades: px, ex, em, %.", "maps-par-height": "Permite definir a altura do mapa. Por defecto, os píxeles asúmense como unidade, pero tamén pode especificar unha destas unidades: px, ex, em, %.", "maps-par-centre": "O lugar no que se debe centrar o mapa", + "maps-par-enable-fullscreen": "Activar o botón de pantalla completa", + "maps-par-kml": "Ficheiros KML que cargar no mapa.", + "maps-par-markercluster": "Permite fusionar múltiples marcadores cercanos nun só marcador", "maps-googlemaps3-incompatbrowser": "O seu navegador é compatible co Google Maps v3.", "maps-googlemaps3-par-imageoverlays": "Permite engadir unha imaxe a mostrar na situación especificada no mapa.", - "maps-googlemaps3-par-markercluster": "Permite fusionar múltiples marcadores cercanos nun só marcador", "maps-googlemaps3-par-type": "O tipo de mapa que mostrar inicialmente.", "maps-googlemaps3-par-types": "Os tipos de mapas que estarán dispoñibles a través do control de tipos.", "maps-googlemaps3-par-layers": "As capas especiais que cargar no mapa.", @@ -144,17 +144,15 @@ "maps-googlemaps3-par-zoomstyle": "O estilo do control do zoom.", "maps-googlemaps3-par-typestyle": "O estilo do control do tipo.", "maps-googlemaps3-par-autoinfowindows": "Abrir automaticamente todas as ventás de información tras a carga da páxina.", - "maps-googlemaps3-par-kml": "Ficheiros KML que cargar no mapa.", "maps-googlemaps3-par-gkml": "Ficheiros KML aloxados polo Google que cargar no mapa.", "maps-googlemaps3-par-fusiontables": "Identificadores das táboas do Google Fusion que se deben cargar no mapa.", "maps-googlemaps3-par-tilt": "Inclinación do mapa ao empregar o Google Maps.", "maps-googlemaps3-par-kmlrezoom": "Axustar o nivel de zoom despois da carga das capas KML.", "maps-googlemaps3-par-poi": "Mostrar os puntos de interese.", + "maps-googlemaps3-par-clustergridsize": "A medida da reixa dun grupo de píxeles.", "maps-openlayers-par-controls": "Os controis que incluír no mapa.", "maps-openlayers-par-layers": "As capas que estarán dispoñibles no selector de capas. A primeira capa aparecerá cando o mapa acabe de cargar.", "maps-openlayers-par-overlays": "As capas de sobreposición que estarán dispoñibles no selector de capas. Estas capas han mostrarse por riba da capa normal, como un marcador.", - "maps-osm-par-thumbs": "Mostrar as miniaturas", - "maps-osm-par-photos": "Mostrar as fotos", "mapeditor": "Editor de mapa", "specialpages-group-maps": "Mapas", "mapeditor-parser-error": "Houbo un erro ao analizar os metadatos. Ignórase a entrada do usuario.", @@ -190,5 +188,42 @@ "mapeditor-imageoverlay-button": "Engadir a sobreposición da imaxe", "mapeditor-form-field-image": "Imaxe", "mapeditor-imageoverlay-title": "Detalles da sobreposición da imaxe", - "mapeditor-form-field-visitedicon": "Icona visitada" + "mapeditor-form-field-visitedicon": "Icona visitada", + "semanticmaps-unrecognizeddistance": "O valor $1 non é unha distancia válida.", + "semanticmaps-kml-link": "Ollar o ficheiro KML", + "semanticmaps-default-kml-pagelink": "Ver a páxina \"$1\"", + "semanticmaps-latitude": "Latitude: $1", + "semanticmaps-longitude": "Lonxitude: $1", + "semanticmaps-altitude": "Altitude: $1", + "semanticmaps-forminput-locations": "Localizacións", + "semanticmaps-par-staticlocations": "Unha lista de localizacións para engadir ao mapa xunto aos datos consultados. Como con display_points, pode engadir un título, unha descrición e mais unha icona por localización mediante o signo \"~\" como separador.", + "semanticmaps-par-showtitle": "Mostrar ou non un título na ventá de información do marcador. Frecuentemente, desactivar isto é útil ao utilizar un modelo para dar formato ao contido da ventá de información.", + "semanticmaps-par-hidenamespace": "Mostrar o título do espazo de nomes na ventá de información do marcador", + "semanticmaps-par-centre": "O centro do mapa. Cando non se proporciona, o mapa ha escoller automaticamente o mellor centro para mostrar todos os marcadores no mapa.", + "semanticmaps-par-template": "Un modelo a empregar para dar formato ao contido da ventá de información.", + "semanticmaps-par-geocodecontrol": "Mostrar o control de xeocodificación.", + "semanticmaps-par-activeicon": "Icona a mostrar no canto do marcador predeterminado, cando a páxina é igual ao resultado da pescuda", + "semanticmaps-par-pagelabel": "Ao definirse en \"si\", todos os macadores terán un \"inlineLabel\" cunha ligazón cara á páxina que conten as coordenadas do marcador", + "semanticmaps-par-ajaxcoordproperty": "Nome da propiedade de coordenadas usada para construír a consulta Ajax.", + "semanticmaps-par-ajaxquery": "Unha segunda consulta que é enviada vía Ajax para recuperar as coordenadas adicionais.", + "semanticmaps-par-userparam": "O valor pasado en cada chamada de modelo, se se empregase algún modelo", + "semanticmaps-kml-text": "O texto asociado a cada páxina. Substituído polas propiedades pescudadas adicionais, se existen.", + "semanticmaps-kml-title": "O título por defecto para os resultados", + "semanticmaps-kml-linkabsolute": "Se as ligazóns deberían ser absolutas (contrario a relativas)", + "semanticmaps-kml-pagelinktext": "O texto a usar para as ligazóns cara á páxina, nas que \"$1\" será substituído polo título da páxina", + "semanticmaps-shapes-improperformat": "Formato incorrecto de \"$1\"; consulte a documentación sobre os formatos", + "semanticmaps-shapes-missingshape": "Non se atopou forma ningunha para \"$1\"; consulte a documentación sobre as formas dispoñibles", + "validator-type-mapscircle": "Círculo xeográfico", + "validator-type-mapscircle-list": "Lista de círculos", + "validator-type-mapsimageoverlay": "Superposición de imaxe", + "validator-type-mapsimageoverlay-list": "Lista de superposicións de imaxe", + "validator-type-mapsline": "Liña xeográfica", + "validator-type-mapsline-list": "Lista de liñas", + "validator-type-mapslocation": "Localización xeográfica", + "validator-type-mapslocation-list": "Lista de localizacións", + "validator-type-mapsrectangle": "Rectángulo xeográfico", + "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" } diff --git a/i18n/he.json b/i18n/he.json index 5c07b8635..8a4eba0d5 100644 --- a/i18n/he.json +++ b/i18n/he.json @@ -10,12 +10,14 @@ "ערן", "תומר ט", "Inkbug", - "Macofe" + "Macofe", + "Guycn2", + "המקיסט" ] }, "maps-desc": "הוספת האפשרות להטמעת מפות דינמיות אל תוך דפי ויקי, קידוד גאוגרפי של כתובות ופעולות גאוגרפיות אחרות", - "right-geocode": "לעשות קידוד גאוגרפי", - "action-geocode": "לעשות קידוד גאוגרפי בוויקי הזה", + "right-geocode": "ביצוע קידודים גאוגרפיים", + "action-geocode": "לבצע קידודים גאוגרפיים באתר הוויקי הזה", "maps_map": "מפה", "maps-tracking-category": "דפים עם מפה שהוכנה באמצעות הרחבת המפות", "maps-loading-map": "המפה נטענת...", @@ -47,7 +49,6 @@ "maps-layer-type-supported-by": "השכבה הזאת יכולה לעבוד רק עם {{PLURAL:$2|שירות המפות הבא|שירותי המפות הבאים}}: $1.", "maps-coordinates-description": "מילת הפעלה לעיצוב נקודות ציון, מכל תסדיר נתמך לכל תסדיר נתמך.", "maps-displaymap-description": "להציג מפות גאוגרפיות ללא שום סמנים מוגדרים בוויקי עליהן.", - "maps-displaypoint-description": "להציג מפות גאוגרפיות עם סמן מוגדר בוויקי אחד או יותר עליהן.", "maps-distance-description": "להמיר את המרחק ביחידות נתמכות מסוימות לערך מתאים ביחידות אחרות.", "maps-finddestination-description": "למצוא את היעד בהינתן נקודת התחלה (שיכולה להיות בכל פורמט), הכיוון הראשוני והמרחק.", "maps-geocode-description": "מפעיל קידוד גאוגרפי של כתובות, במילים אחרות, הופך מיקומים עם שמות שאנשים יכולים לקרוא לערכות של נקודות ציון. יש תמיכה במספר שירותי קידוד גאוגרפי, ואין להתבלבל בינם לבין שירותי מיפוי.", @@ -101,7 +102,6 @@ "maps-displaymap-par-wmsoverlay": "להשתמש בשכבת כיסוי WMS", "maps-fullscreen-button": "להפעיל מסך מלא", "maps-fullscreen-button-tooltip": "להציג את המפה במסך מלא או מוטבעת", - "maps-googlemaps3-par-enable-fullscreen": "להפעיל כתפור מסך מלא", "validation-error-invalid-location": "הערך $1 צריך להיות מיקום תקין.", "validation-error-invalid-locations": "הערך $1 צריך להיות מיקום תקין אחד או יותר.", "validation-error-invalid-width": "הערך $1 צריך להיות רוחב תקין.", @@ -126,6 +126,8 @@ "maps_unrecognized_coords_for": "{{PLURAL:$2|נקודת הציון הבאה לא זוהתה והושמטה|נקודות הציון הבאות לא זוהו והושמטו}} מהמפה: $1", "maps_map_cannot_be_displayed": "לא ניתן להציג את המפה.", "maps-geocoder-not-available": "הקידוד הגאוגרפי של מפות אינו זמין. לא ניתן לקודד את המיקום שנבחר.", + "maps_leaflet": "עלון", + "maps-leaflet-par-defzoom": "מאפשר להגדיר את רמת המרחק ההתחלתית במפה.", "maps_click_to_activate": "יש ללחוץ כדי להפעיל את המפה", "maps_centred_on": "המפה ממורכזת סביב $1,$2", "maps-par-mappingservice": "מאפשר הגדרת שירות המיפוי שישמש לחילול המפה.", @@ -136,9 +138,11 @@ "maps-par-width": "לאפשר הגדרת רוחב המפה. ברירת המחדל היא שהיחידה תהיה פיקסל, אבל אפשר להגדיר במפורש אחת מהיחידות הבאות: px, ex, em, %.", "maps-par-height": "לאפשר הגדרת גובה המפה. ברירת המחדל היא שהיחידה תהיה פיקסל, אבל אפשר להגדיר במפורש אחת מהיחידות הבאות: px, ex, em, %.", "maps-par-centre": "המיקום שבו המפה צריכה להיות ממורכזת", + "maps-par-enable-fullscreen": "להפעיל כתפור מסך מלא", + "maps-par-kml": "קבצי KML שייטענו אל המפה.", + "maps-par-markercluster": "מאפשר מיזוג מספר סמנים קרובים לסמן אחד", "maps-googlemaps3-incompatbrowser": "הדפדפן שלך לא תומך בגוגל מפות גרסה 3.", "maps-googlemaps3-par-imageoverlays": "מאפשר הוספת תמונה שתוצג במיקום מסוים על המפה.", - "maps-googlemaps3-par-markercluster": "מאפשר מיזוג מספר סמנים קרובים לסמן אחד", "maps-googlemaps3-par-type": "סוג המפה שיוצג תחילה.", "maps-googlemaps3-par-types": "סוגי המפה שיהיו זמינים דרך בקר הסוג.", "maps-googlemaps3-par-layers": "שכבות מיוחדות שייטענו אל המפה.", @@ -146,17 +150,18 @@ "maps-googlemaps3-par-zoomstyle": "סגנון בקר התקריב.", "maps-googlemaps3-par-typestyle": "סגנון בקר הסוג.", "maps-googlemaps3-par-autoinfowindows": "לפתוח את כל חלונות המידע אחרי שהדף נטען.", - "maps-googlemaps3-par-kml": "קבצי KML שייטענו אל המפה.", "maps-googlemaps3-par-gkml": "קבצי KML שמתארחים בגוגל וייטענו אל המפה.", "maps-googlemaps3-par-fusiontables": "מזהים של Google Fusion Tables שייטענו אל המפה.", "maps-googlemaps3-par-tilt": "רכינה עבור המפה כאשר נעשה שימוש במפות גוגל.", "maps-googlemaps3-par-kmlrezoom": "לקרב מחדש את המפה אחרי ששכבות KML נטענו.", "maps-googlemaps3-par-poi": "הצגת נקודות עניין.", + "maps-googlemaps3-par-clustergridsize": "גודל הרשת של האשכול בפיקסלים.", + "maps-par-clusterzoomonclick": "האם ההתנהגות הראשונית ללחיצה על אשכולית היא להתקרב לתוכו.", + "maps-googlemaps3-par-clusteraveragecenter": "האם המרכז של כל אשכול אמור להיות הממוצע של כל הסמנים באשכול.", + "maps-googlemaps3-par-clusterminsize": "המספר המזערי של הסמנים שאמורים להיות באשכול לפני שהסמנים מוסתרים ומוצג מונה.", "maps-openlayers-par-controls": "אילו בקרים להציב על המפה.", "maps-openlayers-par-layers": "אילו שכבות יהיו זמינות בבוחר השכבות. השכבה הראשונה תוצג כאשר המפה נטענת.", "maps-openlayers-par-overlays": "שכבות כיסוי שיהיו זמינות בבורר השכבות. השכבות האלו תוצגנה בראש שכבה רגילה, כמו סמן.", - "maps-osm-par-thumbs": "להציג תמונות ממוזערות.", - "maps-osm-par-photos": "להציג צילומים", "mapeditor": "עורך מפות", "specialpages-group-maps": "מפות", "mapeditor-parser-error": "אירעה שגיאה בעת עיבוד מטא־נתונים. קלט ממשתמשים לא יעובד.", @@ -192,5 +197,30 @@ "mapeditor-imageoverlay-button": "הוספת שכבת תמונה", "mapeditor-form-field-image": "תמונה", "mapeditor-imageoverlay-title": "פרטי שכבת תמונה", - "mapeditor-form-field-visitedicon": "סמל לסימון מקומות שביקרת בהם" + "mapeditor-form-field-visitedicon": "סמל לסימון מקומות שביקרת בהם", + "semanticmaps-unrecognizeddistance": "הערך $1 אינו מרחק תקין.", + "semanticmaps-kml-link": "הצגת קובץ KML", + "semanticmaps-default-kml-pagelink": "הצגת הדף $1", + "semanticmaps-latitude": "קו־רוחב: $1", + "semanticmaps-longitude": "קו־אורך: $1", + "semanticmaps-altitude": "גובה: $1", + "semanticmaps-forminput-locations": "מיקומים", + "semanticmaps-par-staticlocations": "רשימת מיקומים להוסיף למפה יחד עם הנתונים המבוקשים בשאילתה. כמו עם display_points, אפשר להוסיף כאן כותרת, תיאור וסמל לכל מיקום עם טילדה (~) בתור תו מפריד.", + "semanticmaps-par-showtitle": "להציג את הכותרת בחלון המידע על הסמן או לא. הכיבוי של זה שימושי לעתים קרובות כאשר נעשה שימוש בתבנית לעיצוב חלון המידע.", + "semanticmaps-par-hidenamespace": "האם להציג את שם המרחב בחלון המידע על סמן.", + "semanticmaps-par-centre": "מרכז המפה. אם לא ניתן, המפה תבחר בעצמה את המרכז המיטבי להצגת כל הסמנים על המפה.", + "semanticmaps-par-template": "תבנית לעיצוב תוכן חלון המידע.", + "semanticmaps-par-geocodecontrol": "הצגת בקר קידוד גאוגרפי.", + "semanticmaps-par-activeicon": "סמל שיוצג במקום הסמן הרגיל כאשר הדף הפעיל זהה לתוצאת השאילתה", + "semanticmaps-par-pagelabel": "כשזה מכון ל־\"yes\", לכל הסמנים יהיה \"inlineLabel\" עם קישור לדף שמכיל את נקודות הציון עבור הסמן", + "semanticmaps-par-ajaxcoordproperty": "שם המאפיין של נקודות הציון שמשמש לבניית שאילתת ה־ajax.", + "semanticmaps-par-ajaxquery": "שאילתה שנייה שנשלחת ב־ajax כדי לאחזר נקודות ציון נוספות.", + "semanticmaps-par-userparam": "ערך שמועבר לכל קריאה לתבנית, אם משמשת תבנית", + "semanticmaps-kml-text": "הטקסט משויך לכל עמוד ועמוד. נדרס במאפיינים אחרים שנעשית עליהם שאילתה, אם יש כאלה.", + "semanticmaps-kml-title": "כותרת לתוצאות לפי בררת המחדל.", + "semanticmaps-kml-linkabsolute": "האם הקישורים צריכים להיות מוחלטים (או יחסיים)", + "semanticmaps-kml-pagelinktext": "הטקסט שישמש לקישורים לדף, כאשר $1 יוחלף בכותרת הדף", + "semanticmaps-shapes-improperformat": "עיצוב לא מתאים עבור $1. נא לראות את התיעוד על עיצוב", + "semanticmaps-shapes-missingshape": "לא נמצאו צורות עבור $1. נא לראות את התיעוד עבור צורות", + "validator-type-mapscircle-list": "רשימת מעגלים" } diff --git a/i18n/hsb.json b/i18n/hsb.json index 183977ada..6777092eb 100644 --- a/i18n/hsb.json +++ b/i18n/hsb.json @@ -36,7 +36,6 @@ "maps-layer-type-supported-by": "Tutón typ runiny móže so {{PLURAL:$2|jenož z kartografiskej słužbu $1|z tutej kartografiskimaj słužbomaj: $1|z tutymi kartografiskimi słužbami: $1|z tutymi kartografiskimi słužbami: $1}}.wužiwać.", "maps-coordinates-description": "Parserowa hóčka za formatowanje koordinatow z a do podpěranych formatow.", "maps-displaymap-description": "Geografiske karty bjez we wikiju definowanych markow na nich zwobraznić.", - "maps-displaypoint-description": "Geografiske karty z jednym we wikiju definowanej marku abo wjace markow na nich zwobraznić.", "maps-distance-description": "Konwertuj distancu z pomocu wěsteje podpěraneje jednotki do jeje ekwiwalenta z pomocu druheje jednotki.", "maps-finddestination-description": "Cil z pomocu podateho startoweho dypka (kotryž móže w někajkim z podpěranych formatow być), spočatneho nasměrjenja a zdalenosće pytać.", "maps-geocode-description": "Zmóžnja geokodowanje adresow, hinak prajene, přetworja wot čłowjeka čitajomne městna do sadźbow koordinatow. Je podpěra za wjacore geokodowanske słužby, kotrež njeměli so z kartowymi słužbami zaměnić.", @@ -80,7 +79,6 @@ "maps-displaymap-par-wmsoverlay": "WMS-naworštowanje wužiwać", "maps-fullscreen-button": "Połnu wobrazowku přepinać", "maps-fullscreen-button-tooltip": "Kartu jako połnu wobrazowku abo zasadźenu pokazać", - "maps-googlemaps3-par-enable-fullscreen": "Tłóčatko połneje wobrazowki zmóžnić", "validation-error-invalid-location": "Parameter $1 dyrbi płaćiwe městno być.", "validation-error-invalid-locations": "Parameter $1 dyrbi jedne městno abo wjacore městna być.", "validation-error-invalid-width": "Parameter $1 dyrbi płaćiwa šěrokosć być.", @@ -110,6 +108,8 @@ "maps-par-mappingservice": "Zmóžnja nastajenje kartografiskeje słužby, kotraž ma so za wutworjenje karty wužiwać,", "maps-par-geoservice": "Geokodowanska słužba, kotraž ma so za konwertowanje adresow do koordinatow wužiwać.", "maps-par-centre": "Městno, hdźež karta ma so centrować", + "maps-par-enable-fullscreen": "Tłóčatko połneje wobrazowki zmóžnić", + "maps-par-kml": "KML-dataje, kotrež maja so na kartu začitać.", "maps-googlemaps3-incompatbrowser": "Twój wobhladowak njeje kompatibelny z Google Maps v3.", "maps-googlemaps3-par-type": "Kartowy typ, kotryž ma so na spočatku pokazać.", "maps-googlemaps3-par-layers": "Wosebite runiny, kotrež maja so na kartu začitać.", @@ -117,12 +117,9 @@ "maps-googlemaps3-par-zoomstyle": "Stil skalowanskeho wodźenja.", "maps-googlemaps3-par-typestyle": "Stil wodźenja za kartowe typy.", "maps-googlemaps3-par-autoinfowindows": "Wšě informaciske wokna awtomatisce wočinić, po tym zo strona je so začitała.", - "maps-googlemaps3-par-kml": "KML-dataje, kotrež maja so na kartu začitać.", "maps-googlemaps3-par-tilt": "Schilenje za kartu, hdyž so Google Maps wužiwa.", "maps-googlemaps3-par-poi": "Zajimawosće pokazać.", "maps-openlayers-par-controls": "Wodźenske elementy, kotrež maja so na karće zwobraznić.", - "maps-osm-par-thumbs": "Miniaturne wobrazki pokazać", - "maps-osm-par-photos": "Fotografije pokazać", "mapeditor": "Kartowy editor", "specialpages-group-maps": "Karty", "mapeditor-parser-error": "Při parsowanju metadatow je so zmylk wustupił. Zapodaća wužiwarja so ignoruja.", @@ -157,5 +154,26 @@ "mapeditor-imageoverlay-button": "Wobrazowe zablendowanje přidać", "mapeditor-form-field-image": "Wobraz", "mapeditor-imageoverlay-title": "Podrobnosće wo wobrazowym zablendowanju", - "mapeditor-form-field-visitedicon": "Symbol za wopytany" + "mapeditor-form-field-visitedicon": "Symbol za wopytany", + "semanticmaps-unrecognizeddistance": "Hódnota $1 płaćiwa distanca njeje.", + "semanticmaps-kml-link": "KML-dataju sej wobhladać", + "semanticmaps-default-kml-pagelink": "Nastawk $1 sej wobhladać", + "semanticmaps-latitude": "Šěrokostnik: $1", + "semanticmaps-longitude": "Dołhostnik: $1", + "semanticmaps-altitude": "Wysokosć: $1", + "semanticmaps-forminput-locations": "Městna", + "semanticmaps-par-staticlocations": "Lisćina městnow, kotrež maja so zhromadnje z naprašowanymi datami karće přidać. Kaž pola zwobraznjenskich dypkow móžeš titul. wopisanje a symbol na městno z pomocu tildy \"~\" jako dźělatko přidać.", + "semanticmaps-par-showtitle": "Titul w informaciskim woknje woznamjenjenja pokazać abo nic. Je husto wužitne, tutu opciju znjemóžnić, hdyž so předłoha wužiwa, zo by so wobsah informaciskeho wokna formatował.", + "semanticmaps-par-hidenamespace": "Mjeno mjenoweho ruma w informaciskim woknje woznamjenjenja pokazać.", + "semanticmaps-par-centre": "Srjedźišćo karty. Jeli je njepodate, budźe so karta awtomatisce optimalne srjedźišćo wuběrać, zo bychu so wšě woznamjenjenja na karće pokazali.", + "semanticmaps-par-template": "Předłoha, kotraž ma so za formatowanje wobsaha infowokna wužiwać,", + "semanticmaps-par-geocodecontrol": "Geokodowanske wodźenje pokazać", + "semanticmaps-par-activeicon": "Symbol, kotryž ma so město standardneho woznamjenjenja zwobraznić, hdyž aktiwna strona naprašowanskemu wuslědkej wotpowěduje.", + "semanticmaps-par-pagelabel": "Jeli na \"haj\" stajene, změju wšě marki \"inlineLabel\" z wotkazom k stronje, kotraž koordinaty marki wobsahuje.", + "semanticmaps-kml-text": "Tekst, kotryž je z kóždej stronu zwjazany. Naruna so přez přidatne naprašowane kajkosće, jeli tajke su.", + "semanticmaps-kml-title": "Standardny titul za wuslědki", + "semanticmaps-kml-linkabsolute": "Wotkazy měli absolutne być (nic relatiwne)", + "semanticmaps-kml-pagelinktext": "Tekst, kotryž ma so za wotkazy k tej stronje wužiwać, w kotrejž $1 so přez titul strony naruna", + "semanticmaps-shapes-improperformat": "$1 je so wopak formatował. Prošu hlej dokumentaciju za formatowanje", + "semanticmaps-shapes-missingshape": "Formy za $1 njejsu so namakali. Prošu hlej dokumentaciju za k dispoziciji stejace formy." } diff --git a/i18n/hu.json b/i18n/hu.json index 22c5c6219..46edba6f7 100644 --- a/i18n/hu.json +++ b/i18n/hu.json @@ -6,7 +6,8 @@ "Glanthor Reviol", "Misibacsi", "Máté", - "Macofe" + "Macofe", + "TK-999" ] }, "maps-desc": "Lehetővé teszi a dinamikus térképek beágyazását a wiki lapokba, címek geográfiai kódolását és más geográfiai műveleteket", @@ -51,12 +52,10 @@ "maps-geocoder-not-available": "A térképek kiterjesztés geokódoló funkciója nem elérhető. A tartózkodási helyed nem geokódolható.", "maps_click_to_activate": "Kattints a térkép aktiválásához", "maps_centred_on": "Térkép középre igazítva a következő koordináták alapján: $1, $2.", + "maps-par-kml": "Betöltendő KML fájlok.", "maps-googlemaps3-incompatbrowser": "A böngésződ nem kompatibilis a Google Maps v3-mal.", "maps-googlemaps3-par-type": "Elsőre megjelenítendő térkép típusa.", "maps-googlemaps3-par-zoomstyle": "A nagyítás irányításának stílusa.", - "maps-googlemaps3-par-kml": "Betöltendő KML fájlok.", - "maps-osm-par-thumbs": "Bélyegképek megjelenítése", - "maps-osm-par-photos": "Fényképek megjelenítése", "mapeditor": "Térkép szerkesztő", "specialpages-group-maps": "Térképek", "mapeditor-none-text": "Nincs", @@ -77,5 +76,21 @@ "mapeditor-form-field-icon": "Ikon", "mapeditor-form-field-group": "Csoport", "mapeditor-form-field-inlinelabel": "Beágyazott címke", - "mapeditor-form-field-image": "Kép" + "mapeditor-form-field-image": "Kép", + "semanticmaps-desc": "Lehetővé teszi a szemantikus MediaWiki kiterjesztéssel tárolt koordinátaadatok megtekintését és szerkesztését ([https://mapping.referata.com/wiki/Examples bemutató]).", + "semanticmaps-unrecognizeddistance": "A(z) $1 érték nem egy érvényes távolság.", + "semanticmaps-kml-link": "KML fájl megtekintése", + "semanticmaps-default-kml-pagelink": "A(z) $1 lap megtekintése", + "semanticmaps-loading-forminput": "Térkép űrlapjának betöltése…", + "semanticmaps_lookupcoordinates": "Koordináták felkeresése", + "semanticmaps_enteraddresshere": "Add meg a címet itt", + "semanticmaps-updatemap": "Térkép frissítése", + "semanticmaps-forminput-remove": "Eltávolítás", + "semanticmaps-forminput-add": "Hozzáadás", + "semanticmaps-latitude": "Szélesség: $1", + "semanticmaps-longitude": "Hosszúság: $1", + "semanticmaps-altitude": "Tengerszint feletti magasság: $1", + "semanticmaps-forminput-locations": "Helyszínek", + "semanticmaps-par-staticlocations": "A térképre a lekérdezett adatok mellett felveendő helyek listája. Akárcsak a megjelenítési pontokkal, minden helyhez megadhatsz címet, leírást és ikont hullámvonal (\"~\") elválasztóval.", + "semanticmaps-par-showtitle": "Megjelenítse a címet a jelző információs ablakában, vagy ne? Ennek kikapcsolás hasznos lehet, ha sablonnal formázod az információs ablak tartalmát." } diff --git a/i18n/ia.json b/i18n/ia.json index c56c0934f..0906445a7 100644 --- a/i18n/ia.json +++ b/i18n/ia.json @@ -21,13 +21,12 @@ "maps-layer-errors": "Errores", "maps-error-invalid-layertype": "Il non ha stratos del typo \"$1\". Solmente iste {{PLURAL:$3|typo|typos}} es supportate: $2", "maps-error-no-layertype": "Tu debe specificar le typo de strato. {{PLURAL:$2|Solmente iste typo|Iste typos}} es supportate: $1", - "validation-error-invalid-layer": "Le parametro $1 debe esser un strato valide.", - "validation-error-invalid-layers": "Le parametro $1 debe esser un o plus stratos valide.", + "validation-error-invalid-layer": "Le parametro \"$1\" debe esser un strato valide.", + "validation-error-invalid-layers": "Le parametro \"$1\" debe esser un o plus stratos valide.", "maps-layer-of-type": "Strato del typo $1", "maps-layer-type-supported-by": "Iste typo de strato pote {{PLURAL:$2|solmente esser usate con le servicio cartographic|esser usate con le sequente servicios cartographic:}} $1.", "maps-coordinates-description": "Uncino analysator pro formatar coordinatas, ex e in tote le formatos supportate.", "maps-displaymap-description": "Monstra cartas geographic sin marcatores definite in wiki.", - "maps-displaypoint-description": "Monstra cartas geographic con un o plus marcatores definite in wiki.", "maps-distance-description": "Converte un distantia in un del unitates supportate a su equivalente in un altere unitate.", "maps-finddestination-description": "Cercar un destination, date un puncto de initio (que pote esser in omne formato supportate), un direction initial e un distantia.", "maps-geocode-description": "Permitte le geocodification de adresses, in altere parolas, converte adresses conventional de locos in gruppos de coordinatas. Existe supporto pro plure servicios de geocodification, le quales non debe esser confundite con servicios cartographic.", @@ -60,7 +59,7 @@ "maps-geodistance-par-unit": "Le unitate in le qual presentar le distantia.", "maps-geodistance-par-decimals": "Le numero maxime de digitos fractional a usar in le valor resultante.", "maps-geodistance-par-mappingservice": "Le servicio de geocodification a usar pro geocodificar adresses.", - "maps-geodistance-par-geoservice": "Le servicio cartographic con le qual isto es usate.\nIsto pote influentiar le valor predefinite de servicio de geocodification.", + "maps-geodistance-par-geoservice": "Le servicio cartographic con le qual iste function del analysator es usate.\nIsto pote influentiar le valor predefinite del servicio de geocodification.", "maps-displaymap-par-mappingservice": "Permitte definir le servicio cartographic a usar pro generar le carta.", "maps-displaymap-par-coordinates": "Le loco in le qual le carta essera initialmente centrate.", "maps-displaymap-par-zoom": "Permitte definir le nivello de zoom del carta.\nSi isto non es fornite, e multiple marcatores es presente in le cata, le zoom que los arrangia melio essera prendite, non le predefinition configurabile.", @@ -99,6 +98,7 @@ "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-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.", "maps-googlemaps3-par-types": "Le typos de carta que essera disponibile via le controlo de typo.", @@ -107,7 +107,6 @@ "maps-googlemaps3-par-zoomstyle": "Le stilo del controlo de zoom.", "maps-googlemaps3-par-typestyle": "Le stilo del controlo de typo.", "maps-googlemaps3-par-autoinfowindows": "Aperir automaticamente tote le fenestras informative post le cargamento del pagina.", - "maps-googlemaps3-par-kml": "Files KML pro cargar in le carta.", "maps-googlemaps3-par-gkml": "KML files albergate per Google a cargar sur le carta.", "maps-googlemaps3-par-fusiontables": "IDs de tabellas de Google Fusion que debe esser cargate in le carta.", "maps-googlemaps3-par-tilt": "Inclination del carta durante le uso de Google Maps.", @@ -115,8 +114,6 @@ "maps-googlemaps3-par-poi": "Monstrar punctos de interesse.", "maps-openlayers-par-controls": "Le controlos a placiar super le carta.", "maps-openlayers-par-layers": "Le stratos que essera disponibile in le selector de stratos. Le prime strato essera monstrate durante le cargamento del carta.", - "maps-osm-par-thumbs": "Monstrar miniaturas", - "maps-osm-par-photos": "Monstrar photos", "mapeditor": "Editor cartographic", "specialpages-group-maps": "Cartas", "mapeditor-parser-error": "Un error occurreva durante le processamento del metadatos. Le entrata del usator es ignorate.", @@ -151,5 +148,22 @@ "mapeditor-imageoverlay-button": "Adder un imagine superponite", "mapeditor-form-field-image": "Imagine", "mapeditor-imageoverlay-title": "Detalios del imagine superponite", - "mapeditor-form-field-visitedicon": "Icone visitate" + "mapeditor-form-field-visitedicon": "Icone visitate", + "semanticmaps-unrecognizeddistance": "Le valor $1 non es un distantia valide.", + "semanticmaps-kml-link": "Vider le file KML", + "semanticmaps-default-kml-pagelink": "Vider articulo $1", + "semanticmaps-latitude": "Latitude: $1", + "semanticmaps-longitude": "Longitude: $1", + "semanticmaps-altitude": "Altitude: $1", + "semanticmaps-forminput-locations": "Locos", + "semanticmaps-par-staticlocations": "Un lista de locos a adder al carta con le datos resultante del consulta. Como con display_points, tu pote adder un titulo, description e icone per loco usante le tilde \"~\" como separator.", + "semanticmaps-par-showtitle": "Monstrar un titulo in le fenestra de information de marcator o non. Disactivar isto es sovente utile si un patrono es usate pro formatar le contento del fenestra de information.", + "semanticmaps-par-hidenamespace": "Monstrar le titulo del spatio de nomines in le fenestra de information del marcator.", + "semanticmaps-par-centre": "Le centro del carta. Si non specificate, le systema selige automaticamente le centro optimal pro monstrar tote le marcatores in le carta.", + "semanticmaps-par-template": "Un patrono a usar pro formatar le contento del fenestra de information.", + "semanticmaps-par-geocodecontrol": "Monstrar le controlo de geocodification.", + "semanticmaps-kml-text": "Le texto associate con cata pagina. Es supplantate per le additional proprietates consultate, si existe.", + "semanticmaps-kml-title": "Le titulo predefinite pro resultatos", + "semanticmaps-kml-linkabsolute": "Debe ligamines esser absolute (in vice de relative)", + "semanticmaps-kml-pagelinktext": "Le texto a usar pro le ligamines al pagina, in le quales $1 essera reimplaciate per le titulo de pagina" } diff --git a/i18n/id.json b/i18n/id.json index dc5692315..12173e1bc 100644 --- a/i18n/id.json +++ b/i18n/id.json @@ -6,7 +6,8 @@ "Irwangatot", "IvanLanin", "පසිඳු කාවින්ද", - "Macofe" + "Macofe", + "C5st4wr6ch" ] }, "maps-desc": "Mengaktifkan penyertaan peta dinamis dalam halaman wiki, pembuatan geokode alamat, dan operasi geografi lain", @@ -29,7 +30,6 @@ "maps-layer-type-supported-by": "Lapisan ini hanya dapat digunakan oleh layanan pemetaan {{PLURAL:$2|$1|$1}}.", "maps-coordinates-description": "Pengait parser untuk memformat koordinat, dari dan ke salah satu format yang didukung.", "maps-displaymap-description": "Menampilkan peta geografis tanpa mencantumkan penanda wiki terdefinisi.", - "maps-displaypoint-description": "Menampilkan peta geografis dengan satu atau lebih penanda wiki terdefinisi.", "maps-distance-description": "Mengubah jarak dengan menggunakan unit terdukung tertentu menjadi setaranya dengan menggunakan unit lain.", "maps-finddestination-description": "Menemukan tujuan dari suatu titik awal (dalam salah satu format yang didukung), arah, dan jarak.", "maps-geocode-description": "Memberikan geokode alamat, atau dengan kata lain, mengubah lokasi yang dapat dibaca manusia ke set koordinat. Tersedia dukungan untuk beberapa layanan geokode, yang berbeda dengan layanan pemetaan.", @@ -98,6 +98,7 @@ "maps-par-zoom": "Tingkat pembesaran peta. Secara bawaan, peta bertanda akan mengambil tingkat pembesaran yang masih menunjukkan semua tanda.", "maps-par-width": "Memungkinkan pengaturan lebar peta. Secara bawaan, piksel akan dianggap sebagai unit, tetapi Anda dapat menentukan secara eksplisit salah satu unit berikut: px, ex, em, %.", "maps-par-height": "Memungkinkan pengaturan tinggi peta. Secara bawaan, piksel akan dianggap sebagai unit, tetapi Anda dapat menentukan secara eksplisit salah satu unit berikut: px, ex, em, %.", + "maps-par-kml": "Berkas KML yang akan dimuat ke dalam peta.", "maps-googlemaps3-incompatbrowser": "Peramban Anda tidak kompatibel dengan Google Maps v3.", "maps-googlemaps3-par-type": "Jenis peta yang ditampilkan saat awal.", "maps-googlemaps3-par-types": "Jenis peta yang akan tersedia melalui kontrol jenis.", @@ -106,12 +107,9 @@ "maps-googlemaps3-par-zoomstyle": "Gaya kontrol pembesaran.", "maps-googlemaps3-par-typestyle": "Gaya jenis kontrol.", "maps-googlemaps3-par-autoinfowindows": "Otomatis membuka semua jendela info setelah halaman dimuat.", - "maps-googlemaps3-par-kml": "Berkas KML yang akan dimuat ke dalam peta.", "maps-googlemaps3-par-fusiontables": "ID Google Fusion Tables yang harus dimuat ke dalam peta.", "maps-openlayers-par-controls": "Kontrol yang akan ditempatkan pada peta.", "maps-openlayers-par-layers": "Lapisan yang akan tersedia pada pemilih lapisan. Lapisan pertama akan ditampilkan ketika peta dimuat.", - "maps-osm-par-thumbs": "Tampilkan gambar mini", - "maps-osm-par-photos": "Tampilkan foto", "mapeditor": "Penyunting peta", "specialpages-group-maps": "Peta", "mapeditor-none-text": "Tidak ada", @@ -139,5 +137,15 @@ "mapeditor-form-field-fillopcaity": "Transparansi isi", "mapeditor-mapparam-title": "Sunting parameter peta", "mapeditor-mapparam-defoption": "-Pilih parameter-", - "mapeditor-form-field-image": "Gambar" + "mapeditor-form-field-image": "Gambar", + "semanticmaps-unrecognizeddistance": "Nilai $1 bukan jarak yang sah.", + "semanticmaps-kml-link": "Lihat berkas KML", + "semanticmaps-default-kml-pagelink": "Lihat halaman $1", + "semanticmaps-forminput-locations": "Lokasi", + "semanticmaps-par-staticlocations": "Daftar lokasi yang akan ditambahkan ke dalam peta, berikut data kueri. Seperti halnya display_points, Anda dapat menambahkan judul, deskripsi, dan ikon per lokasi dengan menggunakan tanda tilde \"~\" sebagai pemisah.", + "semanticmaps-par-showtitle": "Tampilkan judul di jendela info penanda. Penonaktifan judul sering berguna ketika menggunakan templat untuk memformat isi jendela info.", + "semanticmaps-par-centre": "Pusat peta. Jika tidak disediakan, peta secara otomatis akan memilih pusat optimal untuk menampilkan semua penanda di peta.", + "semanticmaps-par-template": "Ttemplat yang digunakan untuk memformat isi jendela info.", + "semanticmaps-kml-linkabsolute": "Pranala sebaiknya adalah mutlak (lawan dari relatif)", + "semanticmaps-shapes-improperformat": "Format yang kurang tepat untuk $1. Silakan lihat dokumentasi untuk format." } diff --git a/i18n/it.json b/i18n/it.json index f78b36bc5..58a734f93 100644 --- a/i18n/it.json +++ b/i18n/it.json @@ -8,7 +8,9 @@ "Gianfranco", "HalphaZ", "Rosh", - "Viscontino" + "Viscontino", + "Matteocng", + "Kaspo" ] }, "maps-desc": "Consente di includere mappe dinamiche nelle pagine wiki, la geocodifica degli indirizzi ed altre operazioni geografiche", @@ -71,22 +73,25 @@ "maps_map_cannot_be_displayed": "La mappa non può essere visualizzata.", "maps-geocoder-not-available": "La funzionalità di geocodifica dell'estensione Maps non è disponibile. La tua posizione non può essere geocodificata.", "maps_leaflet": "Volantino", - "maps-leaflet-par-zoom": "Permette di impostare il livello di zoom della mappa.", "maps-leaflet-par-defzoom": "Permette l'impostazione del livello di zoom predefinito della mappa.", - "maps-leaflet-par-resizable": "Permette di realizzare una mappa di ridimensionabile trascinando al suo angolo in basso a destra.", "maps_click_to_activate": "Clicca per attivare la mappa.", "maps_centred_on": "Mappa centrata su $1, $2.", + "maps-par-enable-fullscreen": "Attiva bottone a tutto schermo", + "maps-par-kml": "File KML da caricare sulla mappa.", "maps-googlemaps3-incompatbrowser": "Si sta utilizzando un browser non compatibile con Google Maps v3.", "maps-googlemaps3-par-type": "Il tipo di mappa da mostrare inizialmente.", "maps-googlemaps3-par-layers": "Livelli speciali da caricare sulla mappa.", "maps-googlemaps3-par-controls": "I controlli da posizionare sulla mappa.", "maps-googlemaps3-par-zoomstyle": "Lo stile del controllo dello zoom.", - "maps-googlemaps3-par-kml": "File KML da caricare sulla mappa.", "maps-googlemaps3-par-gkml": "File KML ospitati da Google da caricare sulla mappa.", "maps-googlemaps3-par-poi": "Mostra punti di interesse.", + "maps-googlemaps3-par-clustergridsize": "La dimensione della griglia di un cluster in pixels.", + "maps-par-clustermaxzoom": "Il massimo livello di zoom in cui i cluster possono esistere.", + "maps-par-clusterzoomonclick": "Se il comportamento predefinito quando si clicca su un cluster è di fare lo zoom su di esso.", + "maps-par-maxclusterradius": "Il raggio massimo coperto da un cluster.", + "maps-googlemaps3-par-clusteraveragecenter": "Se il centro di ogni cluster dovrebbe essere la media di tutti i marcatori del cluster.", + "maps-googlemaps3-par-clusterminsize": "Il numero minimo di marcatori presenti in un cluster dopo il quale tutti i marcatori saranno nascosti e sarà mostrato il loro numero.", "maps-openlayers-par-controls": "I controlli da posizionare sulla mappa.", - "maps-osm-par-thumbs": "Visualizza miniature", - "maps-osm-par-photos": "Mostra foto", "mapeditor": "Editor della mappa", "specialpages-group-maps": "Mappe", "mapeditor-parser-error": "Si verificato un errore durante l'analisi dei metadati. Ignorato l'input dell'utente.", @@ -118,5 +123,39 @@ "mapeditor-mapparam-defoption": "-Selezionare parametro-", "mapeditor-form-field-image": "Immagine", "mapeditor-imageoverlay-title": "Dettagli di sovrapposizione di immagini", - "mapeditor-form-field-visitedicon": "Icona visitato" + "mapeditor-form-field-visitedicon": "Icona visitato", + "semanticmaps-unrecognizeddistance": "Il valore $1 non è una distanza valida.", + "semanticmaps-kml-link": "Visualizza il file KML", + "semanticmaps-default-kml-pagelink": "Visualizza la pagina $1", + "semanticmaps-latitude": "Latitudine: $1", + "semanticmaps-longitude": "Longitudine: $1", + "semanticmaps-altitude": "Altitudine: $1", + "semanticmaps-forminput-locations": "Luoghi", + "semanticmaps-par-staticlocations": "Un elenco di luoghi da aggiungere alla mappa unitamente ai dati interrogati. Come con display_points, si può aggiungere un titolo, la descrizione e l'icona per ogni posizione utilizzando la tilde \"~\" come separatore.", + "semanticmaps-par-showtitle": "Mostrare oppure no un titolo nella finestra informazioni per l'indicatore. Disattivarlo è spesso utile quando si utilizza un template per la formattazione del contenuto della finestra informazioni.", + "semanticmaps-par-hidenamespace": "Mostrare il namespace del titolo nella finestra informazioni per l'indicatore", + "semanticmaps-par-centre": "Il centro della mappa. Quando non indicato, la mappa sceglierà automaticamente il centro ottimale per visualizzare tutti gli indicatori sulla mappa.", + "semanticmaps-par-template": "Un template da utilizzare per formattare il contenuto della finestra informazioni.", + "semanticmaps-par-geocodecontrol": "Mostra il controllo per geocodifica.", + "semanticmaps-par-activeicon": "Icona da mostrare al posto dell'indicatore predefinito, quando la pagina attiva è uguale al risultato dell'interrogazione", + "semanticmaps-par-pagelabel": "Quando impostato su \"yes\", tutti gli indicatori avranno un \"inlineLabel\" con un collegamento alla pagina che contiene le coordinate per l'indicatore", + "semanticmaps-par-userparam": "Un valore passato in ogni chiamata a template, se un template è utilizzato", + "semanticmaps-kml-text": "Il testo associato per ogni pagina. Sovrascritto dall'eventuale proprietà dell'interrogazione aggiuntiva.", + "semanticmaps-kml-title": "Il titolo predefinito per i risultati", + "semanticmaps-kml-linkabsolute": "I collegamenti dovranno essere assoluti (anziché relativi)", + "semanticmaps-kml-pagelinktext": "Il testo da utilizzare per i collegamenti alla pagina, in cui $1 verrà sostituito dal titolo della pagina", + "semanticmaps-shapes-improperformat": "Formattazione impropria di $1. Per favore controlla la documentazione sulla formattazione", + "semanticmaps-shapes-missingshape": "Nessuna forma trovata per $1. Per favore controlla la documentazione sulle forme disponibili", + "validator-type-mapscircle": "Cerchio geografico", + "validator-type-mapscircle-list": "Elenco di cerchi", + "validator-type-mapsimageoverlay": "Sovrapposizione di immagini", + "validator-type-mapsimageoverlay-list": "Elenco delle sovrapposizioni di immagini", + "validator-type-mapsline": "Linea geografica", + "validator-type-mapsline-list": "Elenco di linee", + "validator-type-mapslocation": "Posizione geografica", + "validator-type-mapslocation-list": "Elenco delle località", + "validator-type-mapsrectangle": "Rettangolo geografico", + "validator-type-mapsrectangle-list": "Elenco di rettangoli", + "validator-type-mapspolygon": "Poligono geografico", + "validator-type-mapspolygon-list": "Elenco dei poligoni geografici" } diff --git a/i18n/ja.json b/i18n/ja.json index 44d22fb3f..be0e1a9da 100644 --- a/i18n/ja.json +++ b/i18n/ja.json @@ -5,7 +5,8 @@ "Fryed-peach", "Shirayuki", "Yanajin66", - "青子守歌" + "青子守歌", + "Schu" ] }, "maps-desc": "ウィキページへの動的な地図の埋め込み、住所の座標データへの変換、その他の地図関連の操作ができるようにする。", @@ -46,7 +47,6 @@ "maps-displaymap-par-wmsoverlay": "WMS オーバーレイを使用", "maps-fullscreen-button": "全画面表示を切り替え", "maps-fullscreen-button-tooltip": "地図の表示を全画面または埋め込みに切り替えます。", - "maps-googlemaps3-par-enable-fullscreen": "全画面表示のボタンを有効にする", "validation-error-invalid-location": "引数「$1」には有効な場所を指定してください。", "validation-error-invalid-locations": "引数「$1」には有効な場所を 1 つ以上指定してください。", "validation-error-invalid-width": "引数「$1」には有効な幅を指定してください。", @@ -76,6 +76,8 @@ "maps_click_to_activate": "クリックして地図をアクティブに", "maps_centred_on": "地図の中心は $1、$2。", "maps-par-centre": "地図の中心にしたい場所", + "maps-par-enable-fullscreen": "全画面表示のボタンを有効にする", + "maps-par-kml": "地図に読み込む KML ファイルです。", "maps-googlemaps3-incompatbrowser": "あなたのブラウザーは Google マップ v3 と互換性がありません。", "maps-googlemaps3-par-type": "最初に表示する地図の種類です。", "maps-googlemaps3-par-types": "種類コントロールで利用できる、地図の種類です。", @@ -83,9 +85,6 @@ "maps-googlemaps3-par-controls": "地図上に配置するコントロールです。", "maps-googlemaps3-par-zoomstyle": "ズーム コントロールのスタイルです。", "maps-googlemaps3-par-typestyle": "種類コントロールのスタイルです。", - "maps-googlemaps3-par-kml": "地図に読み込む KML ファイルです。", - "maps-osm-par-thumbs": "縮小版を表示", - "maps-osm-par-photos": "写真を表示", "mapeditor": "地図の編集", "specialpages-group-maps": "地図", "mapeditor-parser-error": "メタデータの構文解析でエラーが発生しました。利用者の入力を無視しています。", @@ -120,5 +119,26 @@ "mapeditor-imageoverlay-button": "画像オーバーレイを追加", "mapeditor-form-field-image": "画像", "mapeditor-imageoverlay-title": "画像オーバーレイの詳細", - "mapeditor-form-field-visitedicon": "訪問済みアイコン" + "mapeditor-form-field-visitedicon": "訪問済みアイコン", + "semanticmaps-unrecognizeddistance": "値$1は有効な距離ではありません。", + "semanticmaps-kml-link": "KMLファイルを閲覧", + "semanticmaps-default-kml-pagelink": "ページ$1を表示", + "semanticmaps-latitude": "緯度: $1", + "semanticmaps-longitude": "経度: $1", + "semanticmaps-altitude": "高度: $1", + "semanticmaps-forminput-locations": "場所", + "semanticmaps-par-staticlocations": "問い合わせたデータと共に地図に追加する場所の列挙です。display_points と同様に、区切り文字としてチルダ「~」を使用して、場所ごとにタイトル、説明、アイコンを追加できます。", + "semanticmaps-par-showtitle": "マーカーの情報ウィンドウのタイトルを表示するかどうか。情報ウィンドウのコンテンツをフォーマットするためにテンプレートを使用するとき、これを無効にすると便利です。", + "semanticmaps-par-hidenamespace": "マーカー情報ウィンドウに名前空間名を表示する", + "semanticmaps-par-centre": "地図の中心。提供されていないときは、自動的に地図上のすべてのマーカーを表示するための最適な中心が選択されます。", + "semanticmaps-par-template": "情報ウィンドウのコンテンツの整形に使用するテンプレートです。", + "semanticmaps-par-geocodecontrol": "ジオコーディングコントロールを表示します。", + "semanticmaps-par-activeicon": "アクティブなページがクエリ結果と等しい場合に、デフォルトのアイコンの代わりに表示されるアイコン", + "semanticmaps-par-pagelabel": "「はい」に設定すると、マーカーの緯度経度を含むページにリンクされた「インライン ラベル」がすべてのマーカーに付きます。", + "semanticmaps-kml-text": "各ページに関連付けられたテキストです。クエリに追加的なプロパティがある場合は上書きされます。", + "semanticmaps-kml-title": "結果の既定のタイトル", + "semanticmaps-kml-linkabsolute": "リンクは絶対表記 (= 相対表記の対義語) にしてください。", + "semanticmaps-kml-pagelinktext": "ページへのリンクに使用するテキスト ($1 はページ名に置換される)", + "semanticmaps-shapes-improperformat": "$1 を不適切な形式に整形しようとしました。整形の説明文書を参照してください。", + "semanticmaps-shapes-missingshape": "$1 の図形が見つかりません。利用できる図形について説明文書を参照してください。" } diff --git a/i18n/ko.json b/i18n/ko.json index 28cab5eaf..9855076b0 100644 --- a/i18n/ko.json +++ b/i18n/ko.json @@ -6,10 +6,11 @@ "Priviet", "아라", "Revi", - "Macofe" + "Macofe", + "Ykhwong" ] }, - "maps-desc": "위키 문서에 동적 지도를 포함하고, 주소의 좌표화와 다른 지리적 작업을 할 수 있습니다", + "maps-desc": "위키 문서에 동적 지도를 넣고, 주소의 좌표화와 다른 지리적 작업을 할 수 있습니다", "right-geocode": "좌표화", "maps_map": "지도", "maps-tracking-category": "지도를 지도 확장 기능으로 표시하는 문서", @@ -41,16 +42,15 @@ "maps-layer-of-type-and-name": "\"$1\" 형식의 \"$2\" 레이어", "maps-layer-type-supported-by": "이 레이어 유형은 {{PLURAL:$2|$1 매핑 서비스에서만 사용할 수 있습니다.|다음 매핑 서비스에서 사용할 수 있습니다: $1}}", "maps-coordinates-description": "지원되는 좌표 형식간에 서로 변환하는 파서 훅입니다.", - "maps-displaymap-description": "지도에 위키 정의한 표시 없이 지리적 지도를 보여줍니다.", - "maps-displaypoint-description": "지도에 하나 이상의 표시가 있는 지리적 지도를 보여줍니다.", + "maps-displaymap-description": "지도에 위키 정의한 표시 없이 지리적 지도를 표시합니다.", "maps-distance-description": "다른 단위를 사용하여 해당하는 특정 지원되는 단위를 사용하여 거리를 변환합니다.", "maps-finddestination-description": "주어진 시작점과 (지원되는 형식을 사용할 수 있습니다) 초기 방위, 거리로 목적지를 찾습니다.", "maps-geocode-description": "주소, 즉 좌표 집합으로 인간이 읽을 수 있는 위치를 전환하는 좌표화를 활성화합니다. 매핑 서비스와 혼동하지 않아야 하는 여러 좌표화 서비스에 대한 지원이 있습니다.", "maps-geodistance-description": "지원하는 형식으로 두 점 사이의 지리적 거리를 계산합니다.", - "maps-mapsdoc-description": "기본 값과 설명과 함께 지정된 좌표화 서비스에 대한 변수로 테이블을 보여줍니다.", + "maps-mapsdoc-description": "기본 값과 설명과 함께 지정된 좌표화 서비스에 대한 변수로 테이블을 표시합니다.", "maps-layerdefinition-description": "지도기능으로 표시할 수 있는 사용자 정의 레이어를 설명하세요.", - "maps-mapsdoc-par-service": "변수 설명문을 보여줄 좌표화 서비스입니다.", - "maps-mapsdoc-par-language": "설명문을 보여줄 언어입니다. 변역을 사용할 수 없으면 영어가 대신 사용됩니다.", + "maps-mapsdoc-par-service": "변수 설명문서를 표시할 좌표화 서비스입니다.", + "maps-mapsdoc-par-language": "설명문을 표시할 언어입니다. 이러한 번역을 사용할 수 없으면 영어가 대신 사용됩니다.", "maps-coordinates-par-location": "형식을 지정할 좌표입니다.", "maps-coordinates-par-format": "죄표에 대한 대상 형식입니다.", "maps-coordinates-par-directional": "좌표가 방향이 출력되어야 할 것인지를 나타냅니다.", @@ -71,32 +71,31 @@ "maps-geocode-par-allowcoordinates": "이 기능에서 좌표에 대한 지원을 비활성화할 수 있습니다. 예나 아니오여야 합니다. 아니오일 때, 모든 값은 올바른 좌표조차 좌표화됩니다.", "maps-geocode-par-format": "결과 좌표에 대한 형식입니다.", "maps-geocode-par-directional": "좌표가 방향이 출력되어야 할 것인지를 나타냅니다.", - "maps-geodistance-par-location1": "집합에서 사이의 거리를 계산하는 첫번째 지점입니다.", - "maps-geodistance-par-location2": "집합에서 사이의 거리를 계산하는 두번째 지점입니다.", + "maps-geodistance-par-location1": "집합에서 사이의 거리를 계산하는 첫 번째 지점입니다.", + "maps-geodistance-par-location2": "집합에서 사이의 거리를 계산하는 두 번째 지점입니다.", "maps-geodistance-par-unit": "거리를 출력할 단위입니다.", "maps-geodistance-par-decimals": "결과 값에 사용할 최대 소수 자릿수입니다.", "maps-geodistance-par-mappingservice": "주소를 좌표화로 사용할 좌표화 서비스입니다.", "maps-geodistance-par-geoservice": "함께 사용될 매핑 서비스입니다.\n기본 좌표화 서비스 값에 영향을 줄 수 있습니다.", "maps-displaymap-par-mappingservice": "지도를 생성하는데 사용될 매핑 서비스를 설정할 수 있습니다.", - "maps-displaymap-par-coordinates": "지도에 보여줄 하나 이상의 위치입니다. 표시로 표시됩니다.", + "maps-displaymap-par-coordinates": "지도에 표시할 하나 이상의 위치입니다. 표시로 표시됩니다.", "maps-displaymap-par-visitedicon": "원래의 마커가 클릭된 후 마커 아이콘에 사용된 이미지 파일 이름이 클릭되었습니다", "maps-displaymap-par-zoom": "지도의 확대 수준을 설정할 수 있습니다.\n제공하지 않고 여러 표시가 지도에 존재하지 않으면 설정한 기본값이 아닌 최고의 맞춤 확대로 설정됩니다.", "maps-displaymap-par-centre": "display_point에 대한 지도의 중심 좌표를 설정할 수 있습니다.\n주소와 좌표를 모두 사용할 수 있습니다.\n이 속성이 제공되어 있지 않은 경우 지도는 제공한 표시에 대한 자체 중심에 있거나 제공한 표시 사이에 있습니다.", "maps-displaymap-par-title": "특정 제목이 없는 모든 표시의 팝업에 보여줄 텍스트를 설정할 수 있습니다.\n레이블과 함께 사용하면 제목은 굵고 밑줄이 그어집니다.", "maps-displaymap-par-label": "특정 레이블이 없는 마든 표시의 팝업에 보여줄 텍스트를 설정할 수 있습니다.", - "maps-displaymap-par-icon": "모든 표시에 사용하는 아이콘을 설정할 수 있습니다.", - "maps-displaymap-par-circles": "보여줄 동그라미", - "maps-displaymap-par-copycoords": "무언가를 클릭할 때 대화 상자에 위키의 좌표를 보여줄 때", - "maps-displaymap-par-lines": "보여줄 선", + "maps-displaymap-par-icon": "모든 표시에 사용하는 아이콘을 설정할 수 있게 합니다.", + "maps-displaymap-par-circles": "표시할 동그라미", + "maps-displaymap-par-copycoords": "좌표를 복사한 위치를 클릭할 때 대화상자를 표시합니다.", + "maps-displaymap-par-lines": "표시할 선", "maps-displaymap-par-maxzoom": "최대 확대 수준", "maps-displaymap-par-minzoom": "최소 확대 수준", - "maps-displaymap-par-polygons": "보여줄 다각형", - "maps-displaymap-par-rectangles": "보려줄 네모", - "maps-displaymap-par-static": "지도는 정적이어야 할 때", + "maps-displaymap-par-polygons": "표시할 다각형", + "maps-displaymap-par-rectangles": "표시할 직사각형", + "maps-displaymap-par-static": "지도를 정적으로 만듭니다.", "maps-displaymap-par-wmsoverlay": "WMS 오버레이 사용", "maps-fullscreen-button": "전체화면으로 전환", "maps-fullscreen-button-tooltip": "지도를 전체 화면 또는 삽입된 형태로 보기", - "maps-googlemaps3-par-enable-fullscreen": "전체 화면 버튼 활성화", "validation-error-invalid-location": "$1 변수는 올바른 위치여야 합니다.", "validation-error-invalid-locations": "$1 변수는 하나 이상의 올바른 위치여야 합니다.", "validation-error-invalid-width": "$1 변수는 올바른 너비여야 합니다.", @@ -131,6 +130,8 @@ "maps-par-width": "지도의 너비을 설정할 수 있습니다. 기본적으로 픽셀은 단위로 간주되지만, 명시적으로 이러한 단위 중 하나를 지정할 수 있습니다: px, ex, em, %.", "maps-par-height": "지도의 높이를 설정할 수 있습니다. 기본적으로 픽셀은 단위로 간주되지만, 명시적으로 이러한 단위 중 하나를 지정할 수 있습니다: px, ex, em, %.", "maps-par-centre": "지도의 중심으로 할 위치", + "maps-par-enable-fullscreen": "전체 화면 버튼 활성화", + "maps-par-kml": "지도에 불러올 KML 파일입니다.", "maps-googlemaps3-incompatbrowser": "브라우저는 Google 지도 v3와 호환되지 않습니다.", "maps-googlemaps3-par-type": "처음 보여줄 지도 종류입니다.", "maps-googlemaps3-par-types": "종류 컨트롤을 통해 사용할 수 있는 지도 종류입니다.", @@ -139,7 +140,6 @@ "maps-googlemaps3-par-zoomstyle": "줌 컨트롤의 스타일입니다.", "maps-googlemaps3-par-typestyle": "종류 컨트롤의 스타일입니다.", "maps-googlemaps3-par-autoinfowindows": "문서를 불러오고 나서 모든 정보 창을 자동으로 엽니다.", - "maps-googlemaps3-par-kml": "지도에 불러올 KML 파일입니다.", "maps-googlemaps3-par-gkml": "지도에 불러올 Google이 호스트하는 KML 파일입니다.", "maps-googlemaps3-par-fusiontables": "지도에 불러와야 할 Google 퓨전 테이블의 ID입니다.", "maps-googlemaps3-par-tilt": "Google 지도를 사용할 때의 기울기입니다.", @@ -148,8 +148,6 @@ "maps-openlayers-par-controls": "지도에 놓을 컨트롤입니다.", "maps-openlayers-par-layers": "레이서 선택기에서 사용할 수 있는 레이어입니다. 첫 레이어는 지도를 불러올 때 보여줍니다.", "maps-openlayers-par-overlays": "레이어 선택기에서 사용 가능하게 될 오버레이 레이어. 이 레이어는 정상 레이어의 맨 위에 일종의 마커처럼 표시될 것입니다.", - "maps-osm-par-thumbs": "섬네일 보기", - "maps-osm-par-photos": "사진 보기", "mapeditor": "지도 편집기", "specialpages-group-maps": "지도", "mapeditor-parser-error": "메타데이터를 구문 분석하는 동안 오류가 발생했습니다. 사용자 입력을 무시합니다.", @@ -185,5 +183,29 @@ "mapeditor-imageoverlay-button": "그림 배치 추가", "mapeditor-form-field-image": "그림", "mapeditor-imageoverlay-title": "그림 배치 자세한 사항", - "mapeditor-form-field-visitedicon": "방문한 아이콘" + "mapeditor-form-field-visitedicon": "방문한 아이콘", + "semanticmaps-unrecognizeddistance": "$1 값은 올바른 거리가 아닙니다.", + "semanticmaps-kml-link": "KML 파일 보기", + "semanticmaps-default-kml-pagelink": "$1 문서 보기", + "semanticmaps-latitude": "위도: $1", + "semanticmaps-longitude": "경도: $1", + "semanticmaps-altitude": "고도: $1", + "semanticmaps-forminput-locations": "위치", + "semanticmaps-par-staticlocations": "쿼리된 데이터와 함께 지도를 추가하는 위치의 목록입니다. display_points와 같이, 구분자로 물결표 \"~\"를 사용하여 위치마다 제목과 설명, 아이콘을 추가할 수 있습니다.", + "semanticmaps-par-showtitle": "표시 정보 창에서 제목에 보여주거나 보여주지 않습니다. 정보 창 내용의 형식에 틀을 사용할 때 이를 비활성화하면 편리합니다.", + "semanticmaps-par-hidenamespace": "표시 정보 창의 이름공간 제목 보이기", + "semanticmaps-par-centre": "지도의 가운데입니다. 제공하지 않으면, 지도는 자동으로 지도에서 모든 표시를 표시할 최적의 가운데를 선택합니다.", + "semanticmaps-par-template": "정보 창 내용을 형식에 사용하는 틀입니다.", + "semanticmaps-par-geocodecontrol": "좌표화 컨트롤을 보여줍니다.", + "semanticmaps-par-activeicon": "활성화된 문서는 쿼리 결과와 같을 때 아이콘이 기본값 표시기 대신에 표시해야 합니다", + "semanticmaps-par-pagelabel": "\"yes\"로 설정하면, 모든 마커가 마커의 좌표를 포함하는 문서로 연결된 링크와 \"inlineLabel\"을 가집니다.", + "semanticmaps-kml-text": "각 문서와 연관된 텍스트입니다. 추가적인 쿼리된 속성이 있으면 덮어씁니다.", + "semanticmaps-kml-title": "결과에 대한 기본 제목", + "semanticmaps-kml-linkabsolute": "링크는 (상대적와 반대인) 절대적이어야 합니다", + "semanticmaps-kml-pagelinktext": "$1 문서는 문서 제목으로 바뀔 문서의 링크에 사용하는 텍스트", + "semanticmaps-shapes-improperformat": "$1의 부적절한 형식입니다. 형식에 대해서는 설명서를 참조하세요.", + "semanticmaps-shapes-missingshape": "$1에 대한 모양을 찾을 수 없습니다. 사용할 수 있는 모양에 대해서는 설명서를 참조하세요", + "validator-type-mapsimageoverlay": "이미지 오버레이", + "validator-type-mapsimageoverlay-list": "이미지 오버레이의 목록", + "validator-type-mapslocation": "지리적 위치" } diff --git a/i18n/ksh.json b/i18n/ksh.json index a5a90823c..b0994cc6e 100644 --- a/i18n/ksh.json +++ b/i18n/ksh.json @@ -48,6 +48,6 @@ "maps_click_to_activate": "Donn klecke, öm op di Kaat ze jonn", "maps_centred_on": "De Kaat met $1, $2 en de Medde.", "maps-googlemaps3-par-imageoverlays": "Määd et müjjelesch, e Beld obb em aanjejovve Plaz op dä Kaat aanzeije ze lohße.", - "maps-googlemaps3-par-markercluster": "Määd et müjjelesch, benaachbaate Makehronge zesamme ze faße.", + "maps-par-markercluster": "Määd et müjjelesch, benaachbaate Makehronge zesamme ze faße.", "maps-googlemaps3-par-kmlrezoom": "Donn di Kaat norr_ens zoome, nohdäm de layers vum KLM jelaade sin." } diff --git a/i18n/lb.json b/i18n/lb.json index 0a800b90f..f61bf4e93 100644 --- a/i18n/lb.json +++ b/i18n/lb.json @@ -16,7 +16,7 @@ "maps-copycoords-prompt": "CTRL+C, ENTER", "maps-searchmarkers-text": "Markéierunge fir Filteren", "maps-others": "anerer", - "maps-layer-property": "Eegeschaft", + "maps-layer-property": "Eegenschaft", "maps-layer-value": "Wäert", "maps-layer-errors": "Feeler", "maps-layerdef-invalid": "Net valabel {{PLURAL:$1|Definitioun|Definitiounen}}", @@ -44,7 +44,6 @@ "maps-displaymap-par-polygons": "Polygonen fir ze weisen", "maps-displaymap-par-rectangles": "Rechtecker fir ze weisen", "maps-displaymap-par-static": "D'Kaart statesch maachen", - "maps-googlemaps3-par-enable-fullscreen": "'Fullscreen'-Knäppchen aschalten", "validation-error-invalid-location": "Parameter $1 muss eng valabel Plaz sinn.", "validation-error-invalid-locations": "Parameter $1 muss eng oder méi valabel Plaze sinn.", "validation-error-invalid-width": "Parameter $1 muss eng valabel Breet sinn.", @@ -68,15 +67,15 @@ "maps_map_cannot_be_displayed": "D'Kaart kann net gewise ginn.", "maps-geocoder-not-available": "D'Funktioun vun der Geocodéierung vu Kaarten ass net disponibel. Äre Standuert konnt net geocodéiert ginn.", "maps_leaflet": "Depliant", + "maps-leaflet-par-layer": "De Layer dee gewise gëtt wann d'Kaart geluede gëtt.", "maps_click_to_activate": "Klickt fir d'kaart z'aktivéieren", "maps_centred_on": "D'Kaart ass zentréiert op $1, $2", + "maps-par-enable-fullscreen": "'Fullscreen'-Knäppchen aschalten", + "maps-par-kml": "KML-Fichieren déi op d'Kaart musse geluede ginn.", "maps-googlemaps3-incompatbrowser": "Äre Browser ass net mat Google Maps v3 kompatibel.", "maps-googlemaps3-par-type": "Den Typ vu Kaart den am Ufank gewise gëtt.", "maps-googlemaps3-par-autoinfowindows": "Automatesch all Fënstere mat Informatiounen opmaachen nodeem d'Säit geluede gouf.", - "maps-googlemaps3-par-kml": "KML-Fichieren déi op d'Kaart musse geluede ginn.", "maps-googlemaps3-par-poi": "Kuckeswäertes weisen", - "maps-osm-par-thumbs": "Miniaturbiller weisen", - "maps-osm-par-photos": "Fotoe weisen", "specialpages-group-maps": "Kaarten", "mapeditor-none-text": "Keen", "mapeditor-done-button": "Fäerdeg", @@ -98,5 +97,26 @@ "mapeditor-form-field-showonhover": "Nëmme weise wann een driwwer fiert", "mapeditor-mapparam-title": "Parametere vun der Kaart änneren", "mapeditor-mapparam-defoption": "-Parameter eraussichen-", - "mapeditor-form-field-image": "Bild" + "mapeditor-form-field-image": "Bild", + "semanticmaps-unrecognizeddistance": "De Wäert $1 ass keng valabel Distanz.", + "semanticmaps-kml-link": "KML-Fichier weisen", + "semanticmaps-default-kml-pagelink": "Säit $1 weisen", + "semanticmaps-latitude": "Breedegrad: $1", + "semanticmaps-longitude": "Längtegrad: $1", + "semanticmaps-altitude": "Héicht: $1", + "semanticmaps-forminput-locations": "Plazen", + "semanticmaps-kml-title": "De Standard-Titel fir Resultater", + "semanticmaps-kml-linkabsolute": "Solle Linken absolut sinn (am Géigesaz zu relativ)", + "validator-type-mapscircle": "Geographesche Krees", + "validator-type-mapscircle-list": "Lëscht vun de Kreesser", + "validator-type-mapsimageoverlay": "Bildiwwerlagerung", + "validator-type-mapsimageoverlay-list": "Lëscht vun de Bildiwwerlagerungen", + "validator-type-mapsline": "Geographesch Linn", + "validator-type-mapsline-list": "Lëscht vu Linnen", + "validator-type-mapslocation": "Geographesch Plaz", + "validator-type-mapslocation-list": "Lëscht vu Plazen", + "validator-type-mapsrectangle": "Geographesche Rechteck", + "validator-type-mapsrectangle-list": "Lëscht vun de Rechtecken", + "validator-type-mapspolygon": "Geographesche Polygon", + "validator-type-mapspolygon-list": "Lëscht vun de geographesche Polygonen" } diff --git a/i18n/lt.json b/i18n/lt.json index b4cc5ac20..1356ce082 100644 --- a/i18n/lt.json +++ b/i18n/lt.json @@ -11,22 +11,108 @@ "action-geocode": " šioje wiki atlikite geokodavimą", "maps_map": "Žemėlapis", "maps-loading-map": "Kraunamas žemėlapis ...", + "maps-load-failed": "Nepavyko įkelti žemėlapio!", + "maps-markers": "Žymekliai", + "maps-copycoords-prompt": "CTRL+C, ENTER", "maps-others": "kita", + "maps-ns-layer": "Sluoksnis", + "maps-ns-layer-talk": "Sluoksnio aptarimas", + "maps-layer-property": "Savybė", + "maps-layer-value": "Vertė", + "maps-layer-errors": "Klaidos", + "maps-layerdef-invalid": "{{PLURAL:$1|Negalimas apibrėžimas|Negalimi apibrėžimai}}", + "maps-layerdef-invalid-fatal": "Galutinis negalimas apibrėžimas", + "maps-layerpage-usage": "Puslapiai su žemėlapiais, naudojantys sluoksnį „$1“", + "maps-layerpage-nousage": "Šiuo metu nėra puslapių, naudojančiu šį sluoksnį", + "maps-error-invalid-layertype": "Nėra „$1“ tipo sluoksnių. Tik {{PLURAL:$3|šis tipas|šie tipai}} yra palaikomi: $2", + "maps-error-no-layertype": "Turite nurodyti sluoksnio tipą. {{PLURAL:$2|Tik šis tipas|Tik šie tipai}} yra palaikomi: $1", + "validation-error-invalid-layer": "Parametras „$1“ turi būti galimas sluoksnis.", + "validation-error-invalid-layers": "Parametras „$1“ turi būti vienas ar daugiau galimų sluoksnių.", + "maps-layer-of-type": "„$1“ tipo sluoksnis", + "maps-layer-of-type-and-name": "„$1“ tipo sluoksnis „$2“", + "maps-coordinates-par-location": "Koordinatės, kurias norite formatuoti.", + "maps-finddestination-par-location": "Pradinė vieta.", + "maps-finddestination-par-distance": "Kelionės atstumas.", + "maps-displaymap-par-circles": "Rodomi apskritimai", + "maps-displaymap-par-lines": "Rodomos linijos", + "maps-displaymap-par-maxzoom": "Didžiausias priartinimo lygis", + "maps-displaymap-par-minzoom": "Mažiausias priartinimo lygis", + "maps-displaymap-par-polygons": "Rodomi daugiakampiai", + "maps-displaymap-par-rectangles": "Rodomi stačiakampiai", + "maps-displaymap-par-static": "Padaryti žemėlapį statinį", + "maps-displaymap-par-wmsoverlay": "Naudoti WMS perdangą", + "maps-fullscreen-button": "Perjungti į visą ekraną", + "maps-fullscreen-button-tooltip": "Peržiūrėti žemėlapį visame ekrane arba kaip integruotą", + "validation-error-invalid-location": "Parametras „$1“ turi būti galima vieta.", + "validation-error-invalid-locations": "Parametras „$1“ turi būti viena ar kelios galimos vietos.", + "validation-error-invalid-width": "Parametras „$1“ turi būti galimas plotis.", + "validation-error-invalid-height": "Parametras „$1“ turi būti galimas aukštis.", + "validation-error-invalid-distance": "Parametras „$1“ turi būti galimas atstumas.", + "validation-error-invalid-distances": "Parametras „$1“ turi būti vienas ar keli galimi atstumai.", + "validation-error-invalid-image": "Parametras „$1“ turi būti galimas paveikslėlis.", + "validation-error-invalid-images": "Parametras „$1“ turi būti vienas ar keli galimi paveikslėliai.", "maps-abb-north": "Š", "maps-abb-east": "R", "maps-abb-south": "P", "maps-abb-west": "V", "maps-latitude": "Platuma:", "maps-longitude": "Ilguma:", - "maps-invalid-coordinates": "Vertė $ 1 nepripažįstama kaip galiojanti koordinatė.", + "maps-invalid-coordinates": "Vertė $1 nepripažįstama kaip galiojančios koordinatės.", "maps_coordinates_missing": "Nesudarytos koordinatės žemėlapiui.", "maps_geocoding_failed": "{{PLURAL:$2|Šis adresas|Šie adresai}} negali būti kartografuoti: $1.", "maps_unrecognized_coords": "{{PLURAL:$2|Ši koordinatė|Šios koordinatės}} nebuvo atpažintos: $1.", "maps_map_cannot_be_displayed": "Žemėlapis negal būti parodytas.", "maps_leaflet": "Lankstinukas", + "maps-leaflet-par-defzoom": "Leidžia nustatyti numatytąjį žemėlapio priartinimo lygį.", + "maps-leaflet-par-layer": "Sluoksnis, kuris bus rodomas kai žemėlapis bus įkeltas.", "maps_click_to_activate": "Spustelėkite, norėdami įjungti žemėlapį", "maps_centred_on": "Žemėlapis centruotas link $1, $2.", + "maps-par-enable-fullscreen": "Įgalinti viso ekrano mygtuką", + "maps-par-markercluster": "Leidžia sujungti kelius netoliese esančius žymeklius į vieną žymeklį", + "maps-googlemaps3-incompatbrowser": "Jūsų naršyklė nėra suderinama su Google Maps v3.", + "maps-googlemaps3-par-type": "Pradinis žemėlapio tipas.", "mapeditor": "Žemėlapio redaktorius", "specialpages-group-maps": "Žemėlapiai", - "mapeditor-done-button": "Atlikta" + "mapeditor-none-text": "Nėra", + "mapeditor-done-button": "Atlikta", + "mapeditor-remove-button": "Pašalinti", + "mapeditor-import-button2": "Importuoti", + "mapeditor-export-button": "Eksportuoti į viki kodą", + "mapeditor-import-button": "Importuoti iš viki kodo", + "mapeditor-select-button": "Pasirinkti šį daugiakampį", + "mapeditor-mapparam-button": "Redaguoti žemėlapio parametrus", + "mapeditor-clear-button": "Valyti žemėlapį", + "mapeditor-code-title": "Viki kodas", + "mapeditor-import-title": "Importuoti vikį kodą", + "mapeditor-form-title": "Redaguoti informaciją", + "mapeditor-link-title-switcher-popup-text": "Iššokantis langas su tekstu", + "mapeditor-link-title-switcher-link-text": "Nuoroda", + "mapeditor-form-field-title": "Pavadinimas", + "mapeditor-form-field-text": "Tekstas", + "mapeditor-form-field-link": "Nuoroda", + "mapeditor-form-field-icon": "Ikona", + "mapeditor-form-field-group": "Grupė", + "mapeditor-form-field-strokecolor": "Potėpio spalva", + "mapeditor-form-field-strokeopacity": "Potėpio matomumas", + "mapeditor-form-field-strokeweight": "Potėpio svoris", + "mapeditor-form-field-fillcolor": "Užpildo spalva", + "mapeditor-form-field-fillopcaity": "Užpildo matomumas", + "mapeditor-form-field-showonhover": "Rodyti tik užvedus pelytę", + "mapeditor-mapparam-title": "Redaguoti žemėlapio parametrus", + "mapeditor-mapparam-defoption": "-Pasirinkti parametrą-", + "mapeditor-form-field-image": "Paveikslėlis", + "semanticmaps-kml-link": "Žiūrėti KML failą", + "semanticmaps-default-kml-pagelink": "Žiūrėti puslapį $1", + "semanticmaps-latitude": "Platuma: $1", + "semanticmaps-longitude": "Ilguma: $1", + "semanticmaps-altitude": "Aukštis: $1", + "semanticmaps-forminput-locations": "Vietovės", + "validator-type-mapscircle": "Geografinis apskritimas", + "validator-type-mapscircle-list": "Apskritimų sąrašas", + "validator-type-mapsline": "Geografinė linija", + "validator-type-mapsline-list": "Linijų sąrašas", + "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" } diff --git a/i18n/mk.json b/i18n/mk.json index 4fad1ddf4..87f62a491 100644 --- a/i18n/mk.json +++ b/i18n/mk.json @@ -6,7 +6,7 @@ "Macofe" ] }, - "maps-desc": "Дава можност за вметнување на динамички карти во викистраници, геокодирање на адреси и други географски операции", + "maps-desc": "Дава можност за вметнување на динамички карти во викистраници, геокодирање на адреси и други географски постапки", "right-geocode": "Геокод", "action-geocode": "вршење на геокодирање на ова вики", "maps_map": "Карта", @@ -38,9 +38,8 @@ "maps-layer-of-type": "Слој од типот $1", "maps-layer-of-type-and-name": "Слој „$2“ од типот „$1“", "maps-layer-type-supported-by": "Овој тип на слој може да се користи {{PLURAL:$2|само со картографската служба $1|само со следниве картографски служби: $1}}.", - "maps-coordinates-description": "Расчленувачка кука за форматирање на координати. Ги претвора сите поддржани формати.", + "maps-coordinates-description": "Расчленувачкиот пресретник за форматирање на координати. Ги претвора сите поддржани формати.", "maps-displaymap-description": "Приказ на географски карти без никакви обележувачи на нив определени од викито.", - "maps-displaypoint-description": "Приказ на географски карти со една или повеќе обележувачи на нив определени од викито.", "maps-distance-description": "Претворање на растојание од една во друга поддржана единица.", "maps-finddestination-description": "Пронаоѓање на одредница со зададена почетна точка (која може да биде во било кој поддржан формат), првична насока и растојание", "maps-geocode-description": "Овозможува геокодирање на адреси, што значи дека претвора места во координати. Има поддршка за неколку служби за геокодирање (не е исто што и картографски служби).", @@ -53,7 +52,7 @@ "maps-coordinates-par-format": "Целниот формат за координатите.", "maps-coordinates-par-directional": "Покажува дали координатите треба да се испишат со или без насока.", "maps-distance-par-distance": "Растојанието во кое претворате е истоветно со единицата што се укажува.", - "maps-distance-par-decimals": "Максимален број на децимални места што ќе се користат во изводната вредност.", + "maps-distance-par-decimals": "Највеќе децимални места што ќе се користат во изводната вредност.", "maps-distance-par-unit": "Единица на испишаното растојание во изводот.", "maps-finddestination-par-location": "Првичната местоположба.", "maps-finddestination-par-bearing": "Првичната насока.", @@ -72,7 +71,7 @@ "maps-geodistance-par-location1": "Првата точка во пресметката на растојанието.", "maps-geodistance-par-location2": "Втората точка во пресметката на растојанието.", "maps-geodistance-par-unit": "Во која единица да се испише растојанието.", - "maps-geodistance-par-decimals": "Максималниот број на децимални места за добиената вредност.", + "maps-geodistance-par-decimals": "Допуштениот број на децимални места за добиената вредност.", "maps-geodistance-par-mappingservice": "Службата за геокодирање што се користи за геокодирање на било која адреса.", "maps-geodistance-par-geoservice": "Со која картографска служба ќе ја користите оваа расчленувачка функција.\nОва може да влијае на основната вредност за служба за геокодирање.", "maps-displaymap-par-mappingservice": "Овозможува задавање на картографска служба што ќе се користи за создавање на картата.", @@ -86,15 +85,14 @@ "maps-displaymap-par-circles": "Кругови за приказ", "maps-displaymap-par-copycoords": "Прикажувај прозорче со координатите на местото при стискање на место од кајшто можат да се прекопираат.", "maps-displaymap-par-lines": "Линии за приказ", - "maps-displaymap-par-maxzoom": "Максимална приближеност", - "maps-displaymap-par-minzoom": "Минимална приближеност", + "maps-displaymap-par-maxzoom": "Најголема приближеност", + "maps-displaymap-par-minzoom": "Најмала приближеност", "maps-displaymap-par-polygons": "Многуаголници за приказ", "maps-displaymap-par-rectangles": "Правоаголници за приказ", "maps-displaymap-par-static": "Напарви ја картата неподвижна", "maps-displaymap-par-wmsoverlay": "Користи WMS-облога", "maps-fullscreen-button": "Префрли на широк екран", "maps-fullscreen-button-tooltip": "Поглед на картата на цел екран или вгнездено.", - "maps-googlemaps3-par-enable-fullscreen": "Овозможи копче за цел екран", "validation-error-invalid-location": "Параметарот $1 мора да претставува важечка местоположба.", "validation-error-invalid-locations": "Параметарот $1 мора да претставува една или повеќе важечки местоположби.", "validation-error-invalid-width": "Параметарот $1 мора да претставува важечка ширина.", @@ -121,6 +119,11 @@ "maps-geocoder-not-available": "Функцијата за геокодирање на Карти е недостапна. Вашата местоположба не може да се геокодира.", "maps_googlemaps3": "Google Карти вер. 2", "maps_leaflet": "Леток", + "maps-leaflet-par-defzoom": "Овозможува задавање на основен степен на приближеност на картата.", + "maps-leaflet-par-layer": "Слојот што ќе се покажува кога се вчитува картата.", + "maps-leaflet-par-overlaylayers": "Слоеви на облогата што ќе се покажуваат кога се вчитува картата.", + "maps-leaflet-par-maxclusterradius": "Најголемиот полупречник што може да го покрие еден грозд од средишниот обележувач (во пиксели).", + "maps-leaflet-par-clusterspiderfy": "Кога ќе стиснете на грозд при најслаба приближеност, истиот ќе се разграни за да можете да ги видите сите негови обележувачи.", "maps_click_to_activate": "Стиснете за активирање на картата", "maps_centred_on": "Средиште на картата во $1, $2.", "maps-par-mappingservice": "Овозможува назначување на картографска служба што ќе се користи за создавање на картата.", @@ -131,9 +134,11 @@ "maps-par-width": "Овозможува задавање на ширина на картата во пиксели по основно, но по желба можете да изберете една од следниве единици: px, ex, em, %.", "maps-par-height": "Овозможува задавање на висина на картата во пиксели по основно, но по желба можете да изберете една од следниве единици: px, ex, em, %.", "maps-par-centre": "На која местоположба да се сосредоточи картата", + "maps-par-enable-fullscreen": "Овозможи копче за цел екран", + "maps-par-kml": "KML-податотеки за вчитување во картата.", + "maps-par-markercluster": "Овозможува спојување на повеќе соседни бележници во еден", "maps-googlemaps3-incompatbrowser": "Вашиот прелистувач не е погоден за Google Карти вер. 3.", "maps-googlemaps3-par-imageoverlays": "Овозможува додавање на слика што ќе се прикажува на укажаното место на картата.", - "maps-googlemaps3-par-markercluster": "Овозможува спојување на повеќе соседни бележници во еден", "maps-googlemaps3-par-type": "Типот на карта за првичниот приказ.", "maps-googlemaps3-par-types": "Типовите на карти што ќе бидат достапни преку контролата за тип.", "maps-googlemaps3-par-layers": "Посебни слоеви за вчитување во картата.", @@ -141,17 +146,20 @@ "maps-googlemaps3-par-zoomstyle": "Стил на контролата за приближување.", "maps-googlemaps3-par-typestyle": "Стилот на контролата за тип.", "maps-googlemaps3-par-autoinfowindows": "Автоматски отворај ги сите инфопрозорци откако ќе се вчита страницата.", - "maps-googlemaps3-par-kml": "KML-податотеки за вчитување во картата.", "maps-googlemaps3-par-gkml": "KML-податотеки вдомени од Google за вчитување на карта.", "maps-googlemaps3-par-fusiontables": "Назнаки (ID) на табели од Google Fusion Tables што треба да се вчитаат во картата.", "maps-googlemaps3-par-tilt": "Наклон на картата кога користите Google Карти.", "maps-googlemaps3-par-kmlrezoom": "Приближи ја картата повторно откако ќе се вчитаат KML-слоевите.", "maps-googlemaps3-par-poi": "Прикажи точки од интерес.", + "maps-googlemaps3-par-clustergridsize": "Решеткаста големина на грозд во пиксели.", + "maps-par-clustermaxzoom": "Најголемиот степен на приближеност при кој можат да постојат гроздови.", + "maps-par-clusterzoomonclick": "Дали, по основно, стискањето на грозд ќе го приближи истиот.", + "maps-par-maxclusterradius": "Најголемиот полупречник што може да го покрие еден грозд.", + "maps-googlemaps3-par-clusteraveragecenter": "Дали средиштето на секој гроз треба да биде просек од сите обележувачи во гроздот.", + "maps-googlemaps3-par-clusterminsize": "Најмалиот број на обележувачи што ќе бидат во грозд пред истите да бидат скриени и да се прикаже нивната бројка.", "maps-openlayers-par-controls": "Контролите што сакате да стојат на картата.", "maps-openlayers-par-layers": "Слоевите што ќе бидат достапни во бирачот на слоеви. Кога ќе се вчита картата ќе се прикаже првиот слој.", "maps-openlayers-par-overlays": "Слоеви за облоги што ќе бидат на располагање во одделот за слоеви. Ќе се прикажуваат врз обичните слоеви, налик на обележувачи.", - "maps-osm-par-thumbs": "Прикажи минијатури", - "maps-osm-par-photos": "Прикажи слики", "mapeditor": "Уредник на карти", "specialpages-group-maps": "Карти", "mapeditor-parser-error": "Се појави грешка при расчленувањето на метаподатоците. Го занемарувам уделот на корисникот.", @@ -187,5 +195,42 @@ "mapeditor-imageoverlay-button": "Додај облога од слика", "mapeditor-form-field-image": "Слика", "mapeditor-imageoverlay-title": "Поединости за облога од слика", - "mapeditor-form-field-visitedicon": "Икона за посетено" + "mapeditor-form-field-visitedicon": "Икона за посетено", + "semanticmaps-unrecognizeddistance": "Вредноста $1 не претставува важечко растојание.", + "semanticmaps-kml-link": "Преглед на KML-податотеката", + "semanticmaps-default-kml-pagelink": "Преглед на статијата $1", + "semanticmaps-latitude": "Геог. ширина: $1", + "semanticmaps-longitude": "Геог. должина: $1", + "semanticmaps-altitude": "Надм. вис: $1", + "semanticmaps-forminput-locations": "Места", + "semanticmaps-par-staticlocations": "Список на места за додавање во картатата заедно со побараните податоци. Како и со „display_points“, тука можете да додадете наслов, опис и икона за секое место, користејќи тилда (~) како одделувач.", + "semanticmaps-par-showtitle": "Дали да се прикажува насловот на инфопрозорецот на ознаката. Оневозможете го кога користите шаблон за форматирање на содржината на инфопрозорецот.", + "semanticmaps-par-hidenamespace": "Дали да се прикажува називот на именскиот простор во инфопрозорецот за ознаката.", + "semanticmaps-par-centre": "Средиштето на картата. Ако не е укажано, картата автоматски ќе го одбере средиштето кајшто оптимално ќе се прикажат сите одбележувачи на картата.", + "semanticmaps-par-template": "Шаблон за форматирање на содржината на инфопрозорецот.", + "semanticmaps-par-geocodecontrol": "Прикажи геокодни котроли.", + "semanticmaps-par-activeicon": "Икона за приказ наместо стандардниот бележник, кога активна страница се совпаѓа со бараното", + "semanticmaps-par-pagelabel": "Ако му е зададено „да“, сите обележувачи ќе имаат „inlineLabel“ со врска до страницата што ги содржат координатите за обележувачот", + "semanticmaps-par-ajaxcoordproperty": "Назив на координатното својство со кое ќе се срочи барање со Ajax.", + "semanticmaps-par-ajaxquery": "Второ барање што се праша преку Ajax за добивање на дополнителни координати.", + "semanticmaps-par-userparam": "Вредност која се дава при секое повикување на шаблон, ако се користи", + "semanticmaps-kml-text": "Текстот на секоја страница. Се презапишува ако има дополнителни барани својства.", + "semanticmaps-kml-title": "Стандарден наслов за исходот", + "semanticmaps-kml-linkabsolute": "Дали врските да бидат апсолутни (наспроти релативни)", + "semanticmaps-kml-pagelinktext": "Текстот на врските на страницата, каде $1 ќе се замени со насловот на страницата", + "semanticmaps-shapes-improperformat": "Несоодветно форматирање на $1. Погледајте во документацијата како треба да се форматира.", + "semanticmaps-shapes-missingshape": "Не пронајдов облици за $1. Погледајте во документацијата кои облици ви се на располагање.", + "validator-type-mapscircle": "Географски круг", + "validator-type-mapscircle-list": "Список на кругови", + "validator-type-mapsimageoverlay": "Обложна слика", + "validator-type-mapsimageoverlay-list": "Список на обложни слики", + "validator-type-mapsline": "Географска линија", + "validator-type-mapsline-list": "Список на линии", + "validator-type-mapslocation": "Географска месност", + "validator-type-mapslocation-list": "Список на месности", + "validator-type-mapsrectangle": "Географски правоаголник", + "validator-type-mapsrectangle-list": "Список на правоаголници", + "validator-type-mapspolygon": "Географски многуаголник", + "validator-type-mapspolygon-list": "Список на географски многуаголници", + "validator-type-wmsoverlay": "Облога Web Map Service" } diff --git a/i18n/ms.json b/i18n/ms.json index eafc3ba9d..aebd8ce61 100644 --- a/i18n/ms.json +++ b/i18n/ms.json @@ -31,7 +31,6 @@ "maps-layer-type-supported-by": "Jenis lapisan ini {{PLURAL:$2|hanya boleh digunakan dengan perkhidmatan pemetaan $1|boleh digunakan dengan perkhidmatan-perkhidmatan pemetaan yang berikut: $1}}.", "maps-coordinates-description": "Cangkuk penghurai untuk memformatkan koordinat daripada/kepada sebarang format yang disokong.", "maps-displaymap-description": "Memaparkan peta geografi tanpa sebarang penanda tentuan wiki padanya.", - "maps-displaypoint-description": "Memaparkan peta geografi dengan sekurang-kurangnya satu penanda tentuan wiki padanya.", "maps-distance-description": "Menukar nilai jarak yang menggunakan unit disokong yang tertentu kepada setaraannya yang menggunakan unit yang lain.", "maps-finddestination-description": "Mencari tujuan dari satu titik permulaan (yang boleh dinyatakan dalam sebarang format yang disokong), bearing permulaan dan jarak.", "maps-geocode-description": "Membolehkan pembuatan geokod alamat, iaitu menukar lokasi yang boleh dibaca oleh manusia kepada peranggu koordinat. Wujudnya sokongan untuk beberapa perkhidmatan geokod yang tidak boleh disamakan dengan perkhidmatan pemetaan.", @@ -115,6 +114,7 @@ "maps-par-width": "Membolehkan penetapan lebar peta. Pada asali, piksel dianggap sebagai unit, tetapi anda boleh menetapkan mana-mana unit yang berikut: px, ex, em, %.", "maps-par-height": "Membolehkan penetapan tinggi peta. Pada asali, piksel dianggap sebagai unit, tetapi anda boleh menetapkan mana-mana unit yang berikut: px, ex, em, %.", "maps-par-centre": "Lokasi yang hendak dijadikan pusat peta", + "maps-par-kml": "Fail-fail KML untuk dimuatkan kepada peta.", "maps-googlemaps3-incompatbrowser": "Pelayar anda tidak serasi dengan Google Maps v3.", "maps-googlemaps3-par-type": "Jenis peta untuk dipaparkan pada mulanya.", "maps-googlemaps3-par-types": "Jenis-jenis peta yang tersedia melalui kawalan jenis.", @@ -123,7 +123,6 @@ "maps-googlemaps3-par-zoomstyle": "Gaya kawalan zum.", "maps-googlemaps3-par-typestyle": "Gaya kawalan jenis.", "maps-googlemaps3-par-autoinfowindows": "Membuka semua tetingkap maklumat secara automatik selepas laman dimuatkan.", - "maps-googlemaps3-par-kml": "Fail-fail KML untuk dimuatkan kepada peta.", "maps-googlemaps3-par-gkml": "Fail-fail KML yang dihoskan oleh Google untuk dimuatkan pada peta.", "maps-googlemaps3-par-fusiontables": "ID Google Fusion Tables yang patut dimuatkan pada peta.", "maps-googlemaps3-par-tilt": "Ciri menyendengkan Peta ketika menggunakan Google Maps.", @@ -131,8 +130,6 @@ "maps-googlemaps3-par-poi": "Tunjukkan tempat-tempat menarik.", "maps-openlayers-par-controls": "Alat kawalan untuk diletakkan pada peta.", "maps-openlayers-par-layers": "Lapisan-lapisan yang akan tersedia pada pemilih lapisan. Lapisan pertama akan dipaparkan apabila peta dimuatkan.", - "maps-osm-par-thumbs": "Tunjukkan gambar kenit (thumbnail)", - "maps-osm-par-photos": "Tunjukkan gambar", "mapeditor": "Editor peta", "specialpages-group-maps": "Peta", "mapeditor-parser-error": "Timbulnya ralat ketika menghuraikan metadata. Input pengguna diabaikan.", @@ -168,5 +165,6 @@ "mapeditor-imageoverlay-button": "Tambah tindihan imej", "mapeditor-form-field-image": "Imej", "mapeditor-imageoverlay-title": "Butiran tindihan imej", - "mapeditor-form-field-visitedicon": "Ikon dikunjungi" + "mapeditor-form-field-visitedicon": "Ikon dikunjungi", + "semanticmaps-forminput-locations": "Lokasi" } diff --git a/i18n/nb.json b/i18n/nb.json index 477cbee77..a419fa0f0 100644 --- a/i18n/nb.json +++ b/i18n/nb.json @@ -5,35 +5,51 @@ "Kim Eik", "Nghtwlkr", "Danmichaelo", - "Macofe" + "Macofe", + "EvenT", + "Jon Harald Søby" ] }, "maps-desc": "Gir mulighet for å bygge inn dynamiske kart i wikisider, geokoding av adresser og andre geografiske operasjoner", "right-geocode": "Geokode", + "action-geocode": "utføre geokoding på denne wikien", "maps_map": "Kart", + "maps-tracking-category": "Sider med kart som vises med kartutvidelsen", "maps-loading-map": "Laster kart...", "maps-load-failed": "Klarte ikke laste kartet.", "maps-markers": "Markører", + "maps-copycoords-prompt": "Chrl+C, Enter", + "maps-searchmarkers-text": "Filtrer markører", "maps-others": "andre", + "maps-kml-parsing-failed": "Klarte ikke å parse én eller flere KML-filer. Dette skjer vanligvis på grunn av en hentingsfeil eller ugyldig XML.", "maps-ns-layer": "Lag", "maps-ns-layer-talk": "Lagdiskusjon", "maps-layer-property": "Egenskap", "maps-layer-value": "Verdi", "maps-layer-errors": "Feil", + "maps-layerdef-invalid": "{{PLURAL:$1|Ugyldig definisjon|Ugyldige definisjoner}}", + "maps-layerdef-invalid-fatal": "Katastrofalt ugyldig definisjon", + "maps-layerdef-wrong-namespace": "Lagdefinisjoner er kun gyldige på sider i navnerommet «$1»", + "maps-layerdef-equal-layer-name": "Lagnavn må være unike innen samme lagside. «$1» brukes allerede av et annet lag.", + "maps-layerpage-usage": "Sider med kart som bruker laget «$1»", + "maps-layerpage-nousage": "Ingen sider bruker dette laget for tiden.", "maps-error-invalid-layertype": "Det er ingen lag av typen «$1». Bare {{PLURAL:$3|denne typen|disse typene}} er støttet: $2", "maps-error-no-layertype": "Du må angi en lagtype. Bare {{PLURAL:$2|denne typen|disse typene}} er støttet: $1", "validation-error-invalid-layer": "Parameter $1 må været et gyldig lag.", "validation-error-invalid-layers": "Parameter $1 må være et eller flere gyldige lag.", + "validation-error-no-non-numeric": "Parameteren «$1» må være en ikke-numerisk streng.", + "validation-error-no-non-numerics": "Parameteren «$1» må være én eller flere ikke-numeriske strenger.", "maps-layer-of-type": "Lagtype $1", + "maps-layer-of-type-and-name": "Lag «$2» av typen «$1»", "maps-layer-type-supported-by": "Denne lagtypen kan bare brukes med {{PLURAL:$2|karttjenesten $1|disse karttjenestene: $1}}.", "maps-coordinates-description": "Parser-hook for å formatere koordinater, fra og til vilkårlige av de støttede formatene.", "maps-displaymap-description": "Vis geografiske kart uten wiki-spesifiserte markører.", - "maps-displaypoint-description": "Vis geografiske kart med en eller flere wiki-spesifiserte markører.", "maps-distance-description": "Gjør om en distanse basert på en viss støttet enhet til tilsvarende verdi i en annen enhet.", "maps-finddestination-description": "Finn et mål med gitt startpunkt (som kan være i ett av de støttede formatene), en initial retning og distanse.", "maps-geocode-description": "Gjør tilgjengelig geokoding av adresser, dvs. gjør menneskelesbare lokasjoner til koordinatsett. Det finnes støtte for flere geokodingstjenester, som ikke må forveksles med mappingtjenester.", "maps-geodistance-description": "Beregn den geografiske avstanden mellom to punkter, fra og til et vilkårlig av de støttede formatene.", "maps-mapsdoc-description": "Vis en tabell med parametrene for en spesifisert mappingstjeneste sammen med deres standardverdier og beskrivelser.", + "maps-layerdefinition-description": "Beskriver et egendefinert lag som kan vises med andre kartfunksjoner.", "maps-mapsdoc-par-service": "Mappingstjenesten for å vise parameterdokumentasjon.", "maps-mapsdoc-par-language": "Språket for å vise dokumentasjonen. Hvis ingen slik oversettelse er tilgjengelig, blir engelsk brukt.", "maps-coordinates-par-location": "Koordinatene du ønsker å formatere.", @@ -52,6 +68,18 @@ "maps-finddestination-par-mappingservice": "Parameter for å angi mappetjenesten som skal brukes for denne funksjonen.\nDette vil tillate kart å overstyre standardverdien av tjenesteparameteren med den som er optimal for mappetjenesten.\n(Eksempel: For Google Maps vil Google geocoder brukes.)", "maps-geocode-par-location": "Adressen du ønsker å geokode.", "maps-geocode-par-mappingservice": "Geokodetjenesten du ønsker å bruke. Sjekk de tilgjengelige geokodetjenestene.", + "maps-geocode-par-allowcoordinates": "Gjør det mulig å deaktivere støtten for koordinater i denne funksjonen. Må være enten ja eller nei. Om den er nei vil hver verdi geokodes, også gyldige koordinater.", + "maps-geocode-par-format": "Formatet for de resulterende koordinatene.", + "maps-geodistance-par-unit": "Enheten avstanden skal måles i.", + "maps-displaymap-par-circles": "Sirkler å vise", + "maps-displaymap-par-lines": "Linjer å vise", + "maps-displaymap-par-maxzoom": "Maksimalt zoomnivå", + "maps-displaymap-par-minzoom": "Minste zoomnivå", + "maps-displaymap-par-polygons": "Polygoner å vise", + "maps-displaymap-par-rectangles": "Rektangler å vise", + "maps-displaymap-par-static": "Gjør kartet statisk", + "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.", "validation-error-invalid-locations": "Parameter $1 må være en eller flere gyldige lokasjoner.", "validation-error-invalid-width": "Parameter $1 må være en gyldig bredde.", @@ -76,6 +104,7 @@ "maps_unrecognized_coords_for": "Følgende {{PLURAL:$2|koordinat|koordinater}} ble ikke gjenkjent og har blitt utelatt fra kartet:\n$1", "maps_map_cannot_be_displayed": "Kartet kan ikke vises.", "maps-geocoder-not-available": "Geokodingsfunksjonen i Maps er ikke tilgjengelig. Din plassering kan ikke geokodes.", + "maps-leaflet-par-layer": "Laget som vil vises mens kartet laster.", "maps_click_to_activate": "Klikk for å aktivere kartet", "maps_centred_on": "Kart sentrert om $1, $2.", "mapeditor": "Kart tegner", @@ -87,6 +116,7 @@ "mapeditor-import-button2": "Importer", "mapeditor-export-button": "Eksporter til wiki kode", "mapeditor-import-button": "Importer fra wiki kode", + "mapeditor-select-button": "Velg dette polygonet", "mapeditor-mapparam-button": "Rediger kart parametere", "mapeditor-clear-button": "Tøm kart", "mapeditor-code-title": "Wiki kode", @@ -112,5 +142,34 @@ "mapeditor-imageoverlay-button": "Legg til bilde", "mapeditor-form-field-image": "Bilde", "mapeditor-imageoverlay-title": "Bilde lag detaljer", - "mapeditor-form-field-visitedicon": "Besøkt ikon" + "mapeditor-form-field-visitedicon": "Besøkt ikon", + "semanticmaps-unrecognizeddistance": "Verdien $1 er ikke en gyldig avstand.", + "semanticmaps-kml-link": "Vis KML-filen", + "semanticmaps-default-kml-pagelink": "Vis siden $1", + "semanticmaps-latitude": "Breddegrad: $1", + "semanticmaps-longitude": "Lengdegrad: $1", + "semanticmaps-altitude": "Høyde over havet: $1", + "semanticmaps-forminput-locations": "Lokasjoner", + "semanticmaps-par-staticlocations": "En lokasjonsliste til å legge inn i kartet sammen med data fra spørringen. Som med display_points, kan du legge inn en tittel, en beskrivelse og et ikon per lokasjon med tilde \"~\" som skilletegn.", + "semanticmaps-par-showtitle": "Vise tittel i markørinfovinduet eller ikke. Deaktivering er ofte nyttig når en bruker en mal for å formatere innholdet i infovinduet.", + "semanticmaps-par-hidenamespace": "Vis tittelen for navnerommet i markørens informasjonsboks", + "semanticmaps-par-centre": "Kartets sentrum. Hvis dette ikke er angitt, vil kartet automatisk velge det optimale senteret for å vise alle kartmarkørene.", + "semanticmaps-par-template": "Mal som brukes for å formatere innholdet i infovinduet.", + "semanticmaps-par-geocodecontrol": "Vis geokodingsstyringen", + "semanticmaps-par-activeicon": "Ikon som vises istedenfor standardmarkøren, når aktiv side er lik resultatet av spørringen", + "semanticmaps-par-pagelabel": "Når satt til \"ja\", vil alle markører ha en \"inlineLabel\" med en lenke til siden som inneholder markørens koordinater", + "semanticmaps-kml-text": "Teksten knyttes til hver side. Overstyrt av spørringer på ekstra egenskaper, om noen.", + "semanticmaps-kml-title": "Standard resultatoverskrift", + "semanticmaps-kml-linkabsolute": "Skal lenker være absolutte eller ikke (dvs. relative)", + "semanticmaps-kml-pagelinktext": "Teksten som skal brukes for lenker til siden, der $1 erstattes av sidetittelen", + "semanticmaps-shapes-improperformat": "Ulovlig formatering av $1. Vennligst les dokumentasjonen for å korrigere", + "semanticmaps-shapes-missingshape": "Ingen figurer funnet for $1. Vennlig bruk tilgjengelige figurer fra dokumentasjonen", + "validator-type-mapscircle": "Geografisk sirkel", + "validator-type-mapscircle-list": "Liste over sirkler", + "validator-type-mapsline": "Geografisk linje", + "validator-type-mapsline-list": "Liste over linjer", + "validator-type-mapslocation": "Geografisk sted", + "validator-type-mapslocation-list": "Liste over steder", + "validator-type-mapsrectangle": "Geografisk rektangel", + "validator-type-mapsrectangle-list": "Liste over rektangler" } diff --git a/i18n/nl.json b/i18n/nl.json index b1b34b924..dac3f73d2 100644 --- a/i18n/nl.json +++ b/i18n/nl.json @@ -7,12 +7,15 @@ "SPQRobin", "Siebrand", "Sjoerddebruin", - "Esketti" + "Esketti", + "MrLeopold", + "Hex", + "Mainframe98" ] }, "maps-desc": "Maakt het insluiten van dynamische kaarten in de wikipagina's, het geocoderen van adressen en andere geografische activiteiten mogelijk", "right-geocode": "Geocoderen", - "action-geocode": "doen geocoding op deze wiki", + "action-geocode": "doe geocoding op deze wiki", "maps_map": "Kaart", "maps-tracking-category": "Pagina's met een kaart die gerenderd is door de uitbreiding Maps", "maps-loading-map": "Bezig met het laden van de kaart...", @@ -29,28 +32,27 @@ "maps-layer-errors": "Fouten", "maps-layerdef-invalid": "Ongeldige definitie{{PLURAL:$1||s}}", "maps-layerdef-invalid-fatal": "Fatale ongeldige definitie", - "maps-layerdef-wrong-namespace": "Definities zijn alleen geldig op de pagina ' s van de naamruimte \"$1\"", - "maps-layerdef-equal-layer-name": "Laag namen moeten uniek zijn binnen dezelfde laag pagina. \"$1'wordt al gebruikt door een andere laag.", - "maps-layerpage-usage": "Pagina ' s met kaarten met behulp van de laag \"$1\"", - "maps-layerpage-nousage": "Geen pagina ' s zijn met behulp van deze laag op het moment.", + "maps-layerdef-wrong-namespace": "Lagendefinities zijn alleen geldig op pagina's van de naamruimte \"$1\"", + "maps-layerdef-equal-layer-name": "Lagennamen moeten uniek zijn binnen dezelfde lagenpagina. \"$1'wordt al gebruikt door een andere laag.", + "maps-layerpage-usage": "Pagina's met kaarten die laag \"$1\" gebruiken", + "maps-layerpage-nousage": "Er zijn op het moment geen pagina's die deze laag gebruiken.", "maps-error-invalid-layertype": "Er zijn geen lagen van het type \"$1\". Alleen {{PLURAL:$3|dit type wordt|deze typen worden}} ondersteund: $2", "maps-error-no-layertype": "U moet het laagtype opgeven. Alleen {{PLURAL:$2|dit type wordt|deze typen worden}} ondersteund: $1", "validation-error-invalid-layer": "Parameter $1 moet een geldige laag zijn.", "validation-error-invalid-layers": "Parameter $1 moet een of meer geldige lagen zijn.", - "validation-error-no-non-numeric": "Parameter \"$1\" moet een niet-numerieke string.", - "validation-error-no-non-numerics": "Parameter \"$1\" moet een of meer niet-numerieke tekenreeksen.", + "validation-error-no-non-numeric": "Parameter \"$1\" moet een niet-numerieke tekenreeks zijn.", + "validation-error-no-non-numerics": "Parameter \"$1\" moet één of meer niet-numerieke tekenreeksen zijn.", "maps-layer-of-type": "Laag van het type $1", "maps-layer-of-type-and-name": "Laag \"$2\" van het type \"$1\"", "maps-layer-type-supported-by": "Dit laagtype kan {{PLURAL:$2|alleen gebruikt worden met de kaartdienst $1|gebruikt worden met de kaartdiensten $1}}.", "maps-coordinates-description": "Parserhook om coördinaten op te maken, van en naar alle ondersteunde notaties.", "maps-displaymap-description": "Geografische kaarten weergeven zonder in de wiki gedefinieerde markeringen.", - "maps-displaypoint-description": "Geografische kaarten weergeven met een of meer in de wiki gedefinieerde markeringen.", "maps-distance-description": "Converteren naar een afstand met behulp van een bepaalde ondersteunde eenheid naar een equivalent in een andere eenheid.", "maps-finddestination-description": "Een bestemming vinden via een gegeven beginpunt (in elke ondersteunde notatie), een richting en een afstand.", "maps-geocode-description": "Maakt het geocoderen van adressen mogelijk, dat wil zeggen dat leesbare locaties worden omgezet in verzamelingen coördinaten. Er is ondersteuning voor verschillende geocoderingsdiensten die niet verward moeten worden met kaartdiensten.", "maps-geodistance-description": "De geografische afstand tussen twee punten berekenen, van en naar alle ondersteunde notaties.", "maps-mapsdoc-description": "Een tabel met de parameters voor een bepaalde kaartdienst weergeven samen met hun standaardwaarde en beschrijving.", - "maps-layerdefinition-description": "Beschrijft een aangepaste laag, die weergegeven kan worden met een andere Kaart functies.", + "maps-layerdefinition-description": "Beschrijft een aangepaste laag die weergegeven kan worden met andere Kaart-functies.", "maps-mapsdoc-par-service": "De kaartdienst waarvoor parameterdocumentatie weergegeven moet worden.", "maps-mapsdoc-par-language": "De taal waarin de documentatie wordt weergegeven. Indien er geen vertaling beschikbaar is, wordt Engels gebruikt.", "maps-coordinates-par-location": "De op te maken coördinaten.", @@ -81,7 +83,7 @@ "maps-geodistance-par-geoservice": "De mapping service deze parser functie wordt gebruikt in combinatie met.\nDit kan invloed hebben op de standaard geocoding service-waarde.", "maps-displaymap-par-mappingservice": "Maakt het mogelijk om de kaartdienst in te stellen die wordt gebruikt om de kaart te maken.", "maps-displaymap-par-coordinates": "De locatie die zich bij de eerste keer laden in het centrum van de kaart bevindt.", - "maps-displaymap-par-visitedicon": "De bestandsnaam van een afbeelding moet worden gebruikt voor de markering pictogrammen na de oorspronkelijke markeringen zijn geklikt", + "maps-displaymap-par-visitedicon": "De bestandsnaam van een afbeelding voen gebruik als markeringspictogrammen nadat op de oorspronkelijke markeringen is geklikt", "maps-displaymap-par-zoom": "Maakt het mogelijk het schaalniveau van de kaart in te stellen.\nAls deze waarde niet wordt ingesteld en er staan meerdere markers op de kaart, dan wordt het best passende schaalniveau gekozen, niet het instelbare standaardniveau.", "maps-displaymap-par-centre": "Maakt het mogelijk de coördinaten voor het midden van de kaart in te stellen voor display_point(s).\nKan ingesteld worden met adressen en coördinaten.\nAls deze waarde niet wordt ingesteld, wordt de kaart gecentreerd op of tussen de opgegeven marker(s).", "maps-displaymap-par-title": "Maakt het mogelijk om tekst in te stellen die wordt weergegeven in de pop-ups van alle markeringen zonder gespecificeerde naam.\nAls dit samen met een label wordt gebruikt, wordt de titel vetgedrukt en onderstreept weergegeven.", @@ -98,7 +100,6 @@ "maps-displaymap-par-wmsoverlay": "WMS-overlay gebruiken", "maps-fullscreen-button": "Volledig scherm in- of uitschakelen", "maps-fullscreen-button-tooltip": "De kaart als volledig scherm of ingesloten bekijken.", - "maps-googlemaps3-par-enable-fullscreen": "Knop voor volledig scherm inschakelen", "validation-error-invalid-location": "Parameter $1 moet een geldige locatie zijn.", "validation-error-invalid-locations": "Parameter $1 moet een of meer geldige locaties zijn.", "validation-error-invalid-width": "Parameter $1 moet een geldige breedte zijn.", @@ -134,9 +135,11 @@ "maps-par-width": "Maakt het mogelijk om de breedte van de kaart in te stellen. Standaard worden pixels als eenheid gebruikt, maar u kunt expliciet een van deze eenheden opgeven: px, ex, em, %.", "maps-par-height": "Maakt het mogelijk om de hoogte van de kaart in te stellen. Standaard worden pixels als eenheid gebruikt, maar u kunt expliciet een van deze eenheden opgeven: px, ex, em, %.", "maps-par-centre": "De locatie waar de kaart op gecentreerd moet worden", + "maps-par-enable-fullscreen": "Knop voor volledig scherm inschakelen", + "maps-par-kml": "Op de kaart te laden KML-bestanden.", + "maps-par-markercluster": "Laat het samenvoegen van meerdere markers in de buurt in één markering", "maps-googlemaps3-incompatbrowser": "Uw browser kan niet werken met Google Maps v3.", "maps-googlemaps3-par-imageoverlays": "Laat het toevoegen van een afbeelding worden weergegeven op de opgegeven locatie op de kaart.", - "maps-googlemaps3-par-markercluster": "Laat het samenvoegen van meerdere markers in de buurt in één markering", "maps-googlemaps3-par-type": "Het initieel weer te geven kaarttype.", "maps-googlemaps3-par-types": "De kaarttypen die beschikbaar zijn in via het besturingselement type.", "maps-googlemaps3-par-layers": "Op de kaart te laden speciale lagen.", @@ -144,17 +147,15 @@ "maps-googlemaps3-par-zoomstyle": "De stijl van het besturingselement zoom.", "maps-googlemaps3-par-typestyle": "De stijl van het besturingselement type.", "maps-googlemaps3-par-autoinfowindows": "Automatisch alle informatievensters openen na het laden van een pagina.", - "maps-googlemaps3-par-kml": "Op de kaart te laden KML-bestanden.", "maps-googlemaps3-par-gkml": "KML-bestanden die gehost worden door Google om op de kaart te laden.", "maps-googlemaps3-par-fusiontables": "Op de kaart te laden ID's van Google Fusion Tables.", "maps-googlemaps3-par-tilt": "Hoek voor de kaart bij gebruik van Google Maps.", "maps-googlemaps3-par-kmlrezoom": "Het zoomniveau van de kaart opnieuw instellen als de KML-lagen geladen zijn.", "maps-googlemaps3-par-poi": "Interessante plaatsen weergeven.", + "maps-par-clustermaxzoom": "Het maximale zoomniveau waar clusters kunnen bestaan.", "maps-openlayers-par-controls": "De op de kaart te plaatsen besturingselementen.", "maps-openlayers-par-layers": "De lagen die beschikbaar zijn in het selectievak. De eerste laag worden weergegeven als de kaart wordt geladen.", "maps-openlayers-par-overlays": "Overlaylagen die beschikbaar zijn in de lagenselector. Deze lagen worden bovenop een normale laag weergegeven, ongeveer zoals een marker.", - "maps-osm-par-thumbs": "Miniaturen weergeven", - "maps-osm-par-photos": "Afbeeldingen weergeven", "mapeditor": "Kaarteditor", "specialpages-group-maps": "Kaarten", "mapeditor-parser-error": "Er is een fout opgetreden tijdens het verwerken van metagegevens. De gebruikersinvoer wordt genegeerd.", @@ -190,5 +191,27 @@ "mapeditor-imageoverlay-button": "Afbeeldingslaag toevoegen", "mapeditor-form-field-image": "Afbeelding", "mapeditor-imageoverlay-title": "Gegevens over afbeeldingslaag", - "mapeditor-form-field-visitedicon": "Pictogram voor bezocht" + "mapeditor-form-field-visitedicon": "Pictogram voor bezocht", + "semanticmaps-unrecognizeddistance": "De waarde \"$1\" is geen geldige afstand.", + "semanticmaps-kml-link": "KML-bestand bekijken", + "semanticmaps-default-kml-pagelink": "Pagina $1 bekijken", + "semanticmaps-latitude": "Breedtegraad: $1", + "semanticmaps-longitude": "Lengtegraad: $1", + "semanticmaps-altitude": "Hoogte: $1", + "semanticmaps-forminput-locations": "Locaties", + "semanticmaps-par-staticlocations": "Een lijst met aan de kaart toe te voegen locaties samen met de opgegeven gegevens. Zoals bij display_points, kunt u een naam, beschrijving en pictogram per locatie toevoegen door de tilde (\"~\") als scheidingsteken te gebruiken.", + "semanticmaps-par-showtitle": "Een naam weergeven in het gegevensvenster van de markering of niet. Dit uitschakelen is vaak handig als er een sjabloon wordt gebruikt om de inhoud van het gegevensvenster vorm te geven.", + "semanticmaps-par-hidenamespace": "De naamruimtenaam in het informatievenster van de marker weergeven", + "semanticmaps-par-centre": "Het centrum van de kaart. Als deze waarde niet wordt opgegeven, wordt automatisch een keuze gemaakt voor een centrum op basis van alle markeringen op de kaart.", + "semanticmaps-par-template": "Een te gebruiken sjabloon om de inhoud van het gegevensvenster op te maken.", + "semanticmaps-par-geocodecontrol": "Besturingselement voor geocodering weergeven.", + "semanticmaps-par-activeicon": "Pictogram dat wordt weergegeven in plaats van de standaard marker, als de actieve pagina gelijk is aan het zoekresultaat", + "semanticmaps-par-pagelabel": "Wanneer dit is ingesteld op \"yes\", krijgen alle markers een \"inlineLabel\" met een koppeling naar de pagina waarop de coördinaten voor de marker staan", + "semanticmaps-kml-text": "De tekst die gekoppeld is aan iedere pagina. Als er extra opgegeven eigenschappen zijn, wordt deze tekst daardoor overschreven.", + "semanticmaps-kml-title": "De standaard titel voor resultaten", + "semanticmaps-kml-linkabsolute": "Moeten koppelingen absoluut zijn (in tegenstelling tot relatief)", + "semanticmaps-kml-pagelinktext": "De tekst om te gebruiken voor de koppelingen naar de pagina, waarin $1 vervangen wordt door de paginanaam", + "semanticmaps-shapes-improperformat": "Onjuiste opmaak van $1. Raadpleeg de documentatie voor de juiste opmaak", + "semanticmaps-shapes-missingshape": "Geen vormen gevonden voor $1. Raadpleeg de documentatie voor beschikbare vormen", + "validator-type-mapscircle-list": "Lijst van circels" } diff --git a/i18n/pa.json b/i18n/pa.json index ffe93b2d5..cfb243c4e 100644 --- a/i18n/pa.json +++ b/i18n/pa.json @@ -44,7 +44,6 @@ "maps-par-centre": "ਉਹ ਥਾਂ ਜਿਸ ਉੱਤੇ ਨਕਸ਼ਾ ਕੇਂਦਰਤ ਹੋਣਾ ਚਾਹੀਦਾ ਹੈ", "maps-googlemaps3-incompatbrowser": "ਤੁਹਾਡਾ ਬਰਾਊਜ਼ਰ Google Maps v3 ਨਾਲ਼ ਕੰਮ ਕਰਨ ਲਈ ਅਨੁਕੂਲ ਨਹੀਂ ਹੈ।", "maps-googlemaps3-par-layers": "ਨਕਸ਼ੇ ਉੱਤੇ ਲੋਡ ਕਰਨ ਲਈ ਖ਼ਾਸ ਪਰਤਾਂ।", - "maps-osm-par-photos": "ਤਸਵੀਰਾਂ ਵਿਖਾਓ", "mapeditor": "ਨਕਸ਼ਾ ਸੰਪਾਦਕ", "specialpages-group-maps": "ਨਕਸ਼ੇ", "mapeditor-none-text": "ਕੋਈ ਨਹੀਂ", diff --git a/i18n/pl.json b/i18n/pl.json index 01fed72cb..5e24b8a69 100644 --- a/i18n/pl.json +++ b/i18n/pl.json @@ -7,7 +7,8 @@ "Ty221", "Yarl", "Alan ffm", - "Macofe" + "Macofe", + "Deejay1" ] }, "maps-desc": "Umożliwia zamieszczanie na stronach wiki map dynamicznych, geokodowanych adresów i innych danych geograficznych", @@ -25,15 +26,16 @@ "maps-layer-value": "Wartość", "maps-layer-errors": "Błędy", "maps-layerdef-invalid": "{{PLURAL:$1|Nieprawidłowa definicja|Nieprawidłowe definicje}}", + "maps-layerpage-nousage": "Nie ma stron, które używają w tej chwili tej warstwy.", "maps-error-invalid-layertype": "Brak warstw typu „$1”. {{PLURAL:$3|Wspierany jest wyłącznie typ|Wspierane są wyłącznie typy:}} $2", "maps-error-no-layertype": "Musisz określić typ warstwy. {{PLURAL:$2|Wspierany jest wyłącznie typ|Wspierane są wyłącznie typy:}} $1", "validation-error-invalid-layer": "Parametr $1 musi określać prawidłową warstwę.", "validation-error-invalid-layers": "Parametr $1 musi wskazywać jedną lub więcej prawidłowych warstw.", "maps-layer-of-type": "Warstwa typu $1", + "maps-layer-of-type-and-name": "Warstwa „$2” typu „$1”", "maps-layer-type-supported-by": "Tego typu warstwa może być używana wyłącznie z {{PLURAL:$2|serwisem map|serwisami map:}} $1.", "maps-coordinates-description": "Przechwycenie analizatora do formatowania współrzędnych z i na dowolny z obsługiwanych formatów.", "maps-displaymap-description": "Wyświetlanie map geograficznych bez żadnych naniesionych na nich znaczników na wiki.", - "maps-displaypoint-description": "Wyświetlanie map geograficznych z naniesionym jednym lub więcej określonych na wiki znaczników.", "maps-distance-description": "Konwertuj odległości za pomocą pewnych obsługiwanych jednostek do ich odpowiedników w innych jednostkach.", "maps-finddestination-description": "Znajdź drogę do celu z podanego punktu początkowego, (który może być w dowolnym z obsługiwanych formatów), początkowy namiar i odległości.", "maps-geocode-description": "Umożliwia geokodowanie adresów, innymi słowy, przekształcenie zapisu miejsca czytelnego dla ludzi w zbiory współrzędnych. Obsługiwane jest kilka usług geokodowania, których nie należy mylić z usługami dostarczania map.", @@ -79,8 +81,12 @@ "maps_unrecognized_coords_for": "{{PLURAL:$2|Następującą współrzędną|Następujące współrzędne}} pominięto, ponieważ nie {{PLURAL:$2|została rozpoznana|zostały rozpoznane}}:\n$1", "maps_map_cannot_be_displayed": "Mapa nie może zostać wyświetlona.", "maps-geocoder-not-available": "Funkcja geokodowania map nie jest dostępna. Lokalizacja nie może zostać zakodowana.", + "maps-leaflet-par-layer": "Warstwa, która będzie wyświetlana podczas ładowania mapy.", + "maps-leaflet-par-overlaylayers": "Nakładane warstwy, które będą wyświetlane podczas ładowania mapy.", "maps_click_to_activate": "Kliknij, aby aktywować mapę", "maps_centred_on": "Środek mapy – $1, $2.", + "maps-par-enable-fullscreen": "Włącz przycisk trybu pełnoekranowego", + "maps-par-kml": "Pliki KML do załadowania na mapie.", "maps-googlemaps3-incompatbrowser": "Twoja przeglądarka nie jest zgodna z Google Maps v3.", "maps-googlemaps3-par-type": "Typ mapy do wyświetlenia na początku.", "maps-googlemaps3-par-types": "Typy map, które będą dostępne za pośrednictwem formantu typu.", @@ -89,22 +95,47 @@ "maps-googlemaps3-par-zoomstyle": "Styl formantu powiększenia.", "maps-googlemaps3-par-typestyle": "Styl formantu typu.", "maps-googlemaps3-par-autoinfowindows": "Automatycznie otwórz wszystkie okna informacyjne po załadowaniu strony.", - "maps-googlemaps3-par-kml": "Pliki KML do załadowania na mapie.", "maps-googlemaps3-par-gkml": "Pliki KML udostępniane przez serwery Google do załadowania na mapie.", + "maps-googlemaps3-par-poi": "Pokaż atrakcje.", + "maps-googlemaps3-par-clustergridsize": "Rozmiar siatki klastra w pikselach.", + "maps-par-clustermaxzoom": "Maksymalny poziom powiększenia, w którym klaster może istnieć.", "maps-openlayers-par-controls": "Formanty do umieszczenia na mapie.", - "maps-osm-par-thumbs": "Pokaż miniatury", - "maps-osm-par-photos": "Pokaż zdjęcia", "mapeditor": "Edytor map", "specialpages-group-maps": "Mapy", + "mapeditor-none-text": "Brak", "mapeditor-done-button": "Gotowe", "mapeditor-remove-button": "Usuń", + "mapeditor-import-button2": "Importuj", + "mapeditor-export-button": "Eksport do kodu wiki", + "mapeditor-import-button": "Import z kodu wiki", + "mapeditor-mapparam-button": "Edytuj parametry mapy", "mapeditor-clear-button": "Wyczyść mapę", "mapeditor-code-title": "Kod wiki", + "mapeditor-import-title": "Import kodu wiki", + "mapeditor-form-title": "Edytuj szczegóły", + "mapeditor-link-title-switcher-link-text": "Link", "mapeditor-form-field-title": "Tytuł", "mapeditor-form-field-text": "Tekst", + "mapeditor-form-field-link": "Link", "mapeditor-form-field-icon": "Ikona", "mapeditor-form-field-group": "Grupa", + "mapeditor-form-field-fillcolor": "Kolor wypełnienia", "mapeditor-form-field-showonhover": "Pokaż tylko po najechaniu", + "mapeditor-mapparam-title": "Edytuj ustawienia mapy", "mapeditor-mapparam-defoption": "-Wybierz parametr-", - "mapeditor-form-field-image": "Grafika" + "mapeditor-form-field-image": "Grafika", + "semanticmaps-unrecognizeddistance": "Wartość $1 nie jest poprawną odległością.", + "semanticmaps-kml-link": "Wyświetla plik KML", + "semanticmaps-default-kml-pagelink": "Pokaż stronę $1", + "semanticmaps-latitude": "Szerokość geograficzna: $1", + "semanticmaps-longitude": "Długość geograficzna: $1", + "semanticmaps-altitude": "Wysokość: $1", + "semanticmaps-forminput-locations": "Miejsca", + "semanticmaps-par-ajaxcoordproperty": "Nazwa właściwości współrzędnych, która jest używana do tworzenia zapytania AJAX.", + "semanticmaps-kml-title": "Domyślny tytuł wyników", + "validator-type-mapsline": "Linia geograficzna", + "validator-type-mapsline-list": "Lista linii", + "validator-type-mapslocation": "Położenie geograficzne", + "validator-type-mapsrectangle": "Prostokąt geograficzny", + "validator-type-mapsrectangle-list": "Lista prostokątów" } diff --git a/i18n/pms.json b/i18n/pms.json index 6b1bae63b..e1657e691 100644 --- a/i18n/pms.json +++ b/i18n/pms.json @@ -30,7 +30,6 @@ "maps-layer-type-supported-by": "Costa sòrt ëd livel a peul {{PLURAL:$2|mach esse dovrà con ël servissi ëd cartografìa $1|esse dovrà con sti servissi ëd cartografìa: $1}}.", "maps-coordinates-description": "Gancio ëd l'analisator për formaté le coordinà, da e vers qualsëssìa dij formà mantnù.", "maps-displaymap-description": "Visualisé le carte geogràfiche sensa gnun marcador definì ëd wiki ansima a lor.", - "maps-displaypoint-description": "Visualisé le carte geogràfiche con un o pi marcador definì ëd wiki ansima.", "maps-distance-description": "Convertì na distansa dovrand na serta unità mantnùa an sò equivalent dovrand n'àutra unità.", "maps-finddestination-description": "Trové na destinassion dàit un pont ëd partensa (che a peul esse an qualsëssìa dij formà mantnù), n'orientassion inissial e na distansa.", "maps-geocode-description": "A abìlita la geocodìfica d'adrësse, an d'àutre paròle, la trasformassion dle posission lesìbij da n'uman an ansema ëd coordinà. Vàire sërvissi ëd geocodìfica a son mantnù, lòn che a dev nen esse confondù con ij sërvissi ëd cartografìa.", @@ -114,6 +113,7 @@ "maps-par-width": "A përmët d'amposté la larghëssa dla carta. Për predefinission ij pontin a saran contà com unità, ma a peul specifiché ëd fasson esplìssita un-a ëd coste unità: px, ex, em, %.", "maps-par-height": "A përmët d'amposté l'autëssa dla carta. Për predefinission ij pontin a saran considerà com unità, ma a peul ëspessifiché ëd fasson esplìssita un-a ëd coste unità: px, ex, em, %.", "maps-par-centre": "Ël pòst anté che la carta a dovrà esse sentrà", + "maps-par-kml": "Archivi KML da carié dzora a la carta.", "maps-googlemaps3-incompatbrowser": "Tò navigator a l'é pa compatìbil con Google Maps v3.", "maps-googlemaps3-par-type": "La sòrt ëd carta da smon-e inissialment.", "maps-googlemaps3-par-types": "La sòrt ëd carta che a sarà disponìbil travers al contròl ëd sòrt.", @@ -122,7 +122,6 @@ "maps-googlemaps3-par-zoomstyle": "Lë stil dël contròl d'angrandiment.", "maps-googlemaps3-par-typestyle": "Lë stil dël contròl ëd sòrt.", "maps-googlemaps3-par-autoinfowindows": "Duverté automaticament tute le fnestre d'anformassion apress che la pàgina a l'é cariasse.", - "maps-googlemaps3-par-kml": "Archivi KML da carié dzora a la carta.", "maps-googlemaps3-par-gkml": "Archivi KML ospità da Google da carié dzor la carta.", "maps-googlemaps3-par-fusiontables": "ID ëd le tàule ëd Google Fusion ch'a dovrìa esse carià dzora a la carta.", "maps-googlemaps3-par-tilt": "Anclinassion për la Carta quand as deuvra Google Maps.", @@ -130,8 +129,6 @@ "maps-googlemaps3-par-poi": "Smon-e ij pont d'anteresse.", "maps-openlayers-par-controls": "Ël control da piassé an sla carta.", "maps-openlayers-par-layers": "Ij seuj che a saran disponìbij ant ël seletor ëd seul. Ël prim seul a sarà mostrà quand la carta as caria.", - "maps-osm-par-thumbs": "Smon-e dle miniadure", - "maps-osm-par-photos": "Smon-e dle fòto", "mapeditor": "Editor ëd carta", "specialpages-group-maps": "Carte", "mapeditor-parser-error": "A l'é capitaje n'eror an analisand dij metadat. Ignorà l'anseriment ëd l'utent.", @@ -167,5 +164,24 @@ "mapeditor-imageoverlay-button": "Gionté la dzorposission ëd plancia", "mapeditor-form-field-image": "Figura", "mapeditor-imageoverlay-title": "Detaj dla dzorposission ëd plancia", - "mapeditor-form-field-visitedicon": "Plancia visità" + "mapeditor-form-field-visitedicon": "Plancia visità", + "semanticmaps-unrecognizeddistance": "Ël valor $1 a l'é pa na distansa bon-a.", + "semanticmaps-kml-link": "Vëdde l'archivi KML", + "semanticmaps-default-kml-pagelink": "Lese la pàgina $1", + "semanticmaps-latitude": "Latitùdin: $1", + "semanticmaps-longitude": "Longitùdin: $1", + "semanticmaps-altitude": "Autitùdin: $1", + "semanticmaps-forminput-locations": "Locassion", + "semanticmaps-par-staticlocations": "Na lista ëd locassion da gionté a la carta ansema ai dat ciamà. Com con dispay_points, a peul gionté un tìtol, na descrission e na plancia për locassion an dovrand la tilde \"~\" com separator.", + "semanticmaps-par-showtitle": "Smon-e un tìtol ant la fnesta d'anformassion dël marcator opura nò. La disabilitassion ëd sòn a l'é soens ùtil quand as deuvra në stamp për formaté ël contnù dla fnesta d'anformassion.", + "semanticmaps-par-hidenamespace": "Mostré ël tìtol dlë spassi nominal ant la fnestra d'anformassion dël marcador.", + "semanticmaps-par-centre": "Ël sènter ëd la carta. Quand a l'é pa dàit, la carta a trovrà automaticament ël sènter otimal për smon-e tùit ij marcador an sla carta.", + "semanticmaps-par-template": "Në stamp da dovré deje a forma ai contnù dla fnesta d'anformassion.", + "semanticmaps-par-geocodecontrol": "Smon-e ël contròl ëd geocodìfica.", + "semanticmaps-kml-text": "Ël test associà con minca pagina. Coatà da le propietà adissionaj ciamà s'a-i në j'é.", + "semanticmaps-kml-title": "Ël tìtol predefinì për j'arzultà", + "semanticmaps-kml-linkabsolute": "Si le liure a devo esse assolùe o nò (visadì relativ)", + "semanticmaps-kml-pagelinktext": "Ël test da dovré për le liure a la pàgina, dont $1 a sarà rimpiassà da 'l tìtol ëd la pàgina", + "semanticmaps-shapes-improperformat": "Formatà ëd $1 nen bon. Për piasì, ch'a fasa arferiment a la documentassion për ël formà vorsù", + "semanticmaps-shapes-missingshape": "Gnun-e forme trovà për $1. Për piasì, ch'a varda la documentassion për le forme disponìbij" } diff --git a/i18n/pt-br.json b/i18n/pt-br.json index 9be517b82..e06796dc0 100644 --- a/i18n/pt-br.json +++ b/i18n/pt-br.json @@ -7,13 +7,17 @@ "Luckas", "Luckas Blade", "555", - "Macofe" + "Macofe", + "Jaideraf", + "Eduardo Addad de Oliveira", + "Felipe L. Ewald" ] }, "maps-desc": "Permite a incorporação de mapas dinâmicos em páginas wiki, geocodificação de endereços e outras operações geográficas", "maps_map": "Mapa", "maps-loading-map": "Carregando mapa...", "maps-markers": "Marcadores", + "maps-copycoords-prompt": "CTRL+C, ENTER", "maps-others": "outros", "maps-ns-layer": "Camada", "maps-ns-layer-talk": "Camada Discussão", @@ -26,6 +30,14 @@ "validation-error-invalid-layers": "O parâmetro $1 precisa ser uma ou mais camada(s) válida(s).", "maps-layer-of-type": "Camada de tipo $1", "maps-layer-type-supported-by": "Este tipo de camada só pode ser usado com {{PLURAL:$2|o serviço de cartografia $1|os serviços de cartografia: $1}}.", + "maps-finddestination-par-location": "A localização inicial.", + "maps-finddestination-par-bearing": "A direção inicial.", + "maps-finddestination-par-distance": "A distância para percorrer.", + "maps-geodistance-par-unit": "A unidade na qual a distância será retornada.", + "maps-geodistance-par-decimals": "O número máximo de casas decimais para usar no resultado.", + "maps-displaymap-par-lines": "Linhas para mostrar", + "maps-displaymap-par-maxzoom": "O nível máximo de zoom", + "maps-displaymap-par-minzoom": "O nível mínimo de zoom", "validation-error-invalid-location": "O parâmetro $1 precisa ser uma localização válida.", "validation-error-invalid-locations": "O parâmetro $1 precisa ser uma ou mais localização(ões) válida(s).", "validation-error-invalid-width": "O parâmetro $1 precisa ser uma largura válida.", @@ -52,14 +64,15 @@ "maps-geocoder-not-available": "A funcionalidade de georeferenciação do Mapas está indisponível; a sua localização não pode ser georeferenciada.", "maps_click_to_activate": "Clique para ativar o mapa", "maps_centred_on": "Mapa centrado nas coordenadas $1, $2.", - "maps-osm-par-thumbs": "Mostrar miniaturas", - "maps-osm-par-photos": "Mostrar fotos", "mapeditor": "Editor de mapas", "specialpages-group-maps": "Mapas", "mapeditor-none-text": "Nenhum", "mapeditor-done-button": "Feito", "mapeditor-remove-button": "Remover", "mapeditor-import-button2": "Importar", + "mapeditor-export-button": "Exportar para código wiki", + "mapeditor-import-button": "Importar de código wiki", + "mapeditor-select-button": "Seleciona este polígono", "mapeditor-mapparam-button": "Editar parâmetros do mapa", "mapeditor-clear-button": "Limpar mapa", "mapeditor-code-title": "Código wiki", @@ -74,5 +87,31 @@ "mapeditor-mapparam-title": "Editar parâmetros do mapa", "mapeditor-mapparam-defoption": "-Selecionar parâmetro-", "mapeditor-form-field-image": "Imagem", - "mapeditor-form-field-visitedicon": "Ícone visitado" + "mapeditor-form-field-visitedicon": "Ícone visitado", + "semanticmaps-unrecognizeddistance": "O valor $1 não é uma distância válida.", + "semanticmaps-kml-link": "Ver o arquivo KML", + "semanticmaps-default-kml-pagelink": "Ver a página $1", + "semanticmaps-latitude": "Latitude: $1", + "semanticmaps-longitude": "Longitude: $1", + "semanticmaps-altitude": "Altitude: $1", + "semanticmaps-forminput-locations": "Locais", + "semanticmaps-par-staticlocations": "Uma lista de localizações para adicionar ao mapa junto aos dados consultados. Assim como nos pontos a serem exibidos (\"display_points\"), você pode adicionar um título, descrição e ícone por localização, usando o til (\"~\") como separador.", + "semanticmaps-par-showtitle": "Mostrar, ou não, um título na janela informativa do marcador. É frequentemente desejável desativar este recurso quando estiver usando uma predefinição para formatar o conteúdo da janela informativa.", + "semanticmaps-par-hidenamespace": "Mostrar o título do domínio na janela de informações do marcador", + "semanticmaps-par-centre": "O centro do mapa. Quando este não for definido, o mapa escolherá automaticamente o centro ideal para apresentar todos os marcadores do mapa.", + "semanticmaps-par-template": "Uma predefinição que será usada para formatar o conteúdo da janela informativa.", + "semanticmaps-par-geocodecontrol": "Exibir o controle de geocodificação.", + "semanticmaps-par-activeicon": "Ícone a ser exibido ao invés do marcador padrão, quando a página ativa é igual ao resultado da consulta", + "semanticmaps-par-pagelabel": "Quando configurado para \"yes\", todos os marcadores terão um \"inlineLabel\" com um link para a página que contém as coordenadas para o marcador", + "semanticmaps-kml-text": "O texto associado a cada página. Será substituído quando propriedades adicionais consultadas existirem.", + "semanticmaps-kml-title": "O título padrão para os resultados", + "semanticmaps-kml-linkabsolute": "Os links deverão ser absolutos (ao contrário de relativos)", + "semanticmaps-kml-pagelinktext": "O texto a ser usado nos links para a página, onde $1 será substituído pelo título da página", + "semanticmaps-shapes-improperformat": "Formatação imprópria em $1. Por favor, veja a documentação para formatação", + "semanticmaps-shapes-missingshape": "Nenhuma forma foi encontrada para $1. Por favor, veja a documentação para formas disponíveis", + "validator-type-mapscircle-list": "Lista de círculos", + "validator-type-mapslocation-list": "Lista de locais", + "validator-type-mapsrectangle-list": "Lista de retângulos", + "validator-type-mapspolygon": "Polígono geográfico", + "validator-type-mapspolygon-list": "Lista de polígonos geográficos" } diff --git a/i18n/pt.json b/i18n/pt.json index e4410b08c..04553d069 100644 --- a/i18n/pt.json +++ b/i18n/pt.json @@ -9,7 +9,9 @@ "Luckas", "Waldir", "Vitorvicentevalente", - "Macofe" + "Macofe", + "Malafaya", + "Fúlvio" ] }, "maps-desc": "Permite incorporar mapas dinâmicos nas páginas da wiki, converter endereços em geocódigos e outras operações geográficas", @@ -32,12 +34,11 @@ "maps-layer-type-supported-by": "Este tipo de camada só pode ser usado com {{PLURAL:$2|o serviço de cartografia $1|os serviços de cartografia: $1}}.", "maps-coordinates-description": "Hook do analisador sintáctico para formatar coordenadas, a partir de qualquer um dos formatos suportados para qualquer outro formato suportado.", "maps-displaymap-description": "Apresentar os mapas sem qualquer marcador definido na wiki.", - "maps-displaypoint-description": "Apresentar os mapas com um ou mais marcadores definidos na wiki.", "maps-distance-description": "Converter uma distância numa unidade suportada para a distância equivalente noutra unidade.", "maps-finddestination-description": "Encontrar um destino a partir de um ponto de partida (expresso em qualquer um dos formatos suportados), uma orientação inicial e uma distância.", "maps-geocode-description": "Permite a geocodificação de moradas; por outras palavras, transforma locais legíveis por seres humanos em conjuntos de coordenadas. Há apoio para vários serviços de geocodificação, que não devem ser confundidos com serviços de cartografia.", "maps-geodistance-description": "Calcula a distância geográfica entre dois pontos, a partir e para qualquer um dos formatos suportados.", - "maps-mapsdoc-description": "Apresentar uma tabela com os parâmetros de um serviço de cartografia especificado, em conjunto com os respectivos valores por omissão e descrições.", + "maps-mapsdoc-description": "Apresentar uma tabela com os parâmetros de um serviço de cartografia especificado, em conjunto com os respetivos valores por omissão e descrições.", "maps-mapsdoc-par-service": "O serviço de cartografia para o qual será apresentada a documentação dos parâmetros.", "maps-mapsdoc-par-language": "A língua de apresentação da documentação. Se essa tradução não estiver disponível, será usado o inglês.", "maps-coordinates-par-location": "As coordenadas que quer formatar.", @@ -65,7 +66,7 @@ "maps-geodistance-par-unit": "As unidades em que a distância será produzida.", "maps-geodistance-par-decimals": "O número máximo de casas decimais a usar no valor resultante.", "maps-geodistance-par-mappingservice": "O serviço de geocodificação que será usado para geocodificar qualquer endereço.", - "maps-geodistance-par-geoservice": "O serviço de cartografia a utilizar em conjunto.\nIsto pode afetar o valor padrão do serviço de geocodificação.", + "maps-geodistance-par-geoservice": "O serviço de cartografia que é utilizado em conjunto com esta função.\nIsto pode afetar o valor padrão do serviço de geocodificação.", "maps-displaymap-par-mappingservice": "Permite definir o serviço de cartografia que será usado para gerar o mapa.", "maps-displaymap-par-coordinates": "A localização na qual o mapa será inicialmente centrado.", "maps-displaymap-par-zoom": "Permite definir o nível de aproximação do mapa.\nQuando este não for fornecido e existirem vários marcadores no mapa, será usada a aproximação que resulte no melhor dimensionamento, não o valor padrão configurado.", @@ -99,13 +100,15 @@ "maps_unrecognized_coords_for": "{{PLURAL:$2|A seguinte coordenada não foi reconhecida e foi omitida|As seguintes coordenadas não foram reconhecidas e foram omitidas}} do mapa:\n$1", "maps_map_cannot_be_displayed": "Não é possível apresentar o mapa.", "maps-geocoder-not-available": "A funcionalidade de georeferenciação do Mapas está indisponível; a sua localização não pode ser georeferenciada.", + "maps-leaflet-par-layer": "A camada que será mostrada quando o mapa é carregado.", "maps_click_to_activate": "Clique para ativar o mapa", "maps_centred_on": "Mapa centrado nas coordenadas $1, $2.", "maps-par-resizable": "Permite alterar as dimensões do mapa, arrastando o canto inferior direito.", "maps-par-geoservice": "O serviço de geocodificação que será usado para fazer a conversão entre endereços e coordenadas.", "maps-par-zoom": "O nível de aproximação do mapa. Nos mapas com marcadores será usada a maior aproximação que, mesmo assim, mostre todos os marcadores.", - "maps-par-width": "Permite definir a largura do mapa. A unidade por omissão é o pixel, mas pode defini-la explicitamente como: px, ex, em, %.", - "maps-par-height": "Permite definir a altura do mapa. A unidade por omissão é o pixel, mas pode defini-la explicitamente como: px, ex, em, %.", + "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-kml": "Ficheiros KML que serão carregados no mapa.", "maps-googlemaps3-incompatbrowser": "O seu browser não é compatível com o Google Maps v3.", "maps-googlemaps3-par-type": "O tipo de mapa que será apresentado inicialmente.", "maps-googlemaps3-par-types": "Os tipos de mapas que estarão disponíveis através do controlo de tipos.", @@ -114,14 +117,11 @@ "maps-googlemaps3-par-zoomstyle": "O estilo do controlo da aproximação.", "maps-googlemaps3-par-typestyle": "O estilo do controlo de tipos.", "maps-googlemaps3-par-autoinfowindows": "Abrir automaticamente todas as janelas informativas depois de carregar a página.", - "maps-googlemaps3-par-kml": "Ficheiros KML que serão carregados no mapa.", "maps-googlemaps3-par-gkml": "Ficheiros KML alojados pelo Google que serão carregados no mapa.", "maps-googlemaps3-par-fusiontables": "Identificação das Google Fusion Tables que deverão ser carregadas no mapa.", "maps-googlemaps3-par-poi": "Mostrar pontos de interesse.", "maps-openlayers-par-controls": "Os controlos que serão colocados no mapa.", "maps-openlayers-par-layers": "As camadas que estarão disponíveis no selector de camadas. A primeira camada será apresentada quando o mapa for carregado.", - "maps-osm-par-thumbs": "Mostrar miniaturas", - "maps-osm-par-photos": "Mostrar fotografias", "mapeditor": "Editor de mapa", "specialpages-group-maps": "Mapas", "mapeditor-parser-error": "Ocorreu um erro durante a análise de metadados. A entrada de utilizador será ignorada.", @@ -133,14 +133,45 @@ "mapeditor-import-button": "Importar a partir de código wiki", "mapeditor-code-title": "Código wiki", "mapeditor-import-title": "Importar código wiki", + "mapeditor-import-note": "Por favor, note que o analizador espera um formato muito rigoroso no código wiki. O código inserido aqui deve corresponder ao código gerado pela funcionalidade de exportação.", "mapeditor-form-title": "Editar detalhes", + "mapeditor-link-title-switcher-popup-text": "Popup com texto", "mapeditor-link-title-switcher-link-text": "Ligação", "mapeditor-form-field-title": "Título", "mapeditor-form-field-text": "Texto", "mapeditor-form-field-link": "Ligação", "mapeditor-form-field-icon": "Ícone", "mapeditor-form-field-group": "Grupo", + "mapeditor-form-field-inlinelabel": "Etiqueta em linha", + "mapeditor-form-field-strokecolor": "Cor da linha", + "mapeditor-form-field-strokeopacity": "Opacidade da linha", + "mapeditor-form-field-strokeweight": "Espessura da linha", + "mapeditor-form-field-fillcolor": "Cor de preenchimento", + "mapeditor-form-field-fillopcaity": "Opacidade do preenchimento", "mapeditor-mapparam-title": "Editar parâmetros do mapa", "mapeditor-mapparam-defoption": "-Selecione o parâmetro-", - "mapeditor-form-field-image": "Imagem" + "mapeditor-imageoverlay-button": "Adicionar sobreposição de imagem", + "mapeditor-form-field-image": "Imagem", + "mapeditor-imageoverlay-title": "Detalhes da sobreposição de imagem", + "semanticmaps-unrecognizeddistance": "O valor $1 não é uma distância válida.", + "semanticmaps-kml-link": "Ver o ficheiro KML", + "semanticmaps-default-kml-pagelink": "Ver a página $1", + "semanticmaps-latitude": "Latitude: $1", + "semanticmaps-longitude": "Longitude: $1", + "semanticmaps-altitude": "Altitude: $1", + "semanticmaps-forminput-locations": "Locais", + "semanticmaps-par-staticlocations": "Uma lista de localizações para acrescentar ao mapa em conjunto com os dados consultados. Tal como nos pontos a apresentar (\"display_points\"), pode adicionar um título, descrição e ícone por localização, usando o til \"~\" como separador.", + "semanticmaps-par-showtitle": "Mostrar, ou não, um título na janela informativa do marcador. É frequentemente desejável desativar esta funcionalidade quando usar uma predefinição para formatar o conteúdo da janela informativa.", + "semanticmaps-par-centre": "O centro do mapa. Quando este não for fornecido, o mapa escolherá automaticamente o centro óptimo para apresentar todos os marcadores do mapa.", + "semanticmaps-par-template": "Uma predefinição que será usada para formatar o conteúdo da janela informativa.", + "validator-type-mapscircle": "Círculo geográfico", + "validator-type-mapscircle-list": "Lista de círculos", + "validator-type-mapsimageoverlay": "Sobreposição de imagem", + "validator-type-mapsimageoverlay-list": "Lista de sobreposições de imagem", + "validator-type-mapsline": "Linha geográfica", + "validator-type-mapsline-list": "Lista de linhas", + "validator-type-mapslocation": "Localização geográfica", + "validator-type-mapslocation-list": "Lista de locais", + "validator-type-mapsrectangle": "Retângulo geográfico", + "validator-type-mapsrectangle-list": "Lista de retângulos" } diff --git a/i18n/qqq.json b/i18n/qqq.json index 840a099f2..7b57071d6 100644 --- a/i18n/qqq.json +++ b/i18n/qqq.json @@ -11,72 +11,145 @@ "Umherirrender", "Тест", "아라", - "Liuxinyu970226" + "Liuxinyu970226", + "Nike", + "Lloffiwr" ] }, - "maps-desc": "{{desc|name=Maps|url=https://www.mediawiki.org/wiki/Extension:Maps}}", + "maps-desc": "{{desc|name=Maps|url=https://www.semantic-mediawiki.org/wiki/Extension:Maps}}", "right-geocode": "{{doc-right|geocode}}", "action-geocode": "{{doc-action|geocode}}", "maps_map": "{{Identical|Map}}", - "maps-tracking-category": "The name of a category for all pages which use the display_map parser extension function or tag.\n\nThe category is automatically added unless the feature is disabled.", + "maps-tracking-category": "The name of a category for all pages which use the #display_map parser function or display_map tag.", + "maps-loading-map": "This is an informatory message.", + "maps-load-failed": "This is an error message.", "maps-markers": "{{Identical|Marker}}", - "maps-copycoords-prompt": "text displayed in JavaScript prompt to indicate first press ctrl+c to copy text, and press enter to close prompt", + "maps-copycoords-prompt": "This is the text displayed in JavaScript prompt to indicate first press ctrl+c to copy text, and press enter to close prompt.", "maps-searchmarkers-text": "{{doc-important|Translate \"Filter\" as being a word.}}\nThis message is located within an input box to assist users with the input. This fuctionality allows to filter the markers based on the input one provides. See the [https://semantic-mediawiki.org/wiki/Maps_examples/OpenLayers_searchmarkers live example].", "maps-others": "{{Identical|Other}}", - "maps-kml-parsing-failed": "text displayed in the event of parsing failure of kml file(s).", - "maps-ns-layer": "{{Identical|Layer}}", + "maps-kml-parsing-failed": "This is an error message.", + "maps-ns-layer": "This is the name of the \"Layer\" namespace. The term \"Layer\" should be identical to the one used for the corresponding namespace. See {{msg-mw|Maps-ns-layer-talk}}\n\n{{Identical|Layer}}", + "maps-ns-layer-talk": "This is the name of the talk page for the \"Layer\" namespace. The term \"Layer\" should be identical to the one used for the corresponding namespace. See {{msg-mw|Maps-ns-layer}}\n\n{{Identical|Layer}}", "maps-layer-property": "{{Identical|Property}}", "maps-layer-value": "{{Identical|Value}}", "maps-layer-errors": "{{Identical|Error}}", - "maps-layerdef-invalid": "Parameters:\n* $1 - number of definitions", - "maps-layerdef-wrong-namespace": "Parameters:\n* $1 - namespace name", - "maps-layerdef-equal-layer-name": "Parameters:\n* $1 - layer name", - "maps-layerpage-usage": "Parameters:\n* $1 - layer name", - "maps-error-invalid-layertype": "Used as error message. Parameters:\n* $1 - a layer type\n* $2 - list of available layer types\n* $3 - number of available layer types", - "maps-error-no-layertype": "Used as error message. Parameters:\n* $1 - list of available layer types\n* $2 - number of available layer types", - "validation-error-invalid-layer": "Parameters:\n* $1 - parameter name\n{{Related|Maps-validation}}", - "validation-error-invalid-layers": "Parameters:\n* $1 - parameter name\n{{Related|Maps-validation}}", - "validation-error-no-non-numeric": "Parameters:\n* $1 - parameter name\n{{Related|Maps-validation}}", - "validation-error-no-non-numerics": "Parameters:\n* $1 - parameter name\n{{Related|Maps-validation}}", - "maps-layer-of-type": "Used as

heading. Parameters:\n* $1 - layer type", - "maps-layer-of-type-and-name": "Parameters:\n* $1 - layer type\n* $2 - layer name", - "maps-layer-type-supported-by": "Parameters:\n* $1 - list of supported services (geonames and/or google). not localized.\n* $2 - number of supported services", + "maps-layerdef-invalid": "This is an error message.\n\nParameter:\n* $1 - number of definitions", + "maps-layerdef-invalid-fatal": "This is an error message.", + "maps-layerdef-wrong-namespace": "This is an error message.\n\nParameter:\n* $1 - namespace name", + "maps-layerdef-equal-layer-name": "This is an error message.\n\nParameter:\n* $1 - layer name", + "maps-layerpage-usage": "This is an informatory message.\n\nParameter:\n* $1 - layer name", + "maps-layerpage-nousage": "This is an informatory message.", + "maps-error-invalid-layertype": "This is an error message.\n\nParameters:\n* $1 - a layer type\n* $2 - list of available layer types\n* $3 - number of available layer types", + "maps-error-no-layertype": "This is an error message.\n\nParameters:\n* $1 - list of available layer types\n* $2 - number of available layer types", + "validation-error-invalid-layer": "This is an error message.\n\nParameter:\n* $1 - parameter name\n\n{{Related|Maps-validation}}", + "validation-error-invalid-layers": "This is an error message.\n\nParameter:\n* $1 - parameter name\n\n{{Related|Maps-validation}}", + "validation-error-no-non-numeric": "This is an error message.\n\nParameter:\n* $1 - parameter name\n\n{{Related|Maps-validation}}", + "validation-error-no-non-numerics": "This is an error message.\n\nParameter:\n* $1 - parameter name\n\n{{Related|Maps-validation}}", + "maps-layer-of-type": "This is used as an

heading.\n\nParameter:\n* $1 - layer type", + "maps-layer-of-type-and-name": "This is used as an

heading.\n\nParameters:\n* $1 - layer type\n* $2 - layer name", + "maps-layer-type-supported-by": "This is an informatory and/or error message.\n\nParameters:\n* $1 - list of supported services (geonames and/or google).\n* $2 - number of supported services\n\n{{doc-important|Do not localise the supported services shown in parameter $1!}}", + "maps-coordinates-description": "This message describes the #coordinates parser function as well as the coordinates tag.", + "maps-displaymap-description": "This message describes the #display_map parser function as well as the display_map tag.", + "maps-distance-description": "This message describes the #distance parser function as well as the distance tag.", + "maps-finddestination-description": "This message describes the #finddestination parser function as well as the finddestination tag.", + "maps-geocode-description": "This message describes the #geocode parser function as well as the geocode tag.", + "maps-geodistance-description": "This message describes the #geodistance parser function as well as the geodistance tag.", + "maps-mapsdoc-description": "This message describes the #mapsdoc parser function as well as the mapsdoc tag.", + "maps-layerdefinition-description": "This message describes the #layerdifinition parser function as well as the layerdefinition tag.", + "maps-mapsdoc-par-service": "{{maps-par|mapsdoc|service}}", + "maps-mapsdoc-par-language": "{{maps-par|mapsdoc|language}}", + "maps-coordinates-par-location": "{{maps-par|coordinates|location}}", + "maps-coordinates-par-format": "{{maps-par|coordinates|format}}", + "maps-coordinates-par-directional": "{{maps-par|coordinates|directional}}", + "maps-distance-par-distance": "{{maps-par|distance|distance}}", + "maps-distance-par-decimals": "{{maps-par|distance|decimals}}", + "maps-distance-par-unit": "{{maps-par|distance|unit}}", + "maps-finddestination-par-location": "{{maps-par|finddestination|location}}", + "maps-finddestination-par-bearing": "{{maps-par|finddestination|bearing}}", + "maps-finddestination-par-distance": "{{maps-par|finddestination|distance}}", + "maps-finddestination-par-format": "{{maps-par|finddestination|format}}", + "maps-finddestination-par-directional": "{{maps-par|finddestination|directional}}", + "maps-finddestination-par-allowcoordinates": "{{maps-par|finddestination|allowcoordinates}}", + "maps-finddestination-par-geoservice": "{{maps-par|finddestination|geoservice}}", + "maps-finddestination-par-mappingservice": "{{maps-par|finddestination|mappingservice}}", + "maps-geocode-par-location": "{{maps-par|geocode|location}}", + "maps-geocode-par-mappingservice": "{{maps-par|geocode|mappingservice}}", + "maps-geocode-par-geoservice": "{{maps-par|geocode|geoservice}}", "maps-geocode-par-allowcoordinates": "{{maps-par|geocode|allowcoordinates}}\n\n{{doc-important|Do not translate the parameter values \"yes\" and \"no\".}}", + "maps-geocode-par-format": "{{maps-par|geocode|format}}", + "maps-geocode-par-directional": "{{maps-par|geocode|directional}}", + "maps-geodistance-par-location1": "{{Maps-par|geodistance|location1}}", + "maps-geodistance-par-location2": "{{Maps-par|geodistance|location2}}", + "maps-geodistance-par-unit": "{{Maps-par|geodistance|unit}}", + "maps-geodistance-par-decimals": "{{Maps-par|geodistance|decimals}}", + "maps-geodistance-par-mappingservice": "{{Maps-par|geodistance|mappingservice}}", "maps-geodistance-par-geoservice": "{{Maps-par|geodistance|geoservice}}", + "maps-displaymap-par-mappingservice": "{{Maps-par|displaymap|mappingservice}}", + "maps-displaymap-par-coordinates": "{{Maps-par|displaymap|coordinates}}", "maps-displaymap-par-visitedicon": "{{Maps-par|displaymap|visitedicon}}", - "maps-displaymap-par-copycoords": "{{doc-paramdesc|copycoords}}", - "maps-fullscreen-button": "Text displayed in the map as a button to toggle fullscreen view.\n\nTooltip for this button is {{msg-mw|Maps-fullscreen-button-tooltip}}.", - "maps-fullscreen-button-tooltip": "Text displayed when hovering over the button which is labeled {{msg-mw|Maps-fullscreen-button}}.", - "maps-googlemaps3-par-enable-fullscreen": "{{maps-par|googlemaps3|enablefullscreen}}", - "validation-error-invalid-location": "Parameters:\n* $1 - parameter name\n{{Related|Maps-validation}}", - "validation-error-invalid-locations": "Parameters:\n* $1 - parameter name\n{{Related|Maps-validation}}", - "validation-error-invalid-width": "Parameters:\n* $1 - parameter name\n{{Related|Maps-validation}}", - "validation-error-invalid-height": "Parameters:\n* $1 - parameter name\n{{Related|Maps-validation}}", - "validation-error-invalid-distance": "Parameters:\n* $1 - parameter name\n{{Related|Maps-validation}}", - "validation-error-invalid-distances": "Parameters:\n* $1 - parameter name\n{{Related|Maps-validation}}", - "validation-error-invalid-image": "Parameters:\n* $1 - parameter name\n{{Related|Maps-validation}}", - "validation-error-invalid-images": "Parameters:\n* $1 - parameter name\n{{Related|Maps-validation}}", - "validation-error-invalid-goverlay": "Parameters:\n* $1 - parameter name\n{{Related|Maps-validation}}", - "validation-error-invalid-goverlays": "Parameters:\n* $1 - parameter name\n{{Related|Maps-validation}}", + "maps-displaymap-par-zoom": "{{Maps-par|displaymap|zoom}}", + "maps-displaymap-par-centre": "{{Maps-par|displaymap|centre}}", + "maps-displaymap-par-title": "{{Maps-par|displaymap|title}}", + "maps-displaymap-par-label": "{{Maps-par|displaymap|label}}", + "maps-displaymap-par-icon": "{{Maps-par|displaymap|icon}}", + "maps-displaymap-par-circles": "{{Maps-par|displaymap|circles}}", + "maps-displaymap-par-copycoords": "{{Maps-par|displaymap|copycoords}}", + "maps-displaymap-par-lines": "{{Maps-par|displaymap|lines}}", + "maps-displaymap-par-maxzoom": "{{Maps-par|displaymap|maxzoom}}", + "maps-displaymap-par-minzoom": "{{Maps-par|displaymap|minzoom}}", + "maps-displaymap-par-polygons": "{{Maps-par|displaymap|polygons}}", + "maps-displaymap-par-rectangles": "{{Maps-par|displaymap|rectangles}}", + "maps-displaymap-par-static": "{{Maps-par|displaymap|static}}", + "maps-displaymap-par-wmsoverlay": "{{Maps-par|displaymap|wmsoverlay}}\n\nWMS stands for [[wikipedia:en:Web_Map_Service|Web Map Service]].", + "maps-fullscreen-button": "This is the text of a switch.\n\nSee also:\n* {{msg-mw|Maps-fullscreen-button-tooltip}}.", + "maps-fullscreen-button-tooltip": "This is the text of a tooltip popup.\n\nSee also:\n* {{msg-mw|Maps-fullscreen-button}}.", + "validation-error-invalid-location": "This is an error message.\n\nParameter:\n* $1 - parameter name\n\n{{Related|Maps-validation}}", + "validation-error-invalid-locations": "This is an error message.\n\nParameter:\n* $1 - parameter name\n\n{{Related|Maps-validation}}", + "validation-error-invalid-width": "This is an error message.\n\nParameter:\n* $1 - parameter name\n\n{{Related|Maps-validation}}", + "validation-error-invalid-height": "This is an error message.\n\nParameter:\n* $1 - parameter name\n\n{{Related|Maps-validation}}", + "validation-error-invalid-distance": "This is an error message.\n\nParameter:\n* $1 - parameter name\n\n{{Related|Maps-validation}}", + "validation-error-invalid-distances": "This is an error message.\n\nParameter:\n* $1 - parameter name\n\n{{Related|Maps-validation}}", + "validation-error-invalid-image": "This is an error message.\n\nParameter:\n* $1 - parameter name\n\n{{Related|Maps-validation}}", + "validation-error-invalid-images": "This is an error message.\n\nParameter:\n* $1 - parameter name\n\n{{Related|Maps-validation}}", + "validation-error-invalid-goverlay": "This is an error message.\n\nParameter:\n* $1 - parameter name\n\n{{Related|Maps-validation}}", + "validation-error-invalid-goverlays": "This is an error message.\n\nParameter:\n* $1 - parameter name\n\n{{Related|Maps-validation}}", "maps-abb-north": "Symbol for representing \"north\" in geolocation coordinates.", - "maps-latitude": "{{Identical|Latitude}}", - "maps-longitude": "{{Identical|Longitude}}", - "maps-invalid-coordinates": "Unused at this time. Parameters:\n* $1 - value", - "maps_geocoding_failed": "Unused at this time. Parameters:\n* $1 - list of addresses\n* $2 - number of addresses", - "maps_geocoding_failed_for": "Parameters:\n* $1 - list of items\n* $2 - number of items, for PLURAL support", - "maps_unrecognized_coords": "Unused at this time. Parameters:\n* $1 - list of coordinates\n* $2 - number of coordinates\nSee also:\n* {{msg-mw|Maps unrecognized coords for}}", - "maps_unrecognized_coords_for": "Unused at this time. Parameters:\n* $1 - list of coordinates\n* $2 - number of coordinates\nSee also:\n* {{msg-mw|Maps unrecognized coords}}", + "maps-abb-east": "Symbol for representing \"east\" in geolocation coordinates.", + "maps-abb-south": "Symbol for representing \"south\" in geolocation coordinates.", + "maps-abb-west": "Symbol for representing \"west\" in geolocation coordinates.", + "maps-latitude": "This is a field label.\n\n{{Identical|Latitude}}", + "maps-longitude": "This is a field label.\n\n{{Identical|Longitude}}", + "maps-invalid-coordinates": "This is an error message.\n\nParameter:\n* $1 - value", + "maps_coordinates_missing": "This is an error message.", + "maps_geocoding_failed": "This is an error message.\n\nParameters:\n* $1 - list of addresses\n* $2 - number of addresses", + "maps_geocoding_failed_for": "This is an error message.\n\nParameters:\n* $1 - list of items\n* $2 - number of items, for PLURAL support", + "maps_unrecognized_coords": "This is an error message.\n\nParameters:\n* $1 - list of coordinates\n* $2 - number of coordinates\n\nSee also:\n* {{msg-mw|Maps unrecognized coords for}}", + "maps_unrecognized_coords_for": "This is an error message.\n\nParameters:\n* $1 - list of coordinates\n* $2 - number of coordinates\n\nSee also:\n* {{msg-mw|Maps unrecognized coords}}", + "maps_map_cannot_be_displayed": "This is an error message.", + "maps-geocoder-not-available": "This is an informatory and/or error message.", "maps_googlemaps3": "Lable for a result format on SMW's special page \"Ask\".\n\n{{optional}}", - "maps_leaflet": "Lable for a result format on SMW's special page \"Ask\".\n\n{{optional}}", - "maps-leaflet-par-zoom": "{{maps-par|leaflet|zoom}}", + "maps_leaflet": "This is a field label.\n\n{{optional}}", "maps-leaflet-par-defzoom": "{{maps-par|leaflet|defzoom}}", - "maps-leaflet-par-resizable": "{{maps-par|leaflet|resizable}}", + "maps-leaflet-par-layer": "{{maps-par|leaflet|layer}}", + "maps-leaflet-par-overlaylayers": "{{maps-par|leaflet|overlaylayers}}", + "maps-leaflet-par-maxclusterradius": "{{maps-par|leaflet|maxclusterradius}}", + "maps-leaflet-par-clusterspiderfy": "{{maps-par|leaflet|clusterspiderfy}}", "maps_openlayers": "Lable for a result format on SMW's special page \"Ask\".\n\n{{optional}}", - "maps_osm": "Lable for a result format on SMW's special page \"Ask\".\n\n{{optional}}", - "maps_centred_on": "Parameters:\n* $1 - latitude\n* $2 - longitude", - "maps-par-searchmarkers": "{{maps-par|maps|searchmarkers}}", + "maps_click_to_activate": "This is an informatory message.", + "maps_centred_on": "This is an informatory message.\n\nParameters:\n* $1 - latitude\n* $2 - longitude", + "maps-par-mappingservice": "{{maps-par-all|mappingservice}}", + "maps-par-resizable": "System message used by several maps services:\n\n{{maps-par|googlemaps3|resizable}}\n{{maps-par|openlayers|resizable}}\n{{maps-par|leaflet|resizable}}", + "maps-par-searchmarkers": "System message used by serveral maps services:\n\n{{maps-par|googlemaps3|searchmarkers}}\n{{maps-par|leaflet|searchmarkers}}", + "maps-par-geoservice": "{{maps-par-all|geoservice}}", + "maps-par-zoom": "System message used by serveral maps services:\n\n{{maps-par|googlemaps3|zoom}}\n{{maps-par|openlayers|zoom}}\n{{maps-par|leaflet|zoom}}", + "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-kml": "System message used by serveral maps services:\n\n{{maps-par|googlemaps3|kml}}\n{{maps-par|openlayers|kml}}", + "maps-par-markercluster": "{{maps-par-all|markercluster}}", + "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-markercluster": "{{maps-par|googlemaps3|markercluster}}", "maps-googlemaps3-par-type": "{{maps-par|googlemaps3|type}}", "maps-googlemaps3-par-types": "{{maps-par|googlemaps3|types}}", "maps-googlemaps3-par-layers": "{{maps-par|googlemaps3|layers}}", @@ -84,46 +157,90 @@ "maps-googlemaps3-par-zoomstyle": "{{maps-par|googlemaps3|zoomstyle}}", "maps-googlemaps3-par-typestyle": "{{maps-par|googlemaps3|typestyle}}", "maps-googlemaps3-par-autoinfowindows": "{{maps-par|googlemaps3|autoinfowindows}}", - "maps-googlemaps3-par-kml": "{{maps-par|googlemaps3|kml}}", "maps-googlemaps3-par-gkml": "{{maps-par|googlemaps3|gkml}}", "maps-googlemaps3-par-fusiontables": "{{maps-par|googlemaps3|fusiontables}}", "maps-googlemaps3-par-tilt": "{{maps-par|googlemaps3|tilt}}", "maps-googlemaps3-par-kmlrezoom": "{{maps-par|googlemaps3|kmlrezoom}}\n\nKML stands for [[w:Keyhole Markup Language|Keyhole Markup Language]].", "maps-googlemaps3-par-poi": "{{maps-par|googlemaps3|poi}}", - "mapeditor": "title of the special page [[Special:MapEditor]].", - "specialpages-group-maps": "{{doc-special-group|like=[[Special:MapEditor]]}}\n{{Identical|Map}}", - "mapeditor-parser-error": "Error message when parsing error occurs", - "mapeditor-none-text": "Text showing when no value is set (None).\n{{Identical|None}}", - "mapeditor-done-button": "Button text describing that editing details is completed", - "mapeditor-remove-button": "Button text that describes that the given map object should be removed", - "mapeditor-import-button2": "Button text that finishes import process.\n{{Identical|Import}}", - "mapeditor-export-button": "Button text that describes that the process of exporting", - "mapeditor-import-button": "Button text that opens import dialogue", - "mapeditor-select-button": "Button text selects the map object", - "mapeditor-mapparam-button": "Button text that opens map parameter dialogue", - "mapeditor-clear-button": "Button text that clears the map", - "mapeditor-code-title": "Title of dialogue", - "mapeditor-import-title": "Title of dialogue", - "mapeditor-import-note": "Import note", - "mapeditor-form-title": "Title of dialogue", - "mapeditor-link-title-switcher-popup-text": "Text for switch w/popup", - "mapeditor-link-title-switcher-link-text": "Text for switch w/link.\n{{Identical|Link}}", - "mapeditor-form-field-title": "Form field name.\n{{Identical|Title}}", - "mapeditor-form-field-text": "Form field name.\n{{Identical|Text}}", - "mapeditor-form-field-link": "Form field name.\n{{Identical|Link}}", - "mapeditor-form-field-icon": "Form field name.\n{{Identical|Icon}}", - "mapeditor-form-field-group": "Form field name.\n{{Identical|Group}}", - "mapeditor-form-field-inlinelabel": "Form field name", - "mapeditor-form-field-strokecolor": "Form field name", - "mapeditor-form-field-strokeopacity": "Form field name", - "mapeditor-form-field-strokeweight": "Form field name", - "mapeditor-form-field-fillcolor": "Form field name.\n{{Identical|Fill color}}", - "mapeditor-form-field-fillopcaity": "Form field name", - "mapeditor-form-field-showonhover": "Checkbox text", - "mapeditor-mapparam-title": "Title of dialogue", - "mapeditor-mapparam-defoption": "Default option in map parameters select list.\n{{Identical|Select parameter}}", - "mapeditor-imageoverlay-button": "Button text that starts the \"add image overlay process\"", - "mapeditor-form-field-image": "Form field name.\n{{Identical|Image}}", - "mapeditor-imageoverlay-title": "Title of dialogue", - "mapeditor-form-field-visitedicon": "Form field name" + "maps-googlemaps3-par-clustergridsize": "{{maps-par|googlemaps3|clustergridsize}}", + "maps-par-clustermaxzoom": "{{maps-par|googlemaps3|clustermaxzoom}}", + "maps-par-clusterzoomonclick": "{{maps-par|googlemaps3|clusterzoomonclick}}", + "maps-par-maxclusterradius": "{{maps-par|googlemaps3|clustermaxradius}}", + "maps-googlemaps3-par-clusteraveragecenter": "{{maps-par|googlemaps3|clusteraveragecenter}}", + "maps-googlemaps3-par-clusterminsize": "{{maps-par|googlemaps3|clusterminsize}}", + "maps-openlayers-par-controls": "{{maps-par-all|controls}}", + "maps-openlayers-par-layers": "{{maps-par|openlayers|layers}}", + "maps-openlayers-par-overlays": "{{maps-par|openlayers|overlays}}", + "mapeditor": "{{doc-special-group|like=[[Special:MapEditor]]}}", + "specialpages-group-maps": "{{doc-special-group|like=[[Special:MapEditor]]}}\n\n{{Identical|Map}}", + "mapeditor-parser-error": "This is an error message.", + "mapeditor-none-text": "This is an informatory message.\n\n{{Identical|None}}", + "mapeditor-done-button": "This is the text on a button.", + "mapeditor-remove-button": "This is the text on a button.", + "mapeditor-import-button2": "This is the text on a button.\n\n{{Identical|Import}}", + "mapeditor-export-button": "This is the text on a button.", + "mapeditor-import-button": "This is the text on a button.", + "mapeditor-select-button": "This is the text on a button.", + "mapeditor-mapparam-button": "This is the text on a button.", + "mapeditor-clear-button": "This is the text on a button.", + "mapeditor-code-title": "This is the title of a dialogue.", + "mapeditor-import-title": "This is the title of a dialogue.", + "mapeditor-import-note": "This is an informatory message.", + "mapeditor-form-title": "This is the title of a dialogue.", + "mapeditor-link-title-switcher-popup-text": "This is the text of a switch with a popup.", + "mapeditor-link-title-switcher-link-text": "This is the text for a switch with a link.\n\n{{Identical|Link}}", + "mapeditor-form-field-title": "This is and form field name.\n\n{{Identical|Title}}", + "mapeditor-form-field-text": "This is and form field name.\n\n{{Identical|Text}}", + "mapeditor-form-field-link": "This is and form field name.\n\n{{Identical|Link}}", + "mapeditor-form-field-icon": "This is and form field name.\n\n{{Identical|Icon}}", + "mapeditor-form-field-group": "This is and form field name.\n\n{{Identical|Group}}", + "mapeditor-form-field-inlinelabel": "This is and form field name.", + "mapeditor-form-field-strokecolor": "This is and form field name.", + "mapeditor-form-field-strokeopacity": "This is and form field name.", + "mapeditor-form-field-strokeweight": "This is and form field name.", + "mapeditor-form-field-fillcolor": "This is and form field name.\n\n{{Identical|Fill color}}", + "mapeditor-form-field-fillopcaity": "This is and form field name.", + "mapeditor-form-field-showonhover": "This is the text for a checkbox.", + "mapeditor-mapparam-title": "This is the title of a dialogue.", + "mapeditor-mapparam-defoption": "This is the default option in a select list.\n\n{{Identical|Select parameter}}", + "mapeditor-imageoverlay-button": "This is the text on a button.", + "mapeditor-form-field-image": "This is and form field name.\n\n{{Identical|Image}}", + "mapeditor-imageoverlay-title": "This is the title of a dialogue.", + "mapeditor-form-field-visitedicon": "This is and form field name.", + "semanticmaps-unrecognizeddistance": "This is an error message.\n\nParameter:\n* $1 - distance", + "semanticmaps-kml-link": "This is the label of a link.", + "semanticmaps-kml": "{{optional}}", + "semanticmaps-default-kml-pagelink": "Used as default value for \"pagelinktext\" input box.\nSee example: [{{canonicalurl:Special:Ask|format=kml}} Special:Ask]\n\n$1 is not a parameter (appears as is), but it will be replaced by the page title.\n\nSee also:\n* {{msg-mw|Semanticmaps-kml-pagelinktext}}\n{{Identical|View page}}", + "semanticmaps-latitude": "Parameters:\n* $1 - latitude\nSee also:\n* {{msg-mw|Semanticmaps-longitude}}\n* {{msg-mw|Semanticmaps-altitude}}\n{{Identical|Latitude}}", + "semanticmaps-longitude": "Parameters:\n* $1 - longitude\nSee also:\n* {{msg-mw|Semanticmaps-latitude}}\n* {{msg-mw|Semanticmaps-altitude}}\n{{Identical|Longitude}}", + "semanticmaps-altitude": "Parameters:\n* $1 - altitude\nSee also:\n* {{msg-mw|Semanticmaps-latitude}}\n* {{msg-mw|Semanticmaps-longitude}}", + "semanticmaps-forminput-locations": "This is a button label.\n\n{{Identical|Location}}", + "semanticmaps-par-staticlocations": "{{doc-paramdesc|staticlocations}}", + "semanticmaps-par-showtitle": "{{doc-paramdesc|showtitle}}", + "semanticmaps-par-hidenamespace": "{{doc-paramdesc|hidenamespace}}", + "semanticmaps-par-centre": "{{doc-paramdesc|centre}}", + "semanticmaps-par-template": "{{doc-paramdesc|template}}", + "semanticmaps-par-geocodecontrol": "{{doc-paramdesc|geocodecontrol}}", + "semanticmaps-par-activeicon": "{{doc-paramdesc|activeicon}}", + "semanticmaps-par-pagelabel": "{{doc-paramdesc|pagelabel}}\n\n{{doc-important|Do not translate the parameter value \"yes\".}}", + "semanticmaps-par-ajaxcoordproperty": "{{doc-paramdesc|ajaxcoordproperty}}", + "semanticmaps-par-ajaxquery": "{{doc-paramdesc|ajaxquery}}", + "semanticmaps-par-userparam": "{{doc-paramdesc|userparam}}", + "semanticmaps-kml-text": "{{doc-paramdesc|text}}", + "semanticmaps-kml-title": "{{doc-paramdesc|title}}", + "semanticmaps-kml-linkabsolute": "{{doc-paramdesc|absolute}}", + "semanticmaps-kml-pagelinktext": "Used as description for \"pagelinktext\" input box.\nSee example: [{{canonicalurl:Special:Ask|format=kml}} Special:Ask]\n\n$1 is not a parameter, and appears as is.\n\nDefault value for the input box is {{msg-mw|Semanticmaps-default-kml-pagelink}}.", + "semanticmaps-shapes-improperformat": "This is an error message.\n\nParameter:\n* $1 - improper text", + "semanticmaps-shapes-missingshape": "This is an error message.\n\nParameter:\n* $1 - name of the shape", + "validator-type-mapscircle": "This is the name of a type of values that may be assigned to a parameter.", + "validator-type-mapscircle-list": "This is the name of a type of values that may be assigned to a parameter.", + "validator-type-mapsimageoverlay": "This is the name of a type of values that may be assigned to a parameter.", + "validator-type-mapsimageoverlay-list": "This is the name of a type of values that may be assigned to a parameter.", + "validator-type-mapsline": "This is the name of a type of values that may be assigned to a parameter.", + "validator-type-mapsline-list": "This is the name of a type of values that may be assigned to a parameter.", + "validator-type-mapslocation": "This is the name of a type of values that may be assigned to a parameter.", + "validator-type-mapslocation-list": "This is the name of a type of values that may be assigned to a parameter.", + "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]]." } diff --git a/i18n/ro.json b/i18n/ro.json index 199bcef07..88911cdb0 100644 --- a/i18n/ro.json +++ b/i18n/ro.json @@ -58,8 +58,6 @@ "maps-geocoder-not-available": "Opțiunea de geocodare pentru Hărți nu este disponibilă. Locația dumneavoastră nu a putut fi geocodată.", "maps_click_to_activate": "Apăsați pentru a activa harta", "maps_centred_on": "Hartă centrată la $1, $2.", - "maps-osm-par-thumbs": "Afișează miniaturi", - "maps-osm-par-photos": "Afișează fotografii", "mapeditor": "Editor de hărți", "specialpages-group-maps": "Hărți", "mapeditor-none-text": "Nimic", @@ -93,5 +91,6 @@ "mapeditor-imageoverlay-button": "Adaugă suprapunerea de imagine", "mapeditor-form-field-image": "Imagine", "mapeditor-imageoverlay-title": "Detalii suprapunere imagine", - "mapeditor-form-field-visitedicon": "Pictogramă vizitată" + "mapeditor-form-field-visitedicon": "Pictogramă vizitată", + "semanticmaps-forminput-locations": "Locuri" } diff --git a/i18n/ru.json b/i18n/ru.json index 969c0d448..d7874e86a 100644 --- a/i18n/ru.json +++ b/i18n/ru.json @@ -15,7 +15,11 @@ "Yuriy Apostol", "Александр Сигачёв", "Macofe", - "Perevod16" + "Perevod16", + "Alexandr Efremov", + "Cat1987", + "Туллук", + "Pastakhov" ] }, "maps-desc": "Позволяет встраивать динамические карты в вики-страницы, геокодировать адреса и выполнять другие географические действия", @@ -25,7 +29,7 @@ "maps-tracking-category": "Страницы с картой, сгенерированной расширением Maps", "maps-loading-map": "Идёт загрузка карты…", "maps-load-failed": "Невозможно загрузить карту!", - "maps-markers": "Отметки", + "maps-markers": "Маркеры", "maps-copycoords-prompt": "CTRL+C, ENTER", "maps-searchmarkers-text": "Маркеры фильтра", "maps-others": "другие", @@ -52,7 +56,6 @@ "maps-layer-type-supported-by": "Этот тип слоя может быть использован {{PLURAL:$2|1=только с картографической службой|только со следующими картографическими службами:}} $1", "maps-coordinates-description": "Перехватчик синтаксического анализатора для форматирования координат из любого и в любой поддерживаемый формат.", "maps-displaymap-description": "Отображение географических карт без каких-либо вики-маркеров на них.", - "maps-displaypoint-description": "Отображение географических карт с одним или несколькими вики-маркерами на них.", "maps-distance-description": "Преобразование расстояния, выраженного в определенных поддерживаемых единицах, в его эквивалент в других единицах.", "maps-finddestination-description": "Найти место назначения от заданной начальной точки (может быть в любом формате из поддерживаемых), начальное направление и расстояние.", "maps-geocode-description": "Включает геокодирование адресов. Иными словами, преобразует понятные человеку названия мест в наборы координат. Поддерживается несколько сервисов геокодирования, которые не следует путать с картографическими сервисами.", @@ -106,7 +109,6 @@ "maps-displaymap-par-wmsoverlay": "Использовать слой WMS", "maps-fullscreen-button": "Переключить полноэкранный режим", "maps-fullscreen-button-tooltip": "Посмотреть карту в полноэкранном или встроенном режиме.", - "maps-googlemaps3-par-enable-fullscreen": "Включить кнопку полноэкранного режима", "validation-error-invalid-location": "Параметр $1 должен быть корректным местоположением.", "validation-error-invalid-locations": "Параметр $1 должен содержать одно или несколько корректных местоположений.", "validation-error-invalid-width": "Параметр $1 должен быть корректной шириной.", @@ -132,6 +134,9 @@ "maps_map_cannot_be_displayed": "Карта не может быть показана.", "maps-geocoder-not-available": "Функция геокодирования карт недоступна, ваше местоположение не может быть геокодировано.", "maps_leaflet": "Листовка", + "maps-leaflet-par-defzoom": "Позволяет задавать масштаб карты по умолчанию.", + "maps-leaflet-par-layer": "Уровень, который отображается при загрузке карты.", + "maps-leaflet-par-overlaylayers": "Наложение слоев, которые будут показаны, когда карта загружена.", "maps_click_to_activate": "Нажмите для активации карты", "maps_centred_on": "Центр карты — $1, $2.", "maps-par-mappingservice": "Позволяет выбрать сервис карт, который будет использоваться.", @@ -142,9 +147,11 @@ "maps-par-width": "Позволяет задать ширину карты. По умолчанию единицей измерения будет считаться пиксель, но можно явно указать одну из следующих единиц: px, ex, em, %.", "maps-par-height": "Позволяет задать высоту карты. По умолчанию единицей измерения будет считаться пиксель, но можно явно указать одну из следующих единиц: px, ex, em, %.", "maps-par-centre": "Расположение на карте, по которому она должна быть отцентрована", + "maps-par-enable-fullscreen": "Включить кнопку полноэкранного режима", + "maps-par-kml": "KML файлы для загрузки на карту.", + "maps-par-markercluster": "Позволяет объединить несколько находящихся рядом маркеров в один маркер", "maps-googlemaps3-incompatbrowser": "Ваш браузер несовместим с Google Maps v3.", "maps-googlemaps3-par-imageoverlays": "Позволяет добавить изображение, которое будет показано в определённом месте карты.", - "maps-googlemaps3-par-markercluster": "Позволяет объединить несколько находящихся рядом маркеров в один маркер", "maps-googlemaps3-par-type": "Тип карты для начального отображения.", "maps-googlemaps3-par-types": "Типы карты, которые будут доступны через элемент управления типом карты.", "maps-googlemaps3-par-layers": "Специальные слои для загрузки на карту.", @@ -152,17 +159,17 @@ "maps-googlemaps3-par-zoomstyle": "Стиль элемента управления масштабом.", "maps-googlemaps3-par-typestyle": "Стиль элемента управления типа.", "maps-googlemaps3-par-autoinfowindows": "Автоматически открывает все информационные окна после загрузки страницы.", - "maps-googlemaps3-par-kml": "KML файлы для загрузки на карту.", "maps-googlemaps3-par-gkml": "KML файлы, хранящиеся в Google для загрузки на карту.", "maps-googlemaps3-par-fusiontables": "Идентификаторы Сводных таблиц Google, которые должны быть загружены на карту.", "maps-googlemaps3-par-tilt": "Наклон карты при использовании Google Maps.", "maps-googlemaps3-par-kmlrezoom": "Перемасштабировать карту после загрузки слоёв KML.", "maps-googlemaps3-par-poi": "Показать достопримечательности.", + "maps-googlemaps3-par-clustergridsize": "Размер сетки кластера в пикселях.", + "maps-par-clustermaxzoom": "Максимальный уровень увеличения, при котором может существовать кластер.", + "maps-par-maxclusterradius": "Максимальный радиус, охватываемый кластером.", "maps-openlayers-par-controls": "Элементы управления для размещения на карте.", "maps-openlayers-par-layers": "Слои, которые будут доступны при выборе слоёв. Первый слой будет отображаться при загрузке карты.", "maps-openlayers-par-overlays": "Наложенные слои, которые будут доступны при выборе слоёв. Эти слои будут отображаться поверх обычного слоя, аналогично маркерам.", - "maps-osm-par-thumbs": "Показать превью", - "maps-osm-par-photos": "Показать фото", "mapeditor": "Редактор карт", "specialpages-group-maps": "Карты", "mapeditor-parser-error": "Произошла ошибка обработки метаданных. Введённые данные проигнорированы.", @@ -198,5 +205,29 @@ "mapeditor-imageoverlay-button": "Добавить наложение изображения", "mapeditor-form-field-image": "Изображение", "mapeditor-imageoverlay-title": "Изображение", - "mapeditor-form-field-visitedicon": "Иконка для посещённого" + "mapeditor-form-field-visitedicon": "Иконка для посещённого", + "semanticmaps-unrecognizeddistance": "Значение $1 — это недопустимое расстояние.", + "semanticmaps-kml-link": "Просмотреть файл KML", + "semanticmaps-default-kml-pagelink": "Просмотреть страницу $1", + "semanticmaps-latitude": "Широта: $1", + "semanticmaps-longitude": "Долгота: $1", + "semanticmaps-altitude": "Высота: $1", + "semanticmaps-forminput-locations": "Места", + "semanticmaps-par-staticlocations": "Список мест для добавления на карту вместе с запрашиваемыми данными. Например, к display_points можно добавить название, описание и значок, используя тильду ~ в качестве разделителя.", + "semanticmaps-par-showtitle": "Показывать или нет заголовок в окне информации маркера. Его отключение часто бывает полезно при использовании шаблона для форматирования содержимого окна информация.", + "semanticmaps-par-hidenamespace": "Показывать название пространства имен в информационном окне маркера", + "semanticmaps-par-centre": "Центр карты. Если не задан, карта автоматически выберет оптимальный центр, позволяющий отобразить все маркеры на карте.", + "semanticmaps-par-template": "Шаблон для форматирования содержимого окна информация.", + "semanticmaps-par-geocodecontrol": "Показать панель управления геокодированием.", + "semanticmaps-par-activeicon": "Значок, который будет отображаться вместо стандартного маркера в случаях, когда активная страница совпадает с результатом запроса", + "semanticmaps-par-pagelabel": "Если задано значение «yes» («да»), все маркеры будут иметь «inlineLabel» со ссылкой на страницу, содержащую координаты для маркера", + "semanticmaps-par-userparam": "Значение, которое передается за каждый вызов шаблона, если шаблон используется", + "semanticmaps-kml-text": "Текст, связанный с каждой страницы. Переопределяется дополнительными запрашиваемыми свойствами, если таковые имеются.", + "semanticmaps-kml-title": "Заголовок по умолчанию для результатов", + "semanticmaps-kml-linkabsolute": "Ссылки должны быть абсолютными (а не относительными)", + "semanticmaps-kml-pagelinktext": "Текст, используемый для ссылки на страницу, в которой $1 будет заменён названием страницы.", + "semanticmaps-shapes-improperformat": "Неправильное форматирование $1. Обратитесь к документации по форматированию.", + "semanticmaps-shapes-missingshape": "Фигуры для $1 не найдены. Пожалуйста, обратитесь к документации по доступным фигурам", + "validator-type-mapsline-list": "Список линий", + "validator-type-mapsrectangle-list": "Список прямоугольников" } diff --git a/i18n/si.json b/i18n/si.json index f09615f22..2f3b0ec74 100644 --- a/i18n/si.json +++ b/i18n/si.json @@ -58,15 +58,13 @@ "maps_map_cannot_be_displayed": "සිතියම සංදර්ශනය කල නොහැක.", "maps_click_to_activate": "සිතියම සක්‍රිය කිරීම සඳහා ක්ලික් කරන්න", "maps_centred_on": "$1 හිදී සිතියම මධ්‍යගත වේ, $2.", + "maps-par-kml": "සිතියම මත පැටවිය යුතු KML ගොනු.", "maps-googlemaps3-par-type": "ආරම්භක වශයෙන් පෙන්විය යුතු සිතියම් වර්ගය.", "maps-googlemaps3-par-layers": "සිතියම මත පැටවිය යුතු විශේෂ ස්ථර.", "maps-googlemaps3-par-zoomstyle": "විශාලන පාලකයේ ශෛලිය.", "maps-googlemaps3-par-typestyle": "වර්ග පාලකයේ ශෛලිය.", - "maps-googlemaps3-par-kml": "සිතියම මත පැටවිය යුතු KML ගොනු.", "maps-googlemaps3-par-poi": "අභිරුචි ලක්ෂ්‍ය පෙන්වන්න.", "maps-openlayers-par-controls": "සිතියම මත ස්ථානගත විය යුතු පාලක.", - "maps-osm-par-thumbs": "සංක්ෂිප්ත පෙන්වන්න", - "maps-osm-par-photos": "ඡායාරූප පෙන්වන්න", "mapeditor": "සිතියම් සංස්කාරක", "specialpages-group-maps": "සිතියම්", "mapeditor-none-text": "කිසිවක් නොමැත", @@ -100,5 +98,14 @@ "mapeditor-imageoverlay-button": "පින්තූර වසාලනයක් එක් කරන්න", "mapeditor-form-field-image": "පිංතූරය", "mapeditor-imageoverlay-title": "පින්තූර වසාලන විස්තර", - "mapeditor-form-field-visitedicon": "ගොඩ වැදුණු අයිකනය" + "mapeditor-form-field-visitedicon": "ගොඩ වැදුණු අයිකනය", + "semanticmaps-unrecognizeddistance": "$1 අගය වලංගු දුර ප්‍රමාණයක් නොවේ.", + "semanticmaps-kml-link": "KML ගොනුව නරඹන්න", + "semanticmaps-default-kml-pagelink": "$1 පිටුව නරඹන්න", + "semanticmaps-latitude": "අක්ෂාංශය: $1", + "semanticmaps-longitude": "දේශාංශය: $1", + "semanticmaps-altitude": "උන්නතාංශය: $1", + "semanticmaps-forminput-locations": "ස්ථාන", + "semanticmaps-par-geocodecontrol": "භූකේතීකරණ පාලකය පෙන්වන්න.", + "semanticmaps-kml-title": "ප්‍රතිඑල සඳහා සාමාන්‍ය ශීර්ෂය" } diff --git a/i18n/sv.json b/i18n/sv.json index 332234150..7350ab3be 100644 --- a/i18n/sv.json +++ b/i18n/sv.json @@ -10,7 +10,8 @@ "Per", "Rotsee", "WikiPhoenix", - "Macofe" + "Macofe", + "Martinwiss" ] }, "maps-desc": "Ger möjlighet att bädda in dynamiska kartor i wiki-sidor, geokoding av adresser och andra geografiska åtgärder", @@ -46,7 +47,6 @@ "maps-layer-type-supported-by": "Denna lagertyp kan endast användas med {{PLURAL:$2|kartläggningstjänsten $1|dessa kartläggningstjänster: $1}}.", "maps-coordinates-description": "Tolk-hook för att formatera koordinater, från och till något av de format som stöds.", "maps-displaymap-description": "Visa geografiska kartor utan några wiki-definierade markörer på dem.", - "maps-displaypoint-description": "Visa geografiska kartor med en eller flera wiki-definierade markörer på dem.", "maps-distance-description": "Konvertera ett avstånd mätt i en giltig enhet till en annan enhet.", "maps-finddestination-description": "Hitta ett mål utifrån en given startpunkt (på något giltigt format), en inledande bäring och ett avstånd.", "maps-geocode-description": "Aktiverar geokodning av adresser, med andra ord, konvertering av mänskligt läsbara platser till en uppsättning koordinater. det finns stöd för flera geokodningstjänster, som inte bör förväxlas med karttjänster.", @@ -100,7 +100,6 @@ "maps-displaymap-par-wmsoverlay": "Använd ett WMS-överdrag", "maps-fullscreen-button": "Växla fullskärmsläge", "maps-fullscreen-button-tooltip": "Visa kartan i helskärm eller som inbäddad.", - "maps-googlemaps3-par-enable-fullscreen": "Aktivera helskärmsknappen", "validation-error-invalid-location": "Parameter $1 måste vara en giltig plats.", "validation-error-invalid-locations": "Parameter $1 måste vara en eller flera giltiga platser.", "validation-error-invalid-width": "Parameter $1 måste vara en giltig bredd.", @@ -134,6 +133,8 @@ "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-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.", "maps-googlemaps3-incompatbrowser": "Din webbläsare är inte kompatibel med Google Maps v3.", "maps-googlemaps3-par-type": "Karttyp att visa initialt", "maps-googlemaps3-par-types": "De karttyper som kommer att finnas tillgänglig via typ-reglaget.", @@ -142,7 +143,6 @@ "maps-googlemaps3-par-zoomstyle": "Stilen för zoomreglaget.", "maps-googlemaps3-par-typestyle": "Stilen för typreglaget.", "maps-googlemaps3-par-autoinfowindows": "Öppna automatiskt alla informationsfönster när sidan har lästs in.", - "maps-googlemaps3-par-kml": "KML-filer att ladda upp på kartan.", "maps-googlemaps3-par-gkml": "KML-filer, tillhandahållna av Google, att ladda på kartan.", "maps-googlemaps3-par-fusiontables": "ID för Google Fusion-tabeller som ska laddas på kartan.", "maps-googlemaps3-par-tilt": "Tilta för karta när du använder Google Maps.", @@ -151,8 +151,6 @@ "maps-openlayers-par-controls": "Kontroller att placera på kartan.", "maps-openlayers-par-layers": "De lager som kommer att finnas tillgängliga i lagerväljaren. Det första lagret visas när kartan laddas.", "maps-openlayers-par-overlays": "Överdragslager som kommer att finnas tillgängliga i lager-väljare. Dessa lager kommer att visas på toppen av ett normalt lager, ungefär som en markör.", - "maps-osm-par-thumbs": "Visa miniatyrer", - "maps-osm-par-photos": "Visa foton", "mapeditor": "Kartredigerare", "specialpages-group-maps": "Kartor", "mapeditor-parser-error": "Ett fel uppstod när metadata tolkades. Ignorerar data från användaren.", @@ -188,5 +186,38 @@ "mapeditor-imageoverlay-button": "Lägg till bildöverdrag", "mapeditor-form-field-image": "Bild", "mapeditor-imageoverlay-title": "Bildöverdragsdetaljer", - "mapeditor-form-field-visitedicon": "Besökt ikon" + "mapeditor-form-field-visitedicon": "Besökt ikon", + "semanticmaps-unrecognizeddistance": "Värdet $1 är inte ett giltigt avstånd.", + "semanticmaps-kml-link": "Visa KML-filen", + "semanticmaps-default-kml-pagelink": "Visa sida $1", + "semanticmaps-latitude": "Breddgrad: $1", + "semanticmaps-longitude": "Längdgrad: $1", + "semanticmaps-altitude": "Höjd över havet: $1", + "semanticmaps-forminput-locations": "Platser", + "semanticmaps-par-staticlocations": "En lista med platser som man kan placera på kartan tillsammans med efterfrågad data. Precis som för display_points, så kan du lägga till en titel, en beskrivning och en ikon för varje plats med hjälp av tilde \"~\" som avgränsare.", + "semanticmaps-par-showtitle": "Visa en titel i markörens informationsruta eller inte. Det är ofta lämpligt att inte använda denna funktion när en mall används för informationsrutans innehåll.", + "semanticmaps-par-hidenamespace": "Visa namnrymdens titel i markörens informationsruta.", + "semanticmaps-par-centre": "Kartans mitt. Om inte angiven så kommer kartan automatiskt att hitta markörernas mittpunkt.", + "semanticmaps-par-template": "En mall som ska användas för informationsrutorna.", + "semanticmaps-par-geocodecontrol": "Visa formulär för geokodning.", + "semanticmaps-par-activeicon": "Ikon som bör visas istället för standardmarkören när den aktiva sidan är samma som resultatet från förfrågan", + "semanticmaps-par-pagelabel": "När satt till \"yes\" (ja), kommer alla markörer att ha en \"inlineLabel\" med en länk till sidan som innehåller koordinaterna för den markören", + "semanticmaps-kml-text": "Texten som hör ihop med varje sida. Om det finns efterfrågade egenskaper så tar de överhand.", + "semanticmaps-kml-title": "Förvald titel för resultaten", + "semanticmaps-kml-linkabsolute": "Ska länkar vara absoluta (i motsats till relativa)", + "semanticmaps-kml-pagelinktext": "Texten som ska användas för länkar till sidan, där $1 kommer att bytas ut med sidtiteln", + "semanticmaps-shapes-improperformat": "Felaktig formatering av $1. Se dokumentationen för formatering", + "semanticmaps-shapes-missingshape": "Inga former hittade för $1. Se dokumentationen för tillgängliga former", + "validator-type-mapscircle": "Geografisk cirkel", + "validator-type-mapscircle-list": "Lista över cirklar", + "validator-type-mapsimageoverlay": "Bildöverlägg", + "validator-type-mapsimageoverlay-list": "Lista över bildöverlägg", + "validator-type-mapsline": "Geografisk linje", + "validator-type-mapsline-list": "Lista över linjer", + "validator-type-mapslocation": "Geografisk plats", + "validator-type-mapslocation-list": "Lista över platser", + "validator-type-mapsrectangle": "Geografisk rektangel", + "validator-type-mapsrectangle-list": "Lista över rektanglar", + "validator-type-mapspolygon": "Geografisk polygon", + "validator-type-mapspolygon-list": "Lista över geografiska polygoner" } diff --git a/i18n/tl.json b/i18n/tl.json index 47f813080..a9d10823b 100644 --- a/i18n/tl.json +++ b/i18n/tl.json @@ -1,7 +1,8 @@ { "@metadata": { "authors": [ - "AnakngAraw" + "AnakngAraw", + "Jojit fb" ] }, "maps-desc": "Nagpapagana ng pagbabaon ng gumagalaw na mga mapa papaloob sa mga pahina ng wiki, pagkokodigong pangheograpiya ng mga tirahan at ibang mga gawaing pangheograpiya. ([http://mapping.referata.com/wiki/Examples mga pagpapatunghay])\n\nkakayahang ipakita ang dato ng tugmaang-pampook sa loob ng mga mapa, at mga triahan ([http://mapping.referata.com/wiki/Maps_examples mga pagpapatunghay])", @@ -26,7 +27,6 @@ "maps-layer-type-supported-by": "Ang ganitong uri ng patong ay {{PLURAL:$2|magagamit lamang sa $1 na palingkuran ng pagmamapa|magagamit lamang sa ganitong mga palingkuran ng pagmamapa: $1}}.", "maps-coordinates-description": "Kawint ng banghay upang maianyo ang mga tugmaang-pampook, magmula at papunta sa anuman sa tinatangkilik na mga anyo.", "maps-displaymap-description": "Ipakita ang mga mapang pangheograpiya na walang anumang tinukoy na pangwiking mga pananda sa ibabaw nila.", - "maps-displaypoint-description": "Ipakita ang mga mapang pangheograpiya na may isa o higit pang tinukoy na pangwiking mga pananda sa ibabaw nila.", "maps-distance-description": "Palitan ang isang kalayuan na ginagamit ang isang partikular na sinusuportahang yunit papunta sa katumbas nito na ginagamit ang isa pang yunit.", "maps-finddestination-description": "Maghanap ng isang patutunguhan na binigyan ng tuldok ng pagsisimula (na maaaring nasa loob ng anuman sa tinatangkilik na mga anyo), isang paunang kapupuntahan at isang layo.", "maps-geocode-description": "Nagpapagana ng pagsasakodigo na pangheograpiya ng mga tirahan, na sa ibang pananalita ay ang pagpapaglit ng mga kinalalagyan na mababasa ng tao upang maging mga pangkat ng mga tugmaang pampook. Mayroong suporta para sa ilang mga paglilingkod na pangkodigo ng heograpiya, na hindi dapat ikalito sa mga paglilingkod ng pagmamapa.", @@ -93,7 +93,6 @@ "maps-geocoder-not-available": "Wala ang katangiang-kasangkapang pang-geokodigo ng Mga Mapa. Hindi mageokodigo ang lokasyon mo.", "maps_googlemaps3": "Google Maps v3", "maps_openlayers": "OpenLayers", - "maps_osm": "OpenStreetMap", "maps_click_to_activate": "Pindutin upang mabuhay ang mapa", "maps_centred_on": "Nakagitna ang mapa sa $1, $2.", "maps-par-resizable": "Nakagagawang mababago ang sukat ng mapa sa pamamagitan ng pagkaladkad doon sa pang-ibabang kanang sulok nito.", @@ -101,6 +100,7 @@ "maps-par-zoom": "Ang antas ng paglapit para sa mapa. Para sa mga mapang mayroong mga pangmarka, ito ay likas na nakatakda sa pinaka malapit na antas ng pagkakatutok na nagpapakita pa rin ng lahat ng mga pangmarka.", "maps-par-width": "Nagpapahintulot ng pagtatakda ng lapad ng mapa. Ayon sa likas na pagkakatakda, ang mga piksel ay ipapalagay bilang yunit, subalit maaari mong maliwanag na tukuyin ang isa sa mga yunit na ito: px, ex, em, %.", "maps-par-height": "Nagpapahintulot ng pagtatakda ng taas ng mapa. Ayon sa likas na pagkakatakda, ang mga piksel ay ipapalagay bilang yunit, subalit maaari mong maliwanag na tukuyin ang isa sa mga yunit na ito: px, ex, em, %.", + "maps-par-kml": "Mga talaksan ng Wikang Pangmarka ng Butas ng Susian (Keyhole Markup Language, KML) na ikakarga sa ibabaw ng mapa.", "maps-googlemaps3-incompatbrowser": "Ang pantingin-tingin mo ay hindi katambal ng Mga Mapa ng Google v3.", "maps-googlemaps3-par-type": "Ang uri ng mapa na unang ipapakita.", "maps-googlemaps3-par-types": "Ang mga uri ng mapa na magiging makukuha sa pamamagitan ng pantaban ng uri.", @@ -109,7 +109,6 @@ "maps-googlemaps3-par-zoomstyle": "Ang estilo ng pantaban ng paglapit.", "maps-googlemaps3-par-typestyle": "Ang estilo ng pantaban ng uri.", "maps-googlemaps3-par-autoinfowindows": "Kusang buksan ang lahat ng mga dungawan ng kabatiran pagkaraang maikarga ang pahina.", - "maps-googlemaps3-par-kml": "Mga talaksan ng Wikang Pangmarka ng Butas ng Susian (Keyhole Markup Language, KML) na ikakarga sa ibabaw ng mapa.", "maps-googlemaps3-par-gkml": "Mga talaksan ng Wikang Pangmarka ng Butas ng Susian (Keyhole Markup Language, KML) na pinasisinayahan ng Google upang ikarga sa ibabaw ng mapa.", "maps-googlemaps3-par-fusiontables": "Mga ID ng mga Talahanayan ng Pagtutumbaga ng Google na dapat ikarga sa ibabaw ng mapa.", "maps-googlemaps3-par-tilt": "Pagkiling para sa Mapa kapag ginagamit ang Mga Mapa ng Google.", @@ -117,8 +116,6 @@ "maps-googlemaps3-par-poi": "Ipakita ang mga tuldok na mapagtutuunan ng pansin.", "maps-openlayers-par-controls": "Ang mga pantaban na ilalagay sa ibabaw ng mapa.", "maps-openlayers-par-layers": "Ang mga patong ay magiging makukuha sa loob ng pilian ng patong. Ang unang patong ay ipapakita kapag kumarga na ang mapa.", - "maps-osm-par-thumbs": "Ipakita ang mga kagyat", - "maps-osm-par-photos": "Ipakita ang mga larawan", "mapeditor": "Patnugot ng mapa", "specialpages-group-maps": "Mga mapa", "mapeditor-parser-error": "Naganap ang isang kamalian noong binabanghay ang metadato. Hindi papansinin ang ipinasok ng tagagamit.", @@ -135,10 +132,10 @@ "mapeditor-import-note": "Paki tandaan na ang pambanghay ay umaasa ng isang napaka higpit na kaanyuan sa kodigo ng wiki. Ang ipinasok na kodigo rito ay dapat na tumugma sa kodigong inilibas ng panunungkulan ng pagluluwas.", "mapeditor-form-title": "Baguhin ang mga detalye", "mapeditor-link-title-switcher-popup-text": "Pagsulpot na mayroong teksto", - "mapeditor-link-title-switcher-link-text": "Kawing", + "mapeditor-link-title-switcher-link-text": "Link", "mapeditor-form-field-title": "Pamagat", "mapeditor-form-field-text": "Teksto", - "mapeditor-form-field-link": "Kawing", + "mapeditor-form-field-link": "Link", "mapeditor-form-field-icon": "Kinatawang larawan", "mapeditor-form-field-group": "Pangkat", "mapeditor-form-field-inlinelabel": "Katatakang nasa guhit", @@ -153,5 +150,23 @@ "mapeditor-imageoverlay-button": "Idagdag ang kalupkop ng larawan", "mapeditor-form-field-image": "Larawan", "mapeditor-imageoverlay-title": "Mga detalye ng kalupkop ng larawan", - "mapeditor-form-field-visitedicon": "Kinatawang larawan ng pagka nadalaw" + "mapeditor-form-field-visitedicon": "Kinatawang larawan ng pagka nadalaw", + "semanticmaps-unrecognizeddistance": "Hindi isang tanggap na layo ang halagang $1.", + "semanticmaps-kml-link": "Tingnan ang talaksang KML", + "semanticmaps-kml": "KML", + "semanticmaps-default-kml-pagelink": "Tingnan ang pahinang $1", + "semanticmaps-latitude": "Latitud: $1", + "semanticmaps-longitude": "Longhitud: $1", + "semanticmaps-altitude": "Altitud: $1", + "semanticmaps-forminput-locations": "Mga kinalalagyan", + "semanticmaps-par-staticlocations": "Isang listahan ng mga lokasyon na idaragdag sa mapa na kasama ng inusisang dato. Katulad ng sa tuldok_ng_pagpapakita, makapagdaragdag ka ng isang pamagat, paglalarawan at kinatawang larawan sa bawat lokasyon na ginagamit ang tilde \"~\" bilang panghiwalay.", + "semanticmaps-par-showtitle": "Magpapakita o hindi magpapakita ng isang pamagat sa loob ng pangmarkang bintana ng impormasyon. Ang hindi pagpapagana nito ay kadasalang mas kapakipakinabang kapag gumagamit ng isang suleras upang maiayos ang nilalaman ng bintana ng kabatiran.", + "semanticmaps-par-hidenamespace": "Ipakita o huwag ipakita ang pamagat ng puwang na pampangalan sa loob ng pangmarkang bintana ng impormasyon.", + "semanticmaps-par-centre": "Ang gitna ng mapa. Kapag hindi ibinigay, kusang pipiliin ng mapa ang pinakamabuting gitna na pagpapakitaan ng lahat ng mga pangmarka", + "semanticmaps-par-template": "Isang suleras na gagamit upang iayos ang mga nilalaman ng bintana ng kabatiran.", + "semanticmaps-par-geocodecontrol": "Ipakita ang pantaban ng pagkokodigong pangheograpiya.", + "semanticmaps-kml-text": "Umuugnay ang teksto sa bawat isang pahina. Pinangingibabawan ng karagdagang sinisiyasat na mga katangiang angkin kung mayroon.", + "semanticmaps-kml-title": "Ang likas na nakatakdang pamagat para sa mga resulta", + "semanticmaps-kml-linkabsolute": "Kung ang mga kawing ay magiging lubos o hindi (iyong nauukol)", + "semanticmaps-kml-pagelinktext": "Ang tekstong gagamitin para sa mga kawing sa pahina, kung saan ang $1 ay mapapalitan ng pamagat ng pahina" } diff --git a/i18n/tr.json b/i18n/tr.json index 8ff6514c0..b18d3b8d1 100644 --- a/i18n/tr.json +++ b/i18n/tr.json @@ -8,7 +8,8 @@ "Trncmvsr", "Vito Genovese", "McAang", - "Hbseren" + "Hbseren", + "Sadrettin" ] }, "right-geocode": "Coğrafi kod", @@ -43,14 +44,19 @@ "maps_unrecognized_coords": "Şu {{PLURAL:$2|koordinat|koordinatlar}} tanınamadı: $1", "maps_map_cannot_be_displayed": "Harita görüntülenemiyor.", "maps-geocoder-not-available": "Haritanın coğrafi kodlama özelliği etkin değil. Konumunuz kodlanamıyor.", - "maps-leaflet-par-zoom": "Harita yakınlaştırma düzeyini ayarlamaya olanak sağlar.", "maps-leaflet-par-defzoom": "Haritanın varsayılan yakınlaştırma düzeyini ayarlamaya olanak sağlar.", - "maps-leaflet-par-resizable": "Haritanın sağ alt köşesinden sürüklenerek yeniden boyutlandırılabilir hale getirilmesine olanak sağlar.", "maps_click_to_activate": "Haritayı etkinleştirmek için tıkla", "maps_centred_on": "Harita, $1 $2 koordinatlarında ortalandı.", "mapeditor": "Harita düzenleyici", + "specialpages-group-maps": "Haritalar", + "mapeditor-parser-error": "Metaveri ayrıştırılırken bir hata oluştu. Kullanıcı girişi yok sayılıyor.", + "mapeditor-none-text": "Hiçbiri", + "mapeditor-done-button": "Yapıldı", + "mapeditor-remove-button": "Kaldır", + "mapeditor-import-button2": "İçe aktar", "mapeditor-export-button": "Viki kodu dışa aktar", "mapeditor-import-button": "Viki kodunu içe aktar", + "mapeditor-select-button": "Bu çokgeni seçin", "mapeditor-mapparam-button": "Harita parametrelerini düzenle", "mapeditor-clear-button": "Haritayı temizle", "mapeditor-code-title": "Viki kodu", @@ -58,6 +64,19 @@ "mapeditor-form-title": "Ayrıntıları düzenle", "mapeditor-form-field-title": "Başlık", "mapeditor-form-field-text": "Metin", + "mapeditor-form-field-link": "Bağlantı", + "mapeditor-form-field-icon": "Simge", + "mapeditor-form-field-group": "Grup", + "mapeditor-form-field-inlinelabel": "Satır içi etiket", + "mapeditor-form-field-strokecolor": "Çizgi rengi", + "mapeditor-form-field-strokeopacity": "Çizgi saydamlığı", + "mapeditor-form-field-strokeweight": "Çizgi kalınlığı", + "mapeditor-form-field-fillcolor": "Dolgu rengi", + "mapeditor-form-field-fillopcaity": "Dolgu saydamlığı", + "mapeditor-form-field-showonhover": "Yalnızca vurgulu olanı göster", "mapeditor-mapparam-title": "Harita parametrelerini düzenle", - "mapeditor-mapparam-defoption": "-Parametre seçiniz-" + "mapeditor-mapparam-defoption": "-Parametre seçiniz-", + "mapeditor-imageoverlay-button": "Resim katmanı ekle", + "mapeditor-form-field-image": "Resim", + "mapeditor-imageoverlay-title": "Resim katmanı ayrıntıları" } diff --git a/i18n/uk.json b/i18n/uk.json index 4e381e583..67b8ace5e 100644 --- a/i18n/uk.json +++ b/i18n/uk.json @@ -7,7 +7,9 @@ "Ата", "Тест", "Olion", - "Piramidion" + "Piramidion", + "Alex Khimich", + "SteveR" ] }, "maps-desc": "Дає змогу вбудовування динамічних карт у сторінки вікі, геокодування адрес та інші географічні операції", @@ -44,7 +46,6 @@ "maps-layer-type-supported-by": "Цей тип шару може бути використано {{PLURAL:$2|1=лише із картографічним сервісом|лише із наступними картографічними сервісами:}} $1", "maps-coordinates-description": "Перехопиник парсеру для форматування координат із будь-якого у будьякий формат, що підтримується.", "maps-displaymap-description": "Відображення географічних карт без жодних вікі-маркерів на них.", - "maps-displaypoint-description": "Показ географічних карт із одією або декількома вікі-позначками на них.", "maps-distance-description": "Перетворення відстані, вираженої в одних одиницях, у їх еквівалент у інших одиницях.", "maps-finddestination-description": "Знаходить місце призначення від заданої початкової точки (може бути у будь якому із підтримуваних форматів), початкового нарямку і відстані.", "maps-geocode-description": "Дозволяє геокодування адрес, іншими словами перетворює зрозумілі для людини розташування у набори координат. Підтримується декілька сервісів геокодування, які не варто плутати з сервісами картографування.", @@ -98,7 +99,6 @@ "maps-displaymap-par-wmsoverlay": "Використовувати накладення WMS", "maps-fullscreen-button": "Перемкнути повноекранний режим", "maps-fullscreen-button-tooltip": "Переглянути мапу на весь екран або як вбудовану.", - "maps-googlemaps3-par-enable-fullscreen": "Увімкнути кнопку повноекранного режиму", "validation-error-invalid-location": "Параметр $1 повинен бути коректним місцем розташування.", "validation-error-invalid-locations": "Параметр $1 повинен бути одним або більше коректних місць розташування.", "validation-error-invalid-width": "Параметр $1 повинен бути коректною шириною.", @@ -124,7 +124,12 @@ "maps_map_cannot_be_displayed": "Мапа не може бути відображена.", "maps-geocoder-not-available": "Функція геокодування мап недоступна. Ваше місце розташування не може бути геокодоване.", "maps_leaflet": "Leaflet", - "maps_click_to_activate": "Натисність щоб активувати мапу", + "maps-leaflet-par-defzoom": "Дозволяє задавати масштаб карти за замовчуванням.", + "maps-leaflet-par-layer": "Шар, що буде показано, коли мапа завантажиться.", + "maps-leaflet-par-overlaylayers": "Накладення шарів, що будуть показані, коли завантажиться мапа.", + "maps-leaflet-par-maxclusterradius": "Максимальний радіус, що буде покритий кластером від центрального маркера (в пікселях).", + "maps-leaflet-par-clusterspiderfy": "При клацанні на кластер при нижньому рівні масштабу ми розгортаємо його так, щоб Ви могли побачити всі його маркери.", + "maps_click_to_activate": "Натисність, щоб активувати мапу", "maps_centred_on": "Центр мапи — $1, $2.", "maps-par-mappingservice": "Дозволяє налаштувати картографічний сервіс, який буде використовуватися для генерації карти.", "maps-par-resizable": "Робить карту змінного розміру, перетяганням в правому нижньому куті.", @@ -134,9 +139,11 @@ "maps-par-width": "Дозволяє задати ширину карти. За замовчуванням пікселі прийняті як одиниці, але ви можете вибрати одну з цих одиниць: px, ex, em, %.", "maps-par-height": "Дозволяє задати висоту карти. За замовчуванням пікселі прийняті як одиниці, але ви можете вибрати одну з цих одиниць: px, ex, em, %.", "maps-par-centre": "Місцевість відносно якої карта має центруватися", + "maps-par-enable-fullscreen": "Увімкнути кнопку повноекранного режиму", + "maps-par-kml": "Файли KML для завантаження на карту.", + "maps-par-markercluster": "Дозволяє об'єднувати декілька прилеглих маркерів в один маркер", "maps-googlemaps3-incompatbrowser": "Веб-переглядач несумісний із Картами Google версії 3.", "maps-googlemaps3-par-imageoverlays": "Дозволяє додати зображення, яке буде показано в зазначеному місці на карті.", - "maps-googlemaps3-par-markercluster": "Дозволяє об'єднувати декілька прилеглих маркерів в один маркер", "maps-googlemaps3-par-type": "Тим карти, який буде показуватись спочатку.", "maps-googlemaps3-par-types": "Типи карти, які будуть доступні через керування типом.", "maps-googlemaps3-par-layers": "Спеціальні шари для завантаження у карту.", @@ -144,17 +151,20 @@ "maps-googlemaps3-par-zoomstyle": "Стиль елементу керування масштабом.", "maps-googlemaps3-par-typestyle": "Стиль елементу керування типом.", "maps-googlemaps3-par-autoinfowindows": "Автоматично відкрити всі інформаційні вікна, після завантаження сторінки.", - "maps-googlemaps3-par-kml": "Файли KML для завантаження на карту.", "maps-googlemaps3-par-gkml": "Файли KML, розташовані на серверах Google для завантаження на карту.", "maps-googlemaps3-par-fusiontables": "Ідентифікатори таблиць Google Fusion, які повинні бути завантажені на карту.", "maps-googlemaps3-par-tilt": "Нахил карти при використанні Карт Google.", "maps-googlemaps3-par-kmlrezoom": "Масштабування карти після завантаження KML шарів.", "maps-googlemaps3-par-poi": "Показати визначні пам'ятки.", + "maps-googlemaps3-par-clustergridsize": "Розмір сітки кластера в пікселях.", + "maps-par-clustermaxzoom": "Максимальний рівень наближення, де кластери можуть існувати.", + "maps-par-clusterzoomonclick": "Чи є збільшення до кластеру поведінкою за замовчуванням при натисканні на нього.", + "maps-par-maxclusterradius": "Максимальний радіус, який буде покритий кластером.", + "maps-googlemaps3-par-clusteraveragecenter": "Чи повинен центр кожного кластеру бути середнім усіх міток у ньому.", + "maps-googlemaps3-par-clusterminsize": "Мінімальне число міток у кластері, починаючи з якого мітки сховані, а показано їх число.", "maps-openlayers-par-controls": "Елементи керування, які буде розміщено на карті.", "maps-openlayers-par-layers": "Шари, які будуть доступні в селекторі шару. Перший шар буде показано під час завантаження карти.", "maps-openlayers-par-overlays": "Накладання шарів, які будуть доступні у селекторі шару. Ці шари відображаються поверх звичайного шару на кшталт маркеру.", - "maps-osm-par-thumbs": "Показувати мініатюри", - "maps-osm-par-photos": "Показувати фото", "mapeditor": "Редактор карт", "specialpages-group-maps": "Карти", "mapeditor-parser-error": "Сталася помилка при обробці метаданих. Ігноруються введені дані.", @@ -190,5 +200,42 @@ "mapeditor-imageoverlay-button": "Додати накладення зображення", "mapeditor-form-field-image": "Зображення", "mapeditor-imageoverlay-title": "Деталі накладання зображення", - "mapeditor-form-field-visitedicon": "Іконка для відвіданого" + "mapeditor-form-field-visitedicon": "Іконка для відвіданого", + "semanticmaps-unrecognizeddistance": "Значення $1 є недопустимою відстанню.", + "semanticmaps-kml-link": "Переглянути KML-файл", + "semanticmaps-default-kml-pagelink": "Переглянути сторінку $1", + "semanticmaps-latitude": "Широта: $1", + "semanticmaps-longitude": "Довгота: $1", + "semanticmaps-altitude": "Висота над рівнем моря: $1", + "semanticmaps-forminput-locations": "Місця", + "semanticmaps-par-staticlocations": "Перелік місць для додавання на карту разом із запитуваними даними. Наприклад, до display_points можна додати назву, опис та піктограму, використовуючи тильду „~“ в якості роздільника.", + "semanticmaps-par-showtitle": "Показує чи не показує заголовок в інформаційному вікні маркера. Вимикання цього корисне при використанні шаблону для форматування інформаційного вмісту вікна.", + "semanticmaps-par-hidenamespace": "Показувати заголовок простору назв у інформаційному вікні маркера", + "semanticmaps-par-centre": "Центр карти. Якщо не задано, то карта автоматично вибере оптимальний центр, який дозволяє відобразити всі маркери на карті.", + "semanticmaps-par-template": "Шаблон для форматування вмісту інформаційного вікна.", + "semanticmaps-par-geocodecontrol": "Показати управління геокодуваням.", + "semanticmaps-par-activeicon": "Піктограма, яка буде відображатися замість типового маркера, коли активна сторінка рівна результату запиту", + "semanticmaps-par-pagelabel": "Коли задано \"так\", то усі маркери матимуть \"inlineLabel\" з посиланням на сторінку, яка містить координати для маркера", + "semanticmaps-par-ajaxcoordproperty": "Назва властивості координат, яка використовується для побудови ajax-запиту.", + "semanticmaps-par-ajaxquery": "Другий запит, що надсилається через ajax для отримання додаткових координат.", + "semanticmaps-par-userparam": "Значення, що передається в кожен виклик шаблону, якщо шаблон використовується", + "semanticmaps-kml-text": "Текст, пов'язаний з кожною сторінкою. Нехтується запитаними додатковими властивостями, якщо такі є.", + "semanticmaps-kml-title": "Типовий заголовок для результатів", + "semanticmaps-kml-linkabsolute": "Посилання мають бути абсолютними (на відміну від відносних)", + "semanticmaps-kml-pagelinktext": "Текст, який використовується для посилань на сторінку, в яких $1 буде замінено на назву сторінки", + "semanticmaps-shapes-improperformat": "Неправильне форматування з $1. Будь ласка, дивіться документацію для форматування", + "semanticmaps-shapes-missingshape": "Немає форм для $1, див. документацію за доступними формами", + "validator-type-mapscircle": "Географічне коло", + "validator-type-mapscircle-list": "Список кіл", + "validator-type-mapsimageoverlay": "Накладення зображення", + "validator-type-mapsimageoverlay-list": "Список накладень зображень", + "validator-type-mapsline": "Географічна лінія", + "validator-type-mapsline-list": "Список ліній", + "validator-type-mapslocation": "Географічна місцевість", + "validator-type-mapslocation-list": "Список місцевостей", + "validator-type-mapsrectangle": "Географічний прямокутник", + "validator-type-mapsrectangle-list": "Список прямокутників", + "validator-type-mapspolygon": "Географічний багатокутник", + "validator-type-mapspolygon-list": "Список географічних багатокутників", + "validator-type-wmsoverlay": "Накладення Web Map Service" } diff --git a/i18n/vi.json b/i18n/vi.json index d7b130ca1..36cb04a6c 100644 --- a/i18n/vi.json +++ b/i18n/vi.json @@ -64,13 +64,10 @@ "maps_unrecognized_coords_for": "Không thể nhận ra {{PLURAL:$2|tọa độ|các tọa độ}} sau nên bản đồ bỏ qua nó:\n$1", "maps_map_cannot_be_displayed": "Không thể hiển thị bản đồ.", "maps-geocoder-not-available": "Không thể mã hóa vị trí của bạn vì tính năng mã hóa địa lý của Bản đồ không có sẵn.", - "maps_osm": "OpenStreetMap", "maps_click_to_activate": "Nhấn chuột vào bản đồ để kích hoạt", "maps_centred_on": "Bản đồ với trung tậm tại $1, $2.", "maps-openlayers-par-controls": "Các điều khiển đặt trên bản đồ.", "maps-openlayers-par-layers": "Các lớp sẽ có sẵn trong hộp chọn lớp. Lớp đầu tiên là lớp mặc định.", - "maps-osm-par-thumbs": "Hiện hình thu nhỏ", - "maps-osm-par-photos": "Hiện hình ảnh", "mapeditor": "Sửa đổi bản đồ", "specialpages-group-maps": "Bản đồ", "mapeditor-none-text": "Không có", @@ -98,5 +95,14 @@ "mapeditor-mapparam-defoption": "—Chọn tham số—", "mapeditor-imageoverlay-button": "Thêm lớp phủ hình ảnh", "mapeditor-form-field-image": "Hình ảnh", - "mapeditor-imageoverlay-title": "Chi tiết lớp phủ hình ảnh" + "mapeditor-imageoverlay-title": "Chi tiết lớp phủ hình ảnh", + "semanticmaps-unrecognizeddistance": "Giá trị $1 không phải là tầm hợp lệ.", + "semanticmaps-kml-link": "Xem tập tin KML", + "semanticmaps-default-kml-pagelink": "Xem trang $1", + "semanticmaps-forminput-locations": "Các vị trí", + "semanticmaps-par-staticlocations": "Danh sách các vị trí để thêm vào bản đồ cùng với dữ liệu được truy vấn. Giống như với display_points, bạn có thể đặt tên, miêu tả, và hình tượng cho mỗi đánh dấu bằng cách phân tách dùng dấu ngã (~).", + "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ý." } diff --git a/i18n/zh-hans.json b/i18n/zh-hans.json index 4ea0c01aa..5d3e967b8 100644 --- a/i18n/zh-hans.json +++ b/i18n/zh-hans.json @@ -8,21 +8,23 @@ "PhiLiP", "Qiyue2001", "Xiaomingyan", - "Yfdyh000" + "Yfdyh000", + "Byfserag", + "Linforest" ] }, - "maps-desc": "允许嵌入动态地图到wiki页面、地址的地理编码等地理学操作", + "maps-desc": "允许嵌入动态地图到wiki页面,将地址转换为地理编码,及进行其他地理学操作", "right-geocode": "地理编码", "action-geocode": "在此wiki进行地理编码", "maps_map": "地图", "maps-tracking-category": "有由地图扩展描绘地图的页面", - "maps-loading-map": "载入地图中……", + "maps-loading-map": "正在载入地图...", "maps-load-failed": "无法加载地图!", "maps-markers": "标记", "maps-copycoords-prompt": "CTRL+C, ENTER", "maps-searchmarkers-text": "筛选标记", "maps-others": "其他", - "maps-kml-parsing-failed": "解析一个或更多KML文件失败。这通常是因为检索失败或格式不正确的XML。", + "maps-kml-parsing-failed": "解析一个或多个KML文件失败。这通常是因为检索失败或格式不正确的XML。", "maps-ns-layer": "图层", "maps-ns-layer-talk": "图层讨论", "maps-layer-property": "属性", @@ -31,7 +33,7 @@ "maps-layerdef-invalid": "$1个无效定义", "maps-layerdef-invalid-fatal": "致命的无效定义", "maps-layerdef-wrong-namespace": "图层定义仅在“$1”名字空间的页面有效", - "maps-layerdef-equal-layer-name": "图层名称必须在相同图层页面中是唯一的。“$1”已被另一图层使用。", + "maps-layerdef-equal-layer-name": "图层名称在相同图层页面中必须唯一。“$1”已被另一图层使用。", "maps-layerpage-usage": "带有使用图层“$1”的地图页面", "maps-layerpage-nousage": "目前没有页面使用此图层。", "maps-error-invalid-layertype": "没有类型“$1”的图层。只支持{{PLURAL:$3|此|这些}}类型:$2", @@ -39,29 +41,53 @@ "validation-error-invalid-layer": "参数“$1”必须是一个有效图层。", "validation-error-invalid-layers": "参数“$1”必须是一个或多个有效图层。", "validation-error-no-non-numeric": "参数“$1”必须是一个非数值字符串。", - "validation-error-no-non-numerics": "参数“$1”必须是一个或更多非数值字符串。", + "validation-error-no-non-numerics": "参数“$1”必须是一个或多个非数值字符串。", "maps-layer-of-type": "图层类型“$1”", "maps-layer-of-type-and-name": "类型“$1”的图层“$2”", - "maps-layer-type-supported-by": "此图层类型只能与{{PLURAL:$2|此|这些}}映射服务使用:$1。", + "maps-layer-type-supported-by": "此图层类型{{PLURAL:$2|只能与$1映射服务一起使用|可与这些映射服务一起使用:$1}}。", "maps-coordinates-description": "要格式化坐标的解析器钩,来自并转换至任何支持的格式。", + "maps-displaymap-description": "显示地理地图,而不需要在上面显示任何wiki定义的标记。", + "maps-distance-description": "将使用某个受支持单位的距离转换为其使用其他单位的等值。", + "maps-finddestination-description": "提供起点(可以是任意支持的格式)、起始方位角和一段距离来查找目的地。", + "maps-geocode-description": "启用地址的地理编码,换句话说,将人类可读的位置变成一组坐标。这支持几种地理编码服务,而这不应与映射服务相混淆。", + "maps-geodistance-description": "计算两点之间的地理距离,可使用任何支持的格式作为起点和终点。", + "maps-mapsdoc-description": "显示带参数的表格,用以显示指定的映射服务与其默认值和描述。", + "maps-layerdefinition-description": "描述一个可以通过其他地图功能显示的自定义图册。", + "maps-mapsdoc-par-service": "显示参数文档的映射服务。", "maps-mapsdoc-par-language": "显示文档的语言。如果没有翻译可用,将使用英语。", "maps-coordinates-par-location": "您希望格式化的坐标。", "maps-coordinates-par-format": "目标坐标的格式。", + "maps-coordinates-par-directional": "指示坐标是否应输出方向。", + "maps-distance-par-distance": "要转换至其使用指定单位的等值的距离。", + "maps-distance-par-decimals": "结果值中使用的小数位数最大数。", "maps-distance-par-unit": "输出的距离单位。", "maps-finddestination-par-location": "初始位置。", "maps-finddestination-par-bearing": "最初的影响。", "maps-finddestination-par-distance": "旅行的距离。", "maps-finddestination-par-format": "输出目的地的格式。", - "maps-geocode-par-location": "您希望发送到的地址。", + "maps-finddestination-par-directional": "指示目的地格式是否应有方向性。", + "maps-finddestination-par-allowcoordinates": "指示是否允许坐标。如设置为否,将只接受地址。", + "maps-finddestination-par-geoservice": "当值是一个地址时,要对其进行编码的地理编码服务。", + "maps-finddestination-par-mappingservice": "表明使用此函数的映射服务的参数。这将允许地图使用映射服务最佳参数,覆盖服务参数的默认值。(例如:当使用Google地图时,Google地理编码将被使用。)", + "maps-geocode-par-location": "您希望进行地理编码的地址。", + "maps-geocode-par-mappingservice": "您要使用的地理编码服务。参见可用地理编码服务。", + "maps-geocode-par-geoservice": "此参数允许您表明您在将此地理编码请求与特定映射服务结合使用。每个映射服务可以覆盖地理编码服务的默认值。这是出于法律上的考虑,因为除了与Google地图一起使用的情况外,您不能使用Google地理编码服务。设置此参数而不是用于服务的参数,将确保您不会使用无效的组合。", "maps-geocode-par-allowcoordinates": "允许禁用此功能的坐标支持。值必须为“yes”或“no”。如果为no,所有值将被编码,即便坐标有效。", "maps-geocode-par-format": "生成坐标的格式。", + "maps-geocode-par-directional": "指示坐标是否应输出方向。", "maps-geodistance-par-location1": "要计算间距的集中第一个点。", "maps-geodistance-par-location2": "要计算间距的集中第二个点。", "maps-geodistance-par-unit": "要输出的距离单位。", + "maps-geodistance-par-decimals": "结果值中使用的小数位数最大数。", "maps-geodistance-par-mappingservice": "要用于对任意地址编码的地址编码服务。", "maps-geodistance-par-geoservice": "映射服务与此解析器函数一起工作。这会影响默认的地理编码服务值。", "maps-displaymap-par-mappingservice": "允许设置将被用于生成地图的映射服务。", "maps-displaymap-par-coordinates": "要在地图上显示的一个或多个位置。它们将以标记显示。", + "maps-displaymap-par-visitedicon": "原始标记被点击后,要被用作标记图标的图片文件名", + "maps-displaymap-par-zoom": "允许设置地图的缩放级别。当未提供并且地图上存在多个标记时,将使用最合适缩放级别而非默认配置。", + "maps-displaymap-par-centre": "允许设置地图中心点的坐标以用于display_point。同时接受地址和坐标。当此属性未提供时,地图将集中于提供的标记上,或提供的标记之间。", + "maps-displaymap-par-title": "允许设置将在所有没有具体标题的标记弹窗中显示的文本。当与标签一起使用时,标题将被加粗,并加下划线。", + "maps-displaymap-par-label": "允许设置将在所有标记中的弹出窗口显示的文本,只要它们不包含特定标签的话。", "maps-displaymap-par-icon": "允许设置图标用于所有标记。", "maps-displaymap-par-circles": "圈起以显示", "maps-displaymap-par-copycoords": "当点击可以复制坐标的位置时显示对话框", @@ -74,7 +100,6 @@ "maps-displaymap-par-wmsoverlay": "使用一个WMS覆盖", "maps-fullscreen-button": "切换全屏显示", "maps-fullscreen-button-tooltip": "全屏查看地图或嵌入。", - "maps-googlemaps3-par-enable-fullscreen": "启用全屏按钮", "validation-error-invalid-location": "参数 $1 必须是一个有效的位置。", "validation-error-invalid-locations": "参数 $1 必须有一个或多个有效的位置。", "validation-error-invalid-width": "参数 $1 必须是一个有效的宽度。", @@ -100,18 +125,26 @@ "maps_map_cannot_be_displayed": "该地图无法显示。", "maps-geocoder-not-available": "地图的地理编码功能不可用。您所在地区不能被编码。", "maps_leaflet": "Leaflet", - "maps-leaflet-par-zoom": "允许设置地图的缩放级别。", "maps-leaflet-par-defzoom": "允许设置地图的默认缩放级别。", - "maps-leaflet-par-resizable": "允许地图通过拖放右下角改变大小。", + "maps-leaflet-par-layer": "在地图加载时将显示的图层。", + "maps-leaflet-par-overlaylayers": "在地图加载时将显示的覆盖图层。", + "maps-leaflet-par-maxclusterradius": "群集将从中心标记处遮住的最大半径(像素)。", + "maps-leaflet-par-clusterspiderfy": "当您在下方缩放级别点击群集时,我们使其蜘蛛化,这样您可以看见它所有的标记。", "maps_click_to_activate": "点击激活地图", "maps_centred_on": "地图居中在$1,$2。", + "maps-par-mappingservice": "允许设置将被用于生成地图的映射服务。", + "maps-par-resizable": "通过拖放右下角使地图大小可变。", "maps-par-searchmarkers": "允许通过嵌入在地图中的字段搜索特定标记。", + "maps-par-geoservice": "要用于地址和坐标间翻译的地理编码服务。", + "maps-par-zoom": "地图的缩放级别。对于带标记的地图,这将默认放大到仍可显示所有标记的最大级别。", "maps-par-width": "允许设置地图宽度。默认情况下像素将被假定为单位,但您可以明确指定这些单位之一:px、ex、em、%。", "maps-par-height": "允许设置地图高度。默认情况下像素将被假定为单位,但您可以明确指定这些单位之一:px、ex、em、%。", "maps-par-centre": "地图上的位置应该居中", + "maps-par-enable-fullscreen": "启用全屏按钮", + "maps-par-kml": "加载地图上的KML文件。", + "maps-par-markercluster": "允许将多个附近标记合并为一个标记", "maps-googlemaps3-incompatbrowser": "您的浏览器不兼容Google Maps v3。", "maps-googlemaps3-par-imageoverlays": "允许添加图片以在地图的特定地点显示。", - "maps-googlemaps3-par-markercluster": "允许将多个附近标记合并为一个标记", "maps-googlemaps3-par-type": "首先显示的地图类型。", "maps-googlemaps3-par-types": "将通过类型控制可用的地图类型。", "maps-googlemaps3-par-layers": "要加载在地图上的特殊图层。", @@ -119,15 +152,20 @@ "maps-googlemaps3-par-zoomstyle": "缩放控制的样式。", "maps-googlemaps3-par-typestyle": "类型控制的样式。", "maps-googlemaps3-par-autoinfowindows": "在页面加载后,自动打开所有信息窗口。", - "maps-googlemaps3-par-kml": "加载地图上的KML文件。", "maps-googlemaps3-par-gkml": "由Google运营的加载地图上的KML文件。", "maps-googlemaps3-par-fusiontables": "应加载至地图的Google融合表的ID。", "maps-googlemaps3-par-tilt": "使用Google地图时的地图标题。", "maps-googlemaps3-par-kmlrezoom": "在KML图层加载后重新缩放地图。", "maps-googlemaps3-par-poi": "显示感兴趣的点。", + "maps-googlemaps3-par-clustergridsize": "群集的网格大小(像素)。", + "maps-par-clustermaxzoom": "可能存在群集的最大缩放级别。", + "maps-par-clusterzoomonclick": "在群集上默认的点击行为是否为放大。", + "maps-par-maxclusterradius": "群集将覆盖的半径最大值。", + "maps-googlemaps3-par-clusteraveragecenter": "每个集群的中心是否应为集群中所有标记的平均值。", + "maps-googlemaps3-par-clusterminsize": "在标记被隐藏并且显示计数时,集群中标记的最小值。", "maps-openlayers-par-controls": "要放置地图上的控件。", - "maps-osm-par-thumbs": "显示大拇指", - "maps-osm-par-photos": "显示图片", + "maps-openlayers-par-layers": "将在图层选择器中可用的图层。第一个图层将在地图加载时显示。", + "maps-openlayers-par-overlays": "将在图层选择器中可用的覆盖图层。这些图层将在一般图层上方显示,就像一个标记。", "mapeditor": "地图编辑器", "specialpages-group-maps": "地图", "mapeditor-parser-error": "解析元数据时出错。忽略用户输入。", @@ -142,6 +180,7 @@ "mapeditor-clear-button": "明确地图", "mapeditor-code-title": "维基代码", "mapeditor-import-title": "导入维基代码", + "mapeditor-import-note": "请注意解析器期望在wiki代码上应用非常严格的格式。这里输入的代码应匹配由导出功能输出的代码。", "mapeditor-form-title": "编辑详情", "mapeditor-link-title-switcher-popup-text": "带文本的弹出菜单", "mapeditor-link-title-switcher-link-text": "链接", @@ -162,5 +201,42 @@ "mapeditor-imageoverlay-button": "加入图像覆盖", "mapeditor-form-field-image": "图片", "mapeditor-imageoverlay-title": "图像覆盖详细信息", - "mapeditor-form-field-visitedicon": "已浏览图标" + "mapeditor-form-field-visitedicon": "已浏览图标", + "semanticmaps-unrecognizeddistance": "取值$1不是有效的距离。", + "semanticmaps-kml-link": "查看KML文件", + "semanticmaps-default-kml-pagelink": "检视页面 $1", + "semanticmaps-latitude": "纬度:$1", + "semanticmaps-longitude": "经度:$1", + "semanticmaps-altitude": "绝对海拔高度:$1", + "semanticmaps-forminput-locations": "位置", + "semanticmaps-par-staticlocations": "要添加至地图的位置及查询的数据的列表。如同display_points,你可以给每个位置添加标题、说明,用浪纹分隔“~”。", + "semanticmaps-par-showtitle": "是否在标记信息窗口之中显示标题。当采用模板对信息窗口内容进行格式编排的时候,关闭此项往往会有所帮助。", + "semanticmaps-par-hidenamespace": "在标记信息窗口中显示命名空间标题", + "semanticmaps-par-centre": "地图的中心。当未加提供的时候,地图会自动挑选最佳的中心,从而在地图上显示所有的标记。", + "semanticmaps-par-template": "用来对信息窗口内容进行格式编排的模板。", + "semanticmaps-par-geocodecontrol": "显示地理编码控件。", + "semanticmaps-par-activeicon": "当活跃页面等于查询结果时将显示选定标记而不是默认标记", + "semanticmaps-par-pagelabel": "当您设置为“yes”时,所有标记将拥有一个“inlineLabel”带有一个链接至包含坐标标记的页面", + "semanticmaps-par-ajaxcoordproperty": "用于构造ajax查询的坐标属性名称。", + "semanticmaps-par-ajaxquery": "通过ajax发送以取得额外坐标的第二查询。", + "semanticmaps-par-userparam": "当模板被使用时,传递到每个模板调用的值", + "semanticmaps-kml-text": "与每个页面相关联的文本。会被额外的查询属性(如果有的话)所覆盖。", + "semanticmaps-kml-title": "结果的默认标题", + "semanticmaps-kml-linkabsolute": "链接究竟应当是绝对地址还是相对地址", + "semanticmaps-kml-pagelinktext": "用于那些指向该页面的链接的文本;其中,页面标题将取代$1", + "semanticmaps-shapes-improperformat": "$1 的格式不正确。请参阅文档格式", + "semanticmaps-shapes-missingshape": "找不到用于$1的形状。请参见文档以查看可用形状", + "validator-type-mapscircle": "地理圈", + "validator-type-mapscircle-list": "圈列表", + "validator-type-mapsimageoverlay": "图片覆盖", + "validator-type-mapsimageoverlay-list": "图片覆盖列表", + "validator-type-mapsline": "地理线", + "validator-type-mapsline-list": "线列表", + "validator-type-mapslocation": "地理位置", + "validator-type-mapslocation-list": "位置列表", + "validator-type-mapsrectangle": "地理矩形", + "validator-type-mapsrectangle-list": "矩形列表", + "validator-type-mapspolygon": "地理多边形", + "validator-type-mapspolygon-list": "地理多边形列表", + "validator-type-wmsoverlay": "网络地图服务覆盖" } diff --git a/i18n/zh-hant.json b/i18n/zh-hant.json index 6aeccb8f1..a1185f381 100644 --- a/i18n/zh-hant.json +++ b/i18n/zh-hant.json @@ -10,7 +10,10 @@ "Simon Shek", "Waihorace", "LNDDYL", - "Macofe" + "Macofe", + "1233thehongkonger", + "Liuxinyu970226", + "Oapbtommy" ] }, "maps-desc": "可讓 Wiki 頁面嵌入動態地圖,支援使用地理編碼標記位置,以及地理相關操作", @@ -46,7 +49,6 @@ "maps-layer-type-supported-by": "此類型圖層{{PLURAL:$2|只可使用 $1 地圖服務|可使用以下地圖服務:$1}}。", "maps-coordinates-description": "用來格式化座標的分析器連結(Hook),可在支援的格式之間轉換。", "maps-displaymap-description": "顯示地理地圖,不顯示任何 Wiki 定義的標記於地圖上。", - "maps-displaypoint-description": "顯示地理地圖,顯示所有 Wiki 定義的標記於地圖上。", "maps-distance-description": "可以使用任何支援的單位轉換距離的單位", "maps-finddestination-description": "根據起點 (可使用支援的任何格式)、初始方位與距離找尋目的地。", "maps-geocode-description": "開啟地圖編碼位置,將可閱讀的位置名稱轉換為座標。地圖編碼有許多不同服務可選擇,與地圖服務不同。", @@ -64,7 +66,6 @@ "maps-displaymap-par-wmsoverlay": "使用 WMS 圖層", "maps-fullscreen-button": "切換全螢幕", "maps-fullscreen-button-tooltip": "使用全螢幕或嵌入方式檢視地圖。", - "maps-googlemaps3-par-enable-fullscreen": "開啟全螢幕的按鈕", "validation-error-invalid-location": "參數 \"$1\" 必須是一個有效的位置。", "validation-error-invalid-locations": "參數 \"$1\" 必須有一個或多個有效的位置。", "validation-error-invalid-width": "參數 \"$1\" 必須是一個有效的寬度。", @@ -82,11 +83,12 @@ "maps-latitude": "緯度:", "maps-longitude": "經度:", "maps_map_cannot_be_displayed": "該地圖無法顯示。", + "maps-leaflet-par-layer": "此圖層將於地圖加載時顯示", + "maps-leaflet-par-overlaylayers": "此覆蓋圖層將於地圖加載時顯示。", "maps_click_to_activate": "按一下以啟動地圖", "maps_centred_on": "地圖以 $1, $2 為中心。", + "maps-par-enable-fullscreen": "開啟全螢幕的按鈕", "maps-openlayers-par-overlays": "將會在圖層選擇器中可用的覆蓋層。這些圖層將顯示在一個正常的圖層,有點像一個標記。", - "maps-osm-par-thumbs": "顯示大拇指", - "maps-osm-par-photos": "顯示圖片", "mapeditor": "地圖編輯器", "specialpages-group-maps": "地圖", "mapeditor-parser-error": "分析描述資料錯誤,忽略使用者輸入。", @@ -121,5 +123,27 @@ "mapeditor-imageoverlay-button": "新增圖片圖層", "mapeditor-form-field-image": "圖片", "mapeditor-imageoverlay-title": "圖片圖層詳細資料", - "mapeditor-form-field-visitedicon": "已瀏覽的圖示" + "mapeditor-form-field-visitedicon": "已瀏覽的圖示", + "semanticmaps-unrecognizeddistance": "數值 $1 不是有效的距離。", + "semanticmaps-kml-link": "檢視 KML 檔案", + "semanticmaps-default-kml-pagelink": "檢視頁面 $1", + "semanticmaps-latitude": "緯度:$1", + "semanticmaps-longitude": "經度:$1", + "semanticmaps-altitude": "海拔:$1", + "semanticmaps-forminput-locations": "位置", + "semanticmaps-par-staticlocations": "與查詢的資料一起新增到地圖的位置清單。 如 display_points,您可新增每個位置的標題、描述與圖示使用波浪號 \"~\" 作為分隔符號。", + "semanticmaps-par-showtitle": "是否在標記資訊視窗之中顯示標題。當使用模板對資訊視窗內容進行格式編排的時,關閉此選項通常會有所幫助。", + "semanticmaps-par-hidenamespace": "在標記資訊視窗中顯示命名空間的標題", + "semanticmaps-par-centre": "地圖的中心位置。若不指定,地圖會自動挑選可顯示所有標記於地圖上的最佳中心位置。", + "semanticmaps-par-template": "用來對資訊視窗內容進行格式編排的模板。", + "semanticmaps-par-geocodecontrol": "顯示地理編碼控制元件。", + "semanticmaps-par-activeicon": "當使用的頁面為查詢結果時,用來替代預設標記要顯示的圖示", + "semanticmaps-par-pagelabel": "當設為 \"是\" 時,所有的標記會有 \"inlineLabel\" 並連結至含有標記座標的頁面。", + "semanticmaps-kml-text": "與每個頁面關聯的文字。可被額外的查詢屬性所覆蓋 (若有設定)。", + "semanticmaps-kml-title": "查詢結果的預設標題", + "semanticmaps-kml-linkabsolute": "連結應為絕對位址 (相反於相對位址)", + "semanticmaps-kml-pagelinktext": "連結至頁面要使用的文字,文字中的 $1 會取代為頁面標題", + "semanticmaps-shapes-improperformat": "$1 的格式有誤,請參考格式說明文件", + "semanticmaps-shapes-missingshape": "查無 $1 的形狀。請查看說明文件瞭解可用的形狀", + "validator-type-mapslocation": "地理座標" } diff --git a/includes/Element.php b/includes/Element.php index 2725a6c47..5a4ee5c5d 100644 --- a/includes/Element.php +++ b/includes/Element.php @@ -5,11 +5,10 @@ use MWException; /** - * Interface for elements that can be places upon a map. + * Interface for elements that can be placed upon a map. * * @since 3.0 * - * * @licence GNU GPL v2+ * @author Jeroen De Dauw < jeroendedauw@gmail.com > */ @@ -45,14 +44,9 @@ public function setOptions( ElementOptions $options ); } -class OptionsObject { +class ElementOptions { - /** - * @since 3.0 - * - * @var array - */ - protected $options = array(); + private $options = []; /** * @since 3.0 @@ -60,11 +54,11 @@ class OptionsObject { * @param string $name * @param mixed $value * - * @throws MWException + * @throws \InvalidArgumentException */ public function setOption( $name, $value ) { if ( !is_string( $name ) ) { - throw new MWException( 'Option name should be a string' ); + throw new \InvalidArgumentException( 'Option name should be a string' ); } $this->options[$name] = $value; @@ -75,15 +69,16 @@ public function setOption( $name, $value ) { * * @param string $name * - * @throws MWException + * @throws \InvalidArgumentException + * @throws \OutOfBoundsException */ public function getOption( $name ) { if ( !is_string( $name ) ) { - throw new MWException( 'Option name should be a string' ); + throw new \InvalidArgumentException( 'Option name should be a string' ); } if ( !array_key_exists( $name, $this->options ) ) { - throw new MWException( 'Tried to obtain option "' . $name . '" while it has not been set' ); + throw new \OutOfBoundsException( 'Tried to obtain option "' . $name . '" while it has not been set' ); } return $this->options[$name]; @@ -95,15 +90,14 @@ public function getOption( $name ) { * @param string $name * * @return boolean + * @throws \InvalidArgumentException */ public function hasOption( $name ) { + if ( !is_string( $name ) ) { + throw new \InvalidArgumentException( 'Option name should be a string' ); + } + return array_key_exists( $name, $this->options ); } -} - -class ElementOptions extends OptionsObject { - - - } \ No newline at end of file diff --git a/includes/Geocoder.php b/includes/Geocoder.php index 5b54810e1..4fc14d7e2 100644 --- a/includes/Geocoder.php +++ b/includes/Geocoder.php @@ -3,7 +3,7 @@ namespace Maps; /** - * Base geocoder class to be inherited by classes with a specific geocding implementation. + * Base geocoder class to be inherited by classes with a specific geocoding implementation. * * @since 0.7 * @@ -143,7 +143,7 @@ public function geocode( $address ) { * @return string or false */ protected static function getXmlElementValue( $xml, $tagName ) { - $match = array(); + $match = []; preg_match( "/<$tagName>(.*?)<\/$tagName>/", $xml, $match ); return count( $match ) > 1 ? $match[1] : false; } @@ -159,7 +159,7 @@ protected static function getXmlElementValue( $xml, $tagName ) { * @return array */ public static function getOverrides() { - return array(); + return []; } /** diff --git a/includes/Geocoders.php b/includes/Geocoders.php index 389fdd00a..5145609c2 100644 --- a/includes/Geocoders.php +++ b/includes/Geocoders.php @@ -2,14 +2,14 @@ namespace Maps; -use DataValues\Geo\Formatters\GeoCoordinateFormatter; use DataValues\Geo\Parsers\GeoCoordinateParser; use DataValues\Geo\Values\LatLongValue; +use MapsOldGeocoderAdapter; use MWException; use ValueParsers\ParseException; /** - * Class for geocoder functionality of the Maps extension. + * Class for geocoder functionality of the Maps extension. * * FIXME: this is procedural spaghetti * @@ -22,29 +22,29 @@ final class Geocoders { /** * Associative with geoservice identifiers as keys containing instances of - * the geocoder classes. - * + * the geocoder classes. + * * Note: This list only contains the instances, so is not to be used for - * looping over all available services, as not all of them are guaranteed + * looping over all available services, as not all of them are guaranteed * to have an instance already, use $registeredServices for this purpouse. - * + * * @since 0.7 - * + * * @var Geocoder[] */ - protected static $geocoders = array(); - + protected static $geocoders = []; + /** * Associative with geoservice identifiers as keys containing the class * name of the geocoders. This is used for registration of a geocoder * without immediately instantiating it. - * + * * @since 0.7 - * + * * @var array of string => string */ - public static $registeredGeocoders = array(); - + public static $registeredGeocoders = []; + /** * The global geocoder cache, holding geocoded data when enabled. * @@ -52,181 +52,130 @@ final class Geocoders { * * @var array */ - private static $globalGeocoderCache = array(); - + private static $globalGeocoderCache = []; + /** * Can geocoding happen, ie are there any geocoders available. - * + * * @since 1.0.3 * @var boolean */ protected static $canGeocode = false; - + /** * Returns if this class can do geocoding operations. * Ie. if there are any geocoders available. - * + * * @since 0.7 - * + * * @return boolean */ public static function canGeocode() { self::init(); return self::$canGeocode; } - + /** * Gets a list of available geocoders. - * + * * @since 1.0.3 - * + * * @return array */ public static function getAvailableGeocoders() { self::init(); return array_keys( self::$registeredGeocoders ); } - + /** * Initiate the geocoding functionality. - * + * * @since 1.0.3 - * + * * @return boolean Indicates if init happened */ public static function init() { static $initiated = false; - + if ( $initiated ) { return false; } - + $initiated = true; - + // Register the geocoders. \Hooks::run( 'GeocoderFirstCallInit' ); - + // Determine if there are any geocoders. self::$canGeocode = count( self::$registeredGeocoders ) > 0; - + return true; } - + /** - * This function first determines wether the provided string is a pair or coordinates + * This function first determines whether the provided string is a pair or coordinates * or an address. If it's the later, an attempt to geocode will be made. The function will - * return the coordinates or false, in case a geocoding attempt was made but failed. - * + * return the coordinates or false, in case a geocoding attempt was made but failed. + * * @since 0.7 - * + * * @param string $coordsOrAddress - * @param string $geoservice - * @param string|false $mappingService + * @param string $geoService * @param boolean $checkForCoords * * @return LatLongValue|false */ - public static function attemptToGeocode( $coordsOrAddress, $geoservice = '', $mappingService = false, $checkForCoords = true ) { + public static function attemptToGeocode( $coordsOrAddress, $geoService = '', $checkForCoords = true ) { if ( $checkForCoords ) { - $coordinateParser = new GeoCoordinateParser( new \ValueParsers\ParserOptions() ); + $coordinateParser = new GeoCoordinateParser(); try { return $coordinateParser->parse( $coordsOrAddress ); } catch ( ParseException $parseException ) { - return self::geocode( $coordsOrAddress, $geoservice, $mappingService ); + return self::geocode( $coordsOrAddress, $geoService ); } } else { - return self::geocode( $coordsOrAddress, $geoservice, $mappingService ); + return self::geocode( $coordsOrAddress, $geoService ); } } - - /** - * - * - * @since 0.7 - * - * @param string $coordsOrAddress - * @param string $geoService - * @param string|false $mappingService - * - * @return boolean - */ - public static function isLocation( $coordsOrAddress, $geoService = '', $mappingService = false ) { - return self::attemptToGeocode( $coordsOrAddress, $geoService, $mappingService ) !== false; - } - - /** - * Geocodes an address with the provided geocoding service and returns the result - * as a string with the optionally provided format, or false when the geocoding failed. - * - * @since 0.7 - * - * @param string $coordsOrAddress - * @param string $service - * @param string $mappingService - * @param boolean $checkForCoords - * @param string $targetFormat The notation to which they should be formatted. Defaults to floats. - * @param boolean $directional Indicates if the target notation should be directional. Defaults to false. - * - * @return string|false - */ - public static function attemptToGeocodeToString( $coordsOrAddress, $service = '', $mappingService = false, $checkForCoords = true, $targetFormat = Maps_COORDS_FLOAT, $directional = false ) { - $geoCoordinate = self::attemptToGeocode( $coordsOrAddress, $service, $mappingService, $checkForCoords ); - - if ( $geoCoordinate === false ) { - return false; - } - - $options = new \ValueFormatters\FormatterOptions( array( - GeoCoordinateFormatter::OPT_FORMAT => $targetFormat, - GeoCoordinateFormatter::OPT_DIRECTIONAL => $directional, - GeoCoordinateFormatter::OPT_PRECISION => 1 / 360000 - ) ); - - $formatter = new GeoCoordinateFormatter( $options ); - return $formatter->format( $geoCoordinate ); - } /** - * Geocodes an address with the provided geocoding service and returns the result + * Geocodes an address with the provided geocoding service and returns the result * as an array, or false when the geocoding failed. * - * FIXME: complexity - * * @since 0.7 * * @param string $address * @param string $geoService - * @param string|false $mappingService - * + * * @return LatLongValue|false * @throws MWException */ - public static function geocode( $address, $geoService = '', $mappingService = false ) { + public static function geocode( $address, $geoService = '' ) { if ( !is_string( $address ) ) { throw new MWException( 'Parameter $address must be a string at ' . __METHOD__ ); - } - + } + if ( !self::canGeocode() ) { return false; } - - $geocoder = self::getValidGeocoderInstance( $geoService, $mappingService ); + + $geocoder = self::getValidGeocoderInstance( $geoService ); // This means there was no suitable geocoder found, so return false. if ( $geocoder === false ) { return false; } - + if ( $geocoder->hasGlobalCacheSupport() ) { $cacheResult = self::cacheRead( $address ); - + // This means the cache returned an already computed set of coordinates. if ( $cacheResult !== false ) { assert( $cacheResult instanceof LatLongValue ); return $cacheResult; - } + } } $coordinates = self::getGeocoded( $geocoder, $address ); @@ -265,20 +214,20 @@ private static function getGeocodedAsArray( Geocoder $geocoder, $address ) { return $coordinates; } - + /** * Returns already coordinates already known from previous geocoding operations, * or false if there is no match found in the cache. - * + * * @since 0.7 - * + * * @param string $address - * + * * @return LatLongValue|boolean false */ - protected static function cacheRead( $address ) { + private static function cacheRead( $address ) { global $egMapsEnableGeoCache; - + if ( $egMapsEnableGeoCache && array_key_exists( $address, self::$globalGeocoderCache ) ) { return self::$globalGeocoderCache[$address]; } @@ -286,73 +235,82 @@ protected static function cacheRead( $address ) { return false; } } - + /** * Writes the geocoded result to the cache if the cache is on. - * + * * @since 0.7 - * + * * @param string $address * @param LatLongValue $coordinates */ - protected static function cacheWrite( $address, LatLongValue $coordinates ) { + private static function cacheWrite( $address, LatLongValue $coordinates ) { global $egMapsEnableGeoCache; - + // Add the obtained coordinates to the cache when there is a result and the cache is enabled. if ( $egMapsEnableGeoCache && $coordinates ) { self::$globalGeocoderCache[$address] = $coordinates; } } - + /** * Registers a geocoder linked to an identifier. - * + * * @since 0.7 - * + * * @param string $geocoderIdentifier - * @param string $geocoderClassName + * @param string|\Maps\Geocoders\Geocoder $geocoder */ - public static function registerGeocoder( $geocoderIdentifier, $geocoderClassName ) { - self::$registeredGeocoders[$geocoderIdentifier] = $geocoderClassName; + public static function registerGeocoder( $geocoderIdentifier, $geocoder ) { + self::$registeredGeocoders[$geocoderIdentifier] = $geocoder; } - + /** * Returns the instance of the geocoder linked to the provided identifier * or the default one when it's not valid. False is returned when there * are no geocoders available. - * + * * @since 0.7 - * + * * @param string $geocoderIdentifier - * - * @return \Maps\Geocoder or false + * + * @return Geocoder|bool */ - protected static function getValidGeocoderInstance( $geocoderIdentifier ) { + private static function getValidGeocoderInstance( $geocoderIdentifier ) { return self::getGeocoderInstance( self::getValidGeocoderIdentifier( $geocoderIdentifier ) ); } - + /** * Returns the instance of a geocoder. This function assumes there is a * geocoder linked to the identifier you provide - if you are not sure * it does, use getValidGeocoderInstance instead. - * + * * @since 0.7 - * + * * @param string $geocoderIdentifier - * - * @return \Maps\Geocoder or false + * + * @return Geocoder|bool + * @throws MWException */ - protected static function getGeocoderInstance( $geocoderIdentifier ) { + private static function getGeocoderInstance( $geocoderIdentifier ) { if ( !array_key_exists( $geocoderIdentifier, self::$geocoders ) ) { if ( array_key_exists( $geocoderIdentifier, self::$registeredGeocoders ) ) { - $geocoder = new self::$registeredGeocoders[$geocoderIdentifier]( $geocoderIdentifier ); - - //if ( $service instanceof iMappingService ) { - self::$geocoders[$geocoderIdentifier] = $geocoder; - //} - //else { - // throw new MWException( 'The geocoder linked to identifier ' . $geocoderIdentifier . ' does not implement .' ); - //} + if ( is_string( self::$registeredGeocoders[$geocoderIdentifier] ) ) { + $geocoderClass = self::$registeredGeocoders[$geocoderIdentifier]; + $geocoder = new $geocoderClass( $geocoderIdentifier ); + } + elseif ( self::$registeredGeocoders[$geocoderIdentifier] instanceof \Maps\Geocoders\Geocoder ) { + $geocoder = new MapsOldGeocoderAdapter( + self::$registeredGeocoders[$geocoderIdentifier], + $geocoderIdentifier + ); + } + else { + throw new MWException( 'Need either class name or Geocoder instance' ); + } + + + self::$geocoders[$geocoderIdentifier] = $geocoder; } else { throw new MWException( 'There is geocoder linked to identifier ' . $geocoderIdentifier . '.' ); @@ -361,23 +319,24 @@ protected static function getGeocoderInstance( $geocoderIdentifier ) { return self::$geocoders[$geocoderIdentifier]; } - + /** * Returns a valid geocoder idenifier. If the given one is a valid main identifier, * it will simply be returned. If it's an alias, it will be turned into the correponding * main identifier. If it's not recognized at all (or empty), the default will be used. * Only call this function when there are geocoders available, else an erro will be thrown. - * + * * @since 0.7 * * @param string $geocoderIdentifier - * - * @return string or false + * + * @return string|bool + * @throws MWException */ - protected static function getValidGeocoderIdentifier( $geocoderIdentifier ) { + private static function getValidGeocoderIdentifier( $geocoderIdentifier ) { global $egMapsDefaultGeoService; static $validatedDefault = false; - + if ( $geocoderIdentifier === '' || !array_key_exists( $geocoderIdentifier, self::$registeredGeocoders ) ) { if ( !$validatedDefault ) { if ( !array_key_exists( $egMapsDefaultGeoService, self::$registeredGeocoders ) ) { @@ -388,7 +347,7 @@ protected static function getValidGeocoderIdentifier( $geocoderIdentifier ) { } } } - + if ( array_key_exists( $egMapsDefaultGeoService, self::$registeredGeocoders ) ) { $geocoderIdentifier = $egMapsDefaultGeoService; } @@ -396,8 +355,8 @@ protected static function getValidGeocoderIdentifier( $geocoderIdentifier ) { return false; } } - + return $geocoderIdentifier; } - -} \ No newline at end of file + +} diff --git a/includes/Maps_BaseFillableElement.php b/includes/Maps_BaseFillableElement.php index 09bbffc5a..e0a459d73 100644 --- a/includes/Maps_BaseFillableElement.php +++ b/includes/Maps_BaseFillableElement.php @@ -3,7 +3,7 @@ /** * @since 2.0 */ -class MapsBaseFillableElement extends MapsBaseStrokableElement implements iFillableMapElement { +class MapsBaseFillableElement extends MapsBaseStrokableElement { protected $fillColor; protected $fillOpacity; @@ -34,10 +34,10 @@ public function hasFillOpacity() { public function getJSONObject( $defText = '' , $defTitle = '' ) { $parentArray = parent::getJSONObject( $defText , $defTitle ); - $array = array( + $array = [ 'fillColor' => $this->hasFillColor() ? $this->getFillColor() : '#FF0000' , 'fillOpacity' => $this->hasFillOpacity() ? $this->getFillOpacity() : '0.5' , - ); + ]; return array_merge( $parentArray , $array ); } } diff --git a/includes/Maps_BaseStrokableElement.php b/includes/Maps_BaseStrokableElement.php index 6e7f37258..1d4bd674b 100644 --- a/includes/Maps_BaseStrokableElement.php +++ b/includes/Maps_BaseStrokableElement.php @@ -8,7 +8,7 @@ * @licence GNU GPL v2+ * @author Kim Eik < kim@heldig.org > */ -class MapsBaseStrokableElement extends BaseElement implements iStrokableMapElement { +class MapsBaseStrokableElement extends BaseElement { protected $strokeColor; protected $strokeOpacity; @@ -52,11 +52,11 @@ public function hasStrokeWeight() { public function getJSONObject( $defText = '' , $defTitle = '' ) { $parentArray = parent::getJSONObject( $defText , $defTitle ); - $array = array( + $array = [ 'strokeColor' => $this->hasStrokeColor() ? $this->getStrokeColor() : '#FF0000' , 'strokeOpacity' => $this->hasStrokeOpacity() ? $this->getStrokeOpacity() : '1' , 'strokeWeight' => $this->hasStrokeWeight() ? $this->getStrokeWeight() : '2' - ); + ]; return array_merge( $parentArray , $array ); } diff --git a/includes/Maps_DisplayMapRenderer.php b/includes/Maps_DisplayMapRenderer.php index 262fba024..ae09bf2cf 100644 --- a/includes/Maps_DisplayMapRenderer.php +++ b/includes/Maps_DisplayMapRenderer.php @@ -1,7 +1,9 @@ service = $service; - } - - /** - * Returns the HTML to display the map. - * - * @since 2.0 - * - * @param array $params - * @param Parser $parser - * @param string $mapName - * - * @return string + * @var LocationParser */ - protected function getMapHTML( array $params, Parser $parser, $mapName ) { - return Html::rawElement( - 'div', - array( - 'id' => $mapName, - 'style' => "width: {$params['width']}; height: {$params['height']}; background-color: #cccccc; overflow: hidden;", - 'class' => 'maps-map maps-' . $this->service->getName() - ), - wfMessage( 'maps-loading-map' )->inContentLanguage()->escaped() . - Html::element( - 'div', - array( 'style' => 'display:none', 'class' => 'mapdata' ), - FormatJson::encode( $this->getJSONObject( $params, $parser ) ) - ) - ); - } + private $locationParser; - /** - * Returns a PHP object to encode to JSON with the map data. - * - * @since 2.0 - * - * @param array $params - * @param Parser $parser - * - * @return mixed - */ - protected function getJSONObject( array $params, Parser $parser ) { - return $params; + public function __construct( MapsMappingService $service ) { + $this->service = $service; } /** @@ -80,35 +35,74 @@ protected function getJSONObject( array $params, Parser $parser ) { * @return string */ public final function renderMap( array $params, Parser $parser ) { + $this->initializeLocationParser( $params ); + $this->handleMarkerData( $params, $parser ); $mapName = $this->service->getMapId(); - $output = $this->getMapHTML( $params, $parser, $mapName ); + $output = $this->getMapHTML( $params, $mapName ); $configVars = Skin::makeVariablesScript( $this->service->getConfigVariables() ); + $this->service->addHtmlDependencies( + self::getLayerDependencies( $params['mappingservice'], $params ) + ); + $this->service->addDependencies( $parser ); - $parser->getOutput()->addHeadItem( $configVars ); + + $parserOutput = $parser->getOutput(); + $parserOutput->addHeadItem( $configVars ); + + // Wikia change: Load library dependencies in OutputPageParserOutput hook + // Loading them in the will cause Oasis skin to load them after ResourceLoader + // so RL modules that depend on them will fail + if ( !isset( $parserOutput->mapsMappingServices ) ) { + $parserOutput->mapsMappingServices = []; + } + + $parserOutput->mapsMappingServices[] = $this->service; return $output; } + private function initializeLocationParser( array $params ) { + $this->locationParser = new LocationParser( new ValueParserOptions( [ + 'geoService' => $params['geoservice'] + ] ) ); + } + /** - * Converts the data in the coordinates parameter to JSON-ready objects. - * These get stored in the locations parameter, and the coordinates on gets deleted. - * - * FIXME: complexity + * Returns the HTML to display the map. * - * @since 1.0 + * @param array $params + * @param string $mapName * - * @param array &$params - * @param Parser $parser + * @return string */ - protected function handleMarkerData( array &$params, Parser $parser ) { - if ( is_object( $params['centre'] ) ) { - $params['centre'] = $params['centre']->getJSONObject(); - } + protected function getMapHTML( array $params, $mapName ) { + return Html::rawElement( + 'div', + [ + 'id' => $mapName, + 'style' => "width: {$params['width']}; height: {$params['height']}; background-color: #cccccc; overflow: hidden;", + 'class' => 'maps-map maps-' . $this->service->getName() + ], + wfMessage( 'maps-loading-map' )->inContentLanguage()->escaped() . + Html::element( + 'div', + [ 'style' => 'display:none', 'class' => 'mapdata' ], + FormatJson::encode( $params ) + ) + ); + } + + /** + * Converts the data in the coordinates parameter to JSON-ready objects. + * These get stored in the locations parameter, and the coordinates on gets deleted. + */ + private function handleMarkerData( array &$params, Parser $parser ) { + $params['centre'] = $this->getCenter( $params['centre'] ); $parserClone = clone $parser; @@ -116,44 +110,80 @@ protected function handleMarkerData( array &$params, Parser $parser ) { $params['wmsoverlay'] = $params['wmsoverlay']->getJSONObject(); } + $params['locations'] = $this->getLocationJson( $params, $parserClone ); + + unset( $params['coordinates'] ); + + $this->handleShapeData( $params, $parserClone ); + + if ( $params['mappingservice'] === 'openlayers' ) { + $params['layers'] = self::evilOpenLayersHack( $params['layers'] ); + } + } + + private function getCenter( $coordinatesOrAddress ) { + if ( $coordinatesOrAddress === false ) { + return false; + } + + try { + // FIXME: a Location makes no sense here, since the non-coordinate data is not used + $location = $this->locationParser->stringParse( $coordinatesOrAddress ); + } + catch ( \Exception $ex ) { + // TODO: somehow report this to the user + return false; + } + + return $location->getJSONObject(); + } + + private function getLocationJson( array $params, $parserClone ) { $iconUrl = MapsMapper::getFileUrl( $params['icon'] ); $visitedIconUrl = MapsMapper::getFileUrl( $params['visitedicon'] ); - $params['locations'] = array(); - /** - * @var Location $location - */ - foreach ( $params['coordinates'] as $location ) { - $jsonObj = $location->getJSONObject( $params['title'], $params['label'], $iconUrl, '', '',$visitedIconUrl); + $locationJsonObjects = []; - $jsonObj['title'] = $parserClone->parse( $jsonObj['title'], $parserClone->getTitle(), new ParserOptions() )->getText(); - $jsonObj['text'] = $parserClone->parse( $jsonObj['text'], $parserClone->getTitle(), new ParserOptions() )->getText(); - $jsonObj['inlineLabel'] = strip_tags($parserClone->parse( $jsonObj['inlineLabel'], $parserClone->getTitle(), new ParserOptions() )->getText(),''); - - $hasTitleAndtext = $jsonObj['title'] !== '' && $jsonObj['text'] !== ''; - $jsonObj['text'] = ( $hasTitleAndtext ? '' . $jsonObj['title'] . '
' : $jsonObj['title'] ) . $jsonObj['text']; - $jsonObj['title'] = strip_tags( $jsonObj['title'] ); + foreach ( $params['coordinates'] as $coordinatesOrAddress ) { + try { + $location = $this->locationParser->stringParse( $coordinatesOrAddress ); + } + catch ( \Exception $ex ) { + // TODO: somehow report this to the user + continue; + } - $params['locations'][] = $jsonObj; + $locationJsonObjects[] = $this->getLocationJsonObject( $location, $params, $iconUrl, $visitedIconUrl, $parserClone ); } - unset( $params['coordinates'] ); + return $locationJsonObjects; + } - $this->handleShapeData( $params, $parserClone ); + private function getLocationJsonObject( Location $location, array $params, $iconUrl, $visitedIconUrl, Parser $parserClone ) { + $jsonObj = $location->getJSONObject( $params['title'], $params['label'], $iconUrl, '', '', $visitedIconUrl ); - if ( $params['mappingservice'] === 'openlayers' ) { - $params['layers'] = self::evilOpenLayersHack( $params['layers'] ); + $jsonObj['title'] = $parserClone->parse( $jsonObj['title'], $parserClone->getTitle(), new ParserOptions() )->getText(); + $jsonObj['text'] = $parserClone->parse( $jsonObj['text'], $parserClone->getTitle(), new ParserOptions() )->getText(); + + if ( isset( $jsonObj['inlineLabel'] ) ) { + $jsonObj['inlineLabel'] = strip_tags($parserClone->parse( $jsonObj['inlineLabel'], $parserClone->getTitle(), new ParserOptions() )->getText(),'
'); } + + $hasTitleAndtext = $jsonObj['title'] !== '' && $jsonObj['text'] !== ''; + $jsonObj['text'] = ( $hasTitleAndtext ? '' . $jsonObj['title'] . '
' : $jsonObj['title'] ) . $jsonObj['text']; + $jsonObj['title'] = strip_tags( $jsonObj['title'] ); + + return $jsonObj; } - protected function handleShapeData( array &$params, Parser $parserClone ) { - $textContainers = array( + private function handleShapeData( array &$params, Parser $parserClone ) { + $textContainers = [ &$params['lines'] , &$params['polygons'] , &$params['circles'] , &$params['rectangles'], &$params['imageoverlays'], // FIXME: this is Google Maps specific!! - ); + ]; foreach ( $textContainers as &$textContainer ) { if ( is_array( $textContainer ) ) { @@ -189,8 +219,8 @@ protected function handleShapeData( array &$params, Parser $parserClone ) { public static function evilOpenLayersHack( $layers ) { global $egMapsOLLayerGroups, $egMapsOLAvailableLayers; - $layerDefs = array(); - $layerNames = array(); + $layerDefs = []; + $layerNames = []; foreach ( $layers as $layerOrGroup ) { $lcLayerOrGroup = strtolower( $layerOrGroup ); @@ -222,91 +252,40 @@ public static function evilOpenLayersHack( $layers ) { $layerNames[] = $lcLayerOrGroup; } } - // Image layers. Check validity and add if not present yet: - else { - $layerParts = explode( ';', $layerOrGroup, 2 ); - $layerGroup = $layerParts[0]; - $layerName = count( $layerParts ) > 1 ? $layerParts[1] : null; - - $title = Title::newFromText( $layerGroup, Maps_NS_LAYER ); - - if ( $title !== null && $title->getNamespace() == Maps_NS_LAYER ) { - // TODO: FIXME: This shouldn't be here and using $wgParser, instead it should - // be somewhere around MapsBaseMap::renderMap. But since we do a lot more than - // 'parameter manipulation' in here, we already diminish the information needed - // for this which will never arrive there. - global $wgParser; - // add dependency to the layer page so if the layer definition gets updated, - // the page where it is used will be updated as well: - $rev = Revision::newFromTitle( $title ); - $revId = null; - if( $rev !== null ) { - $revId = $rev->getId(); - } - $wgParser->getOutput()->addTemplate( $title, $title->getArticleID(), $revId ); - - // if the whole layer group is not yet loaded into the map and the group exists: - if( !in_array( $layerGroup, $layerNames ) - && $title->exists() - ) { - if( $layerName !== null ) { - // load specific layer with name: - $layer = MapsLayers::loadLayer( $title, $layerName ); - $layers = new MapsLayerGroup( $layer ); - $usedLayer = $layerOrGroup; - } - else { - // load all layers from group: - $layers = MapsLayers::loadLayerGroup( $title ); - $usedLayer = $layerGroup; - } - - foreach( $layers->getLayers() as $layer ) { - if( ( // make sure named layer is only taken once (in case it was requested on its own before) - $layer->getName() === null - || !in_array( $layerGroup . ';' . $layer->getName(), $layerNames ) - ) - && $layer->isOk() - ) { - $layerDefs[] = $layer->getJavaScriptDefinition(); - } - } - - $layerNames[] = $usedLayer; // have to add this after loop of course! - } - } - else { - wfWarn( "Invalid layer ($layerOrGroup) encountered after validation." ); - } - } } - - MapsMappingServices::getServiceInstance( 'openlayers' )->addLayerDependencies( self::getLayerDependencies( $layerNames ) ); - -// print_r( $layerDefs ); -// die(); return $layerDefs; } - /** - * FIXME - * @see evilOpenLayersHack - */ - private static function getLayerDependencies( array $layerNames ) { - global $egMapsOLLayerDependencies, $egMapsOLAvailableLayers; - - $layerDependencies = array(); - - foreach ( $layerNames as $layerName ) { - if ( array_key_exists( $layerName, $egMapsOLAvailableLayers ) // The layer must be defined in php - && is_array( $egMapsOLAvailableLayers[$layerName] ) // The layer must be an array... - && count( $egMapsOLAvailableLayers[$layerName] ) > 1 // ...with a second element... - && array_key_exists( $egMapsOLAvailableLayers[$layerName][1], $egMapsOLLayerDependencies ) ) { //...that is a dependency. - $layerDependencies[] = $egMapsOLLayerDependencies[$egMapsOLAvailableLayers[$layerName][1]]; + public static function getLayerDependencies( $service, $params ) { + global $egMapsOLLayerDependencies, $egMapsOLAvailableLayers, + $egMapsLeafletLayerDependencies, $egMapsLeafletAvailableLayers, + $egMapsLeafletLayersApiKeys; + + $layerDependencies = []; + + if ( $service === 'leaflet' ) { + $layerName = $params['layer']; + if ( array_key_exists( $layerName, $egMapsLeafletAvailableLayers ) + && $egMapsLeafletAvailableLayers[$layerName] + && array_key_exists( $layerName, $egMapsLeafletLayersApiKeys ) + && array_key_exists( $layerName, $egMapsLeafletLayerDependencies ) ) { + $layerDependencies[] = ''; } + } else if ( $service === 'openlayers' ) { + $layerNames = $params['layers']; + foreach ( $layerNames as $layerName ) { + if ( array_key_exists( $layerName, $egMapsOLAvailableLayers ) // The layer must be defined in php + && is_array( $egMapsOLAvailableLayers[$layerName] ) // The layer must be an array... + && count( $egMapsOLAvailableLayers[$layerName] ) > 1 // ...with a second element... + && array_key_exists( $egMapsOLAvailableLayers[$layerName][1], $egMapsOLLayerDependencies ) ) { //...that is a dependency. + $layerDependencies[] = $egMapsOLLayerDependencies[$egMapsOLAvailableLayers[$layerName][1]]; + } + } + } return array_unique( $layerDependencies ); } - + } diff --git a/includes/Maps_DistanceParser.php b/includes/Maps_DistanceParser.php index d3b906295..fd58bb73f 100644 --- a/includes/Maps_DistanceParser.php +++ b/includes/Maps_DistanceParser.php @@ -34,7 +34,7 @@ public static function parseDistance( $distance ) { self::initUnitRegex(); - $matches = array(); + $matches = []; preg_match( '/^\d+(\.\d+)?\s?(' . self::$unitRegex . ')?$/', $distance, $matches ); $value = (float)( $matches[0] . $matches[1] ); @@ -161,7 +161,7 @@ protected static function normalizeDistance( $distance ) { $strlen = strlen( $distance ); for ( $i = 0; $i < $strlen; $i++ ) { - if ( !ctype_digit( $distance{$i} ) && !in_array( $distance{$i}, array( ',', '.' ) ) ) { + if ( !ctype_digit( $distance{$i} ) && !in_array( $distance{$i}, [ ',', '.' ] ) ) { $value = substr( $distance, 0, $i ); $unit = substr( $distance, $i ); break; @@ -171,7 +171,7 @@ protected static function normalizeDistance( $distance ) { $value = str_replace( ',', '.', isset( $value ) ? $value : $distance ); if ( isset( $unit ) ) { - $value .= ' ' . str_replace( array( ' ', "\t" ), '', $unit ); + $value .= ' ' . str_replace( [ ' ', "\t" ], '', $unit ); } return $value; diff --git a/includes/Maps_GeoFunctions.php b/includes/Maps_GeoFunctions.php index 77696b5f9..94c33dbf1 100644 --- a/includes/Maps_GeoFunctions.php +++ b/includes/Maps_GeoFunctions.php @@ -72,10 +72,10 @@ public static function calculateDistance( LatLongValue $start, LatLongValue $end * @return array The destination coordinates, as non-directional floats in an array with lat and lon keys. */ public static function findDestination( LatLongValue $startingCoordinates, $bearing, $distance ) { - $startingCoordinates = array( + $startingCoordinates = [ 'lat' => deg2rad( $startingCoordinates->getLatitude() ), 'lon' => deg2rad( $startingCoordinates->getLongitude() ), - ); + ]; $radBearing = deg2rad ( (float)$bearing ); $angularDistance = $distance / Maps_EARTH_RADIUS; @@ -83,10 +83,10 @@ public static function findDestination( LatLongValue $startingCoordinates, $bear $lat = asin (sin ( $startingCoordinates['lat'] ) * cos ( $angularDistance ) + cos ( $startingCoordinates['lat'] ) * sin ( $angularDistance ) * cos ( $radBearing ) ); $lon = $startingCoordinates['lon'] + atan2 ( sin ( $radBearing ) * sin ( $angularDistance ) * cos ( $startingCoordinates['lat'] ), cos ( $angularDistance ) - sin ( $startingCoordinates['lat'] ) * sin ( $lat ) ); - return array( + return [ 'lat' => rad2deg( $lat ), 'lon' => rad2deg( $lon ) - ); + ]; } } \ No newline at end of file diff --git a/includes/Maps_KMLFormatter.php b/includes/Maps_KMLFormatter.php index 71acb3ae3..f99244fbe 100644 --- a/includes/Maps_KMLFormatter.php +++ b/includes/Maps_KMLFormatter.php @@ -33,7 +33,7 @@ class MapsKMLFormatter { * * @param array $params */ - public function __construct( array $params = array() ) { + public function __construct( array $params = [] ) { $this->params = $params; $this->clearElements(); } @@ -98,7 +98,7 @@ public function clearElements() { * @since 0.7.3 */ public function clearPlacemarks() { - $this->placemarks = array(); + $this->placemarks = []; } /** @@ -109,7 +109,7 @@ public function clearPlacemarks() { * @return string */ protected function getKMLElements() { - $elements = array(); + $elements = []; $elements = array_merge( $elements, $this->getPlacemarks() ); @@ -125,7 +125,7 @@ protected function getKMLElements() { * @return array */ protected function getPlacemarks() { - $placemarks = array(); + $placemarks = []; foreach ( $this->placemarks as $location ) { $placemarks[] = $this->getKMLForLocation( $location ); @@ -153,7 +153,7 @@ protected function getKMLForLocation( Location $location ) { // lon,lat[,alt] $coordinates = Xml::element( 'coordinates', - array(), + [], $coordinates->getLongitude() . ',' . $coordinates->getLatitude() . ',0' ); diff --git a/includes/Maps_Layer.php b/includes/Maps_Layer.php deleted file mode 100644 index 5cbd5b80d..000000000 --- a/includes/Maps_Layer.php +++ /dev/null @@ -1,339 +0,0 @@ - - * @author Daniel Werner - */ -abstract class MapsLayer { - - /** - * Returns a string containing the JavaScript definition of this layer. - * Only call this function when you are sure the layer is valid! - * - * @since 0.7.1 - * - * @return string - */ - public abstract function getJavaScriptDefinition(); - - /** - * @since 0.7.1 - * - * @var array - */ - protected $properties; - - /** - * @since 3.0 - * - * @var array - */ - protected $originalPropertyValues; - - /** - * @since 0.7.1 - * - * @var array - */ - protected $errors = array(); - - /** - * Keeps track if the layer has been validated, to prevent doing redundant work. - * - * @since 0.7.1 - * - * @var boolean - */ - protected $hasValidated = false; - - /** - * Optional name of a layer within its group. This is for identifying a layer for only - * displaying this specific layer instead of all layers within a agroup. If this layer - * should not be selectable on its own without any other group members, the value is null - * - * @var string - */ - protected $name; - - /** - * Constructor. - * - * @since 0.7.1 ($name since 3.0) - * - * @param array $properties - * @param string $name optional name of the layer within its group for being able to select - * the layer on its own without having other members of the group available to be - * displayed in the maps layer control. - */ - public function __construct( array $properties, $name = null ) { - $this->properties = $properties; - $this->name = $name; - $this->originalPropertyValues = $properties; - } - - /** - * Returns the error messages, optionally filtered by an error tag. - * - * @since 0.7.1 - * - * @param mixed $tag - * - * @return array of string - */ - public function getErrorMessages( $tag = false ) { - $messages = array(); - - foreach ( $this->errors as $error ) { - if ( $tag === false || $error->hasTag( $tag ) ) { - $messages[] = $error->getMessage(); - } - } - - return $messages; - } - - /** - * Returns the layers properties. - * - * @since 0.7.1 - * - * @return array - */ - public function getProperties() { - return $this->properties; - } - - /** - * Returns the layer type represented by the class of this layer instance. - * - * @since 3.0 - * - * @return string - */ - final public function getType() { - return MapsLayerTypes::getTypeOfLayer( $this ); - } - - /** - * Returns the layers name within its group. If not name is defined, this will - * return null - * - * @since 3.0 - * - * @return string|null - */ - public function getName() { - return $this->name; - } - - /** - * Convenient function for getting mapping services supported by this layer. - * - * @since 3.0 - * - * @return string[] - */ - final public function getSupportedServices() { - return MapsLayerTypes::getServicesForType( $this->getType() ); - - } - - /** - * Convenience function to find out whether the layer is supporting a certain mapping service. - * - * @since 3.0 - * @param string $service - * - * @return boolean - */ - final public function isSupportingService( $service ) { - $services = $this->getSupportedServices(); - return in_array( $service, $services ); - } - - /** - * Validates the layer. - * - * @since 0.7.1 - */ - protected function validate() { - if( $this->hasValidated ) { - return; - } - $this->hasValidated = true; - - $validator = Processor::newDefault(); - - $validator->setParameters( $this->properties, $this->getParameterDefinitions() ); - $validator->validateParameters(); - - if ( $validator->hasErrors() !== false ) { - $this->errors = $validator->getErrors(); - } - $params = $validator->getParameterValues(); - $this->properties = $params; - } - - /** - * Returns whether the properties make up a valid layer definition without any errors. - * - * @since 0.7.1 - * - * @return boolean - */ - public function isValid() { - if ( !$this->hasValidated ) { - $this->validate(); - } - return empty( $this->errors ); - } - - /** - * Returns whether the layer can be used as it is defined. Returns true even if errors - * which are non fatal have occurred. - * - * @since 3.0 - * - * @return boolean - */ - public function isOk() { - if ( ! $this->isValid() ) { - // check whether one fatal error has occurred: - foreach( $this->errors as $error ) { - if( $error->isFatal() ) { - return false; - } - } - } - return true; - } - - /** - * Returns an array of parameter definitions. - * - * @since 3.0 (and before as abstract function since 0.7.2) - * - * @return array - */ - protected function getParameterDefinitions() { - $params = array(); - - $params['label'] = array( - 'message' => 'maps-displaymap-par-coordinates', // TODO-customMaps: create a message - ); - - // units for extent data: - $params['units'] = array( - 'default' => 'degree', - 'message' => 'maps-displaymap-par-coordinates', // TODO-customMaps: create a message - 'values' => array( 'degree', 'm', 'ft', 'km', 'mi', 'inches' ), - ); - - // zoom information: - $params['minscale'] = array( - 'type' => 'float', - 'default' => false, - 'manipulatedefault' => false, - 'message' => 'maps-displaymap-par-coordinates', // TODO-customMaps: create a message - ); - - $params['maxscale'] = array( - 'type' => 'float', - 'default' => false, - 'manipulatedefault' => false, - 'message' => 'maps-displaymap-par-coordinates', // TODO-customMaps: create a message - // TODO-customMaps: addManipulations( new MapsParamSwitchIfGreaterThan( $params['minscale'] ) ); - ); - - $params['zoomlevels'] = array( - 'type' => 'integer', - 'default' => false, - 'manipulatedefault' => false, - 'message' => 'maps-displaymap-par-coordinates', // TODO-customMaps: create a message - // TODO-customMaps: addManipulations( new MapsParamSwitchIfGreaterThan( $params['minscale'] ) ); - ); - - return $params; - } - - /** - * Returns the value of a property value formated for html output. - * The result contains pure html. - * - * @since 3.0 - * - * @param string $name Name of the property value - * @param Parser $parser - * - * @return array - */ - public function getPropertiesHtmlRepresentation( &$parser ) { - $this->validate(); // make sure properties are available! - $transformed = array(); - foreach( $this->properties as $property => $value ) { - - if( ! $this->isValid() ) { - // datermine whether value for this parameter is valid: - $errors = $this->getErrorMessages( $property ); - - if ( $errors ) { - $transformed[ $property ] = '' . implode( '
', array_map( 'htmlspecialchars', $errors ) ) . '
'; - continue; - } - } - $transformed[ $property ] = $this->getPropertyHtmlRepresentation( $property, $parser ); - } - $this->doPropertiesHtmlTransform( $transformed ); - return $transformed; - } - - /** - * Returns the value of a property value formatted for html output - * - * @since 3.0 - * - * @param string $name Name of the property value - * @param Parser $parser - * - * @return string - */ - protected function getPropertyHtmlRepresentation( $name, &$parser ) { - $value = $this->properties[ $name ]; - - switch( $name ) { - case 'maxscale': - case 'minscale': - // if default value is in use: - if( $value === false ) { - return 'auto'; - } - break; - } - - return htmlspecialchars( $value ); - } - - /** - * Does the final transform of the properties array for html representation after - * each single property value is transformed by 'getPropertyHtmlRepresentation()' - * already. This is used to group certain properties for nicer output. - * - * @since 3.0 - * - * @param array &$properties - * - * @return array - */ - protected function doPropertiesHtmlTransform( &$properties ) { - $properties['scale'] = "max: {$properties['maxscale']}, min: {$properties['minscale']}"; - unset( $properties['maxscale'], $properties['minscale'] ); - } -} - diff --git a/includes/Maps_LayerGroup.php b/includes/Maps_LayerGroup.php deleted file mode 100644 index 3f4538bad..000000000 --- a/includes/Maps_LayerGroup.php +++ /dev/null @@ -1,254 +0,0 @@ -addLayer( $layer ); - } - } - - /** - * Returns the layers which are members of this group. An empty array will be - * returned in case no layers belong to this group. - * - * @since 3.0 - * - * @param integer $types bitfield defining whether named, numeric or all layers should be returned. - * MapsLayerGroup::LAYERS_NAMED, MapsLayerGroup::LAYERS_NUMERIC or MapsLayerGroup::LAYERS_ALL - * - * @return MapsLayer[] - */ - public function getLayers( $types = self::LAYERS_ALL ) { - /* - * For all layers: If given, take the name as key. - * by not doing this in the constructor we won't have conflicts with layer - * name changes later on. - */ - $namedLayers = array(); - - foreach( $this->layers as $layer ) { - - if( $layer->getName() !== null ) { - - if( $types & self::LAYERS_NAMED ) { - // name as key: - $namedLayers[ $layer->getName() ] = $layer; - } - } - elseif( $types & self::LAYERS_NUMERIC ) { - // numeric (random) key: - $namedLayers[] = $layer; - } - } - return $namedLayers; - } - - /** - * Returns the layer with the given name. If the layer doesn't exist, return null. - * - * @since 3.0 - * - * @param string $name - * - * @return MapsLayer|null - */ - public function getLayerByName( $name ) { - // get Layers in array with named index for named layers: - $layers = $this->getLayers(); - - if( array_key_exists( $name, $layers ) ) { - return $layers[ $name ]; - } - return null; - } - - /** - * Returns whether a specific layer exists within the group. - * - * @since 3.0 - * - * @param MapsLayer $layer - * - * @return boolean - */ - public function hasLayer( MapsLayer $layer ) { - foreach( $this->layers as $groupLayer ) { - if( $layer === $groupLayer ) { - return true; - } - } - return false; - } - - /** - * This will add a layer to the collection of layers in this group if it is not a - * member already. - * Does NOT automatically store the layer in the database if the group is loaded - * from a page. - * - * @since 3.0 - * - * @param MapsLayer $layer - * - * @return boolean whether a layer with the same name has been overwritten. Also - * returns false in case the same layer exists within the group already. - */ - public function addLayer( MapsLayer $layer ) { - - if( $this->hasLayer( $layer ) ) { - return false; // exact same layer already exists within group - } - - $overwritten = false; - - // check for layer with same name in this group - if( $layer->getName() !== null ) { - - // remove all layers with same name (shouldn't be more than one but be paranoid): - do { - $existingLayer = $this->getLayerByName( $layer->getName() ); - - if( $existingLayer !== null ) { - $this->removeLayer( $existingLayer ); - $overwritten = true; - } - } - while( $existingLayer !== null ); - } - - $this->layers[] = $layer; - return $overwritten; // layer with same name overwritten? - } - - /** - * This will remove a layer from the collection of layers in this group. - * Does NOT automatically store the layer in the database if the group is loaded - * from a page. - * - * @since 3.0 - * - * @param MapsLayer $layer - * - * @return boolean whether the layer was a member of the group and has been removed. - */ - public function removeLayer ( MapsLayer $layer ) { - foreach( $this->layers as $key => $groupLayer ) { - if( $layer === $groupLayer ) { - unset( $this->layers[ $key ] ); - return true; - } - } - return false; - } - - /** - * Get a group of layers by the title of the group. If the page doesn't contain - * any layers, the group will be returned anyway but won't contain any layers. - * - * @since 3.0 - * - * @param Title $title - * - * @return MapsLayerGroup - */ - public static function newFromTitle( Title $title ) { - // load all members defined on the page $title: - $db = wfGetDB( DB_SLAVE ); - $conditions = array( 'layer_page_id' => $title->getArticleID() ); - - $layers = self::loadMembersFromConds( $db, $conditions ); - if( $layers === false && wfGetLB()->getServerCount() > 1 ) { - $db = wfGetDB( DB_MASTER ); - $layers = self::loadMembersFromConds( $db, $conditions ); - } - $obj = new MapsLayerGroup( $layers ); - return $obj; - } - - /** - * Given a set of conditions, fetch all matching layers from the given database - * connection and return them in an array - * - * @param Database $db - * @param array $conditions - * - * @return array|false - */ - protected static function loadMembersFromConds( $db, $conditions ) { - $results = array(); - $res = self::fetchMembersFromConds( $db, $conditions ); - if( $res ) { - // load all matching layer objects into the layer group: - foreach( $res as $row ) { - if( $row ) { - $obj = MapsLayers::newLayerFromRow( $row ); - $results[] = $obj; - } - } - $res->free(); - return $results; - } - return false; - } - - /** - * Given a set of conditions, return a ResultWrapper - * which will return matching database rows with the - * fields necessary to build the group. - * - * @param Database $db - * @param array $conditions - * - * @return ResultWrapper|false - */ - protected static function fetchMembersFromConds( $db, $conditions ) { - $fields = MapsLayers::databaseFields(); - $res = $db->select( - array( 'maps_layers' ), - $fields, - $conditions, - __METHOD__ - ); - return $res; - } -} diff --git a/includes/Maps_LayerPage.php b/includes/Maps_LayerPage.php deleted file mode 100644 index b569cefdd..000000000 --- a/includes/Maps_LayerPage.php +++ /dev/null @@ -1,179 +0,0 @@ - - * @author Daniel Werner - */ -class MapsLayerPage extends Article { - - protected $usageTitles = null; - - function __construct( Title $title, $oldId = null ) { - parent::__construct( $title, $oldId ); - } - - /** - * Designed similar to CategoryPage. - * - * @see Article::view - * - * @since 3.0 - */ - public function view() { - global $wgRequest, $wgUser; - - $diff = $wgRequest->getVal( 'diff' ); - $diffOnly = $wgRequest->getBool( 'diffonly', $wgUser->getOption( 'diffonly' ) ); - - if( isset( $diff ) && $diffOnly ) { - return parent::view(); - } - - if( !Hooks::run( 'MapsLayerPageView', array( &$this ) ) ) { - return; - } - - if( Maps_NS_LAYER == $this->mTitle->getNamespace() ) { - $this->openShowLayer(); - } - - parent::view(); - - if( Maps_NS_LAYER == $this->mTitle->getNamespace() ) { - $this->closeShowLayer(); - } - } - - /** - * Function called before page rendering. - * - * @since 3.0 - */ - protected function openShowLayer() { - } - - /** - * Function called at the end of page rendering. - * - * @since 3.0 - */ - protected function closeShowLayer() { - $this->renderUsage(); - } - - /** - * Renders the category-page like view which shows the usage of this - * layer page in other pages. - * - * @since 3.0 - */ - public function renderUsage() { - global $wgOut; - $out = ''; - - $titles = $this->getUsageTitles(); - - $viewer = new CategoryViewer( $this->mTitle, $this->getContext() ); - $viewer->limit = 9999; // just overwrite the default limit of pages displayed in a normal category - - // now add apges in sorted order to category viewer: - foreach( $titles as $title ) { - $viewer->addPage( $title, $title->getPrefixedText(), null ); - } - - //$wgOut->addHTML( $viewer->formatList( $viewer->articles, '' ) ); - $out = "
\n"; - $out .= '

' . wfMessage( 'maps-layerpage-usage', $this->mTitle->getText() )->text() . "

\n"; - - if( !empty( $viewer->articles ) ) { - $out .= $viewer->formatList( $viewer->articles, $viewer->articles_start_char ); - } else { - $out .= wfMessage( 'maps-layerpage-nousage' )->text(); - } - $out .= "\n
"; - - $wgOut->addHTML( $out ); - } - - /** - * returns all Titles using this layer page - * - * @since 3.0 - * - * @return Array - */ - public function getUsageTitles() { - // cached result: - if( $this->usageTitles !== null ) { - return $this->usageTitles; - } - - $db = wfGetDB( DB_SLAVE ); - $items = $db->select( - 'templatelinks', - '*', - array( - 'tl_title = "' . $this->mTitle->getDBkey() . '"', - 'tl_namespace = ' . $this->mTitle->getNamespace(), - ), - __METHOD__ - ); - - // get pages and sort them first: - $titles = array(); - foreach( $items as $item ) { - $title = Title::newFromID( $item->tl_from ); - - if( $title !== null ) { - $titles[ $title->getPrefixedText() ] = $title; - } - } - unset( $items, $item ); - ksort( $titles, SORT_STRING ); - - return $this->usageTitles = $titles; - } - - /** - * Returns if the layer page has any layer defined which has a definition that is 'ok', - * meaning, the layer can be used in a map. - * - * @since 3.0 - * - * @param string $name if set, only for the layer definition with this name will be searched. - * @param string $service if set, only layers supporthing this service will be taken in account. - * - * @return boolean - */ - public function hasUsableLayer( $name = null, $service = null ) { - // if name is given, get only that layer to check on, otherwise all: - if( $name !== null ) { - $layer = MapsLayers::loadLayer( $this->getTitle(), $name ); - - if( $layer === null ) { - return false; - } - $layers = array( $layer ); - } else { - $layers = MapsLayers::loadLayerGroup( $this->getTitle() ); - $layers = $layers->getLayers(); - } - - // datermine whether any layer is usable: - foreach( $layers as $layerName => $layer ) { - if( - $layer->isOk() // doesn't have to be 100% valid, just valid enough to be usable! - && ( $service === null || $layer->isSupportingService( $service ) ) - ) { - return true; - } - } - return false; - } -} diff --git a/includes/Maps_LayerTypes.php b/includes/Maps_LayerTypes.php deleted file mode 100644 index 725803a56..000000000 --- a/includes/Maps_LayerTypes.php +++ /dev/null @@ -1,161 +0,0 @@ - $supportedServices ) { - if ( in_array( $service, $supportedServices ) ) { - $layers[] = $layerType; - } - } - - return $layers; - } - } - - /** - * Returns the mapping services supported by the provided layer type. - * - * @since 3.0 (available in MapsLayers class in 0.7.2 to 3.0) - * - * @param string $type - * - * @return array - */ - public static function getServicesForType( $type ) { - return array_key_exists( $type, self::$services ) ? self::$services[$type] : array(); - } - - /** - * Returns whether the layer type exists (that's the case if the type is associated - * with a layer class). - * - * @since 3.0 (previously 'hasLayer' since 0.7.2) - * - * @param string $type - * @param string $service - * - * @return boolean - */ - public static function hasType( $type, $service = null ) { - self::initializeTypes(); - - if ( array_key_exists( $type, self::$classes ) && array_key_exists( $type, self::$services ) ) { - return is_null( $service ) || in_array( $service, self::$services[$type] ); - } - else { - return false; - } - } - - /** - * Returns the class of a layer template class or null if the type doesn't exist. - * - * @since 3.0 - * - * @param string $type - * - * @return string|null - */ - public static function getTypesClass( $type ) { - if( ! self::hasType( $type ) ) - return null; - - return self::$classes[ $type ]; - } - - /** - * Register a layer type by associating a specific class with it. - * - * @param string $type - * @param string $layerClass - * @param string[]|string $serviceIdentifier - * - * @since 3.0 - */ - public static function registerLayerType( $type, $layerClass, $serviceIdentifier ) { - self::$classes[$type] = $layerClass; - if( is_array( $serviceIdentifier ) ) { - self::$services[$type] = $serviceIdentifier; - } else { - self::$services[$type] = array( $serviceIdentifier ); - } - } - - /** - * Initializes the layers functionality by registering the layer types - * by firing the hook. - * - * @since 3.0 - */ - protected static function initializeTypes() { - // only initialize once! - if( empty( self::$classes ) ) { - Hooks::run( 'MappingLayersInitialization' ); - } - } -} diff --git a/includes/Maps_Layers.php b/includes/Maps_Layers.php deleted file mode 100644 index e0bcae302..000000000 --- a/includes/Maps_Layers.php +++ /dev/null @@ -1,229 +0,0 @@ - - * @author Daniel Werner - */ -class MapsLayers { - - /** - * Array with layer page site names as keys and MapsLayerGroup - * objects as values. - * - * @since 3.0 - * - * @var MapsLayerGroup - */ - protected static $layerGroups = array(); - - /** - * Returns a new instance of a layer class for the provided layer type to create - * an actual layer definition based on the basic layer type class. - * - * @since 3.0 - * - * @param string $type name of the basic layer type. - * @param array $properties definition describing the layer characteristics. - * @param string $name optional name to identify this particular layer within a - * group of layers within a layer page. If not set, an increasing numeric - * name will be assigned. - * - * @return MapsLayer - */ - public static function newLayerFromDefinition( $type, array $properties, $name = null ) { - - $class = MapsLayerTypes::getTypesClass( $type ); - - if ( $class !== null ) { - return new $class( $properties, $name ); - } - else { - throw new exception( "There is no layer class for layers of type \"$type\"." ); - } - } - - /** - * Creates a new type specific MapsLayer from a feteched database row. - * - * @since 3.0 - * - * @param object $row - * - * @return MapsLayer - */ - public static function newLayerFromRow( $row ) { - if( is_object( $row ) ) { - $type = $row->layer_type; - $name = $row->layer_name; // might be null - $data = self::parseLayerParameters( $row->layer_data ); - - return self::newLayerFromDefinition( $type, $data, $name ); - } - else { - throw new MWException( 'Invalid row format for "MapsLayer" creation.' ); - } - } - - /** - * Reads layer parameter definition in string form and returns and array containing - * all parameters as structured data where the key is the parameter name and the - * associated value its value. - * - * @since 3.0 - * - * @param string $parameters - * @param string $itemSep separator between prameters. - * @param string $keyValueSep separator between parameter name and associated value. - * - * @return array - */ - public static function parseLayerParameters( $parameters, $itemSep = "\n", $keyValueSep = '=' ) { - $keyValuePairs = array(); - - // get 'key=value' pairs and put them into an array where key is the index for each value - foreach ( explode( $itemSep, $parameters ) as $line ) { - $parts = explode( $keyValueSep, $line, 2 ); - - if ( count( $parts ) == 2 ) { - // only allow basic characters as layer-description parameters: - $key = preg_replace( '/[^a-z0-9]/', '', strtolower( $parts[0] ) ); - $keyValuePairs[ $key ] = trim( $parts[1] ); - } - } - return $keyValuePairs; - } - - /** - * This will load an already defined layer from a layer page within the wiki. Since - * there can be several layers as a group on one page, this also requires the layers - * name within the group to identify it. - * If the requested layer doesn't exist, this will return null. - * - * @since 3.0 - * - * @param Title $layerPage title of a page with layer definitions - * @param string $name layers name within the group of all layers defined in $layerPage - * - * @return MapsLayer|null - */ - public static function loadLayer( Title $layerPage, $name ) { - $layers = self::loadLayerGroup( $layerPage ); - return $layers->getLayerByName( $name ); - } - - /** - * This will load all layers defined on a layer page and return them as - * MapsLayerGroup object. - * - * @since 3.0 - * - * @param Title $layerPage - * - * @return MapsLayerGroup - */ - public static function loadLayerGroup( Title $layerPage ) { - // try to get it from cached layers: - $groupId = $layerPage->getPrefixedDBkey(); - if( array_key_exists( $groupId, self::$layerGroups ) ) { - return self::$layerGroups[ $groupId ]; - } - - $layerGroup = MapsLayerGroup::newFromTitle( $layerPage ); - - self::$layerGroups[ $groupId ] = $layerGroup; - return $layerGroup; - } - - /** - * Store layers of a page to database. This will remove all previous layers - * of that page from the database first. - * - * @since 3.0 - * - * @param MapsLayerGroup $layerGroup contains all layers of the page. - * @param Title $title the page title the layers are associated with. - * - * @return boolean - */ - public static function storeLayers( MapsLayerGroup $layerGroup, Title $title ) { - - // clear cache for this one: - unset( self::$layerGroups[ $title->getPrefixedDBkey() ] ); - - /* - * create data for multiple row insert: - */ - $pageId = $title->getArticleID(); - - foreach( $layerGroup->getLayers() as $layer ) { - $dbLayers[] = self::databaseRowFromLayer( $layer, $pageId ); - } - - /* - * insert all layer rows of the page into database: - */ - $db = wfGetDB( DB_MASTER ); - - // delete old, stored layers first: - $db->delete( 'maps_layers', array( 'layer_page_id' => $pageId ), __METHOD__ ); - - if( empty( $dbLayers ) ) { - // empty group, nothing to insert - return true; - } - else { - // insert new rows: - return $db->insert( 'maps_layers', $dbLayers, __METHOD__ ); - } - } - - /** - * Return the list of database fields that should be selected to create a new MapsLayer. - * - * @since 3.0 - */ - public static function databaseFields() { - return array( - 'layer_page_id', - 'layer_name', - 'layer_type', - 'layer_data' - ); - } - - /** - * Returns database fields as keys and an associated value taken from a layer as value, ready - * for database insert. - * - * @since 3.0 - * - * @param MapsLayer $layer - * @param integer $pageId The article id of the page the layer should be associated to. - * For example "Title::getArticleID()". - * - * @return array - */ - public static function databaseRowFromLayer( MapsLayer $layer, $pageId ) { - - // format layer properties array: - $properties = array(); - foreach( $layer->getProperties() as $key => $prop ) { - $properties[] = "$key=$prop"; - } - $properties = implode( "\n", $properties ); - - return array( - 'layer_page_id' => $pageId, - 'layer_name' => $layer->getName(), // might be null - 'layer_type' => $layer->getType(), - 'layer_data' => $properties - ); - } -} - diff --git a/includes/Maps_Mapper.php b/includes/Maps_Mapper.php index 5bc42fd87..e1d9faf23 100644 --- a/includes/Maps_Mapper.php +++ b/includes/Maps_Mapper.php @@ -54,12 +54,12 @@ public static function encodeJsVar( $value ) { if ( $s != '{' ) { $s .= ', '; } - $s .= '"' . Xml::escapeJsString( $name ) . '": ' . + $s .= '"' . Xml::encodeJsVar( $name ) . '": ' . self::encodeJsVar( $elt ); } $s .= '}'; } else { - $s = '"' . Xml::escapeJsString( $value ) . '"'; + $s = '"' . Xml::encodeJsVar( $value ) . '"'; } return $s; } @@ -67,50 +67,48 @@ public static function encodeJsVar( $value ) { /** * This function returns the definitions for the parameters used by every map feature. * - * @deprecated - * * @return array */ public static function getCommonParameters() { global $egMapsAvailableGeoServices, $egMapsDefaultGeoService, $egMapsMapWidth, $egMapsMapHeight, $egMapsDefaultService; - $params = array(); + $params = []; - $params['mappingservice'] = array( + $params['mappingservice'] = [ 'type' => 'mappingservice', 'aliases' => 'service', 'default' => $egMapsDefaultService, - ); + ]; - $params['geoservice'] = array( + $params['geoservice'] = [ 'default' => $egMapsDefaultGeoService, 'values' => $egMapsAvailableGeoServices, 'dependencies' => 'mappingservice', // TODO 'manipulations' => new MapsParamGeoService( 'mappingservice' ), - ); + ]; - $params['width'] = array( + $params['width'] = [ 'type' => 'dimension', 'allowauto' => true, - 'units' => array( 'px', 'ex', 'em', '%', '' ), + 'units' => [ 'px', 'ex', 'em', '%', '' ], 'default' => $egMapsMapWidth, - ); + ]; - $params['height'] = array( + $params['height'] = [ 'type' => 'dimension', - 'units' => array( 'px', 'ex', 'em', '' ), + 'units' => [ 'px', 'ex', 'em', '' ], 'default' => $egMapsMapHeight, - ); + ]; // TODO$manipulation = new MapsParamLocation(); // TODO$manipulation->toJSONObj = true; - $params['centre'] = array( - 'type' => 'mapslocation', - 'aliases' => array( 'center' ), + $params['centre'] = [ + 'type' => 'string', + 'aliases' => [ 'center' ], 'default' => false, 'manipulatedefault' => false, - ); + ]; // Give grep a chance to find the usages: // maps-par-mappingservice, maps-par-geoservice, maps-par-width, @@ -120,6 +118,102 @@ public static function getCommonParameters() { $data['message'] = 'maps-par-' . $name; } + return array_merge( $params, self::getEvenMawrCommonParameters() ); + } + + private static function getEvenMawrCommonParameters() { + global $egMapsDefaultTitle, $egMapsDefaultLabel; + + $params = []; + + $params['title'] = [ + 'name' => 'title', + 'default' => $egMapsDefaultTitle, + ]; + + $params['label'] = [ + 'default' => $egMapsDefaultLabel, + 'aliases' => 'text', + ]; + + $params['icon'] = [ + 'default' => '', // TODO: image param + ]; + + $params['visitedicon'] = [ + 'default' => '', //TODO: image param + ]; + + $params['lines'] = [ + 'type' => 'mapsline', + 'default' => [], + 'delimiter' => ';', + 'islist' => true, + ]; + + $params['polygons'] = [ + 'type' => 'mapspolygon', + 'default' => [], + 'delimiter' => ';', + 'islist' => true, + ]; + + $params['circles'] = [ + 'type' => 'mapscircle', + 'default' => [], + 'delimiter' => ';', + 'islist' => true, + ]; + + $params['rectangles'] = [ + 'type' => 'mapsrectangle', + 'default' => [], + 'delimiter' => ';', + 'islist' => true, + ]; + + $params['wmsoverlay'] = [ + 'type' => 'wmsoverlay', + 'default' => false, + 'delimiter' => ' ', + ]; + + $params['maxzoom'] = [ + 'type' => 'integer', + 'default' => false, + 'manipulatedefault' => false, + 'dependencies' => 'minzoom', + ]; + + $params['minzoom'] = [ + 'type' => 'integer', + 'default' => false, + 'manipulatedefault' => false, + 'lowerbound' => 0, + ]; + + $params['copycoords'] = [ + 'type' => 'boolean', + 'default' => false, + ]; + + $params['static'] = [ + 'type' => 'boolean', + 'default' => false, + ]; + + // Give grep a chance to find the usages: + // maps-displaymap-par-title, maps-displaymap-par-label, maps-displaymap-par-icon, + // maps-displaymap-par-visitedicon, aps-displaymap-par-lines, maps-displaymap-par-polygons, + // maps-displaymap-par-circles, maps-displaymap-par-rectangles, maps-displaymap-par-wmsoverlay, + // maps-displaymap-par-maxzoom, maps-displaymap-par-minzoom, maps-displaymap-par-copycoords, + // maps-displaymap-par-static + foreach ( $params as $name => &$param ) { + if ( !array_key_exists( 'message', $param ) ) { + $param['message'] = 'maps-displaymap-par-' . $name; + } + } + return $params; } @@ -140,7 +234,7 @@ public static function getFileUrl( $file ) { $imagePage = new ImagePage( $title ); return $imagePage->getDisplayedFile()->getURL(); } - return ''; + return $file; } /** @@ -155,7 +249,7 @@ public static function getFileUrl( $file ) { */ public static function getBaseMapJSON( $serviceName ) { static $baseInit = false; - static $serviceInit = array(); + static $serviceInit = []; $json = ''; diff --git a/includes/Maps_MappingService.php b/includes/Maps_MappingService.php index 365d6925d..9fbd0659a 100644 --- a/includes/Maps_MappingService.php +++ b/includes/Maps_MappingService.php @@ -9,13 +9,11 @@ * @licence GNU GPL v2+ * @author Jeroen De Dauw < jeroendedauw@gmail.com > */ -abstract class MapsMappingService implements iMappingService { +abstract class MapsMappingService { /** * The internal name of the service. * - * @since 0.6.3 - * * @var string */ protected $serviceName; @@ -23,8 +21,6 @@ abstract class MapsMappingService implements iMappingService { /** * A list of aliases for the internal name. * - * @since 0.6.3 - * * @var array */ protected $aliases; @@ -32,8 +28,6 @@ abstract class MapsMappingService implements iMappingService { /** * A list of features that support the service, used for validation and defaulting. * - * @since 0.6.3 - * * @var array */ protected $features; @@ -41,46 +35,34 @@ abstract class MapsMappingService implements iMappingService { /** * A list of names of resource modules to add. * - * @since 0.7.3 - * * @var array */ - protected $resourceModules = array(); + protected $resourceModules = []; /** * A list of dependencies (header items) that have been added. * - * @since 0.6.3 - * * @var array */ - private $addedDependencies = array(); + private $addedDependencies = []; /** * A list of dependencies (header items) that need to be added. * - * @since 0.6.3 - * * @var array */ - private $dependencies = array(); + private $dependencies = []; /** - * Constructor. Creates a new instance of MapsMappingService. - * - * @since 0.6.3 - * * @param string $serviceName * @param array $aliases */ - public function __construct( $serviceName, array $aliases = array() ) { + public function __construct( $serviceName, array $aliases = [] ) { $this->serviceName = $serviceName; $this->aliases = $aliases; } /** - * @see iMappingService::addParameterInfo - * * @since 0.7 * * @param $parameterInfo array of IParam @@ -89,8 +71,6 @@ public function addParameterInfo( array &$parameterInfo ) { } /** - * @see iMappingService::addFeature - * * @since 0.6.3 */ public function addFeature( $featureName, $handlingClass ) { @@ -98,22 +78,16 @@ public function addFeature( $featureName, $handlingClass ) { } /** - * @see iMappingService::addDependencies - * * @since 0.6.3 */ - public final function addDependencies( &$parserOrOut ) { - $dependencies = $this->getDependencyHtml(); - + public final function addDependencies( $parserOrOut ) { // Only add a head item when there are dependencies. if ( $parserOrOut instanceof Parser ) { - if ( $dependencies ) { - $parserOrOut->getOutput()->addHeadItem( $dependencies ); - } - $parserOrOut->getOutput()->addModules( $this->getResourceModules() ); } elseif ( $parserOrOut instanceof OutputPage ) { + $dependencies = $this->getDependencyHtml(); + if ( $dependencies !== false ) { $parserOrOut->addHeadItem( md5( $dependencies ), $dependencies ); } @@ -130,17 +104,15 @@ public final function addDependencies( &$parserOrOut ) { * @return array */ public function getConfigVariables() { - return array(); + return []; } /** - * @see iMappingService::getDependencyHtml - * * @since 0.6.3 */ public final function getDependencyHtml() { $allDependencies = array_merge( $this->getDependencies(), $this->dependencies ); - $dependencies = array(); + $dependencies = []; // Only add dependnecies that have not yet been added. foreach ( $allDependencies as $dependency ) { @@ -151,7 +123,7 @@ public final function getDependencyHtml() { } // If there are dependencies, put them all together in a string, otherwise return false. - return $dependencies !== array() ? implode( '', $dependencies ) : false; + return $dependencies !== [] ? implode( '', $dependencies ) : false; } /** @@ -162,12 +134,10 @@ public final function getDependencyHtml() { * @return array */ protected function getDependencies() { - return array(); + return []; } /** - * @see iMappingService::getName - * * @since 0.6.3 */ public function getName() { @@ -175,8 +145,6 @@ public function getName() { } /** - * @see iMappingService::getFeature - * * @since 0.6.3 */ public function getFeature( $featureName ) { @@ -184,8 +152,6 @@ public function getFeature( $featureName ) { } /** - * @see iMappingService::getFeatureInstance - * * @since 0.6.6 */ public function getFeatureInstance( $featureName ) { @@ -199,8 +165,6 @@ public function getFeatureInstance( $featureName ) { } /** - * @see iMappingService::getAliases - * * @since 0.6.3 */ public function getAliases() { @@ -208,8 +172,6 @@ public function getAliases() { } /** - * @see iMappingService::hasAlias - * * @since 0.6.3 */ public function hasAlias( $alias ) { @@ -239,21 +201,30 @@ public function addResourceModules( $modules ) { } /** - * @see iMappingService::addDependency - * * @since 0.6.3 + * + * @param $dependencyHtml */ - public final function addDependency( $dependencyHtml ) { + public final function addHtmlDependency( $dependencyHtml ) { $this->dependencies[] = $dependencyHtml; } /** - * @see iMappingService::getEarthZoom - * + * @param array $dependencies + */ + public function addHtmlDependencies(array $dependencies ) { + foreach ( $dependencies as $dependency ) { + $this->addHtmlDependency( $dependency ); + } + } + + /** * @since 1.0 */ public function getEarthZoom() { return 1; } -} \ No newline at end of file + public abstract function getMapId( $increment = true ); + +} diff --git a/includes/Maps_MappingServices.php b/includes/Maps_MappingServices.php index 3d35d65ec..a6ebaa4d6 100644 --- a/includes/Maps_MappingServices.php +++ b/includes/Maps_MappingServices.php @@ -18,7 +18,7 @@ final class MapsMappingServices { * * @var string[] */ - protected static $registeredServices = array(); + protected static $registeredServices = []; /** * Associative with service identifiers as keys containing instances of @@ -30,9 +30,9 @@ final class MapsMappingServices { * * @since 0.6.6 * - * @var iMappingService[] + * @var MapsMappingService[] */ - protected static $services = array(); + protected static $services = []; /** * Registers a service class linked to an identifier. @@ -40,11 +40,11 @@ final class MapsMappingServices { * * @since 0.6.6 * - * @param $serviceIdentifier String: internal service identifier - * @param $serviceClassName String - * @param $features Array + * @param string $serviceIdentifier + * @param string $serviceClassName + * @param string[] $features */ - public static function registerService( $serviceIdentifier, $serviceClassName, array $features = array() ) { + public static function registerService( $serviceIdentifier, $serviceClassName, array $features = [] ) { self::$registeredServices[$serviceIdentifier] = $serviceClassName; foreach( $features as $featureName => $featureClassName ) { @@ -79,16 +79,17 @@ public static function registerServiceFeature( $serviceIdentifier, $featureName, * * @since 0.6.6 * - * @param $serviceIdentifier String: internal service identifier + * @param string $serviceIdentifier * - * @return iMappingService + * @return MapsMappingService + * @throws MWException */ public static function getServiceInstance( $serviceIdentifier ) { if ( !array_key_exists( $serviceIdentifier, self::$services ) ) { if ( array_key_exists( $serviceIdentifier, self::$registeredServices ) ) { $service = new self::$registeredServices[$serviceIdentifier]( $serviceIdentifier ); - if ( $service instanceof iMappingService ) { + if ( $service instanceof MapsMappingService ) { self::$services[$serviceIdentifier] = $service; } else { @@ -114,7 +115,7 @@ public static function getServiceInstance( $serviceIdentifier ) { * @param $service String: service name or alias, does not need to be secure * @param $feature String * - * @return iMappingService + * @return MapsMappingService */ public static function getValidServiceInstance( $service, $feature ) { return self::getServiceInstance( self::getValidServiceName( $service, $feature ) ); @@ -207,7 +208,7 @@ protected static function getMainServiceName( $serviceName ) { public static function getAllServiceValues() { global $egMapsAvailableServices; - $allServiceValues = array(); + $allServiceValues = []; foreach ( $egMapsAvailableServices as $availableService ) { $allServiceValues[] = $availableService; @@ -225,7 +226,7 @@ public static function getAllServiceValues() { * @return array of MappingService */ public static function getAllObjects() { - $objects = array(); + $objects = []; foreach ( self::$registeredServices as $service => $class ) { $objects[] = self::getServiceInstance( $service ); diff --git a/includes/api/ApiGeocode.php b/includes/api/ApiGeocode.php index 067d94b7a..e7cda4b5c 100644 --- a/includes/api/ApiGeocode.php +++ b/includes/api/ApiGeocode.php @@ -24,20 +24,20 @@ public function execute() { global $wgUser; if ( !$wgUser->isAllowed( 'geocode' ) || $wgUser->isBlocked() ) { - $this->dieUsageMsg( array( 'badaccess-groups' ) ); + $this->dieUsageMsg( [ 'badaccess-groups' ] ); } $params = $this->extractRequestParams(); - $results = array(); + $results = []; foreach ( array_unique( $params['locations'] ) as $location ) { $result = \Maps\Geocoders::geocode( $location, $params['service'] ); - $results[$location] = array( + $results[$location] = [ 'count' => $result === false ? 0 : 1, - 'locations' => array() - ); + 'locations' => [] + ]; if ( $result !== false ) { $results[$location]['locations'][] = $result; @@ -54,42 +54,42 @@ public function execute() { } public function getAllowedParams() { - return array( - 'locations' => array( + return [ + 'locations' => [ ApiBase::PARAM_TYPE => 'string', ApiBase::PARAM_REQUIRED => true, ApiBase::PARAM_ISMULTI => true, - ), - 'service' => array( + ], + 'service' => [ ApiBase::PARAM_TYPE => \Maps\Geocoders::getAvailableGeocoders(), - ), - 'props' => array( + ], + 'props' => [ ApiBase::PARAM_ISMULTI => true, - ApiBase::PARAM_TYPE => array( 'lat', 'lon', 'alt' ), + ApiBase::PARAM_TYPE => [ 'lat', 'lon', 'alt' ], ApiBase::PARAM_DFLT => 'lat|lon', - ), - ); + ], + ]; } public function getParamDescription() { - return array( + return [ 'locations' => 'The locations to geocode', 'service' => 'The geocoding service to use', - ); + ]; } public function getDescription() { - return array( + return [ 'API module for geocoding.' - ); + ]; } public function getExamples() { - return array( + return [ 'api.php?action=geocode&locations=new york', 'api.php?action=geocode&locations=new york|brussels|london', 'api.php?action=geocode&locations=new york&service=geonames', - ); + ]; } /** diff --git a/includes/criteria/CriterionIsNonNumeric.php b/includes/criteria/CriterionIsNonNumeric.php deleted file mode 100644 index 9f04d61dd..000000000 --- a/includes/criteria/CriterionIsNonNumeric.php +++ /dev/null @@ -1,46 +0,0 @@ -getOriginalName() )->parse(); - } - - /** - * @see ItemParameterCriterion::getFullListErrorMessage - */ - protected function getFullListErrorMessage( Parameter $parameter ) { - global $wgLang; - return wfMessage( 'validation-error-no-non-numerics', $parameter->getOriginalName() )->parse(); - } -} diff --git a/includes/criteria/CriterionMapLayer.php b/includes/criteria/CriterionMapLayer.php deleted file mode 100644 index c404a617a..000000000 --- a/includes/criteria/CriterionMapLayer.php +++ /dev/null @@ -1,74 +0,0 @@ -groupNameSep = $groupNameSeparator; - } - - /** - * @see ItemParameterCriterion::validate - */ - protected function doValidation( $value, Parameter $parameter, array $parameters ) { - - $parts = explode( $this->groupNameSep, $value, 2 ); - $layerTitle = Title::newFromText( $parts[0], Maps_NS_LAYER ); - - // if page with layer definition doesn't exist: - if ( $layerTitle === null - || $layerTitle->getNamespace() !== Maps_NS_LAYER - || ! $layerTitle->exists() - ) { - return false; - } - - $layerName = count( $parts ) > 1 ? $parts[1] : null; - - $layerPage = new MapsLayerPage( $layerTitle ); - return $layerPage->hasUsableLayer( $layerName ); - } - - /** - * @see ItemParameterCriterion::getItemErrorMessage - */ - protected function getItemErrorMessage( Parameter $parameter ) { - return wfMessage( 'validation-error-invalid-layer', $parameter->getOriginalName() )->parse(); - } - - /** - * @see ItemParameterCriterion::getFullListErrorMessage - */ - protected function getFullListErrorMessage( Parameter $parameter ) { - return wfMessage( 'validation-error-invalid-layers', $parameter->getOriginalName() )->parse(); - } - -} diff --git a/includes/editor/MapEditorHTML.php b/includes/editor/MapEditorHTML.php index 21aa70397..4829b8e64 100644 --- a/includes/editor/MapEditorHTML.php +++ b/includes/editor/MapEditorHTML.php @@ -22,7 +22,7 @@ class MapEditorHtml extends ContextSource{ * @param ContextSource|null $ * @since 2.1 */ - public function __construct( $attribs = array(), ContextSource $contextSource = null ){ + public function __construct( $attribs = [], ContextSource $contextSource = null ){ $this->attribs = $attribs; } diff --git a/includes/ext.maps.common.js b/includes/ext.maps.common.js index f61da8854..4fe9045e5 100644 --- a/includes/ext.maps.common.js +++ b/includes/ext.maps.common.js @@ -14,5 +14,8 @@ window.maps = new ( function( $, mw ) { mw.log( message ); } }; - -} )( jQuery, mediaWiki ); \ No newline at end of file + + this.googlemapsList = []; + this.leafletList = []; + this.openlayersList = []; +} )( jQuery, mediaWiki ); diff --git a/includes/ext.maps.layers.css b/includes/ext.maps.layers.css deleted file mode 100644 index 50b364589..000000000 --- a/includes/ext.maps.layers.css +++ /dev/null @@ -1,70 +0,0 @@ -/** - * Stylesheet for layer pages - * The definitions for the maps layer box is based on Semantic-MediaWikis - * (http://semantic-mediawiki.org) factbox styles. - * - * @since 3.0 - * @ingroup Maps - * - * @licence GNU GPL v3+ - * @author Daniel Werner < daniel.a.r.werner@gmail.com > - */ - -div.mapslayer { - clear: both; - background-color: #F9F9F9; - padding: 5px; - margin-top: 1em; - margin-bottom: 1em; - border: 1px solid #AAAAAA; - font-size: 95%; -} - -div.mapslayererror { - border-color: #cc0000; -} - -div.mapslayer td, div.mapslayer tr, div.mapslayer table { - background-color: #F9F9F9; -} - -span.mapslayerhead { - font-size: 110%; - font-weight: bold; - float: left; -} - -span.mapslayersupports { - font-size: 90%; - margin-left: 0.8em; -} - -table.mapslayertable, table.mapslayerwarntable, table.mapslayererrortable { - border-top: 1px dotted #AAAAAA; - width: 100%; - clear: both; -} - -table.mapslayerwarntable, td.mapslayerpropval .error { - color: #ff7f00; -} - -table.mapslayererrortable { - color: #cc0000; -} - -td.mapslayerpropname { - text-align: right; - vertical-align: middle; - padding-right: 1em; - font-weight: bold; -} - -td.mapslayerpropval { - vertical-align: top; - width: 75%; -} - -td.mapslayerpropval .thumb { - margin: 0; -} diff --git a/includes/geocoders/Maps_GeocoderusGeocoder.php b/includes/geocoders/Maps_GeocoderusGeocoder.php index 825e05e3a..87d5194d8 100644 --- a/includes/geocoders/Maps_GeocoderusGeocoder.php +++ b/includes/geocoders/Maps_GeocoderusGeocoder.php @@ -38,7 +38,7 @@ protected function getRequestUrl( $address ) { * * @since 3.0 * - * @param string $address + * @param string $response * * @return array */ @@ -49,9 +49,9 @@ protected function parseResponse( $response ) { // In case one of the values is not found, return false. if ( !$lon || !$lat ) return false; - return array( + return [ 'lat' => (float)$lat, 'lon' => (float)$lon - ); + ]; } } diff --git a/includes/geocoders/Maps_GeonamesGeocoder.php b/includes/geocoders/Maps_GeonamesGeocoder.php index 5fd8b85a5..c64608c00 100644 --- a/includes/geocoders/Maps_GeonamesGeocoder.php +++ b/includes/geocoders/Maps_GeonamesGeocoder.php @@ -47,7 +47,7 @@ protected function getRequestUrl( $address ) { * * @since 1.0 * - * @param string $address + * @param string $response * * @return array */ @@ -58,10 +58,10 @@ protected function parseResponse( $response ) { // In case one of the values is not found, return false. if ( !$lon || !$lat ) return false; - return array( + return [ 'lat' => (float)$lat, 'lon' => (float)$lon - ); + ]; } } \ No newline at end of file diff --git a/includes/geocoders/Maps_GoogleGeocoder.php b/includes/geocoders/Maps_GoogleGeocoder.php index d73347a19..456436de3 100644 --- a/includes/geocoders/Maps_GoogleGeocoder.php +++ b/includes/geocoders/Maps_GoogleGeocoder.php @@ -35,7 +35,17 @@ public static function register() { * @return string */ protected function getRequestUrl( $address ) { - return 'http://maps.googleapis.com/maps/api/geocode/xml?address=' . urlencode( $address ) . '&sensor=false'; + $urlArgs = [ + 'address' => $address + ]; + if ( $GLOBALS['egMapsGMaps3ApiKey'] !== '' ) { + $urlArgs['key'] = $GLOBALS['egMapsGMaps3ApiKey']; + } + if ( $GLOBALS['egMapsGMaps3ApiVersion'] !== '' ) { + $urlArgs['v'] = $GLOBALS['egMapsGMaps3ApiVersion']; + } + + return 'https://maps.googleapis.com/maps/api/geocode/xml?' . wfArrayToCgi($urlArgs); } /** @@ -43,7 +53,7 @@ protected function getRequestUrl( $address ) { * * @since 0.7 * - * @param string $address + * @param string $response * * @return array */ @@ -54,10 +64,10 @@ protected function parseResponse( $response ) { // In case on of the values is not found, return false. if ( !$lon || !$lat ) return false; - return array( + return [ 'lat' => (float)$lat, 'lon' => (float)$lon - ); + ]; } /** @@ -68,7 +78,7 @@ protected function parseResponse( $response ) { * @return array */ public static function getOverrides() { - return array( 'googlemaps3' ); + return [ 'googlemaps3' ]; } -} \ No newline at end of file +} diff --git a/includes/geocoders/Maps_OldGeocoderAdapter.php b/includes/geocoders/Maps_OldGeocoderAdapter.php new file mode 100644 index 000000000..1b29ffa74 --- /dev/null +++ b/includes/geocoders/Maps_OldGeocoderAdapter.php @@ -0,0 +1,45 @@ + + */ +final class MapsOldGeocoderAdapter extends \Maps\Geocoder { + + private $geocoder; + + /** + * @param Geocoder $geocoder + * @param string $identifier + */ + public function __construct( Geocoder $geocoder, $identifier ) { + $this->geocoder = $geocoder; + + parent::__construct( $identifier ); + } + + public function geocode( $address ) { + $result = $this->geocoder->geocode( $address ); + + if ( $result === null ) { + return false; + } + + return [ + 'lat' => $result->getLatitude(), + 'lon' => $result->getLongitude(), + ]; + } + + protected function getRequestUrl( $address ) {} + + protected function parseResponse( $response ) {} + +} diff --git a/includes/iMappingService.php b/includes/iMappingService.php deleted file mode 100644 index d73fc7455..000000000 --- a/includes/iMappingService.php +++ /dev/null @@ -1,125 +0,0 @@ - - */ -interface iMappingService { - - /** - * Returns the internal name of the service. - * - * @since 0.6.5 - * - * @return string - */ - public function getName(); - - /** - * Adds the dependencies to the parser output as head items. - * - * @since 0.6.3 - * - * @param mixed $parserOrOut - */ - public function addDependencies( &$parserOrOut ); - - /** - * Adds service-specific parameter definitions to the provided parameter list. - * - * @since 0.7 - * - * @param array $parameterInfo - * - * @return array - */ - public function addParameterInfo( array &$parameterInfo ); - - /** - * Adds a feature to this service. This is to indicate this service has support for this feature. - * - * @since 0.6.5 - * - * @param string $featureName - * @param string $handlingClass - */ - public function addFeature( $featureName, $handlingClass ); - - /** - * Returns the name of the class that handles the provided feature in this service, or false if there is none. - * - * @since 0.6.5 - * - * @param string $featureName. - * - * @return mixed String or false - */ - public function getFeature( $featureName ); - - /** - * Returns an instance of the class handling the provided feature with this service, or false if there is none. - * - * @since 0.6.6 - * - * @param string $featureName. - * - * @return object or false - */ - public function getFeatureInstance( $featureName ); - - /** - * Returns a list of aliases. - * - * @since 0.6.5 - * - * @return array - */ - public function getAliases(); - - /** - * Returns if the service has a certain alias or not. - * - * @since 0.6.5 - * - * @param string $alias - * - * @return boolean - */ - public function hasAlias( $alias ); - - /** - * Returns the default zoomlevel for the mapping service. - * - * @since 0.6.5 - * - * @return integer - */ - public function getDefaultZoom(); - - /** - * Returns the zoomlevel that shows the whole earth for the mapping service. - * - * @since 1.0 - * - * @return integer - */ - public function getEarthZoom(); - - /** - * Returns a string that can be used as an unique ID for the map html element. - * Increments the number by default, providing false for $increment will get - * you the same ID as on the last request. - * - * @since 0.6.5 - * - * @param boolean $increment - * - * @return string - */ - public function getMapId( $increment = true ); - -} \ No newline at end of file diff --git a/includes/images/m1.png b/includes/images/m1.png new file mode 100644 index 0000000000000000000000000000000000000000..329ff524c59e4ac6ba91db135e2257106253d7b9 GIT binary patch literal 3003 zcmV;s3qPx#24YJ`L;(K){{a7>y{D4^000SaNLh0L01FcU01FcV0GgZ_00007bV*G`2iXJ| z2r3bh0E3kP01G=wL_t(&-o2Vzj9q7S$AABQX2xTW$M(c=Vw^w|nxv@{1u$6Lp-Ky> z+Heu!fwodn5%56Z1ca1j>?!8rnn2T)Z6sCkHxDm5yJkb+93CZRwCC@~?$Zb;HN z#N*iG8IR|F&i?bT*WTxwiOs&+2FL&b4WNMy(g=2e z)B%iLzLtLldDgqXsSQnG{dczt>WP%^M}bkKp)e#v%EEwnng|2I7E*!TMe1U&AT7|; z%0|~!>+(cXrBNWHF<@sH0k)CKac$Snh>vOm;ljUz72qteR4-cp0&c2NJ%ci&o!C3V z8w^8GT>98JxyH7vE}aBCIYk#+-Fgi;k=l}C&{2h1aN zE;^C6!QO_n1ABXKMu~CdSOwB)n(7Q{nAYSS*aK__iu(T2J00u=qie}4FNogkQ4Ay z?5luB!h8GTc~dmi*=YC?n{x;DB5*5kT_0E!VZ zM}Q}>hk=`ru8y&tB+K9Uhx8EkKBRvNuZ$%7r%B_!>|ozS zo;|asXjw&*%DQHEpjekyu@qEigr*iRTE3S99svGj)Anb%D8opf2i`x>qPmW>k>IH- zqU|hrRU>{h7Z3OgU@p+?ccCS?_GGyjpFnDFx_DFZ#gb>PHmnkMN@9C0O+7{1`+{bH zv?$tcMERgdelG^)c}+0#A*B0(_eA0MH1H(SaqK6M4sNnRRT$q(Q-2>`7z6$^82?O^ zI=iC2dmswS9g!xAO3KQTO}>nDRr3BUO|4+x2D}F8y-0tFy&E{V>0av9Vt*F~eF69? z(no>6#$Jl@zl*ejeHC`6mKGq(fZ?P7>xQdGlE+lxcufMoPXm9;R$W#8Y8AI2HKKkh zkZwUbSM$r#kg;I>bsEVM$%>?q8w)fmnL^uR*bVH84xL5%GbFauuyU_YMKz#!{&*5a z4z1DYT(XM{=)~AAupg<2SYHB8VNdie2wN12lYPiKbV2k>=8|0&h01}?B?Auw<55Sk z#g~KCyASp+=Gic6wQ{Np6mi?gv&ke>GhkaNJ6mj$3*Z`{sOzt$POnmZtNpjiQ{|1T zJy*h}!c#lqyE8F}frHrh#)5cDJXB8d2-3FjPKGp1+oQ?iE5w}!eMWSV6q6OLVhET? z+&LbvM`I8p-3T0uB2io5AtC)XRI1Tf=mzju%%@uX4Cz#Ci3&)_!nK{4oFkE)NhIYF zTEztR(OL?I8-ab;-`+yx`v~@-$Vm>B_h-P~B$pX`7HPQF@ulS~;INwvxgC2x#7f0? zHvmu6pOq5x`+@HQOSPoDd7e5#Q$I%fo205fL7qJaT%8y`MV=Mmsp{V4jHZm;tt~HO zxPyYsmV531(&>P5qMvjFFpd2{qS|i8Ug{z>X%%k+u8eUr;77EIUBKQX(H^H&4A(id zG<30o^rZwYjlB(fvWE0BM6U(zLmEwx<{yB&fS+u5Z}nKs*Rg+tbXREXAkASvn#gmC z*GZ)Dh&Bb&=u}3{C%|A}2XHKuoyvnRBhMbg{&k|sT^D-9U&ZI?1lNXi$x%afY(paq-bt&t75HbQ*}8=zy&CvO z;CAf)NSJ^J@J`we`YiA@?2pwvv*dlhqp5qbZ>!Uf)WCiU7_Zq+Io2Y1MwZkn(@6pr za0T!T(%E=lAiX>k)bA&BWf?dW8pZpt?+hT(MU!){R|W7Z-~je_kvRy4&XgVO9?P6V1Fx`a<5Gk-VV}(*gI>juw0|Fl`O5k z@_Yn)Z}RY}ZF30wn!3YLhU$MJ9mf7p7*naU0!{(X0{eL0qn$ZZg1APjVv7ZIL5#fI*SVgrMFOX-C()LUG_~{w)Z1H@S-mHo3 zR2<-_XvZjR@4|j45^c3t3gFGy+mSwveIIZR7>$j4GL88Xip1}D<|KLcD6Qhk1YCr@lp1t?=}{?0_L;uTyfZl1kC7H?gUe948T(DZ!$^6^ z$aBE|)70`MsL*f2+b;ku^6WDq^4y7ZZD03f8tLb>y)RDA5Mka0ruubDT|&WXB}MC0u+($&#Ss~+44#&IkNi&sdbt z=>X4>XFrHA-k&tatNmOW)FTe-I2=-f!4*`ezRFN^Alyu7a4(JK+bH%=$+P1Fj6DI| z9(!X6I10QFIcZP5e|il$M^btxgXrsnJ-IpxmuKDB2UEcXRr4;#0V)_7SY;}`xf6RFdpKDr z)m?de5@~i&gErRlEhjY=q;j~9`M&HvfMM0)tn?Jx64HF#by;IaaqYdwvWZ=Ja4z=R xi_!k+Bn*4dYdJi$9NNgq0E6-n$U0i2e*pgzX-8pBlM(;`002ovPDHLkV1iGktbG6g literal 0 HcmV?d00001 diff --git a/includes/images/m2.png b/includes/images/m2.png new file mode 100644 index 0000000000000000000000000000000000000000..b999cbcf69441f194768157d79a113b0fae03e3c GIT binary patch literal 3259 zcmV;s3`FyZP)Px#24YJ`L;(K){{a7>y{D4^000SaNLh0L01FcU01FcV0GgZ_00007bV*G`2iXJ| z2q`s%E;NS#01P`xL_t(&-o=_*Y^7&)hM)KM_F)`*4xUNE_0hLw-_KzSa zVYuKVxBwCf3AG4Ss^F#)>P1gdE2Q>!-(Glqdp_^j9((Lbf63a`KK%c;zV)tmt@W-?T!O1=fP`!muQfmteL&4- zS)#J3Bsw;hu#Jmyf2w7w)5lOMYu^nFc?Pk1AP*G_W7?!5QeY3vGQ3*q75tq z%Qmf|q`ecHt?hUz+wEbS%Ya(CZy1t{TK73Nr0<5&25hqo%oZiB?RZq%@ScDnU=L8N zWlXmCrK~UBG2FgK5XJg)9DV0ZRe9woR3s z4;1088qjAmATa>+r_IQ`nG9XDnJr3|-CWs_grIF3*kjz7%}^FoibYAH1I*ei6eUy* z`hoqxUWt(ui{Y^fn3b5cnJP-Es(~a%WAl{Vp9}%>Maj&Lc`zAQHKqIa)e3N$qno~BXB*uXSdj%y~TsvFyqQtc7R2PD*HbW#;gF#?l zK+CbX0%%E0*%T5t*t`nZ8~3fW34A2)XU4W|CV}5eoDA6RjpsRAb6a9UbvilWZP|n2 zxhpfTIY?!uD4BJW>uqj{l%O86=`JY6X7|Av`DEm>+IF)j@l#;hX1~;p`Sjh&X1plz zMR_o}-%WM{BN<6d22|f5anPm{p3Nb*XtN+OVKXCfCLos#>x8%WNQ?oyfL)RNbITr~vpHLooZD6m_8J=3L7O`gv=UiJ%mK$# zXWC8v#OA%g?hLkh^6!PHfZy4CvnYAeO|G#y90ZdXIuT6+SV%;5p;V!*RxVY8TO4B`8?1CQ(EhXh*DF8I0Km+F8ykfMtoNZN3T& zOT0M>hDTLrd?VJ}cBz(m^7RtG1&-L7gcZbHM#AK&`ia zwV49`Av`xyl$?4+YSCn*n_Lh4WmZQPVc?Sz57?Xn?g;4q0k9jm8Tg6B0`Q2<4<()g zZU_F@=7#XHqa&FC{-P-H#T4AzDYjFxKWcL{*mNHFGVl`cd5ODiUY*6-syrC)ElK*%XSw}>{Z-m%SK-%aW^jdg0uiwf~r%b}v=#KlS|0E*^uA21}*iol)& z28)uV3*Dee?vy-n_ga3Hc4f9+b(dM)rj2oy!hT-D&MRf7)j zlEf+1nai5q+|bVgySEy1n}qG%Hn*wHvGoj#_^$;qodL!|=qiB4WLV#-(rZO;8H+M( zpUqWOgX0o!w|T^7+)dii!rYy!WIO4^DBnH<+?%DQ8qlXYOI3rLfvY7(Bi($@rZ0X6 zFi=Y^N%U5dSO%_0ZA-+}1aPg*yMd$8Hu^0iU=HK;f>LCtXgO9dWy}^b=t9v zzNh16gZ8DjF2bN4{Uw=n()+!aiCx7y2GO(dcI3vCgs+R=&hQFdA&>NDZ!NDC8sUuY zRm;n*jBFNq=z5pUe1d2|<^b^grG{v1^s}MK)?%~!fM-K@C98dx*NVaRs>)`CRhKde zo=~RGWOUrIc?0l3c>7WTHPQY3zztdFF&QapPvVOxHJ9wwVN(Q_ongHn7!Q>!$q!?p zyCkKn9bNd-5|fvLMT4y+@ucd^0XHQ@&St`9G166H)(V*Koop3xqjq-Vn(8dXR3~Yi zBfxm%m;}+A!r%ulNq$gwp?_U9xCyu>^XLl_wU}Wf)vXmoRAj1MtB`PfkW$1z7&;#D zmTJ_&P?`I(vDTfyzs79hQo!_S;C|p{{ z3`*?~ijD$zNj#irdXrU-vjU?6z8H$!M}ap+SEEP)|KcVGZLW?Gk;HU#H+wlh-#4&S zlq=0`a_LN#3vIK@X2#~l#7tq#5uj=F3yHyiPAlZT_u0G)_=L@5CJlO{R1h)!Gl^2- zuYgb6yfemUL~(zAR8wz?J~eI537bBNfz)(oUC;TlM)eh4GgStW2S=_l%h?&RktV!+^<@xzVt*0%jfO(sd>_+=T zmpNf`V|ce^bF0m$#8+%ei4R3PQ%Nk_+$?bm@GaH(AK-u2P{D2l{yd)7g;hYVWAinM zM{WKn^{5Hbz_AF8EAoR15@*+JmsIZxNwZl3b?nfX>Kq47r}=|K74pIDz;nM z2}fd}YOuQ~`B5lW3Eq}C0Ng1tC-HgUzhhQd#ow>i(=dyYP9*4|I63i@&3{PzO~mj! z0^Z5^0yqi$61Y4I8O7vxHgnY4QbRU58jh0{&J?!=;H=G?fa~I1On$)NY_w3PVu9Bd zC68<*9#;+CBC*Tn35nULn63{ptFllL@N?jKV1LAJ9;;`Hl2)%ystXJ{_k_5Uj1+KA zbtc{9CY#r-HAIQ;KPT~o>MNUYf%*Sl;QBa<(t8LY9T|BHc){j!iCR`<6Z86oHI?lP z3|1tuFHS&I*{r=4d2Wx*>*Mf1)iWuoNql1?1GCM%E84V9f_~J&RUA?{9!4I>DwwpE z8P%CvZ%9*Yw7W%#Lo%Gt6}UKCc10xq9|I3Z67J7fdE8z(q_c_P&5J#oGXR{CcqsDZ zP;6q69bD)GrlVI}cLKGpfyvbpsLt77S0~jim~>R)fX$TZG;JQTIU70Zv~=OoS3rLv zEBhuSzE_ky3@idy2Gf$CxUiWhO6IfEA3JggVL0T&B=zTqf0lsxnCT4xouXuUllR>f zaG#b~w7E=TIFRCOc9@_=WWsY|Px#24YJ`L;(K){{a7>y{D4^000SaNLh0L01FcU01FcV0GgZ_00007bV*G`2iXJ| z2r4YfXwQTI01os?L_t(|+QppRkDT{a$3N$p**Dv}w&S-XPE*@SlQd3B1ag};2>KdS z3R0`Kf(8}!h8x;)!zEG$5*PdhhzklW6{ScNDMCV2qzDpHq7qaJPU0kXV!KJ~#36p$ z*O{H?aB;rBea<`{$GhuYdn9XiXJ?-0_dCCHzUOpUNOgc;$jOTr?a^X%GF|X4NdJap#A~2|uv$fZ8(>%hV156SpKOs+WZTyP; zmlwRXm{ITd!Wnvc^p@M2^E6wi|3Ir=7h$o&2K1tc4qw@Y(LLgYcrs6h7qw z3&5Pi1%pclwTv?fZ1s5d>~U!ywZmKmm)D9LwpuT+VKBUf0KwwE)(VGt;F3^&o5OyC zy&lG^lDlJ9$Blvo7l1c_GxDeoFk>*4LDhy2V$s5ozFLFW8oglL;xxMj#PVqr56~ud z0e2J6c$4;48^&dmW?}r=;4R>U!+9BeTA9h(I0E!gsI0~qw%U-OWrWsgEFpJvxD~j| zV24PNd$Yi71hZD+@D@~LJfPtv9pG)?6@xcL#-H+O z5~xb2_K0%5B^NKJaU6I_?*3teUjlZCes_fav@oI=bD6-G8hG8|-+&*hSi8^RVDh|t z&pC%5iHdu*h7`JHLX`}1zeQH?#2rlF^sMObqYj^z2YHKZdGey1o--EYQ@;#6Zt$cB z5dS=|tr#R>)-i+Q^=1;P5ux%R;j(4XCxDj>4mmt#a6mPIw@y4W&OypR-4?AT5iaux z#TOmEYjD=#FmOk@)^USl>nXt2U27x|eSnDf7d-O(CxI_0WesR*j7qDW6^LC1=7`wt zS%lhc4pRc<8D)YxojMkwwrub{ho@!iL%^#QytLjbbR){}iU80FgI@ zEidR2IsOTU9}^V&QHKu!cMu!grj5R#!tAU^mU-IY34!K^9X=tiS@uTdS_J;g;OI>k zW)fbEJABsQ<1$9q;ip7A&j4R{c+}va2Y3!uOe6G(Sqsd09Qaci^~(fj>8bqw2ZJA4 zW1O4&@Zq0-vxdK{;q!GECgz%35(?jh^8iMcc1!=mC3dbjsn58uoeA1Kafa zmU={LuCH=&heI{IQg4C*T840kf0y=g&4a2(@4NC)+qc#*R%sIxdVhzeAAtphe%RrD zhkdnr6jjRriA#J9xO20kR`)&P@I|gdqVnd&zPfdzmQAYw+#xc!rEvQ$!+E@?5A4x);nBzx*dp5B>If}>qq6)t=RU;EV z;BcqGQy#?Y?o{&krJVO$7aQx^a7U@s(#tM4N~~|uqYl`wHeSa(J;ZI|;EThqYCtrmP;z+DylWZ@groAl+6hti{ag z{RgcnuQ&Z+P4DEeYLLFgzhy$vy4}Kq2dz*YC*Ct8=vGMsET`{Pgu%|1r}Wi8naqyj zaq}?`&GKf@`}-vEEhhEawWivKH*9JWUD0=~u9jo%%B-_2w4Ep?q89x-@~Y}4*a4k* zN17$cQ>O``xo*`$KFVd+vUs;htDVFfYQ@|5h!DGx#y}#^s5b4W!3%Gr`C4VYIfX)b zp(Ay9K>&5Ls$VzQVJ!g6&FE41@~$3{$dR?MV@wKC&xgBFr3rJx^#E&h(Ar^NR>M6) z_F_t%D?Qaw*Q%_=5Jf#reT96}aW!k^8CIjW3N&BzCO&V77wAU+rKVH2MPLSM6n9(U zeL1V=*(3U?G2=9YUM-1%31S%Jw1`Q$=@g+ppX3In&$1bV7u3+n#o}dypJ~0F)`FJQ zYw23kidbpU*D0o)NImC#ILl1R@~@`pH}u*ej|R*;egmX}g#5307~=hw$^`Xd-}DfM z$C9U(Hf1+8hRkAcrKv44>8K(%_q<_ol`MbS8(=J+a+sjnM{R?&(AAJBhbIhP)%`n( z+ssbP12vzx5?jgPIf)~ zB7vG&h(ezMo&mn%F-THd(^`DiVx;Jvmg^hvd>z==E%;Uj-;5A#R|Y^!JFOT;Ma!HA zcjzRdvR}Hy_vEIfEbmF2`F-G@fiF0`-k!W$s{!q*82QWPOJRwb<_{Zx;r)4DBUeA}X_{6HxKw2z*_F`B{E%!=ogT~muCo68WmSs?MVz$Xnpr_@{G)+CIaa(G(O*&Yj__aTFaTr(7UbQJ$TC1U-8-uJN7 z@ea+xI|QzM-(o!Ah{L|LwyqB0S*6!)3{Tt50+-S_RFgLJ;-vUOl?=2j`n;DAOUGOG zJ6XZe8a`$rv3LB+A@ZJ>>ho1JamT!I)yxx}UDF2tOJoA~Cch5|wEhS&=T^Wv;6;JwTf(RV z7K5w3RDe|yaZeF*T3b|k7BQxz?Mqd(AFkNAt)f%DUB;+~g)A;9e;*>cNn6SWb)(WM zI8UJWS-Isk=b|<4JR^P7)T7u#;8RsHO=?%pIlNrKNso!VX@+s7Qvc=R)rL^*T0J!_ zYNsaP>Xe6FIsBq@io*(xfen9or4&8`i>cTPt7Mjw9!1p@ac@g1T3JI^zdaY67Tqzu+jV^q=&wveX% zX@=4J;w7gf74AvwHhaPx@l7DF-{s`HtLd~F3Is=s!*~_}V;yzJg2DW1reo{j`G(shv7Hk@OsbtX-s)zRHL;4CM|OH zc&jd1=`u;}UeM|E*K;rJbV73tlSNNp*!Tq(dKLv>Z>>Wb>r$K3iB-TB4Uc!z6uvXe z?H7o6_q7_Mr0>KDh9><}M}AHsi`VPuXLi?Ph?I+K<>w`9zL(iai!PM6w;n-PVdbqj zFSOhA1(#XLjLT~Yg$*%8%NQ8+Ykf6Yt$=Mxt3}xDuhmsk#w!0yK$XI-UoeRDzXx2; zywbW2LDut|QyA1?jCmPitNygS);LU?>nqc^hC$}a5PH?>|6fm6>Hh&6nXpifC1hg& O0000KLZ*U+=)p!fv7f#TG` zAxLl%!EgG`&*5<32cu%worY0{L9A7~=}6b}76Isk~1IN~P)K3@?4 z&zpALY4A7Z!>t0&I7qECf*j`WHIYAjW_h^ivJu4lvb8y9VL`DD`rG0ZKvcZ$L@8 zo)6*!Fng}&gE0q~LGCMnsiR8`P)pL0I_sTkS+y)n+TA3G@GlDCw3FVN#n+KY{!yU zS&J#rq84&EoSA$2F#kgyUX3UYMT+SK2vM9n_kQ31JL~_P@3WGNtmiTe$FKX_peydn z->Lo%)PMpo{aZL*2YQBe0oP9tDD!h~G@d*r$8R|1_-vS~&emK7Fak6*hPnW=1c;@H z`-*kw|8(`!(NFJoPT+O{R~XiCykS@)q|307tRk;?`rViQv=TUTydlXefzt7wVI9W< z$nyUyaD@PB7#7l1OVZ`(r>v@U0?Y?UT-;Er%%LmE>!hFVW`J1VM8HOqI@TOF!`$%_ za0^WYOTRCHBoIph87ge?d#JFU7x&bFx?v-Z2Y_w;^fnT}jRY=i)VM%v>i?zVAyomM z$soHvbHhwdpckL%yXJV27Nz012Y~Tj$IFU6EMgpvF9Hkt=~X0nOMx>1H5say0EtNk zSYgk4K-;iIU{MmXLM^MFP~R&It2sUbjK~bsWh-5PXX&+c&p2=uX#F$*R|6&u8%;(& zO9YOGN_HKe2j+opvSp*d6fmLR8x^%O*{T*W@Ax&t<{Y0BsCC1}4I2}nUh%RXA&y~F zjyHihsq&TpH>$~wgbkBqS@NcC*n$SymU@l@2Z8O5j~g~3uUk*?oL6Eiml{VHvZ!ay z0v8>>V%QZ){)Ay;6)zm>U(;uW7IEQr0%yReVcX;Y2qT?huu{^|k~$W^USOwTJArME zk4cRHN=?jf6vRquEd|MohRp+49KQ_A8g|L?SxM)ZY+TK-G8~Sc5#X7?^UVgX&`Y=J z`%AHw22o0~7J!c9b;tJ`whPz~?2uF~#=Yse*~tu=NLl%}CcC;zs(Bf>09=$LUeoi( zr2d7z>&Y(H_4n2=Ue|_9xkA#WG-$5?R|>F}{=U=kBfvq~pRPP-Db+4K`vO8-S4f^~ zL5dW=SxTh>h;SNB+0b)_odzz+D~G4ArBJ2_+Y4OL_tpl4Gc&BTO-Rj(v_d9hKL;#0 zz704E95!qM=nB;Ewg|L{$rgcj3L837r<^&A$nZA=z(|BQS_qOuDm1=%!_ENj%b|>D zY>n{v;kX3c+*+m1){*4b3=8{I(@WdHtm7TSjskZ&J{7AQph-ePH5LrJ2+TNs*|2#T zYu9&nDZ#Hfenk$!lINW=Y&WpS@iE!1ZY)f)gI%fZ8Q@)wd54}+DukJL{Ms=2?s|Yj zli4Oaw*XucNXH#N2y9ax8j-z4j_UXf@Qz_;C7qMNe#7=Tz6Tf=$m0T`2Fw}O1Lhn* zC&@l5DQN69S8P0zFT&4D!g<~?2~%81$@h}OTY(?p90>Iq#gwBaD1=q z*kT0TvcZdnO*{T3aMiG;<88xwK4da+D{)-JQ%&G*;J8#ajCH8-e5i?3;U3^4!2Q6T zvNxpfdDW{X+t?JiOW_Fc9`Iw}B=EL8>QUehsdeP2rbW41&wh0nXRsNWL&Qm~WXEb6 z>}3IV2G|4qg5!?@hoq)$!`kV4IbO>Iqf6~qP)<_Cdky<2FbzBhykOXyvRTK0?J^NP zy<ZwDiiSd$!z zgfs_JG9p`Ll@u<2ui^NJVP_qG+OQY3sQ1bC6?)zo;0<}u8#2SIG25#Gx0>A>035Ox zClrF50e<56cHnW~a|$ua1V-eJCMlSc@t>E`zo_ptfZdMoko}x+d_r-d%kXDASd;yI z4tUD(ONQO=_#wr1Cj+##05^zIUpH{8Tw`+eErlh&2z*>@Z712UQAy7<@Fwu8<7Wk) ziz#j@q`53F*wts-fCGkoL=I`Mq_35N<#^vKz;|RWrhp4d(O3&`tJP430`6uao$S+5 z;MajqDO+gkd!s;868T-nUy@WEa{MmffYPTOhHZCzpJCIol~J>Yo(aRT@_@M+*@L;`G6 zeAP(L49+f8_zmDif${_3bxHSwiUY&w&nb6pNn$sSwH;RCZl=QNp1r_hhCM3k=#t~l z%k%9p>D6 z0)MN_;i`DL)d8qT_m%@!c~K4cbHl!@u%H;W za=r%XbE)K$z@JKW*DPtn0#|IP!5sXe8t_|&eZ}!3saV4XzuQEVeg*hVdGJjw8s`bt zHu(7?z!zoH_H!F9u`R!+aHzi7zy(2>D@_~m(oX55brR`)cNq3* z;Mr778+45WJH-k|as8@NJPfOgat~^&9_GQ*~gEfu0=J=h;Xo^IVHWH%~})oZpxiFBFDe~W58xZAKpVp|Uyb`02O*xQaD7rSfg2yQn@>VkznD&q7s@I{%O zNlDm@a^GpGVI!3&>mos~M6o?NxT1ao?-_LSXbMqGe_v4F#%_7wXrYSgQA2?4TqiZP z(bqP^jye8{^4PnQ+SbLuye*quCOcS@3eU$nBQ4Ng{`!^lo^MK4Q{ZOEO#LFewu)j z%^sDsWmS%TJQQjTvXxe`e=C8jc6~=TPi56=@<361j#aBGwku^j%(hr#Wo`-;e|m}p zMm27eYF-n?zoHN>EQ+SFI|j3<}|N+ zY85;pdm8BB3S|j-g-lXqY7OnAm9VsDVXl&|np#vFj-PgXMxyUqq?)ex&jLSIEw+|a z(hcj-@9G>^=#f>VRJk%z((AlxH^57iT0+$RsC(s%ZjwL}^R614M7Kk(;UYfA91{pq(;ztB|1k_%Lay((MsB_B;rfe zaCJ+PB(e@nIet+M%eiRMq~C?~p5s4I<21KBu1f*yjN>mDb{@D-@#m6#bNs5bV?sH3 zfVz}kvfN-DrpkeC7{hMGV4?>RhTgDR@{+ss(DRO8t88LBCY61Oerv<+&R4U|e%-L| zD<3_qM($Y11;Z{nzMy=zo`j|=8}3T{b2#FTcZy&_xW1!1?7^(#t(1DX68H)FeIKXg z;-eRL2k{MI}t>gv3QB zQI$F#5j!v^Pa6?-6F6$v8N+_U^>xmNwA1BD!@g^`TT}bjx9Jz8qraq$0G;%vv{&!fvzh>CG8la1H*ati&p6idp z!`{Zk{<7nL;rL0zz5wh~-dPI34}o{Z=<{W=Og_;+EKawq3ksEGC+@{HW&9LXyZt*F-J6ga- z4SUJ3{}I8tTVT0Dk2wKLA~HP9y93MH8F7439~3E}t%+Ls(D7*nw z`h13d*JcnIQBJ5yV%yb9er$~OO3pg27f&k+54wHPu)Shbrw#jOrD4%Dz2Nvx;Fo|$ zfWHF1>iEgTvf6s$ybM-)#{0nUJN|XYk2wCzh8;3&L7&$Y=syj-CTjlx{mm!`_n@L7Br_p;?BF zrV!1fqV2H%RX#5EvDcG53hw=i8k6^H!a*O6s?ya`{c4V&}aW;*gL8& zeNH9DBLe`r*>#?Nd;YV)-zYSCSfK4!bK#=K(F0z0{3+S#k1FAB$syDgub$T5b_wWS z8rNbjF;@3-#n$lc*}LlRSEb)~8Fp{V_aX}a(6AplzQ?dHI{rzi^xckAzEDg#bC{(btpX1+wf zcchc{a>f4oYlgk9d*75jIV2!IY1mN(p(O8i7WlsEqaPS{S+&rgH|%~%buU$_qHW=n z+EG1dFPCW|lPRfUVDM{Kv-5W7m2*hjjTlvR;iR2tj^S&A>*lINg7s9!LIr=J^@+{}HGO-7~ zBXH~VcMcYkjBiL%ekIW~q25iFd)Ou&{}0)v)dLrYmWfN~uaPBjb9|I}-Ujbx*04tSdyR z13v-&!SN5&n|qh;?WC>fHTrF+?KOlUU8(sVwdQwInX@6gHtqPczzgz%Cqz6TK*Hgb zX+vD0dDYV%`&H@RV%kk5) zS1kqKXXtlKz3lkBGJ^ZmX%)u=6)D7+*0^Wok()`YBA{NRzfX2xaK4s}zM~2lo9QG4 zlL4OBN;f1)ccc|vYF<%5JtIcqj0S(iuoI3?8TPO8S~sk?rE5F=%^>>)z-tOmN_ot~ zh8;*Yb*W;dN>|WHR}(#!k1Z->*DkZXS#Mpy1$f&s%cO-+1SwlUhdCQ?A2om5X5()`Wx=e4##grO{SNJ}j-B)R2bo__>@%f5dF@`NtRCKRKDV0Rxp82_8IVR-Vud0|cEe7R)80Bb2UNG#Ea#_Dg{ouOlyf*Oele9+{ z)n#Qj(~`0U+2`n-_q40pQnoV`XzKwkKnj~QA!cEz3KXhHzY%#slX+M4`d)GTdmV4< zypi5ICGM&gvCw$pNR2aT47+5TBV`OyGE%9IBzNd`{-Mqt8L$m<=`=5v4jME zwhV4i*rPJ&tDdHHld|Ht!j4kvIIs6cr$MH@z0y_SlG-^ruYr5nHWRq8D`TRBa!b2c zCok!WPNyC>IX&WO@;59qqG#5nh~VDoX<-+F+%)on-7(Nij69#iSCky=B8 zWsTEU>yprcIpC1*j;&P9^!z0tB7PJZ&^2s9pxyHEw;Q!dETpF^rX&uH%APlPyEp)J zBZ{pN*3@I6Qmz>tMm+`C!Qgf_V!|7}yDnM>79<5tQB(D70~1Amt5=!w&?Uh1jyTn@ zEmCwt4i()j;6jDEqJUZga7^!t{*&%nRAmjeCf?J~q69eIbdFuQ8NDl;ee!e!s;$K3 vjj+ZudDk%EUh0hRsTA1SD0N)z`o94Hp>J(tm=v(I00000NkvXXu0mjfCr9_Z literal 0 HcmV?d00001 diff --git a/includes/images/m5.png b/includes/images/m5.png new file mode 100644 index 0000000000000000000000000000000000000000..61387d2ab5c8d22efef4846617567a3b1ae6d72e GIT binary patch literal 6839 zcmV;o8c5}dP)KLZ*U+=)p!fv7f#TG` zAxLl%!EgG`&*5<32cu%worY0{L9A7~=}6b}76Isk~1IN~P)K3@?4 z&zpALY4A7Z!>t0&I7qECf*j`WHIYAjW_h^ivJu4lvb8y9VL`DD`rG0ZKvcZ$L@8 zo)6*!Fng}&gE0q~LGCMnsiR8`P)pL0I_sTkS+y)n+TA3GebL))$`zPyB#}i;vpt> zLLd%=I3q+TkOe{@7A!zw2TPWbvSNdT62yXthz%fiNPq$+195Cj66`p(zB>*$BqWgX9JhrUc>v7R-~u^B{E_ zRNwKukD}6E^I^<(eYPUNRzv61Pl7Im-uqb+bjIE`FfKtC0bd6-?ajjVnnCl5U6k;i zmwQneMjWget|uE!3&)N499snAqm#HHM%#c3(xQq$!z?Yadsi?%jSauHMF6wvJ_S@6;o^RNM{@x1w+DCX1JsGz}E&YY?b6&?;)Xi!K zn^A)m&nv4L=w_j9`*&N=g#@xaP^m#!&Q|+d7c5`6Nz1 z*ouDy$KV6z~8VF(_GH%`%1Lz5&B3?I!=^SI=W7R6N z=+yMYbSw$lr-6OIX|Q*|=k))nWRTq|kJY1BYU|MGbzyryX{!|#bd+Ju`J^G<1KSH6 z2HT^HH_^FB_ZroC*gV)3;0pMSWVmcWH!h#=Her54H(?>H&P)fV!FHyV<5N0D=H)fT z@NK~T()6?7XVQLcM*E9Sk?7;v`Sp6CBU7q5eV!5)&I($FceeKNM=0j{PbFbSGDY3*#f!PW)N11H5Sc7pFs=i%Dca#YU}-_Hn#$n*Tit?2%vKG$-LtZ9ehrtg9Sej4! z_g8_JfggjN2JRKz+b&k*AzYdxd=G$aOWWG@t>%KepyEIhlr2?Q=0#v9r-3&FF*|`z zfgohJ^@nPU~;5)#ZV5fkGz>i4T1_8gVqJefu zn!U8X+(spujgECJXt_>?o0wU`H3|8Yu=s1O5@P$H1Qho=s5Vi-MBf+K-H> z9TT>5d)mAz=&*I%P@4sg2|EvhT?5_%E(7lZJ`MhXU`RcllP5bEy zCT0>h_nbAEs-kpQ6&7{ZYC{a>Q@|168Q?XrIYG%@Da$diOB*5J4Z>(2@w>a0klOK{h<5{`qt0{()S&2g~% z!QT%YO<5ExbRJz9J#8kgZ(z}qA>Wsz+MJEqE)p^0`4bAj3;n;N% zg-t0p=!@3Nn5)lEDQTtVe(-K{s+LLz#jvjQ}}X}wO!BE?FN2D z?<2rp>G+Gn^Fk}>&CL*ieR(8vxS3nN7MFcw5zMqa!c%FP^Jie62#m#ox5vr>|23=K ztH2k4e-%R+S7`JW_tI-nxxS*#O)Lqsz(#x#_!F?-6WwFE+iiBc2dW9{=fFNMJMbmZ zxphOq7J`lyFJKJ#1n`I8kK8u+tzOGpDfYpB!Pu9@csAu#j7SUJ$qQ}^GyfFq;Si0x z!)j$Q!MiINUOp?Txaop!%4)|N<&AzF1^z%Z%a+5%X1p9ZP=5qGscYU0LAzqQKCbl5 zOvO>l%!X}%;K#s!3;Z*8=q9dJ!5#;GN5QW<@oEqXbuvb`7_Q!F$pPpnm@_GEn_~vK zB51V*-EH70;4>pA+}(QZ1b$sH>I)$(upVR7kcV0?rf*7)OU(cq`OC-)E0kb$IRQtt z(SlswYkJ3YPc02!QltIgp99{#1qAFND{~0^WAYBazY6LeOB(XeZMxhHYg%tHGGd;~v(`{f4mDPH)f{W5Xxr(!u+R0j;p~XpIPRmiS_bVWD zLkB*gXxw)2CsMqZ_9oG})jBmwcli^*Hy94|x2clE^P+?;#Z~szVB&^Cy*|96%aVp> zGXQr{`%=zmx5)+BZO>YW?%Jr%u^+KhnQV6&?~QDRBmC&>G|9*qPjFw@roZzZnZ7RP!`n}Ee!kcUb?%!r10qO%4xXULLTY8?n;Ky+S1$wU0YYzHIvGUt(&qn=ApJE z7DBqNeNacnB9zz0gL~D|;FGBiZCUh5C04Yg2{AzJqJn(|><`>!Oup4?zY?^$b~~SY zUn{#MSnMYS^ZKe(ONI-6%|{V;XJn5^`L|qgAE3nt%G0S|#G50+qD#`^sif7ul?6Se zsA5kz+)+H-D!XNB7a7@`n0*ghDY99t92U&lZT*nmCmei0)lN}V*lVUGcAcsO%-PZ} z>q&7M%KFSQ)a2x$fYKVZc08)@nXj~PZ>PvncR72LW$%va>MZlhakf^^`AyiK5lS1Z z7KpB2JGWNSjSG|ZR&c|cVk;KD^E2|Y+;ke)*AOFEcR~4j0?Rqjw5Up-eoz1xy~_r0 zfjiWVdI#v~3Zk;L3c94%xilaOGM8RuH+?YNWP{bRCB@by9fpj$ zs{1|n*5m@shM2}x8OmHHT!)OC#`25G_g^+dB+tdjRrk@ckw0I;N3Vuwg<>Mj6oB$` zGAayLhTe5-&V2Ynac^_r4ccDn;^d+s{Rod6bi_K-%8!q^z0%Dl4PXO^2I zb2M*I+denlTp<<6Y-Q$gMs9X4La$TRdA;*B+lcQhGF)gsO3;~>cx?J9Li}S?^L1`> zxGvXp5O}u9%aZ?ZXOq@%HP5ij@|;&|$1X+V;;WNlHg-V_t6gnJY`v2*dP{B5p;yK; z|8=i*90j-1XHz5QP${h@7x;ww^e)wA8x?9ZzDEU$ZzTxwx-)c_AtQTmeoFlLkXkQd z9HS4+rE)P>kBc{IeqJwe?=no94Q4Ky_b{Dh88gVi5-p3&k;?+1eMAKdK~N}l@~v#;88!YivjpGu*-tOi9ibSwPfgE*g-Rs0``i4G5zHR zkCr|!Qq>n{&rnU8-b=xMzh2u%L24M90)7JiqQa0{>9s_v{d-f%domlc5$mL(Fs%wzYs~!GAh3 zw6f`LV0jJ~!Jbo;@jYq~3JGf*>|D}jSDGrj;3dq?Q`U!Ab419&!TUvsXY|5~jv0vD z&o>)zJkyvdrFG`PUK6Jts|={O(gko3>|fG%>@Fim8Qkh7P4=%V0QP82TLl}unOKSK zN;e`WT-{~2=8_VY2oUChcp=;#(&%0-@Sm4Prf?T2%VtY1+yFZue)(GBquvj#y*S zTjUAt__74mue{<9lX6`bgo>j{|NSXs>^-~{E%jes1(7+lBU`> zsaoWw1$%?g1$J2&O>ksl+&&k(Qxmjzg4=1~?zV+Xr*)@Wfe#N7PW!z{Q~b5WLds!K zn&@SF!9S~;@;9pNJRR_maC0|GxIRqhtPgw<_zMZ}Q>k@>rnD~$Y+lVuZziR&1CuN`qkcd-*dzoVe|5&e9H)Ogbmae*B5dLmsM z)4<;*&F1x6L<(;kC-gcE{I!I98u*aj+o*cH^E|vCCYD5Bly5gxvwdb2xvZiu3Tq>R z)44fmS@cdu(feVU4FnbA3YMHxSE>2|O9nEN92C0>_U&XuyrZITSO0gP)B^Xr682XT zX6K&kOq={RAZ194U4i_QI@X^j=fgh%{z-YZikIiqm7N89K6$eoN{lHFB6QSnG@qD= zXKWs?O`;65L6T$B_$;4~G3^}Q)UvneyoeInlH$#~DhpjUO>URLyr3q@!!nlrU_6W9 z99#xo0AA8k%d4WUl(sf4jz#bMH`o_|zXzWIK27Qk-=&C~W`CH54Ei&`_tek+u(Y)o z!ac2Y-u9NXd`d+`fm_=^4N=*VVN_Bzw}mys&RrC4imJbF52+qPzP>4<=SQ?;UKJZr z%)=L?JzYW3Q7yyoib8!6{CQIM)T3ZOU)h3D^GCUfE%mpOuy_vaJHpZj!Jg6-g&kEK zordDiSM=Q%WRD(}0kohXZF#_Fl3!tYmWU4zOT;NB=d7H{($K%5tgeU9XN#(VKF_&} zbZ-}_*6t19fAqhP%dydzAA0)z5#is9q{gAI3F99SemZSI;48&4~x?e#Dsd31Fg8G8}p1Dkar0-1|h7F1%8G!7o?@t*Qn9r(6e4?z$iiI z`9)*ZT)23*d{UI=yk^y@4$Wd*d$+jriz$}i<4K#?sghJ_7bQ*z4dg0sjI1 z%~UFE_k!QAWxPY@7SF#Y@9lMA@f8J#_N$Roo+%UZ00*H>IS*)g5%{K@mxt2vvVAH` zz6SCY&T5;Pn!BB6k>sIW{dy9W(ZZ(7vRZ|bfppaje?}U;tCq@YfbWv9e*%16Jp5xR zmuT0+sEB?DP`x+dw9O`i>)!!;7VLjj&DvU?V6Z{bX163f#10N+d?Q}ziJnwg>us_4Wo8Ui`VZCq*8@N-D^OTI90%Ez?he0)< z#ag7E@fU>4mlLe`eKKrb&7Jl}f*(7tm`9$o)Tqe?IVVe_vi{ALBV6U&x>2+3a*5a# z;njo;UW?(mq~r1iC$zMmqbw3+pIAy>mV@EsZt3o?_SMw>4pYrNt(R>Lbkoktemt+6 z{h(s!jo^e?2%ufmypaXUQ5DNr&Vb=rtE&>UHuO2&{pJYM-xe3XCe|>f*%|GS66;V+ zf0-hc^1htLL)g=572aFRqUU zNHSZV_i;U)hhIuJ(kH6~tkrq*ilr5|Dxevp7=Y!})u5LBosdzxp=j76s^@4_%eBPd z=|tO@274R$cQwPmFE!cQE1#hlF{=M65gT2j(mq^ zdJJmXFCOkc7JHFnQm*cZ_qhVKf3xMbnk+k zEl-L$nY4>5q-kOMWC-sQ-k;*8vol8eFgLW%!#eL%f{C+9+x>zzykGWXdv%UO1F(9D zVQ@;&Sm?VgmdBEF8f%f83?6K*Ze99fFoKO5jEu>;&nlCgXMEm6IRe5}fobzv8Shq* z>8$wp_ca?yTE>L<>IlK!6t-tm{*Skl(CwInd|CUvH!zZ>w9rEVOpe`LlmUN7*EdrI z)|RzSERLdc>k)KjA*!(Si5hRt+N!{c&nC^LqvbghoEZybzAdfVFK6R=ntsa*Aw_g6 zTppp(_L8iBC&g%dr^2B-)T~s52Pfo0`%uH;?`VZx(@c@6idI^!TELbBRo&HCYSt!f zGsTy4<$uh!Oja>yJFcaCM6DHVxuo4NfkrpT+M;B86(>2#iuHOo9mtZ0bMKSG($!u2YG?(#ql=%X zns(j}Vbgw%n(fv9&$GT~qh#Ub0pRhe6@zfxVnn#}v~W3xJ-5}ih2hcpbF5+h|9Z-H zWyP(YRfzRwllq@#kJ>c_{NE*~Bx`3;Wopc1z!tUi*O>tDBEXu;0fuIOgh(XwGI-hW zwlzV0EZn$|F)7b+mNgE!T5o-HSG2ECJS0>+l@k~nV6xOcuOTFtg~>;hgKJa--wQDo zjbFQ&p#4_oHmkVC6vI{K4N z;^|70m)PX7#!=WFsNARr6XpwPO9+?ro|1;o2)}dcesG)Tm5W*?b#s@fiZL(M?7~El z!-%J2c)xg+0|39UMx;?Zb^ibW002ovPDHLkV1n(iOrih) literal 0 HcmV?d00001 diff --git a/includes/layers/Maps_ImageLayer.php b/includes/layers/Maps_ImageLayer.php deleted file mode 100644 index be1f7f0ec..000000000 --- a/includes/layers/Maps_ImageLayer.php +++ /dev/null @@ -1,198 +0,0 @@ - - * @author Daniel Werner - */ -class MapsImageLayer extends MapsLayer { - - /** - * Registers the layer. - * - * @since 0.7.2 - */ - public static function register() { - MapsLayerTypes::registerLayerType( 'image', __CLASS__, 'openlayers' ); - return true; - } - - /** - * @see MapsLayer::getParameterDefinitions - * - * @since 0.7.2 - * - * @return array - */ - protected function getParameterDefinitions() { - $params = parent::getParameterDefinitions(); - - // map extent for extents bound object: - $params['topextent'] = array( - 'type' => 'float', - 'aliases' => array( 'upperbound', 'topbound' ), - 'message' => 'maps-displaymap-par-coordinates', // TODO-customMaps: create a message - ); - - $params['rightextent'] = array( - 'type' => 'float', - 'aliases' => array( 'rightbound' ), - 'message' => 'maps-displaymap-par-coordinates', // TODO-customMaps: create a message - ); - - $params['bottomextent'] = array( - 'type' => 'float', - 'aliases' => array( 'lowerbound', 'bottombound' ), - 'message' => 'maps-displaymap-par-coordinates', // TODO-customMaps: create a message - ); - - $params['leftextent'] = array( - 'type' => 'float', - 'aliases' => array( 'leftbound' ), - 'message' => 'maps-displaymap-par-coordinates', // TODO-customMaps: create a message - ); - - // image-source information: - $params['source'] = array( - // TODO-customMaps: addCriteria( new CriterionIsImage() ) - 'message' => 'maps-displaymap-par-coordinates', // TODO-customMaps: create a message - 'post-format' => function( $source ) { - $imageUrl = MapsMapper::getFileUrl( $source ); - - global $egMapsAllowExternalImages; - if( $imageUrl === '' && $egMapsAllowExternalImages ) { - return $source; - } - return $imageUrl; - } - ); - - $params['width'] = array( - 'type' => 'float', - 'message' => 'maps-displaymap-par-coordinates', // TODO-customMaps: create a message - ); - - $params['height'] = array( - 'type' => 'float', - 'message' => 'maps-displaymap-par-coordinates', // TODO-customMaps: create a message - ); - - return $params; - } - - /** - * @see MapsLayer::getPropertyHtmlRepresentation - * - * @since 3.0 - * - * @return array - */ - protected function getPropertyHtmlRepresentation( $name, &$parser ) { - $value = $this->properties[ $name ]; - - switch( $name ) { - case 'source': - $value = $this->originalPropertyValues['source']; // get original, non-modified value - - $title = Title::newFromText( $value, NS_FILE ); - - // if title has invalid characters or doesn't exist and has url-style - if( $title === null - || ( !$title->exists() && preg_match( '|^.+\://.+\..+$|', $value ) ) - ) { - // url link: - $value = $parser->recursiveTagParse( "[$value $value]" ); - } else { - // wikilink (can be red link to non-existant file): - $imgName = $title->getPrefixedText(); - $value = $parser->recursiveTagParse( "[[$imgName|thumb|[[:$imgName]]|left]]" ); - } - return $value; // html already - - default: - // if we don't have any special handling here, leave it to base class: - return parent::getPropertyHtmlRepresentation( $name, $parser ); - } - return htmlspecialchars( $value );; - } - - /** - * @see MapsLayer::doPropertiesHtmlTransform - * - * @since 3.0 - * - * @return array - */ - protected function doPropertiesHtmlTransform( &$properties ) { - parent::doPropertiesHtmlTransform( $properties ); - - $sp = ' '; // non-breaking thin space - - // image-size: - $properties['image-size'] = "width: {$properties['width']}{$sp}pixel, height: {$properties['height']}{$sp}pixel"; - unset( $properties['width'], $properties['height'] ); - - // extent: - $unit = $properties['units']; - $properties['extent'] = - "left: {$properties['leftextent']}{$sp}$unit, " . - "bottom: {$properties['bottomextent']}{$sp}$unit, " . - "right: {$properties['rightextent']}{$sp}$unit, " . - "top: {$properties['topextent']}{$sp}$unit"; - unset( $properties['leftextent'], $properties['bottomextent'], $properties['rightextent'], $properties['topextent'] ); - } - - /** - * @see MapsLayer::getJavaScriptDefinition - * - * @since 0.7.2 - * - * @return string - */ - public function getJavaScriptDefinition() { - $this->validate(); - - // do image layer options: - - $options = array( - 'isImage' => true, - 'units' => $this->properties['units'], - ); - - if( $this->properties['zoomlevels'] !== false ) { - $options['numZoomLevels'] = $this->properties['zoomlevels']; - } - if( $this->properties['minscale'] !== false ) { - $options['minScale'] = $this->properties['minscale']; - } - if( $this->properties['maxscale'] !== false ) { - $options['maxScale'] = $this->properties['maxscale']; - } - - $options = Xml::encodeJsVar( (object)$options ); //js-encode all options ); - - // for non-option params, get JavaScript-encoded config values: - foreach( $this->properties as $name => $value ) { - ${ $name } = MapsMapper::encodeJsVar( $value ); - } - - return <<itemSep = $itemSep; - $this->keyValueSep = $keyValueSep; - } - - /** - * @see ItemParameterManipulation::doManipulation - * - * @since 3.0 - */ - public function doManipulation( &$value, Parameter $parameter, array &$parameters ) { - // string to array describing layer parameters: - $value = MapsLayers::parseLayerParameters( $value, $this->itemSep, $this->keyValueSep ); - } -} - diff --git a/includes/manipulations/Maps_ParamSwitchIfGreaterThan.php b/includes/manipulations/Maps_ParamSwitchIfGreaterThan.php deleted file mode 100644 index 4b66ce2c1..000000000 --- a/includes/manipulations/Maps_ParamSwitchIfGreaterThan.php +++ /dev/null @@ -1,49 +0,0 @@ -greaterParam = $param; - } - - /** - * @see ItemParameterManipulation::doManipulation - * - * @since 3.0 - */ - public function doManipulation( &$value, Parameter $parameter, array &$parameters ) { - /* - * make sure maxScale is lower than minScale. Base layers would work fine anyhow - * but overlays would behave strange in some cases. Also useres could be confused - * by this so we take care of it. - */ - if ( $value > $this->greaterParam->getValue() ) { - $minScale = $value; - $value = $this->greaterParam->getValue(); - $this->greaterParam->setValue( $minScale ); - } - } -} - diff --git a/includes/parserhooks/Maps_Coordinates.php b/includes/parserhooks/Maps_Coordinates.php index 132f37929..39e4e1fc0 100644 --- a/includes/parserhooks/Maps_Coordinates.php +++ b/includes/parserhooks/Maps_Coordinates.php @@ -1,4 +1,5 @@ 'coordinate', - ); + ]; - $params['format'] = array( + $params['format'] = [ 'default' => $egMapsCoordinateNotation, 'values' => $egMapsAvailableCoordNotations, 'aliases' => 'notation', 'tolower' => true, - ); + ]; - $params['directional'] = array( + $params['directional'] = [ 'type' => 'boolean', 'default' => $egMapsCoordinateDirectional, - ); + ]; // Give grep a chance to find the usages: // maps-coordinates-par-location, maps-coordinates-par-format, maps-coordinates-par-directional @@ -73,7 +74,7 @@ protected function getParameterInfo( $type ) { * @return array */ protected function getDefaultParameters( $type ) { - return array( 'location', 'format', 'directional' ); + return [ 'location', 'format', 'directional' ]; } /** @@ -87,11 +88,11 @@ protected function getDefaultParameters( $type ) { * @return string */ public function render( array $parameters ) { - $options = new \ValueFormatters\FormatterOptions( array( + $options = new \ValueFormatters\FormatterOptions( [ GeoCoordinateFormatter::OPT_FORMAT => $parameters['format'], GeoCoordinateFormatter::OPT_DIRECTIONAL => $parameters['directional'], GeoCoordinateFormatter::OPT_PRECISION => 1 / 360000 - ) ); + ] ); $coordinateFormatter = new GeoCoordinateFormatter( $options ); diff --git a/includes/parserhooks/Maps_DisplayMap.php b/includes/parserhooks/Maps_DisplayMap.php index a053fc67d..f38e2c971 100644 --- a/includes/parserhooks/Maps_DisplayMap.php +++ b/includes/parserhooks/Maps_DisplayMap.php @@ -30,7 +30,7 @@ protected function getName() { * @return array */ protected function getNames() { - return array( $this->getName(), 'display_point', 'display_points', 'display_line' ); + return [ $this->getName(), 'display_point', 'display_points', 'display_line' ]; } /** @@ -46,121 +46,18 @@ protected function getParameterInfo( $type ) { $params['mappingservice']['feature'] = 'display_map'; - $params['coordinates'] = array( - 'type' => 'mapslocation', - 'aliases' => array( 'coords', 'location', 'address', 'addresses', 'locations', 'points' ), - 'dependencies' => array( 'mappingservice', 'geoservice' ), - 'default' => array(), + $params['coordinates'] = [ + 'type' => 'string', + 'aliases' => [ 'coords', 'location', 'address', 'addresses', 'locations', 'points' ], + 'default' => [], 'islist' => true, 'delimiter' => $type === ParserHook::TYPE_FUNCTION ? ';' : "\n", 'message' => 'maps-displaymap-par-coordinates', - ); + ]; - $params = array_merge( $params, self::getCommonMapParams() ); - return $params; } - /** - * Temporary hack. Do not rely upon. - * @since 3.0 - * @deprecated - * @return array - */ - public static function getCommonMapParams() { - global $egMapsDefaultTitle, $egMapsDefaultLabel; - - $params['title'] = array( - 'name' => 'title', - 'default' => $egMapsDefaultTitle, - ); - - $params['label'] = array( - 'default' => $egMapsDefaultLabel, - 'aliases' => 'text', - ); - - $params['icon'] = array( - 'default' => '', // TODO: image param - ); - - $params['visitedicon'] = array( - 'default' => '', //TODO: image param - ); - - $params['lines'] = array( - 'type' => 'mapsline', - 'default' => array(), - 'delimiter' => ';', - 'islist' => true, - ); - - $params['polygons'] = array( - 'type' => 'mapspolygon', - 'default' => array(), - 'delimiter' => ';', - 'islist' => true, - ); - - $params['circles'] = array( - 'type' => 'mapscircle', - 'default' => array(), - 'delimiter' => ';', - 'islist' => true, - ); - - $params['rectangles'] = array( - 'type' => 'mapsrectangle', - 'default' => array(), - 'delimiter' => ';', - 'islist' => true, - ); - - $params['wmsoverlay'] = array( - 'type' => 'wmsoverlay', - 'default' => false, - 'delimiter' => ' ', - ); - - $params['maxzoom'] = array( - 'type' => 'integer', - 'default' => false, - 'manipulatedefault' => false, - 'dependencies' => 'minzoom', - ); - - $params['minzoom'] = array( - 'type' => 'integer', - 'default' => false, - 'manipulatedefault' => false, - 'lowerbound' => 0, - ); - - $params['copycoords'] = array( - 'type' => 'boolean', - 'default' => false, - ); - - $params['static'] = array( - 'type' => 'boolean', - 'default' => false, - ); - - // Give grep a chance to find the usages: - // maps-displaymap-par-title, maps-displaymap-par-label, maps-displaymap-par-icon, - // maps-displaymap-par-visitedicon, aps-displaymap-par-lines, maps-displaymap-par-polygons, - // maps-displaymap-par-circles, maps-displaymap-par-rectangles, maps-displaymap-par-wmsoverlay, - // maps-displaymap-par-maxzoom, maps-displaymap-par-minzoom, maps-displaymap-par-copycoords, - // maps-displaymap-par-static - foreach ( $params as $name => &$param ) { - if ( !array_key_exists( 'message', $param ) ) { - $param['message'] = 'maps-displaymap-par-' . $name; - } - } - - return $params; - } - /** * Returns the list of default parameters. * @see ParserHook::getDefaultParameters @@ -170,7 +67,7 @@ public static function getCommonMapParams() { * @return array */ protected function getDefaultParameters( $type ) { - return array( 'coordinates' ); + return [ 'coordinates' ]; } /** @@ -184,22 +81,26 @@ protected function getDefaultParameters( $type ) { * @return string */ public function render( array $parameters ) { - // Get the instance of the service class. - $service = MapsMappingServices::getServiceInstance( $parameters['mappingservice'], $this->getName() ); - - $mapClass = new MapsDisplayMapRenderer( $service ); + $this->defaultMapZoom( $parameters ); + $this->trackMap(); + + $renderer = new MapsDisplayMapRenderer( MapsMappingServices::getServiceInstance( $parameters['mappingservice'] ) ); + return $renderer->renderMap( $parameters, $this->parser ); + } + + private function defaultMapZoom( &$parameters ) { $fullParams = $this->validator->getParameters(); if ( array_key_exists( 'zoom', $fullParams ) && $fullParams['zoom']->wasSetToDefault() && count( $parameters['coordinates'] ) > 1 ) { $parameters['zoom'] = false; } + } - global $egMapsEnableCategory; - if ($egMapsEnableCategory) { + private function trackMap() { + if ( $GLOBALS['egMapsEnableCategory'] ) { $this->parser->addTrackingCategory( 'maps-tracking-category' ); } - return $mapClass->renderMap( $parameters, $this->parser ); } /** @@ -211,10 +112,10 @@ public function render( array $parameters ) { * @return array */ protected function getFunctionOptions() { - return array( + return [ 'noparse' => true, 'isHTML' => true - ); + ]; } /** diff --git a/includes/parserhooks/Maps_Distance.php b/includes/parserhooks/Maps_Distance.php index 45f2cccbb..5295759b3 100644 --- a/includes/parserhooks/Maps_Distance.php +++ b/includes/parserhooks/Maps_Distance.php @@ -34,21 +34,21 @@ protected function getName() { protected function getParameterInfo( $type ) { global $egMapsDistanceUnit, $egMapsDistanceDecimals; - $params = array(); + $params = []; - $params['distance'] = array( + $params['distance'] = [ 'type' => 'distance', - ); + ]; - $params['unit'] = array( + $params['unit'] = [ 'default' => $egMapsDistanceUnit, 'values' => MapsDistanceParser::getUnits(), - ); + ]; - $params['decimals'] = array( + $params['decimals'] = [ 'type' => 'integer', 'default' => $egMapsDistanceDecimals, - ); + ]; // Give grep a chance to find the usages: // maps-distance-par-distance, maps-distance-par-unit, maps-distance-par-decimals @@ -70,7 +70,7 @@ protected function getParameterInfo( $type ) { * @return array */ protected function getDefaultParameters( $type ) { - return array( 'distance', 'unit', 'decimals' ); + return [ 'distance', 'unit', 'decimals' ]; } /** diff --git a/includes/parserhooks/Maps_Finddestination.php b/includes/parserhooks/Maps_Finddestination.php index 1cde0d341..6bd08629f 100644 --- a/includes/parserhooks/Maps_Finddestination.php +++ b/includes/parserhooks/Maps_Finddestination.php @@ -1,4 +1,5 @@ */ - class MapsFinddestination extends ParserHook { /** @@ -37,50 +37,50 @@ protected function getParameterInfo( $type ) { global $egMapsAvailableGeoServices, $egMapsDefaultGeoService, $egMapsAvailableCoordNotations; global $egMapsCoordinateNotation, $egMapsAllowCoordsGeocoding, $egMapsCoordinateDirectional; - $params = array(); + $params = []; - $params['location'] = array( - 'dependencies' => array( 'mappingservice', 'geoservice' ), - 'type' => 'mapslocation', - ); + $params['location'] = [ + 'dependencies' => [ 'mappingservice', 'geoservice' ], + 'type' => 'mapslocation', // FIXME: geoservice is not used + ]; - $params['format'] = array( + $params['format'] = [ 'default' => $egMapsCoordinateNotation, 'values' => $egMapsAvailableCoordNotations, 'aliases' => 'notation', 'tolower' => true, - ); + ]; - $params['directional'] = array( + $params['directional'] = [ 'type' => 'boolean', 'default' => $egMapsCoordinateDirectional, - ); + ]; - $params['bearing'] = array( + $params['bearing'] = [ 'type' => 'float', - ); + ]; - $params['distance'] = array( + $params['distance'] = [ 'type' => 'distance', - ); + ]; - $params['mappingservice'] = array( + $params['mappingservice'] = [ 'default' => '', 'values' => MapsMappingServices::getAllServiceValues(), 'tolower' => true, - ); + ]; - $params['geoservice'] = array( + $params['geoservice'] = [ 'default' => $egMapsDefaultGeoService, 'aliases' => 'service', 'values' => $egMapsAvailableGeoServices, 'tolower' => true, - ); + ]; - $params['allowcoordinates'] = array( + $params['allowcoordinates'] = [ 'type' => 'boolean', 'default' => $egMapsAllowCoordsGeocoding, - ); + ]; // Give grep a chance to find the usages: // maps-finddestination-par-location, maps-finddestination-par-format, @@ -103,7 +103,7 @@ protected function getParameterInfo( $type ) { * @return array */ protected function getDefaultParameters( $type ) { - return array( 'location', 'bearing', 'distance' ); + return [ 'location', 'bearing', 'distance' ]; } /** @@ -123,11 +123,11 @@ public function render( array $parameters ) { $parameters['distance'] ); - $options = new \ValueFormatters\FormatterOptions( array( + $options = new \ValueFormatters\FormatterOptions( [ GeoCoordinateFormatter::OPT_FORMAT => $parameters['format'], GeoCoordinateFormatter::OPT_DIRECTIONAL => $parameters['directional'], GeoCoordinateFormatter::OPT_PRECISION => 1 / 360000 - ) ); + ] ); $formatter = new GeoCoordinateFormatter( $options ); diff --git a/includes/parserhooks/Maps_Geocode.php b/includes/parserhooks/Maps_Geocode.php index 69df034b7..3604cbea7 100644 --- a/includes/parserhooks/Maps_Geocode.php +++ b/includes/parserhooks/Maps_Geocode.php @@ -1,5 +1,7 @@ 'mapslocation', - 'dependencies' => array( 'mappingservice', 'geoservice' ), - ); + $params = []; - $params['mappingservice'] = array( - 'default' => '', - 'values' => MapsMappingServices::getAllServiceValues(), - 'tolower' => true, - ); + $params['location'] = [ + 'type' => 'string', + 'message' => 'maps-geocode-par-location', + ]; - $params['geoservice'] = array( + $params['geoservice'] = [ 'default' => $egMapsDefaultGeoService, 'aliases' => 'service', 'values' => $egMapsAvailableGeoServices, 'tolower' => true, - ); + 'message' => 'maps-geocode-par-geoservice', + ]; - $params['allowcoordinates'] = array( + $params['allowcoordinates'] = [ 'type' => 'boolean', 'default' => $egMapsAllowCoordsGeocoding, - ); + 'message' => 'maps-geocode-par-allowcoordinates', + ]; - $params['format'] = array( + $params['format'] = [ 'default' => $egMapsCoordinateNotation, 'values' => $egMapsAvailableCoordNotations, 'aliases' => 'notation', 'tolower' => true, - ); + 'message' => 'maps-geocode-par-format', + ]; - $params['directional'] = array( + $params['directional'] = [ 'type' => 'boolean', 'default' => $egMapsCoordinateDirectional, - ); - - // Give grep a chance to find the usages: - // maps-geocode-par-location, maps-geocode-par-mappingservice, maps-geocode-par-geoservice, - // maps-geocode-par-allowcoordinates, maps-geocode-par-format, maps-geocode-par-directional - foreach ( $params as $name => &$param ) { - $param['message'] = 'maps-geocode-par-' . $name; - } + 'message' => 'maps-geocode-par-directional', + ]; return $params; } @@ -93,7 +86,7 @@ protected function getParameterInfo( $type ) { * @return array */ protected function getDefaultParameters( $type ) { - return array( 'location', 'geoservice', 'mappingservice' ); + return [ 'location', 'geoservice' ]; } /** @@ -107,16 +100,25 @@ protected function getDefaultParameters( $type ) { * @return string */ public function render( array $parameters ) { - /** - * @var \DataValues\LatLongValue $coordinates - */ - $coordinates = $parameters['location']->getCoordinates(); + if ( !Geocoders::canGeocode() ) { + return 'No geocoders available'; + } + + $coordinates = Geocoders::attemptToGeocode( + $parameters['location'], + $parameters['geoservice'], + $parameters['allowcoordinates'] + ); + + if ( $coordinates === false ) { + return 'Geocoding failed'; + } - $options = new \ValueFormatters\FormatterOptions( array( + $options = new \ValueFormatters\FormatterOptions( [ GeoCoordinateFormatter::OPT_FORMAT => $parameters['format'], GeoCoordinateFormatter::OPT_DIRECTIONAL => $parameters['directional'], GeoCoordinateFormatter::OPT_PRECISION => 1 / 360000 - ) ); + ] ); $formatter = new GeoCoordinateFormatter( $options ); diff --git a/includes/parserhooks/Maps_Geodistance.php b/includes/parserhooks/Maps_Geodistance.php index 34ee88d11..f691a0c8c 100644 --- a/includes/parserhooks/Maps_Geodistance.php +++ b/includes/parserhooks/Maps_Geodistance.php @@ -9,7 +9,6 @@ * @licence GNU GPL v2+ * @author Jeroen De Dauw < jeroendedauw@gmail.com > */ - class MapsGeodistance extends ParserHook { /** @@ -35,42 +34,42 @@ protected function getName() { protected function getParameterInfo( $type ) { global $egMapsDistanceUnit, $egMapsDistanceDecimals, $egMapsAvailableGeoServices, $egMapsDefaultGeoService; - $params = array(); + $params = []; - $params['mappingservice'] = array( + $params['mappingservice'] = [ 'default' => '', 'values' => MapsMappingServices::getAllServiceValues(), 'tolower' => true, - ); + ]; - $params['geoservice'] = array( + $params['geoservice'] = [ 'default' => $egMapsDefaultGeoService, 'aliases' => 'service', 'values' => $egMapsAvailableGeoServices, 'tolower' => true, - ); + ]; - $params['unit'] = array( + $params['unit'] = [ 'default' => $egMapsDistanceUnit, 'values' => MapsDistanceParser::getUnits(), - ); + ]; - $params['decimals'] = array( + $params['decimals'] = [ 'type' => 'integer', 'default' => $egMapsDistanceDecimals, - ); + ]; - $params['location1'] = array( - 'type' => 'mapslocation', + $params['location1'] = [ + 'type' => 'mapslocation', // FIXME: geoservice is not used 'aliases' => 'from', - 'dependencies' => array( 'mappingservice', 'geoservice' ), - ); + 'dependencies' => [ 'mappingservice', 'geoservice' ], + ]; - $params['location2'] = array( - 'type' => 'mapslocation', + $params['location2'] = [ + 'type' => 'mapslocation', // FIXME: geoservice is not used 'aliases' => 'to', - 'dependencies' => array( 'mappingservice', 'geoservice' ), - ); + 'dependencies' => [ 'mappingservice', 'geoservice' ], + ]; // Give grep a chance to find the usages: // maps-geodistance-par-mappingservice, maps-geodistance-par-geoservice, @@ -94,7 +93,7 @@ protected function getParameterInfo( $type ) { * @return array */ protected function getDefaultParameters( $type ) { - return array( 'location1', 'location2', 'unit', 'decimals' ); + return [ 'location1', 'location2', 'unit', 'decimals' ]; } /** diff --git a/includes/parserhooks/Maps_LayerDefinition.php b/includes/parserhooks/Maps_LayerDefinition.php deleted file mode 100644 index b5d59285b..000000000 --- a/includes/parserhooks/Maps_LayerDefinition.php +++ /dev/null @@ -1,426 +0,0 @@ - false, - 'manipulatedefault' => false, - 'message' => 'maps-displaymap-par-coordinates', // TODO-customMaps: create a message - ); - - $params['name'] = array( - 'default' => false, - 'manipulatedefault' => false, - // TODO-customMaps: addCriteria( new CriterionIsNonNumeric ); - 'message' => 'maps-displaymap-par-coordinates', // TODO-customMaps: create a message - ); - - $params['definition'] = array( - 'default' => false, - 'manipulatedefault' => false, - 'message' => 'maps-displaymap-par-coordinates', // TODO-customMaps: create a message - 'post-format' => function( $value ) { - return MapsLayers::parseLayerParameters( $value, "\n", '=' ); - } - ); - - return $params; - } - - /** - * Returns the list of default parameters. - * @see ParserHook::getDefaultParameters - * - * @since 3.0 - * - * @return array - */ - protected function getDefaultParameters( $type ) { - return array( 'definition' ); - } - - /** - * Returns the parser function options. - * @see ParserHook::getFunctionOptions - * - * @since 3.0 - * - * @return array - */ - protected function getFunctionOptions() { - return array( - 'noparse' => true, - 'isHTML' => true - ); - } - - /** - * @see ParserHook::getMessage() - * - * @since 3.0 - */ - public function getMessage() { - return 'maps-layerdefinition-description'; - } - - /** - * Returns the MapsLayerGroup with all layers of the same page which have been - * processed already. If the store is not attached to the parser object yet, - * an empty MapsLayerGroup will be attached as store after calling the function. - * - * @since 3.0 - * - * @return MapsLayerGroup - */ - protected function getLayerStore() { - $parserOutput = $this->parser->getOutput(); - - // make sure layers store in current parsers ParserOutput is initialized: - if( ! isset( $parserOutput->mExtMapsLayers ) ) { - $parserOutput->mExtMapsLayers = new MapsLayerGroup(); - } - return $parserOutput->mExtMapsLayers; - } - - /** - * This will attach a user defined layer to the parser output of the parser which has - * started the rendering. All added layers will be stored in the database - * after page parsing. - * $layer will only be stored in case it is a subclass of MapsLayer and its definition - * is at least considered 'ok'. - * - * @since 3.0 - * - * @param type $layer - * - * @return boolean whether $layer has been added to the store - */ - protected function addLayerToStore( $layer ) { - - $store = $this->getLayerStore(); - - // check whether $layer is a layer worthy to end up in the database: - if( $layer === null || $layer === false - || ! is_subclass_of( $layer, 'MapsLayer' ) - || ! $layer->isOk() - || $store->getLayerByName( $layer->getName() ) !== null // layer of same name in store already - - ) { - return false; - } - // add layer to store: - $overwritten = $store->addLayer( $layer ); - - if( $overwritten ) { - /** @ToDo: Message that a layer was defined twice on that site */ - } - return true; - } - - /** - * Renders and returns the output. - * @see ParserHook::renderTag - * - * @since 3.0 - * - * @param array $parameters - * - * @return string - */ - public function render( array $parameters ) { - global $wgLang; - - // Check whether parser tag is used in the right namespace context, abort if not - if( $this->parser->getTitle()->getNamespace() !== Maps_NS_LAYER ) { - global $wgContLang; - return $this->rawErrorbox( - wfMessage( - 'maps-layerdef-wrong-namespace', - $wgContLang->getNsText( Maps_NS_LAYER ) - )->inContentLanguage()->text() - ); - } - - $type = $parameters['type']; - - if( $type === false ) { - // no layer type specified - - $availableLayerTypes = MapsLayerTypes::getAvailableTypes(); - - $out = $this->rawErrorbox( - wfMessage( - 'maps-error-no-layertype', - $wgLang->listToText( $availableLayerTypes ), - count( $availableLayerTypes ) - )->inContentLanguage()->text() - ); - } - elseif( MapsLayerTypes::hasType( $type ) ) { - // get layer name if any: - $name = $parameters['name'] !== false ? $parameters['name'] : null; - - // make sure the layer has a label, if no user data, make something up: - if( empty( $parameters['definition']['label'] ) ) { - if( $name !== null ) { - $labelSuffix = "- $name"; - } else { - // label for unnamed layer: - $labelSuffix = '#' . ( count( $this->getLayerStore()->getLayers( MapsLayerGroup::LAYERS_NUMERIC ) ) + 1 ); - } - $parameters['definition']['label'] = $this->parser->getTitle()->getText() . ' ' . $labelSuffix; - } - - // new layer from user input (could still be invalid): - $layer = MapsLayers::newLayerFromDefinition( $type, $parameters['definition'], $name ); - - $out = $this->renderLayerInfo( $layer ); - } - else { - // specified layer type is non-existant! - - $availableLayerTypes = MapsLayerTypes::getAvailableTypes(); - - $out = $this->rawErrorbox( - wfMessage( - 'maps-error-invalid-layertype', - $this->validator->getParameter('type')->getOriginalValue(), - $wgLang->listToText( $availableLayerTypes ), - count( $availableLayerTypes ) - )->inContentLanguage()->text() - ); - } - - // add the layer to the store after all info has been rendered: - $this->addLayerToStore( $layer ); - - return $out; - } - - /** - * Responsible for actual output on the layer page which gives an overview of the layer definition. - * - * @since 3.0 - * - * @param MapsLayer - * - * @return string - */ - public function renderLayerInfo( MapsLayer $layer ) { - global $wgLang; - - // appropriate layer header: - if( $layer->getName() !== null ) { - - // if layer with same name is defined on same page already: - if( $this->getLayerStore()->getLayerByName( $layer->getName() ) !== null ) { - return - $this->errorbox( - wfMessage( - 'maps-layerdef-equal-layer-name', - $layer->getName() - )->inContentLanguage()->text() - ); - } - $outHeader = wfMessage( - 'maps-layer-of-type-and-name', - $layer->getType(), - $layer->getName() - )->inContentLanguage()->text(); - } - else { - $outHeader = wfMessage( - 'maps-layer-of-type', - $layer->getType() - )->inContentLanguage()->text(); - } - $outHeader = "$outHeader"; - - // info message about which services are supporting the layer(-type): - $supportedServices = MapsLayerTypes::getServicesForType( $layer->getType() ); - $outServices = '' . - wfMessage( - 'maps-layer-type-supported-by', - $wgLang->listToText( $supportedServices ), - count( $supportedServices ) - )->inContentLanguage()->escaped() . ''; - - $outTable = $this->getLayerDefinitionTable( $layer ); - - return - Html::rawElement( - 'div', - array( 'class' => 'mapslayer' . ( $layer->isOk() ? '' : ' mapslayererror' ) ), - $outHeader . $outServices . $outTable - ); - } - - /** - * Displays the layer definition as a table. - * - * @since 3.0 - * - * @param MapsLayer $layer - * - * @return string - */ - protected function getLayerDefinitionTable( MapsLayer $layer ) { - $out = ''; - $outWarning = ''; - - // check whether any error occurred during parameter validaton: - if ( ! $layer->isValid() ) { - $messages = $layer->getErrorMessages(); - $warnings = ''; - - if( count( $messages ) === 1 ) { - $warnings = htmlspecialchars( $messages[0] ); - } else { - $warnings = '
  • ' . implode( '
  • ', array_map( 'htmlspecialchars', $messages ) ) . '
'; - } - - $warnings = - '' . - wfMessage( - 'maps-layerdef-invalid' . ( $layer->isOk() ? '' : '-fatal' ), - count( $messages ) - )->inContentLanguage()->escaped() . - "{$warnings}"; - - $outWarning .= Html::rawElement( - 'table', - array( 'width' => '100%', 'class' => ( $layer->isOk() ? 'mapslayerwarntable' : 'mapslayererrortable' ) ), - $warnings - ); - - if( ! $layer->isOk() ) { - // fatal error occurred, don't print definition table since this would be quite empty since - // parameter validation aborted after fatal error parameter! - return $outWarning; - } - } - - global $wgOut; - $wgOut->addModules( 'ext.maps.layers' ); - - $rows = array(); - - // rows with layer definition: - $properties = $layer->getPropertiesHtmlRepresentation( $this->parser ); - - foreach ( $properties as $property => $value ) { - $rows[] = Html::rawElement( - 'tr', - array(), - Html::element( - 'td', - array( 'class' => 'mapslayerpropname' ), - $property - ) . - Html::rawElement( - 'td', - array( 'class' => 'mapslayerpropval' ), - $value - ) - ); - } - - $out .= Html::rawElement( - 'table', - array( 'width' => '100%', 'class' => 'mapslayertable' ), - implode( "\n", $rows ) - ); - - return ( $out . $outWarning ); - } - - /** - * wraps text inside an error box. - * - * @since 3.0 - * - * @param string $text text of the error, html-escaped. - * - * @return string - */ - protected function errorbox( $text, $raw = true ) { - /** - * FIXME: using 'errorbox' isn't the best idea since it has - * some weird css definition, better would be introducing a - * own class and puttin the whole definition into a nicer box. - */ - return '
' . $text .'
'; - } - - /** - * wraps text inside an error box. - * - * @since 3.0 - * - * @param string $text text of the error, NOT html-escaped - * - * @return string - */ - protected function rawErrorbox( $text ) { - $text = htmlspecialchars( $text ); - return $this->errorbox( $text ); - } -} diff --git a/includes/parserhooks/Maps_MapsDoc.php b/includes/parserhooks/Maps_MapsDoc.php index cbde24940..babc211a3 100644 --- a/includes/parserhooks/Maps_MapsDoc.php +++ b/includes/parserhooks/Maps_MapsDoc.php @@ -43,16 +43,16 @@ protected function getName() { * @return array */ protected function getParameterInfo( $type ) { - $params = array(); + $params = []; - $params['service'] = array( + $params['service'] = [ 'values' => $GLOBALS['egMapsAvailableServices'], 'tolower' => true, - ); + ]; - $params['language'] = array( + $params['language'] = [ 'default' => $GLOBALS['wgLanguageCode'], - ); + ]; // Give grep a chance to find the usages: // maps-geocode-par-service, maps-geocode-par-language @@ -72,7 +72,7 @@ protected function getParameterInfo( $type ) { * @return array */ protected function getDefaultParameters( $type ) { - return array( 'service', 'language' ); + return [ 'service', 'language' ]; } /** @@ -103,7 +103,7 @@ public function render( array $parameters ) { * * @return string */ - protected function msg() { + private function msg() { $args = func_get_args(); $key = array_shift( $args ); return wfMessage( $key, $args )->inLanguage( $this->language )->text(); @@ -118,8 +118,8 @@ protected function msg() { * * @return string */ - protected function getParameterTable( array $parameters ) { - $tableRows = array(); + private function getParameterTable( array $parameters ) { + $tableRows = []; $parameters = ParamDefinition::getCleanDefinitions( $parameters ); @@ -130,13 +130,13 @@ protected function getParameterTable( array $parameters ) { $table = ''; if ( count( $tableRows ) > 0 ) { - $tableRows = array_merge( array( + $tableRows = array_merge( [ '!' . $this->msg( 'validator-describe-header-parameter' ) ."\n" . //'!' . $this->msg( 'validator-describe-header-aliases' ) ."\n" . '!' . $this->msg( 'validator-describe-header-type' ) ."\n" . '!' . $this->msg( 'validator-describe-header-default' ) ."\n" . '!' . $this->msg( 'validator-describe-header-description' ) - ), $tableRows ); + ], $tableRows ); $table = implode( "\n|-\n", $tableRows ); @@ -152,16 +152,14 @@ protected function getParameterTable( array $parameters ) { /** * Returns the wikitext for a table row describing a single parameter. * - * @since 1.0 - * * @param ParamDefinition $parameter * * @return string */ - protected function getDescriptionRow( ParamDefinition $parameter ) { + private function getDescriptionRow( ParamDefinition $parameter ) { $description = $this->msg( $parameter->getMessage() ); - $type = $parameter->getTypeMessage(); + $type = $this->msg( $parameter->getTypeMessage() ); $default = $parameter->isRequired() ? "''" . $this->msg( 'validator-describe-required' ) . "''" : $parameter->getDefault(); if ( is_array( $default ) ) { @@ -181,15 +179,15 @@ protected function getDescriptionRow( ParamDefinition $parameter ) { EOT; } - protected function getServiceParameters( $service ) { + private function getServiceParameters( $service ) { $service = MapsMappingServices::getServiceInstance( $service ); - $params = array(); + $params = []; - $params['zoom'] = array( + $params['zoom'] = [ 'type' => 'integer', 'message' => 'maps-par-zoom', - ); + ]; $service->addParameterInfo( $params ); diff --git a/includes/parsers/CircleParser.php b/includes/parsers/CircleParser.php index fd9120b77..3e6bd5a02 100644 --- a/includes/parsers/CircleParser.php +++ b/includes/parsers/CircleParser.php @@ -37,31 +37,31 @@ public function stringParse( $value ) { $circle = new Circle( $this->stringToLatLongValue( $circleData[0] ), (float)$circleData[1] ); - if ( $metaData !== array() ) { + if ( $metaData !== [] ) { $circle->setTitle( array_shift( $metaData ) ); } - if ( $metaData !== array() ) { + if ( $metaData !== [] ) { $circle->setText( array_shift( $metaData ) ); } - if ( $metaData !== array() ) { + if ( $metaData !== [] ) { $circle->setStrokeColor( array_shift( $metaData ) ); } - if ( $metaData !== array() ) { + if ( $metaData !== [] ) { $circle->setStrokeOpacity( array_shift( $metaData ) ); } - if ( $metaData !== array() ) { + if ( $metaData !== [] ) { $circle->setStrokeWeight( array_shift( $metaData ) ); } - if ( $metaData !== array() ) { + if ( $metaData !== [] ) { $circle->setFillColor( array_shift( $metaData ) ); } - if ( $metaData !== array() ) { + if ( $metaData !== [] ) { $circle->setFillOpacity( array_shift( $metaData ) ); } diff --git a/includes/parsers/LineParser.php b/includes/parsers/LineParser.php index f08a00329..d92746a30 100644 --- a/includes/parsers/LineParser.php +++ b/includes/parsers/LineParser.php @@ -52,7 +52,7 @@ public function stringParse( $value ) { * @return LatLongValue[] */ protected function parseCoordinates( array $coordinateStrings ) { - $coordinates = array(); + $coordinates = []; $coordinateParser = new GeoCoordinateParser( new \ValueParsers\ParserOptions() ); $supportsGeocoding = $this->supportGeocoding && \Maps\Geocoders::canGeocode(); diff --git a/includes/parsers/LocationParser.php b/includes/parsers/LocationParser.php index 5c609f015..f47b3ef42 100644 --- a/includes/parsers/LocationParser.php +++ b/includes/parsers/LocationParser.php @@ -7,6 +7,7 @@ use Maps\Elements\Location; use MWException; use Title; +use ValueParsers\ParserOptions; use ValueParsers\ParseException; use ValueParsers\StringValueParser; @@ -20,8 +21,15 @@ */ class LocationParser extends StringValueParser { - // TODO - private $supportGeocoding = true; + /** + * @param ParserOptions|null $options + */ + public function __construct( ParserOptions $options = null ) { + parent::__construct( $options ); + + $this->defaultOption( 'useaddressastitle', false ); + $this->defaultOption( 'geoService', '' ); + } /** * @see StringValueParser::stringParse @@ -31,34 +39,40 @@ class LocationParser extends StringValueParser { * @param string $value * * @return Location - * @throws MWException + * @throws ParseException */ public function stringParse( $value ) { $separator = '~'; + $useaddressastitle = $this->getOption( 'useaddressastitle' ); + $metaData = explode( $separator, $value ); - $coordinates = $this->stringToLatLongValue( array_shift( $metaData ) ); + $coordinatesOrAddress = array_shift( $metaData ); + $coordinates = $this->stringToLatLongValue( $coordinatesOrAddress ); $location = new Location( $coordinates ); - if ( $metaData !== array() ) { + if ( $metaData !== [] ) { $this->setTitleOrLink( $location, array_shift( $metaData ) ); } + else if ( $useaddressastitle && $this->isAddress( $coordinatesOrAddress ) ) { + $location->setTitle( $coordinatesOrAddress ); + } - if ( $metaData !== array() ) { + if ( $metaData !== [] ) { $location->setText( array_shift( $metaData ) ); } - if ( $metaData !== array() ) { + if ( $metaData !== [] ) { $location->setIcon( array_shift( $metaData ) ); } - if ( $metaData !== array() ) { + if ( $metaData !== [] ) { $location->setGroup( array_shift( $metaData ) ); } - if ( $metaData !== array() ) { + if ( $metaData !== [] ) { $location->setInlineLabel( array_shift( $metaData ) ); } @@ -94,7 +108,7 @@ private function getExpandedLink( $link ) { return ''; } - return $title->getFullUrl(); + return $title->getFullURL(); } /** @@ -104,19 +118,37 @@ private function getExpandedLink( $link ) { * @throws ParseException */ private function stringToLatLongValue( $location ) { - if ( $this->supportGeocoding && Geocoders::canGeocode() ) { - $location = Geocoders::attemptToGeocode( $location ); + if ( Geocoders::canGeocode() ) { + $latLongValue = Geocoders::attemptToGeocode( $location, $this->getOption( 'geoService' ) ); - if ( $location === false ) { + if ( $latLongValue === false ) { throw new ParseException( 'Failed to parse or geocode' ); } - assert( $location instanceof LatLongValue ); - return $location; + assert( $latLongValue instanceof LatLongValue ); + return $latLongValue; } $parser = new GeoCoordinateParser( new \ValueParsers\ParserOptions() ); return $parser->parse( $location ); } + /** + * @param string $coordsOrAddress + * + * @return boolean + */ + private function isAddress( $coordsOrAddress ) { + $coordinateParser = new GeoCoordinateParser( new \ValueParsers\ParserOptions() ); + + try { + $coordinateParser->parse( $coordsOrAddress ); + } + catch ( ParseException $parseException ) { + return true; + } + + return false; + } + } diff --git a/includes/parsers/RectangleParser.php b/includes/parsers/RectangleParser.php index 7fdb61373..981d8a21e 100644 --- a/includes/parsers/RectangleParser.php +++ b/includes/parsers/RectangleParser.php @@ -40,23 +40,23 @@ public function stringParse( $value ) { $this->stringToLatLongValue( $rectangleData[1] ) ); - if ( $metaData !== array() ) { + if ( $metaData !== [] ) { $rectangle->setTitle( array_shift( $metaData ) ); } - if ( $metaData !== array() ) { + if ( $metaData !== [] ) { $rectangle->setText( array_shift( $metaData ) ); } - if ( $metaData !== array() ) { + if ( $metaData !== [] ) { $rectangle->setStrokeColor( array_shift( $metaData ) ); } - if ( $metaData !== array() ) { + if ( $metaData !== [] ) { $rectangle->setStrokeOpacity( array_shift( $metaData ) ); } - if ( $metaData !== array() ) { + if ( $metaData !== [] ) { $rectangle->setStrokeWeight( array_shift( $metaData ) ); } diff --git a/includes/properties/iBubbleMapElement.php b/includes/properties/iBubbleMapElement.php deleted file mode 100644 index 74203daff..000000000 --- a/includes/properties/iBubbleMapElement.php +++ /dev/null @@ -1,24 +0,0 @@ - @@ -18,110 +18,86 @@ } call_user_func( function() { - global $wgResourceModules, $wgHooks; + global $wgResourceModules; $pathParts = ( explode( DIRECTORY_SEPARATOR . 'extensions' . DIRECTORY_SEPARATOR, __DIR__, 2 ) ); - $wgResourceModules['ext.maps.googlemaps3'] = array( - 'dependencies' => array( 'ext.maps.common' ), + $wgResourceModules['ext.maps.googlemaps3'] = [ + 'dependencies' => [ 'ext.maps.common' ], 'localBasePath' => __DIR__, 'remoteExtPath' => end( $pathParts ), 'group' => 'ext.maps', - 'targets' => array( + 'targets' => [ 'mobile', 'desktop' - ), - 'scripts' => array( + ], + 'scripts' => [ 'jquery.googlemap.js', 'ext.maps.googlemaps3.js' - ), - 'messages' => array( + ], + 'messages' => [ 'maps-googlemaps3-incompatbrowser', 'maps-copycoords-prompt', 'maps-searchmarkers-text', 'maps-fullscreen-button', 'maps-fullscreen-button-tooltip', - ) - ); + ] + ]; - $wgResourceModules['ext.maps.gm3.markercluster'] = array( + $wgResourceModules['ext.maps.gm3.markercluster'] = [ 'localBasePath' => __DIR__ . '/gm3-util-library', 'remoteExtPath' => end( $pathParts ), 'group' => 'ext.maps', - 'targets' => array( + 'targets' => [ 'mobile', 'desktop' - ), - 'scripts' => array( + ], + 'scripts' => [ 'markerclusterer.js', - ), - ); + ], + ]; - $wgResourceModules['ext.maps.gm3.markerwithlabel'] = array( + $wgResourceModules['ext.maps.gm3.markerwithlabel'] = [ 'localBasePath' => __DIR__ . '/gm3-util-library', 'remoteExtPath' => end( $pathParts ), 'group' => 'ext.maps', - 'targets' => array( + 'targets' => [ 'mobile', 'desktop' - ), - 'scripts' => array( + ], + 'scripts' => [ 'markerwithlabel.js', - ), - 'styles' => array( + ], + 'styles' => [ 'markerwithlabel.css', - ), - ); + ], + ]; - $wgResourceModules['ext.maps.gm3.geoxml'] = array( + $wgResourceModules['ext.maps.gm3.geoxml'] = [ 'localBasePath' => __DIR__ . '/geoxml3', 'remoteExtPath' => end( $pathParts ), 'group' => 'ext.maps', - 'targets' => array( + 'targets' => [ 'mobile', 'desktop' - ), - 'scripts' => array( + ], + 'scripts' => [ 'geoxml3.js', 'ZipFile.complete.js', //kmz handling 'ProjectedOverlay.js', //Overlay handling - ), - ); + ], + ]; - $wgResourceModules['ext.maps.gm3.earth'] = array( + $wgResourceModules['ext.maps.gm3.earth'] = [ 'localBasePath' => __DIR__ . '/gm3-util-library', 'remoteExtPath' => end( $pathParts ), 'group' => 'ext.maps', - 'targets' => array( + 'targets' => [ 'mobile', 'desktop' - ), - 'scripts' => array( + ], + 'scripts' => [ 'googleearth-compiled.js', - ), - ); - - $wgHooks['MappingServiceLoad'][] = 'efMapsInitGoogleMaps3'; + ], + ]; } ); - -/** - * Initialization function for the Google Maps v3 service. - * - * @since 0.6.3 - * @ingroup MapsGoogleMaps3 - * - * @return boolean true - */ -function efMapsInitGoogleMaps3() { - global $wgAutoloadClasses; - - $wgAutoloadClasses['MapsGoogleMaps3'] = __DIR__ . '/Maps_GoogleMaps3.php'; - - MapsMappingServices::registerService( 'googlemaps3', 'MapsGoogleMaps3' ); - - // TODO: kill below code - $googleMaps = MapsMappingServices::getServiceInstance( 'googlemaps3' ); - $googleMaps->addFeature( 'display_map', 'MapsDisplayMapRenderer' ); - - return true; -} diff --git a/includes/services/GoogleMaps3/Maps_GoogleMaps3.php b/includes/services/GoogleMaps3/Maps_GoogleMaps3.php index a2c84008c..c8a9615eb 100644 --- a/includes/services/GoogleMaps3/Maps_GoogleMaps3.php +++ b/includes/services/GoogleMaps3/Maps_GoogleMaps3.php @@ -8,18 +8,15 @@ * * @licence GNU GPL v2+ * @author Jeroen De Dauw < jeroendedauw@gmail.com > + * @author Peter Grassberger < petertheone@gmail.com > */ class MapsGoogleMaps3 extends MapsMappingService { /** - * List of map types (keys) and their internal values (values). - * - * @since 0.7 - * - * @var array + * Maps user input map types to the Google Maps names for the map types. */ - public static $mapTypes = array( + private static $mapTypes = [ 'normal' => 'ROADMAP', 'roadmap' => 'ROADMAP', 'satellite' => 'SATELLITE', @@ -27,50 +24,32 @@ class MapsGoogleMaps3 extends MapsMappingService { 'terrain' => 'TERRAIN', 'physical' => 'TERRAIN', 'earth' => 'earth' - ); + ]; - /** - * List of supported map layers. - * - * @since 1.0 - * - * @var array - */ - protected static $mapLayers = array( + private static $mapLayers = [ 'traffic', 'bicycling' - ); + ]; - public static $typeControlStyles = array( + private static $typeControlStyles = [ 'default' => 'DEFAULT', 'horizontal' => 'HORIZONTAL_BAR', 'dropdown' => 'DROPDOWN_MENU' - ); + ]; - /** - * List of supported control names. - * - * @since 1.0 - * - * @var array - */ - protected static $controlNames = array( + private static $controlNames = [ 'pan', 'zoom', 'type', 'scale', - 'streetview' - ); + 'streetview', + 'rotate' + ]; - /** - * Constructor. - * - * @since 0.6.6 - */ public function __construct( $serviceName ) { parent::__construct( $serviceName, - array( 'googlemaps', 'google' ) + [ 'googlemaps', 'google' ] ); } @@ -84,23 +63,23 @@ public function addParameterInfo( array &$params ) { global $egMapsGMaps3DefTypeStyle, $egMapsGMaps3DefZoomStyle, $egMapsGMaps3AutoInfoWindows; global $egMapsResizableByDefault, $egMapsGMaps3DefaultTilt; - $params['zoom'] = array( + $params['zoom'] = [ 'type' => 'integer', - 'range' => array( 0, 20 ), + 'range' => [ 0, 20 ], 'default' => self::getDefaultZoom(), - 'message' => 'maps-googlemaps3-par-zoom', - ); + 'message' => 'maps-par-zoom', + ]; - $params['type'] = array( + $params['type'] = [ 'default' => $egMapsGMaps3Type, 'values' => self::getTypeNames(), 'message' => 'maps-googlemaps3-par-type', 'post-format' => function( $value ) { return MapsGoogleMaps3::$mapTypes[strtolower( $value )]; }, - ); + ]; - $params['types'] = array( + $params['types'] = [ 'dependencies' => 'type', 'default' => $egMapsGMaps3Types, 'values' => self::getTypeNames(), @@ -108,21 +87,21 @@ public function addParameterInfo( array &$params ) { 'islist' => true, 'post-format' => function( array $value ) { foreach ( $value as &$part ) { - $part = MapsGoogleMaps3::$mapTypes[strtolower( $part )]; + $part = self::$mapTypes[strtolower( $part )]; } return $value; }, - ); + ]; - $params['layers'] = array( + $params['layers'] = [ 'default' => $egMapsGMaps3Layers, 'values' => self::getLayerNames(), 'message' => 'maps-googlemaps3-par-layers', 'islist' => true, - ); + ]; - $params['controls'] = array( + $params['controls'] = [ 'default' => $egMapsGMaps3Controls, 'values' => self::$controlNames, 'message' => 'maps-googlemaps3-par-controls', @@ -130,103 +109,131 @@ public function addParameterInfo( array &$params ) { 'post-format' => function( $value ) { return array_map( 'strtolower', $value ); }, - ); + ]; - $params['zoomstyle'] = array( + $params['zoomstyle'] = [ 'default' => $egMapsGMaps3DefZoomStyle, - 'values' => array( 'default', 'small', 'large' ), + 'values' => [ 'default', 'small', 'large' ], 'message' => 'maps-googlemaps3-par-zoomstyle', 'post-format' => 'strtoupper', - ); + ]; - $params['typestyle'] = array( + $params['typestyle'] = [ 'default' => $egMapsGMaps3DefTypeStyle, 'values' => array_keys( self::$typeControlStyles ), 'message' => 'maps-googlemaps3-par-typestyle', 'post-format' => function( $value ) { - return MapsGoogleMaps3::$typeControlStyles[strtolower( $value )]; + return self::$typeControlStyles[strtolower( $value )]; }, - ); + ]; - $params['autoinfowindows'] = array( + $params['autoinfowindows'] = [ 'type' => 'boolean', 'default' => $egMapsGMaps3AutoInfoWindows, 'message' => 'maps-googlemaps3-par-autoinfowindows', - ); + ]; - $params['resizable'] = array( + $params['resizable'] = [ 'type' => 'boolean', 'default' => $egMapsResizableByDefault, - 'message' => 'maps-googlemaps3-par-resizable', - ); + 'message' => 'maps-par-resizable', + ]; - $params['kmlrezoom'] = array( + $params['kmlrezoom'] = [ 'type' => 'boolean', 'default' => $GLOBALS['egMapsRezoomForKML'], 'message' => 'maps-googlemaps3-par-kmlrezoom', - ); + ]; - $params['poi'] = array( + $params['poi'] = [ 'type' => 'boolean', 'default' => $GLOBALS['egMapsShowPOI'], 'message' => 'maps-googlemaps3-par-poi', - ); + ]; - $params['markercluster'] = array( + $params['markercluster'] = [ 'type' => 'boolean', 'default' => false, - 'message' => 'maps-googlemaps3-par-markercluster', - ); + 'message' => 'maps-par-markercluster', + ]; + + $params['clustergridsize'] = [ + 'type' => 'integer', + 'default' => 60, + 'message' => 'maps-googlemaps3-par-clustergridsize', + ]; + + $params['clustermaxzoom'] = [ + 'type' => 'integer', + 'default' => 20, + 'message' => 'maps-par-clustermaxzoom', + ]; + + $params['clusterzoomonclick'] = [ + 'type' => 'boolean', + 'default' => true, + 'message' => 'maps-par-clusterzoomonclick', + ]; + + $params['clusteraveragecenter'] = [ + 'type' => 'boolean', + 'default' => true, + 'message' => 'maps-googlemaps3-par-clusteraveragecenter', + ]; + + $params['clusterminsize'] = [ + 'type' => 'integer', + 'default' => 2, + 'message' => 'maps-googlemaps3-par-clusterminsize', + ]; - $params['tilt'] = array( + $params['tilt'] = [ 'type' => 'integer', 'default' => $egMapsGMaps3DefaultTilt, 'message' => 'maps-googlemaps3-par-tilt', - ); + ]; - $params['imageoverlays'] = array( + $params['imageoverlays'] = [ 'type' => 'mapsimageoverlay', - 'default' => array(), + 'default' => [], 'delimiter' => ';', 'islist' => true, 'message' => 'maps-googlemaps3-par-imageoverlays', - ); + ]; - $params['kml'] = array( - 'default' => array(), - 'message' => 'maps-googlemaps3-par-kml', + $params['kml'] = [ + 'default' => [], + 'message' => 'maps-par-kml', 'islist' => true, // new MapsParamFile() FIXME - ); + ]; - $params['gkml'] = array( - 'default' => array(), + $params['gkml'] = [ + 'default' => [], 'message' => 'maps-googlemaps3-par-gkml', 'islist' => true, - ); + ]; - $params['fusiontables'] = array( - 'default' => array(), + $params['fusiontables'] = [ + 'default' => [], 'message' => 'maps-googlemaps3-par-fusiontables', 'islist' => true, - ); + ]; - $params['searchmarkers'] = array( + $params['searchmarkers'] = [ 'default' => '', - 'message' => 'maps-googlemaps3-par-searchmarkers', + 'message' => 'maps-par-searchmarkers', // new CriterionSearchMarkers() FIXME - ); + ]; - $params['enablefullscreen'] = array( + $params['enablefullscreen'] = [ 'type' => 'boolean', 'default' => false, - 'message' => 'maps-googlemaps3-par-enable-fullscreen', - ); + 'message' => 'maps-par-enable-fullscreen', + ]; } /** - * @see iMappingService::getDefaultZoom - * * @since 0.6.5 */ public function getDefaultZoom() { @@ -275,22 +282,27 @@ public static function getLayerNames() { * @return array */ protected function getDependencies() { - return array( + return [ self::getApiScript( is_string( $GLOBALS['egMapsGMaps3Language'] ) ? $GLOBALS['egMapsGMaps3Language'] : $GLOBALS['egMapsGMaps3Language']->getCode() ) - ); + ]; } - public static function getApiScript( $langCode, array $urlArgs = array() ) { + public static function getApiScript( $langCode, array $urlArgs = [] ) { $urlArgs = array_merge( - array( - 'language' => self::getMappedLanguageCode( $langCode ), - 'sensor' => 'false' - ), + [ + 'language' => self::getMappedLanguageCode( $langCode ) + ], $urlArgs ); + if ( $GLOBALS['egMapsGMaps3ApiKey'] !== '' ) { + $urlArgs['key'] = $GLOBALS['egMapsGMaps3ApiKey']; + } + if ( $GLOBALS['egMapsGMaps3ApiVersion'] !== '' ) { + $urlArgs['v'] = $GLOBALS['egMapsGMaps3ApiVersion']; + } return Html::linkedScript( '//maps.googleapis.com/maps/api/js?' . wfArrayToCgi( $urlArgs ) ); } @@ -303,11 +315,11 @@ public static function getApiScript( $langCode, array $urlArgs = array() ) { * @return string The mapped code */ protected static function getMappedLanguageCode( $code ) { - $mappings = array( + $mappings = [ 'en_gb' => 'en-gb',// v3 supports en_gb - but wants us to call it en-gb 'he' => 'iw', // iw is googlish for hebrew 'fj' => 'fil', // google does not support Fijian - use Filipino as close(?) supported relative - ); + ]; if ( array_key_exists( $code, $mappings ) ) { $code = $mappings[$code]; @@ -326,7 +338,7 @@ protected static function getMappedLanguageCode( $code ) { public function getResourceModules() { return array_merge( parent::getResourceModules(), - array( 'ext.maps.googlemaps3' ) + [ 'ext.maps.googlemaps3' ] ); } } diff --git a/includes/services/GoogleMaps3/ext.maps.googlemaps3.js b/includes/services/GoogleMaps3/ext.maps.googlemaps3.js index e91248554..ce5880c2e 100644 --- a/includes/services/GoogleMaps3/ext.maps.googlemaps3.js +++ b/includes/services/GoogleMaps3/ext.maps.googlemaps3.js @@ -15,10 +15,11 @@ else { $( '.maps-googlemaps3' ).each( function() { var $this = $( this ); - $this.googlemaps( $.parseJSON( $this.find( 'div').text() ) ); + var map = $this.googlemaps( $.parseJSON( $this.find( 'div').text() ) ); + window.maps.googlemapsList.push(map); } ); } } ); -})( window.jQuery, mediaWiki ); \ No newline at end of file +})( window.jQuery, mediaWiki ); diff --git a/includes/services/GoogleMaps3/gm3-util-library/README b/includes/services/GoogleMaps3/gm3-util-library/README index e6cb45c71..64a2208cf 100644 --- a/includes/services/GoogleMaps3/gm3-util-library/README +++ b/includes/services/GoogleMaps3/gm3-util-library/README @@ -1,2 +1,6 @@ ==markerclusterer.js and markerwithlabel.js== -Is fetched from trunk @ http://google-maps-utility-library-v3.googlecode.com +Is fetched from trunk @ https://github.com/googlemaps/v3-utility-library + +On update don't forget to add the following line at the end of the file: + + window.MarkerClusterer = MarkerClusterer; diff --git a/includes/services/GoogleMaps3/gm3-util-library/markerclusterer.js b/includes/services/GoogleMaps3/gm3-util-library/markerclusterer.js index f8dcc5830..e5379c62a 100644 --- a/includes/services/GoogleMaps3/gm3-util-library/markerclusterer.js +++ b/includes/services/GoogleMaps3/gm3-util-library/markerclusterer.js @@ -3,7 +3,7 @@ /** * @name MarkerClustererPlus for Google Maps V3 - * @version 2.0.9 [February 20, 2012] + * @version 2.1.2 [May 28, 2014] * @author Gary Little * @fileoverview * The library creates and manages per-zoom-level clusters for large amounts of markers. @@ -15,14 +15,13 @@ * >V3 MarkerClusterer
port by Luke Mahe. MarkerClustererPlus was created by Gary Little. *

* v2.0 release: MarkerClustererPlus v2.0 is backward compatible with MarkerClusterer v1.0. It - * adds support for the ignoreHidden, title, printable, - * batchSizeIE, and calculator properties as well as support for - * four more events. It also allows greater control over the styling of the text that appears - * on the cluster marker. The documentation has been significantly improved and the overall - * code has been simplified and polished. Very large numbers of markers can now be managed - * without causing Javascript timeout errors on Internet Explorer. Note that the name of the - * clusterclick event has been deprecated. The new name is click, - * so please change your application code now. + * adds support for the ignoreHidden, title, batchSizeIE, + * and calculator properties as well as support for four more events. It also allows + * greater control over the styling of the text that appears on the cluster marker. The + * documentation has been significantly improved and the overall code has been simplified and + * polished. Very large numbers of markers can now be managed without causing Javascript timeout + * errors on Internet Explorer. Note that the name of the clusterclick event has been + * deprecated. The new name is click, so please change your application code now. */ /** @@ -47,36 +46,34 @@ * style the cluster icon is determined by calling the calculator function. * * @property {string} url The URL of the cluster icon image file. Required. - * @property {number} height The height (in pixels) of the cluster icon. Required. - * @property {number} width The width (in pixels) of the cluster icon. Required. - * @property {Array} [anchor] The anchor position (in pixels) of the label text to be shown on - * the cluster icon, relative to the top left corner of the icon. - * The format is [yoffset, xoffset]. The yoffset must be positive - * and less than height and the xoffset must be positive and less - * than width. The default is to anchor the label text so that it is centered - * on the icon. + * @property {number} height The display height (in pixels) of the cluster icon. Required. + * @property {number} width The display width (in pixels) of the cluster icon. Required. + * @property {Array} [anchorText] The position (in pixels) from the center of the cluster icon to + * where the text label is to be centered and drawn. The format is [yoffset, xoffset] + * where yoffset increases as you go down from center and xoffset + * increases to the right of center. The default is [0, 0]. * @property {Array} [anchorIcon] The anchor position (in pixels) of the cluster icon. This is the * spot on the cluster icon that is to be aligned with the cluster position. The format is * [yoffset, xoffset] where yoffset increases as you go down and - * xoffset increases to the right. The default anchor position is the center of the - * cluster icon. + * xoffset increases to the right of the top-left corner of the icon. The default + * anchor position is the center of the cluster icon. * @property {string} [textColor="black"] The color of the label text shown on the * cluster icon. * @property {number} [textSize=11] The size (in pixels) of the label text shown on the * cluster icon. - * @property {number} [textDecoration="none"] The value of the CSS text-decoration + * @property {string} [textDecoration="none"] The value of the CSS text-decoration * property for the label text shown on the cluster icon. - * @property {number} [fontWeight="bold"] The value of the CSS font-weight + * @property {string} [fontWeight="bold"] The value of the CSS font-weight * property for the label text shown on the cluster icon. - * @property {number} [fontStyle="normal"] The value of the CSS font-style + * @property {string} [fontStyle="normal"] The value of the CSS font-style * property for the label text shown on the cluster icon. - * @property {number} [fontFamily="Arial,sans-serif"] The value of the CSS font-family + * @property {string} [fontFamily="Arial,sans-serif"] The value of the CSS font-family * property for the label text shown on the cluster icon. * @property {string} [backgroundPosition="0 0"] The position of the cluster icon image * within the image defined by url. The format is "xpos ypos" * (the same format as for the CSS background-position property). You must set * this property appropriately when the image defined by url represents a sprite - * containing multiple images. + * containing multiple images. Note that the position must be specified in px units. */ /** * @name ClusterIconInfo @@ -86,6 +83,9 @@ * @property {string} text The text of the label to be shown on the cluster icon. * @property {number} index The index plus 1 of the element in the styles * array to be used to style the cluster icon. + * @property {string} title The tooltip to display when the mouse moves over the cluster icon. + * If this value is undefined or "", title is set to the + * value of the title property passed to the MarkerClusterer. */ /** * A cluster icon. @@ -101,6 +101,7 @@ function ClusterIcon(cluster, styles) { cluster.getMarkerClusterer().extend(ClusterIcon, google.maps.OverlayView); this.cluster_ = cluster; + this.className_ = cluster.getMarkerClusterer().getClusterClass(); this.styles_ = styles; this.center_ = null; this.div_ = null; @@ -120,6 +121,7 @@ ClusterIcon.prototype.onAdd = function () { var cDraggingMapByCluster; this.div_ = document.createElement("div"); + this.div_.className = this.className_; if (this.visible_) { this.show(); } @@ -127,7 +129,7 @@ ClusterIcon.prototype.onAdd = function () { this.getPanes().overlayMouseTarget.appendChild(this.div_); // Fix for Issue 157 - google.maps.event.addListener(this.getMap(), "bounds_changed", function () { + this.boundsChangedListener_ = google.maps.event.addListener(this.getMap(), "bounds_changed", function () { cDraggingMapByCluster = cMouseDownInCluster; }); @@ -139,6 +141,7 @@ ClusterIcon.prototype.onAdd = function () { google.maps.event.addDomListener(this.div_, "click", function (e) { cMouseDownInCluster = false; if (!cDraggingMapByCluster) { + var theBounds; var mz; var mc = cClusterIcon.cluster_.getMarkerClusterer(); /** @@ -155,11 +158,16 @@ ClusterIcon.prototype.onAdd = function () { if (mc.getZoomOnClick()) { // Zoom into the cluster. mz = mc.getMaxZoom(); - mc.getMap().fitBounds(cClusterIcon.cluster_.getBounds()); - // Don't zoom beyond the max zoom level - if (mz !== null && (mc.getMap().getZoom() > mz)) { - mc.getMap().setZoom(mz + 1); - } + theBounds = cClusterIcon.cluster_.getBounds(); + mc.getMap().fitBounds(theBounds); + // There is a fix for Issue 170 here: + setTimeout(function () { + mc.getMap().fitBounds(theBounds); + // Don't zoom beyond the max zoom level + if (mz !== null && (mc.getMap().getZoom() > mz)) { + mc.getMap().setZoom(mz + 1); + } + }, 100); } // Prevent event propagation to the map: @@ -200,6 +208,7 @@ ClusterIcon.prototype.onAdd = function () { ClusterIcon.prototype.onRemove = function () { if (this.div_ && this.div_.parentNode) { this.hide(); + google.maps.event.removeListener(this.boundsChangedListener_); google.maps.event.clearInstanceListeners(this.div_); this.div_.parentNode.removeChild(this.div_); this.div_ = null; @@ -235,15 +244,38 @@ ClusterIcon.prototype.hide = function () { */ ClusterIcon.prototype.show = function () { if (this.div_) { + var img = ""; + // NOTE: values must be specified in px units + var bp = this.backgroundPosition_.split(" "); + var spriteH = parseInt(bp[0].replace(/^\s+|\s+$/g, ""), 10); + var spriteV = parseInt(bp[1].replace(/^\s+|\s+$/g, ""), 10); var pos = this.getPosFromLatLng_(this.center_); this.div_.style.cssText = this.createCss(pos); - if (this.cluster_.printable_) { - // (Would like to use "width: inherit;" below, but doesn't work with MSIE) - this.div_.innerHTML = "

" + this.sums_.text + "
"; + img = ""; + this.div_.innerHTML = img + "
" + this.sums_.text + "
"; + if (typeof this.sums_.title === "undefined" || this.sums_.title === "") { + this.div_.title = this.cluster_.getMarkerClusterer().getTitle(); } else { - this.div_.innerHTML = this.sums_.text; + this.div_.title = this.sums_.title; } - this.div_.title = this.cluster_.getMarkerClusterer().getTitle(); this.div_.style.display = ""; } this.visible_ = true; @@ -263,7 +295,7 @@ ClusterIcon.prototype.useStyle = function (sums) { this.url_ = style.url; this.height_ = style.height; this.width_ = style.width; - this.anchor_ = style.anchor; + this.anchorText_ = style.anchorText || [0, 0]; this.anchorIcon_ = style.anchorIcon || [parseInt(this.height_ / 2, 10), parseInt(this.width_ / 2, 10)]; this.textColor_ = style.textColor || "black"; this.textSize_ = style.textSize || 11; @@ -293,38 +325,9 @@ ClusterIcon.prototype.setCenter = function (center) { */ ClusterIcon.prototype.createCss = function (pos) { var style = []; - if (!this.cluster_.printable_) { - style.push('background-image:url(' + this.url_ + ');'); - style.push('background-position:' + this.backgroundPosition_ + ';'); - } - - if (typeof this.anchor_ === 'object') { - if (typeof this.anchor_[0] === 'number' && this.anchor_[0] > 0 && - this.anchor_[0] < this.height_) { - style.push('height:' + (this.height_ - this.anchor_[0]) + - 'px; padding-top:' + this.anchor_[0] + 'px;'); - } else { - style.push('height:' + this.height_ + 'px; line-height:' + this.height_ + - 'px;'); - } - if (typeof this.anchor_[1] === 'number' && this.anchor_[1] > 0 && - this.anchor_[1] < this.width_) { - style.push('width:' + (this.width_ - this.anchor_[1]) + - 'px; padding-left:' + this.anchor_[1] + 'px;'); - } else { - style.push('width:' + this.width_ + 'px; text-align:center;'); - } - } else { - style.push('height:' + this.height_ + 'px; line-height:' + - this.height_ + 'px; width:' + this.width_ + 'px; text-align:center;'); - } - - style.push('cursor:pointer; top:' + pos.y + 'px; left:' + - pos.x + 'px; color:' + this.textColor_ + '; position:absolute; font-size:' + - this.textSize_ + 'px; font-family:' + this.fontFamily_ + '; font-weight:' + - this.fontWeight_ + '; font-style:' + this.fontStyle_ + '; text-decoration:' + - this.textDecoration_ + ';'); - + style.push("cursor: pointer;"); + style.push("position: absolute; top: " + pos.y + "px; left: " + pos.x + "px;"); + style.push("width: " + this.width_ + "px; height: " + this.height_ + "px;"); return style.join(""); }; @@ -339,6 +342,8 @@ ClusterIcon.prototype.getPosFromLatLng_ = function (latlng) { var pos = this.getProjection().fromLatLngToDivPixel(latlng); pos.x -= this.anchorIcon_[1]; pos.y -= this.anchorIcon_[0]; + pos.x = parseInt(pos.x, 10); + pos.y = parseInt(pos.y, 10); return pos; }; @@ -356,7 +361,6 @@ function Cluster(mc) { this.gridSize_ = mc.getGridSize(); this.minClusterSize_ = mc.getMinimumClusterSize(); this.averageCenter_ = mc.getAverageCenter(); - this.printable_ = mc.getPrintable(); this.markers_ = []; this.center_ = null; this.bounds_ = null; @@ -598,11 +602,9 @@ Cluster.prototype.isMarkerAlreadyAdded_ = function (marker) { * text property of the result returned by the default calculator). * If set to true and you change the visibility of a marker being clustered, be * sure to also call MarkerClusterer.repaint(). - * @property {boolean} [printable=false] Whether to make the cluster icons printable. Do not - * set to true if the url fields in the styles array - * refer to image sprite files. * @property {string} [title=""] The tooltip to display when the mouse moves over a cluster - * marker. + * marker. (Alternatively, you can use a custom calculator function to specify a + * different tooltip for each cluster marker.) * @property {function} [calculator=MarkerClusterer.CALCULATOR] The function used to determine * the text to be displayed on a cluster marker and the index indicating which style to use * for the cluster marker. The input parameters for the function are (1) the array of markers @@ -615,13 +617,23 @@ Cluster.prototype.isMarkerAlreadyAdded_ = function (marker) { * index minus 1. For example, the default calculator returns a * text value of "125" and an index of 3 * for a cluster icon representing 125 markers so the element used in the styles - * array is 2. + * array is 2. A calculator may also return a title + * property that contains the text of the tooltip to be used for the cluster marker. If + * title is not defined, the tooltip is set to the value of the title + * property for the MarkerClusterer. + * @property {string} [clusterClass="cluster"] The name of the CSS class defining general styles + * for the cluster markers. Use this class to define CSS styles that are not set up by the code + * that processes the styles array. * @property {Array} [styles] An array of {@link ClusterIconStyle} elements defining the styles * of the cluster markers to be used. The element to be used to style a given cluster marker * is determined by the function defined by the calculator property. * The default is an array of {@link ClusterIconStyle} elements whose properties are derived * from the values for imagePath, imageExtension, and * imageSizes. + * @property {boolean} [enableRetinaIcons=false] Whether to allow the use of cluster icons that + * have sizes that are some multiple (typically double) of their actual display size. Icons such + * as these look better when viewed on high-resolution monitors such as Apple's Retina displays. + * Note: if this property is true, sprites cannot be used as cluster icons. * @property {number} [batchSize=MarkerClusterer.BATCH_SIZE] Set this property to the * number of markers to be processed in a single batch when using a browser other than * Internet Explorer (for Internet Explorer, use the batchSizeIE property instead). @@ -685,9 +697,9 @@ function MarkerClusterer(map, opt_markers, opt_options) { if (opt_options.ignoreHidden !== undefined) { this.ignoreHidden_ = opt_options.ignoreHidden; } - this.printable_ = false; - if (opt_options.printable !== undefined) { - this.printable_ = opt_options.printable; + this.enableRetinaIcons_ = false; + if (opt_options.enableRetinaIcons !== undefined) { + this.enableRetinaIcons_ = opt_options.enableRetinaIcons; } this.imagePath_ = opt_options.imagePath || MarkerClusterer.IMAGE_PATH; this.imageExtension_ = opt_options.imageExtension || MarkerClusterer.IMAGE_EXTENSION; @@ -695,6 +707,7 @@ function MarkerClusterer(map, opt_markers, opt_options) { this.calculator_ = opt_options.calculator || MarkerClusterer.CALCULATOR; this.batchSize_ = opt_options.batchSize || MarkerClusterer.BATCH_SIZE; this.batchSizeIE_ = opt_options.batchSizeIE || MarkerClusterer.BATCH_SIZE_IE; + this.clusterClass_ = opt_options.clusterClass || "cluster"; if (navigator.userAgent.toLowerCase().indexOf("msie") !== -1) { // Try to avoid IE timeout when processing a huge number of markers: @@ -728,8 +741,8 @@ MarkerClusterer.prototype.onAdd = function () { // zoom slider is clicked, a "zoom_changed" event is fired even though // the map doesn't zoom out any further. In this situation, no "idle" // event is triggered so the cluster markers that have been removed - // do not get redrawn. - if (this.getZoom() === 0) { + // do not get redrawn. Same goes for a zoom in at maxZoom. + if (this.getZoom() === (this.get("minZoom") || 0) || this.getZoom() === this.get("maxZoom")) { google.maps.event.trigger(this, "idle"); } }), @@ -751,7 +764,9 @@ MarkerClusterer.prototype.onRemove = function () { // Put all the managed markers back on the map: for (i = 0; i < this.markers_.length; i++) { - this.markers_[i].setMap(this.activeMap_); + if (this.markers_[i].getMap() !== this.activeMap_) { + this.markers_[i].setMap(this.activeMap_); + } } // Remove all clusters: @@ -972,6 +987,26 @@ MarkerClusterer.prototype.setIgnoreHidden = function (ignoreHidden) { }; +/** + * Returns the value of the enableRetinaIcons property. + * + * @return {boolean} True if enableRetinaIcons property is set. + */ +MarkerClusterer.prototype.getEnableRetinaIcons = function () { + return this.enableRetinaIcons_; +}; + + +/** + * Sets the value of the enableRetinaIcons property. + * + * @param {boolean} enableRetinaIcons The value of the enableRetinaIcons property. + */ +MarkerClusterer.prototype.setEnableRetinaIcons = function (enableRetinaIcons) { + this.enableRetinaIcons_ = enableRetinaIcons; +}; + + /** * Returns the value of the imageExtension property. * @@ -1054,42 +1089,42 @@ MarkerClusterer.prototype.setCalculator = function (calculator) { /** - * Returns the value of the printable property. + * Returns the value of the batchSizeIE property. * - * @return {boolean} the value of the printable property. + * @return {number} the value of the batchSizeIE property. */ -MarkerClusterer.prototype.getPrintable = function () { - return this.printable_; +MarkerClusterer.prototype.getBatchSizeIE = function () { + return this.batchSizeIE_; }; /** - * Sets the value of the printable property. + * Sets the value of the batchSizeIE property. * - * @param {boolean} printable The value of the printable property. + * @param {number} batchSizeIE The value of the batchSizeIE property. */ -MarkerClusterer.prototype.setPrintable = function (printable) { - this.printable_ = printable; +MarkerClusterer.prototype.setBatchSizeIE = function (batchSizeIE) { + this.batchSizeIE_ = batchSizeIE; }; /** - * Returns the value of the batchSizeIE property. + * Returns the value of the clusterClass property. * - * @return {number} the value of the batchSizeIE property. + * @return {string} the value of the clusterClass property. */ -MarkerClusterer.prototype.getBatchSizeIE = function () { - return this.batchSizeIE_; +MarkerClusterer.prototype.getClusterClass = function () { + return this.clusterClass_; }; /** - * Sets the value of the batchSizeIE property. + * Sets the value of the clusterClass property. * - * @param {number} batchSizeIE The value of the batchSizeIE property. + * @param {string} clusterClass The value of the clusterClass property. */ -MarkerClusterer.prototype.setBatchSizeIE = function (batchSizeIE) { - this.batchSizeIE_ = batchSizeIE; +MarkerClusterer.prototype.setClusterClass = function (clusterClass) { + this.clusterClass_ = clusterClass; }; @@ -1156,9 +1191,11 @@ MarkerClusterer.prototype.addMarker = function (marker, opt_nodraw) { * @param {boolean} [opt_nodraw] Set to true to prevent redrawing. */ MarkerClusterer.prototype.addMarkers = function (markers, opt_nodraw) { - var i; - for (i = 0; i < markers.length; i++) { - this.pushMarkerTo_(markers[i]); + var key; + for (key in markers) { + if (markers.hasOwnProperty(key)) { + this.pushMarkerTo_(markers[key]); + } } if (!opt_nodraw) { this.redraw_(); @@ -1380,7 +1417,7 @@ MarkerClusterer.prototype.distanceBetweenPoints_ = function (p1, p2) { var dLon = (p2.lng() - p1.lng()) * Math.PI / 180; var a = Math.sin(dLat / 2) * Math.sin(dLat / 2) + Math.cos(p1.lat() * Math.PI / 180) * Math.cos(p2.lat() * Math.PI / 180) * - Math.sin(dLon / 2) * Math.sin(dLon / 2); + Math.sin(dLon / 2) * Math.sin(dLon / 2); var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a)); var d = R * c; return d; @@ -1535,6 +1572,7 @@ MarkerClusterer.prototype.extend = function (obj1, obj2) { */ MarkerClusterer.CALCULATOR = function (markers, numStyles) { var index = 0; + var title = ""; var count = markers.length.toString(); var dv = count; @@ -1546,7 +1584,8 @@ MarkerClusterer.CALCULATOR = function (markers, numStyles) { index = Math.min(index, numStyles); return { text: count, - index: index + index: index, + title: title }; }; @@ -1575,7 +1614,7 @@ MarkerClusterer.BATCH_SIZE_IE = 500; * @type {string} * @constant */ -MarkerClusterer.IMAGE_PATH = "//google-maps-utility-library-v3.googlecode.com/svn/trunk/markerclustererplus/images/m"; +MarkerClusterer.IMAGE_PATH = "../images/m"; /** diff --git a/includes/services/GoogleMaps3/jquery.googlemap.js b/includes/services/GoogleMaps3/jquery.googlemap.js index 09065a133..194e775cc 100644 --- a/includes/services/GoogleMaps3/jquery.googlemap.js +++ b/includes/services/GoogleMaps3/jquery.googlemap.js @@ -3,6 +3,7 @@ * @see http://www.mediawiki.org/wiki/Extension:Maps * * @author Jeroen De Dauw + * @author Peter Grassberger < petertheone@gmail.com > */ (function ($, mw) { @@ -53,7 +54,7 @@ var getBounds = function() { if (( options.centre === false || options.zoom === false ) && options.locations.length > 1) { - bounds = new google.maps.LatLngBounds(); + var bounds = new google.maps.LatLngBounds(); for (var i = _this.markers.length - 1; i >= 0; i--) { bounds.extend(_this.markers[i].getPosition()); @@ -64,7 +65,7 @@ }; var setZoom = function(bounds) { - if (options.zoom === false) { + if (options.zoom === false) { _this.map.fitBounds(bounds); } else { @@ -89,7 +90,7 @@ labelClass:'markerwithlabel' }; - if (markerData.icon !== '') { + if (!markerData.hasOwnProperty('icon') || markerData.icon !== '') { markerOptions.icon = markerData.icon; } @@ -161,6 +162,10 @@ * Removes all markers from the map. */ this.removeMarkers = function () { + if (this.markercluster) { + this.markercluster.setMap(null); + this.markercluster = null; + } for (var i = this.markers.length - 1; i >= 0; i--) { this.markers[i].setMap(null); } @@ -215,7 +220,7 @@ doc[i].gpolygons, doc[i].gpolylines, doc[i].ggroundoverlays - ]); + ]); } } } @@ -225,7 +230,7 @@ map:_this.map, zoom:options.kmlrezoom, failedParse:function(){ - alert(mediaWiki.msg('maps-kml-parsing-failed')); + alert(mw.msg('maps-kml-parsing-failed')); } }); geoXml.options.afterParse = function(docs){ @@ -255,8 +260,6 @@ label.appendChild(text); toggleDiv.appendChild(label); - - console.log(toggleDiv); })(docs[i]); } _this.map.controls[google.maps.ControlPosition.TOP_RIGHT].push(toggleDiv); @@ -444,7 +447,26 @@ } } this.map.fitBounds(bounds); - } + }; + + this.createMarkerCluster = function() { + if ( !options.markercluster ) { + return; + } + if (this.markercluster) { + this.markercluster.setMap(null); + this.markercluster = null; + } + this.markercluster = new MarkerClusterer( this.map, this.markers, { + imagePath: mw.config.get( 'wgScriptPath' ) + + '/extensions/Maps/includes/images/m', + gridSize: this.options.clustergridsize, + maxZoom: this.options.clustermaxzoom, + zoomOnClick: this.options.clusterzoomonclick, + averageCenter: this.options.clusteraveragecenter, + minimumClusterSize: this.options.clusterminsize + } ); + }; this.initializeMap = function () { var mapOptions = { @@ -458,6 +480,7 @@ mapOptions.mapTypeControl = $.inArray('type', options.controls) != -1; mapOptions.scaleControl = $.inArray('scale', options.controls) != -1; mapOptions.streetViewControl = $.inArray('streetview', options.controls) != -1; + mapOptions.rotateControl = $.inArray('rotate', options.controls) != -1; for (i in options.types) { if (typeof( options.types[i] ) !== 'function') { @@ -613,21 +636,11 @@ /** * Allows grouping of markers. */ - if ( options.markercluster ) { - mw.loader.using( - 'ext.maps.gm3.markercluster', - function() { - _this.markercluster = new MarkerClusterer( _this.map, _this.markers, { - averageCenter: true - } ); - } - ); - } - + this.createMarkerCluster(); if (options.searchmarkers) { - var searchBoxValue = mediaWiki.msg('maps-searchmarkers-text'); + var searchBoxValue = mw.msg('maps-searchmarkers-text'); var searchBox = $(''); var searchContainer = document.createElement('div'); searchContainer.style.padding = '5px'; @@ -647,7 +660,7 @@ var marker = _this.markers[i]; if (options.searchmarkers == 'title') { haystack = marker.title; - } else if (options.searchmarkers == 'all') { + } else { haystack = marker.title + marker.text; } @@ -780,7 +793,7 @@ controlUI.style.textAlign = 'center'; controlUI.style.boxShadow = 'rgba(0, 0, 0, 0.298039) 0px 1px 4px -1px'; controlUI.style.backgroundClip = 'padding-box'; - controlUI.title = mediaWiki.msg('maps-fullscreen-button-tooltip'); + controlUI.title = mw.msg('maps-fullscreen-button-tooltip'); controlDiv.appendChild(controlUI); var controlText = document.createElement('div'); @@ -789,7 +802,7 @@ controlText.style.fontWeight = '400'; controlText.style.color = 'rgb(86, 86, 86)'; controlText.style.padding = '1px 6px'; - controlText.innerHTML = mediaWiki.msg('maps-fullscreen-button'); + controlText.innerHTML = mw.msg('maps-fullscreen-button'); controlUI.appendChild(controlText); google.maps.event.addDomListener(controlUI, 'click', function() { @@ -846,10 +859,10 @@ }; if ( event.latLng === undefined ) { - this.openWindow.open( this.map, this ); + this.openWindow.open( _this.map, this ); } else { - this.openWindow.open( this.map ); + this.openWindow.open( _this.map ); } } @@ -860,14 +873,20 @@ } }else{ google.maps.event.addListener(object, 'rightclick', function (event) { - prompt(mediaWiki.msg('maps-copycoords-prompt'), event.latLng.lat() + ',' + event.latLng.lng()); + prompt(mw.msg('maps-copycoords-prompt'), event.latLng.lat() + ',' + event.latLng.lng()); }); } } //Complete path to OpenLayers WMS layer - this.setup(); + if (!options.markercluster) { + this.setup(); + } else { + mw.loader.using( 'ext.maps.gm3.markercluster', function() { + _this.setup(); + } ); + } return this; diff --git a/includes/services/Leaflet/Leaflet.php b/includes/services/Leaflet/Leaflet.php index 00b6f7256..9baaac852 100644 --- a/includes/services/Leaflet/Leaflet.php +++ b/includes/services/Leaflet/Leaflet.php @@ -1,7 +1,7 @@ array( 'ext.maps.common' ), + $wgResourceModules['ext.maps.leaflet'] = [ + 'dependencies' => [ 'ext.maps.common' ], 'localBasePath' => __DIR__, 'remoteExtPath' => end( $pathParts ), 'group' => 'ext.maps', - 'targets' => array( + 'targets' => [ 'mobile', 'desktop' - ), - 'scripts' => array( + ], + 'scripts' => [ 'jquery.leaflet.js', 'ext.maps.leaflet.js', - ), - 'messages' => array( + ], + 'messages' => [ 'maps-markers', 'maps-copycoords-prompt', 'maps-searchmarkers-text', - ), - ); -} ); - -/** - * Initialization function for the Leaflet service. - * - * @ingroup Leaflet - * - * @return boolean true - */ -function efMapsInitLeaflet() { - global $wgAutoloadClasses; + ], + ]; - $wgAutoloadClasses['MapsLeaflet'] = __DIR__ . '/Maps_Leaflet.php'; + $wgResourceModules['ext.maps.leaflet.fullscreen'] = [ + 'localBasePath' => __DIR__ . '/leaflet.fullscreen', + 'remoteExtPath' => end( $pathParts ) . '/leaflet.fullscreen', + 'group' => 'ext.maps', + 'targets' => [ + 'mobile', + 'desktop' + ], + 'scripts' => [ + 'Control.FullScreen.js', + ], + 'styles' => [ + 'Control.FullScreen.css', + ], + ]; - MapsMappingServices::registerService( 'leaflet', 'MapsLeaflet' ); - $leafletMaps = MapsMappingServices::getServiceInstance( 'leaflet' ); - $leafletMaps->addFeature( 'display_map', 'MapsDisplayMapRenderer' ); + $wgResourceModules['ext.maps.leaflet.markercluster'] = [ + 'localBasePath' => __DIR__ . '/leaflet.markercluster', + 'remoteExtPath' => end( $pathParts ), + 'group' => 'ext.maps', + 'targets' => [ + 'mobile', + 'desktop' + ], + 'scripts' => [ + 'leaflet.markercluster.js', + ], + 'styles' => [ + 'MarkerCluster.css', + ], + ]; - return true; -} + $wgResourceModules['ext.maps.leaflet.providers'] = [ + 'localBasePath' => __DIR__ . '/leaflet-providers', + 'remoteExtPath' => end( $pathParts ) . '/leaflet-providers', + 'group' => 'ext.maps', + 'targets' => [ + 'mobile', + 'desktop' + ], + 'scripts' => [ + 'leaflet-providers.js', + ], + ]; +} ); diff --git a/includes/services/Leaflet/Maps_Leaflet.php b/includes/services/Leaflet/Maps_Leaflet.php index ad8f246ee..0aad0d1e8 100644 --- a/includes/services/Leaflet/Maps_Leaflet.php +++ b/includes/services/Leaflet/Maps_Leaflet.php @@ -9,13 +9,10 @@ */ class MapsLeaflet extends MapsMappingService { - /** - * Constructor - */ public function __construct( $serviceName ) { parent::__construct( $serviceName, - array( 'leafletmaps', 'leaflet' ) + [ 'leafletmaps', 'leaflet' ] ); } @@ -25,30 +22,81 @@ public function __construct( $serviceName ) { * @since 3.0 */ public function addParameterInfo( array &$params ) { - $params['zoom'] = array( + global $GLOBALS; + + $params['zoom'] = [ 'type' => 'integer', - 'range' => array( 0, 20 ), + 'range' => [ 0, 20 ], 'default' => false, - 'message' => 'maps-leaflet-par-zoom' - ); + 'message' => 'maps-par-zoom' + ]; - $params['defzoom'] = array( + $params['defzoom'] = [ 'type' => 'integer', - 'range' => array( 0, 20 ), + 'range' => [ 0, 20 ], 'default' => self::getDefaultZoom(), 'message' => 'maps-leaflet-par-defzoom' - ); + ]; + + $params['layer'] = [ + 'type' => 'string', + 'values' => array_keys( $GLOBALS['egMapsLeafletAvailableLayers'], true, true ), + 'default' => $GLOBALS['egMapsLeafletLayer'], + 'message' =>'maps-leaflet-par-layer', + ]; - $params['resizable'] = array( + $params['overlaylayers'] = [ + 'type' => 'string', + 'values' => array_keys( $GLOBALS['egMapsLeafletAvailableOverlayLayers'], true, true ), + 'default' => $GLOBALS['egMapsLeafletOverlayLayers'], + 'message' =>'maps-leaflet-par-overlaylayers', + 'islist' => true, + ]; + + $params['resizable'] = [ 'type' => 'boolean', 'default' => $GLOBALS['egMapsResizableByDefault'], - 'message' => 'maps-leaflet-par-resizable' - ); + 'message' => 'maps-par-resizable' + ]; + + $params['enablefullscreen'] = [ + 'type' => 'boolean', + 'default' => false, + 'message' => 'maps-par-enable-fullscreen', + ]; + + $params['markercluster'] = [ + 'type' => 'boolean', + 'default' => false, + 'message' => 'maps-par-markercluster', + ]; + + $params['clustermaxzoom'] = [ + 'type' => 'integer', + 'default' => 20, + 'message' => 'maps-par-clustermaxzoom', + ]; + + $params['clusterzoomonclick'] = [ + 'type' => 'boolean', + 'default' => true, + 'message' => 'maps-par-clusterzoomonclick', + ]; + + $params['clustermaxradius'] = [ + 'type' => 'integer', + 'default' => 80, + 'message' => 'maps-par-maxclusterradius', + ]; + + $params['clusterspiderfy'] = [ + 'type' => 'boolean', + 'default' => true, + 'message' => 'maps-leaflet-par-clusterspiderfy', + ]; } /** - * @see iMappingService::getDefaultZoom - * * @since 3.0 */ public function getDefaultZoom() { @@ -80,17 +128,16 @@ public function getMapId( $increment = true ) { public function getResourceModules() { return array_merge( parent::getResourceModules(), - array( 'ext.maps.leaflet' ) + [ 'ext.maps.leaflet' ] ); } protected function getDependencies() { $leafletPath = $GLOBALS['wgScriptPath'] . '/extensions/Maps/includes/services/Leaflet/leaflet'; - return array( + return [ Html::linkedStyle( "$leafletPath/leaflet.css" ), - '', Html::linkedScript( "$leafletPath/leaflet.js" ), - ); + ]; } } diff --git a/includes/services/Leaflet/ext.maps.leaflet.js b/includes/services/Leaflet/ext.maps.leaflet.js index 175b8c2d7..94e23b9e1 100644 --- a/includes/services/Leaflet/ext.maps.leaflet.js +++ b/includes/services/Leaflet/ext.maps.leaflet.js @@ -11,7 +11,8 @@ $( '.maps-leaflet' ).each( function() { var $this = $( this ); - $this.leafletmaps( $.parseJSON( $this.find( 'div').text() ) ); + var map = $this.leafletmaps( $.parseJSON( $this.find( 'div').text() ) ); + window.maps.leafletList.push(map); } ); } ); diff --git a/includes/services/Leaflet/jquery.leaflet.js b/includes/services/Leaflet/jquery.leaflet.js index a2d6e3ef2..79d355a74 100644 --- a/includes/services/Leaflet/jquery.leaflet.js +++ b/includes/services/Leaflet/jquery.leaflet.js @@ -3,13 +3,16 @@ * @see https://www.mediawiki.org/wiki/Extension:Maps * * @author Pavel Astakhov < pastakhov@yandex.ru > + * @author Peter Grassberger < petertheone@gmail.com > */ -(function ($, mw) { +(function ($, mw, L, MQ) { $.fn.leafletmaps = function ( options ) { var _this = this; this.map = null; this.options = options; + this.markers = []; + this.markercluster = null; /** * array point of all map elements (markers, lines, polygons, etc.) @@ -18,15 +21,14 @@ this.points = []; /** - * Creates a new marker with the provided data, - * adds it to the map, and returns it. + * Creates a new marker with the provided data and returns it. * @param {Object} markerData Contains the fields lat, lon, title, text and icon * @return {L.Marker} */ - this.addMarker = function (properties) { + this.createMarker = function (properties) { this.points.push( new L.LatLng(properties.lat, properties.lon) ); - if (properties.icon === '') { + if (!properties.hasOwnProperty('icon') || properties.icon === '') { var icon = new L.Icon.Default(); } else { var icon = new L.Icon({ @@ -39,8 +41,47 @@ icon:icon }; - var marker = L.marker( [properties.lat, properties.lon], markerOptions ).addTo( this.map ); - if( properties.text.length > 0 ) marker.bindPopup( properties.text ); + var marker = L.marker( [properties.lat, properties.lon], markerOptions ); + if( properties.hasOwnProperty('text') && properties.text.length > 0 ) marker.bindPopup( properties.text ); + + return marker; + }; + + /** + * Creates a new marker with the provided data, adds it to the map + * and returns it. + * @param {Object} markerData Contains the fields lat, lon, title, text and icon + * @return {L.Marker} + */ + this.addMarker = function (properties) { + var marker = this.createMarker(properties); + if (!this.options.markercluster) { + marker.addTo( this.map ); + } + this.markers.push( marker ); + return marker; + }; + + this.removeMarker = function (marker) { + this.map.removeLayer(marker); + this.points = []; + this.markers = this.markers.filter(function(object) { + return object !== marker; + }); + }; + + this.removeMarkers = function () { + if (this.markercluster) { + this.map.removeLayer(this.markercluster); + this.markercluster = null; + } + var map = this.map; + $.each(this.markers, function(index, marker) { + map.removeLayer(marker); + }); + + this.points = []; + this.markers = []; }; this.addLine = function (properties) { @@ -121,34 +162,133 @@ L.rectangle( bounds, options ).addTo(this.map); }; + this.createMarkerCluster = function () { + if ( !options.markercluster ) { + return; + } + var markers = this.markers; + + var markercluster = new L.MarkerClusterGroup({ + maxClusterRadius: options.clustermaxradius, + disableClusteringAtZoom: options.clustermaxzoom + 1, + zoomToBoundsOnClick: options.clusterzoomonclick, + spiderfyOnMaxZoom: options.clusterspiderfy, + iconCreateFunction: function(cluster) { + var childCount = cluster.getChildCount(); + + var imagePath = mw.config.get( 'wgScriptPath' ) + + '/extensions/Maps/includes/images/m'; + + var styles = [ + { + iconUrl: imagePath + '1.png', + iconSize: [53, 52] + }, + { + iconUrl: imagePath + '2.png', + iconSize: [56, 55] + }, + { + iconUrl: imagePath + '3.png', + iconSize: [66, 65] + }, + { + iconUrl: imagePath + '4.png', + iconSize: [78, 77] + }, + { + iconUrl: imagePath + '5.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 + '' + }); + } + }); + $.each(this.markers, function(index, marker) { + markercluster.addLayer(marker); + }); + if (this.markercluster) { + this.map.removeLayer(this.markercluster); + this.markercluster = null; + } + this.map.addLayer(markercluster); + this.markercluster = markercluster; + }; + this.setup = function () { var mapOptions = {}; if (options.minzoom !== false ) mapOptions.minZoom = options.minzoom; if (options.maxzoom !== false ) mapOptions.maxZoom = options.maxzoom; + if (options.enablefullscreen) { + mapOptions.fullscreenControl = true; + mapOptions.fullscreenControlOptions= { + position: 'topleft' + }; + } + var map = L.map( this.get(0), mapOptions ).fitWorld(); this.map = map; // add an OpenStreetMap tile layer - L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', { + var layerOptions = { attribution: '© OpenStreetMap contributors' - }).addTo(map); + }; + if (options.layer === 'OpenStreetMap') { + new L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', layerOptions).addTo(map); + } else if (options.layer === 'MapQuestOpen') { + new MQ.TileLayer(layerOptions).addTo(map); + } else { + new L.tileLayer.provider(options.layer, layerOptions).addTo(map); + } + + $.each(options.overlaylayers, function(index, overlaylayer) { + L.tileLayer.provider(overlaylayer).addTo(_this.map); + }); if (options.resizable) { - mw.loader.using('ext.maps.resizable', function () { //TODO: Fix moving map when resized - _this.resizable(); - }); + //TODO: Fix moving map when resized + _this.resizable(); } if (!options.locations) { options.locations = []; } + // Add the markers. for (var i = options.locations.length - 1; i >= 0; i--) { this.addMarker(options.locations[i]); } + // Add markercluster + if (options.markercluster) { + this.createMarkerCluster(); + } + // Add lines if (options.lines) { for (var i = 0; i < options.lines.length; i++) { @@ -197,6 +337,7 @@ } break; } + this.points = []; } else { centre = new L.LatLng(options.centre.lat, options.centre.lon); } @@ -205,9 +346,28 @@ } }; - this.setup(); + this.getDependencies = function ( options ) { + var dependencies = []; + if (options.layer !== 'OpenStreetMap' || options.overlaylayers.length > 0) { + dependencies.push( 'ext.maps.leaflet.providers' ); + } + if (options.enablefullscreen) { + dependencies.push( 'ext.maps.leaflet.fullscreen' ); + } + if (options.resizable) { + dependencies.push( 'ext.maps.resizable' ); + } + if (options.markercluster) { + dependencies.push( 'ext.maps.leaflet.markercluster' ); + } + return dependencies; + }; + + mw.loader.using( this.getDependencies( options ) ).then( function() { + _this.setup(); + } ); return this; }; -})(jQuery, window.mediaWiki); +})(jQuery, window.mediaWiki, L, window.MQ); diff --git a/includes/services/Leaflet/leaflet-providers/.eslintrc b/includes/services/Leaflet/leaflet-providers/.eslintrc new file mode 100644 index 000000000..62f190384 --- /dev/null +++ b/includes/services/Leaflet/leaflet-providers/.eslintrc @@ -0,0 +1,39 @@ +{ + "rules": { + "camelcase": 2, + "quotes": [2, "single", "avoid-escape"], + "no-mixed-spaces-and-tabs": [2, "smart-tabs"], + "space-before-function-paren": 2, + "space-in-parens": 2, + "object-curly-spacing": 2, + "array-bracket-spacing": 2, + "computed-property-spacing": 2, + "space-before-blocks": 2, + "keyword-spacing": 2, + "no-lonely-if": 2, + "comma-style": 2, + "no-underscore-dangle": 0, + "no-constant-condition": 0, + "no-multi-spaces": 0, + "strict": 0, + "key-spacing": 0, + "no-shadow": 0, + "no-unused-vars": 2, + "eqeqeq": 2 + }, + "globals": { + "L": true, + "module": false, + "define": false, + "require": true + }, + "plugins": [ + "html" + ], + "settings": { + "html/report-bad-indent": 2 + }, + "env": { + "browser": true + } +} diff --git a/includes/services/Leaflet/leaflet-providers/.gitignore b/includes/services/Leaflet/leaflet-providers/.gitignore new file mode 100644 index 000000000..0e6b7ded0 --- /dev/null +++ b/includes/services/Leaflet/leaflet-providers/.gitignore @@ -0,0 +1 @@ +leaflet-providers.min.js diff --git a/includes/services/Leaflet/leaflet-providers/.mversionrc b/includes/services/Leaflet/leaflet-providers/.mversionrc new file mode 100644 index 000000000..2d7fc1a87 --- /dev/null +++ b/includes/services/Leaflet/leaflet-providers/.mversionrc @@ -0,0 +1,8 @@ +{ + "commitMessage": "Bumped version to %s", + "tagName": "%s", + "scripts": { + "preupdate": "npm run min", + "postupdate": "git push && git push --tags && npm publish" + } +} diff --git a/includes/services/Leaflet/leaflet-providers/.travis.yml b/includes/services/Leaflet/leaflet-providers/.travis.yml new file mode 100644 index 000000000..204dab530 --- /dev/null +++ b/includes/services/Leaflet/leaflet-providers/.travis.yml @@ -0,0 +1,8 @@ +--- +language: node_js +node_js: + - "0.10" +notifications: + email: false +git: + depth: 10 diff --git a/includes/services/Leaflet/leaflet-providers/CHANGELOG.md b/includes/services/Leaflet/leaflet-providers/CHANGELOG.md new file mode 100644 index 000000000..c6ba59db2 --- /dev/null +++ b/includes/services/Leaflet/leaflet-providers/CHANGELOG.md @@ -0,0 +1,46 @@ + +# Leaflet-providers changelog + +## 1.1.14 (2016-07-15) +- Remove MapQuest, fixes #219 +- Accidently skipped v1.1.12 and v1.1.13 + +## 1.1.11 (2016-06-04) + - Added protocol relativity to OSM FR, OSM HOT and Hydda providers (#214, #215). + +## 1.1.9 (2016-03-23) + - Re-added HERE layers #209, discussion in #206. + +## 1.1.8 (2016-03-22) + - Removed HERE layers #206 + +## 1.1.7 (2015-12-16) + - Removed Acetate tile layers #198 + +## 1.1.6 (2015-11-03) + - Removed most of the NLS layers per NLS request #193, fixes #178 + - Added new variants to the HERE provider #183 by [@andreaswc](https://github.com/andreaswc) + - Added some tests to make sure all the placeholders in the url template are replaced #188 + +## 1.1.5 (2015-10-01) + - Improvements for the NLS layers #182 by [@tomhughes](https://github.com/tomhughes) + - Check for valid bounds before fitting the preview map to undefined (fixes #185) + - Add bounds for FreeMapSK (fixes #184) + - Fix Stamen layers with `.jpg` extension (#187, fixes #184) + +## 1.1.4 (2015-09-27) + - Only include the interesting files in the npm package #180 + - Add GSGS_Ireland to NLS provider with `tms:true` to invert y-axis #181 + +## 1.1.3 (2015-09-26) + - Add various historical layers of the Natioanal library of Scotland (NLS) #179 + - Add a page to visually check bounds #179 + +## 1.1.2 (2015-09-05) + - Add CartoDB labels-only styles #170 by [@almccon](https://github.com/almccon) + - Implement commonjs module #172 + - Added retina URL option #177, [@routexl](https://github.com/routexl) + +## 1.1.1 (2015-06-22) + - Update Mapbox API to v4 #167 by [@gutenye](https://github.com/gutenye) + - Started maintaining a changelog in CHANGELOG.md. diff --git a/includes/services/Leaflet/leaflet-providers/CONTRIBUTING.md b/includes/services/Leaflet/leaflet-providers/CONTRIBUTING.md new file mode 100644 index 000000000..87766e997 --- /dev/null +++ b/includes/services/Leaflet/leaflet-providers/CONTRIBUTING.md @@ -0,0 +1,10 @@ +So you want to add a layer? +======= + +Yay! go add it to the leaflet-providers.js as long as it follows the following +rules: + +- Don't violate a providers TOS (if it exists, include a link to it) +- Don't pre-populate api keys with working keys. +- It should be a basic tile source, no exteral libraries etc. +- The owner hasn't asked us to remove it (hasn't happened yet) \ No newline at end of file diff --git a/includes/services/Leaflet/leaflet-providers/README.md b/includes/services/Leaflet/leaflet-providers/README.md new file mode 100644 index 000000000..0819e15ed --- /dev/null +++ b/includes/services/Leaflet/leaflet-providers/README.md @@ -0,0 +1,63 @@ +Leaflet-providers +================= +An extension to [Leaflet](http://leafletjs.com/) that contains configurations for various free[1](#what-is-free) tile providers. + +# Usage +Leaflet-providers [providers](#providers) are refered to with a `provider[.]`-string. Let's say you want to add the nice [Watercolor](http://maps.stamen.com/#watercolor/) style from Stamen to your map, you pass `Stamen.Watercolor` to the `L.tileLayer.provider`-constructor, which will return a [L.TileLayer](http://leafletjs.com/reference.html#tilelayer) instance for Stamens Watercolor tile layer. + +```Javascript +// add Stamen Watercolor to map. +L.tileLayer.provider('Stamen.Watercolor').addTo(map); +``` + +## Protocol relativity (`https://`-urls) + +Leaflet-providers tries to use `https://` if the page uses `https://` and the provider supports it. +You can force the use of `http://` by passing `force_http: true` in the options argument. + +## Retina tiles + +Some providers have retina tiles for which the URL only needs to be slightly adjusted, e.g. `-----@2x.png`. For this, add the retina option in the URL, e.g. `-----{retina}.png`, and set a retina value in the options, e.g. `retina: '@2x'`. If Leaflet detects a retina screen (`L.Browser.retina`), the retina option passed to the tileLayer is set to the value supplied, otherwise it's replaced by an empty string. + +# Providers + +Leaflet-providers provides tile layers from different providers, including *OpenStreetMap*, *Stamen*, *Esri* and *OpenWeatherMap*. The full listing of free to use layers can be [previewed](http://leaflet-extras.github.io/leaflet-providers/preview/index.html). The page will show you the name to use with `leaflet-providers.js` and the code to use it without dependencies. + +## Providers requiring registration + +In addition to the providers you are free1 to use, we support some layers which require registration. + +### HERE (formerly Nokia). + +In order to use HERE layers, you must [register](http://developer.here.com/). Once registered, you can create an `app_id` and `app_code` which you have to pass to `L.tileLayer.provider` in the options: + +```Javascript +L.tileLayer.provider('HERE.terrainDay', { + app_id: '', + app_code: '' +}).addTo(map); +``` + +[Available HERE layers](http://leaflet-extras.github.io/leaflet-providers/preview/#filter=HERE) + +### Mapbox + +In order to use Mapbox maps, you must [register](https://tiles.mapbox.com/signup). You can get map ID and ACCESS_TOKEN from [Mapbox projects](https://www.mapbox.com/projects): +```JavaScript +L.tileLayer.provider('MapBox', {id: 'ID', accessToken: 'ACCESS_TOKEN'}).addTo(map); +``` + +### Esri/ArcGIS + +In order to use ArcGIS maps, you must [register](https://developers.arcgis.com/en/sign-up/) and abide by the [terms of service](https://developers.arcgis.com/en/terms/). No special syntax is required. + +[Available Esri layers](http://leaflet-extras.github.io/leaflet-providers/preview/#filter=Esri) + +# Attribution + +This work was inspired from , and originally created by [Stefan Seelmann](https://github.com/seelmann). + +### What do we mean by *free*? +1 +We try to maintain leaflet-providers in such a way that you'll be able to use the layers we include without paying money. +This doesn't mean no limits apply, you should always check before using these layers for anything serious. diff --git a/includes/services/Leaflet/leaflet-providers/bower.json b/includes/services/Leaflet/leaflet-providers/bower.json new file mode 100644 index 000000000..744fd4c91 --- /dev/null +++ b/includes/services/Leaflet/leaflet-providers/bower.json @@ -0,0 +1,25 @@ +{ + "name": "leaflet-providers", + "version": "1.1.15", + "homepage": "https://github.com/leaflet-extras/leaflet-providers", + "description": "An extension to Leaflet that contains configurations for various free tile providers.", + "dependencies": { + "leaflet": "~0.7.3" + }, + "main": "leaflet-providers.js", + "keywords": [ + "leaflet", + "stamen", + "osm" + ], + "license": "BSD-2-Clause", + "ignore": [ + "**/.*", + "node_modules", + "bower_components", + "test", + "tests", + "preview", + "*.html" + ] +} diff --git a/includes/services/Leaflet/leaflet-providers/css/gh-fork-ribbon.css b/includes/services/Leaflet/leaflet-providers/css/gh-fork-ribbon.css new file mode 100644 index 000000000..84af6ac27 --- /dev/null +++ b/includes/services/Leaflet/leaflet-providers/css/gh-fork-ribbon.css @@ -0,0 +1,127 @@ +/* Left will inherit from right (so we don't need to duplicate code */ +.github-fork-ribbon { + /* The right and left lasses determine the side we attach our banner to */ + position: absolute; + + /* Add a bit of padding to give some substance outside the "stitching" */ + padding: 2px 0; + + /* Set the base colour */ + background-color: #a00; + + /* Set a gradient: transparent black at the top to almost-transparent black at the bottom */ + background-image: -webkit-gradient(linear, left top, left bottom, from(rgba(0, 0, 0, 0.00)), to(rgba(0, 0, 0, 0.15))); + background-image: -webkit-linear-gradient(top, rgba(0, 0, 0, 0.00), rgba(0, 0, 0, 0.15)); + background-image: -moz-linear-gradient(top, rgba(0, 0, 0, 0.00), rgba(0, 0, 0, 0.15)); + background-image: -o-linear-gradient(top, rgba(0, 0, 0, 0.00), rgba(0, 0, 0, 0.15)); + background-image: -ms-linear-gradient(top, rgba(0, 0, 0, 0.00), rgba(0, 0, 0, 0.15)); + background-image: linear-gradient(top, rgba(0, 0, 0, 0.00), rgba(0, 0, 0, 0.15)); + filter: progid:DXImageTransform.Microsoft.gradient(GradientType=0,StartColorStr='#000000', EndColorStr='#000000'); + + /* Add a drop shadow */ + -webkit-box-shadow: 0px 2px 3px 0px rgba(0, 0, 0, 0.5); + box-shadow: 0px 2px 3px 0px rgba(0, 0, 0, 0.5); + + z-index: 9999; +} + +.github-fork-ribbon a { + /* Set the font */ + font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; + font-size: 13px; + font-weight: 700; + color: white; + + /* Set the text properties */ + text-decoration: none; + text-shadow: 0 -1px rgba(0,0,0,0.5); + text-align: center; + + /* Set the geometry. If you fiddle with these you'll also need to tweak the top and right values in #github-fork-ribbon. */ + width: 200px; + line-height: 20px; + + /* Set the layout properties */ + display: inline-block; + padding: 2px 0; + + /* Add "stitching" effect */ + border-width: 1px 0; + border-style: dotted; + border-color: rgba(255,255,255,0.7); +} + +.github-fork-ribbon-wrapper { + width: 150px; + height: 150px; + position: absolute; + overflow: hidden; + top: 0; +} + +.github-fork-ribbon-wrapper.left { + left: 0; +} + +.github-fork-ribbon-wrapper.right { + right: 0; +} + +.github-fork-ribbon-wrapper.left-bottom { + position: fixed; + top: inherit; + bottom: 0; + left: 0; +} + +.github-fork-ribbon-wrapper.right-bottom { + position: fixed; + top: inherit; + bottom: 0; + right: 0; +} + +.github-fork-ribbon-wrapper.right .github-fork-ribbon { + top: 42px; + right: -43px; + + /* Rotate the banner 45 degrees */ + -webkit-transform: rotate(45deg); + -moz-transform: rotate(45deg); + -o-transform: rotate(45deg); + transform: rotate(45deg); +} + +.github-fork-ribbon-wrapper.left .github-fork-ribbon { + top: 42px; + left: -43px; + + /* Rotate the banner -45 degrees */ + -webkit-transform: rotate(-45deg); + -moz-transform: rotate(-45deg); + -o-transform: rotate(-45deg); + transform: rotate(-45deg); +} + + +.github-fork-ribbon-wrapper.left-bottom .github-fork-ribbon { + top: 80px; + left: -43px; + + /* Rotate the banner -45 degrees */ + -webkit-transform: rotate(45deg); + -moz-transform: rotate(45deg); + -o-transform: rotate(45deg); + transform: rotate(45deg); +} + +.github-fork-ribbon-wrapper.right-bottom .github-fork-ribbon { + top: 80px; + right: -43px; + + /* Rotate the banner -45 degrees */ + -webkit-transform: rotate(-45deg); + -moz-transform: rotate(-45deg); + -o-transform: rotate(-45deg); + transform: rotate(-45deg); +} \ No newline at end of file diff --git a/includes/services/Leaflet/leaflet-providers/css/gh-fork-ribbon.ie.css b/includes/services/Leaflet/leaflet-providers/css/gh-fork-ribbon.ie.css new file mode 100644 index 000000000..77fcab505 --- /dev/null +++ b/includes/services/Leaflet/leaflet-providers/css/gh-fork-ribbon.ie.css @@ -0,0 +1,68 @@ +/* IE voodoo courtesy of http://stackoverflow.com/a/4617511/263871 and + * http://www.useragentman.com/IETransformsTranslator */ +.github-fork-ribbon-wrapper.right .github-fork-ribbon { + /* IE positioning hack (couldn't find a transform-origin alternative for IE) */ + top: -22px; + right: -62px; + + /* IE8+ */ + -ms-filter: "progid:DXImageTransform.Microsoft.Matrix(M11=0.7071067811865474, M12=-0.7071067811865477, M21=0.7071067811865477, M22=0.7071067811865474, SizingMethod='auto expand')"; + /* IE6 and 7 */ + filter: progid:DXImageTransform.Microsoft.Matrix( + M11=0.7071067811865474, + M12=-0.7071067811865477, + M21=0.7071067811865477, + M22=0.7071067811865474, + SizingMethod='auto expand' + ); +} + +.github-fork-ribbon-wrapper.left .github-fork-ribbon { + top: -22px; + left: -22px; + + /* IE8+ */ + -ms-filter: "progid:DXImageTransform.Microsoft.Matrix(M11=0.7071067811865483, M12=0.7071067811865467, M21=-0.7071067811865467, M22=0.7071067811865483, SizingMethod='auto expand')"; + /* IE6 and 7 */ + filter: progid:DXImageTransform.Microsoft.Matrix( + M11=0.7071067811865483, + M12=0.7071067811865467, + M21=-0.7071067811865467, + M22=0.7071067811865483, + SizingMethod='auto expand' + ); +} + +.github-fork-ribbon-wrapper.left-bottom .github-fork-ribbon { + /* IE positioning hack (couldn't find a transform-origin alternative for IE) */ + top: 12px; + left: -22px; + + + /* IE8+ */ + -ms-filter: "progid:DXImageTransform.Microsoft.Matrix(M11=0.7071067811865474, M12=-0.7071067811865477, M21=0.7071067811865477, M22=0.7071067811865474, SizingMethod='auto expand')"; + /* IE6 and 7 */ +/* filter: progid:DXImageTransform.Microsoft.Matrix( + M11=0.7071067811865474, + M12=-0.7071067811865477, + M21=0.7071067811865477, + M22=0.7071067811865474, + SizingMethod='auto expand' + ); +*/} + +.github-fork-ribbon-wrapper.right-bottom .github-fork-ribbon { + top: 12px; + right: -62px; + + /* IE8+ */ + -ms-filter: "progid:DXImageTransform.Microsoft.Matrix(M11=0.7071067811865483, M12=0.7071067811865467, M21=-0.7071067811865467, M22=0.7071067811865483, SizingMethod='auto expand')"; + /* IE6 and 7 */ + filter: progid:DXImageTransform.Microsoft.Matrix( + M11=0.7071067811865483, + M12=0.7071067811865467, + M21=-0.7071067811865467, + M22=0.7071067811865483, + SizingMethod='auto expand' + ); +} \ No newline at end of file diff --git a/includes/services/Leaflet/leaflet-providers/index.html b/includes/services/Leaflet/leaflet-providers/index.html new file mode 100644 index 000000000..df73cc780 --- /dev/null +++ b/includes/services/Leaflet/leaflet-providers/index.html @@ -0,0 +1,94 @@ + + + + Leaflet Provider Demo + + + + + + + + + + + + +
+ + + + + + diff --git a/includes/services/Leaflet/leaflet-providers/leaflet-providers.js b/includes/services/Leaflet/leaflet-providers/leaflet-providers.js new file mode 100644 index 000000000..22e2cbdbd --- /dev/null +++ b/includes/services/Leaflet/leaflet-providers/leaflet-providers.js @@ -0,0 +1,639 @@ +(function (root, factory) { + if (typeof define === 'function' && define.amd) { + // AMD. Register as an anonymous module. + define(['leaflet'], factory); + } else if (typeof modules === 'object' && module.exports) { + // define a Common JS module that relies on 'leaflet' + module.exports = factory(require('leaflet')); + } else { + // Assume Leaflet is loaded into global object L already + factory(L); + } +}(this, function (L) { + 'use strict'; + + L.TileLayer.Provider = L.TileLayer.extend({ + initialize: function (arg, options) { + var providers = L.TileLayer.Provider.providers; + + var parts = arg.split('.'); + + var providerName = parts[0]; + var variantName = parts[1]; + + if (!providers[providerName]) { + throw 'No such provider (' + providerName + ')'; + } + + var provider = { + url: providers[providerName].url, + options: providers[providerName].options + }; + + // overwrite values in provider from variant. + if (variantName && 'variants' in providers[providerName]) { + if (!(variantName in providers[providerName].variants)) { + throw 'No such variant of ' + providerName + ' (' + variantName + ')'; + } + var variant = providers[providerName].variants[variantName]; + var variantOptions; + if (typeof variant === 'string') { + variantOptions = { + variant: variant + }; + } else { + variantOptions = variant.options; + } + provider = { + url: variant.url || provider.url, + options: L.Util.extend({}, provider.options, variantOptions) + }; + } + + var forceHTTP = window.location.protocol === 'file:' || provider.options.forceHTTP; + if (provider.url.indexOf('//') === 0 && forceHTTP) { + provider.url = 'http:' + provider.url; + } + + // If retina option is set + if (provider.options.retina) { + // Check retina screen + if (options.detectRetina && L.Browser.retina) { + // The retina option will be active now + // But we need to prevent Leaflet retina mode + options.detectRetina = false; + } else { + // No retina, remove option + provider.options.retina = ''; + } + } + + // replace attribution placeholders with their values from toplevel provider attribution, + // recursively + var attributionReplacer = function (attr) { + if (attr.indexOf('{attribution.') === -1) { + return attr; + } + return attr.replace(/\{attribution.(\w*)\}/, + function (match, attributionName) { + return attributionReplacer(providers[attributionName].options.attribution); + } + ); + }; + provider.options.attribution = attributionReplacer(provider.options.attribution); + + // Compute final options combining provider options with any user overrides + var layerOpts = L.Util.extend({}, provider.options, options); + L.TileLayer.prototype.initialize.call(this, provider.url, layerOpts); + } + }); + + /** + * Definition of providers. + * see http://leafletjs.com/reference.html#tilelayer for options in the options map. + */ + + L.TileLayer.Provider.providers = { + OpenStreetMap: { + url: '//{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', + options: { + maxZoom: 19, + attribution: + '© OpenStreetMap' + }, + variants: { + Mapnik: {}, + BlackAndWhite: { + url: 'http://{s}.tiles.wmflabs.org/bw-mapnik/{z}/{x}/{y}.png', + options: { + maxZoom: 18 + } + }, + DE: { + url: 'http://{s}.tile.openstreetmap.de/tiles/osmde/{z}/{x}/{y}.png', + options: { + maxZoom: 18 + } + }, + France: { + url: '//{s}.tile.openstreetmap.fr/osmfr/{z}/{x}/{y}.png', + options: { + maxZoom: 20, + attribution: '© Openstreetmap France | {attribution.OpenStreetMap}' + } + }, + HOT: { + url: '//{s}.tile.openstreetmap.fr/hot/{z}/{x}/{y}.png', + options: { + attribution: '{attribution.OpenStreetMap}, Tiles courtesy of Humanitarian OpenStreetMap Team' + } + } + } + }, + OpenSeaMap: { + url: 'http://tiles.openseamap.org/seamark/{z}/{x}/{y}.png', + options: { + attribution: 'Map data: © OpenSeaMap contributors' + } + }, + OpenTopoMap: { + url: '//{s}.tile.opentopomap.org/{z}/{x}/{y}.png', + options: { + maxZoom: 17, + attribution: 'Map data: {attribution.OpenStreetMap}, SRTM | Map style: © OpenTopoMap (CC-BY-SA)' + } + }, + Thunderforest: { + url: '//{s}.tile.thunderforest.com/{variant}/{z}/{x}/{y}.png', + options: { + attribution: + '© Thunderforest, {attribution.OpenStreetMap}', + variant: 'cycle' + }, + variants: { + OpenCycleMap: 'cycle', + Transport: { + options: { + variant: 'transport', + maxZoom: 19 + } + }, + TransportDark: { + options: { + variant: 'transport-dark', + maxZoom: 19 + } + }, + SpinalMap: { + options: { + variant: 'spinal-map', + maxZoom: 11 + } + }, + Landscape: 'landscape', + Outdoors: 'outdoors', + Pioneer: 'pioneer' + } + }, + OpenMapSurfer: { + url: 'http://korona.geog.uni-heidelberg.de/tiles/{variant}/x={x}&y={y}&z={z}', + options: { + maxZoom: 20, + variant: 'roads', + attribution: 'Imagery from GIScience Research Group @ University of Heidelberg — Map data {attribution.OpenStreetMap}' + }, + variants: { + Roads: 'roads', + AdminBounds: { + options: { + variant: 'adminb', + maxZoom: 19 + } + }, + Grayscale: { + options: { + variant: 'roadsg', + maxZoom: 19 + } + } + } + }, + Hydda: { + url: '//{s}.tile.openstreetmap.se/hydda/{variant}/{z}/{x}/{y}.png', + options: { + variant: 'full', + attribution: 'Tiles courtesy of OpenStreetMap Sweden — Map data {attribution.OpenStreetMap}' + }, + variants: { + Full: 'full', + Base: 'base', + RoadsAndLabels: 'roads_and_labels' + } + }, + MapBox: { + url: '//api.tiles.mapbox.com/v4/{id}/{z}/{x}/{y}.png?access_token={accessToken}', + options: { + attribution: + 'Imagery from MapBox — ' + + 'Map data {attribution.OpenStreetMap}', + subdomains: 'abcd' + } + }, + Stamen: { + url: '//stamen-tiles-{s}.a.ssl.fastly.net/{variant}/{z}/{x}/{y}.{ext}', + options: { + attribution: + 'Map tiles by Stamen Design, ' + + 'CC BY 3.0 — ' + + 'Map data {attribution.OpenStreetMap}', + subdomains: 'abcd', + minZoom: 0, + maxZoom: 20, + variant: 'toner', + ext: 'png' + }, + variants: { + Toner: 'toner', + TonerBackground: 'toner-background', + TonerHybrid: 'toner-hybrid', + TonerLines: 'toner-lines', + TonerLabels: 'toner-labels', + TonerLite: 'toner-lite', + Watercolor: { + options: { + variant: 'watercolor', + minZoom: 1, + maxZoom: 16 + } + }, + Terrain: { + options: { + variant: 'terrain', + minZoom: 0, + maxZoom: 18 + } + }, + TerrainBackground: { + options: { + variant: 'terrain-background', + minZoom: 0, + maxZoom: 18 + } + }, + TopOSMRelief: { + options: { + variant: 'toposm-color-relief', + ext: 'jpg', + bounds: [[22, -132], [51, -56]] + } + }, + TopOSMFeatures: { + options: { + variant: 'toposm-features', + bounds: [[22, -132], [51, -56]], + opacity: 0.9 + } + } + } + }, + Esri: { + url: '//server.arcgisonline.com/ArcGIS/rest/services/{variant}/MapServer/tile/{z}/{y}/{x}', + options: { + variant: 'World_Street_Map', + attribution: 'Tiles © Esri' + }, + variants: { + WorldStreetMap: { + options: { + attribution: + '{attribution.Esri} — ' + + 'Source: Esri, DeLorme, NAVTEQ, USGS, Intermap, iPC, NRCAN, Esri Japan, METI, Esri China (Hong Kong), Esri (Thailand), TomTom, 2012' + } + }, + DeLorme: { + options: { + variant: 'Specialty/DeLorme_World_Base_Map', + minZoom: 1, + maxZoom: 11, + attribution: '{attribution.Esri} — Copyright: ©2012 DeLorme' + } + }, + WorldTopoMap: { + options: { + variant: 'World_Topo_Map', + attribution: + '{attribution.Esri} — ' + + 'Esri, DeLorme, NAVTEQ, TomTom, Intermap, iPC, USGS, FAO, NPS, NRCAN, GeoBase, Kadaster NL, Ordnance Survey, Esri Japan, METI, Esri China (Hong Kong), and the GIS User Community' + } + }, + WorldImagery: { + options: { + variant: 'World_Imagery', + attribution: + '{attribution.Esri} — ' + + 'Source: Esri, i-cubed, USDA, USGS, AEX, GeoEye, Getmapping, Aerogrid, IGN, IGP, UPR-EGP, and the GIS User Community' + } + }, + WorldTerrain: { + options: { + variant: 'World_Terrain_Base', + maxZoom: 13, + attribution: + '{attribution.Esri} — ' + + 'Source: USGS, Esri, TANA, DeLorme, and NPS' + } + }, + WorldShadedRelief: { + options: { + variant: 'World_Shaded_Relief', + maxZoom: 13, + attribution: '{attribution.Esri} — Source: Esri' + } + }, + WorldPhysical: { + options: { + variant: 'World_Physical_Map', + maxZoom: 8, + attribution: '{attribution.Esri} — Source: US National Park Service' + } + }, + OceanBasemap: { + options: { + variant: 'Ocean_Basemap', + maxZoom: 13, + attribution: '{attribution.Esri} — Sources: GEBCO, NOAA, CHS, OSU, UNH, CSUMB, National Geographic, DeLorme, NAVTEQ, and Esri' + } + }, + NatGeoWorldMap: { + options: { + variant: 'NatGeo_World_Map', + maxZoom: 16, + attribution: '{attribution.Esri} — National Geographic, Esri, DeLorme, NAVTEQ, UNEP-WCMC, USGS, NASA, ESA, METI, NRCAN, GEBCO, NOAA, iPC' + } + }, + WorldGrayCanvas: { + options: { + variant: 'Canvas/World_Light_Gray_Base', + maxZoom: 16, + attribution: '{attribution.Esri} — Esri, DeLorme, NAVTEQ' + } + } + } + }, + OpenWeatherMap: { + url: 'http://{s}.tile.openweathermap.org/map/{variant}/{z}/{x}/{y}.png', + options: { + maxZoom: 19, + attribution: 'Map data © OpenWeatherMap', + opacity: 0.5 + }, + variants: { + Clouds: 'clouds', + CloudsClassic: 'clouds_cls', + Precipitation: 'precipitation', + PrecipitationClassic: 'precipitation_cls', + Rain: 'rain', + RainClassic: 'rain_cls', + Pressure: 'pressure', + PressureContour: 'pressure_cntr', + Wind: 'wind', + Temperature: 'temp', + Snow: 'snow' + } + }, + HERE: { + /* + * HERE maps, formerly Nokia maps. + * These basemaps are free, but you need an API key. Please sign up at + * http://developer.here.com/getting-started + * + * Note that the base urls contain '.cit' whichs is HERE's + * 'Customer Integration Testing' environment. Please remove for production + * envirionments. + */ + url: + '//{s}.{base}.maps.cit.api.here.com/maptile/2.1/' + + '{type}/{mapID}/{variant}/{z}/{x}/{y}/{size}/{format}?' + + 'app_id={app_id}&app_code={app_code}&lg={language}', + options: { + attribution: + 'Map © 1987-2014 HERE', + subdomains: '1234', + mapID: 'newest', + 'app_id': '', + 'app_code': '', + base: 'base', + variant: 'normal.day', + maxZoom: 20, + type: 'maptile', + language: 'eng', + format: 'png8', + size: '256' + }, + variants: { + normalDay: 'normal.day', + normalDayCustom: 'normal.day.custom', + normalDayGrey: 'normal.day.grey', + normalDayMobile: 'normal.day.mobile', + normalDayGreyMobile: 'normal.day.grey.mobile', + normalDayTransit: 'normal.day.transit', + normalDayTransitMobile: 'normal.day.transit.mobile', + normalNight: 'normal.night', + normalNightMobile: 'normal.night.mobile', + normalNightGrey: 'normal.night.grey', + normalNightGreyMobile: 'normal.night.grey.mobile', + + basicMap: { + options: { + type: 'basetile' + } + }, + mapLabels: { + options: { + type: 'labeltile', + format: 'png' + } + }, + trafficFlow: { + options: { + base: 'traffic', + type: 'flowtile' + } + }, + carnavDayGrey: 'carnav.day.grey', + hybridDay: { + options: { + base: 'aerial', + variant: 'hybrid.day' + } + }, + hybridDayMobile: { + options: { + base: 'aerial', + variant: 'hybrid.day.mobile' + } + }, + pedestrianDay: 'pedestrian.day', + pedestrianNight: 'pedestrian.night', + satelliteDay: { + options: { + base: 'aerial', + variant: 'satellite.day' + } + }, + terrainDay: { + options: { + base: 'aerial', + variant: 'terrain.day' + } + }, + terrainDayMobile: { + options: { + base: 'aerial', + variant: 'terrain.day.mobile' + } + } + } + }, + FreeMapSK: { + url: 'http://t{s}.freemap.sk/T/{z}/{x}/{y}.jpeg', + options: { + minZoom: 8, + maxZoom: 16, + subdomains: '1234', + bounds: [[47.204642, 15.996093], [49.830896, 22.576904]], + attribution: + '{attribution.OpenStreetMap}, vizualization CC-By-SA 2.0 Freemap.sk' + } + }, + MtbMap: { + url: 'http://tile.mtbmap.cz/mtbmap_tiles/{z}/{x}/{y}.png', + options: { + attribution: + '{attribution.OpenStreetMap} & USGS' + } + }, + CartoDB: { + url: 'http://{s}.basemaps.cartocdn.com/{variant}/{z}/{x}/{y}.png', + options: { + attribution: '{attribution.OpenStreetMap} © CartoDB', + subdomains: 'abcd', + maxZoom: 19, + variant: 'light_all' + }, + variants: { + Positron: 'light_all', + PositronNoLabels: 'light_nolabels', + PositronOnlyLabels: 'light_only_labels', + DarkMatter: 'dark_all', + DarkMatterNoLabels: 'dark_nolabels', + DarkMatterOnlyLabels: 'dark_only_labels' + } + }, + HikeBike: { + url: 'http://{s}.tiles.wmflabs.org/{variant}/{z}/{x}/{y}.png', + options: { + maxZoom: 19, + attribution: '{attribution.OpenStreetMap}', + variant: 'hikebike' + }, + variants: { + HikeBike: {}, + HillShading: { + options: { + maxZoom: 15, + variant: 'hillshading' + } + } + } + }, + BasemapAT: { + url: '//maps{s}.wien.gv.at/basemap/{variant}/normal/google3857/{z}/{y}/{x}.{format}', + options: { + maxZoom: 19, + attribution: 'Datenquelle: basemap.at', + subdomains: ['', '1', '2', '3', '4'], + format: 'png', + bounds: [[46.358770, 8.782379], [49.037872, 17.189532]], + variant: 'geolandbasemap' + }, + variants: { + basemap: 'geolandbasemap', + grau: 'bmapgrau', + overlay: 'bmapoverlay', + highdpi: { + options: { + variant: 'bmaphidpi', + format: 'jpeg' + } + }, + orthofoto: { + options: { + variant: 'bmaporthofoto30cm', + format: 'jpeg' + } + } + } + }, + NASAGIBS: { + url: '//map1.vis.earthdata.nasa.gov/wmts-webmerc/{variant}/default/{time}/{tilematrixset}{maxZoom}/{z}/{y}/{x}.{format}', + options: { + attribution: + 'Imagery provided by services from the Global Imagery Browse Services (GIBS), operated by the NASA/GSFC/Earth Science Data and Information System ' + + '(ESDIS) with funding provided by NASA/HQ.', + bounds: [[-85.0511287776, -179.999999975], [85.0511287776, 179.999999975]], + minZoom: 1, + maxZoom: 9, + format: 'jpg', + time: '', + tilematrixset: 'GoogleMapsCompatible_Level' + }, + variants: { + ModisTerraTrueColorCR: 'MODIS_Terra_CorrectedReflectance_TrueColor', + ModisTerraBands367CR: 'MODIS_Terra_CorrectedReflectance_Bands367', + ViirsEarthAtNight2012: { + options: { + variant: 'VIIRS_CityLights_2012', + maxZoom: 8 + } + }, + ModisTerraLSTDay: { + options: { + variant: 'MODIS_Terra_Land_Surface_Temp_Day', + format: 'png', + maxZoom: 7, + opacity: 0.75 + } + }, + ModisTerraSnowCover: { + options: { + variant: 'MODIS_Terra_Snow_Cover', + format: 'png', + maxZoom: 8, + opacity: 0.75 + } + }, + ModisTerraAOD: { + options: { + variant: 'MODIS_Terra_Aerosol', + format: 'png', + maxZoom: 6, + opacity: 0.75 + } + }, + ModisTerraChlorophyll: { + options: { + variant: 'MODIS_Terra_Chlorophyll_A', + format: 'png', + maxZoom: 7, + opacity: 0.75 + } + } + } + }, + NLS: { + // NLS maps are copyright National library of Scotland. + // http://maps.nls.uk/projects/api/index.html + // Please contact NLS for anything other than non-commercial low volume usage + // + // Map sources: Ordnance Survey 1:1m to 1:63K, 1920s-1940s + // z0-9 - 1:1m + // z10-11 - quarter inch (1:253440) + // z12-18 - one inch (1:63360) + url: '//nls-{s}.tileserver.com/nls/{z}/{x}/{y}.jpg', + options: { + attribution: 'National Library of Scotland Historic Maps', + bounds: [[49.6, -12], [61.7, 3]], + minZoom: 1, + maxZoom: 18, + subdomains: '0123', + } + } + }; + + L.tileLayer.provider = function (provider, options) { + return new L.TileLayer.Provider(provider, options); + }; + + return L; +})); diff --git a/includes/services/Leaflet/leaflet-providers/license.md b/includes/services/Leaflet/leaflet-providers/license.md new file mode 100644 index 000000000..4c48832e5 --- /dev/null +++ b/includes/services/Leaflet/leaflet-providers/license.md @@ -0,0 +1,9 @@ +Copyright (c) 2013 Leaflet Providers contributors +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. +* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + +_THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE._ \ No newline at end of file diff --git a/includes/services/Leaflet/leaflet-providers/package.json b/includes/services/Leaflet/leaflet-providers/package.json new file mode 100644 index 000000000..f4cbdc36d --- /dev/null +++ b/includes/services/Leaflet/leaflet-providers/package.json @@ -0,0 +1,45 @@ +{ + "name": "leaflet-providers", + "version": "1.1.15", + "description": "An extension to Leaflet that contains configurations for various free tile providers.", + "main": "leaflet-providers.js", + "repository": { + "type": "git", + "url": "git://github.com/leaflet-extras/leaflet-providers.git" + }, + "scripts": { + "test": "npm run lint && npm run testsuite", + "testsuite": "mocha-phantomjs tests/index.html", + "lint": "eslint --config .eslintrc leaflet-providers.js index.html preview/*.js preview/*.html tests/*", + "min": "uglifyjs leaflet-providers.js -mc -o leaflet-providers.min.js", + "release": "mversion patch -m" + }, + "license": "BSD-2-Clause", + "bugs": { + "url": "https://github.com/leaflet-extras/leaflet-providers/issues" + }, + "files": [ + "leaflet-providers.js", + "README.md", + "CHANGELOG.md", + "licence.md" + ], + "devDependencies": { + "chai": "^2.3.0", + "eslint": "^2.7.0", + "eslint-plugin-html": "^1.4.0", + "mocha": "^2.2.4", + "mocha-phantomjs": "^3.5.3", + "mversion": "^1.3.0", + "phantomjs": "1.9.7-15", + "uglify-js": "^2.4.15" + }, + "autoupdate": { + "source": "git", + "target": "git://github.com/leaflet-extras/leaflet-providers.git", + "basePath": "/", + "files": [ + "leaflet-providers.js" + ] + } +} diff --git a/includes/services/Leaflet/leaflet-providers/preview/https-support.js b/includes/services/Leaflet/leaflet-providers/preview/https-support.js new file mode 100644 index 000000000..de079787a --- /dev/null +++ b/includes/services/Leaflet/leaflet-providers/preview/https-support.js @@ -0,0 +1,57 @@ +L.TileLayer.prototype._tileOnError = function () { + if (this.errorContainer) { + this.errorContainer.innerHTML = 'seems to be not supported'; + } +} + + +var container = L.DomUtil.get('maps'); + +function addLayer (provider) { + var layer = L.tileLayer.provider(provider); + + var httpsSupported = layer._url.indexOf('//') === 0; + var table = L.DomUtil.get('table-' + (httpsSupported ? 'supported' : 'unknown')); + + var url = layer._url.replace('{variant}', layer.options.variant); + var options = L.extend({}, layer.options, layer._options); + + if (url.indexOf('http:') === 0) { + url = url.slice(5); + } else if (url.indexOf('https:') === 0) { + url = url.slice(6); + } + + var row = L.DomUtil.create('tr', '', table); + L.DomUtil.create('td', '', row).innerHTML = provider; + var result = L.DomUtil.create('td', '', row); + L.DomUtil.create('button', '', result).innerHTML = 'test...'; + if (httpsSupported) { + result.innerHTML = ' ' + result.innerHTML; + } + + L.DomEvent.on(result, 'click', function () { + var center = [52, 4]; + if ('bounds' in options && options.bounds) { + center = L.latLngBounds(options.bounds).getCenter(); + } + result.innerHTML = 'testing...'; + container.innerHTML = ''; + L.DomUtil.create('h2', '', container).innerHTML = provider + ' (http):'; + + var map1 = L.map(L.DomUtil.create('div', 'map', container)).setView(center, 9); + map1.addLayer(L.tileLayer('http:' + url, options)); + + L.DomUtil.create('h2', '', container).innerHTML = provider + ' (https):'; + var map2 = L.map(L.DomUtil.create('div', 'map', container)).setView(center, 9); + + var httpsLayer = L.tileLayer('https:' + url, options).addTo(map2); + + httpsLayer.errorContainer = result; + httpsLayer.on('load', function () { + result.innerHTML = 'seems to be OK'; + }); + }); +} + +L.tileLayer.provider.eachLayer(addLayer); diff --git a/includes/services/Leaflet/leaflet-providers/preview/index.html b/includes/services/Leaflet/leaflet-providers/preview/index.html new file mode 100644 index 000000000..f6645b854 --- /dev/null +++ b/includes/services/Leaflet/leaflet-providers/preview/index.html @@ -0,0 +1,90 @@ + + + + Leaflet Provider Demo + + + + + + + + + + + + + + + + +
+

Leaflet-providers preview

+

+ This page shows mini maps for all the layers available in Leaflet-providers. +

+
+
+ + + + + + + + + + + + diff --git a/includes/services/Leaflet/leaflet-providers/preview/layer-bounds.js b/includes/services/Leaflet/leaflet-providers/preview/layer-bounds.js new file mode 100644 index 000000000..14b415bc2 --- /dev/null +++ b/includes/services/Leaflet/leaflet-providers/preview/layer-bounds.js @@ -0,0 +1,63 @@ +var table = L.DomUtil.get('table'); +var container = L.DomUtil.get('maps'); +var result = L.DomUtil.get('result'); + +var map, rect; + +function showBounds () { + if (!(map && result)) { + return; + } + var b = rect.getBounds() + result.innerHTML = '

Bounds for this layer:

' + + '\n\n[' + + '[' + b.getSouth() + ', ' + b.getWest() + '], ' + + '[' + b.getNorth() + ', ' + b.getEast() + ']' + + ']

' + + 'zoomlevel: ' + map.getZoom(); +} + +function addLayer (provider) { + var layer = L.tileLayer.provider(provider); + + // we're only interested in layers with bounds here. + if (!layer.options.bounds) { + return; + } + var bounds = L.latLngBounds(layer.options.bounds); + + var row = L.DomUtil.create('tr', '', table); + L.DomUtil.create('td', '', row).innerHTML = provider; + + L.DomEvent.on(row, 'click', function () { + if (map && map.remove) { + map.remove(); + } + for (var i = 0; i < table.children.length; i++) { + L.DomUtil.removeClass(table.children[i], 'active'); + } + L.DomUtil.addClass(row, 'active'); + + container.innerHTML = ''; + L.DomUtil.create('h2', '', container).innerHTML = provider; + + map = L.map(L.DomUtil.create('div', 'map', container)).setView([52, 4], 6); + map.addLayer(L.tileLayer.provider('Hydda.Base')); + map.addLayer(layer); + + rect = L.rectangle(bounds, { + fill: false, + weight: 2, + opacity: 1 + }).addTo(map); + + rect.editing.enable(); + map.on('click zoomend', showBounds); + showBounds(); + + map.fitBounds(bounds); + }); +} +L.DomEvent.on(L.DomUtil.get('dump-bounds'), 'click', showBounds); + +L.tileLayer.provider.eachLayer(addLayer); diff --git a/includes/services/Leaflet/leaflet-providers/preview/preview.js b/includes/services/Leaflet/leaflet-providers/preview/preview.js new file mode 100644 index 000000000..9e8c85da4 --- /dev/null +++ b/includes/services/Leaflet/leaflet-providers/preview/preview.js @@ -0,0 +1,227 @@ +(function () { + 'use strict'; + + var map = new L.Map('map', { + zoomControl: false, + center: [48, -3], + zoom: 5 + }); + + function escapeHtml (string) { + return string + .replace(/&/g, '&') + .replace(//g, '>') + .replace(/"/g, '"') + .replace(/'/g, '''); + } + + function renderValue (value) { + if (typeof value === 'string') { + return "'" + escapeHtml(value) + "'"; + } else { + return JSON.stringify(value).replace(/,/g, ', '); + } + } + + L.TileLayer.include({ + getExampleJS: function () { + var layerName = this._providerName.replace('.', '_'); + + var url = this._exampleUrl || this._url; + var options = L.extend({}, this._options, this._exampleAPIcodes || {}); + + // replace {variant} in urls with the selected variant, since + // keeping it in the options map doesn't make sense for one layer + if (options.variant) { + url = url.replace('{variant}', options.variant); + delete options.variant; + } + + var code = ''; + if (url.indexOf('//') === 0) { + code += '// https: also suppported.\n'; + url = 'http:' + url; + } + code += 'var ' + layerName + ' = L.tileLayer(\'' + url + '\', {\n'; + + var first = true; + for (var option in options) { + if (first) { + first = false; + } else { + code += ',\n'; + } + code += '\t' + option + ': ' + renderValue(options[option]); + } + code += '\n});\n'; + + return code; + } + }); + + var isOverlay = function (providerName, layer) { + if (layer.options.opacity && layer.options.opacity < 1) { + return true; + } + var overlayPatterns = [ + '^(OpenWeatherMap|OpenSeaMap)', + 'OpenMapSurfer.AdminBounds', + 'Stamen.Toner(Hybrid|Lines|Labels)', + 'Acetate.(foreground|labels|roads)', + 'Hydda.RoadsAndLabels' + ]; + + return providerName.match('(' + overlayPatterns.join('|') + ')') !== null; + }; + + // Ignore some providers in the preview + var isIgnored = function (providerName) { + if (providerName === 'ignored') { + return true; + } + // reduce the number of layers previewed for some providers + if (providerName.startsWith('HERE') || providerName.startsWith('OpenWeatherMap')) { + var whitelist = [ + 'HERE.normalDay', + 'HERE.basicMap', + 'HERE.hybridDay', + 'OpenWeatherMap.Clouds', + 'OpenWeatherMap.Pressure', + 'OpenWeatherMap.Wind' + ]; + return whitelist.indexOf(providerName) === -1; + } + return false; + }; + + // collect all layers available in the provider definition + var baseLayers = {}; + var overlays = {}; + + var addLayer = function (name) { + if (isIgnored(name)) { + return; + } + var layer = L.tileLayer.provider(name); + if (isOverlay(name, layer)) { + overlays[name] = layer; + } else { + baseLayers[name] = layer; + } + }; + L.tileLayer.provider.eachLayer(addLayer); + + // add minimap control to the map + var layersControl = L.control.layers.minimap(baseLayers, overlays, { + collapsed: false + }).addTo(map); + + // Pass a filter in the hash tag to show only layers containing that string + // for example: #filter=Open + var filterLayersControl = function () { + var hash = window.location.hash; + var filterIndex = hash.indexOf('filter='); + if (filterIndex !== -1) { + var filterString = hash.substr(filterIndex + 7).trim(); + var visible = layersControl.filter(filterString); + + // enable first layer as actual layer. + var first = Object.keys(visible)[0]; + if (first in baseLayers) { + map.addLayer(baseLayers[first]); + map.eachLayer(function (layer) { + if (layer._providerName !== first) { + map.removeLayer(layer); + } + }); + layersControl.filter(filterString); + } + } + }; + L.DomEvent.on(window, 'hashchange', filterLayersControl); + + // Does not work if called immediately, so ugly hack to apply filter + // at first page load + setTimeout(filterLayersControl, 100); + + // add OpenStreetMap.Mapnik, or the first if it does not exist + if (baseLayers['OpenStreetMap.Mapnik']) { + baseLayers['OpenStreetMap.Mapnik'].addTo(map); + } else { + baseLayers[Object.keys(baseLayers)[0]].addTo(map); + } + + // if a layer is selected and if it has bounds an the bounds are not in the + // current view, move the map view to contain the bounds + map.on('baselayerchange', function (e) { + var layer = e.layer; + if (!map.hasLayer(layer)) { + return; + } + if (layer.options.minZoom > 1 && map.getZoom() > layer.options.minZoom) { + map.setZoom(layer.options.minZoom); + } + if (!layer.options.bounds) { + return; + } + var bounds = L.latLngBounds(layer.options.bounds); + map.fitBounds(bounds, { + paddingTopLeft: [0, 200], + paddingBottomRight: [200, 0] + }); + }); + + // Add the TileLayer source code control to the map + map.addControl(new (L.Control.extend({ + options: { + position: 'topleft' + }, + onAdd: function (map) { + var container = L.DomUtil.get('info'); + L.DomEvent.disableClickPropagation(container); + + L.DomUtil.create('h4', null, container).innerHTML = 'Provider names for leaflet-providers.js'; + var providerNames = L.DomUtil.create('code', 'provider-names', container); + + L.DomUtil.create('h4', '', container).innerHTML = 'Plain JavaScript:'; + var pre = L.DomUtil.create('pre', null, container); + var code = L.DomUtil.create('code', 'javascript', pre); + + var update = function (event) { + code.innerHTML = ''; + + var names = []; + + // loop over the layers in the map and add the JS + for (var key in map._layers) { + var layer = map._layers[key]; + if (!layer.getExampleJS) { + continue; + } + + // do not add the layer currently being removed + if (event && event.type === 'layerremove' && layer === event.layer) { + continue; + } + names.push(L.Util.template('{name}', { + name: layer._providerName + })); + code.innerHTML += layer.getExampleJS(); + } + providerNames.innerHTML = names.join(', '); + + /* global hljs:true */ + hljs.highlightBlock(code); + }; + + map.on({ + 'layeradd': update, + 'layerremove': update + }); + update(); + + return container; + } + }))()); +})(); diff --git a/includes/services/Leaflet/leaflet-providers/preview/shared.js b/includes/services/Leaflet/leaflet-providers/preview/shared.js new file mode 100644 index 000000000..4d4162652 --- /dev/null +++ b/includes/services/Leaflet/leaflet-providers/preview/shared.js @@ -0,0 +1,63 @@ +// This is a list of example API codes, to make this preview +// functioning. Please register with the providers to use them +// with your own app. +var exampleAPIcodes = { + 'HERE': { + 'app_id': 'oenPwMCqbQkUSqj1WhRx', + 'app_code': 'kBxLcdTofTHUlsT7tl2X5w' + }, + 'MapBox': { + 'id': 'mapbox.streets', + 'accessToken': 'pk.eyJ1IjoiZ3V0ZW55ZSIsImEiOiJmNjJlMDNmYTUyMzNjMzQxZmY4Mzc1ZmFiYmExNjMxOSJ9.xgl1PBwQV9CtwW-usedrcQ' + } +}; + +var origProviderInit = L.TileLayer.Provider.prototype.initialize; +L.TileLayer.Provider.include({ + initialize: function (providerName, options) { + this._providerName = providerName; + options = options || {}; + + // replace example API codes in options + var provider = this._providerName.split('.')[0]; + if (provider in exampleAPIcodes) { + + // overwrite exampleAPIcodes with a placeholder to prevent accidental use + // of these API codes. + this._exampleAPIcodes = {}; + for (var key in exampleAPIcodes[provider]) { + this._exampleAPIcodes[key] = ''; + } + L.extend(options, exampleAPIcodes[provider]); + } + origProviderInit.call(this, providerName, options); + } +}); + +// save the options while creating tilelayers to cleanly access them later. +var origTileLayerInit = L.TileLayer.prototype.initialize; +L.TileLayer.include({ + initialize: function (url, options) { + this._options = options; + origTileLayerInit.apply(this, arguments); + } +}); + +L.tileLayer.provider.eachLayer = function (callback) { + for (var provider in L.TileLayer.Provider.providers) { + if (L.TileLayer.Provider.providers[provider].variants) { + for (var variant in L.TileLayer.Provider.providers[provider].variants) { + callback(provider + '.' + variant); + } + } else { + callback(provider); + } + } +}; + +if (!String.prototype.startsWith) { + String.prototype.startsWith = function (searchString, position) { + position = position || 0; + return this.substr(position, searchString.length) === searchString; + }; +} diff --git a/includes/services/Leaflet/leaflet-providers/preview/test-bounds.html b/includes/services/Leaflet/leaflet-providers/preview/test-bounds.html new file mode 100644 index 000000000..50aa246e4 --- /dev/null +++ b/includes/services/Leaflet/leaflet-providers/preview/test-bounds.html @@ -0,0 +1,87 @@ + + + + Leaflet Provider - layer bounds check page + + + + + + + + + + + + + + + + +
+ + + + + + + + + diff --git a/includes/services/Leaflet/leaflet-providers/preview/test-https-support.html b/includes/services/Leaflet/leaflet-providers/preview/test-https-support.html new file mode 100644 index 000000000..7e7b0bff8 --- /dev/null +++ b/includes/services/Leaflet/leaflet-providers/preview/test-https-support.html @@ -0,0 +1,79 @@ + + + + Leaflet Provider - HTTPS support check page + + + + + + + + + + + + + + +

Testing provider protocols

+
+

Known https support:

+ + +
ProviderHTTPS
+
+ +
+

Unknown https support:

+ + +
ProviderHTTPS
+
+
+ + + + + + + + diff --git a/includes/services/Leaflet/leaflet-providers/tests/index.html b/includes/services/Leaflet/leaflet-providers/tests/index.html new file mode 100644 index 000000000..cfa30c13c --- /dev/null +++ b/includes/services/Leaflet/leaflet-providers/tests/index.html @@ -0,0 +1,44 @@ + + + + leaflet-poviders Mocha Tests + + + + + + + +
+
+ + + + + + + + + + + + + + + diff --git a/includes/services/Leaflet/leaflet-providers/tests/test.js b/includes/services/Leaflet/leaflet-providers/tests/test.js new file mode 100644 index 000000000..28ef6e981 --- /dev/null +++ b/includes/services/Leaflet/leaflet-providers/tests/test.js @@ -0,0 +1,99 @@ +/* global describe, chai, it */ + +function isEmpty (obj) { + for (var prop in obj) { + if (obj.hasOwnProperty(prop)) { + return false; + } + } + + return true; +} + +// List of valid L.TileLayer options to check options against +var validTileLayerOptions = [ + 'minZoom', 'maxZoom', 'maxNativeZoom', 'tileSize', 'subdomains', 'errorTileUrl', + 'attribution', 'tms', 'continuousWorld', 'noWrap', 'zoomOffset', 'zoomReverse', + 'opacity', 'zIndex', 'unloadInvisibleTiles', 'updateWhenIdle', 'detectRetina', + 'reuseTiles', 'bounds', 'crossOrigin', 'updateInterval', 'pane', 'nonBubblingEvents' +]; + +// monkey-patch getTileUrl with fake values. +L.TileLayer.prototype.getTileUrl = function (coords) { + return L.Util.template(this._url, L.extend({ + r: '', + s: this._getSubdomain(coords), + x: coords.x, + y: coords.y, + z: 15 + }, this.options)); +}; + +describe('leaflet-providers', function () { + chai.should(); + var providers = L.TileLayer.Provider.providers; + + describe('variant definition structure', function () { + it('each provider has keys url, options, variants', function () { + for (var name in providers) { + providers[name].should.contain.any.keys('url', 'options', 'variants'); + } + }); + it('each variant should be an object or a string', function () { + for (var name in providers) { + var variants = providers[name].variants; + + for (var v in variants) { + if (typeof variants[v] === 'string' || isEmpty(variants[v])) { + // string or empty object, which is fine. + continue; + } else { + variants[v].should.be.an.instanceof(Object); + variants[v].should.contain.any.keys('url', 'options'); + } + } + } + }); + }); + + describe('Nonexistant providers', function () { + it('should fail for non-existant providers', function () { + var fn = function () { + L.tileLayer.provider('Example'); + }; + fn.should.throw('No such provider (Example)'); + }); + it('should fail for non-existant variants of existing providers', function () { + var fn = function () { + L.tileLayer.provider('OpenStreetMap.Example'); + }; + fn.should.throw('No such variant of OpenStreetMap (Example)'); + }); + }); + + describe('Each layer', function () { + L.tileLayer.provider.eachLayer(function (name) { + describe(name, function () { + var layer = L.tileLayer.provider(name); + + it('should be a L.TileLayer', function () { + layer.should.be.an.instanceof(L.TileLayer); + }); + + it('should not throw while requesting a tile url', function () { + layer.getTileUrl({x: 16369, y: 10896}); + }); + + it('should have valid options', function () { + for (var key in layer.options) { + if (validTileLayerOptions.indexOf(key) !== -1) { + continue; + } + var placeholder = '{' + key + '}'; + layer._url.should.contain(placeholder); + } + }) + }); + }); + }) +}); diff --git a/includes/services/Leaflet/leaflet.fullscreen/.jshintrc b/includes/services/Leaflet/leaflet.fullscreen/.jshintrc new file mode 100644 index 000000000..b76361190 --- /dev/null +++ b/includes/services/Leaflet/leaflet.fullscreen/.jshintrc @@ -0,0 +1,12 @@ +{ + "browser": true, + "curly": true, + "eqeqeq": true, + "undef": true, + "quotmark": "single", + "trailing": true, + "globals": { + "L": true, + "jQuery": true + } +} \ No newline at end of file diff --git a/includes/services/Leaflet/leaflet.fullscreen/Control.FullScreen.css b/includes/services/Leaflet/leaflet.fullscreen/Control.FullScreen.css new file mode 100644 index 000000000..c93b1bf96 --- /dev/null +++ b/includes/services/Leaflet/leaflet.fullscreen/Control.FullScreen.css @@ -0,0 +1,4 @@ +.leaflet-control-zoom-fullscreen { background-image: url(icon-fullscreen.png); } +.leaflet-retina .leaflet-control-zoom-fullscreen { background-image: url(icon-fullscreen-2x.png); background-size: 26px 26px; } +.leaflet-container:-webkit-full-screen { width: 100% !important; height: 100% !important; z-index: 99999; } +.leaflet-pseudo-fullscreen { position: fixed !important; width: 100% !important; height: 100% !important; top: 0px !important; left: 0px !important; z-index: 99999; } \ No newline at end of file diff --git a/includes/services/Leaflet/leaflet.fullscreen/Control.FullScreen.js b/includes/services/Leaflet/leaflet.fullscreen/Control.FullScreen.js new file mode 100644 index 000000000..f1cd7ccc0 --- /dev/null +++ b/includes/services/Leaflet/leaflet.fullscreen/Control.FullScreen.js @@ -0,0 +1,164 @@ +(function() { + +L.Control.FullScreen = L.Control.extend({ + options: { + position: 'topleft', + title: 'Full Screen', + forceSeparateButton: false, + forcePseudoFullscreen: false + }, + + onAdd: function (map) { + var className = 'leaflet-control-zoom-fullscreen', container; + + if (map.zoomControl && !this.options.forceSeparateButton) { + container = map.zoomControl._container; + } else { + container = L.DomUtil.create('div', 'leaflet-bar'); + } + + this._createButton(this.options.title, className, container, this.toggleFullScreen, this); + + return container; + }, + + _createButton: function (title, className, container, fn, context) { + var link = L.DomUtil.create('a', className, container); + link.href = '#'; + link.title = title; + + L.DomEvent + .addListener(link, 'click', L.DomEvent.stopPropagation) + .addListener(link, 'click', L.DomEvent.preventDefault) + .addListener(link, 'click', fn, context); + + L.DomEvent + .addListener(container, fullScreenApi.fullScreenEventName, L.DomEvent.stopPropagation) + .addListener(container, fullScreenApi.fullScreenEventName, L.DomEvent.preventDefault) + .addListener(container, fullScreenApi.fullScreenEventName, this._handleEscKey, context); + + L.DomEvent + .addListener(document, fullScreenApi.fullScreenEventName, L.DomEvent.stopPropagation) + .addListener(document, fullScreenApi.fullScreenEventName, L.DomEvent.preventDefault) + .addListener(document, fullScreenApi.fullScreenEventName, this._handleEscKey, context); + + return link; + }, + + toggleFullScreen: function () { + var map = this._map; + map._exitFired = false; + if (map._isFullscreen) { + if (fullScreenApi.supportsFullScreen && !this.options.forcePseudoFullscreen) { + fullScreenApi.cancelFullScreen(map._container); + } else { + L.DomUtil.removeClass(map._container, 'leaflet-pseudo-fullscreen'); + } + map.invalidateSize(); + map.fire('exitFullscreen'); + map._exitFired = true; + map._isFullscreen = false; + } + else { + if (fullScreenApi.supportsFullScreen && !this.options.forcePseudoFullscreen) { + fullScreenApi.requestFullScreen(map._container); + } else { + L.DomUtil.addClass(map._container, 'leaflet-pseudo-fullscreen'); + } + map.invalidateSize(); + map.fire('enterFullscreen'); + map._isFullscreen = true; + } + }, + + _handleEscKey: function () { + var map = this._map; + if (!fullScreenApi.isFullScreen(map) && !map._exitFired) { + map.fire('exitFullscreen'); + map._exitFired = true; + map._isFullscreen = false; + } + } +}); + +L.Map.addInitHook(function () { + if (this.options.fullscreenControl) { + this.fullscreenControl = L.control.fullscreen(this.options.fullscreenControlOptions); + this.addControl(this.fullscreenControl); + } +}); + +L.control.fullscreen = function (options) { + return new L.Control.FullScreen(options); +}; + +/* +Native FullScreen JavaScript API +------------- +Assumes Mozilla naming conventions instead of W3C for now + +source : http://johndyer.name/native-fullscreen-javascript-api-plus-jquery-plugin/ + +*/ + + var + fullScreenApi = { + supportsFullScreen: false, + isFullScreen: function() { return false; }, + requestFullScreen: function() {}, + cancelFullScreen: function() {}, + fullScreenEventName: '', + prefix: '' + }, + browserPrefixes = 'webkit moz o ms khtml'.split(' '); + + // check for native support + if (typeof document.exitFullscreen !== 'undefined') { + fullScreenApi.supportsFullScreen = true; + } else { + // check for fullscreen support by vendor prefix + for (var i = 0, il = browserPrefixes.length; i < il; i++ ) { + fullScreenApi.prefix = browserPrefixes[i]; + if (typeof document[fullScreenApi.prefix + 'CancelFullScreen' ] !== 'undefined' ) { + fullScreenApi.supportsFullScreen = true; + break; + } + } + } + + // update methods to do something useful + if (fullScreenApi.supportsFullScreen) { + fullScreenApi.fullScreenEventName = fullScreenApi.prefix + 'fullscreenchange'; + fullScreenApi.isFullScreen = function() { + switch (this.prefix) { + case '': + return document.fullScreen; + case 'webkit': + return document.webkitIsFullScreen; + default: + return document[this.prefix + 'FullScreen']; + } + }; + fullScreenApi.requestFullScreen = function(el) { + return (this.prefix === '') ? el.requestFullscreen() : el[this.prefix + 'RequestFullScreen'](); + }; + fullScreenApi.cancelFullScreen = function(el) { + return (this.prefix === '') ? document.exitFullscreen() : document[this.prefix + 'CancelFullScreen'](); + }; + } + + // jQuery plugin + if (typeof jQuery !== 'undefined') { + jQuery.fn.requestFullScreen = function() { + return this.each(function() { + var el = jQuery(this); + if (fullScreenApi.supportsFullScreen) { + fullScreenApi.requestFullScreen(el); + } + }); + }; + } + + // export api + window.fullScreenApi = fullScreenApi; +})(); diff --git a/includes/services/Leaflet/leaflet.fullscreen/LICENSE b/includes/services/Leaflet/leaflet.fullscreen/LICENSE new file mode 100644 index 000000000..07ddddccb --- /dev/null +++ b/includes/services/Leaflet/leaflet.fullscreen/LICENSE @@ -0,0 +1,22 @@ +Copyright (c) 2013, Bruno Bergot +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are +permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this list of + conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright notice, this list + of conditions and the following disclaimer in the documentation and/or other materials + provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR +TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/includes/services/Leaflet/leaflet.fullscreen/README.md b/includes/services/Leaflet/leaflet.fullscreen/README.md new file mode 100644 index 000000000..a111801fe --- /dev/null +++ b/includes/services/Leaflet/leaflet.fullscreen/README.md @@ -0,0 +1,68 @@ +Leaflet.Control.FullScreen +============ + +What ? +------ + +Simple plugin for Leaflet that adds fullscreen button to your maps. + +Inspired by http://elidupuis.github.com/leaflet.zoomfs/ + +Use the native javascript fullscreen API http://johndyer.name/native-fullscreen-javascript-api-plus-jquery-plugin/ + +Released under the MIT License http://opensource.org/licenses/mit-license.php + +How ? +------ + +Include Control.FullScreen.js and Control.FullScreen.css in your page: + +``` html + + +``` + +Add the fullscreen control to the map: + +``` js +var map = new L.Map('map', { + fullscreenControl: true, + fullscreenControlOptions: { + position: 'topleft' + } +}); +``` + +If your map have a zoomControl the fullscreen button will be added at the bottom of this one. + +If your map doesn't have a zoomContron the fullscreen button will be added to topleft corner of the map (same as the zoomcontrol). + +__Events and options__: + +``` js +// create a fullscreen button and add it to the map +L.control.fullscreen({ + position: 'topleft', // change the position of the button can be topleft, topright, bottomright or bottomleft, defaut topleft + title: 'Show me the fullscreen !', // change the title of the button, default Full Screen + forceSeparateButton: true, // force seperate button to detach from zoom buttons, default false + forcePseudoFullscreen: true // force use of pseudo full screen even if full screen API is available, default false +}).addTo(map); + +// events are fired when entering or exiting fullscreen. +map.on('enterFullscreen', function(){ + console.log('entered fullscreen'); +}); + +map.on('exitFullscreen', function(){ + console.log('exited fullscreen'); +}); +``` + +Where ? +------ + +Source code : https://github.com/brunob/leaflet.fullscreen + +Downloads : https://github.com/brunob/leaflet.fullscreen/releases + +Demo : http://brunob.github.com/leaflet.fullscreen/ diff --git a/includes/services/Leaflet/leaflet.fullscreen/bower.json b/includes/services/Leaflet/leaflet.fullscreen/bower.json new file mode 100644 index 000000000..6de9eee81 --- /dev/null +++ b/includes/services/Leaflet/leaflet.fullscreen/bower.json @@ -0,0 +1,30 @@ +{ + "name": "leaflet.fullscreen", + "version": "1.1.4", + "homepage": "https://github.com/brunob/leaflet.fullscreen", + "authors": [ + "brunob " + ], + "description": "Leaflet.Control.FullScreen for Leaflet", + "main": [ + "Control.FullScreen.js", + "Control.FullScreen.css", + "icon-fullscreen.png", + "icon-fullscreen-2x.png" + ], + "keywords": [ + "leaflet", + "plugins", + "maps", + "fullscreen" + ], + "license": "MIT", + "ignore": [ + "**/.*", + "node_modules", + "bower_components", + "test", + "tests", + "index.html" + ] +} diff --git a/includes/services/Leaflet/leaflet.fullscreen/icon-fullscreen-2x.png b/includes/services/Leaflet/leaflet.fullscreen/icon-fullscreen-2x.png new file mode 100644 index 0000000000000000000000000000000000000000..7320d953bcdaf9e2ae787dbedb4a1b44393538e4 GIT binary patch literal 228 zcmeAS@N?(olHy`uVBq!ia0vp^CLqkg1|*MGNWTVBt36#DLn>~)opF(~!GMQV_Ta@k z2FjLB?k_k(|7fsWbn<%5{Qg0$ltpy%Y*wIhhC0{ElX5Gzn`iYcRou+>YHg^Ohlcgc z86jcOiqVf|oa6fHJ@b_L6n3tsl}`H@Z#3wpuM6aPe(&rW_h&2prv94o)@N1z?zttl z%B|BiC$8{X%C~TdMo3kYfD?z}6Q|qHj%yWtd024jYvLu=er*fqNu0?#Y`r~Aj6jPW ZW-|XRt>!qZetHYY9iFa!F6*2UngIPLSBn4u literal 0 HcmV?d00001 diff --git a/includes/services/Leaflet/leaflet.fullscreen/icon-fullscreen.png b/includes/services/Leaflet/leaflet.fullscreen/icon-fullscreen.png new file mode 100644 index 0000000000000000000000000000000000000000..17478145d1fdb3eb25901c7dcd70cd4af4e4b2aa GIT binary patch literal 153 zcmeAS@N?(olHy`uVBq!ia0vp^QXtI11|(N{`J4k%37#&FAr-gYPC3ZQpuoc{ds5J9 zmUJ15d!UG*;|cYq+9wM8Gk$r@ZsBmXesbXX^69)r4>nJC6#bzk{UrI;*Fd&?du+_^ zPncr+aqXuq5*}Q?1YD*lI_>@0%3Is?W3iTtgjMOm#D{_Fzj*=eV(@hJb6Mw<&;$S{ CQ#W`3 literal 0 HcmV?d00001 diff --git a/includes/services/Leaflet/leaflet.fullscreen/index.html b/includes/services/Leaflet/leaflet.fullscreen/index.html new file mode 100644 index 000000000..87b345c4d --- /dev/null +++ b/includes/services/Leaflet/leaflet.fullscreen/index.html @@ -0,0 +1,48 @@ + + + + + Leaflet.Control.FullScreen Demo + + + + + + + +
+ + + + diff --git a/includes/services/Leaflet/leaflet.fullscreen/package.json b/includes/services/Leaflet/leaflet.fullscreen/package.json new file mode 100644 index 000000000..e0c9aa7b5 --- /dev/null +++ b/includes/services/Leaflet/leaflet.fullscreen/package.json @@ -0,0 +1,25 @@ +{ + "name": "leaflet.fullscreen", + "version": "1.1.4", + "description": "Simple plugin for Leaflet that adds fullscreen button to your maps.", + "main": "Control.FullScreen.js", + "scripts": { + "test": "jshint Control.FullScreen.js" + }, + "repository": { + "type": "git", + "url": "git://github.com/brunob/leaflet.fullscreen.git" + }, + "keywords": [ + "leaflet", + "plugins", + "maps", + "fullscreen" + ], + "devDependencies": { + "jshint": "2.5.0" + }, + "author": "b_b", + "license": "MIT License", + "readmeFilename": "README.md" +} diff --git a/includes/services/Leaflet/leaflet.markercluster/MarkerCluster.Default.css b/includes/services/Leaflet/leaflet.markercluster/MarkerCluster.Default.css new file mode 100644 index 000000000..bbc8c9fb0 --- /dev/null +++ b/includes/services/Leaflet/leaflet.markercluster/MarkerCluster.Default.css @@ -0,0 +1,60 @@ +.marker-cluster-small { + background-color: rgba(181, 226, 140, 0.6); + } +.marker-cluster-small div { + background-color: rgba(110, 204, 57, 0.6); + } + +.marker-cluster-medium { + background-color: rgba(241, 211, 87, 0.6); + } +.marker-cluster-medium div { + background-color: rgba(240, 194, 12, 0.6); + } + +.marker-cluster-large { + background-color: rgba(253, 156, 115, 0.6); + } +.marker-cluster-large div { + background-color: rgba(241, 128, 23, 0.6); + } + + /* IE 6-8 fallback colors */ +.leaflet-oldie .marker-cluster-small { + background-color: rgb(181, 226, 140); + } +.leaflet-oldie .marker-cluster-small div { + background-color: rgb(110, 204, 57); + } + +.leaflet-oldie .marker-cluster-medium { + background-color: rgb(241, 211, 87); + } +.leaflet-oldie .marker-cluster-medium div { + background-color: rgb(240, 194, 12); + } + +.leaflet-oldie .marker-cluster-large { + background-color: rgb(253, 156, 115); + } +.leaflet-oldie .marker-cluster-large div { + background-color: rgb(241, 128, 23); +} + +.marker-cluster { + background-clip: padding-box; + border-radius: 20px; + } +.marker-cluster div { + width: 30px; + height: 30px; + margin-left: 5px; + margin-top: 5px; + + text-align: center; + border-radius: 15px; + font: 12px "Helvetica Neue", Arial, Helvetica, sans-serif; + } +.marker-cluster span { + line-height: 30px; + } \ No newline at end of file diff --git a/includes/services/Leaflet/leaflet.markercluster/MarkerCluster.css b/includes/services/Leaflet/leaflet.markercluster/MarkerCluster.css new file mode 100644 index 000000000..c60d71b7a --- /dev/null +++ b/includes/services/Leaflet/leaflet.markercluster/MarkerCluster.css @@ -0,0 +1,14 @@ +.leaflet-cluster-anim .leaflet-marker-icon, .leaflet-cluster-anim .leaflet-marker-shadow { + -webkit-transition: -webkit-transform 0.3s ease-out, opacity 0.3s ease-in; + -moz-transition: -moz-transform 0.3s ease-out, opacity 0.3s ease-in; + -o-transition: -o-transform 0.3s ease-out, opacity 0.3s ease-in; + transition: transform 0.3s ease-out, opacity 0.3s ease-in; +} + +.leaflet-cluster-spider-leg { + /* stroke-dashoffset (duration and function) should match with leaflet-marker-icon transform in order to track it exactly */ + -webkit-transition: -webkit-stroke-dashoffset 0.3s ease-out, -webkit-stroke-opacity 0.3s ease-in; + -moz-transition: -moz-stroke-dashoffset 0.3s ease-out, -moz-stroke-opacity 0.3s ease-in; + -o-transition: -o-stroke-dashoffset 0.3s ease-out, -o-stroke-opacity 0.3s ease-in; + transition: stroke-dashoffset 0.3s ease-out, stroke-opacity 0.3s ease-in; +} diff --git a/includes/services/Leaflet/leaflet.markercluster/leaflet.markercluster-src.js b/includes/services/Leaflet/leaflet.markercluster/leaflet.markercluster-src.js new file mode 100644 index 000000000..9a61450d7 --- /dev/null +++ b/includes/services/Leaflet/leaflet.markercluster/leaflet.markercluster-src.js @@ -0,0 +1,2632 @@ +/* + Leaflet.markercluster, Provides Beautiful Animated Marker Clustering functionality for Leaflet, a JS library for interactive maps. + https://github.com/Leaflet/Leaflet.markercluster + (c) 2012-2013, Dave Leaver, smartrak +*/ +(function (window, document, undefined) {/* + * L.MarkerClusterGroup extends L.FeatureGroup by clustering the markers contained within + */ + +L.MarkerClusterGroup = L.FeatureGroup.extend({ + + options: { + maxClusterRadius: 80, //A cluster will cover at most this many pixels from its center + iconCreateFunction: null, + + spiderfyOnMaxZoom: true, + showCoverageOnHover: true, + zoomToBoundsOnClick: true, + singleMarkerMode: false, + + disableClusteringAtZoom: null, + + // Setting this to false prevents the removal of any clusters outside of the viewpoint, which + // is the default behaviour for performance reasons. + removeOutsideVisibleBounds: true, + + // Set to false to disable all animations (zoom and spiderfy). + // If false, option animateAddingMarkers below has no effect. + // If L.DomUtil.TRANSITION is falsy, this option has no effect. + animate: true, + + //Whether to animate adding markers after adding the MarkerClusterGroup to the map + // If you are adding individual markers set to true, if adding bulk markers leave false for massive performance gains. + animateAddingMarkers: false, + + //Increase to increase the distance away that spiderfied markers appear from the center + spiderfyDistanceMultiplier: 1, + + // Make it possible to specify a polyline options on a spider leg + spiderLegPolylineOptions: { weight: 1.5, color: '#222', opacity: 0.5 }, + + // When bulk adding layers, adds markers in chunks. Means addLayers may not add all the layers in the call, others will be loaded during setTimeouts + chunkedLoading: false, + chunkInterval: 200, // process markers for a maximum of ~ n milliseconds (then trigger the chunkProgress callback) + chunkDelay: 50, // at the end of each interval, give n milliseconds back to system/browser + chunkProgress: null, // progress callback: function(processed, total, elapsed) (e.g. for a progress indicator) + + //Options to pass to the L.Polygon constructor + polygonOptions: {} + }, + + initialize: function (options) { + L.Util.setOptions(this, options); + if (!this.options.iconCreateFunction) { + this.options.iconCreateFunction = this._defaultIconCreateFunction; + } + + this._featureGroup = L.featureGroup(); + this._featureGroup.addEventParent(this); + + this._nonPointGroup = L.featureGroup(); + this._nonPointGroup.addEventParent(this); + + this._inZoomAnimation = 0; + this._needsClustering = []; + this._needsRemoving = []; //Markers removed while we aren't on the map need to be kept track of + //The bounds of the currently shown area (from _getExpandedVisibleBounds) Updated on zoom/move + this._currentShownBounds = null; + + this._queue = []; + + // Hook the appropriate animation methods. + var animate = L.DomUtil.TRANSITION && this.options.animate; + L.extend(this, animate ? this._withAnimation : this._noAnimation); + // Remember which MarkerCluster class to instantiate (animated or not). + this._markerCluster = animate ? L.MarkerCluster : L.MarkerClusterNonAnimated; + }, + + addLayer: function (layer) { + + if (layer instanceof L.LayerGroup) { + return this.addLayers([layer]); + } + + //Don't cluster non point data + if (!layer.getLatLng) { + this._nonPointGroup.addLayer(layer); + return this; + } + + if (!this._map) { + this._needsClustering.push(layer); + return this; + } + + if (this.hasLayer(layer)) { + return this; + } + + + //If we have already clustered we'll need to add this one to a cluster + + if (this._unspiderfy) { + this._unspiderfy(); + } + + this._addLayer(layer, this._maxZoom); + + // Refresh bounds and weighted positions. + this._topClusterLevel._recalculateBounds(); + + //Work out what is visible + var visibleLayer = layer, + currentZoom = this._map.getZoom(); + if (layer.__parent) { + while (visibleLayer.__parent._zoom >= currentZoom) { + visibleLayer = visibleLayer.__parent; + } + } + + if (this._currentShownBounds.contains(visibleLayer.getLatLng())) { + if (this.options.animateAddingMarkers) { + this._animationAddLayer(layer, visibleLayer); + } else { + this._animationAddLayerNonAnimated(layer, visibleLayer); + } + } + return this; + }, + + removeLayer: function (layer) { + + if (layer instanceof L.LayerGroup) { + return this.removeLayers([layer]); + } + + //Non point layers + if (!layer.getLatLng) { + this._nonPointGroup.removeLayer(layer); + return this; + } + + if (!this._map) { + if (!this._arraySplice(this._needsClustering, layer) && this.hasLayer(layer)) { + this._needsRemoving.push(layer); + } + return this; + } + + if (!layer.__parent) { + return this; + } + + if (this._unspiderfy) { + this._unspiderfy(); + this._unspiderfyLayer(layer); + } + + //Remove the marker from clusters + this._removeLayer(layer, true); + + // Refresh bounds and weighted positions. + this._topClusterLevel._recalculateBounds(); + + layer.off('move', this._childMarkerMoved, this); + + if (this._featureGroup.hasLayer(layer)) { + this._featureGroup.removeLayer(layer); + if (layer.clusterShow) { + layer.clusterShow(); + } + } + + return this; + }, + + //Takes an array of markers and adds them in bulk + addLayers: function (layersArray) { + if (!L.Util.isArray(layersArray)) { + return this.addLayer(layersArray); + } + + var fg = this._featureGroup, + npg = this._nonPointGroup, + chunked = this.options.chunkedLoading, + chunkInterval = this.options.chunkInterval, + chunkProgress = this.options.chunkProgress, + l = layersArray.length, + offset = 0, + originalArray = true, + m; + + if (this._map) { + var started = (new Date()).getTime(); + var process = L.bind(function () { + var start = (new Date()).getTime(); + for (; offset < l; offset++) { + if (chunked && offset % 200 === 0) { + // every couple hundred markers, instrument the time elapsed since processing started: + var elapsed = (new Date()).getTime() - start; + if (elapsed > chunkInterval) { + break; // been working too hard, time to take a break :-) + } + } + + m = layersArray[offset]; + + // Group of layers, append children to layersArray and skip. + // Side effects: + // - Total increases, so chunkProgress ratio jumps backward. + // - Groups are not included in this group, only their non-group child layers (hasLayer). + // Changing array length while looping does not affect performance in current browsers: + // http://jsperf.com/for-loop-changing-length/6 + if (m instanceof L.LayerGroup) { + if (originalArray) { + layersArray = layersArray.slice(); + originalArray = false; + } + this._extractNonGroupLayers(m, layersArray); + l = layersArray.length; + continue; + } + + //Not point data, can't be clustered + if (!m.getLatLng) { + npg.addLayer(m); + continue; + } + + if (this.hasLayer(m)) { + continue; + } + + this._addLayer(m, this._maxZoom); + + //If we just made a cluster of size 2 then we need to remove the other marker from the map (if it is) or we never will + if (m.__parent) { + if (m.__parent.getChildCount() === 2) { + var markers = m.__parent.getAllChildMarkers(), + otherMarker = markers[0] === m ? markers[1] : markers[0]; + fg.removeLayer(otherMarker); + } + } + } + + if (chunkProgress) { + // report progress and time elapsed: + chunkProgress(offset, l, (new Date()).getTime() - started); + } + + // Completed processing all markers. + if (offset === l) { + + // Refresh bounds and weighted positions. + this._topClusterLevel._recalculateBounds(); + + //Update the icons of all those visible clusters that were affected + this._featureGroup.eachLayer(function (c) { + if (c instanceof L.MarkerCluster && c._iconNeedsUpdate) { + c._updateIcon(); + } + }); + + this._topClusterLevel._recursivelyAddChildrenToMap(null, this._zoom, this._currentShownBounds); + } else { + setTimeout(process, this.options.chunkDelay); + } + }, this); + + process(); + } else { + var needsClustering = this._needsClustering; + + for (; offset < l; offset++) { + m = layersArray[offset]; + + // Group of layers, append children to layersArray and skip. + if (m instanceof L.LayerGroup) { + if (originalArray) { + layersArray = layersArray.slice(); + originalArray = false; + } + this._extractNonGroupLayers(m, layersArray); + l = layersArray.length; + continue; + } + + //Not point data, can't be clustered + if (!m.getLatLng) { + npg.addLayer(m); + continue; + } + + if (this.hasLayer(m)) { + continue; + } + + needsClustering.push(m); + } + } + return this; + }, + + //Takes an array of markers and removes them in bulk + removeLayers: function (layersArray) { + var i, m, + l = layersArray.length, + fg = this._featureGroup, + npg = this._nonPointGroup, + originalArray = true; + + if (!this._map) { + for (i = 0; i < l; i++) { + m = layersArray[i]; + + // Group of layers, append children to layersArray and skip. + if (m instanceof L.LayerGroup) { + if (originalArray) { + layersArray = layersArray.slice(); + originalArray = false; + } + this._extractNonGroupLayers(m, layersArray); + l = layersArray.length; + continue; + } + + this._arraySplice(this._needsClustering, m); + npg.removeLayer(m); + if (this.hasLayer(m)) { + this._needsRemoving.push(m); + } + } + return this; + } + + if (this._unspiderfy) { + this._unspiderfy(); + + // Work on a copy of the array, so that next loop is not affected. + var layersArray2 = layersArray.slice(), + l2 = l; + for (i = 0; i < l2; i++) { + m = layersArray2[i]; + + // Group of layers, append children to layersArray and skip. + if (m instanceof L.LayerGroup) { + this._extractNonGroupLayers(m, layersArray2); + l2 = layersArray2.length; + continue; + } + + this._unspiderfyLayer(m); + } + } + + for (i = 0; i < l; i++) { + m = layersArray[i]; + + // Group of layers, append children to layersArray and skip. + if (m instanceof L.LayerGroup) { + if (originalArray) { + layersArray = layersArray.slice(); + originalArray = false; + } + this._extractNonGroupLayers(m, layersArray); + l = layersArray.length; + continue; + } + + if (!m.__parent) { + npg.removeLayer(m); + continue; + } + + this._removeLayer(m, true, true); + + if (fg.hasLayer(m)) { + fg.removeLayer(m); + if (m.clusterShow) { + m.clusterShow(); + } + } + } + + // Refresh bounds and weighted positions. + this._topClusterLevel._recalculateBounds(); + + //Fix up the clusters and markers on the map + this._topClusterLevel._recursivelyAddChildrenToMap(null, this._zoom, this._currentShownBounds); + + fg.eachLayer(function (c) { + if (c instanceof L.MarkerCluster) { + c._updateIcon(); + } + }); + + return this; + }, + + //Removes all layers from the MarkerClusterGroup + clearLayers: function () { + //Need our own special implementation as the LayerGroup one doesn't work for us + + //If we aren't on the map (yet), blow away the markers we know of + if (!this._map) { + this._needsClustering = []; + delete this._gridClusters; + delete this._gridUnclustered; + } + + if (this._noanimationUnspiderfy) { + this._noanimationUnspiderfy(); + } + + //Remove all the visible layers + this._featureGroup.clearLayers(); + this._nonPointGroup.clearLayers(); + + this.eachLayer(function (marker) { + marker.off('move', this._childMarkerMoved, this); + delete marker.__parent; + }); + + if (this._map) { + //Reset _topClusterLevel and the DistanceGrids + this._generateInitialClusters(); + } + + return this; + }, + + //Override FeatureGroup.getBounds as it doesn't work + getBounds: function () { + var bounds = new L.LatLngBounds(); + + if (this._topClusterLevel) { + bounds.extend(this._topClusterLevel._bounds); + } + + for (var i = this._needsClustering.length - 1; i >= 0; i--) { + bounds.extend(this._needsClustering[i].getLatLng()); + } + + bounds.extend(this._nonPointGroup.getBounds()); + + return bounds; + }, + + //Overrides LayerGroup.eachLayer + eachLayer: function (method, context) { + var markers = this._needsClustering.slice(), + needsRemoving = this._needsRemoving, + i; + + if (this._topClusterLevel) { + this._topClusterLevel.getAllChildMarkers(markers); + } + + for (i = markers.length - 1; i >= 0; i--) { + if (needsRemoving.indexOf(markers[i]) === -1) { + method.call(context, markers[i]); + } + } + + this._nonPointGroup.eachLayer(method, context); + }, + + //Overrides LayerGroup.getLayers + getLayers: function () { + var layers = []; + this.eachLayer(function (l) { + layers.push(l); + }); + return layers; + }, + + //Overrides LayerGroup.getLayer, WARNING: Really bad performance + getLayer: function (id) { + var result = null; + + id = parseInt(id, 10); + + this.eachLayer(function (l) { + if (L.stamp(l) === id) { + result = l; + } + }); + + return result; + }, + + //Returns true if the given layer is in this MarkerClusterGroup + hasLayer: function (layer) { + if (!layer) { + return false; + } + + var i, anArray = this._needsClustering; + + for (i = anArray.length - 1; i >= 0; i--) { + if (anArray[i] === layer) { + return true; + } + } + + anArray = this._needsRemoving; + for (i = anArray.length - 1; i >= 0; i--) { + if (anArray[i] === layer) { + return false; + } + } + + return !!(layer.__parent && layer.__parent._group === this) || this._nonPointGroup.hasLayer(layer); + }, + + //Zoom down to show the given layer (spiderfying if necessary) then calls the callback + zoomToShowLayer: function (layer, callback) { + + if (typeof callback !== 'function') { + callback = function () {}; + } + + var showMarker = function () { + if ((layer._icon || layer.__parent._icon) && !this._inZoomAnimation) { + this._map.off('moveend', showMarker, this); + this.off('animationend', showMarker, this); + + if (layer._icon) { + callback(); + } else if (layer.__parent._icon) { + this.once('spiderfied', callback, this); + layer.__parent.spiderfy(); + } + } + }; + + if (layer._icon && this._map.getBounds().contains(layer.getLatLng())) { + //Layer is visible ond on screen, immediate return + callback(); + } else if (layer.__parent._zoom < this._map.getZoom()) { + //Layer should be visible at this zoom level. It must not be on screen so just pan over to it + this._map.on('moveend', showMarker, this); + this._map.panTo(layer.getLatLng()); + } else { + var moveStart = function () { + this._map.off('movestart', moveStart, this); + moveStart = null; + }; + + this._map.on('movestart', moveStart, this); + this._map.on('moveend', showMarker, this); + this.on('animationend', showMarker, this); + layer.__parent.zoomToBounds(); + + if (moveStart) { + //Never started moving, must already be there, probably need clustering however + showMarker.call(this); + } + } + }, + + //Overrides FeatureGroup.onAdd + onAdd: function (map) { + this._map = map; + var i, l, layer; + + if (!isFinite(this._map.getMaxZoom())) { + throw "Map has no maxZoom specified"; + } + + this._featureGroup.addTo(map); + this._nonPointGroup.addTo(map); + + if (!this._gridClusters) { + this._generateInitialClusters(); + } + + this._maxLat = map.options.crs.projection.MAX_LATITUDE; + + for (i = 0, l = this._needsRemoving.length; i < l; i++) { + layer = this._needsRemoving[i]; + this._removeLayer(layer, true); + } + this._needsRemoving = []; + + //Remember the current zoom level and bounds + this._zoom = this._map.getZoom(); + this._currentShownBounds = this._getExpandedVisibleBounds(); + + this._map.on('zoomend', this._zoomEnd, this); + this._map.on('moveend', this._moveEnd, this); + + if (this._spiderfierOnAdd) { //TODO FIXME: Not sure how to have spiderfier add something on here nicely + this._spiderfierOnAdd(); + } + + this._bindEvents(); + + //Actually add our markers to the map: + l = this._needsClustering; + this._needsClustering = []; + this.addLayers(l); + }, + + //Overrides FeatureGroup.onRemove + onRemove: function (map) { + map.off('zoomend', this._zoomEnd, this); + map.off('moveend', this._moveEnd, this); + + this._unbindEvents(); + + //In case we are in a cluster animation + this._map._mapPane.className = this._map._mapPane.className.replace(' leaflet-cluster-anim', ''); + + if (this._spiderfierOnRemove) { //TODO FIXME: Not sure how to have spiderfier add something on here nicely + this._spiderfierOnRemove(); + } + + delete this._maxLat; + + //Clean up all the layers we added to the map + this._hideCoverage(); + this._featureGroup.remove(); + this._nonPointGroup.remove(); + + this._featureGroup.clearLayers(); + + this._map = null; + }, + + getVisibleParent: function (marker) { + var vMarker = marker; + while (vMarker && !vMarker._icon) { + vMarker = vMarker.__parent; + } + return vMarker || null; + }, + + //Remove the given object from the given array + _arraySplice: function (anArray, obj) { + for (var i = anArray.length - 1; i >= 0; i--) { + if (anArray[i] === obj) { + anArray.splice(i, 1); + return true; + } + } + }, + + /** + * Removes a marker from all _gridUnclustered zoom levels, starting at the supplied zoom. + * @param marker to be removed from _gridUnclustered. + * @param z integer bottom start zoom level (included) + * @private + */ + _removeFromGridUnclustered: function (marker, z) { + var map = this._map, + gridUnclustered = this._gridUnclustered; + + for (; z >= 0; z--) { + if (!gridUnclustered[z].removeObject(marker, map.project(marker.getLatLng(), z))) { + break; + } + } + }, + + _childMarkerMoved: function (e) { + if (!this._ignoreMove) { + e.target._latlng = e.oldLatLng; + this.removeLayer(e.target); + + e.target._latlng = e.latlng; + this.addLayer(e.target); + } + }, + + //Internal function for removing a marker from everything. + //dontUpdateMap: set to true if you will handle updating the map manually (for bulk functions) + _removeLayer: function (marker, removeFromDistanceGrid, dontUpdateMap) { + var gridClusters = this._gridClusters, + gridUnclustered = this._gridUnclustered, + fg = this._featureGroup, + map = this._map; + + //Remove the marker from distance clusters it might be in + if (removeFromDistanceGrid) { + this._removeFromGridUnclustered(marker, this._maxZoom); + } + + //Work our way up the clusters removing them as we go if required + var cluster = marker.__parent, + markers = cluster._markers, + otherMarker; + + //Remove the marker from the immediate parents marker list + this._arraySplice(markers, marker); + + while (cluster) { + cluster._childCount--; + cluster._boundsNeedUpdate = true; + + if (cluster._zoom < 0) { + //Top level, do nothing + break; + } else if (removeFromDistanceGrid && cluster._childCount <= 1) { //Cluster no longer required + //We need to push the other marker up to the parent + otherMarker = cluster._markers[0] === marker ? cluster._markers[1] : cluster._markers[0]; + + //Update distance grid + gridClusters[cluster._zoom].removeObject(cluster, map.project(cluster._cLatLng, cluster._zoom)); + gridUnclustered[cluster._zoom].addObject(otherMarker, map.project(otherMarker.getLatLng(), cluster._zoom)); + + //Move otherMarker up to parent + this._arraySplice(cluster.__parent._childClusters, cluster); + cluster.__parent._markers.push(otherMarker); + otherMarker.__parent = cluster.__parent; + + if (cluster._icon) { + //Cluster is currently on the map, need to put the marker on the map instead + fg.removeLayer(cluster); + if (!dontUpdateMap) { + fg.addLayer(otherMarker); + } + } + } else { + if (!dontUpdateMap || !cluster._icon) { + cluster._updateIcon(); + } + } + + cluster = cluster.__parent; + } + + delete marker.__parent; + }, + + _isOrIsParent: function (el, oel) { + while (oel) { + if (el === oel) { + return true; + } + oel = oel.parentNode; + } + return false; + }, + + //Override L.Evented.fire + fire: function (type, data, propagate) { + if (data && data.layer instanceof L.MarkerCluster) { + //Prevent multiple clustermouseover/off events if the icon is made up of stacked divs (Doesn't work in ie <= 8, no relatedTarget) + if (data.originalEvent && this._isOrIsParent(data.layer._icon, data.originalEvent.relatedTarget)) { + return; + } + type = 'cluster' + type; + } + + L.FeatureGroup.prototype.fire.call(this, type, data, propagate); + }, + + //Override L.Evented.listens + listens: function (type, propagate) { + return L.FeatureGroup.prototype.listens.call(this, type, propagate) || L.FeatureGroup.prototype.listens.call(this, 'cluster' + type, propagate); + }, + + //Default functionality + _defaultIconCreateFunction: function (cluster) { + var childCount = cluster.getChildCount(); + + var c = ' marker-cluster-'; + if (childCount < 10) { + c += 'small'; + } else if (childCount < 100) { + c += 'medium'; + } else { + c += 'large'; + } + + return new L.DivIcon({ html: '
' + childCount + '
', className: 'marker-cluster' + c, iconSize: new L.Point(40, 40) }); + }, + + _bindEvents: function () { + var map = this._map, + spiderfyOnMaxZoom = this.options.spiderfyOnMaxZoom, + showCoverageOnHover = this.options.showCoverageOnHover, + zoomToBoundsOnClick = this.options.zoomToBoundsOnClick; + + //Zoom on cluster click or spiderfy if we are at the lowest level + if (spiderfyOnMaxZoom || zoomToBoundsOnClick) { + this.on('clusterclick', this._zoomOrSpiderfy, this); + } + + //Show convex hull (boundary) polygon on mouse over + if (showCoverageOnHover) { + this.on('clustermouseover', this._showCoverage, this); + this.on('clustermouseout', this._hideCoverage, this); + map.on('zoomend', this._hideCoverage, this); + } + }, + + _zoomOrSpiderfy: function (e) { + var cluster = e.layer, + bottomCluster = cluster; + + while (bottomCluster._childClusters.length === 1) { + bottomCluster = bottomCluster._childClusters[0]; + } + + if (bottomCluster._zoom === this._maxZoom && + bottomCluster._childCount === cluster._childCount && + this.options.spiderfyOnMaxZoom) { + + // All child markers are contained in a single cluster from this._maxZoom to this cluster. + cluster.spiderfy(); + } else if (this.options.zoomToBoundsOnClick) { + cluster.zoomToBounds(); + } + + // Focus the map again for keyboard users. + if (e.originalEvent && e.originalEvent.keyCode === 13) { + this._map._container.focus(); + } + }, + + _showCoverage: function (e) { + var map = this._map; + if (this._inZoomAnimation) { + return; + } + if (this._shownPolygon) { + map.removeLayer(this._shownPolygon); + } + if (e.layer.getChildCount() > 2 && e.layer !== this._spiderfied) { + this._shownPolygon = new L.Polygon(e.layer.getConvexHull(), this.options.polygonOptions); + map.addLayer(this._shownPolygon); + } + }, + + _hideCoverage: function () { + if (this._shownPolygon) { + this._map.removeLayer(this._shownPolygon); + this._shownPolygon = null; + } + }, + + _unbindEvents: function () { + var spiderfyOnMaxZoom = this.options.spiderfyOnMaxZoom, + showCoverageOnHover = this.options.showCoverageOnHover, + zoomToBoundsOnClick = this.options.zoomToBoundsOnClick, + map = this._map; + + if (spiderfyOnMaxZoom || zoomToBoundsOnClick) { + this.off('clusterclick', this._zoomOrSpiderfy, this); + } + if (showCoverageOnHover) { + this.off('clustermouseover', this._showCoverage, this); + this.off('clustermouseout', this._hideCoverage, this); + map.off('zoomend', this._hideCoverage, this); + } + }, + + _zoomEnd: function () { + if (!this._map) { //May have been removed from the map by a zoomEnd handler + return; + } + this._mergeSplitClusters(); + + this._zoom = Math.round(this._map._zoom); + this._currentShownBounds = this._getExpandedVisibleBounds(); + }, + + _moveEnd: function () { + if (this._inZoomAnimation) { + return; + } + + var newBounds = this._getExpandedVisibleBounds(); + + this._topClusterLevel._recursivelyRemoveChildrenFromMap(this._currentShownBounds, this._zoom, newBounds); + this._topClusterLevel._recursivelyAddChildrenToMap(null, Math.round(this._map._zoom), newBounds); + + this._currentShownBounds = newBounds; + return; + }, + + _generateInitialClusters: function () { + var maxZoom = this._map.getMaxZoom(), + radius = this.options.maxClusterRadius, + radiusFn = radius; + + //If we just set maxClusterRadius to a single number, we need to create + //a simple function to return that number. Otherwise, we just have to + //use the function we've passed in. + if (typeof radius !== "function") { + radiusFn = function () { return radius; }; + } + + if (this.options.disableClusteringAtZoom) { + maxZoom = this.options.disableClusteringAtZoom - 1; + } + this._maxZoom = maxZoom; + this._gridClusters = {}; + this._gridUnclustered = {}; + + //Set up DistanceGrids for each zoom + for (var zoom = maxZoom; zoom >= 0; zoom--) { + this._gridClusters[zoom] = new L.DistanceGrid(radiusFn(zoom)); + this._gridUnclustered[zoom] = new L.DistanceGrid(radiusFn(zoom)); + } + + // Instantiate the appropriate L.MarkerCluster class (animated or not). + this._topClusterLevel = new this._markerCluster(this, -1); + }, + + //Zoom: Zoom to start adding at (Pass this._maxZoom to start at the bottom) + _addLayer: function (layer, zoom) { + var gridClusters = this._gridClusters, + gridUnclustered = this._gridUnclustered, + markerPoint, z; + + if (this.options.singleMarkerMode) { + this._overrideMarkerIcon(layer); + } + + layer.on('move', this._childMarkerMoved, this); + + //Find the lowest zoom level to slot this one in + for (; zoom >= 0; zoom--) { + markerPoint = this._map.project(layer.getLatLng(), zoom); // calculate pixel position + + //Try find a cluster close by + var closest = gridClusters[zoom].getNearObject(markerPoint); + if (closest) { + closest._addChild(layer); + layer.__parent = closest; + return; + } + + //Try find a marker close by to form a new cluster with + closest = gridUnclustered[zoom].getNearObject(markerPoint); + if (closest) { + var parent = closest.__parent; + if (parent) { + this._removeLayer(closest, false); + } + + //Create new cluster with these 2 in it + + var newCluster = new this._markerCluster(this, zoom, closest, layer); + gridClusters[zoom].addObject(newCluster, this._map.project(newCluster._cLatLng, zoom)); + closest.__parent = newCluster; + layer.__parent = newCluster; + + //First create any new intermediate parent clusters that don't exist + var lastParent = newCluster; + for (z = zoom - 1; z > parent._zoom; z--) { + lastParent = new this._markerCluster(this, z, lastParent); + gridClusters[z].addObject(lastParent, this._map.project(closest.getLatLng(), z)); + } + parent._addChild(lastParent); + + //Remove closest from this zoom level and any above that it is in, replace with newCluster + this._removeFromGridUnclustered(closest, zoom); + + return; + } + + //Didn't manage to cluster in at this zoom, record us as a marker here and continue upwards + gridUnclustered[zoom].addObject(layer, markerPoint); + } + + //Didn't get in anything, add us to the top + this._topClusterLevel._addChild(layer); + layer.__parent = this._topClusterLevel; + return; + }, + + //Enqueue code to fire after the marker expand/contract has happened + _enqueue: function (fn) { + this._queue.push(fn); + if (!this._queueTimeout) { + this._queueTimeout = setTimeout(L.bind(this._processQueue, this), 300); + } + }, + _processQueue: function () { + for (var i = 0; i < this._queue.length; i++) { + this._queue[i].call(this); + } + this._queue.length = 0; + clearTimeout(this._queueTimeout); + this._queueTimeout = null; + }, + + //Merge and split any existing clusters that are too big or small + _mergeSplitClusters: function () { + var mapZoom = Math.round(this._map._zoom); + + //In case we are starting to split before the animation finished + this._processQueue(); + + if (this._zoom < mapZoom && this._currentShownBounds.intersects(this._getExpandedVisibleBounds())) { //Zoom in, split + this._animationStart(); + //Remove clusters now off screen + this._topClusterLevel._recursivelyRemoveChildrenFromMap(this._currentShownBounds, this._zoom, this._getExpandedVisibleBounds()); + + this._animationZoomIn(this._zoom, mapZoom); + + } else if (this._zoom > mapZoom) { //Zoom out, merge + this._animationStart(); + + this._animationZoomOut(this._zoom, mapZoom); + } else { + this._moveEnd(); + } + }, + + //Gets the maps visible bounds expanded in each direction by the size of the screen (so the user cannot see an area we do not cover in one pan) + _getExpandedVisibleBounds: function () { + if (!this.options.removeOutsideVisibleBounds) { + return this._mapBoundsInfinite; + } else if (L.Browser.mobile) { + return this._checkBoundsMaxLat(this._map.getBounds()); + } + + return this._checkBoundsMaxLat(this._map.getBounds().pad(1)); // Padding expands the bounds by its own dimensions but scaled with the given factor. + }, + + /** + * Expands the latitude to Infinity (or -Infinity) if the input bounds reach the map projection maximum defined latitude + * (in the case of Web/Spherical Mercator, it is 85.0511287798 / see https://en.wikipedia.org/wiki/Web_Mercator#Formulas). + * Otherwise, the removeOutsideVisibleBounds option will remove markers beyond that limit, whereas the same markers without + * this option (or outside MCG) will have their position floored (ceiled) by the projection and rendered at that limit, + * making the user think that MCG "eats" them and never displays them again. + * @param bounds L.LatLngBounds + * @returns {L.LatLngBounds} + * @private + */ + _checkBoundsMaxLat: function (bounds) { + var maxLat = this._maxLat; + + if (maxLat !== undefined) { + if (bounds.getNorth() >= maxLat) { + bounds._northEast.lat = Infinity; + } + if (bounds.getSouth() <= -maxLat) { + bounds._southWest.lat = -Infinity; + } + } + + return bounds; + }, + + //Shared animation code + _animationAddLayerNonAnimated: function (layer, newCluster) { + if (newCluster === layer) { + this._featureGroup.addLayer(layer); + } else if (newCluster._childCount === 2) { + newCluster._addToMap(); + + var markers = newCluster.getAllChildMarkers(); + this._featureGroup.removeLayer(markers[0]); + this._featureGroup.removeLayer(markers[1]); + } else { + newCluster._updateIcon(); + } + }, + + /** + * Extracts individual (i.e. non-group) layers from a Layer Group. + * @param group to extract layers from. + * @param output {Array} in which to store the extracted layers. + * @returns {*|Array} + * @private + */ + _extractNonGroupLayers: function (group, output) { + var layers = group.getLayers(), + i = 0, + layer; + + output = output || []; + + for (; i < layers.length; i++) { + layer = layers[i]; + + if (layer instanceof L.LayerGroup) { + this._extractNonGroupLayers(layer, output); + continue; + } + + output.push(layer); + } + + return output; + }, + + /** + * Implements the singleMarkerMode option. + * @param layer Marker to re-style using the Clusters iconCreateFunction. + * @returns {L.Icon} The newly created icon. + * @private + */ + _overrideMarkerIcon: function (layer) { + var icon = layer.options.icon = this.options.iconCreateFunction({ + getChildCount: function () { + return 1; + }, + getAllChildMarkers: function () { + return [layer]; + } + }); + + return icon; + } +}); + +// Constant bounds used in case option "removeOutsideVisibleBounds" is set to false. +L.MarkerClusterGroup.include({ + _mapBoundsInfinite: new L.LatLngBounds(new L.LatLng(-Infinity, -Infinity), new L.LatLng(Infinity, Infinity)) +}); + +L.MarkerClusterGroup.include({ + _noAnimation: { + //Non Animated versions of everything + _animationStart: function () { + //Do nothing... + }, + _animationZoomIn: function (previousZoomLevel, newZoomLevel) { + this._topClusterLevel._recursivelyRemoveChildrenFromMap(this._currentShownBounds, previousZoomLevel); + this._topClusterLevel._recursivelyAddChildrenToMap(null, newZoomLevel, this._getExpandedVisibleBounds()); + + //We didn't actually animate, but we use this event to mean "clustering animations have finished" + this.fire('animationend'); + }, + _animationZoomOut: function (previousZoomLevel, newZoomLevel) { + this._topClusterLevel._recursivelyRemoveChildrenFromMap(this._currentShownBounds, previousZoomLevel); + this._topClusterLevel._recursivelyAddChildrenToMap(null, newZoomLevel, this._getExpandedVisibleBounds()); + + //We didn't actually animate, but we use this event to mean "clustering animations have finished" + this.fire('animationend'); + }, + _animationAddLayer: function (layer, newCluster) { + this._animationAddLayerNonAnimated(layer, newCluster); + } + }, + + _withAnimation: { + //Animated versions here + _animationStart: function () { + this._map._mapPane.className += ' leaflet-cluster-anim'; + this._inZoomAnimation++; + }, + + _animationZoomIn: function (previousZoomLevel, newZoomLevel) { + var bounds = this._getExpandedVisibleBounds(), + fg = this._featureGroup, + i; + + this._ignoreMove = true; + + //Add all children of current clusters to map and remove those clusters from map + this._topClusterLevel._recursively(bounds, previousZoomLevel, 0, function (c) { + var startPos = c._latlng, + markers = c._markers, + m; + + if (!bounds.contains(startPos)) { + startPos = null; + } + + if (c._isSingleParent() && previousZoomLevel + 1 === newZoomLevel) { //Immediately add the new child and remove us + fg.removeLayer(c); + c._recursivelyAddChildrenToMap(null, newZoomLevel, bounds); + } else { + //Fade out old cluster + c.clusterHide(); + c._recursivelyAddChildrenToMap(startPos, newZoomLevel, bounds); + } + + //Remove all markers that aren't visible any more + //TODO: Do we actually need to do this on the higher levels too? + for (i = markers.length - 1; i >= 0; i--) { + m = markers[i]; + if (!bounds.contains(m._latlng)) { + fg.removeLayer(m); + } + } + + }); + + this._forceLayout(); + + //Update opacities + this._topClusterLevel._recursivelyBecomeVisible(bounds, newZoomLevel); + //TODO Maybe? Update markers in _recursivelyBecomeVisible + fg.eachLayer(function (n) { + if (!(n instanceof L.MarkerCluster) && n._icon) { + n.clusterShow(); + } + }); + + //update the positions of the just added clusters/markers + this._topClusterLevel._recursively(bounds, previousZoomLevel, newZoomLevel, function (c) { + c._recursivelyRestoreChildPositions(newZoomLevel); + }); + + this._ignoreMove = false; + + //Remove the old clusters and close the zoom animation + this._enqueue(function () { + //update the positions of the just added clusters/markers + this._topClusterLevel._recursively(bounds, previousZoomLevel, 0, function (c) { + fg.removeLayer(c); + c.clusterShow(); + }); + + this._animationEnd(); + }); + }, + + _animationZoomOut: function (previousZoomLevel, newZoomLevel) { + this._animationZoomOutSingle(this._topClusterLevel, previousZoomLevel - 1, newZoomLevel); + + //Need to add markers for those that weren't on the map before but are now + this._topClusterLevel._recursivelyAddChildrenToMap(null, newZoomLevel, this._getExpandedVisibleBounds()); + //Remove markers that were on the map before but won't be now + this._topClusterLevel._recursivelyRemoveChildrenFromMap(this._currentShownBounds, previousZoomLevel, this._getExpandedVisibleBounds()); + }, + + _animationAddLayer: function (layer, newCluster) { + var me = this, + fg = this._featureGroup; + + fg.addLayer(layer); + if (newCluster !== layer) { + if (newCluster._childCount > 2) { //Was already a cluster + + newCluster._updateIcon(); + this._forceLayout(); + this._animationStart(); + + layer._setPos(this._map.latLngToLayerPoint(newCluster.getLatLng())); + layer.clusterHide(); + + this._enqueue(function () { + fg.removeLayer(layer); + layer.clusterShow(); + + me._animationEnd(); + }); + + } else { //Just became a cluster + this._forceLayout(); + + me._animationStart(); + me._animationZoomOutSingle(newCluster, this._map.getMaxZoom(), this._map.getZoom()); + } + } + } + }, + + // Private methods for animated versions. + _animationZoomOutSingle: function (cluster, previousZoomLevel, newZoomLevel) { + var bounds = this._getExpandedVisibleBounds(); + + //Animate all of the markers in the clusters to move to their cluster center point + cluster._recursivelyAnimateChildrenInAndAddSelfToMap(bounds, previousZoomLevel + 1, newZoomLevel); + + var me = this; + + //Update the opacity (If we immediately set it they won't animate) + this._forceLayout(); + cluster._recursivelyBecomeVisible(bounds, newZoomLevel); + + //TODO: Maybe use the transition timing stuff to make this more reliable + //When the animations are done, tidy up + this._enqueue(function () { + + //This cluster stopped being a cluster before the timeout fired + if (cluster._childCount === 1) { + var m = cluster._markers[0]; + //If we were in a cluster animation at the time then the opacity and position of our child could be wrong now, so fix it + this._ignoreMove = true; + m.setLatLng(m.getLatLng()); + this._ignoreMove = false; + if (m.clusterShow) { + m.clusterShow(); + } + } else { + cluster._recursively(bounds, newZoomLevel, 0, function (c) { + c._recursivelyRemoveChildrenFromMap(bounds, previousZoomLevel + 1); + }); + } + me._animationEnd(); + }); + }, + + _animationEnd: function () { + if (this._map) { + this._map._mapPane.className = this._map._mapPane.className.replace(' leaflet-cluster-anim', ''); + } + this._inZoomAnimation--; + this.fire('animationend'); + }, + + //Force a browser layout of stuff in the map + // Should apply the current opacity and location to all elements so we can update them again for an animation + _forceLayout: function () { + //In my testing this works, infact offsetWidth of any element seems to work. + //Could loop all this._layers and do this for each _icon if it stops working + + L.Util.falseFn(document.body.offsetWidth); + } +}); + +L.markerClusterGroup = function (options) { + return new L.MarkerClusterGroup(options); +}; + + +L.MarkerCluster = L.Marker.extend({ + initialize: function (group, zoom, a, b) { + + L.Marker.prototype.initialize.call(this, a ? (a._cLatLng || a.getLatLng()) : new L.LatLng(0, 0), { icon: this }); + + + this._group = group; + this._zoom = zoom; + + this._markers = []; + this._childClusters = []; + this._childCount = 0; + this._iconNeedsUpdate = true; + this._boundsNeedUpdate = true; + + this._bounds = new L.LatLngBounds(); + + if (a) { + this._addChild(a); + } + if (b) { + this._addChild(b); + } + }, + + //Recursively retrieve all child markers of this cluster + getAllChildMarkers: function (storageArray) { + storageArray = storageArray || []; + + for (var i = this._childClusters.length - 1; i >= 0; i--) { + this._childClusters[i].getAllChildMarkers(storageArray); + } + + for (var j = this._markers.length - 1; j >= 0; j--) { + storageArray.push(this._markers[j]); + } + + return storageArray; + }, + + //Returns the count of how many child markers we have + getChildCount: function () { + return this._childCount; + }, + + //Zoom to the minimum of showing all of the child markers, or the extents of this cluster + zoomToBounds: function () { + var childClusters = this._childClusters.slice(), + map = this._group._map, + boundsZoom = map.getBoundsZoom(this._bounds), + zoom = this._zoom + 1, + mapZoom = map.getZoom(), + i; + + //calculate how far we need to zoom down to see all of the markers + while (childClusters.length > 0 && boundsZoom > zoom) { + zoom++; + var newClusters = []; + for (i = 0; i < childClusters.length; i++) { + newClusters = newClusters.concat(childClusters[i]._childClusters); + } + childClusters = newClusters; + } + + if (boundsZoom > zoom) { + this._group._map.setView(this._latlng, zoom); + } else if (boundsZoom <= mapZoom) { //If fitBounds wouldn't zoom us down, zoom us down instead + this._group._map.setView(this._latlng, mapZoom + 1); + } else { + this._group._map.fitBounds(this._bounds); + } + }, + + getBounds: function () { + var bounds = new L.LatLngBounds(); + bounds.extend(this._bounds); + return bounds; + }, + + _updateIcon: function () { + this._iconNeedsUpdate = true; + if (this._icon) { + this.setIcon(this); + } + }, + + //Cludge for Icon, we pretend to be an icon for performance + createIcon: function () { + if (this._iconNeedsUpdate) { + this._iconObj = this._group.options.iconCreateFunction(this); + this._iconNeedsUpdate = false; + } + return this._iconObj.createIcon(); + }, + createShadow: function () { + return this._iconObj.createShadow(); + }, + + + _addChild: function (new1, isNotificationFromChild) { + + this._iconNeedsUpdate = true; + + this._boundsNeedUpdate = true; + this._setClusterCenter(new1); + + if (new1 instanceof L.MarkerCluster) { + if (!isNotificationFromChild) { + this._childClusters.push(new1); + new1.__parent = this; + } + this._childCount += new1._childCount; + } else { + if (!isNotificationFromChild) { + this._markers.push(new1); + } + this._childCount++; + } + + if (this.__parent) { + this.__parent._addChild(new1, true); + } + }, + + /** + * Makes sure the cluster center is set. If not, uses the child center if it is a cluster, or the marker position. + * @param child L.MarkerCluster|L.Marker that will be used as cluster center if not defined yet. + * @private + */ + _setClusterCenter: function (child) { + if (!this._cLatLng) { + // when clustering, take position of the first point as the cluster center + this._cLatLng = child._cLatLng || child._latlng; + } + }, + + /** + * Assigns impossible bounding values so that the next extend entirely determines the new bounds. + * This method avoids having to trash the previous L.LatLngBounds object and to create a new one, which is much slower for this class. + * As long as the bounds are not extended, most other methods would probably fail, as they would with bounds initialized but not extended. + * @private + */ + _resetBounds: function () { + var bounds = this._bounds; + + if (bounds._southWest) { + bounds._southWest.lat = Infinity; + bounds._southWest.lng = Infinity; + } + if (bounds._northEast) { + bounds._northEast.lat = -Infinity; + bounds._northEast.lng = -Infinity; + } + }, + + _recalculateBounds: function () { + var markers = this._markers, + childClusters = this._childClusters, + latSum = 0, + lngSum = 0, + totalCount = this._childCount, + i, child, childLatLng, childCount; + + // Case where all markers are removed from the map and we are left with just an empty _topClusterLevel. + if (totalCount === 0) { + return; + } + + // Reset rather than creating a new object, for performance. + this._resetBounds(); + + // Child markers. + for (i = 0; i < markers.length; i++) { + childLatLng = markers[i]._latlng; + + this._bounds.extend(childLatLng); + + latSum += childLatLng.lat; + lngSum += childLatLng.lng; + } + + // Child clusters. + for (i = 0; i < childClusters.length; i++) { + child = childClusters[i]; + + // Re-compute child bounds and weighted position first if necessary. + if (child._boundsNeedUpdate) { + child._recalculateBounds(); + } + + this._bounds.extend(child._bounds); + + childLatLng = child._wLatLng; + childCount = child._childCount; + + latSum += childLatLng.lat * childCount; + lngSum += childLatLng.lng * childCount; + } + + this._latlng = this._wLatLng = new L.LatLng(latSum / totalCount, lngSum / totalCount); + + // Reset dirty flag. + this._boundsNeedUpdate = false; + }, + + //Set our markers position as given and add it to the map + _addToMap: function (startPos) { + if (startPos) { + this._backupLatlng = this._latlng; + this.setLatLng(startPos); + } + this._group._featureGroup.addLayer(this); + }, + + _recursivelyAnimateChildrenIn: function (bounds, center, maxZoom) { + this._recursively(bounds, 0, maxZoom - 1, + function (c) { + var markers = c._markers, + i, m; + for (i = markers.length - 1; i >= 0; i--) { + m = markers[i]; + + //Only do it if the icon is still on the map + if (m._icon) { + m._setPos(center); + m.clusterHide(); + } + } + }, + function (c) { + var childClusters = c._childClusters, + j, cm; + for (j = childClusters.length - 1; j >= 0; j--) { + cm = childClusters[j]; + if (cm._icon) { + cm._setPos(center); + cm.clusterHide(); + } + } + } + ); + }, + + _recursivelyAnimateChildrenInAndAddSelfToMap: function (bounds, previousZoomLevel, newZoomLevel) { + this._recursively(bounds, newZoomLevel, 0, + function (c) { + c._recursivelyAnimateChildrenIn(bounds, c._group._map.latLngToLayerPoint(c.getLatLng()).round(), previousZoomLevel); + + //TODO: depthToAnimateIn affects _isSingleParent, if there is a multizoom we may/may not be. + //As a hack we only do a animation free zoom on a single level zoom, if someone does multiple levels then we always animate + if (c._isSingleParent() && previousZoomLevel - 1 === newZoomLevel) { + c.clusterShow(); + c._recursivelyRemoveChildrenFromMap(bounds, previousZoomLevel); //Immediately remove our children as we are replacing them. TODO previousBounds not bounds + } else { + c.clusterHide(); + } + + c._addToMap(); + } + ); + }, + + _recursivelyBecomeVisible: function (bounds, zoomLevel) { + this._recursively(bounds, 0, zoomLevel, null, function (c) { + c.clusterShow(); + }); + }, + + _recursivelyAddChildrenToMap: function (startPos, zoomLevel, bounds) { + this._recursively(bounds, -1, zoomLevel, + function (c) { + if (zoomLevel === c._zoom) { + return; + } + + //Add our child markers at startPos (so they can be animated out) + for (var i = c._markers.length - 1; i >= 0; i--) { + var nm = c._markers[i]; + + if (!bounds.contains(nm._latlng)) { + continue; + } + + if (startPos) { + nm._backupLatlng = nm.getLatLng(); + + nm.setLatLng(startPos); + if (nm.clusterHide) { + nm.clusterHide(); + } + } + + c._group._featureGroup.addLayer(nm); + } + }, + function (c) { + c._addToMap(startPos); + } + ); + }, + + _recursivelyRestoreChildPositions: function (zoomLevel) { + //Fix positions of child markers + for (var i = this._markers.length - 1; i >= 0; i--) { + var nm = this._markers[i]; + if (nm._backupLatlng) { + nm.setLatLng(nm._backupLatlng); + delete nm._backupLatlng; + } + } + + if (zoomLevel - 1 === this._zoom) { + //Reposition child clusters + for (var j = this._childClusters.length - 1; j >= 0; j--) { + this._childClusters[j]._restorePosition(); + } + } else { + for (var k = this._childClusters.length - 1; k >= 0; k--) { + this._childClusters[k]._recursivelyRestoreChildPositions(zoomLevel); + } + } + }, + + _restorePosition: function () { + if (this._backupLatlng) { + this.setLatLng(this._backupLatlng); + delete this._backupLatlng; + } + }, + + //exceptBounds: If set, don't remove any markers/clusters in it + _recursivelyRemoveChildrenFromMap: function (previousBounds, zoomLevel, exceptBounds) { + var m, i; + this._recursively(previousBounds, -1, zoomLevel - 1, + function (c) { + //Remove markers at every level + for (i = c._markers.length - 1; i >= 0; i--) { + m = c._markers[i]; + if (!exceptBounds || !exceptBounds.contains(m._latlng)) { + c._group._featureGroup.removeLayer(m); + if (m.clusterShow) { + m.clusterShow(); + } + } + } + }, + function (c) { + //Remove child clusters at just the bottom level + for (i = c._childClusters.length - 1; i >= 0; i--) { + m = c._childClusters[i]; + if (!exceptBounds || !exceptBounds.contains(m._latlng)) { + c._group._featureGroup.removeLayer(m); + if (m.clusterShow) { + m.clusterShow(); + } + } + } + } + ); + }, + + //Run the given functions recursively to this and child clusters + // boundsToApplyTo: a L.LatLngBounds representing the bounds of what clusters to recurse in to + // zoomLevelToStart: zoom level to start running functions (inclusive) + // zoomLevelToStop: zoom level to stop running functions (inclusive) + // runAtEveryLevel: function that takes an L.MarkerCluster as an argument that should be applied on every level + // runAtBottomLevel: function that takes an L.MarkerCluster as an argument that should be applied at only the bottom level + _recursively: function (boundsToApplyTo, zoomLevelToStart, zoomLevelToStop, runAtEveryLevel, runAtBottomLevel) { + var childClusters = this._childClusters, + zoom = this._zoom, + i, c; + + if (zoomLevelToStart > zoom) { //Still going down to required depth, just recurse to child clusters + for (i = childClusters.length - 1; i >= 0; i--) { + c = childClusters[i]; + if (boundsToApplyTo.intersects(c._bounds)) { + c._recursively(boundsToApplyTo, zoomLevelToStart, zoomLevelToStop, runAtEveryLevel, runAtBottomLevel); + } + } + } else { //In required depth + + if (runAtEveryLevel) { + runAtEveryLevel(this); + } + if (runAtBottomLevel && this._zoom === zoomLevelToStop) { + runAtBottomLevel(this); + } + + //TODO: This loop is almost the same as above + if (zoomLevelToStop > zoom) { + for (i = childClusters.length - 1; i >= 0; i--) { + c = childClusters[i]; + if (boundsToApplyTo.intersects(c._bounds)) { + c._recursively(boundsToApplyTo, zoomLevelToStart, zoomLevelToStop, runAtEveryLevel, runAtBottomLevel); + } + } + } + } + }, + + //Returns true if we are the parent of only one cluster and that cluster is the same as us + _isSingleParent: function () { + //Don't need to check this._markers as the rest won't work if there are any + return this._childClusters.length > 0 && this._childClusters[0]._childCount === this._childCount; + } +}); + + + +/* +* Extends L.Marker to include two extra methods: clusterHide and clusterShow. +* +* They work as setOpacity(0) and setOpacity(1) respectively, but +* they will remember the marker's opacity when hiding and showing it again. +* +*/ + + +L.Marker.include({ + + clusterHide: function () { + this.options.opacityWhenUnclustered = this.options.opacity || 1; + return this.setOpacity(0); + }, + + clusterShow: function () { + var ret = this.setOpacity(this.options.opacity || this.options.opacityWhenUnclustered); + delete this.options.opacityWhenUnclustered; + return ret; + } + +}); + + + + + +L.DistanceGrid = function (cellSize) { + this._cellSize = cellSize; + this._sqCellSize = cellSize * cellSize; + this._grid = {}; + this._objectPoint = { }; +}; + +L.DistanceGrid.prototype = { + + addObject: function (obj, point) { + var x = this._getCoord(point.x), + y = this._getCoord(point.y), + grid = this._grid, + row = grid[y] = grid[y] || {}, + cell = row[x] = row[x] || [], + stamp = L.Util.stamp(obj); + + this._objectPoint[stamp] = point; + + cell.push(obj); + }, + + updateObject: function (obj, point) { + this.removeObject(obj); + this.addObject(obj, point); + }, + + //Returns true if the object was found + removeObject: function (obj, point) { + var x = this._getCoord(point.x), + y = this._getCoord(point.y), + grid = this._grid, + row = grid[y] = grid[y] || {}, + cell = row[x] = row[x] || [], + i, len; + + delete this._objectPoint[L.Util.stamp(obj)]; + + for (i = 0, len = cell.length; i < len; i++) { + if (cell[i] === obj) { + + cell.splice(i, 1); + + if (len === 1) { + delete row[x]; + } + + return true; + } + } + + }, + + eachObject: function (fn, context) { + var i, j, k, len, row, cell, removed, + grid = this._grid; + + for (i in grid) { + row = grid[i]; + + for (j in row) { + cell = row[j]; + + for (k = 0, len = cell.length; k < len; k++) { + removed = fn.call(context, cell[k]); + if (removed) { + k--; + len--; + } + } + } + } + }, + + getNearObject: function (point) { + var x = this._getCoord(point.x), + y = this._getCoord(point.y), + i, j, k, row, cell, len, obj, dist, + objectPoint = this._objectPoint, + closestDistSq = this._sqCellSize, + closest = null; + + for (i = y - 1; i <= y + 1; i++) { + row = this._grid[i]; + if (row) { + + for (j = x - 1; j <= x + 1; j++) { + cell = row[j]; + if (cell) { + + for (k = 0, len = cell.length; k < len; k++) { + obj = cell[k]; + dist = this._sqDist(objectPoint[L.Util.stamp(obj)], point); + if (dist < closestDistSq) { + closestDistSq = dist; + closest = obj; + } + } + } + } + } + } + return closest; + }, + + _getCoord: function (x) { + return Math.floor(x / this._cellSize); + }, + + _sqDist: function (p, p2) { + var dx = p2.x - p.x, + dy = p2.y - p.y; + return dx * dx + dy * dy; + } +}; + + +/* Copyright (c) 2012 the authors listed at the following URL, and/or +the authors of referenced articles or incorporated external code: +http://en.literateprograms.org/Quickhull_(Javascript)?action=history&offset=20120410175256 + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +Retrieved from: http://en.literateprograms.org/Quickhull_(Javascript)?oldid=18434 +*/ + +(function () { + L.QuickHull = { + + /* + * @param {Object} cpt a point to be measured from the baseline + * @param {Array} bl the baseline, as represented by a two-element + * array of latlng objects. + * @returns {Number} an approximate distance measure + */ + getDistant: function (cpt, bl) { + var vY = bl[1].lat - bl[0].lat, + vX = bl[0].lng - bl[1].lng; + return (vX * (cpt.lat - bl[0].lat) + vY * (cpt.lng - bl[0].lng)); + }, + + /* + * @param {Array} baseLine a two-element array of latlng objects + * representing the baseline to project from + * @param {Array} latLngs an array of latlng objects + * @returns {Object} the maximum point and all new points to stay + * in consideration for the hull. + */ + findMostDistantPointFromBaseLine: function (baseLine, latLngs) { + var maxD = 0, + maxPt = null, + newPoints = [], + i, pt, d; + + for (i = latLngs.length - 1; i >= 0; i--) { + pt = latLngs[i]; + d = this.getDistant(pt, baseLine); + + if (d > 0) { + newPoints.push(pt); + } else { + continue; + } + + if (d > maxD) { + maxD = d; + maxPt = pt; + } + } + + return { maxPoint: maxPt, newPoints: newPoints }; + }, + + + /* + * Given a baseline, compute the convex hull of latLngs as an array + * of latLngs. + * + * @param {Array} latLngs + * @returns {Array} + */ + buildConvexHull: function (baseLine, latLngs) { + var convexHullBaseLines = [], + t = this.findMostDistantPointFromBaseLine(baseLine, latLngs); + + if (t.maxPoint) { // if there is still a point "outside" the base line + convexHullBaseLines = + convexHullBaseLines.concat( + this.buildConvexHull([baseLine[0], t.maxPoint], t.newPoints) + ); + convexHullBaseLines = + convexHullBaseLines.concat( + this.buildConvexHull([t.maxPoint, baseLine[1]], t.newPoints) + ); + return convexHullBaseLines; + } else { // if there is no more point "outside" the base line, the current base line is part of the convex hull + return [baseLine[0]]; + } + }, + + /* + * Given an array of latlngs, compute a convex hull as an array + * of latlngs + * + * @param {Array} latLngs + * @returns {Array} + */ + getConvexHull: function (latLngs) { + // find first baseline + var maxLat = false, minLat = false, + maxLng = false, minLng = false, + maxLatPt = null, minLatPt = null, + maxLngPt = null, minLngPt = null, + maxPt = null, minPt = null, + i; + + for (i = latLngs.length - 1; i >= 0; i--) { + var pt = latLngs[i]; + if (maxLat === false || pt.lat > maxLat) { + maxLatPt = pt; + maxLat = pt.lat; + } + if (minLat === false || pt.lat < minLat) { + minLatPt = pt; + minLat = pt.lat; + } + if (maxLng === false || pt.lng > maxLng) { + maxLngPt = pt; + maxLng = pt.lng; + } + if (minLng === false || pt.lng < minLng) { + minLngPt = pt; + minLng = pt.lng; + } + } + + if (minLat !== maxLat) { + minPt = minLatPt; + maxPt = maxLatPt; + } else { + minPt = minLngPt; + maxPt = maxLngPt; + } + + var ch = [].concat(this.buildConvexHull([minPt, maxPt], latLngs), + this.buildConvexHull([maxPt, minPt], latLngs)); + return ch; + } + }; +}()); + +L.MarkerCluster.include({ + getConvexHull: function () { + var childMarkers = this.getAllChildMarkers(), + points = [], + p, i; + + for (i = childMarkers.length - 1; i >= 0; i--) { + p = childMarkers[i].getLatLng(); + points.push(p); + } + + return L.QuickHull.getConvexHull(points); + } +}); + + +//This code is 100% based on https://github.com/jawj/OverlappingMarkerSpiderfier-Leaflet +//Huge thanks to jawj for implementing it first to make my job easy :-) + +L.MarkerCluster.include({ + + _2PI: Math.PI * 2, + _circleFootSeparation: 25, //related to circumference of circle + _circleStartAngle: Math.PI / 6, + + _spiralFootSeparation: 28, //related to size of spiral (experiment!) + _spiralLengthStart: 11, + _spiralLengthFactor: 5, + + _circleSpiralSwitchover: 9, //show spiral instead of circle from this marker count upwards. + // 0 -> always spiral; Infinity -> always circle + + spiderfy: function () { + if (this._group._spiderfied === this || this._group._inZoomAnimation) { + return; + } + + var childMarkers = this.getAllChildMarkers(), + group = this._group, + map = group._map, + center = map.latLngToLayerPoint(this._latlng), + positions; + + this._group._unspiderfy(); + this._group._spiderfied = this; + + //TODO Maybe: childMarkers order by distance to center + + if (childMarkers.length >= this._circleSpiralSwitchover) { + positions = this._generatePointsSpiral(childMarkers.length, center); + } else { + center.y += 10; // Otherwise circles look wrong => hack for standard blue icon, renders differently for other icons. + positions = this._generatePointsCircle(childMarkers.length, center); + } + + this._animationSpiderfy(childMarkers, positions); + }, + + unspiderfy: function (zoomDetails) { + /// Argument from zoomanim if being called in a zoom animation or null otherwise + if (this._group._inZoomAnimation) { + return; + } + this._animationUnspiderfy(zoomDetails); + + this._group._spiderfied = null; + }, + + _generatePointsCircle: function (count, centerPt) { + var circumference = this._group.options.spiderfyDistanceMultiplier * this._circleFootSeparation * (2 + count), + legLength = circumference / this._2PI, //radius from circumference + angleStep = this._2PI / count, + res = [], + i, angle; + + res.length = count; + + for (i = count - 1; i >= 0; i--) { + angle = this._circleStartAngle + i * angleStep; + res[i] = new L.Point(centerPt.x + legLength * Math.cos(angle), centerPt.y + legLength * Math.sin(angle))._round(); + } + + return res; + }, + + _generatePointsSpiral: function (count, centerPt) { + var spiderfyDistanceMultiplier = this._group.options.spiderfyDistanceMultiplier, + legLength = spiderfyDistanceMultiplier * this._spiralLengthStart, + separation = spiderfyDistanceMultiplier * this._spiralFootSeparation, + lengthFactor = spiderfyDistanceMultiplier * this._spiralLengthFactor * this._2PI, + angle = 0, + res = [], + i; + + res.length = count; + + // Higher index, closer position to cluster center. + for (i = count - 1; i >= 0; i--) { + angle += separation / legLength + i * 0.0005; + res[i] = new L.Point(centerPt.x + legLength * Math.cos(angle), centerPt.y + legLength * Math.sin(angle))._round(); + legLength += lengthFactor / angle; + } + return res; + }, + + _noanimationUnspiderfy: function () { + var group = this._group, + map = group._map, + fg = group._featureGroup, + childMarkers = this.getAllChildMarkers(), + m, i; + + group._ignoreMove = true; + + this.setOpacity(1); + for (i = childMarkers.length - 1; i >= 0; i--) { + m = childMarkers[i]; + + fg.removeLayer(m); + + if (m._preSpiderfyLatlng) { + m.setLatLng(m._preSpiderfyLatlng); + delete m._preSpiderfyLatlng; + } + if (m.setZIndexOffset) { + m.setZIndexOffset(0); + } + + if (m._spiderLeg) { + map.removeLayer(m._spiderLeg); + delete m._spiderLeg; + } + } + + group.fire('unspiderfied', { + cluster: this, + markers: childMarkers + }); + group._ignoreMove = false; + group._spiderfied = null; + } +}); + +//Non Animated versions of everything +L.MarkerClusterNonAnimated = L.MarkerCluster.extend({ + _animationSpiderfy: function (childMarkers, positions) { + var group = this._group, + map = group._map, + fg = group._featureGroup, + legOptions = this._group.options.spiderLegPolylineOptions, + i, m, leg, newPos; + + group._ignoreMove = true; + + // Traverse in ascending order to make sure that inner circleMarkers are on top of further legs. Normal markers are re-ordered by newPosition. + // The reverse order trick no longer improves performance on modern browsers. + for (i = 0; i < childMarkers.length; i++) { + newPos = map.layerPointToLatLng(positions[i]); + m = childMarkers[i]; + + // Add the leg before the marker, so that in case the latter is a circleMarker, the leg is behind it. + leg = new L.Polyline([this._latlng, newPos], legOptions); + map.addLayer(leg); + m._spiderLeg = leg; + + // Now add the marker. + m._preSpiderfyLatlng = m._latlng; + m.setLatLng(newPos); + if (m.setZIndexOffset) { + m.setZIndexOffset(1000000); //Make these appear on top of EVERYTHING + } + + fg.addLayer(m); + } + this.setOpacity(0.3); + + group._ignoreMove = false; + group.fire('spiderfied', { + cluster: this, + markers: childMarkers + }); + }, + + _animationUnspiderfy: function () { + this._noanimationUnspiderfy(); + } +}); + +//Animated versions here +L.MarkerCluster.include({ + + _animationSpiderfy: function (childMarkers, positions) { + var me = this, + group = this._group, + map = group._map, + fg = group._featureGroup, + thisLayerLatLng = this._latlng, + thisLayerPos = map.latLngToLayerPoint(thisLayerLatLng), + svg = L.Path.SVG, + legOptions = L.extend({}, this._group.options.spiderLegPolylineOptions), // Copy the options so that we can modify them for animation. + finalLegOpacity = legOptions.opacity, + i, m, leg, legPath, legLength, newPos; + + if (finalLegOpacity === undefined) { + finalLegOpacity = L.MarkerClusterGroup.prototype.options.spiderLegPolylineOptions.opacity; + } + + if (svg) { + // If the initial opacity of the spider leg is not 0 then it appears before the animation starts. + legOptions.opacity = 0; + + // Add the class for CSS transitions. + legOptions.className = (legOptions.className || '') + ' leaflet-cluster-spider-leg'; + } else { + // Make sure we have a defined opacity. + legOptions.opacity = finalLegOpacity; + } + + group._ignoreMove = true; + + // Add markers and spider legs to map, hidden at our center point. + // Traverse in ascending order to make sure that inner circleMarkers are on top of further legs. Normal markers are re-ordered by newPosition. + // The reverse order trick no longer improves performance on modern browsers. + for (i = 0; i < childMarkers.length; i++) { + m = childMarkers[i]; + + newPos = map.layerPointToLatLng(positions[i]); + + // Add the leg before the marker, so that in case the latter is a circleMarker, the leg is behind it. + leg = new L.Polyline([thisLayerLatLng, newPos], legOptions); + map.addLayer(leg); + m._spiderLeg = leg; + + // Explanations: https://jakearchibald.com/2013/animated-line-drawing-svg/ + // In our case the transition property is declared in the CSS file. + if (svg) { + legPath = leg._path; + legLength = legPath.getTotalLength() + 0.1; // Need a small extra length to avoid remaining dot in Firefox. + legPath.style.strokeDasharray = legLength; // Just 1 length is enough, it will be duplicated. + legPath.style.strokeDashoffset = legLength; + } + + // If it is a marker, add it now and we'll animate it out + if (m.setZIndexOffset) { + m.setZIndexOffset(1000000); // Make normal markers appear on top of EVERYTHING + } + if (m.clusterHide) { + m.clusterHide(); + } + + // Vectors just get immediately added + fg.addLayer(m); + + if (m._setPos) { + m._setPos(thisLayerPos); + } + } + + group._forceLayout(); + group._animationStart(); + + // Reveal markers and spider legs. + for (i = childMarkers.length - 1; i >= 0; i--) { + newPos = map.layerPointToLatLng(positions[i]); + m = childMarkers[i]; + + //Move marker to new position + m._preSpiderfyLatlng = m._latlng; + m.setLatLng(newPos); + + if (m.clusterShow) { + m.clusterShow(); + } + + // Animate leg (animation is actually delegated to CSS transition). + if (svg) { + leg = m._spiderLeg; + legPath = leg._path; + legPath.style.strokeDashoffset = 0; + //legPath.style.strokeOpacity = finalLegOpacity; + leg.setStyle({opacity: finalLegOpacity}); + } + } + this.setOpacity(0.3); + + group._ignoreMove = false; + + setTimeout(function () { + group._animationEnd(); + group.fire('spiderfied', { + cluster: me, + markers: childMarkers + }); + }, 200); + }, + + _animationUnspiderfy: function (zoomDetails) { + var me = this, + group = this._group, + map = group._map, + fg = group._featureGroup, + thisLayerPos = zoomDetails ? map._latLngToNewLayerPoint(this._latlng, zoomDetails.zoom, zoomDetails.center) : map.latLngToLayerPoint(this._latlng), + childMarkers = this.getAllChildMarkers(), + svg = L.Path.SVG, + m, i, leg, legPath, legLength, nonAnimatable; + + group._ignoreMove = true; + group._animationStart(); + + //Make us visible and bring the child markers back in + this.setOpacity(1); + for (i = childMarkers.length - 1; i >= 0; i--) { + m = childMarkers[i]; + + //Marker was added to us after we were spiderfied + if (!m._preSpiderfyLatlng) { + continue; + } + + //Fix up the location to the real one + m.setLatLng(m._preSpiderfyLatlng); + delete m._preSpiderfyLatlng; + + //Hack override the location to be our center + nonAnimatable = true; + if (m._setPos) { + m._setPos(thisLayerPos); + nonAnimatable = false; + } + if (m.clusterHide) { + m.clusterHide(); + nonAnimatable = false; + } + if (nonAnimatable) { + fg.removeLayer(m); + } + + // Animate the spider leg back in (animation is actually delegated to CSS transition). + if (svg) { + leg = m._spiderLeg; + legPath = leg._path; + legLength = legPath.getTotalLength() + 0.1; + legPath.style.strokeDashoffset = legLength; + leg.setStyle({opacity: 0}); + } + } + + group._ignoreMove = false; + + setTimeout(function () { + //If we have only <= one child left then that marker will be shown on the map so don't remove it! + var stillThereChildCount = 0; + for (i = childMarkers.length - 1; i >= 0; i--) { + m = childMarkers[i]; + if (m._spiderLeg) { + stillThereChildCount++; + } + } + + + for (i = childMarkers.length - 1; i >= 0; i--) { + m = childMarkers[i]; + + if (!m._spiderLeg) { //Has already been unspiderfied + continue; + } + + if (m.clusterShow) { + m.clusterShow(); + } + if (m.setZIndexOffset) { + m.setZIndexOffset(0); + } + + if (stillThereChildCount > 1) { + fg.removeLayer(m); + } + + map.removeLayer(m._spiderLeg); + delete m._spiderLeg; + } + group._animationEnd(); + group.fire('unspiderfied', { + cluster: me, + markers: childMarkers + }); + }, 200); + } +}); + + +L.MarkerClusterGroup.include({ + //The MarkerCluster currently spiderfied (if any) + _spiderfied: null, + + unspiderfy: function () { + this._unspiderfy.apply(this, arguments); + }, + + _spiderfierOnAdd: function () { + this._map.on('click', this._unspiderfyWrapper, this); + + if (this._map.options.zoomAnimation) { + this._map.on('zoomstart', this._unspiderfyZoomStart, this); + } + //Browsers without zoomAnimation or a big zoom don't fire zoomstart + this._map.on('zoomend', this._noanimationUnspiderfy, this); + + if (!L.Browser.touch) { + this._map.getRenderer(this); + //Needs to happen in the pageload, not after, or animations don't work in webkit + // http://stackoverflow.com/questions/8455200/svg-animate-with-dynamically-added-elements + //Disable on touch browsers as the animation messes up on a touch zoom and isn't very noticable + } + }, + + _spiderfierOnRemove: function () { + this._map.off('click', this._unspiderfyWrapper, this); + this._map.off('zoomstart', this._unspiderfyZoomStart, this); + this._map.off('zoomanim', this._unspiderfyZoomAnim, this); + this._map.off('zoomend', this._noanimationUnspiderfy, this); + + //Ensure that markers are back where they should be + // Use no animation to avoid a sticky leaflet-cluster-anim class on mapPane + this._noanimationUnspiderfy(); + }, + + //On zoom start we add a zoomanim handler so that we are guaranteed to be last (after markers are animated) + //This means we can define the animation they do rather than Markers doing an animation to their actual location + _unspiderfyZoomStart: function () { + if (!this._map) { //May have been removed from the map by a zoomEnd handler + return; + } + + this._map.on('zoomanim', this._unspiderfyZoomAnim, this); + }, + + _unspiderfyZoomAnim: function (zoomDetails) { + //Wait until the first zoomanim after the user has finished touch-zooming before running the animation + if (L.DomUtil.hasClass(this._map._mapPane, 'leaflet-touching')) { + return; + } + + this._map.off('zoomanim', this._unspiderfyZoomAnim, this); + this._unspiderfy(zoomDetails); + }, + + _unspiderfyWrapper: function () { + /// _unspiderfy but passes no arguments + this._unspiderfy(); + }, + + _unspiderfy: function (zoomDetails) { + if (this._spiderfied) { + this._spiderfied.unspiderfy(zoomDetails); + } + }, + + _noanimationUnspiderfy: function () { + if (this._spiderfied) { + this._spiderfied._noanimationUnspiderfy(); + } + }, + + //If the given layer is currently being spiderfied then we unspiderfy it so it isn't on the map anymore etc + _unspiderfyLayer: function (layer) { + if (layer._spiderLeg) { + this._featureGroup.removeLayer(layer); + + if (layer.clusterShow) { + layer.clusterShow(); + } + //Position will be fixed up immediately in _animationUnspiderfy + if (layer.setZIndexOffset) { + layer.setZIndexOffset(0); + } + + this._map.removeLayer(layer._spiderLeg); + delete layer._spiderLeg; + } + } +}); + + +/** + * Adds 1 public method to MCG and 1 to L.Marker to facilitate changing + * markers' icon options and refreshing their icon and their parent clusters + * accordingly (case where their iconCreateFunction uses data of childMarkers + * to make up the cluster icon). + */ + + +L.MarkerClusterGroup.include({ + /** + * Updates the icon of all clusters which are parents of the given marker(s). + * In singleMarkerMode, also updates the given marker(s) icon. + * @param layers L.MarkerClusterGroup|L.LayerGroup|Array(L.Marker)|Map(L.Marker)| + * L.MarkerCluster|L.Marker (optional) list of markers (or single marker) whose parent + * clusters need to be updated. If not provided, retrieves all child markers of this. + * @returns {L.MarkerClusterGroup} + */ + refreshClusters: function (layers) { + if (!layers) { + layers = this._topClusterLevel.getAllChildMarkers(); + } else if (layers instanceof L.MarkerClusterGroup) { + layers = layers._topClusterLevel.getAllChildMarkers(); + } else if (layers instanceof L.LayerGroup) { + layers = layers._layers; + } else if (layers instanceof L.MarkerCluster) { + layers = layers.getAllChildMarkers(); + } else if (layers instanceof L.Marker) { + layers = [layers]; + } // else: must be an Array(L.Marker)|Map(L.Marker) + this._flagParentsIconsNeedUpdate(layers); + this._refreshClustersIcons(); + + // In case of singleMarkerMode, also re-draw the markers. + if (this.options.singleMarkerMode) { + this._refreshSingleMarkerModeMarkers(layers); + } + + return this; + }, + + /** + * Simply flags all parent clusters of the given markers as having a "dirty" icon. + * @param layers Array(L.Marker)|Map(L.Marker) list of markers. + * @private + */ + _flagParentsIconsNeedUpdate: function (layers) { + var id, parent; + + // Assumes layers is an Array or an Object whose prototype is non-enumerable. + for (id in layers) { + // Flag parent clusters' icon as "dirty", all the way up. + // Dumb process that flags multiple times upper parents, but still + // much more efficient than trying to be smart and make short lists, + // at least in the case of a hierarchy following a power law: + // http://jsperf.com/flag-nodes-in-power-hierarchy/2 + parent = layers[id].__parent; + while (parent) { + parent._iconNeedsUpdate = true; + parent = parent.__parent; + } + } + }, + + /** + * Refreshes the icon of all "dirty" visible clusters. + * Non-visible "dirty" clusters will be updated when they are added to the map. + * @private + */ + _refreshClustersIcons: function () { + this._featureGroup.eachLayer(function (c) { + if (c instanceof L.MarkerCluster && c._iconNeedsUpdate) { + c._updateIcon(); + } + }); + }, + + /** + * Re-draws the icon of the supplied markers. + * To be used in singleMarkerMode only. + * @param layers Array(L.Marker)|Map(L.Marker) list of markers. + * @private + */ + _refreshSingleMarkerModeMarkers: function (layers) { + var id, layer; + + for (id in layers) { + layer = layers[id]; + + // Make sure we do not override markers that do not belong to THIS group. + if (this.hasLayer(layer)) { + // Need to re-create the icon first, then re-draw the marker. + layer.setIcon(this._overrideMarkerIcon(layer)); + } + } + } +}); + +L.Marker.include({ + /** + * Updates the given options in the marker's icon and refreshes the marker. + * @param options map object of icon options. + * @param directlyRefreshClusters boolean (optional) true to trigger + * MCG.refreshClustersOf() right away with this single marker. + * @returns {L.Marker} + */ + refreshIconOptions: function (options, directlyRefreshClusters) { + var icon = this.options.icon; + + L.setOptions(icon, options); + + this.setIcon(icon); + + // Shortcut to refresh the associated MCG clusters right away. + // To be used when refreshing a single marker. + // Otherwise, better use MCG.refreshClusters() once at the end with + // the list of modified markers. + if (directlyRefreshClusters && this.__parent) { + this.__parent._group.refreshClusters(this); + } + + return this; + } +}); + + +}(window, document)); \ No newline at end of file diff --git a/includes/services/Leaflet/leaflet.markercluster/leaflet.markercluster.js b/includes/services/Leaflet/leaflet.markercluster/leaflet.markercluster.js new file mode 100644 index 000000000..020abe678 --- /dev/null +++ b/includes/services/Leaflet/leaflet.markercluster/leaflet.markercluster.js @@ -0,0 +1,6 @@ +/* + Leaflet.markercluster, Provides Beautiful Animated Marker Clustering functionality for Leaflet, a JS library for interactive maps. + https://github.com/Leaflet/Leaflet.markercluster + (c) 2012-2013, Dave Leaver, smartrak +*/ +!function(e,t,i){L.MarkerClusterGroup=L.FeatureGroup.extend({options:{maxClusterRadius:80,iconCreateFunction:null,spiderfyOnMaxZoom:!0,showCoverageOnHover:!0,zoomToBoundsOnClick:!0,singleMarkerMode:!1,disableClusteringAtZoom:null,removeOutsideVisibleBounds:!0,animate:!0,animateAddingMarkers:!1,spiderfyDistanceMultiplier:1,spiderLegPolylineOptions:{weight:1.5,color:"#222",opacity:.5},chunkedLoading:!1,chunkInterval:200,chunkDelay:50,chunkProgress:null,polygonOptions:{}},initialize:function(e){L.Util.setOptions(this,e),this.options.iconCreateFunction||(this.options.iconCreateFunction=this._defaultIconCreateFunction),this._featureGroup=L.featureGroup(),this._featureGroup.addEventParent(this),this._nonPointGroup=L.featureGroup(),this._nonPointGroup.addEventParent(this),this._inZoomAnimation=0,this._needsClustering=[],this._needsRemoving=[],this._currentShownBounds=null,this._queue=[];var t=L.DomUtil.TRANSITION&&this.options.animate;L.extend(this,t?this._withAnimation:this._noAnimation),this._markerCluster=t?L.MarkerCluster:L.MarkerClusterNonAnimated},addLayer:function(e){if(e instanceof L.LayerGroup)return this.addLayers([e]);if(!e.getLatLng)return this._nonPointGroup.addLayer(e),this;if(!this._map)return this._needsClustering.push(e),this;if(this.hasLayer(e))return this;this._unspiderfy&&this._unspiderfy(),this._addLayer(e,this._maxZoom),this._topClusterLevel._recalculateBounds();var t=e,i=this._map.getZoom();if(e.__parent)for(;t.__parent._zoom>=i;)t=t.__parent;return this._currentShownBounds.contains(t.getLatLng())&&(this.options.animateAddingMarkers?this._animationAddLayer(e,t):this._animationAddLayerNonAnimated(e,t)),this},removeLayer:function(e){return e instanceof L.LayerGroup?this.removeLayers([e]):e.getLatLng?this._map?e.__parent?(this._unspiderfy&&(this._unspiderfy(),this._unspiderfyLayer(e)),this._removeLayer(e,!0),this._topClusterLevel._recalculateBounds(),e.off("move",this._childMarkerMoved,this),this._featureGroup.hasLayer(e)&&(this._featureGroup.removeLayer(e),e.clusterShow&&e.clusterShow()),this):this:(!this._arraySplice(this._needsClustering,e)&&this.hasLayer(e)&&this._needsRemoving.push(e),this):(this._nonPointGroup.removeLayer(e),this)},addLayers:function(e){if(!L.Util.isArray(e))return this.addLayer(e);var t,i=this._featureGroup,n=this._nonPointGroup,s=this.options.chunkedLoading,r=this.options.chunkInterval,o=this.options.chunkProgress,a=e.length,h=0,u=!0;if(this._map){var l=(new Date).getTime(),_=L.bind(function(){for(var d=(new Date).getTime();a>h;h++){if(s&&0===h%200){var c=(new Date).getTime()-d;if(c>r)break}if(t=e[h],t instanceof L.LayerGroup)u&&(e=e.slice(),u=!1),this._extractNonGroupLayers(t,e),a=e.length;else if(t.getLatLng){if(!this.hasLayer(t)&&(this._addLayer(t,this._maxZoom),t.__parent&&2===t.__parent.getChildCount())){var p=t.__parent.getAllChildMarkers(),f=p[0]===t?p[1]:p[0];i.removeLayer(f)}}else n.addLayer(t)}o&&o(h,a,(new Date).getTime()-l),h===a?(this._topClusterLevel._recalculateBounds(),this._featureGroup.eachLayer(function(e){e instanceof L.MarkerCluster&&e._iconNeedsUpdate&&e._updateIcon()}),this._topClusterLevel._recursivelyAddChildrenToMap(null,this._zoom,this._currentShownBounds)):setTimeout(_,this.options.chunkDelay)},this);_()}else for(var d=this._needsClustering;a>h;h++)t=e[h],t instanceof L.LayerGroup?(u&&(e=e.slice(),u=!1),this._extractNonGroupLayers(t,e),a=e.length):t.getLatLng?this.hasLayer(t)||d.push(t):n.addLayer(t);return this},removeLayers:function(e){var t,i,n=e.length,s=this._featureGroup,r=this._nonPointGroup,o=!0;if(!this._map){for(t=0;n>t;t++)i=e[t],i instanceof L.LayerGroup?(o&&(e=e.slice(),o=!1),this._extractNonGroupLayers(i,e),n=e.length):(this._arraySplice(this._needsClustering,i),r.removeLayer(i),this.hasLayer(i)&&this._needsRemoving.push(i));return this}if(this._unspiderfy){this._unspiderfy();var a=e.slice(),h=n;for(t=0;h>t;t++)i=a[t],i instanceof L.LayerGroup?(this._extractNonGroupLayers(i,a),h=a.length):this._unspiderfyLayer(i)}for(t=0;n>t;t++)i=e[t],i instanceof L.LayerGroup?(o&&(e=e.slice(),o=!1),this._extractNonGroupLayers(i,e),n=e.length):i.__parent?(this._removeLayer(i,!0,!0),s.hasLayer(i)&&(s.removeLayer(i),i.clusterShow&&i.clusterShow())):r.removeLayer(i);return this._topClusterLevel._recalculateBounds(),this._topClusterLevel._recursivelyAddChildrenToMap(null,this._zoom,this._currentShownBounds),s.eachLayer(function(e){e instanceof L.MarkerCluster&&e._updateIcon()}),this},clearLayers:function(){return this._map||(this._needsClustering=[],delete this._gridClusters,delete this._gridUnclustered),this._noanimationUnspiderfy&&this._noanimationUnspiderfy(),this._featureGroup.clearLayers(),this._nonPointGroup.clearLayers(),this.eachLayer(function(e){e.off("move",this._childMarkerMoved,this),delete e.__parent}),this._map&&this._generateInitialClusters(),this},getBounds:function(){var e=new L.LatLngBounds;this._topClusterLevel&&e.extend(this._topClusterLevel._bounds);for(var t=this._needsClustering.length-1;t>=0;t--)e.extend(this._needsClustering[t].getLatLng());return e.extend(this._nonPointGroup.getBounds()),e},eachLayer:function(e,t){var i,n=this._needsClustering.slice(),s=this._needsRemoving;for(this._topClusterLevel&&this._topClusterLevel.getAllChildMarkers(n),i=n.length-1;i>=0;i--)-1===s.indexOf(n[i])&&e.call(t,n[i]);this._nonPointGroup.eachLayer(e,t)},getLayers:function(){var e=[];return this.eachLayer(function(t){e.push(t)}),e},getLayer:function(e){var t=null;return e=parseInt(e,10),this.eachLayer(function(i){L.stamp(i)===e&&(t=i)}),t},hasLayer:function(e){if(!e)return!1;var t,i=this._needsClustering;for(t=i.length-1;t>=0;t--)if(i[t]===e)return!0;for(i=this._needsRemoving,t=i.length-1;t>=0;t--)if(i[t]===e)return!1;return!(!e.__parent||e.__parent._group!==this)||this._nonPointGroup.hasLayer(e)},zoomToShowLayer:function(e,t){"function"!=typeof t&&(t=function(){});var i=function(){!e._icon&&!e.__parent._icon||this._inZoomAnimation||(this._map.off("moveend",i,this),this.off("animationend",i,this),e._icon?t():e.__parent._icon&&(this.once("spiderfied",t,this),e.__parent.spiderfy()))};if(e._icon&&this._map.getBounds().contains(e.getLatLng()))t();else if(e.__parent._zoomt;t++)n=this._needsRemoving[t],this._removeLayer(n,!0);this._needsRemoving=[],this._zoom=this._map.getZoom(),this._currentShownBounds=this._getExpandedVisibleBounds(),this._map.on("zoomend",this._zoomEnd,this),this._map.on("moveend",this._moveEnd,this),this._spiderfierOnAdd&&this._spiderfierOnAdd(),this._bindEvents(),i=this._needsClustering,this._needsClustering=[],this.addLayers(i)},onRemove:function(e){e.off("zoomend",this._zoomEnd,this),e.off("moveend",this._moveEnd,this),this._unbindEvents(),this._map._mapPane.className=this._map._mapPane.className.replace(" leaflet-cluster-anim",""),this._spiderfierOnRemove&&this._spiderfierOnRemove(),delete this._maxLat,this._hideCoverage(),this._featureGroup.remove(),this._nonPointGroup.remove(),this._featureGroup.clearLayers(),this._map=null},getVisibleParent:function(e){for(var t=e;t&&!t._icon;)t=t.__parent;return t||null},_arraySplice:function(e,t){for(var i=e.length-1;i>=0;i--)if(e[i]===t)return e.splice(i,1),!0},_removeFromGridUnclustered:function(e,t){for(var i=this._map,n=this._gridUnclustered;t>=0&&n[t].removeObject(e,i.project(e.getLatLng(),t));t--);},_childMarkerMoved:function(e){this._ignoreMove||(e.target._latlng=e.oldLatLng,this.removeLayer(e.target),e.target._latlng=e.latlng,this.addLayer(e.target))},_removeLayer:function(e,t,i){var n=this._gridClusters,s=this._gridUnclustered,r=this._featureGroup,o=this._map;t&&this._removeFromGridUnclustered(e,this._maxZoom);var a,h=e.__parent,u=h._markers;for(this._arraySplice(u,e);h&&(h._childCount--,h._boundsNeedUpdate=!0,!(h._zoom<0));)t&&h._childCount<=1?(a=h._markers[0]===e?h._markers[1]:h._markers[0],n[h._zoom].removeObject(h,o.project(h._cLatLng,h._zoom)),s[h._zoom].addObject(a,o.project(a.getLatLng(),h._zoom)),this._arraySplice(h.__parent._childClusters,h),h.__parent._markers.push(a),a.__parent=h.__parent,h._icon&&(r.removeLayer(h),i||r.addLayer(a))):i&&h._icon||h._updateIcon(),h=h.__parent;delete e.__parent},_isOrIsParent:function(e,t){for(;t;){if(e===t)return!0;t=t.parentNode}return!1},fire:function(e,t,i){if(t&&t.layer instanceof L.MarkerCluster){if(t.originalEvent&&this._isOrIsParent(t.layer._icon,t.originalEvent.relatedTarget))return;e="cluster"+e}L.FeatureGroup.prototype.fire.call(this,e,t,i)},listens:function(e,t){return L.FeatureGroup.prototype.listens.call(this,e,t)||L.FeatureGroup.prototype.listens.call(this,"cluster"+e,t)},_defaultIconCreateFunction:function(e){var t=e.getChildCount(),i=" marker-cluster-";return i+=10>t?"small":100>t?"medium":"large",new L.DivIcon({html:"
"+t+"
",className:"marker-cluster"+i,iconSize:new L.Point(40,40)})},_bindEvents:function(){var e=this._map,t=this.options.spiderfyOnMaxZoom,i=this.options.showCoverageOnHover,n=this.options.zoomToBoundsOnClick;(t||n)&&this.on("clusterclick",this._zoomOrSpiderfy,this),i&&(this.on("clustermouseover",this._showCoverage,this),this.on("clustermouseout",this._hideCoverage,this),e.on("zoomend",this._hideCoverage,this))},_zoomOrSpiderfy:function(e){for(var t=e.layer,i=t;1===i._childClusters.length;)i=i._childClusters[0];i._zoom===this._maxZoom&&i._childCount===t._childCount&&this.options.spiderfyOnMaxZoom?t.spiderfy():this.options.zoomToBoundsOnClick&&t.zoomToBounds(),e.originalEvent&&13===e.originalEvent.keyCode&&this._map._container.focus()},_showCoverage:function(e){var t=this._map;this._inZoomAnimation||(this._shownPolygon&&t.removeLayer(this._shownPolygon),e.layer.getChildCount()>2&&e.layer!==this._spiderfied&&(this._shownPolygon=new L.Polygon(e.layer.getConvexHull(),this.options.polygonOptions),t.addLayer(this._shownPolygon)))},_hideCoverage:function(){this._shownPolygon&&(this._map.removeLayer(this._shownPolygon),this._shownPolygon=null)},_unbindEvents:function(){var e=this.options.spiderfyOnMaxZoom,t=this.options.showCoverageOnHover,i=this.options.zoomToBoundsOnClick,n=this._map;(e||i)&&this.off("clusterclick",this._zoomOrSpiderfy,this),t&&(this.off("clustermouseover",this._showCoverage,this),this.off("clustermouseout",this._hideCoverage,this),n.off("zoomend",this._hideCoverage,this))},_zoomEnd:function(){this._map&&(this._mergeSplitClusters(),this._zoom=Math.round(this._map._zoom),this._currentShownBounds=this._getExpandedVisibleBounds())},_moveEnd:function(){if(!this._inZoomAnimation){var e=this._getExpandedVisibleBounds();this._topClusterLevel._recursivelyRemoveChildrenFromMap(this._currentShownBounds,this._zoom,e),this._topClusterLevel._recursivelyAddChildrenToMap(null,Math.round(this._map._zoom),e),this._currentShownBounds=e}},_generateInitialClusters:function(){var e=this._map.getMaxZoom(),t=this.options.maxClusterRadius,i=t;"function"!=typeof t&&(i=function(){return t}),this.options.disableClusteringAtZoom&&(e=this.options.disableClusteringAtZoom-1),this._maxZoom=e,this._gridClusters={},this._gridUnclustered={};for(var n=e;n>=0;n--)this._gridClusters[n]=new L.DistanceGrid(i(n)),this._gridUnclustered[n]=new L.DistanceGrid(i(n));this._topClusterLevel=new this._markerCluster(this,-1)},_addLayer:function(e,t){var i,n,s=this._gridClusters,r=this._gridUnclustered;for(this.options.singleMarkerMode&&this._overrideMarkerIcon(e),e.on("move",this._childMarkerMoved,this);t>=0;t--){i=this._map.project(e.getLatLng(),t);var o=s[t].getNearObject(i);if(o)return o._addChild(e),e.__parent=o,void 0;if(o=r[t].getNearObject(i)){var a=o.__parent;a&&this._removeLayer(o,!1);var h=new this._markerCluster(this,t,o,e);s[t].addObject(h,this._map.project(h._cLatLng,t)),o.__parent=h,e.__parent=h;var u=h;for(n=t-1;n>a._zoom;n--)u=new this._markerCluster(this,n,u),s[n].addObject(u,this._map.project(o.getLatLng(),n));return a._addChild(u),this._removeFromGridUnclustered(o,t),void 0}r[t].addObject(e,i)}this._topClusterLevel._addChild(e),e.__parent=this._topClusterLevel},_enqueue:function(e){this._queue.push(e),this._queueTimeout||(this._queueTimeout=setTimeout(L.bind(this._processQueue,this),300))},_processQueue:function(){for(var e=0;ee?(this._animationStart(),this._animationZoomOut(this._zoom,e)):this._moveEnd()},_getExpandedVisibleBounds:function(){return this.options.removeOutsideVisibleBounds?L.Browser.mobile?this._checkBoundsMaxLat(this._map.getBounds()):this._checkBoundsMaxLat(this._map.getBounds().pad(1)):this._mapBoundsInfinite},_checkBoundsMaxLat:function(e){var t=this._maxLat;return t!==i&&(e.getNorth()>=t&&(e._northEast.lat=1/0),e.getSouth()<=-t&&(e._southWest.lat=-1/0)),e},_animationAddLayerNonAnimated:function(e,t){if(t===e)this._featureGroup.addLayer(e);else if(2===t._childCount){t._addToMap();var i=t.getAllChildMarkers();this._featureGroup.removeLayer(i[0]),this._featureGroup.removeLayer(i[1])}else t._updateIcon()},_extractNonGroupLayers:function(e,t){var i,n=e.getLayers(),s=0;for(t=t||[];s=0;i--)o=h[i],n.contains(o._latlng)||s.removeLayer(o)}),this._forceLayout(),this._topClusterLevel._recursivelyBecomeVisible(n,t),s.eachLayer(function(e){e instanceof L.MarkerCluster||!e._icon||e.clusterShow()}),this._topClusterLevel._recursively(n,e,t,function(e){e._recursivelyRestoreChildPositions(t)}),this._ignoreMove=!1,this._enqueue(function(){this._topClusterLevel._recursively(n,e,0,function(e){s.removeLayer(e),e.clusterShow()}),this._animationEnd()})},_animationZoomOut:function(e,t){this._animationZoomOutSingle(this._topClusterLevel,e-1,t),this._topClusterLevel._recursivelyAddChildrenToMap(null,t,this._getExpandedVisibleBounds()),this._topClusterLevel._recursivelyRemoveChildrenFromMap(this._currentShownBounds,e,this._getExpandedVisibleBounds())},_animationAddLayer:function(e,t){var i=this,n=this._featureGroup;n.addLayer(e),t!==e&&(t._childCount>2?(t._updateIcon(),this._forceLayout(),this._animationStart(),e._setPos(this._map.latLngToLayerPoint(t.getLatLng())),e.clusterHide(),this._enqueue(function(){n.removeLayer(e),e.clusterShow(),i._animationEnd()})):(this._forceLayout(),i._animationStart(),i._animationZoomOutSingle(t,this._map.getMaxZoom(),this._map.getZoom())))}},_animationZoomOutSingle:function(e,t,i){var n=this._getExpandedVisibleBounds();e._recursivelyAnimateChildrenInAndAddSelfToMap(n,t+1,i);var s=this;this._forceLayout(),e._recursivelyBecomeVisible(n,i),this._enqueue(function(){if(1===e._childCount){var r=e._markers[0];this._ignoreMove=!0,r.setLatLng(r.getLatLng()),this._ignoreMove=!1,r.clusterShow&&r.clusterShow()}else e._recursively(n,i,0,function(e){e._recursivelyRemoveChildrenFromMap(n,t+1)});s._animationEnd()})},_animationEnd:function(){this._map&&(this._map._mapPane.className=this._map._mapPane.className.replace(" leaflet-cluster-anim","")),this._inZoomAnimation--,this.fire("animationend")},_forceLayout:function(){L.Util.falseFn(t.body.offsetWidth)}}),L.markerClusterGroup=function(e){return new L.MarkerClusterGroup(e)},L.MarkerCluster=L.Marker.extend({initialize:function(e,t,i,n){L.Marker.prototype.initialize.call(this,i?i._cLatLng||i.getLatLng():new L.LatLng(0,0),{icon:this}),this._group=e,this._zoom=t,this._markers=[],this._childClusters=[],this._childCount=0,this._iconNeedsUpdate=!0,this._boundsNeedUpdate=!0,this._bounds=new L.LatLngBounds,i&&this._addChild(i),n&&this._addChild(n)},getAllChildMarkers:function(e){e=e||[];for(var t=this._childClusters.length-1;t>=0;t--)this._childClusters[t].getAllChildMarkers(e);for(var i=this._markers.length-1;i>=0;i--)e.push(this._markers[i]);return e},getChildCount:function(){return this._childCount},zoomToBounds:function(){for(var e,t=this._childClusters.slice(),i=this._group._map,n=i.getBoundsZoom(this._bounds),s=this._zoom+1,r=i.getZoom();t.length>0&&n>s;){s++;var o=[];for(e=0;es?this._group._map.setView(this._latlng,s):r>=n?this._group._map.setView(this._latlng,r+1):this._group._map.fitBounds(this._bounds)},getBounds:function(){var e=new L.LatLngBounds;return e.extend(this._bounds),e},_updateIcon:function(){this._iconNeedsUpdate=!0,this._icon&&this.setIcon(this)},createIcon:function(){return this._iconNeedsUpdate&&(this._iconObj=this._group.options.iconCreateFunction(this),this._iconNeedsUpdate=!1),this._iconObj.createIcon()},createShadow:function(){return this._iconObj.createShadow()},_addChild:function(e,t){this._iconNeedsUpdate=!0,this._boundsNeedUpdate=!0,this._setClusterCenter(e),e instanceof L.MarkerCluster?(t||(this._childClusters.push(e),e.__parent=this),this._childCount+=e._childCount):(t||this._markers.push(e),this._childCount++),this.__parent&&this.__parent._addChild(e,!0)},_setClusterCenter:function(e){this._cLatLng||(this._cLatLng=e._cLatLng||e._latlng)},_resetBounds:function(){var e=this._bounds;e._southWest&&(e._southWest.lat=1/0,e._southWest.lng=1/0),e._northEast&&(e._northEast.lat=-1/0,e._northEast.lng=-1/0)},_recalculateBounds:function(){var e,t,i,n,s=this._markers,r=this._childClusters,o=0,a=0,h=this._childCount;if(0!==h){for(this._resetBounds(),e=0;e=0;i--)n=s[i],n._icon&&(n._setPos(t),n.clusterHide())},function(e){var i,n,s=e._childClusters;for(i=s.length-1;i>=0;i--)n=s[i],n._icon&&(n._setPos(t),n.clusterHide())})},_recursivelyAnimateChildrenInAndAddSelfToMap:function(e,t,i){this._recursively(e,i,0,function(n){n._recursivelyAnimateChildrenIn(e,n._group._map.latLngToLayerPoint(n.getLatLng()).round(),t),n._isSingleParent()&&t-1===i?(n.clusterShow(),n._recursivelyRemoveChildrenFromMap(e,t)):n.clusterHide(),n._addToMap()})},_recursivelyBecomeVisible:function(e,t){this._recursively(e,0,t,null,function(e){e.clusterShow()})},_recursivelyAddChildrenToMap:function(e,t,i){this._recursively(i,-1,t,function(n){if(t!==n._zoom)for(var s=n._markers.length-1;s>=0;s--){var r=n._markers[s];i.contains(r._latlng)&&(e&&(r._backupLatlng=r.getLatLng(),r.setLatLng(e),r.clusterHide&&r.clusterHide()),n._group._featureGroup.addLayer(r))}},function(t){t._addToMap(e)})},_recursivelyRestoreChildPositions:function(e){for(var t=this._markers.length-1;t>=0;t--){var i=this._markers[t];i._backupLatlng&&(i.setLatLng(i._backupLatlng),delete i._backupLatlng)}if(e-1===this._zoom)for(var n=this._childClusters.length-1;n>=0;n--)this._childClusters[n]._restorePosition();else for(var s=this._childClusters.length-1;s>=0;s--)this._childClusters[s]._recursivelyRestoreChildPositions(e)},_restorePosition:function(){this._backupLatlng&&(this.setLatLng(this._backupLatlng),delete this._backupLatlng)},_recursivelyRemoveChildrenFromMap:function(e,t,i){var n,s;this._recursively(e,-1,t-1,function(e){for(s=e._markers.length-1;s>=0;s--)n=e._markers[s],i&&i.contains(n._latlng)||(e._group._featureGroup.removeLayer(n),n.clusterShow&&n.clusterShow())},function(e){for(s=e._childClusters.length-1;s>=0;s--)n=e._childClusters[s],i&&i.contains(n._latlng)||(e._group._featureGroup.removeLayer(n),n.clusterShow&&n.clusterShow())})},_recursively:function(e,t,i,n,s){var r,o,a=this._childClusters,h=this._zoom;if(t>h)for(r=a.length-1;r>=0;r--)o=a[r],e.intersects(o._bounds)&&o._recursively(e,t,i,n,s);else if(n&&n(this),s&&this._zoom===i&&s(this),i>h)for(r=a.length-1;r>=0;r--)o=a[r],e.intersects(o._bounds)&&o._recursively(e,t,i,n,s)},_isSingleParent:function(){return this._childClusters.length>0&&this._childClusters[0]._childCount===this._childCount}}),L.Marker.include({clusterHide:function(){return this.options.opacityWhenUnclustered=this.options.opacity||1,this.setOpacity(0)},clusterShow:function(){var e=this.setOpacity(this.options.opacity||this.options.opacityWhenUnclustered);return delete this.options.opacityWhenUnclustered,e}}),L.DistanceGrid=function(e){this._cellSize=e,this._sqCellSize=e*e,this._grid={},this._objectPoint={}},L.DistanceGrid.prototype={addObject:function(e,t){var i=this._getCoord(t.x),n=this._getCoord(t.y),s=this._grid,r=s[n]=s[n]||{},o=r[i]=r[i]||[],a=L.Util.stamp(e);this._objectPoint[a]=t,o.push(e)},updateObject:function(e,t){this.removeObject(e),this.addObject(e,t)},removeObject:function(e,t){var i,n,s=this._getCoord(t.x),r=this._getCoord(t.y),o=this._grid,a=o[r]=o[r]||{},h=a[s]=a[s]||[];for(delete this._objectPoint[L.Util.stamp(e)],i=0,n=h.length;n>i;i++)if(h[i]===e)return h.splice(i,1),1===n&&delete a[s],!0},eachObject:function(e,t){var i,n,s,r,o,a,h,u=this._grid;for(i in u){o=u[i];for(n in o)for(a=o[n],s=0,r=a.length;r>s;s++)h=e.call(t,a[s]),h&&(s--,r--)}},getNearObject:function(e){var t,i,n,s,r,o,a,h,u=this._getCoord(e.x),l=this._getCoord(e.y),_=this._objectPoint,d=this._sqCellSize,c=null;for(t=l-1;l+1>=t;t++)if(s=this._grid[t])for(i=u-1;u+1>=i;i++)if(r=s[i])for(n=0,o=r.length;o>n;n++)a=r[n],h=this._sqDist(_[L.Util.stamp(a)],e),d>h&&(d=h,c=a);return c},_getCoord:function(e){return Math.floor(e/this._cellSize)},_sqDist:function(e,t){var i=t.x-e.x,n=t.y-e.y;return i*i+n*n}},function(){L.QuickHull={getDistant:function(e,t){var i=t[1].lat-t[0].lat,n=t[0].lng-t[1].lng;return n*(e.lat-t[0].lat)+i*(e.lng-t[0].lng)},findMostDistantPointFromBaseLine:function(e,t){var i,n,s,r=0,o=null,a=[];for(i=t.length-1;i>=0;i--)n=t[i],s=this.getDistant(n,e),s>0&&(a.push(n),s>r&&(r=s,o=n));return{maxPoint:o,newPoints:a}},buildConvexHull:function(e,t){var i=[],n=this.findMostDistantPointFromBaseLine(e,t);return n.maxPoint?(i=i.concat(this.buildConvexHull([e[0],n.maxPoint],n.newPoints)),i=i.concat(this.buildConvexHull([n.maxPoint,e[1]],n.newPoints))):[e[0]]},getConvexHull:function(e){var t,i=!1,n=!1,s=!1,r=!1,o=null,a=null,h=null,u=null,l=null,_=null;for(t=e.length-1;t>=0;t--){var d=e[t];(i===!1||d.lat>i)&&(o=d,i=d.lat),(n===!1||d.lats)&&(h=d,s=d.lng),(r===!1||d.lng=0;t--)e=i[t].getLatLng(),n.push(e);return L.QuickHull.getConvexHull(n)}}),L.MarkerCluster.include({_2PI:2*Math.PI,_circleFootSeparation:25,_circleStartAngle:Math.PI/6,_spiralFootSeparation:28,_spiralLengthStart:11,_spiralLengthFactor:5,_circleSpiralSwitchover:9,spiderfy:function(){if(this._group._spiderfied!==this&&!this._group._inZoomAnimation){var e,t=this.getAllChildMarkers(),i=this._group,n=i._map,s=n.latLngToLayerPoint(this._latlng);this._group._unspiderfy(),this._group._spiderfied=this,t.length>=this._circleSpiralSwitchover?e=this._generatePointsSpiral(t.length,s):(s.y+=10,e=this._generatePointsCircle(t.length,s)),this._animationSpiderfy(t,e)}},unspiderfy:function(e){this._group._inZoomAnimation||(this._animationUnspiderfy(e),this._group._spiderfied=null)},_generatePointsCircle:function(e,t){var i,n,s=this._group.options.spiderfyDistanceMultiplier*this._circleFootSeparation*(2+e),r=s/this._2PI,o=this._2PI/e,a=[];for(a.length=e,i=e-1;i>=0;i--)n=this._circleStartAngle+i*o,a[i]=new L.Point(t.x+r*Math.cos(n),t.y+r*Math.sin(n))._round();return a},_generatePointsSpiral:function(e,t){var i,n=this._group.options.spiderfyDistanceMultiplier,s=n*this._spiralLengthStart,r=n*this._spiralFootSeparation,o=n*this._spiralLengthFactor*this._2PI,a=0,h=[];for(h.length=e,i=e-1;i>=0;i--)a+=r/s+5e-4*i,h[i]=new L.Point(t.x+s*Math.cos(a),t.y+s*Math.sin(a))._round(),s+=o/a;return h},_noanimationUnspiderfy:function(){var e,t,i=this._group,n=i._map,s=i._featureGroup,r=this.getAllChildMarkers();for(i._ignoreMove=!0,this.setOpacity(1),t=r.length-1;t>=0;t--)e=r[t],s.removeLayer(e),e._preSpiderfyLatlng&&(e.setLatLng(e._preSpiderfyLatlng),delete e._preSpiderfyLatlng),e.setZIndexOffset&&e.setZIndexOffset(0),e._spiderLeg&&(n.removeLayer(e._spiderLeg),delete e._spiderLeg);i.fire("unspiderfied",{cluster:this,markers:r}),i._ignoreMove=!1,i._spiderfied=null}}),L.MarkerClusterNonAnimated=L.MarkerCluster.extend({_animationSpiderfy:function(e,t){var i,n,s,r,o=this._group,a=o._map,h=o._featureGroup,u=this._group.options.spiderLegPolylineOptions;for(o._ignoreMove=!0,i=0;i=0;n--)h=_.layerPointToLatLng(t[n]),s=e[n],s._preSpiderfyLatlng=s._latlng,s.setLatLng(h),s.clusterShow&&s.clusterShow(),f&&(r=s._spiderLeg,o=r._path,o.style.strokeDashoffset=0,r.setStyle({opacity:g}));this.setOpacity(.3),l._ignoreMove=!1,setTimeout(function(){l._animationEnd(),l.fire("spiderfied",{cluster:u,markers:e})},200)},_animationUnspiderfy:function(e){var t,i,n,s,r,o,a=this,h=this._group,u=h._map,l=h._featureGroup,_=e?u._latLngToNewLayerPoint(this._latlng,e.zoom,e.center):u.latLngToLayerPoint(this._latlng),d=this.getAllChildMarkers(),c=L.Path.SVG;for(h._ignoreMove=!0,h._animationStart(),this.setOpacity(1),i=d.length-1;i>=0;i--)t=d[i],t._preSpiderfyLatlng&&(t.setLatLng(t._preSpiderfyLatlng),delete t._preSpiderfyLatlng,o=!0,t._setPos&&(t._setPos(_),o=!1),t.clusterHide&&(t.clusterHide(),o=!1),o&&l.removeLayer(t),c&&(n=t._spiderLeg,s=n._path,r=s.getTotalLength()+.1,s.style.strokeDashoffset=r,n.setStyle({opacity:0})));h._ignoreMove=!1,setTimeout(function(){var e=0;for(i=d.length-1;i>=0;i--)t=d[i],t._spiderLeg&&e++;for(i=d.length-1;i>=0;i--)t=d[i],t._spiderLeg&&(t.clusterShow&&t.clusterShow(),t.setZIndexOffset&&t.setZIndexOffset(0),e>1&&l.removeLayer(t),u.removeLayer(t._spiderLeg),delete t._spiderLeg);h._animationEnd(),h.fire("unspiderfied",{cluster:a,markers:d})},200)}}),L.MarkerClusterGroup.include({_spiderfied:null,unspiderfy:function(){this._unspiderfy.apply(this,arguments)},_spiderfierOnAdd:function(){this._map.on("click",this._unspiderfyWrapper,this),this._map.options.zoomAnimation&&this._map.on("zoomstart",this._unspiderfyZoomStart,this),this._map.on("zoomend",this._noanimationUnspiderfy,this),L.Browser.touch||this._map.getRenderer(this)},_spiderfierOnRemove:function(){this._map.off("click",this._unspiderfyWrapper,this),this._map.off("zoomstart",this._unspiderfyZoomStart,this),this._map.off("zoomanim",this._unspiderfyZoomAnim,this),this._map.off("zoomend",this._noanimationUnspiderfy,this),this._noanimationUnspiderfy()},_unspiderfyZoomStart:function(){this._map&&this._map.on("zoomanim",this._unspiderfyZoomAnim,this)},_unspiderfyZoomAnim:function(e){L.DomUtil.hasClass(this._map._mapPane,"leaflet-touching")||(this._map.off("zoomanim",this._unspiderfyZoomAnim,this),this._unspiderfy(e))},_unspiderfyWrapper:function(){this._unspiderfy()},_unspiderfy:function(e){this._spiderfied&&this._spiderfied.unspiderfy(e)},_noanimationUnspiderfy:function(){this._spiderfied&&this._spiderfied._noanimationUnspiderfy()},_unspiderfyLayer:function(e){e._spiderLeg&&(this._featureGroup.removeLayer(e),e.clusterShow&&e.clusterShow(),e.setZIndexOffset&&e.setZIndexOffset(0),this._map.removeLayer(e._spiderLeg),delete e._spiderLeg)}}),L.MarkerClusterGroup.include({refreshClusters:function(e){return e?e instanceof L.MarkerClusterGroup?e=e._topClusterLevel.getAllChildMarkers():e instanceof L.LayerGroup?e=e._layers:e instanceof L.MarkerCluster?e=e.getAllChildMarkers():e instanceof L.Marker&&(e=[e]):e=this._topClusterLevel.getAllChildMarkers(),this._flagParentsIconsNeedUpdate(e),this._refreshClustersIcons(),this.options.singleMarkerMode&&this._refreshSingleMarkerModeMarkers(e),this},_flagParentsIconsNeedUpdate:function(e){var t,i;for(t in e)for(i=e[t].__parent;i;)i._iconNeedsUpdate=!0,i=i.__parent},_refreshClustersIcons:function(){this._featureGroup.eachLayer(function(e){e instanceof L.MarkerCluster&&e._iconNeedsUpdate&&e._updateIcon()})},_refreshSingleMarkerModeMarkers:function(e){var t,i;for(t in e)i=e[t],this.hasLayer(i)&&i.setIcon(this._overrideMarkerIcon(i))}}),L.Marker.include({refreshIconOptions:function(e,t){var i=this.options.icon;return L.setOptions(i,e),this.setIcon(i),t&&this.__parent&&this.__parent._group.refreshClusters(this),this}})}(window,document); \ No newline at end of file diff --git a/includes/services/Leaflet/leaflet/images/layers-2x.png b/includes/services/Leaflet/leaflet/images/layers-2x.png new file mode 100644 index 0000000000000000000000000000000000000000..200c333dca9652ac4cba004d609e5af4eee168c1 GIT binary patch literal 1259 zcmVFhCYNy;#0irRPomHqW|G1C*;4?@4#E?jH>?v@U%cy?3dQAc-DchXVErpOh~ z-jbon+tNbnl6hoEb;)TVk+%hTDDi_G%i3*RZ&15!$Fjr^f;Ke&A@|?=`2&+{zr+3a z{D*=t(`AXyS%X7N z%a#RZw6vD^t_rnM`L4E>m=U&R!A-&}nZIi$BOPvkhrCuUe@BN~-lRD)f44;J%TwgE zcze8u!PQ_NR7?o(NylLXVTfDO zxs5=@|GsYEsNo4M#nT%N!UE(?dnS)t2+{ELYAFp*3=iF=|EQnTp`#vlSXuGVraYo? z+RCzXo6h3qA8{KG?S4nE(lM+;Eb4nT3XV;7gcAxUi5m)`k5tv}cPy()8ZR3TLW3I- zAS^}cq-IJvL7a4RgR!yk@~RT%$lA7{L5ES*hyx)M4(yxI$Ub(4f)K|^v1>zvwQY!_ zIrWw8q9GS^!Dp~}+?mbnB6jDF8mVlbQ!jFKDY;w=7;XO{9bq7>LXGK24WA`;rL)_Z z)&j}pbV(;6gY;VMhbxgvn`X;6x}VUEE-7 z%)7j-%t8S=ZL3yc)HbXDAqJZvBTPoiW_A-+a8m3_Z?v{DN7Tnr#O_VUMT0UBt$;p` zDh6JbGHN8JJ*JN%y2%msb97@_S>9!%Egwk;?PEkU9ntz&3uR}%Fj5d$JHQbQb3}a{ zSzFT^#n=VInPpcAS}CNxj?_ zVscANk5Cfz(51EI1pz};AWWb|kgbYNb4wCEGUn3+eMUMV?1-{=I4TlmLJMot@rd07 zZuo2hk1ccu{YmGkcYdWAVdk{Z4Nm?^cTD&}jGm+Q1SYIXMwmG*oO*83&#>l%nbR`G zhh=lZ%xIb7kU3#;TBbfECrnC9P=-XpL|TG2BoZdj61*XiFbW8?1Z_wp%#;>${SUIy V$8qr;L*)Pf002ovPDHLkV1hYLS~36t literal 0 HcmV?d00001 diff --git a/includes/services/Leaflet/leaflet/images/layers.png b/includes/services/Leaflet/leaflet/images/layers.png index d25f9c70e63b6d35a0d27628097c351642c45723..1a72e5784b2b456eac5d7670738db80697af3377 100644 GIT binary patch delta 683 zcmV;c0#yCH2e<{08Gi%-0009+ghc=V0)I(FK~#7FjZ`yuBmorOJ=@)(JXa~ts!mr;gV^7YuG4I|wPQJf+@7H)FI1hZJKGONLdFbMO9sV8u#ru{V zV%PImC+@u>Ox)wIvg?-+sy^8pO#ugbqpMFY7|Pw%3NOew9e*$GwL>pXD1QHj7AP5C)IWy#bnXXB;g;j=$a-tW89K%Fb zDceHVq&unY*Wx3L#=EGWH=rjqnp|4c_Ulec!ql3#G-5ZFVlbBA@XP=)C8U&+Lrc)S z4O5%1$&{(;7R^K(CSnvSr$v;+B$8q&7Bf|h$#PARn}4JDh<}`!CNyI>reUuo&`VP| zF;<1c2r*inu#K*XJIa|jtROMT-iLV@g4Ki&nye?)Mrt^@IOK-7bCMuc?JF9oFO$x^ z%+{(#89{B^%1Fs12S=!T7ow8DWv2JGviYBxsK*41MV@R<@WdpL!kHl^su=^AuL;&e zjF+jnpMOX~F@{oWmnG0eQz$W3rEJ7o3o)?X@s`73P=+fTK3h*TnXGk$P@1eK)%Yz+ z0X!U@@jZbQZZQ3+t=85Wj3(5eb<6DVFu@Iw&iUoScF6GUsualRK=m{KHpqbO3;rYE z7^98jn|@kO^p8xzbi%PE{Ec7*czX2ZY70!BMkMfr6-)l_!5*lFD%dl(`X4qaq6gC5 R9gYA1002ovPDHLkV1g|=P@Mn( delta 945 zcmV;i15W(71-u848Gi-<005~izro zl8}%fxw*MOEaa%sGkNl4mZ^aQ2byAQ$-c*rAD09N2TNL7n&jljiIyxGNHRxrASu`$QyjX*)?A5E6>fXKkKa0+v zKYz$LO$jFp3kyR`BO)R~0s;cKDj+6DO@mo9&_Z+ExN(1cBNrE!aqjN!sm7zq&!0af zBqT&aLqq?DzrTI^CSSjP6@Pz!P7TE5sF}{7g(lkUP=6;*oS3|8*RJ$ar%qA8y5i^O z2lDCDCwcYi6`u%F0|0Vb4>LdwEi}<)$KO}CfB*ht>({R@b#rr*d-v|qC{Le0mA7x- zVq^L6;e#pE=%JSxX3;Splp@aVj4+qS|bOO}X}lM_So{P}YY1mVAV zdYNGs4S!cx*LF>tHdT!qH&%@rH8MBD>({Sk^XAR6Y11b0_V$)rw{FRuJ9p&f&6`*l zh{;i-hhAow)yz<@UOfd?&2Ztu1zEg!v25A0h3k+@mo8ylASOqR9(pTg27VX2ckh-t zbLQ|Vo}Qkd<>>j}%)k|K@>#QH0Wmq7W~f@Vs(%89&2aMMNwCWdl`B_P0QQ;zAyaIb zfhAy<8FuX0AwE7nm_V*xy$ZzSs8!qy@a4-FIdI^B%$qk)_UzdsUS3{6OpY2o^g3>a zCr_R*%>S~#;hL5%T`IF@&jw<0S`RbK+BCzFBS-3P-MaNX_wDQJ%Om3|4QFR(Al9Il z8GmNcK+D7oItLFP9Jgl8n$(>;cZ%_#SXW-Yd}#_bdgx__S%ksnA^_u|4;fcfa`^CJ z48RYQs{vwiLX94J>({TZYS*r93W8SIyLWHhb?er>UA}y|oIZV8u3x_n#6pf5J+*4p zQZ;JSun1yyVYp|U@ncL~v}ln4F}dp1t19!@wy=K@_gt-7HML>GhC@J1&R%{4l~U5B T=eu9400000NkvXXu0mjfCvMdj diff --git a/includes/services/Leaflet/leaflet/images/marker-icon-2x.png b/includes/services/Leaflet/leaflet/images/marker-icon-2x.png new file mode 100644 index 0000000000000000000000000000000000000000..e4abba3b511d14752426e8cbadae03c1e5fe15fb GIT binary patch literal 2586 zcmY+F2{e@Z8^=d(p&CX}#B^hnvdqF*GGuA230LC!lc?y*GDh~o80#=n_I())O7`vI zB1@JTX6#9*+_BV^ixA0s->Luqp7Vds@BKcX=lOk~^Pb;%&wG9p3o}FL;Zuhp5D3)R z2yY2yCGfH2=LJmOkQw^9n>daF6?Fz>oOD64$CM+_T`j0x%{zb|G zWolt{H|diO#S`|$6RM$ zYQuE4RW{2yZ`>fAt>jzyYyOB?)~HrQBlbbLT5yF%Xq8FEuzo80dd{%Q!{_)^mTE`^ z2$xe>TH$qiDJ+}(ajTp$Y*4vgGRrt^_?JwUO3+hm&{Mb<8aRtf7%F@*!JJv* zmcB*cag=-t4U&g79u1krRAKHHm?ZXHP8z-#KdAM9?vU7sxldD%A5;r0Rk~kblro}5 z9YhoJP18m~=v^kMBWPltYTV$TD;r4n^eZVWmDs^6;ZN_RG+a#^(N18a+%xd;JvScL zu54_hiMdFR4767cmcp!KOryQBQG{$|3e)h(z_sY-NRM>A$84n-CdxAt6V242bQmV| z86*uGCJtVTXCvLyz=eM@jE-Vz#IeA4DY~VhqL`R_>D;YIh9amQX~+l$Sfbohb*X)d zKiDG!?8t|64T_+_Jzbv6K)P|KG-6qDVGPYUwpPqb#c;-juz~ZW0bFF4JlB>cOB#?3 z9XJ~@0J1u{T_(66oVpmpLOkqOk6}qY=vN7820OS|_L-o5(4!i~Ivv=j{IKzS2m>C_ zhm9Npo09&0s*wy#K%InNpSW)yCZOhAFheUQtcXnn!x)WSjonNUm7@fguKPg0C3ESs~`Bd3Pyd$@XU8m z0JZWv0l=fZ{{jH?{!9Nt!mEGL|9_Oug?i>9H?4E!|Krk+(hy9WRiM;!>w8@J9&fq& z${#rK1z4j2$*KVGO=b{ivL6FFEPprv0No7|9RPB_H>dzW{;{(>P`XWmKn^Y#<8`e9 zc*;k@X>z(^khkvlh3UB1ICnF@RRHbZaQhkI;sl{txVGnBEzaFKZpw96Fm8qu^5@!a z+db!omc48o>}VvJr!j9Mpo^ZMPs2FKikZu-3edWhZ~5&Mp15G60gsVYic)|~eH4Q6 zF8d5^efqo~DD}CwRpRO|j91O-zygw(bv;<>V5MDzeC#nk zosJI@GCU;ylx)tp87H~!5Gl8^4UxdZ-ZLrRy7g=zwjIe|v>O(6W-QBuv-7h4HTLcz&ce9H!^9o^4XLD_t08@f%uD+tdxMAHzHi z6>y1>XBw|wNRu9u6j`13s*X9iz%Z1zep^?+<}$-U*uzd9$?LD0QWc+GSyhyvx<?!6YcvM{vC6CN2-dD>XyCsuOMe zdjA0H)tFMHvR%5Uqd_swkzDP0t5)bhy5xwusp(WsD}~`13N0NuN78MHcc03G_@3v- zZOvStb!W8+G+$o+mNh5)?USue0<9~5nql|l&C!mcb^cmUZGk2gF&p9IOMcs@2-WZX z+M_WESiwx34!IyuOY(`!=Sit;If5uuYqSJm`D>ogL1P7x5=v2W{zicaAxUs>WGzTn zQv?x3HR!VK$IB{-D-)cU&hLE;M2}umynSZBHRVLCW#WkaY>!>~#*V;;^Ck!H4Swwp zDHCGo7gMu}4-?)ga$s&da$6}|l&eSgpl~CnG5lbg z7&|&nHy^@(l0;d(4qw!>Pc+03BPqwvhV@DjJr)KAb74dUY>mzPErgW+cGhAfAE(Hx zg7S551PZuugrt1qVHk*xE*1`NeDO|ZnOO1ye(Ps{N=r+Q=S*|(%4dYb+TIr5*H@Ka z&IFce5q4snQ7O4sQm?Pxu??B#U>#Bu+HC!Ti{Sl150Y#4pk06Ac+lU@`2YRqk-uHH zZoIWi#kr-H+gi|P?w*2JMQ7U)c>*fCAPTksemc#0N4+Zgz+o*bN1@=(#&Q(RLz+r2 zQx|up>q>^w^^^t*`_3bp*JBDwCvP3iT>oMu+dLrW{Yd*GhC1Kx;_L$zF%*j;?iDxZ zrao$m-Bw;}qtlD8Ts>}{*(A|it9iEx_ZRY$yVv3y#q}J<;l}p;3_y0NqKJBW%sac- z#s<-=rSr4%CNFQcuf<8$A3ba|hx+!=-B0jwr*}bFG1p0OLTqz#DYd z16dVY=E5n{UkaA*7{FAF7c$=SE0gV@(AxW_6rfOFvBFyfQpO=ChwyqQo?nZOT`6__ zP3(sCcoy|xktOO{hUoSFKDM)^*yWXvlS$9yTyC~k^q#t~$$O;oU_E7XGiY~S^b+mS zVh=RZHn+0(T-ooM5xx%AW=ZUqv zgKQURIr-z7x5ejdVPYlT>F)dyou|#!MM#5qXK_BVQyz*bJ!*A&^rr((=SaeGlUNwV z01+e{DcnsPPIth+gTfMc34NrqGRM-T5f0=)<0vZ6?K`I0Z1Y3GdqxI|$iyh%qoeNX UQO-*oc+)|Q_08}VdXD6O0C*xx%>V!Z literal 0 HcmV?d00001 diff --git a/includes/services/Leaflet/leaflet/images/marker-icon.png b/includes/services/Leaflet/leaflet/images/marker-icon.png index 1a5cde674bd4f17ef6e63ba7b7895e56fefb0d48..950edf24677ded147df13b26f91baa2b0fa70513 100644 GIT binary patch delta 1448 zcmV;Z1y}mj3%U!CB!7WPL_t(|0j*a9kYiaC&Dw>v?cLaEn`_(NwjE5{4BBI}HMZhp zZQIIt?*8+u_Sa_G>HbxxUcLA3J?A_pyyVw1rUZp{=mMo~SfDTrMWPI;7`kYxxf6CO z4Fk8R3?o%)!|>NC{Sax&M|`1`8~#R#y4o=Ey;?ug2+?VaD1WjnCoA>C`6|QEq1eV9 z2k*7m>G65*&@&6)aC&e}GI)pf-Za|N`DT&Cn1J|o`19mumxW~hiKiKyc z-P`S@q)rdTo84@QI@;0yXrG%9uhI>AG5QHb6s4=<6xy{1-=PGvhhmKGDFgAy!_+ED*Zi$6T6e{0k&@K| z(Y2cnKHB#-#k9L9dNk0Ekxkgo=GSdK$nb6P6Ok7!dqQ2d>cAtt%=8#N6xrtT16)4T z`wr_AF(^?-`-!p0M~u2`wNGBW{3TXIs$F-syl}bi?tcy|-QD>XMYOv7fHrJ^__|FL zq+Lz&qUCHESF?d0>~hn6U1R5f54_;#N%NKlt4db*;4G}g<%dL*m#{`br7K9c+)5vP z_d6XvdW=5*=})R3aIx(mOE!B)<$@2q;OEKm7iEN14EmrR((L2_M-O&0F6XSJzU}YP zSl@u-Gk*%zt;FuaRQctD54_-KMzVNSblo;~E`yt#9I#^&n|IT_H3M|)_;DKVd%AT! z#Widnm5(}j!OxTCF8VI1%}Pxg0+_OOKF zj4n#hFC}Sa4=Ws1yAx}NIKMpbfKQaxbrk&UwIWRG-XB`Nk;0qYB(AjkLBb%k!OfDz zHQPYUmZ*m}y3s!>2RxzWn~sB*ySoG_rhkXTnf<=_HZPkbsu&xJY9|HNxloQ9hj#FY zGWvYN6w`g2$n6 z(A#v@>3|Wj67r-u3o(lFao>2T!$olAE|OI`=~k_E%D^^8p5RUM-;&ol&Kp+*5WZ>D zOR^XGux*iibJ^rT9OA`U^L&CgO$C$wB!UaL(Uk8XNx6e=Dz#1uj2iL$6w@EbpHu{Z z8@LLtb+IHj6z?Aw=tmsR8MmXRB7buG3tea7+$eQhNj%3%*B9*{7Z@$#m~-(9@}?{T z2vUrxaHpwpF^;WtZQ=e=Ks)+zx|z|xKJ6dexCL-(<=V|8G}!5yf_;bazbc;tTSq(kg(=481ODrPyNB6n-$+U}(w$m6U6H$w1Akv07bZ4;0a-B_W^milO4fj+56ZMb+n@&aWfKuOUCUd;-V3{z03{~wiFi*6WY-~ zQxWdHforM3&aMSYVXEcefA7Vti12uH665+^xW~B>6$~9mea8P8N8bX*M7S4e_Yq3e zXI%tf+&cwny~k0aKFcE9XdxmQ5XxB=8GU(9H|Jk9vpyOEFk##P0000b8XvW+jeZHu#P#lZO67Vo{63G zV zs9WZh(+;ZrNt#Ho>5$CBhWv{plIu}bCorz{B4%zD^-NvQA%CIaEP{%9L3`VXqmht9 zx}GMTkrl@=c`b_>8%0c9$0N3dK}@sK=+&IGE{m@_1M0g)ChCQ_XB-^qY4TN6 zj6!@fhly)N*_aka`+fWxL2oaS*TTw=gJu>$4oTZDnHN%W6cg5n2&rWhs79B7M_Ac0 zcw`*JxT;?IJb*xYqT(AaNb59M*%RBu$vrgp8u^#XEq`^N9f}z*XSFgiA`yj?N^N6hH^m;?fj)P|Bs$5g{*m-3h!sr%Cu4J@AYdOT$ zt1+Rf+kard@bIv#qK)oh=?OY(Wq(uy1y`K~&EKtZN!}gco!y3nW)VIWj8;q|-PH0^ z$lQI=Xu#m$Ao|4L;FHs#*B@5LA+q{BXnv2%C25zJbSCjlBD~8Ot*AN*DLaLf?dOdK z3=9mQjrkeAxt)6b;k7)%E6;-FAEc0-uehh~MSonQ2+vYR%fEs}bRCPRfog{{X)F?JDZpS$HJxgrPq7Vn(jHr|tvIwo^H+*d__{NI!^(S`p5LjDl}D zC)WdtPQpFyfYb!Hl)doH>qA%-Yp_SNpXPP~P^p}jZ`C*zSaKF&)dHLf7zLL?7U4Cb z9^Z0aZ?7|XFfbph^)oIgHi(%LVuqo(0Xhp!i42VacYE z9wL|h%UHM;a(F+Ru}VON2-nm@pcxaDA&WQa%5~4^hJTre_c9rac^3=tDL4=6|9^`7 zOA%tfXoIfj$SvOz9`?B$-pODr;#(@hCFL-vcdk5K8M5AxCbx_}_>_ovE1faRwSb3L z!9`Ht&*Z=QAq&?T@&Os7OF&1-;>|S1B=2JBEZRXm3l09i%WlD1WA?db9D`?}fY(#h zM%iU^@W@w#_FpEC8V;Gi#+YD-Wq&*2P9d)*tBsP@os!!@`_qm7H`R9DYEw?RWS)jg z9*tT~2;Dh;_LB9itWjrD8f81b=eIKms1AQLR)GQ16sNNdE@6X!=`i#-kEDy0x dD^Xb!@()HyXVJ)KJO}^)002ovPDHLkV1hIV;HUrq diff --git a/includes/services/Leaflet/leaflet/images/marker-shadow.png b/includes/services/Leaflet/leaflet/images/marker-shadow.png index 86a382bcaa139e4fccf05d674bd5a4eb5c09f012..9fd2979532a19a15b824ce763c76e04a8dafadfb 100644 GIT binary patch delta 604 zcmV-i0;Bz`1?mKl8Gi%-0007M$bA3+0x?NMK~#7F?b3yg<3JDu&4bGlmUw zt}e{0ExFEAztm>RdS+ssW(-8|?xr0(?$ zvBVB*%(xDLtq3Hf0I5yFm<_g=W2`QWAax{1rWVH=I!VrPs(rTFX@W#t$hXNvbgX`g zK&^w_YD;CQ!B@e0QbLIWaKAXQe2-kkloo;{iF#6}z<&`XDT_lny^IyOW>P2{MdSfj zK%nF5j9n~OOCgk>g&P;ie(tAI8g#ALYNP$7igtMWgP_4s&vtfJ7L1Y^w*|DH2 z4k!##fD&h;zFNMCWJeDoAOT#l*EHGhV#p_aw0tIcB&aDmFd_@=aUq#d;5$Cxjpe<8 zU)hi|v{kghh)gw3_|6Lt20h19ydby-=f@buKww1boDKMB%oE(lW4y+v%oW7Mp<#bb qUhx(WaofO`_<|$22vQKmbetbw(=+Wjl0aer00001gr&+8Gi-<005|90QLX?0&_`3K~#7F?b8Qq+&~aOVOj3o{{R0gz4zX8 z{Z1L+0mH*A8c0tT9=?Y5^GMqd!_fWi_1*8k{v*BqBmX14{v)@(!}|V6;lAfT`u&hx z)E|wkgzdivP=KCX0=B{4ql^?#I55JIz7x2ySN|P=J#9DRH-ExXd$MJufo!~!+U0L9 zLo?Qs=Gp}$lS`%NP)KllYR3skU^BXc6sm>WPZ_(LrU{e`RUqq2NMHn5K=SQ3@ao%N zLh6Cfzysp7kg3Kr{hW4{N$O!{6D9`gP|#fgNry{NRlnVu;%>r4Abn_z6qXFE9~@6n zz$4l>PLtd*%zqh}|Mum>5+GnF(XnnZ0aJx3Qt_GOf_nlUdWwb^rPt)?X*z`|?tnU_ zBc=!rARD)#KFR#mDj~V(0l0Ji9(r>D`P&8>r!`a#&JMTuGCBju!C>=R1_DxiK6aA3 zOzIMK+T`y_ z2WN;Ce^$|wkBr=+tFS$F8AK}rNO_8t+YNBSMYoiol%6}mmeE2;K?|o_$gf!Y5i<2Cp8PJuXJ{SOIz`oE|BgX(oBRM<^ zHd+F59e svg, +.leaflet-pane > canvas, .leaflet-zoom-box, .leaflet-image-layer, -.leaflet-layer { /* TODO optimize classes */ +.leaflet-layer { position: absolute; + left: 0; + top: 0; } .leaflet-container { overflow: hidden; - outline: 0; } .leaflet-tile, .leaflet-marker-icon, .leaflet-marker-shadow { - -moz-user-select: none; -webkit-user-select: none; - user-select: none; + -moz-user-select: none; + user-select: none; + -webkit-user-drag: none; + } +/* Safari renders non-retina tile on retina better with this, but Chrome is worse */ +.leaflet-safari .leaflet-tile { + image-rendering: -webkit-optimize-contrast; + } +/* hack that prevents hw layers "stretching" when loading new tiles */ +.leaflet-safari .leaflet-tile-container { + width: 1600px; + height: 1600px; + -webkit-transform-origin: 0 0; } .leaflet-marker-icon, .leaflet-marker-shadow { display: block; } -.leaflet-clickable { - cursor: pointer; - } -.leaflet-dragging, .leaflet-dragging .leaflet-clickable { - cursor: move; - } -.leaflet-container img { - /* map is broken in FF if you have max-width: 100% on tiles */ +/* .leaflet-container svg: reset svg max-width decleration shipped in Joomla! (joomla.org) 3.x */ +/* .leaflet-container img: map is broken in FF if you have max-width: 100% on tiles */ +.leaflet-container .leaflet-overlay-pane svg, +.leaflet-container .leaflet-marker-pane img, +.leaflet-container .leaflet-shadow-pane img, +.leaflet-container .leaflet-tile-pane img, +.leaflet-container img.leaflet-image-layer { max-width: none !important; } -.leaflet-container img.leaflet-image-layer { - /* stupid Android 2 doesn't understand "max-width: none" properly */ - max-width: 15000px !important; - } - -.leaflet-tile-pane { z-index: 2; } -.leaflet-objects-pane { z-index: 3; } -.leaflet-overlay-pane { z-index: 4; } -.leaflet-shadow-pane { z-index: 5; } -.leaflet-marker-pane { z-index: 6; } -.leaflet-popup-pane { z-index: 7; } +.leaflet-container.leaflet-touch-zoom { + -ms-touch-action: pan-x pan-y; + touch-action: pan-x pan-y; + } +.leaflet-container.leaflet-touch-drag { + -ms-touch-action: pinch-zoom; + } +.leaflet-container.leaflet-touch-drag.leaflet-touch-zoom { + -ms-touch-action: none; + touch-action: none; +} .leaflet-tile { - filter: inherit; - visibility: hidden; + filter: inherit; + visibility: hidden; } .leaflet-tile-loaded { visibility: inherit; } - .leaflet-zoom-box { - width: 0; - height: 0; - } + width: 0; + height: 0; + -moz-box-sizing: border-box; + box-sizing: border-box; + z-index: 800; + } +/* workaround for https://bugzilla.mozilla.org/show_bug.cgi?id=888319 */ +.leaflet-overlay-pane svg { + -moz-user-select: none; + } + +.leaflet-pane { z-index: 400; } + +.leaflet-tile-pane { z-index: 200; } +.leaflet-overlay-pane { z-index: 400; } +.leaflet-shadow-pane { z-index: 500; } +.leaflet-marker-pane { z-index: 600; } +.leaflet-tooltip-pane { z-index: 650; } +.leaflet-popup-pane { z-index: 700; } + +.leaflet-map-pane canvas { z-index: 100; } +.leaflet-map-pane svg { z-index: 200; } + +.leaflet-vml-shape { + width: 1px; + height: 1px; + } +.lvml { + behavior: url(#default#VML); + display: inline-block; + position: absolute; + } -/* Leaflet controls */ + +/* control positioning */ .leaflet-control { position: relative; - z-index: 7; + z-index: 800; + pointer-events: visiblePainted; /* IE 9-10 doesn't have auto */ pointer-events: auto; } .leaflet-top, @@ -110,58 +148,199 @@ margin-right: 10px; } -.leaflet-control-zoom { - -moz-border-radius: 7px; - -webkit-border-radius: 7px; - border-radius: 7px; + +/* zoom and fade animations */ + +.leaflet-fade-anim .leaflet-tile { + will-change: opacity; + } +.leaflet-fade-anim .leaflet-popup { + opacity: 0; + -webkit-transition: opacity 0.2s linear; + -moz-transition: opacity 0.2s linear; + -o-transition: opacity 0.2s linear; + transition: opacity 0.2s linear; + } +.leaflet-fade-anim .leaflet-map-pane .leaflet-popup { + opacity: 1; + } +.leaflet-zoom-animated { + -webkit-transform-origin: 0 0; + -ms-transform-origin: 0 0; + transform-origin: 0 0; } -.leaflet-control-zoom { - padding: 5px; - background: rgba(0, 0, 0, 0.25); +.leaflet-zoom-anim .leaflet-zoom-animated { + will-change: transform; + } +.leaflet-zoom-anim .leaflet-zoom-animated { + -webkit-transition: -webkit-transform 0.25s cubic-bezier(0,0,0.25,1); + -moz-transition: -moz-transform 0.25s cubic-bezier(0,0,0.25,1); + -o-transition: -o-transform 0.25s cubic-bezier(0,0,0.25,1); + transition: transform 0.25s cubic-bezier(0,0,0.25,1); } -.leaflet-control-zoom a { - background-color: rgba(255, 255, 255, 0.75); +.leaflet-zoom-anim .leaflet-tile, +.leaflet-pan-anim .leaflet-tile { + -webkit-transition: none; + -moz-transition: none; + -o-transition: none; + transition: none; + } + +.leaflet-zoom-anim .leaflet-zoom-hide { + visibility: hidden; + } + + +/* cursors */ + +.leaflet-interactive { + cursor: pointer; } -.leaflet-control-zoom a, .leaflet-control-layers a { +.leaflet-grab { + cursor: -webkit-grab; + cursor: -moz-grab; + } +.leaflet-crosshair, +.leaflet-crosshair .leaflet-interactive { + cursor: crosshair; + } +.leaflet-popup-pane, +.leaflet-control { + cursor: auto; + } +.leaflet-dragging .leaflet-grab, +.leaflet-dragging .leaflet-grab .leaflet-interactive, +.leaflet-dragging .leaflet-marker-draggable { + cursor: move; + cursor: -webkit-grabbing; + cursor: -moz-grabbing; + } + +/* marker & overlays interactivity */ +.leaflet-marker-icon, +.leaflet-marker-shadow, +.leaflet-image-layer, +.leaflet-pane > svg path, +.leaflet-tile-container { + pointer-events: none; + } + +.leaflet-marker-icon.leaflet-interactive, +.leaflet-image-layer.leaflet-interactive, +.leaflet-pane > svg path.leaflet-interactive { + pointer-events: visiblePainted; /* IE 9-10 doesn't have auto */ + pointer-events: auto; + } + +/* visual tweaks */ + +.leaflet-container { + background: #ddd; + outline: 0; + } +.leaflet-container a { + color: #0078A8; + } +.leaflet-container a.leaflet-active { + outline: 2px solid orange; + } +.leaflet-zoom-box { + border: 2px dotted #38f; + background: rgba(255,255,255,0.5); + } + + +/* general typography */ +.leaflet-container { + font: 12px/1.5 "Helvetica Neue", Arial, Helvetica, sans-serif; + } + + +/* general toolbar styles */ + +.leaflet-bar { + box-shadow: 0 1px 5px rgba(0,0,0,0.65); + border-radius: 4px; + } +.leaflet-bar a, +.leaflet-bar a:hover { + background-color: #fff; + border-bottom: 1px solid #ccc; + width: 26px; + height: 26px; + line-height: 26px; + display: block; + text-align: center; + text-decoration: none; + color: black; + } +.leaflet-bar a, +.leaflet-control-layers-toggle { background-position: 50% 50%; background-repeat: no-repeat; display: block; } -.leaflet-control-zoom a { - -moz-border-radius: 4px; - -webkit-border-radius: 4px; - border-radius: 4px; - width: 19px; - height: 19px; +.leaflet-bar a:hover { + background-color: #f4f4f4; } -.leaflet-control-zoom a:hover { - background-color: #fff; +.leaflet-bar a:first-child { + border-top-left-radius: 4px; + border-top-right-radius: 4px; + } +.leaflet-bar a:last-child { + border-bottom-left-radius: 4px; + border-bottom-right-radius: 4px; + border-bottom: none; } -.leaflet-touch .leaflet-control-zoom a { - width: 27px; - height: 27px; +.leaflet-bar a.leaflet-disabled { + cursor: default; + background-color: #f4f4f4; + color: #bbb; } -.leaflet-control-zoom-in { - background-image: url(images/zoom-in.png); - margin-bottom: 5px; + +.leaflet-touch .leaflet-bar a { + width: 30px; + height: 30px; + line-height: 30px; } + + +/* zoom control */ + +.leaflet-control-zoom-in, .leaflet-control-zoom-out { - background-image: url(images/zoom-out.png); + font: bold 18px 'Lucida Console', Monaco, monospace; + text-indent: 1px; + } +.leaflet-control-zoom-out { + font-size: 20px; } +.leaflet-touch .leaflet-control-zoom-in { + font-size: 22px; + } +.leaflet-touch .leaflet-control-zoom-out { + font-size: 24px; + } + + +/* layers control */ + .leaflet-control-layers { - box-shadow: 0 1px 7px #999; - background: #f8f8f9; - -moz-border-radius: 8px; - -webkit-border-radius: 8px; - border-radius: 8px; + box-shadow: 0 1px 5px rgba(0,0,0,0.4); + background: #fff; + border-radius: 5px; } -.leaflet-control-layers a { +.leaflet-control-layers-toggle { background-image: url(images/layers.png); width: 36px; height: 36px; } -.leaflet-touch .leaflet-control-layers a { +.leaflet-retina .leaflet-control-layers-toggle { + background-image: url(images/layers-2x.png); + background-size: 26px 26px; + } +.leaflet-touch .leaflet-control-layers-toggle { width: 44px; height: 44px; } @@ -175,11 +354,14 @@ } .leaflet-control-layers-expanded { padding: 6px 10px 6px 6px; - font: 12px/1.5 "Helvetica Neue", Arial, Helvetica, sans-serif; color: #333; background: #fff; } -.leaflet-control-layers input { +.leaflet-control-layers-scrollbar { + overflow-y: scroll; + padding-right: 5px; + } +.leaflet-control-layers-selector { margin-top: 2px; position: relative; top: 1px; @@ -193,43 +375,56 @@ margin: 5px -10px 5px -6px; } +/* Default icon URLs */ +.leaflet-default-icon-path { + background-image: url(images/marker-icon.png); + } + + +/* attribution and scale controls */ + .leaflet-container .leaflet-control-attribution { - background-color: rgba(255, 255, 255, 0.7); - box-shadow: 0 0 5px #bbb; + background: #fff; + background: rgba(255, 255, 255, 0.7); margin: 0; - } - + } .leaflet-control-attribution, .leaflet-control-scale-line { padding: 0 5px; color: #333; } - +.leaflet-control-attribution a { + text-decoration: none; + } +.leaflet-control-attribution a:hover { + text-decoration: underline; + } .leaflet-container .leaflet-control-attribution, .leaflet-container .leaflet-control-scale { - font: 11px/1.5 "Helvetica Neue", Arial, Helvetica, sans-serif; + font-size: 11px; } - .leaflet-left .leaflet-control-scale { margin-left: 5px; } .leaflet-bottom .leaflet-control-scale { margin-bottom: 5px; } - .leaflet-control-scale-line { border: 2px solid #777; border-top: none; - color: black; - line-height: 1; - font-size: 10px; - padding-bottom: 2px; - text-shadow: 1px 1px 1px #fff; - background-color: rgba(255, 255, 255, 0.5); + line-height: 1.1; + padding: 2px 5px 1px; + font-size: 11px; + white-space: nowrap; + overflow: hidden; + -moz-box-sizing: border-box; + box-sizing: border-box; + + background: #fff; + background: rgba(255, 255, 255, 0.5); } .leaflet-control-scale-line:not(:first-child) { border-top: 2px solid #777; - padding-top: 1px; border-bottom: none; margin-top: -2px; } @@ -237,87 +432,71 @@ border-bottom: 2px solid #777; } -.leaflet-touch .leaflet-control-attribution, .leaflet-touch .leaflet-control-layers { +.leaflet-touch .leaflet-control-attribution, +.leaflet-touch .leaflet-control-layers, +.leaflet-touch .leaflet-bar { box-shadow: none; } -.leaflet-touch .leaflet-control-layers { - border: 5px solid #bbb; +.leaflet-touch .leaflet-control-layers, +.leaflet-touch .leaflet-bar { + border: 2px solid rgba(0,0,0,0.2); + background-clip: padding-box; } -/* Zoom and fade animations */ - -.leaflet-fade-anim .leaflet-tile, .leaflet-fade-anim .leaflet-popup { - opacity: 0; - - -webkit-transition: opacity 0.2s linear; - -moz-transition: opacity 0.2s linear; - -o-transition: opacity 0.2s linear; - transition: opacity 0.2s linear; - } -.leaflet-fade-anim .leaflet-tile-loaded, .leaflet-fade-anim .leaflet-map-pane .leaflet-popup { - opacity: 1; - } - -.leaflet-zoom-anim .leaflet-zoom-animated { - -webkit-transition: -webkit-transform 0.25s cubic-bezier(0.25,0.1,0.25,0.75); - -moz-transition: -moz-transform 0.25s cubic-bezier(0.25,0.1,0.25,0.75); - -o-transition: -o-transform 0.25s cubic-bezier(0.25,0.1,0.25,0.75); - transition: transform 0.25s cubic-bezier(0.25,0.1,0.25,0.75); - } - -.leaflet-zoom-anim .leaflet-tile, -.leaflet-pan-anim .leaflet-tile, -.leaflet-touching .leaflet-zoom-animated { - -webkit-transition: none; - -moz-transition: none; - -o-transition: none; - transition: none; - } - -.leaflet-zoom-anim .leaflet-zoom-hide { - visibility: hidden; - } - - -/* Popup layout */ +/* popup */ .leaflet-popup { position: absolute; text-align: center; + margin-bottom: 20px; } .leaflet-popup-content-wrapper { padding: 1px; text-align: left; + border-radius: 12px; } .leaflet-popup-content { - margin: 14px 20px; + margin: 13px 19px; + line-height: 1.4; + } +.leaflet-popup-content p { + margin: 18px 0; } .leaflet-popup-tip-container { - margin: 0 auto; width: 40px; height: 20px; - position: relative; + position: absolute; + left: 50%; + margin-left: -20px; overflow: hidden; + pointer-events: none; } .leaflet-popup-tip { - width: 15px; - height: 15px; + width: 17px; + height: 17px; padding: 1px; - margin: -8px auto 0; + margin: -10px auto 0; - -moz-transform: rotate(45deg); -webkit-transform: rotate(45deg); - -ms-transform: rotate(45deg); - -o-transform: rotate(45deg); - transform: rotate(45deg); + -moz-transform: rotate(45deg); + -ms-transform: rotate(45deg); + -o-transform: rotate(45deg); + transform: rotate(45deg); + } +.leaflet-popup-content-wrapper, +.leaflet-popup-tip { + background: white; + color: #333; + box-shadow: 0 3px 14px rgba(0,0,0,0.4); } .leaflet-container a.leaflet-popup-close-button { position: absolute; top: 0; right: 0; - padding: 4px 5px 0 0; + padding: 4px 4px 0 0; + border: none; text-align: center; width: 18px; height: 14px; @@ -325,55 +504,121 @@ color: #c3c3c3; text-decoration: none; font-weight: bold; + background: transparent; } .leaflet-container a.leaflet-popup-close-button:hover { color: #999; } -.leaflet-popup-content p { - margin: 18px 0; - } .leaflet-popup-scrolled { overflow: auto; border-bottom: 1px solid #ddd; border-top: 1px solid #ddd; } +.leaflet-oldie .leaflet-popup-content-wrapper { + zoom: 1; + } +.leaflet-oldie .leaflet-popup-tip { + width: 24px; + margin: 0 auto; -/* Visual appearance */ - -.leaflet-container { - background: #ddd; + -ms-filter: "progid:DXImageTransform.Microsoft.Matrix(M11=0.70710678, M12=0.70710678, M21=-0.70710678, M22=0.70710678)"; + filter: progid:DXImageTransform.Microsoft.Matrix(M11=0.70710678, M12=0.70710678, M21=-0.70710678, M22=0.70710678); } -.leaflet-container a { - color: #0078A8; +.leaflet-oldie .leaflet-popup-tip-container { + margin-top: -1px; } -.leaflet-container a.leaflet-active { - outline: 2px solid orange; - } -.leaflet-zoom-box { - border: 2px dotted #05f; - background: white; - opacity: 0.5; + +.leaflet-oldie .leaflet-control-zoom, +.leaflet-oldie .leaflet-control-layers, +.leaflet-oldie .leaflet-popup-content-wrapper, +.leaflet-oldie .leaflet-popup-tip { + border: 1px solid #999; } + + +/* div icon */ + .leaflet-div-icon { - background: #fff; - border: 1px solid #666; - } -.leaflet-editing-icon { - border-radius: 2px; - } -.leaflet-popup-content-wrapper, .leaflet-popup-tip { - background: white; + background: #fff; + border: 1px solid #666; + } + - box-shadow: 0 3px 10px #888; - -moz-box-shadow: 0 3px 10px #888; - -webkit-box-shadow: 0 3px 14px #999; +/* Tooltip */ +/* Base styles for the element that has a tooltip */ +.leaflet-tooltip { + position: absolute; + padding: 6px; + background-color: #fff; + border: 1px solid #fff; + border-radius: 3px; + color: #222; + white-space: nowrap; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + pointer-events: none; + box-shadow: 0 1px 3px rgba(0,0,0,0.4); } -.leaflet-popup-content-wrapper { - -moz-border-radius: 20px; - -webkit-border-radius: 20px; - border-radius: 20px; +.leaflet-tooltip.leaflet-clickable { + cursor: pointer; + pointer-events: auto; } -.leaflet-popup-content { - font: 12px/1.4 "Helvetica Neue", Arial, Helvetica, sans-serif; +.leaflet-tooltip-top:before, +.leaflet-tooltip-bottom:before, +.leaflet-tooltip-left:before, +.leaflet-tooltip-right:before { + position: absolute; + pointer-events: none; + border: 6px solid transparent; + background: transparent; + content: ""; + } + +/* Directions */ + +.leaflet-tooltip-bottom { + margin-top: 6px; +} +.leaflet-tooltip-top { + margin-top: -6px; +} +.leaflet-tooltip-bottom:before, +.leaflet-tooltip-top:before { + left: 50%; + margin-left: -6px; + } +.leaflet-tooltip-top:before { + bottom: 0; + margin-bottom: -12px; + border-top-color: #fff; + } +.leaflet-tooltip-bottom:before { + top: 0; + margin-top: -12px; + margin-left: -6px; + border-bottom-color: #fff; + } +.leaflet-tooltip-left { + margin-left: -6px; +} +.leaflet-tooltip-right { + margin-left: 6px; +} +.leaflet-tooltip-left:before, +.leaflet-tooltip-right:before { + top: 50%; + margin-top: -6px; + } +.leaflet-tooltip-left:before { + right: 0; + margin-right: -12px; + border-left-color: #fff; + } +.leaflet-tooltip-right:before { + left: 0; + margin-left: -12px; + border-right-color: #fff; } diff --git a/includes/services/Leaflet/leaflet/leaflet.ie.css b/includes/services/Leaflet/leaflet/leaflet.ie.css deleted file mode 100644 index 9d2a52fba..000000000 --- a/includes/services/Leaflet/leaflet/leaflet.ie.css +++ /dev/null @@ -1,44 +0,0 @@ -.leaflet-vml-shape { - width: 1px; - height: 1px; - } -.lvml { - behavior: url(#default#VML); - display: inline-block; - position: absolute; - } - -.leaflet-control { - display: inline; - } - -.leaflet-popup-tip { - width: 21px; - _width: 27px; - margin: 0 auto; - _margin-top: -3px; - - filter: progid:DXImageTransform.Microsoft.Matrix(M11=0.70710678, M12=0.70710678, M21=-0.70710678, M22=0.70710678); - -ms-filter: "progid:DXImageTransform.Microsoft.Matrix(M11=0.70710678, M12=0.70710678, M21=-0.70710678, M22=0.70710678)"; - } -.leaflet-popup-tip-container { - margin-top: -1px; - } -.leaflet-popup-content-wrapper, .leaflet-popup-tip { - border: 1px solid #bbb; - } - -.leaflet-control-zoom { - filter: progid:DXImageTransform.Microsoft.gradient(startColorStr='#3F000000',EndColorStr='#3F000000'); - } -.leaflet-control-zoom a { - background-color: #eee; - } -.leaflet-control-zoom a:hover { - background-color: #fff; - } -.leaflet-control-layers-toggle { - } -.leaflet-control-attribution, .leaflet-control-layers { - background: white; - } \ No newline at end of file diff --git a/includes/services/Leaflet/leaflet/leaflet.js b/includes/services/Leaflet/leaflet/leaflet.js index b24900b06..2a0d32df4 100644 --- a/includes/services/Leaflet/leaflet/leaflet.js +++ b/includes/services/Leaflet/leaflet/leaflet.js @@ -1,6 +1,13581 @@ /* - Copyright (c) 2010-2012, CloudMade, Vladimir Agafonkin - Leaflet is an open-source JavaScript library for mobile-friendly interactive maps. - http://leaflet.cloudmade.com + * Leaflet 1.1.0+Detached: a3a7e045229898137bb9a3af0a1407e409e06c4f.a3a7e04, a JS library for interactive maps. http://leafletjs.com + * (c) 2010-2017 Vladimir Agafonkin, (c) 2010-2011 CloudMade + */ +(function (global, factory) { + factory(global.L = global.L || {}); +}(this, (function (exports) { 'use strict'; + + var version = "1.1.0+HEAD.a3a7e04"; + + /* + * @namespace Util + * + * Various utility functions, used by Leaflet internally. + */ + +// @function extend(dest: Object, src?: Object): Object +// Merges the properties of the `src` object (or multiple objects) into `dest` object and returns the latter. Has an `L.extend` shortcut. + function extend(dest) { + var i, j, len, src; + + for (j = 1, len = arguments.length; j < len; j++) { + src = arguments[j]; + for (i in src) { + dest[i] = src[i]; + } + } + return dest; + } + +// @function create(proto: Object, properties?: Object): Object +// Compatibility polyfill for [Object.create](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/create) + var create = Object.create || (function () { + function F() {} + return function (proto) { + F.prototype = proto; + return new F(); + }; + })(); + +// @function bind(fn: Function, …): Function +// Returns a new function bound to the arguments passed, like [Function.prototype.bind](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Function/bind). +// Has a `L.bind()` shortcut. + function bind(fn, obj) { + var slice = Array.prototype.slice; + + if (fn.bind) { + return fn.bind.apply(fn, slice.call(arguments, 1)); + } + + var args = slice.call(arguments, 2); + + return function () { + return fn.apply(obj, args.length ? args.concat(slice.call(arguments)) : arguments); + }; + } + +// @property lastId: Number +// Last unique ID used by [`stamp()`](#util-stamp) + var lastId = 0; + +// @function stamp(obj: Object): Number +// Returns the unique ID of an object, assiging it one if it doesn't have it. + function stamp(obj) { + /*eslint-disable */ + obj._leaflet_id = obj._leaflet_id || ++lastId; + return obj._leaflet_id; + /*eslint-enable */ + } + +// @function throttle(fn: Function, time: Number, context: Object): Function +// Returns a function which executes function `fn` with the given scope `context` +// (so that the `this` keyword refers to `context` inside `fn`'s code). The function +// `fn` will be called no more than one time per given amount of `time`. The arguments +// received by the bound function will be any arguments passed when binding the +// function, followed by any arguments passed when invoking the bound function. +// Has an `L.throttle` shortcut. + function throttle(fn, time, context) { + var lock, args, wrapperFn, later; + + later = function () { + // reset lock and call if queued + lock = false; + if (args) { + wrapperFn.apply(context, args); + args = false; + } + }; + + wrapperFn = function () { + if (lock) { + // called too soon, queue to call later + args = arguments; + + } else { + // call and lock until later + fn.apply(context, arguments); + setTimeout(later, time); + lock = true; + } + }; + + return wrapperFn; + } + +// @function wrapNum(num: Number, range: Number[], includeMax?: Boolean): Number +// Returns the number `num` modulo `range` in such a way so it lies within +// `range[0]` and `range[1]`. The returned value will be always smaller than +// `range[1]` unless `includeMax` is set to `true`. + function wrapNum(x, range, includeMax) { + var max = range[1], + min = range[0], + d = max - min; + return x === max && includeMax ? x : ((x - min) % d + d) % d + min; + } + +// @function falseFn(): Function +// Returns a function which always returns `false`. + function falseFn() { return false; } + +// @function formatNum(num: Number, digits?: Number): Number +// Returns the number `num` rounded to `digits` decimals, or to 5 decimals by default. + function formatNum(num, digits) { + var pow = Math.pow(10, digits || 5); + return Math.round(num * pow) / pow; + } + +// @function trim(str: String): String +// Compatibility polyfill for [String.prototype.trim](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String/Trim) + function trim(str) { + return str.trim ? str.trim() : str.replace(/^\s+|\s+$/g, ''); + } + +// @function splitWords(str: String): String[] +// Trims and splits the string on whitespace and returns the array of parts. + function splitWords(str) { + return trim(str).split(/\s+/); + } + +// @function setOptions(obj: Object, options: Object): Object +// Merges the given properties to the `options` of the `obj` object, returning the resulting options. See `Class options`. Has an `L.setOptions` shortcut. + function setOptions(obj, options) { + if (!obj.hasOwnProperty('options')) { + obj.options = obj.options ? create(obj.options) : {}; + } + for (var i in options) { + obj.options[i] = options[i]; + } + return obj.options; + } + +// @function getParamString(obj: Object, existingUrl?: String, uppercase?: Boolean): String +// Converts an object into a parameter URL string, e.g. `{a: "foo", b: "bar"}` +// translates to `'?a=foo&b=bar'`. If `existingUrl` is set, the parameters will +// be appended at the end. If `uppercase` is `true`, the parameter names will +// be uppercased (e.g. `'?A=foo&B=bar'`) + function getParamString(obj, existingUrl, uppercase) { + var params = []; + for (var i in obj) { + params.push(encodeURIComponent(uppercase ? i.toUpperCase() : i) + '=' + encodeURIComponent(obj[i])); + } + return ((!existingUrl || existingUrl.indexOf('?') === -1) ? '?' : '&') + params.join('&'); + } + + var templateRe = /\{ *([\w_\-]+) *\}/g; + +// @function template(str: String, data: Object): String +// Simple templating facility, accepts a template string of the form `'Hello {a}, {b}'` +// and a data object like `{a: 'foo', b: 'bar'}`, returns evaluated string +// `('Hello foo, bar')`. You can also specify functions instead of strings for +// data values — they will be evaluated passing `data` as an argument. + function template(str, data) { + return str.replace(templateRe, function (str, key) { + var value = data[key]; + + if (value === undefined) { + throw new Error('No value provided for variable ' + str); + + } else if (typeof value === 'function') { + value = value(data); + } + return value; + }); + } + +// @function isArray(obj): Boolean +// Compatibility polyfill for [Array.isArray](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/isArray) + var isArray = Array.isArray || function (obj) { + return (Object.prototype.toString.call(obj) === '[object Array]'); + }; + +// @function indexOf(array: Array, el: Object): Number +// Compatibility polyfill for [Array.prototype.indexOf](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/indexOf) + function indexOf(array, el) { + for (var i = 0; i < array.length; i++) { + if (array[i] === el) { return i; } + } + return -1; + } + +// @property emptyImageUrl: String +// Data URI string containing a base64-encoded empty GIF image. +// Used as a hack to free memory from unused images on WebKit-powered +// mobile devices (by setting image `src` to this string). + var emptyImageUrl = 'data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs='; + +// inspired by http://paulirish.com/2011/requestanimationframe-for-smart-animating/ + + function getPrefixed(name) { + return window['webkit' + name] || window['moz' + name] || window['ms' + name]; + } + + var lastTime = 0; + +// fallback for IE 7-8 + function timeoutDefer(fn) { + var time = +new Date(), + timeToCall = Math.max(0, 16 - (time - lastTime)); + + lastTime = time + timeToCall; + return window.setTimeout(fn, timeToCall); + } + + var requestFn = window.requestAnimationFrame || getPrefixed('RequestAnimationFrame') || timeoutDefer; + var cancelFn = window.cancelAnimationFrame || getPrefixed('CancelAnimationFrame') || + getPrefixed('CancelRequestAnimationFrame') || function (id) { window.clearTimeout(id); }; + +// @function requestAnimFrame(fn: Function, context?: Object, immediate?: Boolean): Number +// Schedules `fn` to be executed when the browser repaints. `fn` is bound to +// `context` if given. When `immediate` is set, `fn` is called immediately if +// the browser doesn't have native support for +// [`window.requestAnimationFrame`](https://developer.mozilla.org/docs/Web/API/window/requestAnimationFrame), +// otherwise it's delayed. Returns a request ID that can be used to cancel the request. + function requestAnimFrame(fn, context, immediate) { + if (immediate && requestFn === timeoutDefer) { + fn.call(context); + } else { + return requestFn.call(window, bind(fn, context)); + } + } + +// @function cancelAnimFrame(id: Number): undefined +// Cancels a previous `requestAnimFrame`. See also [window.cancelAnimationFrame](https://developer.mozilla.org/docs/Web/API/window/cancelAnimationFrame). + function cancelAnimFrame(id) { + if (id) { + cancelFn.call(window, id); + } + } + + + var Util = (Object.freeze || Object)({ + extend: extend, + create: create, + bind: bind, + lastId: lastId, + stamp: stamp, + throttle: throttle, + wrapNum: wrapNum, + falseFn: falseFn, + formatNum: formatNum, + trim: trim, + splitWords: splitWords, + setOptions: setOptions, + getParamString: getParamString, + template: template, + isArray: isArray, + indexOf: indexOf, + emptyImageUrl: emptyImageUrl, + requestFn: requestFn, + cancelFn: cancelFn, + requestAnimFrame: requestAnimFrame, + cancelAnimFrame: cancelAnimFrame + }); + +// @class Class +// @aka L.Class + +// @section +// @uninheritable + +// Thanks to John Resig and Dean Edwards for inspiration! + + function Class() {} + + Class.extend = function (props) { + + // @function extend(props: Object): Function + // [Extends the current class](#class-inheritance) given the properties to be included. + // Returns a Javascript function that is a class constructor (to be called with `new`). + var NewClass = function () { + + // call the constructor + if (this.initialize) { + this.initialize.apply(this, arguments); + } + + // call all constructor hooks + this.callInitHooks(); + }; + + var parentProto = NewClass.__super__ = this.prototype; + + var proto = create(parentProto); + proto.constructor = NewClass; + + NewClass.prototype = proto; + + // inherit parent's statics + for (var i in this) { + if (this.hasOwnProperty(i) && i !== 'prototype' && i !== '__super__') { + NewClass[i] = this[i]; + } + } + + // mix static properties into the class + if (props.statics) { + extend(NewClass, props.statics); + delete props.statics; + } + + // mix includes into the prototype + if (props.includes) { + checkDeprecatedMixinEvents(props.includes); + extend.apply(null, [proto].concat(props.includes)); + delete props.includes; + } + + // merge options + if (proto.options) { + props.options = extend(create(proto.options), props.options); + } + + // mix given properties into the prototype + extend(proto, props); + + proto._initHooks = []; + + // add method for calling all hooks + proto.callInitHooks = function () { + + if (this._initHooksCalled) { return; } + + if (parentProto.callInitHooks) { + parentProto.callInitHooks.call(this); + } + + this._initHooksCalled = true; + + for (var i = 0, len = proto._initHooks.length; i < len; i++) { + proto._initHooks[i].call(this); + } + }; + + return NewClass; + }; + + +// @function include(properties: Object): this +// [Includes a mixin](#class-includes) into the current class. + Class.include = function (props) { + extend(this.prototype, props); + return this; + }; + +// @function mergeOptions(options: Object): this +// [Merges `options`](#class-options) into the defaults of the class. + Class.mergeOptions = function (options) { + extend(this.prototype.options, options); + return this; + }; + +// @function addInitHook(fn: Function): this +// Adds a [constructor hook](#class-constructor-hooks) to the class. + Class.addInitHook = function (fn) { // (Function) || (String, args...) + var args = Array.prototype.slice.call(arguments, 1); + + var init = typeof fn === 'function' ? fn : function () { + this[fn].apply(this, args); + }; + + this.prototype._initHooks = this.prototype._initHooks || []; + this.prototype._initHooks.push(init); + return this; + }; + + function checkDeprecatedMixinEvents(includes) { + if (!L || !L.Mixin) { return; } + + includes = isArray(includes) ? includes : [includes]; + + for (var i = 0; i < includes.length; i++) { + if (includes[i] === L.Mixin.Events) { + console.warn('Deprecated include of L.Mixin.Events: ' + + 'this property will be removed in future releases, ' + + 'please inherit from L.Evented instead.', new Error().stack); + } + } + } + + /* + * @class Evented + * @aka L.Evented + * @inherits Class + * + * A set of methods shared between event-powered classes (like `Map` and `Marker`). Generally, events allow you to execute some function when something happens with an object (e.g. the user clicks on the map, causing the map to fire `'click'` event). + * + * @example + * + * ```js + * map.on('click', function(e) { + * alert(e.latlng); + * } ); + * ``` + * + * Leaflet deals with event listeners by reference, so if you want to add a listener and then remove it, define it as a function: + * + * ```js + * function onClick(e) { ... } + * + * map.on('click', onClick); + * map.off('click', onClick); + * ``` + */ + + var Events = { + /* @method on(type: String, fn: Function, context?: Object): this + * Adds a listener function (`fn`) to a particular event type of the object. You can optionally specify the context of the listener (object the this keyword will point to). You can also pass several space-separated types (e.g. `'click dblclick'`). + * + * @alternative + * @method on(eventMap: Object): this + * Adds a set of type/listener pairs, e.g. `{click: onClick, mousemove: onMouseMove}` + */ + on: function (types, fn, context) { + + // types can be a map of types/handlers + if (typeof types === 'object') { + for (var type in types) { + // we don't process space-separated events here for performance; + // it's a hot path since Layer uses the on(obj) syntax + this._on(type, types[type], fn); + } + + } else { + // types can be a string of space-separated words + types = splitWords(types); + + for (var i = 0, len = types.length; i < len; i++) { + this._on(types[i], fn, context); + } + } + + return this; + }, + + /* @method off(type: String, fn?: Function, context?: Object): this + * Removes a previously added listener function. If no function is specified, it will remove all the listeners of that particular event from the object. Note that if you passed a custom context to `on`, you must pass the same context to `off` in order to remove the listener. + * + * @alternative + * @method off(eventMap: Object): this + * Removes a set of type/listener pairs. + * + * @alternative + * @method off: this + * Removes all listeners to all events on the object. + */ + off: function (types, fn, context) { + + if (!types) { + // clear all listeners if called without arguments + delete this._events; + + } else if (typeof types === 'object') { + for (var type in types) { + this._off(type, types[type], fn); + } + + } else { + types = splitWords(types); + + for (var i = 0, len = types.length; i < len; i++) { + this._off(types[i], fn, context); + } + } + + return this; + }, + + // attach listener (without syntactic sugar now) + _on: function (type, fn, context) { + this._events = this._events || {}; + + /* get/init listeners for type */ + var typeListeners = this._events[type]; + if (!typeListeners) { + typeListeners = []; + this._events[type] = typeListeners; + } + + if (context === this) { + // Less memory footprint. + context = undefined; + } + var newListener = {fn: fn, ctx: context}, + listeners = typeListeners; + + // check if fn already there + for (var i = 0, len = listeners.length; i < len; i++) { + if (listeners[i].fn === fn && listeners[i].ctx === context) { + return; + } + } + + listeners.push(newListener); + }, + + _off: function (type, fn, context) { + var listeners, + i, + len; + + if (!this._events) { return; } + + listeners = this._events[type]; + + if (!listeners) { + return; + } + + if (!fn) { + // Set all removed listeners to noop so they are not called if remove happens in fire + for (i = 0, len = listeners.length; i < len; i++) { + listeners[i].fn = falseFn; + } + // clear all listeners for a type if function isn't specified + delete this._events[type]; + return; + } + + if (context === this) { + context = undefined; + } + + if (listeners) { + + // find fn and remove it + for (i = 0, len = listeners.length; i < len; i++) { + var l = listeners[i]; + if (l.ctx !== context) { continue; } + if (l.fn === fn) { + + // set the removed listener to noop so that's not called if remove happens in fire + l.fn = falseFn; + + if (this._firingCount) { + /* copy array in case events are being fired */ + this._events[type] = listeners = listeners.slice(); + } + listeners.splice(i, 1); + + return; + } + } + } + }, + + // @method fire(type: String, data?: Object, propagate?: Boolean): this + // Fires an event of the specified type. You can optionally provide an data + // object — the first argument of the listener function will contain its + // properties. The event can optionally be propagated to event parents. + fire: function (type, data, propagate) { + if (!this.listens(type, propagate)) { return this; } + + var event = extend({}, data, {type: type, target: this}); + + if (this._events) { + var listeners = this._events[type]; + + if (listeners) { + this._firingCount = (this._firingCount + 1) || 1; + for (var i = 0, len = listeners.length; i < len; i++) { + var l = listeners[i]; + l.fn.call(l.ctx || this, event); + } + + this._firingCount--; + } + } + + if (propagate) { + // propagate the event to parents (set with addEventParent) + this._propagateEvent(event); + } + + return this; + }, + + // @method listens(type: String): Boolean + // Returns `true` if a particular event type has any listeners attached to it. + listens: function (type, propagate) { + var listeners = this._events && this._events[type]; + if (listeners && listeners.length) { return true; } + + if (propagate) { + // also check parents for listeners if event propagates + for (var id in this._eventParents) { + if (this._eventParents[id].listens(type, propagate)) { return true; } + } + } + return false; + }, + + // @method once(…): this + // Behaves as [`on(…)`](#evented-on), except the listener will only get fired once and then removed. + once: function (types, fn, context) { + + if (typeof types === 'object') { + for (var type in types) { + this.once(type, types[type], fn); + } + return this; + } + + var handler = bind(function () { + this + .off(types, fn, context) + .off(types, handler, context); + }, this); + + // add a listener that's executed once and removed after that + return this + .on(types, fn, context) + .on(types, handler, context); + }, + + // @method addEventParent(obj: Evented): this + // Adds an event parent - an `Evented` that will receive propagated events + addEventParent: function (obj) { + this._eventParents = this._eventParents || {}; + this._eventParents[stamp(obj)] = obj; + return this; + }, + + // @method removeEventParent(obj: Evented): this + // Removes an event parent, so it will stop receiving propagated events + removeEventParent: function (obj) { + if (this._eventParents) { + delete this._eventParents[stamp(obj)]; + } + return this; + }, + + _propagateEvent: function (e) { + for (var id in this._eventParents) { + this._eventParents[id].fire(e.type, extend({layer: e.target}, e), true); + } + } + }; + +// aliases; we should ditch those eventually + +// @method addEventListener(…): this +// Alias to [`on(…)`](#evented-on) + Events.addEventListener = Events.on; + +// @method removeEventListener(…): this +// Alias to [`off(…)`](#evented-off) + +// @method clearAllEventListeners(…): this +// Alias to [`off()`](#evented-off) + Events.removeEventListener = Events.clearAllEventListeners = Events.off; + +// @method addOneTimeEventListener(…): this +// Alias to [`once(…)`](#evented-once) + Events.addOneTimeEventListener = Events.once; + +// @method fireEvent(…): this +// Alias to [`fire(…)`](#evented-fire) + Events.fireEvent = Events.fire; + +// @method hasEventListeners(…): Boolean +// Alias to [`listens(…)`](#evented-listens) + Events.hasEventListeners = Events.listens; + + var Evented = Class.extend(Events); + + /* + * @class Point + * @aka L.Point + * + * Represents a point with `x` and `y` coordinates in pixels. + * + * @example + * + * ```js + * var point = L.point(200, 300); + * ``` + * + * All Leaflet methods and options that accept `Point` objects also accept them in a simple Array form (unless noted otherwise), so these lines are equivalent: + * + * ```js + * map.panBy([200, 300]); + * map.panBy(L.point(200, 300)); + * ``` + */ + + function Point(x, y, round) { + // @property x: Number; The `x` coordinate of the point + this.x = (round ? Math.round(x) : x); + // @property y: Number; The `y` coordinate of the point + this.y = (round ? Math.round(y) : y); + } + + Point.prototype = { + + // @method clone(): Point + // Returns a copy of the current point. + clone: function () { + return new Point(this.x, this.y); + }, + + // @method add(otherPoint: Point): Point + // Returns the result of addition of the current and the given points. + add: function (point) { + // non-destructive, returns a new point + return this.clone()._add(toPoint(point)); + }, + + _add: function (point) { + // destructive, used directly for performance in situations where it's safe to modify existing point + this.x += point.x; + this.y += point.y; + return this; + }, + + // @method subtract(otherPoint: Point): Point + // Returns the result of subtraction of the given point from the current. + subtract: function (point) { + return this.clone()._subtract(toPoint(point)); + }, + + _subtract: function (point) { + this.x -= point.x; + this.y -= point.y; + return this; + }, + + // @method divideBy(num: Number): Point + // Returns the result of division of the current point by the given number. + divideBy: function (num) { + return this.clone()._divideBy(num); + }, + + _divideBy: function (num) { + this.x /= num; + this.y /= num; + return this; + }, + + // @method multiplyBy(num: Number): Point + // Returns the result of multiplication of the current point by the given number. + multiplyBy: function (num) { + return this.clone()._multiplyBy(num); + }, + + _multiplyBy: function (num) { + this.x *= num; + this.y *= num; + return this; + }, + + // @method scaleBy(scale: Point): Point + // Multiply each coordinate of the current point by each coordinate of + // `scale`. In linear algebra terms, multiply the point by the + // [scaling matrix](https://en.wikipedia.org/wiki/Scaling_%28geometry%29#Matrix_representation) + // defined by `scale`. + scaleBy: function (point) { + return new Point(this.x * point.x, this.y * point.y); + }, + + // @method unscaleBy(scale: Point): Point + // Inverse of `scaleBy`. Divide each coordinate of the current point by + // each coordinate of `scale`. + unscaleBy: function (point) { + return new Point(this.x / point.x, this.y / point.y); + }, + + // @method round(): Point + // Returns a copy of the current point with rounded coordinates. + round: function () { + return this.clone()._round(); + }, + + _round: function () { + this.x = Math.round(this.x); + this.y = Math.round(this.y); + return this; + }, + + // @method floor(): Point + // Returns a copy of the current point with floored coordinates (rounded down). + floor: function () { + return this.clone()._floor(); + }, + + _floor: function () { + this.x = Math.floor(this.x); + this.y = Math.floor(this.y); + return this; + }, + + // @method ceil(): Point + // Returns a copy of the current point with ceiled coordinates (rounded up). + ceil: function () { + return this.clone()._ceil(); + }, + + _ceil: function () { + this.x = Math.ceil(this.x); + this.y = Math.ceil(this.y); + return this; + }, + + // @method distanceTo(otherPoint: Point): Number + // Returns the cartesian distance between the current and the given points. + distanceTo: function (point) { + point = toPoint(point); + + var x = point.x - this.x, + y = point.y - this.y; + + return Math.sqrt(x * x + y * y); + }, + + // @method equals(otherPoint: Point): Boolean + // Returns `true` if the given point has the same coordinates. + equals: function (point) { + point = toPoint(point); + + return point.x === this.x && + point.y === this.y; + }, + + // @method contains(otherPoint: Point): Boolean + // Returns `true` if both coordinates of the given point are less than the corresponding current point coordinates (in absolute values). + contains: function (point) { + point = toPoint(point); + + return Math.abs(point.x) <= Math.abs(this.x) && + Math.abs(point.y) <= Math.abs(this.y); + }, + + // @method toString(): String + // Returns a string representation of the point for debugging purposes. + toString: function () { + return 'Point(' + + formatNum(this.x) + ', ' + + formatNum(this.y) + ')'; + } + }; + +// @factory L.point(x: Number, y: Number, round?: Boolean) +// Creates a Point object with the given `x` and `y` coordinates. If optional `round` is set to true, rounds the `x` and `y` values. + +// @alternative +// @factory L.point(coords: Number[]) +// Expects an array of the form `[x, y]` instead. + +// @alternative +// @factory L.point(coords: Object) +// Expects a plain object of the form `{x: Number, y: Number}` instead. + function toPoint(x, y, round) { + if (x instanceof Point) { + return x; + } + if (isArray(x)) { + return new Point(x[0], x[1]); + } + if (x === undefined || x === null) { + return x; + } + if (typeof x === 'object' && 'x' in x && 'y' in x) { + return new Point(x.x, x.y); + } + return new Point(x, y, round); + } + + /* + * @class Bounds + * @aka L.Bounds + * + * Represents a rectangular area in pixel coordinates. + * + * @example + * + * ```js + * var p1 = L.point(10, 10), + * p2 = L.point(40, 60), + * bounds = L.bounds(p1, p2); + * ``` + * + * All Leaflet methods that accept `Bounds` objects also accept them in a simple Array form (unless noted otherwise), so the bounds example above can be passed like this: + * + * ```js + * otherBounds.intersects([[10, 10], [40, 60]]); + * ``` + */ + + function Bounds(a, b) { + if (!a) { return; } + + var points = b ? [a, b] : a; + + for (var i = 0, len = points.length; i < len; i++) { + this.extend(points[i]); + } + } + + Bounds.prototype = { + // @method extend(point: Point): this + // Extends the bounds to contain the given point. + extend: function (point) { // (Point) + point = toPoint(point); + + // @property min: Point + // The top left corner of the rectangle. + // @property max: Point + // The bottom right corner of the rectangle. + if (!this.min && !this.max) { + this.min = point.clone(); + this.max = point.clone(); + } else { + this.min.x = Math.min(point.x, this.min.x); + this.max.x = Math.max(point.x, this.max.x); + this.min.y = Math.min(point.y, this.min.y); + this.max.y = Math.max(point.y, this.max.y); + } + return this; + }, + + // @method getCenter(round?: Boolean): Point + // Returns the center point of the bounds. + getCenter: function (round) { + return new Point( + (this.min.x + this.max.x) / 2, + (this.min.y + this.max.y) / 2, round); + }, + + // @method getBottomLeft(): Point + // Returns the bottom-left point of the bounds. + getBottomLeft: function () { + return new Point(this.min.x, this.max.y); + }, + + // @method getTopRight(): Point + // Returns the top-right point of the bounds. + getTopRight: function () { // -> Point + return new Point(this.max.x, this.min.y); + }, + + // @method getTopLeft(): Point + // Returns the top-left point of the bounds (i.e. [`this.min`](#bounds-min)). + getTopLeft: function () { + return this.min; // left, top + }, + + // @method getBottomRight(): Point + // Returns the bottom-right point of the bounds (i.e. [`this.max`](#bounds-max)). + getBottomRight: function () { + return this.max; // right, bottom + }, + + // @method getSize(): Point + // Returns the size of the given bounds + getSize: function () { + return this.max.subtract(this.min); + }, + + // @method contains(otherBounds: Bounds): Boolean + // Returns `true` if the rectangle contains the given one. + // @alternative + // @method contains(point: Point): Boolean + // Returns `true` if the rectangle contains the given point. + contains: function (obj) { + var min, max; + + if (typeof obj[0] === 'number' || obj instanceof Point) { + obj = toPoint(obj); + } else { + obj = toBounds(obj); + } + + if (obj instanceof Bounds) { + min = obj.min; + max = obj.max; + } else { + min = max = obj; + } + + return (min.x >= this.min.x) && + (max.x <= this.max.x) && + (min.y >= this.min.y) && + (max.y <= this.max.y); + }, + + // @method intersects(otherBounds: Bounds): Boolean + // Returns `true` if the rectangle intersects the given bounds. Two bounds + // intersect if they have at least one point in common. + intersects: function (bounds) { // (Bounds) -> Boolean + bounds = toBounds(bounds); + + var min = this.min, + max = this.max, + min2 = bounds.min, + max2 = bounds.max, + xIntersects = (max2.x >= min.x) && (min2.x <= max.x), + yIntersects = (max2.y >= min.y) && (min2.y <= max.y); + + return xIntersects && yIntersects; + }, + + // @method overlaps(otherBounds: Bounds): Boolean + // Returns `true` if the rectangle overlaps the given bounds. Two bounds + // overlap if their intersection is an area. + overlaps: function (bounds) { // (Bounds) -> Boolean + bounds = toBounds(bounds); + + var min = this.min, + max = this.max, + min2 = bounds.min, + max2 = bounds.max, + xOverlaps = (max2.x > min.x) && (min2.x < max.x), + yOverlaps = (max2.y > min.y) && (min2.y < max.y); + + return xOverlaps && yOverlaps; + }, + + isValid: function () { + return !!(this.min && this.max); + } + }; + + +// @factory L.bounds(corner1: Point, corner2: Point) +// Creates a Bounds object from two corners coordinate pairs. +// @alternative +// @factory L.bounds(points: Point[]) +// Creates a Bounds object from the given array of points. + function toBounds(a, b) { + if (!a || a instanceof Bounds) { + return a; + } + return new Bounds(a, b); + } + + /* + * @class LatLngBounds + * @aka L.LatLngBounds + * + * Represents a rectangular geographical area on a map. + * + * @example + * + * ```js + * var corner1 = L.latLng(40.712, -74.227), + * corner2 = L.latLng(40.774, -74.125), + * bounds = L.latLngBounds(corner1, corner2); + * ``` + * + * All Leaflet methods that accept LatLngBounds objects also accept them in a simple Array form (unless noted otherwise), so the bounds example above can be passed like this: + * + * ```js + * map.fitBounds([ + * [40.712, -74.227], + * [40.774, -74.125] + * ]); + * ``` + * + * Caution: if the area crosses the antimeridian (often confused with the International Date Line), you must specify corners _outside_ the [-180, 180] degrees longitude range. + */ + + function LatLngBounds(corner1, corner2) { // (LatLng, LatLng) or (LatLng[]) + if (!corner1) { return; } + + var latlngs = corner2 ? [corner1, corner2] : corner1; + + for (var i = 0, len = latlngs.length; i < len; i++) { + this.extend(latlngs[i]); + } + } + + LatLngBounds.prototype = { + + // @method extend(latlng: LatLng): this + // Extend the bounds to contain the given point + + // @alternative + // @method extend(otherBounds: LatLngBounds): this + // Extend the bounds to contain the given bounds + extend: function (obj) { + var sw = this._southWest, + ne = this._northEast, + sw2, ne2; + + if (obj instanceof LatLng) { + sw2 = obj; + ne2 = obj; + + } else if (obj instanceof LatLngBounds) { + sw2 = obj._southWest; + ne2 = obj._northEast; + + if (!sw2 || !ne2) { return this; } + + } else { + return obj ? this.extend(toLatLng(obj) || toLatLngBounds(obj)) : this; + } + + if (!sw && !ne) { + this._southWest = new LatLng(sw2.lat, sw2.lng); + this._northEast = new LatLng(ne2.lat, ne2.lng); + } else { + sw.lat = Math.min(sw2.lat, sw.lat); + sw.lng = Math.min(sw2.lng, sw.lng); + ne.lat = Math.max(ne2.lat, ne.lat); + ne.lng = Math.max(ne2.lng, ne.lng); + } + + return this; + }, + + // @method pad(bufferRatio: Number): LatLngBounds + // Returns bigger bounds created by extending the current bounds by a given percentage in each direction. + pad: function (bufferRatio) { + var sw = this._southWest, + ne = this._northEast, + heightBuffer = Math.abs(sw.lat - ne.lat) * bufferRatio, + widthBuffer = Math.abs(sw.lng - ne.lng) * bufferRatio; + + return new LatLngBounds( + new LatLng(sw.lat - heightBuffer, sw.lng - widthBuffer), + new LatLng(ne.lat + heightBuffer, ne.lng + widthBuffer)); + }, + + // @method getCenter(): LatLng + // Returns the center point of the bounds. + getCenter: function () { + return new LatLng( + (this._southWest.lat + this._northEast.lat) / 2, + (this._southWest.lng + this._northEast.lng) / 2); + }, + + // @method getSouthWest(): LatLng + // Returns the south-west point of the bounds. + getSouthWest: function () { + return this._southWest; + }, + + // @method getNorthEast(): LatLng + // Returns the north-east point of the bounds. + getNorthEast: function () { + return this._northEast; + }, + + // @method getNorthWest(): LatLng + // Returns the north-west point of the bounds. + getNorthWest: function () { + return new LatLng(this.getNorth(), this.getWest()); + }, + + // @method getSouthEast(): LatLng + // Returns the south-east point of the bounds. + getSouthEast: function () { + return new LatLng(this.getSouth(), this.getEast()); + }, + + // @method getWest(): Number + // Returns the west longitude of the bounds + getWest: function () { + return this._southWest.lng; + }, + + // @method getSouth(): Number + // Returns the south latitude of the bounds + getSouth: function () { + return this._southWest.lat; + }, + + // @method getEast(): Number + // Returns the east longitude of the bounds + getEast: function () { + return this._northEast.lng; + }, + + // @method getNorth(): Number + // Returns the north latitude of the bounds + getNorth: function () { + return this._northEast.lat; + }, + + // @method contains(otherBounds: LatLngBounds): Boolean + // Returns `true` if the rectangle contains the given one. + + // @alternative + // @method contains (latlng: LatLng): Boolean + // Returns `true` if the rectangle contains the given point. + contains: function (obj) { // (LatLngBounds) or (LatLng) -> Boolean + if (typeof obj[0] === 'number' || obj instanceof LatLng || 'lat' in obj) { + obj = toLatLng(obj); + } else { + obj = toLatLngBounds(obj); + } + + var sw = this._southWest, + ne = this._northEast, + sw2, ne2; + + if (obj instanceof LatLngBounds) { + sw2 = obj.getSouthWest(); + ne2 = obj.getNorthEast(); + } else { + sw2 = ne2 = obj; + } + + return (sw2.lat >= sw.lat) && (ne2.lat <= ne.lat) && + (sw2.lng >= sw.lng) && (ne2.lng <= ne.lng); + }, + + // @method intersects(otherBounds: LatLngBounds): Boolean + // Returns `true` if the rectangle intersects the given bounds. Two bounds intersect if they have at least one point in common. + intersects: function (bounds) { + bounds = toLatLngBounds(bounds); + + var sw = this._southWest, + ne = this._northEast, + sw2 = bounds.getSouthWest(), + ne2 = bounds.getNorthEast(), + + latIntersects = (ne2.lat >= sw.lat) && (sw2.lat <= ne.lat), + lngIntersects = (ne2.lng >= sw.lng) && (sw2.lng <= ne.lng); + + return latIntersects && lngIntersects; + }, + + // @method overlaps(otherBounds: Bounds): Boolean + // Returns `true` if the rectangle overlaps the given bounds. Two bounds overlap if their intersection is an area. + overlaps: function (bounds) { + bounds = toLatLngBounds(bounds); + + var sw = this._southWest, + ne = this._northEast, + sw2 = bounds.getSouthWest(), + ne2 = bounds.getNorthEast(), + + latOverlaps = (ne2.lat > sw.lat) && (sw2.lat < ne.lat), + lngOverlaps = (ne2.lng > sw.lng) && (sw2.lng < ne.lng); + + return latOverlaps && lngOverlaps; + }, + + // @method toBBoxString(): String + // Returns a string with bounding box coordinates in a 'southwest_lng,southwest_lat,northeast_lng,northeast_lat' format. Useful for sending requests to web services that return geo data. + toBBoxString: function () { + return [this.getWest(), this.getSouth(), this.getEast(), this.getNorth()].join(','); + }, + + // @method equals(otherBounds: LatLngBounds, maxMargin?: Number): Boolean + // Returns `true` if the rectangle is equivalent (within a small margin of error) to the given bounds. The margin of error can be overriden by setting `maxMargin` to a small number. + equals: function (bounds, maxMargin) { + if (!bounds) { return false; } + + bounds = toLatLngBounds(bounds); + + return this._southWest.equals(bounds.getSouthWest(), maxMargin) && + this._northEast.equals(bounds.getNorthEast(), maxMargin); + }, + + // @method isValid(): Boolean + // Returns `true` if the bounds are properly initialized. + isValid: function () { + return !!(this._southWest && this._northEast); + } + }; + +// TODO International date line? + +// @factory L.latLngBounds(corner1: LatLng, corner2: LatLng) +// Creates a `LatLngBounds` object by defining two diagonally opposite corners of the rectangle. + +// @alternative +// @factory L.latLngBounds(latlngs: LatLng[]) +// Creates a `LatLngBounds` object defined by the geographical points it contains. Very useful for zooming the map to fit a particular set of locations with [`fitBounds`](#map-fitbounds). + function toLatLngBounds(a, b) { + if (a instanceof LatLngBounds) { + return a; + } + return new LatLngBounds(a, b); + } + + /* @class LatLng + * @aka L.LatLng + * + * Represents a geographical point with a certain latitude and longitude. + * + * @example + * + * ``` + * var latlng = L.latLng(50.5, 30.5); + * ``` + * + * All Leaflet methods that accept LatLng objects also accept them in a simple Array form and simple object form (unless noted otherwise), so these lines are equivalent: + * + * ``` + * map.panTo([50, 30]); + * map.panTo({lon: 30, lat: 50}); + * map.panTo({lat: 50, lng: 30}); + * map.panTo(L.latLng(50, 30)); + * ``` + */ + + function LatLng(lat, lng, alt) { + if (isNaN(lat) || isNaN(lng)) { + throw new Error('Invalid LatLng object: (' + lat + ', ' + lng + ')'); + } + + // @property lat: Number + // Latitude in degrees + this.lat = +lat; + + // @property lng: Number + // Longitude in degrees + this.lng = +lng; + + // @property alt: Number + // Altitude in meters (optional) + if (alt !== undefined) { + this.alt = +alt; + } + } + + LatLng.prototype = { + // @method equals(otherLatLng: LatLng, maxMargin?: Number): Boolean + // Returns `true` if the given `LatLng` point is at the same position (within a small margin of error). The margin of error can be overriden by setting `maxMargin` to a small number. + equals: function (obj, maxMargin) { + if (!obj) { return false; } + + obj = toLatLng(obj); + + var margin = Math.max( + Math.abs(this.lat - obj.lat), + Math.abs(this.lng - obj.lng)); + + return margin <= (maxMargin === undefined ? 1.0E-9 : maxMargin); + }, + + // @method toString(): String + // Returns a string representation of the point (for debugging purposes). + toString: function (precision) { + return 'LatLng(' + + formatNum(this.lat, precision) + ', ' + + formatNum(this.lng, precision) + ')'; + }, + + // @method distanceTo(otherLatLng: LatLng): Number + // Returns the distance (in meters) to the given `LatLng` calculated using the [Haversine formula](http://en.wikipedia.org/wiki/Haversine_formula). + distanceTo: function (other) { + return Earth.distance(this, toLatLng(other)); + }, + + // @method wrap(): LatLng + // Returns a new `LatLng` object with the longitude wrapped so it's always between -180 and +180 degrees. + wrap: function () { + return Earth.wrapLatLng(this); + }, + + // @method toBounds(sizeInMeters: Number): LatLngBounds + // Returns a new `LatLngBounds` object in which each boundary is `sizeInMeters/2` meters apart from the `LatLng`. + toBounds: function (sizeInMeters) { + var latAccuracy = 180 * sizeInMeters / 40075017, + lngAccuracy = latAccuracy / Math.cos((Math.PI / 180) * this.lat); + + return toLatLngBounds( + [this.lat - latAccuracy, this.lng - lngAccuracy], + [this.lat + latAccuracy, this.lng + lngAccuracy]); + }, + + clone: function () { + return new LatLng(this.lat, this.lng, this.alt); + } + }; + + + +// @factory L.latLng(latitude: Number, longitude: Number, altitude?: Number): LatLng +// Creates an object representing a geographical point with the given latitude and longitude (and optionally altitude). + +// @alternative +// @factory L.latLng(coords: Array): LatLng +// Expects an array of the form `[Number, Number]` or `[Number, Number, Number]` instead. + +// @alternative +// @factory L.latLng(coords: Object): LatLng +// Expects an plain object of the form `{lat: Number, lng: Number}` or `{lat: Number, lng: Number, alt: Number}` instead. + + function toLatLng(a, b, c) { + if (a instanceof LatLng) { + return a; + } + if (isArray(a) && typeof a[0] !== 'object') { + if (a.length === 3) { + return new LatLng(a[0], a[1], a[2]); + } + if (a.length === 2) { + return new LatLng(a[0], a[1]); + } + return null; + } + if (a === undefined || a === null) { + return a; + } + if (typeof a === 'object' && 'lat' in a) { + return new LatLng(a.lat, 'lng' in a ? a.lng : a.lon, a.alt); + } + if (b === undefined) { + return null; + } + return new LatLng(a, b, c); + } + + /* + * @namespace CRS + * @crs L.CRS.Base + * Object that defines coordinate reference systems for projecting + * geographical points into pixel (screen) coordinates and back (and to + * coordinates in other units for [WMS](https://en.wikipedia.org/wiki/Web_Map_Service) services). See + * [spatial reference system](http://en.wikipedia.org/wiki/Coordinate_reference_system). + * + * Leaflet defines the most usual CRSs by default. If you want to use a + * CRS not defined by default, take a look at the + * [Proj4Leaflet](https://github.com/kartena/Proj4Leaflet) plugin. + */ + + var CRS = { + // @method latLngToPoint(latlng: LatLng, zoom: Number): Point + // Projects geographical coordinates into pixel coordinates for a given zoom. + latLngToPoint: function (latlng, zoom) { + var projectedPoint = this.projection.project(latlng), + scale = this.scale(zoom); + + return this.transformation._transform(projectedPoint, scale); + }, + + // @method pointToLatLng(point: Point, zoom: Number): LatLng + // The inverse of `latLngToPoint`. Projects pixel coordinates on a given + // zoom into geographical coordinates. + pointToLatLng: function (point, zoom) { + var scale = this.scale(zoom), + untransformedPoint = this.transformation.untransform(point, scale); + + return this.projection.unproject(untransformedPoint); + }, + + // @method project(latlng: LatLng): Point + // Projects geographical coordinates into coordinates in units accepted for + // this CRS (e.g. meters for EPSG:3857, for passing it to WMS services). + project: function (latlng) { + return this.projection.project(latlng); + }, + + // @method unproject(point: Point): LatLng + // Given a projected coordinate returns the corresponding LatLng. + // The inverse of `project`. + unproject: function (point) { + return this.projection.unproject(point); + }, + + // @method scale(zoom: Number): Number + // Returns the scale used when transforming projected coordinates into + // pixel coordinates for a particular zoom. For example, it returns + // `256 * 2^zoom` for Mercator-based CRS. + scale: function (zoom) { + return 256 * Math.pow(2, zoom); + }, + + // @method zoom(scale: Number): Number + // Inverse of `scale()`, returns the zoom level corresponding to a scale + // factor of `scale`. + zoom: function (scale) { + return Math.log(scale / 256) / Math.LN2; + }, + + // @method getProjectedBounds(zoom: Number): Bounds + // Returns the projection's bounds scaled and transformed for the provided `zoom`. + getProjectedBounds: function (zoom) { + if (this.infinite) { return null; } + + var b = this.projection.bounds, + s = this.scale(zoom), + min = this.transformation.transform(b.min, s), + max = this.transformation.transform(b.max, s); + + return new Bounds(min, max); + }, + + // @method distance(latlng1: LatLng, latlng2: LatLng): Number + // Returns the distance between two geographical coordinates. + + // @property code: String + // Standard code name of the CRS passed into WMS services (e.g. `'EPSG:3857'`) + // + // @property wrapLng: Number[] + // An array of two numbers defining whether the longitude (horizontal) coordinate + // axis wraps around a given range and how. Defaults to `[-180, 180]` in most + // geographical CRSs. If `undefined`, the longitude axis does not wrap around. + // + // @property wrapLat: Number[] + // Like `wrapLng`, but for the latitude (vertical) axis. + + // wrapLng: [min, max], + // wrapLat: [min, max], + + // @property infinite: Boolean + // If true, the coordinate space will be unbounded (infinite in both axes) + infinite: false, + + // @method wrapLatLng(latlng: LatLng): LatLng + // Returns a `LatLng` where lat and lng has been wrapped according to the + // CRS's `wrapLat` and `wrapLng` properties, if they are outside the CRS's bounds. + wrapLatLng: function (latlng) { + var lng = this.wrapLng ? wrapNum(latlng.lng, this.wrapLng, true) : latlng.lng, + lat = this.wrapLat ? wrapNum(latlng.lat, this.wrapLat, true) : latlng.lat, + alt = latlng.alt; + + return new LatLng(lat, lng, alt); + }, + + // @method wrapLatLngBounds(bounds: LatLngBounds): LatLngBounds + // Returns a `LatLngBounds` with the same size as the given one, ensuring + // that its center is within the CRS's bounds. + // Only accepts actual `L.LatLngBounds` instances, not arrays. + wrapLatLngBounds: function (bounds) { + var center = bounds.getCenter(), + newCenter = this.wrapLatLng(center), + latShift = center.lat - newCenter.lat, + lngShift = center.lng - newCenter.lng; + + if (latShift === 0 && lngShift === 0) { + return bounds; + } + + var sw = bounds.getSouthWest(), + ne = bounds.getNorthEast(), + newSw = new LatLng(sw.lat - latShift, sw.lng - lngShift), + newNe = new LatLng(ne.lat - latShift, ne.lng - lngShift); + + return new LatLngBounds(newSw, newNe); + } + }; + + /* + * @namespace CRS + * @crs L.CRS.Earth + * + * Serves as the base for CRS that are global such that they cover the earth. + * Can only be used as the base for other CRS and cannot be used directly, + * since it does not have a `code`, `projection` or `transformation`. `distance()` returns + * meters. + */ + + var Earth = extend({}, CRS, { + wrapLng: [-180, 180], + + // Mean Earth Radius, as recommended for use by + // the International Union of Geodesy and Geophysics, + // see http://rosettacode.org/wiki/Haversine_formula + R: 6371000, + + // distance between two geographical points using spherical law of cosines approximation + distance: function (latlng1, latlng2) { + var rad = Math.PI / 180, + lat1 = latlng1.lat * rad, + lat2 = latlng2.lat * rad, + a = Math.sin(lat1) * Math.sin(lat2) + + Math.cos(lat1) * Math.cos(lat2) * Math.cos((latlng2.lng - latlng1.lng) * rad); + + return this.R * Math.acos(Math.min(a, 1)); + } + }); + + /* + * @namespace Projection + * @projection L.Projection.SphericalMercator + * + * Spherical Mercator projection — the most common projection for online maps, + * used by almost all free and commercial tile providers. Assumes that Earth is + * a sphere. Used by the `EPSG:3857` CRS. + */ + + var SphericalMercator = { + + R: 6378137, + MAX_LATITUDE: 85.0511287798, + + project: function (latlng) { + var d = Math.PI / 180, + max = this.MAX_LATITUDE, + lat = Math.max(Math.min(max, latlng.lat), -max), + sin = Math.sin(lat * d); + + return new Point( + this.R * latlng.lng * d, + this.R * Math.log((1 + sin) / (1 - sin)) / 2); + }, + + unproject: function (point) { + var d = 180 / Math.PI; + + return new LatLng( + (2 * Math.atan(Math.exp(point.y / this.R)) - (Math.PI / 2)) * d, + point.x * d / this.R); + }, + + bounds: (function () { + var d = 6378137 * Math.PI; + return new Bounds([-d, -d], [d, d]); + })() + }; + + /* + * @class Transformation + * @aka L.Transformation + * + * Represents an affine transformation: a set of coefficients `a`, `b`, `c`, `d` + * for transforming a point of a form `(x, y)` into `(a*x + b, c*y + d)` and doing + * the reverse. Used by Leaflet in its projections code. + * + * @example + * + * ```js + * var transformation = L.transformation(2, 5, -1, 10), + * p = L.point(1, 2), + * p2 = transformation.transform(p), // L.point(7, 8) + * p3 = transformation.untransform(p2); // L.point(1, 2) + * ``` + */ + + +// factory new L.Transformation(a: Number, b: Number, c: Number, d: Number) +// Creates a `Transformation` object with the given coefficients. + function Transformation(a, b, c, d) { + if (isArray(a)) { + // use array properties + this._a = a[0]; + this._b = a[1]; + this._c = a[2]; + this._d = a[3]; + return; + } + this._a = a; + this._b = b; + this._c = c; + this._d = d; + } + + Transformation.prototype = { + // @method transform(point: Point, scale?: Number): Point + // Returns a transformed point, optionally multiplied by the given scale. + // Only accepts actual `L.Point` instances, not arrays. + transform: function (point, scale) { // (Point, Number) -> Point + return this._transform(point.clone(), scale); + }, + + // destructive transform (faster) + _transform: function (point, scale) { + scale = scale || 1; + point.x = scale * (this._a * point.x + this._b); + point.y = scale * (this._c * point.y + this._d); + return point; + }, + + // @method untransform(point: Point, scale?: Number): Point + // Returns the reverse transformation of the given point, optionally divided + // by the given scale. Only accepts actual `L.Point` instances, not arrays. + untransform: function (point, scale) { + scale = scale || 1; + return new Point( + (point.x / scale - this._b) / this._a, + (point.y / scale - this._d) / this._c); + } + }; + +// factory L.transformation(a: Number, b: Number, c: Number, d: Number) + +// @factory L.transformation(a: Number, b: Number, c: Number, d: Number) +// Instantiates a Transformation object with the given coefficients. + +// @alternative +// @factory L.transformation(coefficients: Array): Transformation +// Expects an coeficients array of the form +// `[a: Number, b: Number, c: Number, d: Number]`. + + function toTransformation(a, b, c, d) { + return new Transformation(a, b, c, d); + } + + /* + * @namespace CRS + * @crs L.CRS.EPSG3857 + * + * The most common CRS for online maps, used by almost all free and commercial + * tile providers. Uses Spherical Mercator projection. Set in by default in + * Map's `crs` option. + */ + + var EPSG3857 = extend({}, Earth, { + code: 'EPSG:3857', + projection: SphericalMercator, + + transformation: (function () { + var scale = 0.5 / (Math.PI * SphericalMercator.R); + return toTransformation(scale, 0.5, -scale, 0.5); + }()) + }); + + var EPSG900913 = extend({}, EPSG3857, { + code: 'EPSG:900913' + }); + +// @namespace SVG; @section +// There are several static functions which can be called without instantiating L.SVG: + +// @function create(name: String): SVGElement +// Returns a instance of [SVGElement](https://developer.mozilla.org/docs/Web/API/SVGElement), +// corresponding to the class name passed. For example, using 'line' will return +// an instance of [SVGLineElement](https://developer.mozilla.org/docs/Web/API/SVGLineElement). + function svgCreate(name) { + return document.createElementNS('http://www.w3.org/2000/svg', name); + } + +// @function pointsToPath(rings: Point[], closed: Boolean): String +// Generates a SVG path string for multiple rings, with each ring turning +// into "M..L..L.." instructions + function pointsToPath(rings, closed) { + var str = '', + i, j, len, len2, points, p; + + for (i = 0, len = rings.length; i < len; i++) { + points = rings[i]; + + for (j = 0, len2 = points.length; j < len2; j++) { + p = points[j]; + str += (j ? 'L' : 'M') + p.x + ' ' + p.y; + } + + // closes the ring for polygons; "x" is VML syntax + str += closed ? (svg ? 'z' : 'x') : ''; + } + + // SVG complains about empty path strings + return str || 'M0 0'; + } + + /* + * @namespace Browser + * @aka L.Browser + * + * A namespace with static properties for browser/feature detection used by Leaflet internally. + * + * @example + * + * ```js + * if (L.Browser.ielt9) { + * alert('Upgrade your browser, dude!'); + * } + * ``` + */ + + var style$1 = document.documentElement.style; + +// @property ie: Boolean; `true` for all Internet Explorer versions (not Edge). + var ie = 'ActiveXObject' in window; + +// @property ielt9: Boolean; `true` for Internet Explorer versions less than 9. + var ielt9 = ie && !document.addEventListener; + +// @property edge: Boolean; `true` for the Edge web browser. + var edge = 'msLaunchUri' in navigator && !('documentMode' in document); + +// @property webkit: Boolean; +// `true` for webkit-based browsers like Chrome and Safari (including mobile versions). + var webkit = userAgentContains('webkit'); + +// @property android: Boolean +// `true` for any browser running on an Android platform. + var android = userAgentContains('android'); + +// @property android23: Boolean; `true` for browsers running on Android 2 or Android 3. + var android23 = userAgentContains('android 2') || userAgentContains('android 3'); + +// @property opera: Boolean; `true` for the Opera browser + var opera = !!window.opera; + +// @property chrome: Boolean; `true` for the Chrome browser. + var chrome = userAgentContains('chrome'); + +// @property gecko: Boolean; `true` for gecko-based browsers like Firefox. + var gecko = userAgentContains('gecko') && !webkit && !opera && !ie; + +// @property safari: Boolean; `true` for the Safari browser. + var safari = !chrome && userAgentContains('safari'); + + var phantom = userAgentContains('phantom'); + +// @property opera12: Boolean +// `true` for the Opera browser supporting CSS transforms (version 12 or later). + var opera12 = 'OTransition' in style$1; + +// @property win: Boolean; `true` when the browser is running in a Windows platform + var win = navigator.platform.indexOf('Win') === 0; + +// @property ie3d: Boolean; `true` for all Internet Explorer versions supporting CSS transforms. + var ie3d = ie && ('transition' in style$1); + +// @property webkit3d: Boolean; `true` for webkit-based browsers supporting CSS transforms. + var webkit3d = ('WebKitCSSMatrix' in window) && ('m11' in new window.WebKitCSSMatrix()) && !android23; + +// @property gecko3d: Boolean; `true` for gecko-based browsers supporting CSS transforms. + var gecko3d = 'MozPerspective' in style$1; + +// @property any3d: Boolean +// `true` for all browsers supporting CSS transforms. + var any3d = !window.L_DISABLE_3D && (ie3d || webkit3d || gecko3d) && !opera12 && !phantom; + +// @property mobile: Boolean; `true` for all browsers running in a mobile device. + var mobile = typeof orientation !== 'undefined' || userAgentContains('mobile'); + +// @property mobileWebkit: Boolean; `true` for all webkit-based browsers in a mobile device. + var mobileWebkit = mobile && webkit; + +// @property mobileWebkit3d: Boolean +// `true` for all webkit-based browsers in a mobile device supporting CSS transforms. + var mobileWebkit3d = mobile && webkit3d; + +// @property msPointer: Boolean +// `true` for browsers implementing the Microsoft touch events model (notably IE10). + var msPointer = !window.PointerEvent && window.MSPointerEvent; + +// @property pointer: Boolean +// `true` for all browsers supporting [pointer events](https://msdn.microsoft.com/en-us/library/dn433244%28v=vs.85%29.aspx). + var pointer = !!(window.PointerEvent || msPointer); + +// @property touch: Boolean +// `true` for all browsers supporting [touch events](https://developer.mozilla.org/docs/Web/API/Touch_events). +// This does not necessarily mean that the browser is running in a computer with +// a touchscreen, it only means that the browser is capable of understanding +// touch events. + var touch = !window.L_NO_TOUCH && (pointer || 'ontouchstart' in window || + (window.DocumentTouch && document instanceof window.DocumentTouch)); + +// @property mobileOpera: Boolean; `true` for the Opera browser in a mobile device. + var mobileOpera = mobile && opera; + +// @property mobileGecko: Boolean +// `true` for gecko-based browsers running in a mobile device. + var mobileGecko = mobile && gecko; + +// @property retina: Boolean +// `true` for browsers on a high-resolution "retina" screen. + var retina = (window.devicePixelRatio || (window.screen.deviceXDPI / window.screen.logicalXDPI)) > 1; + + +// @property canvas: Boolean +// `true` when the browser supports [``](https://developer.mozilla.org/docs/Web/API/Canvas_API). + var canvas = (function () { + return !!document.createElement('canvas').getContext; + }()); + +// @property svg: Boolean +// `true` when the browser supports [SVG](https://developer.mozilla.org/docs/Web/SVG). + var svg = !!(document.createElementNS && svgCreate('svg').createSVGRect); + +// @property vml: Boolean +// `true` if the browser supports [VML](https://en.wikipedia.org/wiki/Vector_Markup_Language). + var vml = !svg && (function () { + try { + var div = document.createElement('div'); + div.innerHTML = ''; + + var shape = div.firstChild; + shape.style.behavior = 'url(#default#VML)'; + + return shape && (typeof shape.adj === 'object'); + + } catch (e) { + return false; + } + }()); + + + function userAgentContains(str) { + return navigator.userAgent.toLowerCase().indexOf(str) >= 0; + } + + + var Browser = (Object.freeze || Object)({ + ie: ie, + ielt9: ielt9, + edge: edge, + webkit: webkit, + android: android, + android23: android23, + opera: opera, + chrome: chrome, + gecko: gecko, + safari: safari, + phantom: phantom, + opera12: opera12, + win: win, + ie3d: ie3d, + webkit3d: webkit3d, + gecko3d: gecko3d, + any3d: any3d, + mobile: mobile, + mobileWebkit: mobileWebkit, + mobileWebkit3d: mobileWebkit3d, + msPointer: msPointer, + pointer: pointer, + touch: touch, + mobileOpera: mobileOpera, + mobileGecko: mobileGecko, + retina: retina, + canvas: canvas, + svg: svg, + vml: vml + }); + + /* + * Extends L.DomEvent to provide touch support for Internet Explorer and Windows-based devices. + */ + + + var POINTER_DOWN = msPointer ? 'MSPointerDown' : 'pointerdown'; + var POINTER_MOVE = msPointer ? 'MSPointerMove' : 'pointermove'; + var POINTER_UP = msPointer ? 'MSPointerUp' : 'pointerup'; + var POINTER_CANCEL = msPointer ? 'MSPointerCancel' : 'pointercancel'; + var TAG_WHITE_LIST = ['INPUT', 'SELECT', 'OPTION']; + var _pointers = {}; + var _pointerDocListener = false; + +// DomEvent.DoubleTap needs to know about this + var _pointersCount = 0; + +// Provides a touch events wrapper for (ms)pointer events. +// ref http://www.w3.org/TR/pointerevents/ https://www.w3.org/Bugs/Public/show_bug.cgi?id=22890 + + function addPointerListener(obj, type, handler, id) { + if (type === 'touchstart') { + _addPointerStart(obj, handler, id); + + } else if (type === 'touchmove') { + _addPointerMove(obj, handler, id); + + } else if (type === 'touchend') { + _addPointerEnd(obj, handler, id); + } + + return this; + } + + function removePointerListener(obj, type, id) { + var handler = obj['_leaflet_' + type + id]; + + if (type === 'touchstart') { + obj.removeEventListener(POINTER_DOWN, handler, false); + + } else if (type === 'touchmove') { + obj.removeEventListener(POINTER_MOVE, handler, false); + + } else if (type === 'touchend') { + obj.removeEventListener(POINTER_UP, handler, false); + obj.removeEventListener(POINTER_CANCEL, handler, false); + } + + return this; + } + + function _addPointerStart(obj, handler, id) { + var onDown = bind(function (e) { + if (e.pointerType !== 'mouse' && e.pointerType !== e.MSPOINTER_TYPE_MOUSE && e.pointerType !== e.MSPOINTER_TYPE_MOUSE) { + // In IE11, some touch events needs to fire for form controls, or + // the controls will stop working. We keep a whitelist of tag names that + // need these events. For other target tags, we prevent default on the event. + if (TAG_WHITE_LIST.indexOf(e.target.tagName) < 0) { + preventDefault(e); + } else { + return; + } + } + + _handlePointer(e, handler); + }); + + obj['_leaflet_touchstart' + id] = onDown; + obj.addEventListener(POINTER_DOWN, onDown, false); + + // need to keep track of what pointers and how many are active to provide e.touches emulation + if (!_pointerDocListener) { + // we listen documentElement as any drags that end by moving the touch off the screen get fired there + document.documentElement.addEventListener(POINTER_DOWN, _globalPointerDown, true); + document.documentElement.addEventListener(POINTER_MOVE, _globalPointerMove, true); + document.documentElement.addEventListener(POINTER_UP, _globalPointerUp, true); + document.documentElement.addEventListener(POINTER_CANCEL, _globalPointerUp, true); + + _pointerDocListener = true; + } + } + + function _globalPointerDown(e) { + _pointers[e.pointerId] = e; + _pointersCount++; + } + + function _globalPointerMove(e) { + if (_pointers[e.pointerId]) { + _pointers[e.pointerId] = e; + } + } + + function _globalPointerUp(e) { + delete _pointers[e.pointerId]; + _pointersCount--; + } + + function _handlePointer(e, handler) { + e.touches = []; + for (var i in _pointers) { + e.touches.push(_pointers[i]); + } + e.changedTouches = [e]; + + handler(e); + } + + function _addPointerMove(obj, handler, id) { + var onMove = function (e) { + // don't fire touch moves when mouse isn't down + if ((e.pointerType === e.MSPOINTER_TYPE_MOUSE || e.pointerType === 'mouse') && e.buttons === 0) { return; } + + _handlePointer(e, handler); + }; + + obj['_leaflet_touchmove' + id] = onMove; + obj.addEventListener(POINTER_MOVE, onMove, false); + } + + function _addPointerEnd(obj, handler, id) { + var onUp = function (e) { + _handlePointer(e, handler); + }; + + obj['_leaflet_touchend' + id] = onUp; + obj.addEventListener(POINTER_UP, onUp, false); + obj.addEventListener(POINTER_CANCEL, onUp, false); + } + + /* + * Extends the event handling code with double tap support for mobile browsers. + */ + + var _touchstart = msPointer ? 'MSPointerDown' : pointer ? 'pointerdown' : 'touchstart'; + var _touchend = msPointer ? 'MSPointerUp' : pointer ? 'pointerup' : 'touchend'; + var _pre = '_leaflet_'; + +// inspired by Zepto touch code by Thomas Fuchs + function addDoubleTapListener(obj, handler, id) { + var last, touch$$1, + doubleTap = false, + delay = 250; + + function onTouchStart(e) { + var count; + + if (pointer) { + if ((!edge) || e.pointerType === 'mouse') { return; } + count = _pointersCount; + } else { + count = e.touches.length; + } + + if (count > 1) { return; } + + var now = Date.now(), + delta = now - (last || now); + + touch$$1 = e.touches ? e.touches[0] : e; + doubleTap = (delta > 0 && delta <= delay); + last = now; + } + + function onTouchEnd(e) { + if (doubleTap && !touch$$1.cancelBubble) { + if (pointer) { + if ((!edge) || e.pointerType === 'mouse') { return; } + // work around .type being readonly with MSPointer* events + var newTouch = {}, + prop, i; + + for (i in touch$$1) { + prop = touch$$1[i]; + newTouch[i] = prop && prop.bind ? prop.bind(touch$$1) : prop; + } + touch$$1 = newTouch; + } + touch$$1.type = 'dblclick'; + handler(touch$$1); + last = null; + } + } + + obj[_pre + _touchstart + id] = onTouchStart; + obj[_pre + _touchend + id] = onTouchEnd; + obj[_pre + 'dblclick' + id] = handler; + + obj.addEventListener(_touchstart, onTouchStart, false); + obj.addEventListener(_touchend, onTouchEnd, false); + + // On some platforms (notably, chrome<55 on win10 + touchscreen + mouse), + // the browser doesn't fire touchend/pointerup events but does fire + // native dblclicks. See #4127. + // Edge 14 also fires native dblclicks, but only for pointerType mouse, see #5180. + obj.addEventListener('dblclick', handler, false); + + return this; + } + + function removeDoubleTapListener(obj, id) { + var touchstart = obj[_pre + _touchstart + id], + touchend = obj[_pre + _touchend + id], + dblclick = obj[_pre + 'dblclick' + id]; + + obj.removeEventListener(_touchstart, touchstart, false); + obj.removeEventListener(_touchend, touchend, false); + if (!edge) { + obj.removeEventListener('dblclick', dblclick, false); + } + + return this; + } + + /* + * @namespace DomEvent + * Utility functions to work with the [DOM events](https://developer.mozilla.org/docs/Web/API/Event), used by Leaflet internally. + */ + +// Inspired by John Resig, Dean Edwards and YUI addEvent implementations. + +// @function on(el: HTMLElement, types: String, fn: Function, context?: Object): this +// Adds a listener function (`fn`) to a particular DOM event type of the +// element `el`. You can optionally specify the context of the listener +// (object the `this` keyword will point to). You can also pass several +// space-separated types (e.g. `'click dblclick'`). + +// @alternative +// @function on(el: HTMLElement, eventMap: Object, context?: Object): this +// Adds a set of type/listener pairs, e.g. `{click: onClick, mousemove: onMouseMove}` + function on(obj, types, fn, context) { + + if (typeof types === 'object') { + for (var type in types) { + addOne(obj, type, types[type], fn); + } + } else { + types = splitWords(types); + + for (var i = 0, len = types.length; i < len; i++) { + addOne(obj, types[i], fn, context); + } + } + + return this; + } + + var eventsKey = '_leaflet_events'; + +// @function off(el: HTMLElement, types: String, fn: Function, context?: Object): this +// Removes a previously added listener function. If no function is specified, +// it will remove all the listeners of that particular DOM event from the element. +// Note that if you passed a custom context to on, you must pass the same +// context to `off` in order to remove the listener. + +// @alternative +// @function off(el: HTMLElement, eventMap: Object, context?: Object): this +// Removes a set of type/listener pairs, e.g. `{click: onClick, mousemove: onMouseMove}` + +// @alternative +// @function off(el: HTMLElement): this +// Removes all known event listeners + function off(obj, types, fn, context) { + + if (typeof types === 'object') { + for (var type in types) { + removeOne(obj, type, types[type], fn); + } + } else if (types) { + types = splitWords(types); + + for (var i = 0, len = types.length; i < len; i++) { + removeOne(obj, types[i], fn, context); + } + } else { + for (var j in obj[eventsKey]) { + removeOne(obj, j, obj[eventsKey][j]); + } + delete obj[eventsKey]; + } + } + + function addOne(obj, type, fn, context) { + var id = type + stamp(fn) + (context ? '_' + stamp(context) : ''); + + if (obj[eventsKey] && obj[eventsKey][id]) { return this; } + + var handler = function (e) { + return fn.call(context || obj, e || window.event); + }; + + var originalHandler = handler; + + if (pointer && type.indexOf('touch') === 0) { + // Needs DomEvent.Pointer.js + addPointerListener(obj, type, handler, id); + + } else if (touch && (type === 'dblclick') && addDoubleTapListener && + !(pointer && chrome)) { + // Chrome >55 does not need the synthetic dblclicks from addDoubleTapListener + // See #5180 + addDoubleTapListener(obj, handler, id); + + } else if ('addEventListener' in obj) { + + if (type === 'mousewheel') { + obj.addEventListener('onwheel' in obj ? 'wheel' : 'mousewheel', handler, false); + + } else if ((type === 'mouseenter') || (type === 'mouseleave')) { + handler = function (e) { + e = e || window.event; + if (isExternalTarget(obj, e)) { + originalHandler(e); + } + }; + obj.addEventListener(type === 'mouseenter' ? 'mouseover' : 'mouseout', handler, false); + + } else { + if (type === 'click' && android) { + handler = function (e) { + filterClick(e, originalHandler); + }; + } + obj.addEventListener(type, handler, false); + } + + } else if ('attachEvent' in obj) { + obj.attachEvent('on' + type, handler); + } + + obj[eventsKey] = obj[eventsKey] || {}; + obj[eventsKey][id] = handler; + } + + function removeOne(obj, type, fn, context) { + + var id = type + stamp(fn) + (context ? '_' + stamp(context) : ''), + handler = obj[eventsKey] && obj[eventsKey][id]; + + if (!handler) { return this; } + + if (pointer && type.indexOf('touch') === 0) { + removePointerListener(obj, type, id); + + } else if (touch && (type === 'dblclick') && removeDoubleTapListener) { + removeDoubleTapListener(obj, id); + + } else if ('removeEventListener' in obj) { + + if (type === 'mousewheel') { + obj.removeEventListener('onwheel' in obj ? 'wheel' : 'mousewheel', handler, false); + + } else { + obj.removeEventListener( + type === 'mouseenter' ? 'mouseover' : + type === 'mouseleave' ? 'mouseout' : type, handler, false); + } + + } else if ('detachEvent' in obj) { + obj.detachEvent('on' + type, handler); + } + + obj[eventsKey][id] = null; + } + +// @function stopPropagation(ev: DOMEvent): this +// Stop the given event from propagation to parent elements. Used inside the listener functions: +// ```js +// L.DomEvent.on(div, 'click', function (ev) { +// L.DomEvent.stopPropagation(ev); +// }); +// ``` + function stopPropagation(e) { + + if (e.stopPropagation) { + e.stopPropagation(); + } else if (e.originalEvent) { // In case of Leaflet event. + e.originalEvent._stopped = true; + } else { + e.cancelBubble = true; + } + skipped(e); + + return this; + } + +// @function disableScrollPropagation(el: HTMLElement): this +// Adds `stopPropagation` to the element's `'mousewheel'` events (plus browser variants). + function disableScrollPropagation(el) { + return addOne(el, 'mousewheel', stopPropagation); + } + +// @function disableClickPropagation(el: HTMLElement): this +// Adds `stopPropagation` to the element's `'click'`, `'doubleclick'`, +// `'mousedown'` and `'touchstart'` events (plus browser variants). + function disableClickPropagation(el) { + on(el, 'mousedown touchstart dblclick', stopPropagation); + addOne(el, 'click', fakeStop); + return this; + } + +// @function preventDefault(ev: DOMEvent): this +// Prevents the default action of the DOM Event `ev` from happening (such as +// following a link in the href of the a element, or doing a POST request +// with page reload when a `
` is submitted). +// Use it inside listener functions. + function preventDefault(e) { + if (e.preventDefault) { + e.preventDefault(); + } else { + e.returnValue = false; + } + return this; + } + +// @function stop(ev): this +// Does `stopPropagation` and `preventDefault` at the same time. + function stop(e) { + preventDefault(e); + stopPropagation(e); + return this; + } + +// @function getMousePosition(ev: DOMEvent, container?: HTMLElement): Point +// Gets normalized mouse position from a DOM event relative to the +// `container` or to the whole page if not specified. + function getMousePosition(e, container) { + if (!container) { + return new Point(e.clientX, e.clientY); + } + + var rect = container.getBoundingClientRect(); + + return new Point( + e.clientX - rect.left - container.clientLeft, + e.clientY - rect.top - container.clientTop); + } + +// Chrome on Win scrolls double the pixels as in other platforms (see #4538), +// and Firefox scrolls device pixels, not CSS pixels + var wheelPxFactor = + (win && chrome) ? 2 * window.devicePixelRatio : + gecko ? window.devicePixelRatio : 1; + +// @function getWheelDelta(ev: DOMEvent): Number +// Gets normalized wheel delta from a mousewheel DOM event, in vertical +// pixels scrolled (negative if scrolling down). +// Events from pointing devices without precise scrolling are mapped to +// a best guess of 60 pixels. + function getWheelDelta(e) { + return (edge) ? e.wheelDeltaY / 2 : // Don't trust window-geometry-based delta + (e.deltaY && e.deltaMode === 0) ? -e.deltaY / wheelPxFactor : // Pixels + (e.deltaY && e.deltaMode === 1) ? -e.deltaY * 20 : // Lines + (e.deltaY && e.deltaMode === 2) ? -e.deltaY * 60 : // Pages + (e.deltaX || e.deltaZ) ? 0 : // Skip horizontal/depth wheel events + e.wheelDelta ? (e.wheelDeltaY || e.wheelDelta) / 2 : // Legacy IE pixels + (e.detail && Math.abs(e.detail) < 32765) ? -e.detail * 20 : // Legacy Moz lines + e.detail ? e.detail / -32765 * 60 : // Legacy Moz pages + 0; + } + + var skipEvents = {}; + + function fakeStop(e) { + // fakes stopPropagation by setting a special event flag, checked/reset with skipped(e) + skipEvents[e.type] = true; + } + + function skipped(e) { + var events = skipEvents[e.type]; + // reset when checking, as it's only used in map container and propagates outside of the map + skipEvents[e.type] = false; + return events; + } + +// check if element really left/entered the event target (for mouseenter/mouseleave) + function isExternalTarget(el, e) { + + var related = e.relatedTarget; + + if (!related) { return true; } + + try { + while (related && (related !== el)) { + related = related.parentNode; + } + } catch (err) { + return false; + } + return (related !== el); + } + + var lastClick; + +// this is a horrible workaround for a bug in Android where a single touch triggers two click events + function filterClick(e, handler) { + var timeStamp = (e.timeStamp || (e.originalEvent && e.originalEvent.timeStamp)), + elapsed = lastClick && (timeStamp - lastClick); + + // are they closer together than 500ms yet more than 100ms? + // Android typically triggers them ~300ms apart while multiple listeners + // on the same event should be triggered far faster; + // or check if click is simulated on the element, and if it is, reject any non-simulated events + + if ((elapsed && elapsed > 100 && elapsed < 500) || (e.target._simulatedClick && !e._simulated)) { + stop(e); + return; + } + lastClick = timeStamp; + + handler(e); + } + + + + + var DomEvent = (Object.freeze || Object)({ + on: on, + off: off, + stopPropagation: stopPropagation, + disableScrollPropagation: disableScrollPropagation, + disableClickPropagation: disableClickPropagation, + preventDefault: preventDefault, + stop: stop, + getMousePosition: getMousePosition, + getWheelDelta: getWheelDelta, + fakeStop: fakeStop, + skipped: skipped, + isExternalTarget: isExternalTarget, + addListener: on, + removeListener: off + }); + + /* + * @namespace DomUtil + * + * Utility functions to work with the [DOM](https://developer.mozilla.org/docs/Web/API/Document_Object_Model) + * tree, used by Leaflet internally. + * + * Most functions expecting or returning a `HTMLElement` also work for + * SVG elements. The only difference is that classes refer to CSS classes + * in HTML and SVG classes in SVG. + */ + + +// @property TRANSFORM: String +// Vendor-prefixed transform style name (e.g. `'webkitTransform'` for WebKit). + var TRANSFORM = testProp( + ['transform', 'WebkitTransform', 'OTransform', 'MozTransform', 'msTransform']); + +// webkitTransition comes first because some browser versions that drop vendor prefix don't do +// the same for the transitionend event, in particular the Android 4.1 stock browser + +// @property TRANSITION: String +// Vendor-prefixed transition style name. + var TRANSITION = testProp( + ['webkitTransition', 'transition', 'OTransition', 'MozTransition', 'msTransition']); + +// @property TRANSITION_END: String +// Vendor-prefixed transitionend event name. + var TRANSITION_END = + TRANSITION === 'webkitTransition' || TRANSITION === 'OTransition' ? TRANSITION + 'End' : 'transitionend'; + + +// @function get(id: String|HTMLElement): HTMLElement +// Returns an element given its DOM id, or returns the element itself +// if it was passed directly. + function get(id) { + return typeof id === 'string' ? document.getElementById(id) : id; + } + +// @function getStyle(el: HTMLElement, styleAttrib: String): String +// Returns the value for a certain style attribute on an element, +// including computed values or values set through CSS. + function getStyle(el, style) { + var value = el.style[style] || (el.currentStyle && el.currentStyle[style]); + + if ((!value || value === 'auto') && document.defaultView) { + var css = document.defaultView.getComputedStyle(el, null); + value = css ? css[style] : null; + } + return value === 'auto' ? null : value; + } + +// @function create(tagName: String, className?: String, container?: HTMLElement): HTMLElement +// Creates an HTML element with `tagName`, sets its class to `className`, and optionally appends it to `container` element. + function create$1(tagName, className, container) { + var el = document.createElement(tagName); + el.className = className || ''; + + if (container) { + container.appendChild(el); + } + return el; + } + +// @function remove(el: HTMLElement) +// Removes `el` from its parent element + function remove(el) { + var parent = el.parentNode; + if (parent) { + parent.removeChild(el); + } + } + +// @function empty(el: HTMLElement) +// Removes all of `el`'s children elements from `el` + function empty(el) { + while (el.firstChild) { + el.removeChild(el.firstChild); + } + } + +// @function toFront(el: HTMLElement) +// Makes `el` the last child of its parent, so it renders in front of the other children. + function toFront(el) { + var parent = el.parentNode; + if (parent.lastChild !== el) { + parent.appendChild(el); + } + } + +// @function toBack(el: HTMLElement) +// Makes `el` the first child of its parent, so it renders behind the other children. + function toBack(el) { + var parent = el.parentNode; + if (parent.firstChild !== el) { + parent.insertBefore(el, parent.firstChild); + } + } + +// @function hasClass(el: HTMLElement, name: String): Boolean +// Returns `true` if the element's class attribute contains `name`. + function hasClass(el, name) { + if (el.classList !== undefined) { + return el.classList.contains(name); + } + var className = getClass(el); + return className.length > 0 && new RegExp('(^|\\s)' + name + '(\\s|$)').test(className); + } + +// @function addClass(el: HTMLElement, name: String) +// Adds `name` to the element's class attribute. + function addClass(el, name) { + if (el.classList !== undefined) { + var classes = splitWords(name); + for (var i = 0, len = classes.length; i < len; i++) { + el.classList.add(classes[i]); + } + } else if (!hasClass(el, name)) { + var className = getClass(el); + setClass(el, (className ? className + ' ' : '') + name); + } + } + +// @function removeClass(el: HTMLElement, name: String) +// Removes `name` from the element's class attribute. + function removeClass(el, name) { + if (el.classList !== undefined) { + el.classList.remove(name); + } else { + setClass(el, trim((' ' + getClass(el) + ' ').replace(' ' + name + ' ', ' '))); + } + } + +// @function setClass(el: HTMLElement, name: String) +// Sets the element's class. + function setClass(el, name) { + if (el.className.baseVal === undefined) { + el.className = name; + } else { + // in case of SVG element + el.className.baseVal = name; + } + } + +// @function getClass(el: HTMLElement): String +// Returns the element's class. + function getClass(el) { + return el.className.baseVal === undefined ? el.className : el.className.baseVal; + } + +// @function setOpacity(el: HTMLElement, opacity: Number) +// Set the opacity of an element (including old IE support). +// `opacity` must be a number from `0` to `1`. + function setOpacity(el, value) { + if ('opacity' in el.style) { + el.style.opacity = value; + } else if ('filter' in el.style) { + _setOpacityIE(el, value); + } + } + + function _setOpacityIE(el, value) { + var filter = false, + filterName = 'DXImageTransform.Microsoft.Alpha'; + + // filters collection throws an error if we try to retrieve a filter that doesn't exist + try { + filter = el.filters.item(filterName); + } catch (e) { + // don't set opacity to 1 if we haven't already set an opacity, + // it isn't needed and breaks transparent pngs. + if (value === 1) { return; } + } + + value = Math.round(value * 100); + + if (filter) { + filter.Enabled = (value !== 100); + filter.Opacity = value; + } else { + el.style.filter += ' progid:' + filterName + '(opacity=' + value + ')'; + } + } + +// @function testProp(props: String[]): String|false +// Goes through the array of style names and returns the first name +// that is a valid style name for an element. If no such name is found, +// it returns false. Useful for vendor-prefixed styles like `transform`. + function testProp(props) { + var style = document.documentElement.style; + + for (var i = 0; i < props.length; i++) { + if (props[i] in style) { + return props[i]; + } + } + return false; + } + +// @function setTransform(el: HTMLElement, offset: Point, scale?: Number) +// Resets the 3D CSS transform of `el` so it is translated by `offset` pixels +// and optionally scaled by `scale`. Does not have an effect if the +// browser doesn't support 3D CSS transforms. + function setTransform(el, offset, scale) { + var pos = offset || new Point(0, 0); + + el.style[TRANSFORM] = + (ie3d ? + 'translate(' + pos.x + 'px,' + pos.y + 'px)' : + 'translate3d(' + pos.x + 'px,' + pos.y + 'px,0)') + + (scale ? ' scale(' + scale + ')' : ''); + } + +// @function setPosition(el: HTMLElement, position: Point) +// Sets the position of `el` to coordinates specified by `position`, +// using CSS translate or top/left positioning depending on the browser +// (used by Leaflet internally to position its layers). + function setPosition(el, point) { + + /*eslint-disable */ + el._leaflet_pos = point; + /*eslint-enable */ + + if (any3d) { + setTransform(el, point); + } else { + el.style.left = point.x + 'px'; + el.style.top = point.y + 'px'; + } + } + +// @function getPosition(el: HTMLElement): Point +// Returns the coordinates of an element previously positioned with setPosition. + function getPosition(el) { + // this method is only used for elements previously positioned using setPosition, + // so it's safe to cache the position for performance + + return el._leaflet_pos || new Point(0, 0); + } + +// @function disableTextSelection() +// Prevents the user from generating `selectstart` DOM events, usually generated +// when the user drags the mouse through a page with text. Used internally +// by Leaflet to override the behaviour of any click-and-drag interaction on +// the map. Affects drag interactions on the whole document. + +// @function enableTextSelection() +// Cancels the effects of a previous [`L.DomUtil.disableTextSelection`](#domutil-disabletextselection). + var disableTextSelection; + var enableTextSelection; + var _userSelect; + if ('onselectstart' in document) { + disableTextSelection = function () { + on(window, 'selectstart', preventDefault); + }; + enableTextSelection = function () { + off(window, 'selectstart', preventDefault); + }; + } else { + var userSelectProperty = testProp( + ['userSelect', 'WebkitUserSelect', 'OUserSelect', 'MozUserSelect', 'msUserSelect']); + + disableTextSelection = function () { + if (userSelectProperty) { + var style = document.documentElement.style; + _userSelect = style[userSelectProperty]; + style[userSelectProperty] = 'none'; + } + }; + enableTextSelection = function () { + if (userSelectProperty) { + document.documentElement.style[userSelectProperty] = _userSelect; + _userSelect = undefined; + } + }; + } + +// @function disableImageDrag() +// As [`L.DomUtil.disableTextSelection`](#domutil-disabletextselection), but +// for `dragstart` DOM events, usually generated when the user drags an image. + function disableImageDrag() { + on(window, 'dragstart', preventDefault); + } + +// @function enableImageDrag() +// Cancels the effects of a previous [`L.DomUtil.disableImageDrag`](#domutil-disabletextselection). + function enableImageDrag() { + off(window, 'dragstart', preventDefault); + } + + var _outlineElement; + var _outlineStyle; +// @function preventOutline(el: HTMLElement) +// Makes the [outline](https://developer.mozilla.org/docs/Web/CSS/outline) +// of the element `el` invisible. Used internally by Leaflet to prevent +// focusable elements from displaying an outline when the user performs a +// drag interaction on them. + function preventOutline(element) { + while (element.tabIndex === -1) { + element = element.parentNode; + } + if (!element.style) { return; } + restoreOutline(); + _outlineElement = element; + _outlineStyle = element.style.outline; + element.style.outline = 'none'; + on(window, 'keydown', restoreOutline); + } + +// @function restoreOutline() +// Cancels the effects of a previous [`L.DomUtil.preventOutline`](). + function restoreOutline() { + if (!_outlineElement) { return; } + _outlineElement.style.outline = _outlineStyle; + _outlineElement = undefined; + _outlineStyle = undefined; + off(window, 'keydown', restoreOutline); + } + + + var DomUtil = (Object.freeze || Object)({ + TRANSFORM: TRANSFORM, + TRANSITION: TRANSITION, + TRANSITION_END: TRANSITION_END, + get: get, + getStyle: getStyle, + create: create$1, + remove: remove, + empty: empty, + toFront: toFront, + toBack: toBack, + hasClass: hasClass, + addClass: addClass, + removeClass: removeClass, + setClass: setClass, + getClass: getClass, + setOpacity: setOpacity, + testProp: testProp, + setTransform: setTransform, + setPosition: setPosition, + getPosition: getPosition, + disableTextSelection: disableTextSelection, + enableTextSelection: enableTextSelection, + disableImageDrag: disableImageDrag, + enableImageDrag: enableImageDrag, + preventOutline: preventOutline, + restoreOutline: restoreOutline + }); + + /* + * @class PosAnimation + * @aka L.PosAnimation + * @inherits Evented + * Used internally for panning animations, utilizing CSS3 Transitions for modern browsers and a timer fallback for IE6-9. + * + * @example + * ```js + * var fx = new L.PosAnimation(); + * fx.run(el, [300, 500], 0.5); + * ``` + * + * @constructor L.PosAnimation() + * Creates a `PosAnimation` object. + * + */ + + var PosAnimation = Evented.extend({ + + // @method run(el: HTMLElement, newPos: Point, duration?: Number, easeLinearity?: Number) + // Run an animation of a given element to a new position, optionally setting + // duration in seconds (`0.25` by default) and easing linearity factor (3rd + // argument of the [cubic bezier curve](http://cubic-bezier.com/#0,0,.5,1), + // `0.5` by default). + run: function (el, newPos, duration, easeLinearity) { + this.stop(); + + this._el = el; + this._inProgress = true; + this._duration = duration || 0.25; + this._easeOutPower = 1 / Math.max(easeLinearity || 0.5, 0.2); + + this._startPos = getPosition(el); + this._offset = newPos.subtract(this._startPos); + this._startTime = +new Date(); + + // @event start: Event + // Fired when the animation starts + this.fire('start'); + + this._animate(); + }, + + // @method stop() + // Stops the animation (if currently running). + stop: function () { + if (!this._inProgress) { return; } + + this._step(true); + this._complete(); + }, + + _animate: function () { + // animation loop + this._animId = requestAnimFrame(this._animate, this); + this._step(); + }, + + _step: function (round) { + var elapsed = (+new Date()) - this._startTime, + duration = this._duration * 1000; + + if (elapsed < duration) { + this._runFrame(this._easeOut(elapsed / duration), round); + } else { + this._runFrame(1); + this._complete(); + } + }, + + _runFrame: function (progress, round) { + var pos = this._startPos.add(this._offset.multiplyBy(progress)); + if (round) { + pos._round(); + } + setPosition(this._el, pos); + + // @event step: Event + // Fired continuously during the animation. + this.fire('step'); + }, + + _complete: function () { + cancelAnimFrame(this._animId); + + this._inProgress = false; + // @event end: Event + // Fired when the animation ends. + this.fire('end'); + }, + + _easeOut: function (t) { + return 1 - Math.pow(1 - t, this._easeOutPower); + } + }); + + /* + * @class Map + * @aka L.Map + * @inherits Evented + * + * The central class of the API — it is used to create a map on a page and manipulate it. + * + * @example + * + * ```js + * // initialize the map on the "map" div with a given center and zoom + * var map = L.map('map', { + * center: [51.505, -0.09], + * zoom: 13 + * }); + * ``` + * + */ + + var Map = Evented.extend({ + + options: { + // @section Map State Options + // @option crs: CRS = L.CRS.EPSG3857 + // The [Coordinate Reference System](#crs) to use. Don't change this if you're not + // sure what it means. + crs: EPSG3857, + + // @option center: LatLng = undefined + // Initial geographic center of the map + center: undefined, + + // @option zoom: Number = undefined + // Initial map zoom level + zoom: undefined, + + // @option minZoom: Number = * + // Minimum zoom level of the map. + // If not specified and at least one `GridLayer` or `TileLayer` is in the map, + // the lowest of their `minZoom` options will be used instead. + minZoom: undefined, + + // @option maxZoom: Number = * + // Maximum zoom level of the map. + // If not specified and at least one `GridLayer` or `TileLayer` is in the map, + // the highest of their `maxZoom` options will be used instead. + maxZoom: undefined, + + // @option layers: Layer[] = [] + // Array of layers that will be added to the map initially + layers: [], + + // @option maxBounds: LatLngBounds = null + // When this option is set, the map restricts the view to the given + // geographical bounds, bouncing the user back if the user tries to pan + // outside the view. To set the restriction dynamically, use + // [`setMaxBounds`](#map-setmaxbounds) method. + maxBounds: undefined, + + // @option renderer: Renderer = * + // The default method for drawing vector layers on the map. `L.SVG` + // or `L.Canvas` by default depending on browser support. + renderer: undefined, + + + // @section Animation Options + // @option zoomAnimation: Boolean = true + // Whether the map zoom animation is enabled. By default it's enabled + // in all browsers that support CSS3 Transitions except Android. + zoomAnimation: true, + + // @option zoomAnimationThreshold: Number = 4 + // Won't animate zoom if the zoom difference exceeds this value. + zoomAnimationThreshold: 4, + + // @option fadeAnimation: Boolean = true + // Whether the tile fade animation is enabled. By default it's enabled + // in all browsers that support CSS3 Transitions except Android. + fadeAnimation: true, + + // @option markerZoomAnimation: Boolean = true + // Whether markers animate their zoom with the zoom animation, if disabled + // they will disappear for the length of the animation. By default it's + // enabled in all browsers that support CSS3 Transitions except Android. + markerZoomAnimation: true, + + // @option transform3DLimit: Number = 2^23 + // Defines the maximum size of a CSS translation transform. The default + // value should not be changed unless a web browser positions layers in + // the wrong place after doing a large `panBy`. + transform3DLimit: 8388608, // Precision limit of a 32-bit float + + // @section Interaction Options + // @option zoomSnap: Number = 1 + // Forces the map's zoom level to always be a multiple of this, particularly + // right after a [`fitBounds()`](#map-fitbounds) or a pinch-zoom. + // By default, the zoom level snaps to the nearest integer; lower values + // (e.g. `0.5` or `0.1`) allow for greater granularity. A value of `0` + // means the zoom level will not be snapped after `fitBounds` or a pinch-zoom. + zoomSnap: 1, + + // @option zoomDelta: Number = 1 + // Controls how much the map's zoom level will change after a + // [`zoomIn()`](#map-zoomin), [`zoomOut()`](#map-zoomout), pressing `+` + // or `-` on the keyboard, or using the [zoom controls](#control-zoom). + // Values smaller than `1` (e.g. `0.5`) allow for greater granularity. + zoomDelta: 1, + + // @option trackResize: Boolean = true + // Whether the map automatically handles browser window resize to update itself. + trackResize: true + }, + + initialize: function (id, options) { // (HTMLElement or String, Object) + options = setOptions(this, options); + + this._initContainer(id); + this._initLayout(); + + // hack for https://github.com/Leaflet/Leaflet/issues/1980 + this._onResize = bind(this._onResize, this); + + this._initEvents(); + + if (options.maxBounds) { + this.setMaxBounds(options.maxBounds); + } + + if (options.zoom !== undefined) { + this._zoom = this._limitZoom(options.zoom); + } + + if (options.center && options.zoom !== undefined) { + this.setView(toLatLng(options.center), options.zoom, {reset: true}); + } + + this._handlers = []; + this._layers = {}; + this._zoomBoundLayers = {}; + this._sizeChanged = true; + + this.callInitHooks(); + + // don't animate on browsers without hardware-accelerated transitions or old Android/Opera + this._zoomAnimated = TRANSITION && any3d && !mobileOpera && + this.options.zoomAnimation; + + // zoom transitions run with the same duration for all layers, so if one of transitionend events + // happens after starting zoom animation (propagating to the map pane), we know that it ended globally + if (this._zoomAnimated) { + this._createAnimProxy(); + on(this._proxy, TRANSITION_END, this._catchTransitionEnd, this); + } + + this._addLayers(this.options.layers); + }, + + + // @section Methods for modifying map state + + // @method setView(center: LatLng, zoom: Number, options?: Zoom/pan options): this + // Sets the view of the map (geographical center and zoom) with the given + // animation options. + setView: function (center, zoom, options) { + + zoom = zoom === undefined ? this._zoom : this._limitZoom(zoom); + center = this._limitCenter(toLatLng(center), zoom, this.options.maxBounds); + options = options || {}; + + this._stop(); + + if (this._loaded && !options.reset && options !== true) { + + if (options.animate !== undefined) { + options.zoom = extend({animate: options.animate}, options.zoom); + options.pan = extend({animate: options.animate, duration: options.duration}, options.pan); + } + + // try animating pan or zoom + var moved = (this._zoom !== zoom) ? + this._tryAnimatedZoom && this._tryAnimatedZoom(center, zoom, options.zoom) : + this._tryAnimatedPan(center, options.pan); + + if (moved) { + // prevent resize handler call, the view will refresh after animation anyway + clearTimeout(this._sizeTimer); + return this; + } + } + + // animation didn't start, just reset the map view + this._resetView(center, zoom); + + return this; + }, + + // @method setZoom(zoom: Number, options?: Zoom/pan options): this + // Sets the zoom of the map. + setZoom: function (zoom, options) { + if (!this._loaded) { + this._zoom = zoom; + return this; + } + return this.setView(this.getCenter(), zoom, {zoom: options}); + }, + + // @method zoomIn(delta?: Number, options?: Zoom options): this + // Increases the zoom of the map by `delta` ([`zoomDelta`](#map-zoomdelta) by default). + zoomIn: function (delta, options) { + delta = delta || (any3d ? this.options.zoomDelta : 1); + return this.setZoom(this._zoom + delta, options); + }, + + // @method zoomOut(delta?: Number, options?: Zoom options): this + // Decreases the zoom of the map by `delta` ([`zoomDelta`](#map-zoomdelta) by default). + zoomOut: function (delta, options) { + delta = delta || (any3d ? this.options.zoomDelta : 1); + return this.setZoom(this._zoom - delta, options); + }, + + // @method setZoomAround(latlng: LatLng, zoom: Number, options: Zoom options): this + // Zooms the map while keeping a specified geographical point on the map + // stationary (e.g. used internally for scroll zoom and double-click zoom). + // @alternative + // @method setZoomAround(offset: Point, zoom: Number, options: Zoom options): this + // Zooms the map while keeping a specified pixel on the map (relative to the top-left corner) stationary. + setZoomAround: function (latlng, zoom, options) { + var scale = this.getZoomScale(zoom), + viewHalf = this.getSize().divideBy(2), + containerPoint = latlng instanceof Point ? latlng : this.latLngToContainerPoint(latlng), + + centerOffset = containerPoint.subtract(viewHalf).multiplyBy(1 - 1 / scale), + newCenter = this.containerPointToLatLng(viewHalf.add(centerOffset)); + + return this.setView(newCenter, zoom, {zoom: options}); + }, + + _getBoundsCenterZoom: function (bounds, options) { + + options = options || {}; + bounds = bounds.getBounds ? bounds.getBounds() : toLatLngBounds(bounds); + + var paddingTL = toPoint(options.paddingTopLeft || options.padding || [0, 0]), + paddingBR = toPoint(options.paddingBottomRight || options.padding || [0, 0]), + + zoom = this.getBoundsZoom(bounds, false, paddingTL.add(paddingBR)); + + zoom = (typeof options.maxZoom === 'number') ? Math.min(options.maxZoom, zoom) : zoom; + + if (zoom === Infinity) { + return { + center: bounds.getCenter(), + zoom: zoom + }; + } + + var paddingOffset = paddingBR.subtract(paddingTL).divideBy(2), + + swPoint = this.project(bounds.getSouthWest(), zoom), + nePoint = this.project(bounds.getNorthEast(), zoom), + center = this.unproject(swPoint.add(nePoint).divideBy(2).add(paddingOffset), zoom); + + return { + center: center, + zoom: zoom + }; + }, + + // @method fitBounds(bounds: LatLngBounds, options?: fitBounds options): this + // Sets a map view that contains the given geographical bounds with the + // maximum zoom level possible. + fitBounds: function (bounds, options) { + + bounds = toLatLngBounds(bounds); + + if (!bounds.isValid()) { + throw new Error('Bounds are not valid.'); + } + + var target = this._getBoundsCenterZoom(bounds, options); + return this.setView(target.center, target.zoom, options); + }, + + // @method fitWorld(options?: fitBounds options): this + // Sets a map view that mostly contains the whole world with the maximum + // zoom level possible. + fitWorld: function (options) { + return this.fitBounds([[-90, -180], [90, 180]], options); + }, + + // @method panTo(latlng: LatLng, options?: Pan options): this + // Pans the map to a given center. + panTo: function (center, options) { // (LatLng) + return this.setView(center, this._zoom, {pan: options}); + }, + + // @method panBy(offset: Point, options?: Pan options): this + // Pans the map by a given number of pixels (animated). + panBy: function (offset, options) { + offset = toPoint(offset).round(); + options = options || {}; + + if (!offset.x && !offset.y) { + return this.fire('moveend'); + } + // If we pan too far, Chrome gets issues with tiles + // and makes them disappear or appear in the wrong place (slightly offset) #2602 + if (options.animate !== true && !this.getSize().contains(offset)) { + this._resetView(this.unproject(this.project(this.getCenter()).add(offset)), this.getZoom()); + return this; + } + + if (!this._panAnim) { + this._panAnim = new PosAnimation(); + + this._panAnim.on({ + 'step': this._onPanTransitionStep, + 'end': this._onPanTransitionEnd + }, this); + } + + // don't fire movestart if animating inertia + if (!options.noMoveStart) { + this.fire('movestart'); + } + + // animate pan unless animate: false specified + if (options.animate !== false) { + addClass(this._mapPane, 'leaflet-pan-anim'); + + var newPos = this._getMapPanePos().subtract(offset).round(); + this._panAnim.run(this._mapPane, newPos, options.duration || 0.25, options.easeLinearity); + } else { + this._rawPanBy(offset); + this.fire('move').fire('moveend'); + } + + return this; + }, + + // @method flyTo(latlng: LatLng, zoom?: Number, options?: Zoom/pan options): this + // Sets the view of the map (geographical center and zoom) performing a smooth + // pan-zoom animation. + flyTo: function (targetCenter, targetZoom, options) { + + options = options || {}; + if (options.animate === false || !any3d) { + return this.setView(targetCenter, targetZoom, options); + } + + this._stop(); + + var from = this.project(this.getCenter()), + to = this.project(targetCenter), + size = this.getSize(), + startZoom = this._zoom; + + targetCenter = toLatLng(targetCenter); + targetZoom = targetZoom === undefined ? startZoom : targetZoom; + + var w0 = Math.max(size.x, size.y), + w1 = w0 * this.getZoomScale(startZoom, targetZoom), + u1 = (to.distanceTo(from)) || 1, + rho = 1.42, + rho2 = rho * rho; + + function r(i) { + var s1 = i ? -1 : 1, + s2 = i ? w1 : w0, + t1 = w1 * w1 - w0 * w0 + s1 * rho2 * rho2 * u1 * u1, + b1 = 2 * s2 * rho2 * u1, + b = t1 / b1, + sq = Math.sqrt(b * b + 1) - b; + + // workaround for floating point precision bug when sq = 0, log = -Infinite, + // thus triggering an infinite loop in flyTo + var log = sq < 0.000000001 ? -18 : Math.log(sq); + + return log; + } + + function sinh(n) { return (Math.exp(n) - Math.exp(-n)) / 2; } + function cosh(n) { return (Math.exp(n) + Math.exp(-n)) / 2; } + function tanh(n) { return sinh(n) / cosh(n); } + + var r0 = r(0); + + function w(s) { return w0 * (cosh(r0) / cosh(r0 + rho * s)); } + function u(s) { return w0 * (cosh(r0) * tanh(r0 + rho * s) - sinh(r0)) / rho2; } + + function easeOut(t) { return 1 - Math.pow(1 - t, 1.5); } + + var start = Date.now(), + S = (r(1) - r0) / rho, + duration = options.duration ? 1000 * options.duration : 1000 * S * 0.8; + + function frame() { + var t = (Date.now() - start) / duration, + s = easeOut(t) * S; + + if (t <= 1) { + this._flyToFrame = requestAnimFrame(frame, this); + + this._move( + this.unproject(from.add(to.subtract(from).multiplyBy(u(s) / u1)), startZoom), + this.getScaleZoom(w0 / w(s), startZoom), + {flyTo: true}); + + } else { + this + ._move(targetCenter, targetZoom) + ._moveEnd(true); + } + } + + this._moveStart(true); + + frame.call(this); + return this; + }, + + // @method flyToBounds(bounds: LatLngBounds, options?: fitBounds options): this + // Sets the view of the map with a smooth animation like [`flyTo`](#map-flyto), + // but takes a bounds parameter like [`fitBounds`](#map-fitbounds). + flyToBounds: function (bounds, options) { + var target = this._getBoundsCenterZoom(bounds, options); + return this.flyTo(target.center, target.zoom, options); + }, + + // @method setMaxBounds(bounds: Bounds): this + // Restricts the map view to the given bounds (see the [maxBounds](#map-maxbounds) option). + setMaxBounds: function (bounds) { + bounds = toLatLngBounds(bounds); + + if (!bounds.isValid()) { + this.options.maxBounds = null; + return this.off('moveend', this._panInsideMaxBounds); + } else if (this.options.maxBounds) { + this.off('moveend', this._panInsideMaxBounds); + } + + this.options.maxBounds = bounds; + + if (this._loaded) { + this._panInsideMaxBounds(); + } + + return this.on('moveend', this._panInsideMaxBounds); + }, + + // @method setMinZoom(zoom: Number): this + // Sets the lower limit for the available zoom levels (see the [minZoom](#map-minzoom) option). + setMinZoom: function (zoom) { + this.options.minZoom = zoom; + + if (this._loaded && this.getZoom() < this.options.minZoom) { + return this.setZoom(zoom); + } + + return this; + }, + + // @method setMaxZoom(zoom: Number): this + // Sets the upper limit for the available zoom levels (see the [maxZoom](#map-maxzoom) option). + setMaxZoom: function (zoom) { + this.options.maxZoom = zoom; + + if (this._loaded && (this.getZoom() > this.options.maxZoom)) { + return this.setZoom(zoom); + } + + return this; + }, + + // @method panInsideBounds(bounds: LatLngBounds, options?: Pan options): this + // Pans the map to the closest view that would lie inside the given bounds (if it's not already), controlling the animation using the options specific, if any. + panInsideBounds: function (bounds, options) { + this._enforcingBounds = true; + var center = this.getCenter(), + newCenter = this._limitCenter(center, this._zoom, toLatLngBounds(bounds)); + + if (!center.equals(newCenter)) { + this.panTo(newCenter, options); + } + + this._enforcingBounds = false; + return this; + }, + + // @method invalidateSize(options: Zoom/Pan options): this + // Checks if the map container size changed and updates the map if so — + // call it after you've changed the map size dynamically, also animating + // pan by default. If `options.pan` is `false`, panning will not occur. + // If `options.debounceMoveend` is `true`, it will delay `moveend` event so + // that it doesn't happen often even if the method is called many + // times in a row. + + // @alternative + // @method invalidateSize(animate: Boolean): this + // Checks if the map container size changed and updates the map if so — + // call it after you've changed the map size dynamically, also animating + // pan by default. + invalidateSize: function (options) { + if (!this._loaded) { return this; } + + options = extend({ + animate: false, + pan: true + }, options === true ? {animate: true} : options); + + var oldSize = this.getSize(); + this._sizeChanged = true; + this._lastCenter = null; + + var newSize = this.getSize(), + oldCenter = oldSize.divideBy(2).round(), + newCenter = newSize.divideBy(2).round(), + offset = oldCenter.subtract(newCenter); + + if (!offset.x && !offset.y) { return this; } + + if (options.animate && options.pan) { + this.panBy(offset); + + } else { + if (options.pan) { + this._rawPanBy(offset); + } + + this.fire('move'); + + if (options.debounceMoveend) { + clearTimeout(this._sizeTimer); + this._sizeTimer = setTimeout(bind(this.fire, this, 'moveend'), 200); + } else { + this.fire('moveend'); + } + } + + // @section Map state change events + // @event resize: ResizeEvent + // Fired when the map is resized. + return this.fire('resize', { + oldSize: oldSize, + newSize: newSize + }); + }, + + // @section Methods for modifying map state + // @method stop(): this + // Stops the currently running `panTo` or `flyTo` animation, if any. + stop: function () { + this.setZoom(this._limitZoom(this._zoom)); + if (!this.options.zoomSnap) { + this.fire('viewreset'); + } + return this._stop(); + }, + + // @section Geolocation methods + // @method locate(options?: Locate options): this + // Tries to locate the user using the Geolocation API, firing a [`locationfound`](#map-locationfound) + // event with location data on success or a [`locationerror`](#map-locationerror) event on failure, + // and optionally sets the map view to the user's location with respect to + // detection accuracy (or to the world view if geolocation failed). + // Note that, if your page doesn't use HTTPS, this method will fail in + // modern browsers ([Chrome 50 and newer](https://sites.google.com/a/chromium.org/dev/Home/chromium-security/deprecating-powerful-features-on-insecure-origins)) + // See `Locate options` for more details. + locate: function (options) { + + options = this._locateOptions = extend({ + timeout: 10000, + watch: false + // setView: false + // maxZoom: + // maximumAge: 0 + // enableHighAccuracy: false + }, options); + + if (!('geolocation' in navigator)) { + this._handleGeolocationError({ + code: 0, + message: 'Geolocation not supported.' + }); + return this; + } + + var onResponse = bind(this._handleGeolocationResponse, this), + onError = bind(this._handleGeolocationError, this); + + if (options.watch) { + this._locationWatchId = + navigator.geolocation.watchPosition(onResponse, onError, options); + } else { + navigator.geolocation.getCurrentPosition(onResponse, onError, options); + } + return this; + }, + + // @method stopLocate(): this + // Stops watching location previously initiated by `map.locate({watch: true})` + // and aborts resetting the map view if map.locate was called with + // `{setView: true}`. + stopLocate: function () { + if (navigator.geolocation && navigator.geolocation.clearWatch) { + navigator.geolocation.clearWatch(this._locationWatchId); + } + if (this._locateOptions) { + this._locateOptions.setView = false; + } + return this; + }, + + _handleGeolocationError: function (error) { + var c = error.code, + message = error.message || + (c === 1 ? 'permission denied' : + (c === 2 ? 'position unavailable' : 'timeout')); + + if (this._locateOptions.setView && !this._loaded) { + this.fitWorld(); + } + + // @section Location events + // @event locationerror: ErrorEvent + // Fired when geolocation (using the [`locate`](#map-locate) method) failed. + this.fire('locationerror', { + code: c, + message: 'Geolocation error: ' + message + '.' + }); + }, + + _handleGeolocationResponse: function (pos) { + var lat = pos.coords.latitude, + lng = pos.coords.longitude, + latlng = new LatLng(lat, lng), + bounds = latlng.toBounds(pos.coords.accuracy), + options = this._locateOptions; + + if (options.setView) { + var zoom = this.getBoundsZoom(bounds); + this.setView(latlng, options.maxZoom ? Math.min(zoom, options.maxZoom) : zoom); + } + + var data = { + latlng: latlng, + bounds: bounds, + timestamp: pos.timestamp + }; + + for (var i in pos.coords) { + if (typeof pos.coords[i] === 'number') { + data[i] = pos.coords[i]; + } + } + + // @event locationfound: LocationEvent + // Fired when geolocation (using the [`locate`](#map-locate) method) + // went successfully. + this.fire('locationfound', data); + }, + + // TODO handler.addTo + // TODO Appropiate docs section? + // @section Other Methods + // @method addHandler(name: String, HandlerClass: Function): this + // Adds a new `Handler` to the map, given its name and constructor function. + addHandler: function (name, HandlerClass) { + if (!HandlerClass) { return this; } + + var handler = this[name] = new HandlerClass(this); + + this._handlers.push(handler); + + if (this.options[name]) { + handler.enable(); + } + + return this; + }, + + // @method remove(): this + // Destroys the map and clears all related event listeners. + remove: function () { + + this._initEvents(true); + + if (this._containerId !== this._container._leaflet_id) { + throw new Error('Map container is being reused by another instance'); + } + + try { + // throws error in IE6-8 + delete this._container._leaflet_id; + delete this._containerId; + } catch (e) { + /*eslint-disable */ + this._container._leaflet_id = undefined; + /*eslint-enable */ + this._containerId = undefined; + } + + remove(this._mapPane); + + if (this._clearControlPos) { + this._clearControlPos(); + } + + this._clearHandlers(); + + if (this._loaded) { + // @section Map state change events + // @event unload: Event + // Fired when the map is destroyed with [remove](#map-remove) method. + this.fire('unload'); + } + + var i; + for (i in this._layers) { + this._layers[i].remove(); + } + for (i in this._panes) { + remove(this._panes[i]); + } + + this._layers = []; + this._panes = []; + delete this._mapPane; + delete this._renderer; + + return this; + }, + + // @section Other Methods + // @method createPane(name: String, container?: HTMLElement): HTMLElement + // Creates a new [map pane](#map-pane) with the given name if it doesn't exist already, + // then returns it. The pane is created as a child of `container`, or + // as a child of the main map pane if not set. + createPane: function (name, container) { + var className = 'leaflet-pane' + (name ? ' leaflet-' + name.replace('Pane', '') + '-pane' : ''), + pane = create$1('div', className, container || this._mapPane); + + if (name) { + this._panes[name] = pane; + } + return pane; + }, + + // @section Methods for Getting Map State + + // @method getCenter(): LatLng + // Returns the geographical center of the map view + getCenter: function () { + this._checkIfLoaded(); + + if (this._lastCenter && !this._moved()) { + return this._lastCenter; + } + return this.layerPointToLatLng(this._getCenterLayerPoint()); + }, + + // @method getZoom(): Number + // Returns the current zoom level of the map view + getZoom: function () { + return this._zoom; + }, + + // @method getBounds(): LatLngBounds + // Returns the geographical bounds visible in the current map view + getBounds: function () { + var bounds = this.getPixelBounds(), + sw = this.unproject(bounds.getBottomLeft()), + ne = this.unproject(bounds.getTopRight()); + + return new LatLngBounds(sw, ne); + }, + + // @method getMinZoom(): Number + // Returns the minimum zoom level of the map (if set in the `minZoom` option of the map or of any layers), or `0` by default. + getMinZoom: function () { + return this.options.minZoom === undefined ? this._layersMinZoom || 0 : this.options.minZoom; + }, + + // @method getMaxZoom(): Number + // Returns the maximum zoom level of the map (if set in the `maxZoom` option of the map or of any layers). + getMaxZoom: function () { + return this.options.maxZoom === undefined ? + (this._layersMaxZoom === undefined ? Infinity : this._layersMaxZoom) : + this.options.maxZoom; + }, + + // @method getBoundsZoom(bounds: LatLngBounds, inside?: Boolean): Number + // Returns the maximum zoom level on which the given bounds fit to the map + // view in its entirety. If `inside` (optional) is set to `true`, the method + // instead returns the minimum zoom level on which the map view fits into + // the given bounds in its entirety. + getBoundsZoom: function (bounds, inside, padding) { // (LatLngBounds[, Boolean, Point]) -> Number + bounds = toLatLngBounds(bounds); + padding = toPoint(padding || [0, 0]); + + var zoom = this.getZoom() || 0, + min = this.getMinZoom(), + max = this.getMaxZoom(), + nw = bounds.getNorthWest(), + se = bounds.getSouthEast(), + size = this.getSize().subtract(padding), + boundsSize = toBounds(this.project(se, zoom), this.project(nw, zoom)).getSize(), + snap = any3d ? this.options.zoomSnap : 1, + scalex = size.x / boundsSize.x, + scaley = size.y / boundsSize.y, + scale = inside ? Math.max(scalex, scaley) : Math.min(scalex, scaley); + + zoom = this.getScaleZoom(scale, zoom); + + if (snap) { + zoom = Math.round(zoom / (snap / 100)) * (snap / 100); // don't jump if within 1% of a snap level + zoom = inside ? Math.ceil(zoom / snap) * snap : Math.floor(zoom / snap) * snap; + } + + return Math.max(min, Math.min(max, zoom)); + }, + + // @method getSize(): Point + // Returns the current size of the map container (in pixels). + getSize: function () { + if (!this._size || this._sizeChanged) { + this._size = new Point( + this._container.clientWidth || 0, + this._container.clientHeight || 0); + + this._sizeChanged = false; + } + return this._size.clone(); + }, + + // @method getPixelBounds(): Bounds + // Returns the bounds of the current map view in projected pixel + // coordinates (sometimes useful in layer and overlay implementations). + getPixelBounds: function (center, zoom) { + var topLeftPoint = this._getTopLeftPoint(center, zoom); + return new Bounds(topLeftPoint, topLeftPoint.add(this.getSize())); + }, + + // TODO: Check semantics - isn't the pixel origin the 0,0 coord relative to + // the map pane? "left point of the map layer" can be confusing, specially + // since there can be negative offsets. + // @method getPixelOrigin(): Point + // Returns the projected pixel coordinates of the top left point of + // the map layer (useful in custom layer and overlay implementations). + getPixelOrigin: function () { + this._checkIfLoaded(); + return this._pixelOrigin; + }, + + // @method getPixelWorldBounds(zoom?: Number): Bounds + // Returns the world's bounds in pixel coordinates for zoom level `zoom`. + // If `zoom` is omitted, the map's current zoom level is used. + getPixelWorldBounds: function (zoom) { + return this.options.crs.getProjectedBounds(zoom === undefined ? this.getZoom() : zoom); + }, + + // @section Other Methods + + // @method getPane(pane: String|HTMLElement): HTMLElement + // Returns a [map pane](#map-pane), given its name or its HTML element (its identity). + getPane: function (pane) { + return typeof pane === 'string' ? this._panes[pane] : pane; + }, + + // @method getPanes(): Object + // Returns a plain object containing the names of all [panes](#map-pane) as keys and + // the panes as values. + getPanes: function () { + return this._panes; + }, + + // @method getContainer: HTMLElement + // Returns the HTML element that contains the map. + getContainer: function () { + return this._container; + }, + + + // @section Conversion Methods + + // @method getZoomScale(toZoom: Number, fromZoom: Number): Number + // Returns the scale factor to be applied to a map transition from zoom level + // `fromZoom` to `toZoom`. Used internally to help with zoom animations. + getZoomScale: function (toZoom, fromZoom) { + // TODO replace with universal implementation after refactoring projections + var crs = this.options.crs; + fromZoom = fromZoom === undefined ? this._zoom : fromZoom; + return crs.scale(toZoom) / crs.scale(fromZoom); + }, + + // @method getScaleZoom(scale: Number, fromZoom: Number): Number + // Returns the zoom level that the map would end up at, if it is at `fromZoom` + // level and everything is scaled by a factor of `scale`. Inverse of + // [`getZoomScale`](#map-getZoomScale). + getScaleZoom: function (scale, fromZoom) { + var crs = this.options.crs; + fromZoom = fromZoom === undefined ? this._zoom : fromZoom; + var zoom = crs.zoom(scale * crs.scale(fromZoom)); + return isNaN(zoom) ? Infinity : zoom; + }, + + // @method project(latlng: LatLng, zoom: Number): Point + // Projects a geographical coordinate `LatLng` according to the projection + // of the map's CRS, then scales it according to `zoom` and the CRS's + // `Transformation`. The result is pixel coordinate relative to + // the CRS origin. + project: function (latlng, zoom) { + zoom = zoom === undefined ? this._zoom : zoom; + return this.options.crs.latLngToPoint(toLatLng(latlng), zoom); + }, + + // @method unproject(point: Point, zoom: Number): LatLng + // Inverse of [`project`](#map-project). + unproject: function (point, zoom) { + zoom = zoom === undefined ? this._zoom : zoom; + return this.options.crs.pointToLatLng(toPoint(point), zoom); + }, + + // @method layerPointToLatLng(point: Point): LatLng + // Given a pixel coordinate relative to the [origin pixel](#map-getpixelorigin), + // returns the corresponding geographical coordinate (for the current zoom level). + layerPointToLatLng: function (point) { + var projectedPoint = toPoint(point).add(this.getPixelOrigin()); + return this.unproject(projectedPoint); + }, + + // @method latLngToLayerPoint(latlng: LatLng): Point + // Given a geographical coordinate, returns the corresponding pixel coordinate + // relative to the [origin pixel](#map-getpixelorigin). + latLngToLayerPoint: function (latlng) { + var projectedPoint = this.project(toLatLng(latlng))._round(); + return projectedPoint._subtract(this.getPixelOrigin()); + }, + + // @method wrapLatLng(latlng: LatLng): LatLng + // Returns a `LatLng` where `lat` and `lng` has been wrapped according to the + // map's CRS's `wrapLat` and `wrapLng` properties, if they are outside the + // CRS's bounds. + // By default this means longitude is wrapped around the dateline so its + // value is between -180 and +180 degrees. + wrapLatLng: function (latlng) { + return this.options.crs.wrapLatLng(toLatLng(latlng)); + }, + + // @method wrapLatLngBounds(bounds: LatLngBounds): LatLngBounds + // Returns a `LatLngBounds` with the same size as the given one, ensuring that + // its center is within the CRS's bounds. + // By default this means the center longitude is wrapped around the dateline so its + // value is between -180 and +180 degrees, and the majority of the bounds + // overlaps the CRS's bounds. + wrapLatLngBounds: function (latlng) { + return this.options.crs.wrapLatLngBounds(toLatLngBounds(latlng)); + }, + + // @method distance(latlng1: LatLng, latlng2: LatLng): Number + // Returns the distance between two geographical coordinates according to + // the map's CRS. By default this measures distance in meters. + distance: function (latlng1, latlng2) { + return this.options.crs.distance(toLatLng(latlng1), toLatLng(latlng2)); + }, + + // @method containerPointToLayerPoint(point: Point): Point + // Given a pixel coordinate relative to the map container, returns the corresponding + // pixel coordinate relative to the [origin pixel](#map-getpixelorigin). + containerPointToLayerPoint: function (point) { // (Point) + return toPoint(point).subtract(this._getMapPanePos()); + }, + + // @method layerPointToContainerPoint(point: Point): Point + // Given a pixel coordinate relative to the [origin pixel](#map-getpixelorigin), + // returns the corresponding pixel coordinate relative to the map container. + layerPointToContainerPoint: function (point) { // (Point) + return toPoint(point).add(this._getMapPanePos()); + }, + + // @method containerPointToLatLng(point: Point): LatLng + // Given a pixel coordinate relative to the map container, returns + // the corresponding geographical coordinate (for the current zoom level). + containerPointToLatLng: function (point) { + var layerPoint = this.containerPointToLayerPoint(toPoint(point)); + return this.layerPointToLatLng(layerPoint); + }, + + // @method latLngToContainerPoint(latlng: LatLng): Point + // Given a geographical coordinate, returns the corresponding pixel coordinate + // relative to the map container. + latLngToContainerPoint: function (latlng) { + return this.layerPointToContainerPoint(this.latLngToLayerPoint(toLatLng(latlng))); + }, + + // @method mouseEventToContainerPoint(ev: MouseEvent): Point + // Given a MouseEvent object, returns the pixel coordinate relative to the + // map container where the event took place. + mouseEventToContainerPoint: function (e) { + return getMousePosition(e, this._container); + }, + + // @method mouseEventToLayerPoint(ev: MouseEvent): Point + // Given a MouseEvent object, returns the pixel coordinate relative to + // the [origin pixel](#map-getpixelorigin) where the event took place. + mouseEventToLayerPoint: function (e) { + return this.containerPointToLayerPoint(this.mouseEventToContainerPoint(e)); + }, + + // @method mouseEventToLatLng(ev: MouseEvent): LatLng + // Given a MouseEvent object, returns geographical coordinate where the + // event took place. + mouseEventToLatLng: function (e) { // (MouseEvent) + return this.layerPointToLatLng(this.mouseEventToLayerPoint(e)); + }, + + + // map initialization methods + + _initContainer: function (id) { + var container = this._container = get(id); + + if (!container) { + throw new Error('Map container not found.'); + } else if (container._leaflet_id) { + throw new Error('Map container is already initialized.'); + } + + on(container, 'scroll', this._onScroll, this); + this._containerId = stamp(container); + }, + + _initLayout: function () { + var container = this._container; + + this._fadeAnimated = this.options.fadeAnimation && any3d; + + addClass(container, 'leaflet-container' + + (touch ? ' leaflet-touch' : '') + + (retina ? ' leaflet-retina' : '') + + (ielt9 ? ' leaflet-oldie' : '') + + (safari ? ' leaflet-safari' : '') + + (this._fadeAnimated ? ' leaflet-fade-anim' : '')); + + var position = getStyle(container, 'position'); + + if (position !== 'absolute' && position !== 'relative' && position !== 'fixed') { + container.style.position = 'relative'; + } + + this._initPanes(); + + if (this._initControlPos) { + this._initControlPos(); + } + }, + + _initPanes: function () { + var panes = this._panes = {}; + this._paneRenderers = {}; + + // @section + // + // Panes are DOM elements used to control the ordering of layers on the map. You + // can access panes with [`map.getPane`](#map-getpane) or + // [`map.getPanes`](#map-getpanes) methods. New panes can be created with the + // [`map.createPane`](#map-createpane) method. + // + // Every map has the following default panes that differ only in zIndex. + // + // @pane mapPane: HTMLElement = 'auto' + // Pane that contains all other map panes + + this._mapPane = this.createPane('mapPane', this._container); + setPosition(this._mapPane, new Point(0, 0)); + + // @pane tilePane: HTMLElement = 200 + // Pane for `GridLayer`s and `TileLayer`s + this.createPane('tilePane'); + // @pane overlayPane: HTMLElement = 400 + // Pane for vector overlays (`Path`s), like `Polyline`s and `Polygon`s + this.createPane('shadowPane'); + // @pane shadowPane: HTMLElement = 500 + // Pane for overlay shadows (e.g. `Marker` shadows) + this.createPane('overlayPane'); + // @pane markerPane: HTMLElement = 600 + // Pane for `Icon`s of `Marker`s + this.createPane('markerPane'); + // @pane tooltipPane: HTMLElement = 650 + // Pane for tooltip. + this.createPane('tooltipPane'); + // @pane popupPane: HTMLElement = 700 + // Pane for `Popup`s. + this.createPane('popupPane'); + + if (!this.options.markerZoomAnimation) { + addClass(panes.markerPane, 'leaflet-zoom-hide'); + addClass(panes.shadowPane, 'leaflet-zoom-hide'); + } + }, + + + // private methods that modify map state + + // @section Map state change events + _resetView: function (center, zoom) { + setPosition(this._mapPane, new Point(0, 0)); + + var loading = !this._loaded; + this._loaded = true; + zoom = this._limitZoom(zoom); + + this.fire('viewprereset'); + + var zoomChanged = this._zoom !== zoom; + this + ._moveStart(zoomChanged) + ._move(center, zoom) + ._moveEnd(zoomChanged); + + // @event viewreset: Event + // Fired when the map needs to redraw its content (this usually happens + // on map zoom or load). Very useful for creating custom overlays. + this.fire('viewreset'); + + // @event load: Event + // Fired when the map is initialized (when its center and zoom are set + // for the first time). + if (loading) { + this.fire('load'); + } + }, + + _moveStart: function (zoomChanged) { + // @event zoomstart: Event + // Fired when the map zoom is about to change (e.g. before zoom animation). + // @event movestart: Event + // Fired when the view of the map starts changing (e.g. user starts dragging the map). + if (zoomChanged) { + this.fire('zoomstart'); + } + return this.fire('movestart'); + }, + + _move: function (center, zoom, data) { + if (zoom === undefined) { + zoom = this._zoom; + } + var zoomChanged = this._zoom !== zoom; + + this._zoom = zoom; + this._lastCenter = center; + this._pixelOrigin = this._getNewPixelOrigin(center); + + // @event zoom: Event + // Fired repeatedly during any change in zoom level, including zoom + // and fly animations. + if (zoomChanged || (data && data.pinch)) { // Always fire 'zoom' if pinching because #3530 + this.fire('zoom', data); + } + + // @event move: Event + // Fired repeatedly during any movement of the map, including pan and + // fly animations. + return this.fire('move', data); + }, + + _moveEnd: function (zoomChanged) { + // @event zoomend: Event + // Fired when the map has changed, after any animations. + if (zoomChanged) { + this.fire('zoomend'); + } + + // @event moveend: Event + // Fired when the center of the map stops changing (e.g. user stopped + // dragging the map). + return this.fire('moveend'); + }, + + _stop: function () { + cancelAnimFrame(this._flyToFrame); + if (this._panAnim) { + this._panAnim.stop(); + } + return this; + }, + + _rawPanBy: function (offset) { + setPosition(this._mapPane, this._getMapPanePos().subtract(offset)); + }, + + _getZoomSpan: function () { + return this.getMaxZoom() - this.getMinZoom(); + }, + + _panInsideMaxBounds: function () { + if (!this._enforcingBounds) { + this.panInsideBounds(this.options.maxBounds); + } + }, + + _checkIfLoaded: function () { + if (!this._loaded) { + throw new Error('Set map center and zoom first.'); + } + }, + + // DOM event handling + + // @section Interaction events + _initEvents: function (remove$$1) { + this._targets = {}; + this._targets[stamp(this._container)] = this; + + var onOff = remove$$1 ? off : on; + + // @event click: MouseEvent + // Fired when the user clicks (or taps) the map. + // @event dblclick: MouseEvent + // Fired when the user double-clicks (or double-taps) the map. + // @event mousedown: MouseEvent + // Fired when the user pushes the mouse button on the map. + // @event mouseup: MouseEvent + // Fired when the user releases the mouse button on the map. + // @event mouseover: MouseEvent + // Fired when the mouse enters the map. + // @event mouseout: MouseEvent + // Fired when the mouse leaves the map. + // @event mousemove: MouseEvent + // Fired while the mouse moves over the map. + // @event contextmenu: MouseEvent + // Fired when the user pushes the right mouse button on the map, prevents + // default browser context menu from showing if there are listeners on + // this event. Also fired on mobile when the user holds a single touch + // for a second (also called long press). + // @event keypress: KeyboardEvent + // Fired when the user presses a key from the keyboard while the map is focused. + onOff(this._container, 'click dblclick mousedown mouseup ' + + 'mouseover mouseout mousemove contextmenu keypress', this._handleDOMEvent, this); + + if (this.options.trackResize) { + onOff(window, 'resize', this._onResize, this); + } + + if (any3d && this.options.transform3DLimit) { + (remove$$1 ? this.off : this.on).call(this, 'moveend', this._onMoveEnd); + } + }, + + _onResize: function () { + cancelAnimFrame(this._resizeRequest); + this._resizeRequest = requestAnimFrame( + function () { this.invalidateSize({debounceMoveend: true}); }, this); + }, + + _onScroll: function () { + this._container.scrollTop = 0; + this._container.scrollLeft = 0; + }, + + _onMoveEnd: function () { + var pos = this._getMapPanePos(); + if (Math.max(Math.abs(pos.x), Math.abs(pos.y)) >= this.options.transform3DLimit) { + // https://bugzilla.mozilla.org/show_bug.cgi?id=1203873 but Webkit also have + // a pixel offset on very high values, see: http://jsfiddle.net/dg6r5hhb/ + this._resetView(this.getCenter(), this.getZoom()); + } + }, + + _findEventTargets: function (e, type) { + var targets = [], + target, + isHover = type === 'mouseout' || type === 'mouseover', + src = e.target || e.srcElement, + dragging = false; + + while (src) { + target = this._targets[stamp(src)]; + if (target && (type === 'click' || type === 'preclick') && !e._simulated && this._draggableMoved(target)) { + // Prevent firing click after you just dragged an object. + dragging = true; + break; + } + if (target && target.listens(type, true)) { + if (isHover && !isExternalTarget(src, e)) { break; } + targets.push(target); + if (isHover) { break; } + } + if (src === this._container) { break; } + src = src.parentNode; + } + if (!targets.length && !dragging && !isHover && isExternalTarget(src, e)) { + targets = [this]; + } + return targets; + }, + + _handleDOMEvent: function (e) { + if (!this._loaded || skipped(e)) { return; } + + var type = e.type; + + if (type === 'mousedown' || type === 'keypress') { + // prevents outline when clicking on keyboard-focusable element + preventOutline(e.target || e.srcElement); + } + + this._fireDOMEvent(e, type); + }, + + _mouseEvents: ['click', 'dblclick', 'mouseover', 'mouseout', 'contextmenu'], + + _fireDOMEvent: function (e, type, targets) { + + if (e.type === 'click') { + // Fire a synthetic 'preclick' event which propagates up (mainly for closing popups). + // @event preclick: MouseEvent + // Fired before mouse click on the map (sometimes useful when you + // want something to happen on click before any existing click + // handlers start running). + var synth = extend({}, e); + synth.type = 'preclick'; + this._fireDOMEvent(synth, synth.type, targets); + } + + if (e._stopped) { return; } + + // Find the layer the event is propagating from and its parents. + targets = (targets || []).concat(this._findEventTargets(e, type)); + + if (!targets.length) { return; } + + var target = targets[0]; + if (type === 'contextmenu' && target.listens(type, true)) { + preventDefault(e); + } + + var data = { + originalEvent: e + }; + + if (e.type !== 'keypress') { + var isMarker = (target.options && 'icon' in target.options); + data.containerPoint = isMarker ? + this.latLngToContainerPoint(target.getLatLng()) : this.mouseEventToContainerPoint(e); + data.layerPoint = this.containerPointToLayerPoint(data.containerPoint); + data.latlng = isMarker ? target.getLatLng() : this.layerPointToLatLng(data.layerPoint); + } + + for (var i = 0; i < targets.length; i++) { + targets[i].fire(type, data, true); + if (data.originalEvent._stopped || + (targets[i].options.bubblingMouseEvents === false && indexOf(this._mouseEvents, type) !== -1)) { return; } + } + }, + + _draggableMoved: function (obj) { + obj = obj.dragging && obj.dragging.enabled() ? obj : this; + return (obj.dragging && obj.dragging.moved()) || (this.boxZoom && this.boxZoom.moved()); + }, + + _clearHandlers: function () { + for (var i = 0, len = this._handlers.length; i < len; i++) { + this._handlers[i].disable(); + } + }, + + // @section Other Methods + + // @method whenReady(fn: Function, context?: Object): this + // Runs the given function `fn` when the map gets initialized with + // a view (center and zoom) and at least one layer, or immediately + // if it's already initialized, optionally passing a function context. + whenReady: function (callback, context) { + if (this._loaded) { + callback.call(context || this, {target: this}); + } else { + this.on('load', callback, context); + } + return this; + }, + + + // private methods for getting map state + + _getMapPanePos: function () { + return getPosition(this._mapPane) || new Point(0, 0); + }, + + _moved: function () { + var pos = this._getMapPanePos(); + return pos && !pos.equals([0, 0]); + }, + + _getTopLeftPoint: function (center, zoom) { + var pixelOrigin = center && zoom !== undefined ? + this._getNewPixelOrigin(center, zoom) : + this.getPixelOrigin(); + return pixelOrigin.subtract(this._getMapPanePos()); + }, + + _getNewPixelOrigin: function (center, zoom) { + var viewHalf = this.getSize()._divideBy(2); + return this.project(center, zoom)._subtract(viewHalf)._add(this._getMapPanePos())._round(); + }, + + _latLngToNewLayerPoint: function (latlng, zoom, center) { + var topLeft = this._getNewPixelOrigin(center, zoom); + return this.project(latlng, zoom)._subtract(topLeft); + }, + + _latLngBoundsToNewLayerBounds: function (latLngBounds, zoom, center) { + var topLeft = this._getNewPixelOrigin(center, zoom); + return toBounds([ + this.project(latLngBounds.getSouthWest(), zoom)._subtract(topLeft), + this.project(latLngBounds.getNorthWest(), zoom)._subtract(topLeft), + this.project(latLngBounds.getSouthEast(), zoom)._subtract(topLeft), + this.project(latLngBounds.getNorthEast(), zoom)._subtract(topLeft) + ]); + }, + + // layer point of the current center + _getCenterLayerPoint: function () { + return this.containerPointToLayerPoint(this.getSize()._divideBy(2)); + }, + + // offset of the specified place to the current center in pixels + _getCenterOffset: function (latlng) { + return this.latLngToLayerPoint(latlng).subtract(this._getCenterLayerPoint()); + }, + + // adjust center for view to get inside bounds + _limitCenter: function (center, zoom, bounds) { + + if (!bounds) { return center; } + + var centerPoint = this.project(center, zoom), + viewHalf = this.getSize().divideBy(2), + viewBounds = new Bounds(centerPoint.subtract(viewHalf), centerPoint.add(viewHalf)), + offset = this._getBoundsOffset(viewBounds, bounds, zoom); + + // If offset is less than a pixel, ignore. + // This prevents unstable projections from getting into + // an infinite loop of tiny offsets. + if (offset.round().equals([0, 0])) { + return center; + } + + return this.unproject(centerPoint.add(offset), zoom); + }, + + // adjust offset for view to get inside bounds + _limitOffset: function (offset, bounds) { + if (!bounds) { return offset; } + + var viewBounds = this.getPixelBounds(), + newBounds = new Bounds(viewBounds.min.add(offset), viewBounds.max.add(offset)); + + return offset.add(this._getBoundsOffset(newBounds, bounds)); + }, + + // returns offset needed for pxBounds to get inside maxBounds at a specified zoom + _getBoundsOffset: function (pxBounds, maxBounds, zoom) { + var projectedMaxBounds = toBounds( + this.project(maxBounds.getNorthEast(), zoom), + this.project(maxBounds.getSouthWest(), zoom) + ), + minOffset = projectedMaxBounds.min.subtract(pxBounds.min), + maxOffset = projectedMaxBounds.max.subtract(pxBounds.max), + + dx = this._rebound(minOffset.x, -maxOffset.x), + dy = this._rebound(minOffset.y, -maxOffset.y); + + return new Point(dx, dy); + }, + + _rebound: function (left, right) { + return left + right > 0 ? + Math.round(left - right) / 2 : + Math.max(0, Math.ceil(left)) - Math.max(0, Math.floor(right)); + }, + + _limitZoom: function (zoom) { + var min = this.getMinZoom(), + max = this.getMaxZoom(), + snap = any3d ? this.options.zoomSnap : 1; + if (snap) { + zoom = Math.round(zoom / snap) * snap; + } + return Math.max(min, Math.min(max, zoom)); + }, + + _onPanTransitionStep: function () { + this.fire('move'); + }, + + _onPanTransitionEnd: function () { + removeClass(this._mapPane, 'leaflet-pan-anim'); + this.fire('moveend'); + }, + + _tryAnimatedPan: function (center, options) { + // difference between the new and current centers in pixels + var offset = this._getCenterOffset(center)._floor(); + + // don't animate too far unless animate: true specified in options + if ((options && options.animate) !== true && !this.getSize().contains(offset)) { return false; } + + this.panBy(offset, options); + + return true; + }, + + _createAnimProxy: function () { + + var proxy = this._proxy = create$1('div', 'leaflet-proxy leaflet-zoom-animated'); + this._panes.mapPane.appendChild(proxy); + + this.on('zoomanim', function (e) { + var prop = TRANSFORM, + transform = this._proxy.style[prop]; + + setTransform(this._proxy, this.project(e.center, e.zoom), this.getZoomScale(e.zoom, 1)); + + // workaround for case when transform is the same and so transitionend event is not fired + if (transform === this._proxy.style[prop] && this._animatingZoom) { + this._onZoomTransitionEnd(); + } + }, this); + + this.on('load moveend', function () { + var c = this.getCenter(), + z = this.getZoom(); + setTransform(this._proxy, this.project(c, z), this.getZoomScale(z, 1)); + }, this); + + this._on('unload', this._destroyAnimProxy, this); + }, + + _destroyAnimProxy: function () { + remove(this._proxy); + delete this._proxy; + }, + + _catchTransitionEnd: function (e) { + if (this._animatingZoom && e.propertyName.indexOf('transform') >= 0) { + this._onZoomTransitionEnd(); + } + }, + + _nothingToAnimate: function () { + return !this._container.getElementsByClassName('leaflet-zoom-animated').length; + }, + + _tryAnimatedZoom: function (center, zoom, options) { + + if (this._animatingZoom) { return true; } + + options = options || {}; + + // don't animate if disabled, not supported or zoom difference is too large + if (!this._zoomAnimated || options.animate === false || this._nothingToAnimate() || + Math.abs(zoom - this._zoom) > this.options.zoomAnimationThreshold) { return false; } + + // offset is the pixel coords of the zoom origin relative to the current center + var scale = this.getZoomScale(zoom), + offset = this._getCenterOffset(center)._divideBy(1 - 1 / scale); + + // don't animate if the zoom origin isn't within one screen from the current center, unless forced + if (options.animate !== true && !this.getSize().contains(offset)) { return false; } + + requestAnimFrame(function () { + this + ._moveStart(true) + ._animateZoom(center, zoom, true); + }, this); + + return true; + }, + + _animateZoom: function (center, zoom, startAnim, noUpdate) { + if (startAnim) { + this._animatingZoom = true; + + // remember what center/zoom to set after animation + this._animateToCenter = center; + this._animateToZoom = zoom; + + addClass(this._mapPane, 'leaflet-zoom-anim'); + } + + // @event zoomanim: ZoomAnimEvent + // Fired on every frame of a zoom animation + this.fire('zoomanim', { + center: center, + zoom: zoom, + noUpdate: noUpdate + }); + + // Work around webkit not firing 'transitionend', see https://github.com/Leaflet/Leaflet/issues/3689, 2693 + setTimeout(bind(this._onZoomTransitionEnd, this), 250); + }, + + _onZoomTransitionEnd: function () { + if (!this._animatingZoom) { return; } + + removeClass(this._mapPane, 'leaflet-zoom-anim'); + + this._animatingZoom = false; + + this._move(this._animateToCenter, this._animateToZoom); + + // This anim frame should prevent an obscure iOS webkit tile loading race condition. + requestAnimFrame(function () { + this._moveEnd(true); + }, this); + } + }); + +// @section + +// @factory L.map(id: String, options?: Map options) +// Instantiates a map object given the DOM ID of a `
` element +// and optionally an object literal with `Map options`. +// +// @alternative +// @factory L.map(el: HTMLElement, options?: Map options) +// Instantiates a map object given an instance of a `
` HTML element +// and optionally an object literal with `Map options`. + function createMap(id, options) { + return new Map(id, options); + } + + /* + * @class Control + * @aka L.Control + * @inherits Class + * + * L.Control is a base class for implementing map controls. Handles positioning. + * All other controls extend from this class. + */ + + var Control = Class.extend({ + // @section + // @aka Control options + options: { + // @option position: String = 'topright' + // The position of the control (one of the map corners). Possible values are `'topleft'`, + // `'topright'`, `'bottomleft'` or `'bottomright'` + position: 'topright' + }, + + initialize: function (options) { + setOptions(this, options); + }, + + /* @section + * Classes extending L.Control will inherit the following methods: + * + * @method getPosition: string + * Returns the position of the control. + */ + getPosition: function () { + return this.options.position; + }, + + // @method setPosition(position: string): this + // Sets the position of the control. + setPosition: function (position) { + var map = this._map; + + if (map) { + map.removeControl(this); + } + + this.options.position = position; + + if (map) { + map.addControl(this); + } + + return this; + }, + + // @method getContainer: HTMLElement + // Returns the HTMLElement that contains the control. + getContainer: function () { + return this._container; + }, + + // @method addTo(map: Map): this + // Adds the control to the given map. + addTo: function (map) { + this.remove(); + this._map = map; + + var container = this._container = this.onAdd(map), + pos = this.getPosition(), + corner = map._controlCorners[pos]; + + addClass(container, 'leaflet-control'); + + if (pos.indexOf('bottom') !== -1) { + corner.insertBefore(container, corner.firstChild); + } else { + corner.appendChild(container); + } + + return this; + }, + + // @method remove: this + // Removes the control from the map it is currently active on. + remove: function () { + if (!this._map) { + return this; + } + + remove(this._container); + + if (this.onRemove) { + this.onRemove(this._map); + } + + this._map = null; + + return this; + }, + + _refocusOnMap: function (e) { + // if map exists and event is not a keyboard event + if (this._map && e && e.screenX > 0 && e.screenY > 0) { + this._map.getContainer().focus(); + } + } + }); + + var control = function (options) { + return new Control(options); + }; + + /* @section Extension methods + * @uninheritable + * + * Every control should extend from `L.Control` and (re-)implement the following methods. + * + * @method onAdd(map: Map): HTMLElement + * Should return the container DOM element for the control and add listeners on relevant map events. Called on [`control.addTo(map)`](#control-addTo). + * + * @method onRemove(map: Map) + * Optional method. Should contain all clean up code that removes the listeners previously added in [`onAdd`](#control-onadd). Called on [`control.remove()`](#control-remove). + */ + + /* @namespace Map + * @section Methods for Layers and Controls + */ + Map.include({ + // @method addControl(control: Control): this + // Adds the given control to the map + addControl: function (control) { + control.addTo(this); + return this; + }, + + // @method removeControl(control: Control): this + // Removes the given control from the map + removeControl: function (control) { + control.remove(); + return this; + }, + + _initControlPos: function () { + var corners = this._controlCorners = {}, + l = 'leaflet-', + container = this._controlContainer = + create$1('div', l + 'control-container', this._container); + + function createCorner(vSide, hSide) { + var className = l + vSide + ' ' + l + hSide; + + corners[vSide + hSide] = create$1('div', className, container); + } + + createCorner('top', 'left'); + createCorner('top', 'right'); + createCorner('bottom', 'left'); + createCorner('bottom', 'right'); + }, + + _clearControlPos: function () { + for (var i in this._controlCorners) { + remove(this._controlCorners[i]); + } + remove(this._controlContainer); + delete this._controlCorners; + delete this._controlContainer; + } + }); + + /* + * @class Control.Layers + * @aka L.Control.Layers + * @inherits Control + * + * The layers control gives users the ability to switch between different base layers and switch overlays on/off (check out the [detailed example](http://leafletjs.com/examples/layers-control/)). Extends `Control`. + * + * @example + * + * ```js + * var baseLayers = { + * "Mapbox": mapbox, + * "OpenStreetMap": osm + * }; + * + * var overlays = { + * "Marker": marker, + * "Roads": roadsLayer + * }; + * + * L.control.layers(baseLayers, overlays).addTo(map); + * ``` + * + * The `baseLayers` and `overlays` parameters are object literals with layer names as keys and `Layer` objects as values: + * + * ```js + * { + * "": layer1, + * "": layer2 + * } + * ``` + * + * The layer names can contain HTML, which allows you to add additional styling to the items: + * + * ```js + * {" My Layer": myLayer} + * ``` + */ + + var Layers = Control.extend({ + // @section + // @aka Control.Layers options + options: { + // @option collapsed: Boolean = true + // If `true`, the control will be collapsed into an icon and expanded on mouse hover or touch. + collapsed: true, + position: 'topright', + + // @option autoZIndex: Boolean = true + // If `true`, the control will assign zIndexes in increasing order to all of its layers so that the order is preserved when switching them on/off. + autoZIndex: true, + + // @option hideSingleBase: Boolean = false + // If `true`, the base layers in the control will be hidden when there is only one. + hideSingleBase: false, + + // @option sortLayers: Boolean = false + // Whether to sort the layers. When `false`, layers will keep the order + // in which they were added to the control. + sortLayers: false, + + // @option sortFunction: Function = * + // A [compare function](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/sort) + // that will be used for sorting the layers, when `sortLayers` is `true`. + // The function receives both the `L.Layer` instances and their names, as in + // `sortFunction(layerA, layerB, nameA, nameB)`. + // By default, it sorts layers alphabetically by their name. + sortFunction: function (layerA, layerB, nameA, nameB) { + return nameA < nameB ? -1 : (nameB < nameA ? 1 : 0); + } + }, + + initialize: function (baseLayers, overlays, options) { + setOptions(this, options); + + this._layerControlInputs = []; + this._layers = []; + this._lastZIndex = 0; + this._handlingClick = false; + + for (var i in baseLayers) { + this._addLayer(baseLayers[i], i); + } + + for (i in overlays) { + this._addLayer(overlays[i], i, true); + } + }, + + onAdd: function (map) { + this._initLayout(); + this._update(); + + this._map = map; + map.on('zoomend', this._checkDisabledLayers, this); + + for (var i = 0; i < this._layers.length; i++) { + this._layers[i].layer.on('add remove', this._onLayerChange, this); + } + + return this._container; + }, + + addTo: function (map) { + Control.prototype.addTo.call(this, map); + // Trigger expand after Layers Control has been inserted into DOM so that is now has an actual height. + return this._expandIfNotCollapsed(); + }, + + onRemove: function () { + this._map.off('zoomend', this._checkDisabledLayers, this); + + for (var i = 0; i < this._layers.length; i++) { + this._layers[i].layer.off('add remove', this._onLayerChange, this); + } + }, + + // @method addBaseLayer(layer: Layer, name: String): this + // Adds a base layer (radio button entry) with the given name to the control. + addBaseLayer: function (layer, name) { + this._addLayer(layer, name); + return (this._map) ? this._update() : this; + }, + + // @method addOverlay(layer: Layer, name: String): this + // Adds an overlay (checkbox entry) with the given name to the control. + addOverlay: function (layer, name) { + this._addLayer(layer, name, true); + return (this._map) ? this._update() : this; + }, + + // @method removeLayer(layer: Layer): this + // Remove the given layer from the control. + removeLayer: function (layer) { + layer.off('add remove', this._onLayerChange, this); + + var obj = this._getLayer(stamp(layer)); + if (obj) { + this._layers.splice(this._layers.indexOf(obj), 1); + } + return (this._map) ? this._update() : this; + }, + + // @method expand(): this + // Expand the control container if collapsed. + expand: function () { + addClass(this._container, 'leaflet-control-layers-expanded'); + this._form.style.height = null; + var acceptableHeight = this._map.getSize().y - (this._container.offsetTop + 50); + if (acceptableHeight < this._form.clientHeight) { + addClass(this._form, 'leaflet-control-layers-scrollbar'); + this._form.style.height = acceptableHeight + 'px'; + } else { + removeClass(this._form, 'leaflet-control-layers-scrollbar'); + } + this._checkDisabledLayers(); + return this; + }, + + // @method collapse(): this + // Collapse the control container if expanded. + collapse: function () { + removeClass(this._container, 'leaflet-control-layers-expanded'); + return this; + }, + + _initLayout: function () { + var className = 'leaflet-control-layers', + container = this._container = create$1('div', className), + collapsed = this.options.collapsed; + + // makes this work on IE touch devices by stopping it from firing a mouseout event when the touch is released + container.setAttribute('aria-haspopup', true); + + disableClickPropagation(container); + disableScrollPropagation(container); + + var form = this._form = create$1('form', className + '-list'); + + if (collapsed) { + this._map.on('click', this.collapse, this); + + if (!android) { + on(container, { + mouseenter: this.expand, + mouseleave: this.collapse + }, this); + } + } + + var link = this._layersLink = create$1('a', className + '-toggle', container); + link.href = '#'; + link.title = 'Layers'; + + if (touch) { + on(link, 'click', stop); + on(link, 'click', this.expand, this); + } else { + on(link, 'focus', this.expand, this); + } + + // work around for Firefox Android issue https://github.com/Leaflet/Leaflet/issues/2033 + on(form, 'click', function () { + setTimeout(bind(this._onInputClick, this), 0); + }, this); + + // TODO keyboard accessibility + + if (!collapsed) { + this.expand(); + } + + this._baseLayersList = create$1('div', className + '-base', form); + this._separator = create$1('div', className + '-separator', form); + this._overlaysList = create$1('div', className + '-overlays', form); + + container.appendChild(form); + }, + + _getLayer: function (id) { + for (var i = 0; i < this._layers.length; i++) { + + if (this._layers[i] && stamp(this._layers[i].layer) === id) { + return this._layers[i]; + } + } + }, + + _addLayer: function (layer, name, overlay) { + if (this._map) { + layer.on('add remove', this._onLayerChange, this); + } + + this._layers.push({ + layer: layer, + name: name, + overlay: overlay + }); + + if (this.options.sortLayers) { + this._layers.sort(L.bind(function (a, b) { + return this.options.sortFunction(a.layer, b.layer, a.name, b.name); + }, this)); + } + + if (this.options.autoZIndex && layer.setZIndex) { + this._lastZIndex++; + layer.setZIndex(this._lastZIndex); + } + + this._expandIfNotCollapsed(); + }, + + _update: function () { + if (!this._container) { return this; } + + empty(this._baseLayersList); + empty(this._overlaysList); + + this._layerControlInputs = []; + var baseLayersPresent, overlaysPresent, i, obj, baseLayersCount = 0; + + for (i = 0; i < this._layers.length; i++) { + obj = this._layers[i]; + this._addItem(obj); + overlaysPresent = overlaysPresent || obj.overlay; + baseLayersPresent = baseLayersPresent || !obj.overlay; + baseLayersCount += !obj.overlay ? 1 : 0; + } + + // Hide base layers section if there's only one layer. + if (this.options.hideSingleBase) { + baseLayersPresent = baseLayersPresent && baseLayersCount > 1; + this._baseLayersList.style.display = baseLayersPresent ? '' : 'none'; + } + + this._separator.style.display = overlaysPresent && baseLayersPresent ? '' : 'none'; + + return this; + }, + + _onLayerChange: function (e) { + if (!this._handlingClick) { + this._update(); + } + + var obj = this._getLayer(stamp(e.target)); + + // @namespace Map + // @section Layer events + // @event baselayerchange: LayersControlEvent + // Fired when the base layer is changed through the [layer control](#control-layers). + // @event overlayadd: LayersControlEvent + // Fired when an overlay is selected through the [layer control](#control-layers). + // @event overlayremove: LayersControlEvent + // Fired when an overlay is deselected through the [layer control](#control-layers). + // @namespace Control.Layers + var type = obj.overlay ? + (e.type === 'add' ? 'overlayadd' : 'overlayremove') : + (e.type === 'add' ? 'baselayerchange' : null); + + if (type) { + this._map.fire(type, obj); + } + }, + + // IE7 bugs out if you create a radio dynamically, so you have to do it this hacky way (see http://bit.ly/PqYLBe) + _createRadioElement: function (name, checked) { + + var radioHtml = ''; + + var radioFragment = document.createElement('div'); + radioFragment.innerHTML = radioHtml; + + return radioFragment.firstChild; + }, + + _addItem: function (obj) { + var label = document.createElement('label'), + checked = this._map.hasLayer(obj.layer), + input; + + if (obj.overlay) { + input = document.createElement('input'); + input.type = 'checkbox'; + input.className = 'leaflet-control-layers-selector'; + input.defaultChecked = checked; + } else { + input = this._createRadioElement('leaflet-base-layers', checked); + } + + this._layerControlInputs.push(input); + input.layerId = stamp(obj.layer); + + on(input, 'click', this._onInputClick, this); + + var name = document.createElement('span'); + name.innerHTML = ' ' + obj.name; + + // Helps from preventing layer control flicker when checkboxes are disabled + // https://github.com/Leaflet/Leaflet/issues/2771 + var holder = document.createElement('div'); + + label.appendChild(holder); + holder.appendChild(input); + holder.appendChild(name); + + var container = obj.overlay ? this._overlaysList : this._baseLayersList; + container.appendChild(label); + + this._checkDisabledLayers(); + return label; + }, + + _onInputClick: function () { + var inputs = this._layerControlInputs, + input, layer, hasLayer; + var addedLayers = [], + removedLayers = []; + + this._handlingClick = true; + + for (var i = inputs.length - 1; i >= 0; i--) { + input = inputs[i]; + layer = this._getLayer(input.layerId).layer; + hasLayer = this._map.hasLayer(layer); + + if (input.checked && !hasLayer) { + addedLayers.push(layer); + + } else if (!input.checked && hasLayer) { + removedLayers.push(layer); + } + } + + // Bugfix issue 2318: Should remove all old layers before readding new ones + for (i = 0; i < removedLayers.length; i++) { + this._map.removeLayer(removedLayers[i]); + } + for (i = 0; i < addedLayers.length; i++) { + this._map.addLayer(addedLayers[i]); + } + + this._handlingClick = false; + + this._refocusOnMap(); + }, + + _checkDisabledLayers: function () { + var inputs = this._layerControlInputs, + input, + layer, + zoom = this._map.getZoom(); + + for (var i = inputs.length - 1; i >= 0; i--) { + input = inputs[i]; + layer = this._getLayer(input.layerId).layer; + input.disabled = (layer.options.minZoom !== undefined && zoom < layer.options.minZoom) || + (layer.options.maxZoom !== undefined && zoom > layer.options.maxZoom); + + } + }, + + _expandIfNotCollapsed: function () { + if (this._map && !this.options.collapsed) { + this.expand(); + } + return this; + }, + + _expand: function () { + // Backward compatibility, remove me in 1.1. + return this.expand(); + }, + + _collapse: function () { + // Backward compatibility, remove me in 1.1. + return this.collapse(); + } + + }); + + +// @factory L.control.layers(baselayers?: Object, overlays?: Object, options?: Control.Layers options) +// Creates an attribution control with the given layers. Base layers will be switched with radio buttons, while overlays will be switched with checkboxes. Note that all base layers should be passed in the base layers object, but only one should be added to the map during map instantiation. + var layers = function (baseLayers, overlays, options) { + return new Layers(baseLayers, overlays, options); + }; + + /* + * @class Control.Zoom + * @aka L.Control.Zoom + * @inherits Control + * + * A basic zoom control with two buttons (zoom in and zoom out). It is put on the map by default unless you set its [`zoomControl` option](#map-zoomcontrol) to `false`. Extends `Control`. + */ + + var Zoom = Control.extend({ + // @section + // @aka Control.Zoom options + options: { + position: 'topleft', + + // @option zoomInText: String = '+' + // The text set on the 'zoom in' button. + zoomInText: '+', + + // @option zoomInTitle: String = 'Zoom in' + // The title set on the 'zoom in' button. + zoomInTitle: 'Zoom in', + + // @option zoomOutText: String = '−' + // The text set on the 'zoom out' button. + zoomOutText: '−', + + // @option zoomOutTitle: String = 'Zoom out' + // The title set on the 'zoom out' button. + zoomOutTitle: 'Zoom out' + }, + + onAdd: function (map) { + var zoomName = 'leaflet-control-zoom', + container = create$1('div', zoomName + ' leaflet-bar'), + options = this.options; + + this._zoomInButton = this._createButton(options.zoomInText, options.zoomInTitle, + zoomName + '-in', container, this._zoomIn); + this._zoomOutButton = this._createButton(options.zoomOutText, options.zoomOutTitle, + zoomName + '-out', container, this._zoomOut); + + this._updateDisabled(); + map.on('zoomend zoomlevelschange', this._updateDisabled, this); + + return container; + }, + + onRemove: function (map) { + map.off('zoomend zoomlevelschange', this._updateDisabled, this); + }, + + disable: function () { + this._disabled = true; + this._updateDisabled(); + return this; + }, + + enable: function () { + this._disabled = false; + this._updateDisabled(); + return this; + }, + + _zoomIn: function (e) { + if (!this._disabled && this._map._zoom < this._map.getMaxZoom()) { + this._map.zoomIn(this._map.options.zoomDelta * (e.shiftKey ? 3 : 1)); + } + }, + + _zoomOut: function (e) { + if (!this._disabled && this._map._zoom > this._map.getMinZoom()) { + this._map.zoomOut(this._map.options.zoomDelta * (e.shiftKey ? 3 : 1)); + } + }, + + _createButton: function (html, title, className, container, fn) { + var link = create$1('a', className, container); + link.innerHTML = html; + link.href = '#'; + link.title = title; + + /* + * Will force screen readers like VoiceOver to read this as "Zoom in - button" + */ + link.setAttribute('role', 'button'); + link.setAttribute('aria-label', title); + + disableClickPropagation(link); + on(link, 'click', stop); + on(link, 'click', fn, this); + on(link, 'click', this._refocusOnMap, this); + + return link; + }, + + _updateDisabled: function () { + var map = this._map, + className = 'leaflet-disabled'; + + removeClass(this._zoomInButton, className); + removeClass(this._zoomOutButton, className); + + if (this._disabled || map._zoom === map.getMinZoom()) { + addClass(this._zoomOutButton, className); + } + if (this._disabled || map._zoom === map.getMaxZoom()) { + addClass(this._zoomInButton, className); + } + } + }); + +// @namespace Map +// @section Control options +// @option zoomControl: Boolean = true +// Whether a [zoom control](#control-zoom) is added to the map by default. + Map.mergeOptions({ + zoomControl: true + }); + + Map.addInitHook(function () { + if (this.options.zoomControl) { + this.zoomControl = new Zoom(); + this.addControl(this.zoomControl); + } + }); + +// @namespace Control.Zoom +// @factory L.control.zoom(options: Control.Zoom options) +// Creates a zoom control + var zoom = function (options) { + return new Zoom(options); + }; + + /* + * @class Control.Scale + * @aka L.Control.Scale + * @inherits Control + * + * A simple scale control that shows the scale of the current center of screen in metric (m/km) and imperial (mi/ft) systems. Extends `Control`. + * + * @example + * + * ```js + * L.control.scale().addTo(map); + * ``` + */ + + var Scale = Control.extend({ + // @section + // @aka Control.Scale options + options: { + position: 'bottomleft', + + // @option maxWidth: Number = 100 + // Maximum width of the control in pixels. The width is set dynamically to show round values (e.g. 100, 200, 500). + maxWidth: 100, + + // @option metric: Boolean = True + // Whether to show the metric scale line (m/km). + metric: true, + + // @option imperial: Boolean = True + // Whether to show the imperial scale line (mi/ft). + imperial: true + + // @option updateWhenIdle: Boolean = false + // If `true`, the control is updated on [`moveend`](#map-moveend), otherwise it's always up-to-date (updated on [`move`](#map-move)). + }, + + onAdd: function (map) { + var className = 'leaflet-control-scale', + container = create$1('div', className), + options = this.options; + + this._addScales(options, className + '-line', container); + + map.on(options.updateWhenIdle ? 'moveend' : 'move', this._update, this); + map.whenReady(this._update, this); + + return container; + }, + + onRemove: function (map) { + map.off(this.options.updateWhenIdle ? 'moveend' : 'move', this._update, this); + }, + + _addScales: function (options, className, container) { + if (options.metric) { + this._mScale = create$1('div', className, container); + } + if (options.imperial) { + this._iScale = create$1('div', className, container); + } + }, + + _update: function () { + var map = this._map, + y = map.getSize().y / 2; + + var maxMeters = map.distance( + map.containerPointToLatLng([0, y]), + map.containerPointToLatLng([this.options.maxWidth, y])); + + this._updateScales(maxMeters); + }, + + _updateScales: function (maxMeters) { + if (this.options.metric && maxMeters) { + this._updateMetric(maxMeters); + } + if (this.options.imperial && maxMeters) { + this._updateImperial(maxMeters); + } + }, + + _updateMetric: function (maxMeters) { + var meters = this._getRoundNum(maxMeters), + label = meters < 1000 ? meters + ' m' : (meters / 1000) + ' km'; + + this._updateScale(this._mScale, label, meters / maxMeters); + }, + + _updateImperial: function (maxMeters) { + var maxFeet = maxMeters * 3.2808399, + maxMiles, miles, feet; + + if (maxFeet > 5280) { + maxMiles = maxFeet / 5280; + miles = this._getRoundNum(maxMiles); + this._updateScale(this._iScale, miles + ' mi', miles / maxMiles); + + } else { + feet = this._getRoundNum(maxFeet); + this._updateScale(this._iScale, feet + ' ft', feet / maxFeet); + } + }, + + _updateScale: function (scale, text, ratio) { + scale.style.width = Math.round(this.options.maxWidth * ratio) + 'px'; + scale.innerHTML = text; + }, + + _getRoundNum: function (num) { + var pow10 = Math.pow(10, (Math.floor(num) + '').length - 1), + d = num / pow10; + + d = d >= 10 ? 10 : + d >= 5 ? 5 : + d >= 3 ? 3 : + d >= 2 ? 2 : 1; + + return pow10 * d; + } + }); + + +// @factory L.control.scale(options?: Control.Scale options) +// Creates an scale control with the given options. + var scale = function (options) { + return new Scale(options); + }; + + /* + * @class Control.Attribution + * @aka L.Control.Attribution + * @inherits Control + * + * The attribution control allows you to display attribution data in a small text box on a map. It is put on the map by default unless you set its [`attributionControl` option](#map-attributioncontrol) to `false`, and it fetches attribution texts from layers with the [`getAttribution` method](#layer-getattribution) automatically. Extends Control. + */ + + var Attribution = Control.extend({ + // @section + // @aka Control.Attribution options + options: { + position: 'bottomright', + + // @option prefix: String = 'Leaflet' + // The HTML text shown before the attributions. Pass `false` to disable. + prefix: 'Leaflet' + }, + + initialize: function (options) { + setOptions(this, options); + + this._attributions = {}; + }, + + onAdd: function (map) { + map.attributionControl = this; + this._container = create$1('div', 'leaflet-control-attribution'); + disableClickPropagation(this._container); + + // TODO ugly, refactor + for (var i in map._layers) { + if (map._layers[i].getAttribution) { + this.addAttribution(map._layers[i].getAttribution()); + } + } + + this._update(); + + return this._container; + }, + + // @method setPrefix(prefix: String): this + // Sets the text before the attributions. + setPrefix: function (prefix) { + this.options.prefix = prefix; + this._update(); + return this; + }, + + // @method addAttribution(text: String): this + // Adds an attribution text (e.g. `'Vector data © Mapbox'`). + addAttribution: function (text) { + if (!text) { return this; } + + if (!this._attributions[text]) { + this._attributions[text] = 0; + } + this._attributions[text]++; + + this._update(); + + return this; + }, + + // @method removeAttribution(text: String): this + // Removes an attribution text. + removeAttribution: function (text) { + if (!text) { return this; } + + if (this._attributions[text]) { + this._attributions[text]--; + this._update(); + } + + return this; + }, + + _update: function () { + if (!this._map) { return; } + + var attribs = []; + + for (var i in this._attributions) { + if (this._attributions[i]) { + attribs.push(i); + } + } + + var prefixAndAttribs = []; + + if (this.options.prefix) { + prefixAndAttribs.push(this.options.prefix); + } + if (attribs.length) { + prefixAndAttribs.push(attribs.join(', ')); + } + + this._container.innerHTML = prefixAndAttribs.join(' | '); + } + }); + +// @namespace Map +// @section Control options +// @option attributionControl: Boolean = true +// Whether a [attribution control](#control-attribution) is added to the map by default. + Map.mergeOptions({ + attributionControl: true + }); + + Map.addInitHook(function () { + if (this.options.attributionControl) { + new Attribution().addTo(this); + } + }); + +// @namespace Control.Attribution +// @factory L.control.attribution(options: Control.Attribution options) +// Creates an attribution control. + var attribution = function (options) { + return new Attribution(options); + }; + + Control.Layers = Layers; + Control.Zoom = Zoom; + Control.Scale = Scale; + Control.Attribution = Attribution; + + control.layers = layers; + control.zoom = zoom; + control.scale = scale; + control.attribution = attribution; + + /* + L.Handler is a base class for handler classes that are used internally to inject + interaction features like dragging to classes like Map and Marker. */ -(function(e,t){var n,r;typeof exports!=t+""?n=exports:(r=e.L,n={},n.noConflict=function(){return e.L=r,this},e.L=n),n.version="0.4.5",n.Util={extend:function(e){var t=Array.prototype.slice.call(arguments,1);for(var n=0,r=t.length,i;n2?Array.prototype.slice.call(arguments,2):null;return function(){return e.apply(t,n||arguments)}},stamp:function(){var e=0,t="_leaflet_id";return function(n){return n[t]=n[t]||++e,n[t]}}(),limitExecByInterval:function(e,t,n){var r,i;return function s(){var o=arguments;if(r){i=!0;return}r=!0,setTimeout(function(){r=!1,i&&(s.apply(n,o),i=!1)},t),e.apply(n,o)}},falseFn:function(){return!1},formatNum:function(e,t){var n=Math.pow(10,t||5);return Math.round(e*n)/n},splitWords:function(e){return e.replace(/^\s+|\s+$/g,"").split(/\s+/)},setOptions:function(e,t){return e.options=n.Util.extend({},e.options,t),e.options},getParamString:function(e){var t=[];for(var n in e)e.hasOwnProperty(n)&&t.push(n+"="+e[n]);return"?"+t.join("&")},template:function(e,t){return e.replace(/\{ *([\w_]+) *\}/g,function(e,n){var r=t[n];if(!t.hasOwnProperty(n))throw Error("No value provided for variable "+e);return r})},emptyImageUrl:"data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs="},function(){function t(t){var n,r,i=["webkit","moz","o","ms"];for(n=0;n0},removeEventListener:function(e,t,r){var s=this[i],o,u,a,f,l;if(typeof e=="object"){for(o in e)e.hasOwnProperty(o)&&this.removeEventListener(o,e[o],t);return this}e=n.Util.splitWords(e);for(u=0,a=e.length;u=0;l--)(!t||f[l].action===t)&&(!r||f[l].context===r)&&f.splice(l,1)}return this},fireEvent:function(e,t){if(!this.hasEventListeners(e))return this;var r=n.Util.extend({type:e,target:this},t),s=this[i][e].slice();for(var o=0,u=s.length;o1||"matchMedia"in e&&e.matchMedia("(min-resolution:144dpi)").matches;n.Browser={ua:r,ie:i,ie6:s,webkit:o,gecko:u,opera:f,android:l,android23:c,chrome:a,ie3d:d,webkit3d:v,gecko3d:m,opera3d:g,any3d:!e.L_DISABLE_3D&&(d||v||m||g),mobile:h,mobileWebkit:h&&o,mobileWebkit3d:h&&v,mobileOpera:h&&f,touch:y,retina:b}}(),n.Point=function(e,t,n){this.x=n?Math.round(e):e,this.y=n?Math.round(t):t},n.Point.prototype={add:function(e){return this.clone()._add(n.point(e))},_add:function(e){return this.x+=e.x,this.y+=e.y,this},subtract:function(e){return this.clone()._subtract(n.point(e))},_subtract:function(e){return this.x-=e.x,this.y-=e.y,this},divideBy:function(e,t){return new n.Point(this.x/e,this.y/e,t)},multiplyBy:function(e,t){return new n.Point(this.x*e,this.y*e,t)},distanceTo:function(e){e=n.point(e);var t=e.x-this.x,r=e.y-this.y;return Math.sqrt(t*t+r*r)},round:function(){return this.clone()._round()},_round:function(){return this.x=Math.round(this.x),this.y=Math.round(this.y),this},floor:function(){return this.clone()._floor()},_floor:function(){return this.x=Math.floor(this.x),this.y=Math.floor(this.y),this},clone:function(){return new n.Point(this.x,this.y)},toString:function(){return"Point("+n.Util.formatNum(this.x)+", "+n.Util.formatNum(this.y)+")"}},n.point=function(e,t,r){return e instanceof n.Point?e:e instanceof Array?new n.Point(e[0],e[1]):isNaN(e)?e:new n.Point(e,t,r)},n.Bounds=n.Class.extend({initialize:function(e,t){if(!e)return;var n=t?[e,t]:e;for(var r=0,i=n.length;r=this.min.x&&r.x<=this.max.x&&t.y>=this.min.y&&r.y<=this.max.y},intersects:function(e){e=n.bounds(e);var t=this.min,r=this.max,i=e.min,s=e.max,o=s.x>=t.x&&i.x<=r.x,u=s.y>=t.y&&i.y<=r.y;return o&&u}}),n.bounds=function(e,t){return!e||e instanceof n.Bounds?e:new n.Bounds(e,t)},n.Transformation=n.Class.extend({initialize:function(e,t,n,r){this._a=e,this._b=t,this._c=n,this._d=r},transform:function(e,t){return this._transform(e.clone(),t)},_transform:function(e,t){return t=t||1,e.x=t*(this._a*e.x+this._b),e.y=t*(this._c*e.y+this._d),e},untransform:function(e,t){return t=t||1,new n.Point((e.x/t-this._b)/this._a,(e.y/t-this._d)/this._c)}}),n.DomUtil={get:function(e){return typeof e=="string"?document.getElementById(e):e},getStyle:function(e,t){var n=e.style[t];!n&&e.currentStyle&&(n=e.currentStyle[t]);if(!n||n==="auto"){var r=document.defaultView.getComputedStyle(e,null);n=r?r[t]:null}return n==="auto"?null:n},getViewportOffset:function(e){var t=0,r=0,i=e,s=document.body;do{t+=i.offsetTop||0,r+=i.offsetLeft||0;if(i.offsetParent===s&&n.DomUtil.getStyle(i,"position")==="absolute")break;if(n.DomUtil.getStyle(i,"position")==="fixed"){t+=s.scrollTop||0,r+=s.scrollLeft||0;break}i=i.offsetParent}while(i);i=e;do{if(i===s)break;t-=i.scrollTop||0,r-=i.scrollLeft||0,i=i.parentNode}while(i);return new n.Point(r,t)},create:function(e,t,n){var r=document.createElement(e);return r.className=t,n&&n.appendChild(r),r},disableTextSelection:function(){document.selection&&document.selection.empty&&document.selection.empty(),this._onselectstart||(this._onselectstart=document.onselectstart,document.onselectstart=n.Util.falseFn)},enableTextSelection:function(){document.onselectstart=this._onselectstart,this._onselectstart=null},hasClass:function(e,t){return e.className.length>0&&RegExp("(^|\\s)"+t+"(\\s|$)").test(e.className)},addClass:function(e,t){n.DomUtil.hasClass(e,t)||(e.className+=(e.className?" ":"")+t)},removeClass:function(e,t){function n(e,n){return n===t?"":e}e.className=e.className.replace(/(\S+)\s*/g,n).replace(/(^\s+|\s+$)/,"")},setOpacity:function(e,t){if("opacity"in e.style)e.style.opacity=t;else if(n.Browser.ie){var r=!1,i="DXImageTransform.Microsoft.Alpha";try{r=e.filters.item(i)}catch(s){}t=Math.round(t*100),r?(r.Enabled=t!==100,r.Opacity=t):e.style.filter+=" progid:"+i+"(opacity="+t+")"}},testProp:function(e){var t=document.documentElement.style;for(var n=0;n=t.lat&&s.lat<=r.lat&&i.lng>=t.lng&&s.lng<=r.lng},intersects:function(e){e=n.latLngBounds(e);var t=this._southWest,r=this._northEast,i=e.getSouthWest(),s=e.getNorthEast(),o=s.lat>=t.lat&&i.lat<=r.lat,u=s.lng>=t.lng&&i.lng<=r.lng;return o&&u},toBBoxString:function(){var e=this._southWest,t=this._northEast;return[e.lng,e.lat,t.lng,t.lat].join(",")},equals:function(e){return e?(e=n.latLngBounds(e),this._southWest.equals(e.getSouthWest())&&this._northEast.equals(e.getNorthEast())):!1}}),n.latLngBounds=function(e,t){return!e||e instanceof n.LatLngBounds?e:new n.LatLngBounds(e,t)},n.Projection={},n.Projection.SphericalMercator={MAX_LATITUDE:85.0511287798,project:function(e){var t=n.LatLng.DEG_TO_RAD,r=this.MAX_LATITUDE,i=Math.max(Math.min(r,e.lat),-r),s=e.lng*t,o=i*t;return o=Math.log(Math.tan(Math.PI/4+o/2)),new n.Point(s,o)},unproject:function(e){var t=n.LatLng.RAD_TO_DEG,r=e.x*t,i=(2*Math.atan(Math.exp(e.y))-Math.PI/2)*t;return new n.LatLng(i,r,!0)}},n.Projection.LonLat={project:function(e){return new n.Point(e.lng,e.lat)},unproject:function(e){return new n.LatLng(e.y,e.x,!0)}},n.CRS={latLngToPoint:function(e,t){var n=this.projection.project(e),r=this.scale(t);return this.transformation._transform(n,r)},pointToLatLng:function(e,t){var n=this.scale(t),r=this.transformation.untransform(e,n);return this.projection.unproject(r)},project:function(e){return this.projection.project(e)},scale:function(e){return 256*Math.pow(2,e)}},n.CRS.EPSG3857=n.Util.extend({},n.CRS,{code:"EPSG:3857",projection:n.Projection.SphericalMercator,transformation:new n.Transformation(.5/Math.PI,.5,-0.5/Math.PI,.5),project:function(e){var t=this.projection.project(e),n=6378137;return t.multiplyBy(n)}}),n.CRS.EPSG900913=n.Util.extend({},n.CRS.EPSG3857,{code:"EPSG:900913"}),n.CRS.EPSG4326=n.Util.extend({},n.CRS,{code:"EPSG:4326",projection:n.Projection.LonLat,transformation:new n.Transformation(1/360,.5,-1/360,.5)}),n.Map=n.Class.extend({includes:n.Mixin.Events,options:{crs:n.CRS.EPSG3857,fadeAnimation:n.DomUtil.TRANSITION&&!n.Browser.android23,trackResize:!0,markerZoomAnimation:n.DomUtil.TRANSITION&&n.Browser.any3d},initialize:function(e,r){r=n.Util.setOptions(this,r),this._initContainer(e),this._initLayout(),this._initHooks(),this._initEvents(),r.maxBounds&&this.setMaxBounds(r.maxBounds),r.center&&r.zoom!==t&&this.setView(n.latLng(r.center),r.zoom,!0),this._initLayers(r.layers)},setView:function(e,t){return this._resetView(n.latLng(e),this._limitZoom(t)),this},setZoom:function(e){return this.setView(this.getCenter(),e)},zoomIn:function(){return this.setZoom(this._zoom+1)},zoomOut:function(){return this.setZoom(this._zoom-1)},fitBounds:function(e){var t=this.getBoundsZoom(e);return this.setView(n.latLngBounds(e).getCenter(),t)},fitWorld:function(){var e=new n.LatLng(-60,-170),t=new n.LatLng(85,179);return this.fitBounds(new n.LatLngBounds(e,t))},panTo:function(e){return this.setView(e,this._zoom)},panBy:function(e){return this.fire("movestart"),this._rawPanBy(n.point(e)),this.fire("move"),this.fire("moveend")},setMaxBounds:function(e){e=n.latLngBounds(e),this.options.maxBounds=e;if(!e)return this._boundsMinZoom=null,this;var t=this.getBoundsZoom(e,!0);return this._boundsMinZoom=t,this._loaded&&(this._zoomo.x&&(u=o.x-i.x),r.y>s.y&&(a=s.y-r.y),r.xc&&--h>0)d=u*Math.sin(f),p=Math.PI/2-2*Math.atan(a*Math.pow((1-d)/(1+d),.5*u))-f,f+=p;return new n.LatLng(f*t,s,!0)}},n.CRS.EPSG3395=n.Util.extend({},n.CRS,{code:"EPSG:3395",projection:n.Projection.Mercator,transformation:function(){var e=n.Projection.Mercator,t=e.R_MAJOR,r=e.R_MINOR;return new n.Transformation(.5/(Math.PI*t),.5,-0.5/(Math.PI*r),.5)}()}),n.TileLayer=n.Class.extend({includes:n.Mixin.Events,options:{minZoom:0,maxZoom:18,tileSize:256,subdomains:"abc",errorTileUrl:"",attribution:"",zoomOffset:0,opacity:1,unloadInvisibleTiles:n.Browser.mobile,updateWhenIdle:n.Browser.mobile},initialize:function(e,t){t=n.Util.setOptions(this,t),t.detectRetina&&n.Browser.retina&&t.maxZoom>0&&(t.tileSize=Math.floor(t.tileSize/2),t.zoomOffset++,t.minZoom>0&&t.minZoom--,this.options.maxZoom--),this._url=e;var r=this.options.subdomains;typeof r=="string"&&(this.options.subdomains=r.split(""))},onAdd:function(e){this._map=e,this._initContainer(),this._createTileProto(),e.on({viewreset:this._resetCallback,moveend:this._update},this),this.options.updateWhenIdle||(this._limitedUpdate=n.Util.limitExecByInterval(this._update,150,this),e.on("move",this._limitedUpdate,this)),this._reset(),this._update()},addTo:function(e){return e.addLayer(this),this},onRemove:function(e){e._panes.tilePane.removeChild(this._container),e.off({viewreset:this._resetCallback,moveend:this._update},this),this.options.updateWhenIdle||e.off("move",this._limitedUpdate,this),this._container=null,this._map=null},bringToFront:function(){var e=this._map._panes.tilePane;return this._container&&(e.appendChild(this._container),this._setAutoZIndex(e,Math.max)),this},bringToBack:function(){var e=this._map._panes.tilePane;return this._container&&(e.insertBefore(this._container,e.firstChild),this._setAutoZIndex(e,Math.min)),this},getAttribution:function(){return this.options.attribution},setOpacity:function(e){return this.options.opacity=e,this._map&&this._updateOpacity(),this},setZIndex:function(e){return this.options.zIndex=e,this._updateZIndex(),this},setUrl:function(e,t){return this._url=e,t||this.redraw(),this},redraw:function(){return this._map&&(this._map._panes.tilePane.empty=!1,this._reset(!0),this._update()),this},_updateZIndex:function(){this._container&&this.options.zIndex!==t&&(this._container.style.zIndex=this.options.zIndex)},_setAutoZIndex:function(e,t){var n=e.getElementsByClassName("leaflet-layer"),r=-t(Infinity,-Infinity),i;for(var s=0,o=n.length;sthis.options.maxZoom||r=t)||e.y<0||e.y>=t)return!1}return!0},_removeOtherTiles:function(e){var t,n,r,i;for(i in this._tiles)this._tiles.hasOwnProperty(i)&&(t=i.split(":"),n=parseInt(t[0],10),r=parseInt(t[1],10),(ne.max.x||re.max.y)&&this._removeTile(i))},_removeTile:function(e){var t=this._tiles[e];this.fire("tileunload",{tile:t,url:t.src}),this.options.reuseTiles?(n.DomUtil.removeClass(t,"leaflet-tile-loaded"),this._unusedTiles.push(t)):t.parentNode===this._container&&this._container.removeChild(t),n.Browser.android||(t.src=n.Util.emptyImageUrl),delete this._tiles[e]},_addTile:function(e,t){var r=this._getTilePos(e),i=this._getTile();n.DomUtil.setPosition(i,r,n.Browser.chrome||n.Browser.android23),this._tiles[e.x+":"+e.y]=i,this._loadTile(i,e),i.parentNode!==this._container&&t.appendChild(i)},_getZoomForUrl:function(){var e=this.options,t=this._map.getZoom();return e.zoomReverse&&(t=e.maxZoom-t),t+e.zoomOffset},_getTilePos:function(e){var t=this._map.getPixelOrigin(),n=this.options.tileSize;return e.multiplyBy(n).subtract(t)},getTileUrl:function(e){return this._adjustTilePoint(e),n.Util.template(this._url,n.Util.extend({s:this._getSubdomain(e),z:this._getZoomForUrl(),x:e.x,y:e.y},this.options))},_getWrapTileNum:function(){return Math.pow(2,this._getZoomForUrl())},_adjustTilePoint:function(e){var t=this._getWrapTileNum();!this.options.continuousWorld&&!this.options.noWrap&&(e.x=(e.x%t+t)%t),this.options.tms&&(e.y=t-e.y-1)},_getSubdomain:function(e){var t=(e.x+e.y)%this.options.subdomains.length;return this.options.subdomains[t]},_createTileProto:function(){var e=this._tileImg=n.DomUtil.create("img","leaflet-tile");e.galleryimg="no";var t=this.options.tileSize;e.style.width=t+"px",e.style.height=t+"px"},_getTile:function(){if(this.options.reuseTiles&&this._unusedTiles.length>0){var e=this._unusedTiles.pop();return this._resetTile(e),e}return this._createTile()},_resetTile:function(e){},_createTile:function(){var e=this._tileImg.cloneNode(!1);return e.onselectstart=e.onmousemove=n.Util.falseFn,e},_loadTile:function(e,t){e._layer=this,e.onload=this._tileOnLoad,e.onerror=this._tileOnError,e.src=this.getTileUrl(t)},_tileLoaded:function(){this._tilesToLoad--,this._tilesToLoad||this.fire("load")},_tileOnLoad:function(e){var t=this._layer;this.src!==n.Util.emptyImageUrl&&(n.DomUtil.addClass(this,"leaflet-tile-loaded"),t.fire("tileload",{tile:this,url:this.src})),t._tileLoaded()},_tileOnError:function(e){var t=this._layer;t.fire("tileerror",{tile:this,url:this.src});var n=t.options.errorTileUrl;n&&(this.src=n),t._tileLoaded()}}),n.tileLayer=function(e,t){return new n.TileLayer(e,t)},n.TileLayer.WMS=n.TileLayer.extend({defaultWmsParams:{service:"WMS",request:"GetMap",version:"1.1.1",layers:"",styles:"",format:"image/jpeg",transparent:!1},initialize:function(e,t){this._url=e;var r=n.Util.extend({},this.defaultWmsParams);t.detectRetina&&n.Browser.retina?r.width=r.height=this.options.tileSize*2:r.width=r.height=this.options.tileSize;for(var i in t)this.options.hasOwnProperty(i)||(r[i]=t[i]);this.wmsParams=r,n.Util.setOptions(this,t)},onAdd:function(e){var t=parseFloat(this.wmsParams.version)>=1.3?"crs":"srs";this.wmsParams[t]=e.options.crs.code,n.TileLayer.prototype.onAdd.call(this,e)},getTileUrl:function(e,t){var r=this._map,i=r.options.crs,s=this.options.tileSize,o=e.multiplyBy(s),u=o.add(new n.Point(s,s)),a=i.project(r.unproject(o,t)),f=i.project(r.unproject(u,t)),l=[a.x,f.y,f.x,a.y].join(","),c=n.Util.template(this._url,{s:this._getSubdomain(e)});return c+n.Util.getParamString(this.wmsParams)+"&bbox="+l},setParams:function(e,t){return n.Util.extend(this.wmsParams,e),t||this.redraw(),this}}),n.tileLayer.wms=function(e,t){return new n.TileLayer.WMS(e,t)},n.TileLayer.Canvas=n.TileLayer.extend({options:{async:!1},initialize:function(e){n.Util.setOptions(this,e)},redraw:function(){var e,t=this._tiles;for(e in t)t.hasOwnProperty(e)&&this._redrawTile(t[e])},_redrawTile:function(e){this.drawTile(e,e._tilePoint,e._zoom)},_createTileProto:function(){var e=this._canvasProto=n.DomUtil.create("canvas","leaflet-tile"),t=this.options.tileSize;e.width=t,e.height=t},_createTile:function(){var e=this._canvasProto.cloneNode(!1);return e.onselectstart=e.onmousemove=n.Util.falseFn,e},_loadTile:function(e,t,n){e._layer=this,e._tilePoint=t,e._zoom=n,this.drawTile(e,t,n),this.options.async||this.tileDrawn(e)},drawTile:function(e,t,n){},tileDrawn:function(e){this._tileOnLoad.call(e)}}),n.tileLayer.canvas=function(e){return new n.TileLayer.Canvas(e)},n.ImageOverlay=n.Class.extend({includes:n.Mixin.Events,options:{opacity:1},initialize:function(e,t,r){this._url=e,this._bounds=n.latLngBounds(t),n.Util.setOptions(this,r)},onAdd:function(e){this._map=e,this._image||this._initImage(),e._panes.overlayPane.appendChild(this._image),e.on("viewreset",this._reset,this),e.options.zoomAnimation&&n.Browser.any3d&&e.on("zoomanim",this._animateZoom,this),this._reset()},onRemove:function(e){e.getPanes().overlayPane.removeChild(this._image),e.off("viewreset",this._reset,this),e.options.zoomAnimation&&e.off("zoomanim",this._animateZoom,this)},addTo:function(e){return e.addLayer(this),this},setOpacity:function(e){return this.options.opacity=e,this._updateOpacity(),this},bringToFront:function(){return this._image&&this._map._panes.overlayPane.appendChild(this._image),this},bringToBack:function(){var e=this._map._panes.overlayPane;return this._image&&e.insertBefore(this._image,e.firstChild),this},_initImage:function(){this._image=n.DomUtil.create("img","leaflet-image-layer"),this._map.options.zoomAnimation&&n.Browser.any3d?n.DomUtil.addClass(this._image,"leaflet-zoom-animated"):n.DomUtil.addClass(this._image,"leaflet-zoom-hide"),this._updateOpacity(),n.Util.extend(this._image,{galleryimg:"no",onselectstart:n.Util.falseFn,onmousemove:n.Util.falseFn,onload:n.Util.bind(this._onImageLoad,this),src:this._url})},_animateZoom:function(e){var t=this._map,r=this._image,i=t.getZoomScale(e.zoom),s=this._bounds.getNorthWest(),o=this._bounds.getSouthEast(),u=t._latLngToNewLayerPoint(s,e.zoom,e.center),a=t._latLngToNewLayerPoint(o,e.zoom,e.center).subtract(u),f=t.latLngToLayerPoint(o).subtract(t.latLngToLayerPoint(s)),l=u.add(a.subtract(f).divideBy(2));r.style[n.DomUtil.TRANSFORM]=n.DomUtil.getTranslateString(l)+" scale("+i+") "},_reset:function(){var e=this._image,t=this._map.latLngToLayerPoint(this._bounds.getNorthWest()),r=this._map.latLngToLayerPoint(this._bounds.getSouthEast()).subtract(t);n.DomUtil.setPosition(e,t),e.style.width=r.x+"px",e.style.height=r.y+"px"},_onImageLoad:function(){this.fire("load")},_updateOpacity:function(){n.DomUtil.setOpacity(this._image,this.options.opacity)}}),n.imageOverlay=function(e,t,r){return new n.ImageOverlay(e,t,r)},n.Icon=n.Class.extend({options:{className:""},initialize:function(e){n.Util.setOptions(this,e)},createIcon:function(){return this._createIcon("icon")},createShadow:function(){return this._createIcon("shadow")},_createIcon:function(e){var t=this._getIconUrl(e);if(!t){if(e==="icon")throw Error("iconUrl not set in Icon options (see the docs).");return null}var n=this._createImg(t);return this._setIconStyles(n,e),n},_setIconStyles:function(e,t){var r=this.options,i=n.point(r[t+"Size"]),s;t==="shadow"?s=n.point(r.shadowAnchor||r.iconAnchor):s=n.point(r.iconAnchor),!s&&i&&(s=i.divideBy(2,!0)),e.className="leaflet-marker-"+t+" "+r.className,s&&(e.style.marginLeft=-s.x+"px",e.style.marginTop=-s.y+"px"),i&&(e.style.width=i.x+"px",e.style.height=i.y+"px")},_createImg:function(e){var t;return n.Browser.ie6?(t=document.createElement("div"),t.style.filter='progid:DXImageTransform.Microsoft.AlphaImageLoader(src="'+e+'")'):(t=document.createElement("img"),t.src=e),t},_getIconUrl:function(e){return this.options[e+"Url"]}}),n.icon=function(e){return new n.Icon(e)},n.Icon.Default=n.Icon.extend({options:{iconSize:new n.Point(25,41),iconAnchor:new n.Point(13,41),popupAnchor:new n.Point(1,-34),shadowSize:new n.Point(41,41)},_getIconUrl:function(e){var t=e+"Url";if(this.options[t])return this.options[t];var r=n.Icon.Default.imagePath;if(!r)throw Error("Couldn't autodetect L.Icon.Default.imagePath, set it manually.");return r+"/marker-"+e+".png"}}),n.Icon.Default.imagePath=function(){var e=document.getElementsByTagName("script"),t=/\/?leaflet[\-\._]?([\w\-\._]*)\.js\??/,n,r,i,s;for(n=0,r=e.length;ns?(t.height=s+"px",n.DomUtil.addClass(e,o)):n.DomUtil.removeClass(e,o),this._containerWidth=this._container.offsetWidth},_updatePosition:function(){var e=this._map.latLngToLayerPoint(this._latlng),t=n.Browser.any3d,r=this.options.offset;t&&n.DomUtil.setPosition(this._container,e),this._containerBottom=-r.y-(t?0:e.y),this._containerLeft=-Math.round(this._containerWidth/2)+r.x+(t?0:e.x),this._container.style.bottom=this._containerBottom+"px",this._container.style.left=this._containerLeft+"px"},_zoomAnimation:function(e){var t=this._map._latLngToNewLayerPoint(this._latlng,e.zoom,e.center);n.DomUtil.setPosition(this._container,t)},_adjustPan:function(){if(!this.options.autoPan)return;var e=this._map,t=this._container.offsetHeight,r=this._containerWidth,i=new n.Point(this._containerLeft,-t-this._containerBottom);n.Browser.any3d&&i._add(n.DomUtil.getPosition(this._container));var s=e.layerPointToContainerPoint(i),o=this.options.autoPanPadding,u=e.getSize(),a=0,f=0;s.x<0&&(a=s.x-o.x),s.x+r>u.x&&(a=s.x+r-u.x+o.x),s.y<0&&(f=s.y-o.y),s.y+t>u.y&&(f=s.y+t-u.y+o.y),(a||f)&&e.panBy(new n.Point(a,f))},_onCloseButtonClick:function(e){this._close(),n.DomEvent.stop(e)}}),n.popup=function(e,t){return new n.Popup(e,t)},n.Marker.include({openPopup:function(){return this._popup&&this._map&&(this._popup.setLatLng(this._latlng),this._map.openPopup(this._popup)),this},closePopup:function(){return this._popup&&this._popup._close(),this},bindPopup:function(e,t){var r=n.point(this.options.icon.options.popupAnchor)||new n.Point(0,0);return r=r.add(n.Popup.prototype.options.offset),t&&t.offset&&(r=r.add(t.offset)),t=n.Util.extend({offset:r},t),this._popup||this.on("click",this.openPopup,this),this._popup=(new n.Popup(t,this)).setContent(e),this},unbindPopup:function(){return this._popup&&(this._popup=null,this.off("click",this.openPopup)),this}}),n.Map.include({openPopup:function(e){return this.closePopup(),this._popup=e,this.addLayer(e).fire("popupopen",{popup:this._popup})},closePopup:function(){return this._popup&&this._popup._close(),this}}),n.LayerGroup=n.Class.extend({initialize:function(e){this._layers={};var t,n;if(e)for(t=0,n=e.length;t';var t=e.firstChild;return t.style.behavior="url(#default#VML)",t&&typeof t.adj=="object"}catch(n){return!1}}(),n.Path=n.Browser.svg||!n.Browser.vml?n.Path:n.Path.extend({statics:{VML:!0,CLIP_PADDING:.02},_createElement:function(){try{return document.namespaces.add("lvml","urn:schemas-microsoft-com:vml"),function(e){return document.createElement("')}}catch(e){return function(e){return document.createElement("<"+e+' xmlns="urn:schemas-microsoft.com:vml" class="lvml">')}}}(),_initPath:function(){var e=this._container=this._createElement("shape");n.DomUtil.addClass(e,"leaflet-vml-shape"),this.options.clickable&&n.DomUtil.addClass(e,"leaflet-clickable"),e.coordsize="1 1",this._path=this._createElement("path"),e.appendChild(this._path),this._map._pathRoot.appendChild(e)},_initStyle:function(){this._updateStyle()},_updateStyle:function(){var e=this._stroke,t=this._fill,n=this.options,r=this._container;r.stroked=n.stroke,r.filled=n.fill,n.stroke?(e||(e=this._stroke=this._createElement("stroke"),e.endcap="round",r.appendChild(e)),e.weight=n.weight+"px",e.color=n.color,e.opacity=n.opacity,n.dashArray?e.dashStyle=n.dashArray.replace(/ *, */g," "):e.dashStyle=""):e&&(r.removeChild(e),this._stroke=null),n.fill?(t||(t=this._fill=this._createElement("fill"),r.appendChild(t)),t.color=n.fillColor||n.color,t.opacity=n.fillOpacity):t&&(r.removeChild(t),this._fill=null)},_updatePath:function(){var e=this._container.style;e.display="none",this._path.v=this.getPathString()+" ",e.display=""}}),n.Map.include(n.Browser.svg||!n.Browser.vml?{}:{_initPathRoot:function(){if(this._pathRoot)return;var e=this._pathRoot=document.createElement("div");e.className="leaflet-vml-container",this._panes.overlayPane.appendChild(e),this.on("moveend",this._updatePathViewport),this._updatePathViewport()}}),n.Browser.canvas=function(){return!!document.createElement("canvas").getContext}(),n.Path=n.Path.SVG&&!e.L_PREFER_CANVAS||!n.Browser.canvas?n.Path:n.Path.extend({statics:{CANVAS:!0,SVG:!1},redraw:function(){return this._map&&(this.projectLatlngs(),this._requestUpdate()),this},setStyle:function(e){return n.Util.setOptions(this,e),this._map&&(this._updateStyle(),this._requestUpdate()),this},onRemove:function(e){e.off("viewreset",this.projectLatlngs,this).off("moveend",this._updatePath,this),this._requestUpdate(),this._map=null},_requestUpdate:function(){this._map&&(n.Util.cancelAnimFrame(this._fireMapMoveEnd),this._updateRequest=n.Util.requestAnimFrame(this._fireMapMoveEnd,this._map))},_fireMapMoveEnd:function(){this.fire("moveend")},_initElements:function(){this._map._initPathRoot(),this._ctx=this._map._canvasCtx},_updateStyle:function(){var e=this.options;e.stroke&&(this._ctx.lineWidth=e.weight,this._ctx.strokeStyle=e.color),e.fill&&(this._ctx.fillStyle=e.fillColor||e.color)},_drawPath:function(){var e,t,r,i,s,o;this._ctx.beginPath();for(e=0,r=this._parts.length;es&&(o=u,s=a);s>n&&(t[o]=1,this._simplifyDPStep(e,t,n,r,o),this._simplifyDPStep(e,t,n,o,i))},_reducePoints:function(e,t){var n=[e[0]];for(var r=1,i=0,s=e.length;rt&&(n.push(e[r]),i=r);return it.max.x&&(n|=2),e.yt.max.y&&(n|=8),n},_sqDist:function(e,t){var n=t.x-e.x,r=t.y-e.y;return n*n+r*r},_sqClosestPointOnSegment:function(e,t,r,i){var s=t.x,o=t.y,u=r.x-s,a=r.y-o,f=u*u+a*a,l;return f>0&&(l=((e.x-s)*u+(e.y-o)*a)/f,l>1?(s=r.x,o=r.y):l>0&&(s+=u*l,o+=a*l)),u=e.x-s,a=e.y-o,i?u*u+a*a:new n.Point(s,o)}},n.Polyline=n.Path.extend({initialize:function(e,t){n.Path.prototype.initialize.call(this,t),this._latlngs=this._convertLatLngs(e),n.Handler.PolyEdit&&(this.editing=new n.Handler.PolyEdit(this),this.options.editable&&this.editing.enable())},options:{smoothFactor:1,noClip:!1},projectLatlngs:function(){this._originalPoints=[];for(var e=0,t=this._latlngs.length;ee.max.x||n.y-t>e.max.y||n.x+te.y!=s.y>e.y&&e.x<(s.x-i.x)*(e.y-i.y)/(s.y-i.y)+i.x&&(t=!t)}return t}}:{}),n.Circle.include(n.Path.CANVAS?{_drawPath:function(){var e=this._point;this._ctx.beginPath(),this._ctx.arc(e.x,e.y,this._radius,0,Math.PI*2,!1)},_containsPoint:function(e){var t=this._point,n=this.options.stroke?this.options.weight/2:0;return e.distanceTo(t)<=this._radius+n}}:{}),n.GeoJSON=n.FeatureGroup.extend({initialize:function(e,t){n.Util.setOptions(this,t),this._layers={},e&&this.addData(e)},addData:function(e){var t=e instanceof Array?e:e.features,r,i;if(t){for(r=0,i=t.length;r1){this._simulateClick=!1;return}var t=e.touches&&e.touches.length===1?e.touches[0]:e,r=t.target;n.DomEvent.preventDefault(e),n.Browser.touch&&r.tagName.toLowerCase()==="a"&&n.DomUtil.addClass(r,"leaflet-active"),this._moved=!1;if(this._moving)return;this._startPos=this._newPos=n.DomUtil.getPosition(this._element),this._startPoint=new n.Point(t.clientX,t.clientY),n.DomEvent.on(document,n.Draggable.MOVE,this._onMove,this),n.DomEvent.on(document,n.Draggable.END,this._onUp,this)},_onMove:function(e){if(e.touches&&e.touches.length>1)return;var t=e.touches&&e.touches.length===1?e.touches[0]:e,r=new n.Point(t.clientX,t.clientY),i=r.subtract(this._startPoint);if(!i.x&&!i.y)return;n.DomEvent.preventDefault(e),this._moved||(this.fire("dragstart"),this._moved=!0,n.Browser.touch||(n.DomUtil.disableTextSelection(),this._setMovingCursor())),this._newPos=this._startPos.add(i),this._moving=!0,n.Util.cancelAnimFrame(this._animRequest),this._animRequest=n.Util.requestAnimFrame(this._updatePosition,this,!0,this._dragStartTarget)},_updatePosition:function(){this.fire("predrag"),n.DomUtil.setPosition(this._element,this._newPos),this.fire("drag")},_onUp:function(e){if(this._simulateClick&&e.changedTouches){var t=e.changedTouches[0],r=t.target,i=this._newPos&&this._newPos.distanceTo(this._startPos)||0;r.tagName.toLowerCase()==="a"&&n.DomUtil.removeClass(r,"leaflet-active"),i200&&(this._positions.shift(),this._times.shift())}this._map.fire("move").fire("drag")},_onViewReset:function(){var e=this._map.getSize().divideBy(2),t=this._map.latLngToLayerPoint(new n.LatLng(0,0));this._initialWorldOffset=t.subtract(e).x,this._worldWidth=this._map.project(new n.LatLng(0,180)).x},_onPreDrag:function(){var e=this._map,t=this._worldWidth,n=Math.round(t/2),r=this._initialWorldOffset,i=this._draggable._newPos.x,s=(i-n+r)%t+n-r,o=(i+n+r)%t-n-r,u=Math.abs(s+r)r.inertiaThreshold||this._positions[0]===t;if(s)e.fire("moveend");else{var o=this._lastPos.subtract(this._positions[0]),u=(this._lastTime+i-this._times[0])/1e3,a=o.multiplyBy(.58/u),f=a.distanceTo(new n.Point(0,0)),l=Math.min(r.inertiaMaxSpeed,f),c=a.multiplyBy(l/f),h=l/r.inertiaDeceleration,p=c.multiplyBy(-h/2).round(),d={duration:h,easing:"ease-out"};n.Util.requestAnimFrame(n.Util.bind(function(){this._map.panBy(p,d)},this))}e.fire("dragend"),r.maxBounds&&n.Util.requestAnimFrame(this._panInsideMaxBounds,e,!0,e._container)},_panInsideMaxBounds:function(){this.panInsideBounds(this.options.maxBounds)}}),n.Map.addInitHook("addHandler","dragging",n.Map.Drag),n.Map.mergeOptions({doubleClickZoom:!0}),n.Map.DoubleClickZoom=n.Handler.extend({addHooks:function(){this._map.on("dblclick",this._onDoubleClick)},removeHooks:function(){this._map.off("dblclick",this._onDoubleClick)},_onDoubleClick:function(e){this.setView(e.latlng,this._zoom+1)}}),n.Map.addInitHook("addHandler","doubleClickZoom",n.Map.DoubleClickZoom),n.Map.mergeOptions({scrollWheelZoom:!n.Browser.touch}),n.Map.ScrollWheelZoom=n.Handler.extend({addHooks:function(){n.DomEvent.on(this._map._container,"mousewheel",this._onWheelScroll,this),this._delta=0},removeHooks:function(){n.DomEvent.off(this._map._container,"mousewheel",this._onWheelScroll)},_onWheelScroll:function(e){var t=n.DomEvent.getWheelDelta(e);this._delta+=t,this._lastMousePos=this._map.mouseEventToContainerPoint(e),clearTimeout(this._timer),this._timer=setTimeout(n.Util.bind(this._performZoom,this),40),n.DomEvent.preventDefault(e)},_performZoom:function(){var e=this._map,t=Math.round(this._delta),n=e.getZoom();t=Math.max(Math.min(t,4),-4),t=e._limitZoom(n+t)-n,this._delta=0;if(!t)return;var r=n+t,i=this._getCenterForScrollWheelZoom(this._lastMousePos,r);e.setView(i,r)},_getCenterForScrollWheelZoom:function(e,t){var n=this._map,r=n.getZoomScale(t),i=n.getSize().divideBy(2),s=e.subtract(i).multiplyBy(1-1/r),o=n._getTopLeftPoint().add(i).add(s);return n.unproject(o)}}),n.Map.addInitHook("addHandler","scrollWheelZoom",n.Map.ScrollWheelZoom),n.Util.extend(n.DomEvent,{addDoubleTapListener:function(e,t,n){function l(e){if(e.touches.length!==1)return;var t=Date.now(),n=t-(r||t);o=e.touches[0],i=n>0&&n<=s,r=t}function c(e){i&&(o.type="dblclick",t(o),r=null)}var r,i=!1,s=250,o,u="_leaflet_",a="touchstart",f="touchend";return e[u+a+n]=l,e[u+f+n]=c,e.addEventListener(a,l,!1),e.addEventListener(f,c,!1),this},removeDoubleTapListener:function(e,t){var n="_leaflet_";return e.removeEventListener(e,e[n+"touchstart"+t],!1),e.removeEventListener(e,e[n+"touchend"+t],!1),this}}),n.Map.mergeOptions({touchZoom:n.Browser.touch&&!n.Browser.android23}),n.Map.TouchZoom=n.Handler.extend({addHooks:function(){n.DomEvent.on(this._map._container,"touchstart",this._onTouchStart,this)},removeHooks:function(){n.DomEvent.off(this._map._container,"touchstart",this._onTouchStart,this)},_onTouchStart:function(e){var t=this._map;if(!e.touches||e.touches.length!==2||t._animatingZoom||this._zooming)return;var r=t.mouseEventToLayerPoint(e.touches[0]),i=t.mouseEventToLayerPoint(e.touches[1]),s=t._getCenterLayerPoint();this._startCenter=r.add(i).divideBy(2,!0),this._startDist=r.distanceTo(i),this._moved=!1,this._zooming=!0,this._centerOffset=s.subtract(this._startCenter),n.DomEvent.on(document,"touchmove",this._onTouchMove,this).on(document,"touchend",this._onTouchEnd,this),n.DomEvent.preventDefault(e)},_onTouchMove:function(e){if(!e.touches||e.touches.length!==2)return;var t=this._map,r=t.mouseEventToLayerPoint(e.touches[0]),i=t.mouseEventToLayerPoint(e.touches[1]);this._scale=r.distanceTo(i)/this._startDist,this._delta=r.add(i).divideBy(2,!0).subtract(this._startCenter);if(this._scale===1)return;this._moved||(n.DomUtil.addClass(t._mapPane,"leaflet-zoom-anim leaflet-touching"),t.fire("movestart").fire("zoomstart")._prepareTileBg(),this._moved=!0),n.Util.cancelAnimFrame(this._animRequest),this._animRequest=n.Util.requestAnimFrame(this._updateOnMove,this,!0,this._map._container),n.DomEvent.preventDefault(e)},_updateOnMove:function(){var e=this._map,t=this._getScaleOrigin(),r=e.layerPointToLatLng(t);e.fire("zoomanim",{center:r,zoom:e.getScaleZoom(this._scale)}),e._tileBg.style[n.DomUtil.TRANSFORM]=n.DomUtil.getTranslateString(this._delta)+" "+n.DomUtil.getScaleString(this._scale,this._startCenter)},_onTouchEnd:function(e){if(!this._moved||!this._zooming)return;var t=this._map;this._zooming=!1,n.DomUtil.removeClass(t._mapPane,"leaflet-touching"),n.DomEvent.off(document,"touchmove",this._onTouchMove).off(document,"touchend",this._onTouchEnd);var r=this._getScaleOrigin(),i=t.layerPointToLatLng(r),s=t.getZoom(),o=t.getScaleZoom(this._scale)-s,u=o>0?Math.ceil(o):Math.floor(o),a=t._limitZoom(s+u);t.fire("zoomanim",{center:i,zoom:a}),t._runAnimation(i,a,t.getZoomScale(a)/this._scale,r,!0)},_getScaleOrigin:function(){var e=this._centerOffset.subtract(this._delta).divideBy(this._scale);return this._startCenter.add(e)}}),n.Map.addInitHook("addHandler","touchZoom",n.Map.TouchZoom),n.Map.mergeOptions({boxZoom:!0}),n.Map.BoxZoom=n.Handler.extend({initialize:function(e){this._map=e,this._container=e._container,this._pane=e._panes.overlayPane},addHooks:function(){n.DomEvent.on(this._container,"mousedown",this._onMouseDown,this)},removeHooks:function(){n.DomEvent.off(this._container,"mousedown",this._onMouseDown)},_onMouseDown:function(e){if(!e.shiftKey||e.which!==1&&e.button!==1)return!1;n.DomUtil.disableTextSelection(),this._startLayerPoint=this._map.mouseEventToLayerPoint(e),this._box=n.DomUtil.create("div","leaflet-zoom-box",this._pane),n.DomUtil.setPosition(this._box,this._startLayerPoint),this._container.style.cursor="crosshair",n.DomEvent.on(document,"mousemove",this._onMouseMove,this).on(document,"mouseup",this._onMouseUp,this).preventDefault(e),this._map.fire("boxzoomstart")},_onMouseMove:function(e){var t=this._startLayerPoint,r=this._box,i=this._map.mouseEventToLayerPoint(e),s=i.subtract(t),o=new n.Point(Math.min(i.x,t.x),Math.min(i.y,t.y));n.DomUtil.setPosition(r,o),r.style.width=Math.abs(s.x)-4+"px",r.style.height=Math.abs(s.y)-4+"px"},_onMouseUp:function(e){this._pane.removeChild(this._box),this._container.style.cursor="",n.DomUtil.enableTextSelection(),n.DomEvent.off(document,"mousemove",this._onMouseMove).off(document,"mouseup",this._onMouseUp);var t=this._map,r=t.mouseEventToLayerPoint(e),i=new n.LatLngBounds(t.layerPointToLatLng(this._startLayerPoint),t.layerPointToLatLng(r));t.fitBounds(i),t.fire("boxzoomend",{boxZoomBounds:i})}}),n.Map.addInitHook("addHandler","boxZoom",n.Map.BoxZoom),n.Map.mergeOptions({keyboard:!0,keyboardPanOffset:80,keyboardZoomOffset:1}),n.Map.Keyboard=n.Handler.extend({keyCodes:{left:[37],right:[39],down:[40],up:[38],zoomIn:[187,107,61],zoomOut:[189,109]},initialize:function(e){this._map=e,this._setPanOffset(e.options.keyboardPanOffset),this._setZoomOffset(e.options.keyboardZoomOffset)},addHooks:function(){var e=this._map._container;e.tabIndex===-1&&(e.tabIndex="0"),n.DomEvent.addListener(e,"focus",this._onFocus,this).addListener(e,"blur",this._onBlur,this).addListener(e,"mousedown",this._onMouseDown,this),this._map.on("focus",this._addHooks,this).on("blur",this._removeHooks,this)},removeHooks:function(){this._removeHooks();var e=this._map._container;n.DomEvent.removeListener(e,"focus",this._onFocus,this).removeListener(e,"blur",this._onBlur,this).removeListener(e,"mousedown",this._onMouseDown,this),this._map.off("focus",this._addHooks,this).off("blur",this._removeHooks,this)},_onMouseDown:function(){this._focused||this._map._container.focus()},_onFocus:function(){this._focused=!0,this._map.fire("focus")},_onBlur:function(){this._focused=!1,this._map.fire("blur")},_setPanOffset:function(e){var t=this._panKeys={},n=this.keyCodes,r,i;for(r=0,i=n.left.length;re&&(n._index+=t)})},_createMiddleMarker:function(e,t){var n=this._getMiddleLatLng(e,t),r=this._createMarker(n),i,s,o;r.setOpacity(.6),e._middleRight=t._middleLeft=r,s=function(){var s=t._index;r._index=s,r.off("click",i).on("click",this._onMarkerClick,this),n.lat=r.getLatLng().lat,n.lng=r.getLatLng().lng,this._poly.spliceLatLngs(s,0,n),this._markers.splice(s,0,r),r.setOpacity(1),this._updateIndexes(s,1),t._index++,this._updatePrevNext(e,r),this._updatePrevNext(r,t)},o=function(){r.off("dragstart",s,this),r.off("dragend",o,this),this._createMiddleMarker(e,r),this._createMiddleMarker(r,t)},i=function(){s.call(this),o.call(this),this._poly.fire("edit")},r.on("click",i,this).on("dragstart",s,this).on("dragend",o,this),this._markerGroup.addLayer(r)},_updatePrevNext:function(e,t){e._next=t,t._prev=e},_getMiddleLatLng:function(e,t){var n=this._poly._map,r=n.latLngToLayerPoint(e.getLatLng()),i=n.latLngToLayerPoint(t.getLatLng());return n.layerPointToLatLng(r._add(i).divideBy(2))}}),n.Control=n.Class.extend({options:{position:"topright"},initialize:function(e){n.Util.setOptions(this,e)},getPosition:function(){return this.options.position},setPosition:function(e){var t=this._map;return t&&t.removeControl(this),this.options.position=e,t&&t.addControl(this),this},addTo:function(e){this._map=e;var t=this._container=this.onAdd(e),r=this.getPosition(),i=e._controlCorners[r];return n.DomUtil.addClass(t,"leaflet-control"),r.indexOf("bottom")!==-1?i.insertBefore(t,i.firstChild):i.appendChild(t),this},removeFrom:function(e){var t=this.getPosition(),n=e._controlCorners[t];return n.removeChild(this._container),this._map=null,this.onRemove&&this.onRemove(e),this}}),n.control=function(e){return new n.Control(e)},n.Map.include({addControl:function(e){return e.addTo(this),this},removeControl:function(e){return e.removeFrom(this),this},_initControlPos:function(){function i(i,s){var o=t+i+" "+t+s;e[i+s]=n.DomUtil.create("div",o,r)}var e=this._controlCorners={},t="leaflet-",r=this._controlContainer=n.DomUtil.create("div",t+"control-container",this._container);i("top","left"),i("top","right"),i("bottom","left"),i("bottom","right")}}),n.Control.Zoom=n.Control.extend({options:{position:"topleft"},onAdd:function(e){var t="leaflet-control-zoom",r=n.DomUtil.create("div",t);return this._createButton("Zoom in",t+"-in",r,e.zoomIn,e),this._createButton("Zoom out",t+"-out",r,e.zoomOut,e),r},_createButton:function(e,t,r,i,s){var o=n.DomUtil.create("a",t,r);return o.href="#",o.title=e,n.DomEvent.on(o,"click",n.DomEvent.stopPropagation).on(o,"click",n.DomEvent.preventDefault).on(o,"click",i,s).on(o,"dblclick",n.DomEvent.stopPropagation),o}}),n.Map.mergeOptions({zoomControl:!0}),n.Map.addInitHook(function(){this.options.zoomControl&&(this.zoomControl=new n.Control.Zoom,this.addControl(this.zoomControl))}),n.control.zoom=function(e){return new n.Control.Zoom(e)},n.Control.Attribution=n.Control.extend({options:{position:"bottomright",prefix:'Powered by Leaflet'},initialize:function(e){n.Util.setOptions(this,e),this._attributions={}},onAdd:function(e){return this._container=n.DomUtil.create("div","leaflet-control-attribution"),n.DomEvent.disableClickPropagation(this._container),e.on("layeradd",this._onLayerAdd,this).on("layerremove",this._onLayerRemove,this),this._update(),this._container},onRemove:function(e){e.off("layeradd",this._onLayerAdd).off("layerremove",this._onLayerRemove)},setPrefix:function(e){return this.options.prefix=e,this._update(),this},addAttribution:function(e){if(!e)return;return this._attributions[e]||(this._attributions[e]=0),this._attributions[e]++,this._update(),this},removeAttribution:function(e){if(!e)return;return this._attributions[e]--,this._update(),this},_update:function(){if(!this._map)return;var e=[];for(var t in this._attributions)this._attributions.hasOwnProperty(t)&&this._attributions[t]&&e.push(t);var n=[];this.options.prefix&&n.push(this.options.prefix),e.length&&n.push(e.join(", ")),this._container.innerHTML=n.join(" — ")},_onLayerAdd:function(e){e.layer.getAttribution&&this.addAttribution(e.layer.getAttribution())},_onLayerRemove:function(e){e.layer.getAttribution&&this.removeAttribution(e.layer.getAttribution())}}),n.Map.mergeOptions({attributionControl:!0}),n.Map.addInitHook(function(){this.options.attributionControl&&(this.attributionControl=(new n.Control.Attribution).addTo(this))}),n.control.attribution=function(e){return new n.Control.Attribution(e)},n.Control.Scale=n.Control.extend({options:{position:"bottomleft",maxWidth:100,metric:!0,imperial:!0,updateWhenIdle:!1},onAdd:function(e){this._map=e;var t="leaflet-control-scale",r=n.DomUtil.create("div",t),i=this.options;return this._addScales(i,t,r),e.on(i.updateWhenIdle?"moveend":"move",this._update,this),this._update(),r},onRemove:function(e){e.off(this.options.updateWhenIdle?"moveend":"move",this._update,this)},_addScales:function(e,t,r){e.metric&&(this._mScale=n.DomUtil.create("div",t+"-line",r)),e.imperial&&(this._iScale=n.DomUtil.create("div",t+"-line",r))},_update:function(){var e=this._map.getBounds(),t=e.getCenter().lat,n=6378137*Math.PI*Math.cos(t*Math.PI/180),r=n*(e.getNorthEast().lng-e.getSouthWest().lng)/180,i=this._map.getSize(),s=this.options,o=0;i.x>0&&(o=r*(s.maxWidth/i.x)),this._updateScales(s,o)},_updateScales:function(e,t){e.metric&&t&&this._updateMetric(t),e.imperial&&t&&this._updateImperial(t)},_updateMetric:function(e){var t=this._getRoundNum(e);this._mScale.style.width=this._getScaleWidth(t/e)+"px",this._mScale.innerHTML=t<1e3?t+" m":t/1e3+" km"},_updateImperial:function(e){var t=e*3.2808399,n=this._iScale,r,i,s;t>5280?(r=t/5280,i=this._getRoundNum(r),n.style.width=this._getScaleWidth(i/r)+"px",n.innerHTML=i+" mi"):(s=this._getRoundNum(t),n.style.width=this._getScaleWidth(s/t)+"px",n.innerHTML=s+" ft")},_getScaleWidth:function(e){return Math.round(this.options.maxWidth*e)-10},_getRoundNum:function(e){var t=Math.pow(10,(Math.floor(e)+"").length-1),n=e/t;return n=n>=10?10:n>=5?5:n>=3?3:n>=2?2:1,t*n}}),n.control.scale=function(e){return new n.Control.Scale(e)},n.Control.Layers=n.Control.extend({options:{collapsed:!0,position:"topright",autoZIndex:!0},initialize:function(e,t,r){n.Util.setOptions(this,r),this._layers={},this._lastZIndex=0;for(var i in e)e.hasOwnProperty(i)&&this._addLayer(e[i],i);for(i in t)t.hasOwnProperty(i)&&this._addLayer(t[i],i,!0)},onAdd:function(e){return this._initLayout(),this._update(),this._container},addBaseLayer:function(e,t){return this._addLayer(e,t),this._update(),this},addOverlay:function(e,t){return this._addLayer(e,t,!0),this._update(),this},removeLayer:function(e){var t=n.Util.stamp(e);return delete this._layers[t],this._update(),this},_initLayout:function(){var e="leaflet-control-layers",t=this._container=n.DomUtil.create("div",e);n.Browser.touch?n.DomEvent.on(t,"click",n.DomEvent.stopPropagation):n.DomEvent.disableClickPropagation(t);var r=this._form=n.DomUtil.create("form",e+"-list");if(this.options.collapsed){n.DomEvent.on(t,"mouseover",this._expand,this).on(t,"mouseout",this._collapse,this);var i=this._layersLink=n.DomUtil.create("a",e+"-toggle",t);i.href="#",i.title="Layers",n.Browser.touch?n.DomEvent.on(i,"click",n.DomEvent.stopPropagation).on(i,"click",n.DomEvent.preventDefault).on(i,"click",this._expand,this):n.DomEvent.on(i,"focus",this._expand,this),this._map.on("movestart",this._collapse,this)}else this._expand();this._baseLayersList=n.DomUtil.create("div",e+"-base",r),this._separator=n.DomUtil.create("div",e+"-separator",r),this._overlaysList=n.DomUtil.create("div",e+"-overlays",r),t.appendChild(r)},_addLayer:function(e,t,r){var i=n.Util.stamp(e);this._layers[i]={layer:e,name:t,overlay:r},this.options.autoZIndex&&e.setZIndex&&(this._lastZIndex++,e.setZIndex(this._lastZIndex))},_update:function(){if(!this._container)return;this._baseLayersList.innerHTML="",this._overlaysList.innerHTML="";var e=!1,t=!1;for(var n in this._layers)if(this._layers.hasOwnProperty(n)){var r=this._layers[n];this._addItem(r),t=t||r.overlay,e=e||!r.overlay}this._separator.style.display=t&&e?"":"none"},_createRadioElement:function(e,t){var n='.5&&this._getLoadedTilesPercentage(e)<.5){e.style.visibility="hidden",e.empty=!0,this._stopLoadingImages(e);return}t||(t=this._tileBg=this._createPane("leaflet-tile-pane",this._mapPane),t.style.zIndex=1),t.style[n.DomUtil.TRANSFORM]="",t.style.visibility="hidden",t.empty=!0,e.empty=!1,this._tilePane=this._panes.tilePane=t;var r=this._tileBg=e;n.DomUtil.addClass(r,"leaflet-zoom-animated"),this._stopLoadingImages(r)},_getLoadedTilesPercentage:function(e){var t=e.getElementsByTagName("img"),n,r,i=0;for(n=0,r=t.length;n 1) { + this._moved = true; + return; + } + + var first = (e.touches && e.touches.length === 1 ? e.touches[0] : e), + newPoint = new Point(first.clientX, first.clientY), + offset = newPoint.subtract(this._startPoint); + + if (!offset.x && !offset.y) { return; } + if (Math.abs(offset.x) + Math.abs(offset.y) < this.options.clickTolerance) { return; } + + preventDefault(e); + + if (!this._moved) { + // @event dragstart: Event + // Fired when a drag starts + this.fire('dragstart'); + + this._moved = true; + this._startPos = getPosition(this._element).subtract(offset); + + addClass(document.body, 'leaflet-dragging'); + + this._lastTarget = e.target || e.srcElement; + // IE and Edge do not give the element, so fetch it + // if necessary + if ((window.SVGElementInstance) && (this._lastTarget instanceof SVGElementInstance)) { + this._lastTarget = this._lastTarget.correspondingUseElement; + } + addClass(this._lastTarget, 'leaflet-drag-target'); + } + + this._newPos = this._startPos.add(offset); + this._moving = true; + + cancelAnimFrame(this._animRequest); + this._lastEvent = e; + this._animRequest = requestAnimFrame(this._updatePosition, this, true); + }, + + _updatePosition: function () { + var e = {originalEvent: this._lastEvent}; + + // @event predrag: Event + // Fired continuously during dragging *before* each corresponding + // update of the element's position. + this.fire('predrag', e); + setPosition(this._element, this._newPos); + + // @event drag: Event + // Fired continuously during dragging. + this.fire('drag', e); + }, + + _onUp: function (e) { + // Ignore simulated events, since we handle both touch and + // mouse explicitly; otherwise we risk getting duplicates of + // touch events, see #4315. + // Also ignore the event if disabled; this happens in IE11 + // under some circumstances, see #3666. + if (e._simulated || !this._enabled) { return; } + this.finishDrag(); + }, + + finishDrag: function () { + removeClass(document.body, 'leaflet-dragging'); + + if (this._lastTarget) { + removeClass(this._lastTarget, 'leaflet-drag-target'); + this._lastTarget = null; + } + + for (var i in MOVE) { + off(document, MOVE[i], this._onMove, this); + off(document, END[i], this._onUp, this); + } + + enableImageDrag(); + enableTextSelection(); + + if (this._moved && this._moving) { + // ensure drag is not fired after dragend + cancelAnimFrame(this._animRequest); + + // @event dragend: DragEndEvent + // Fired when the drag ends. + this.fire('dragend', { + distance: this._newPos.distanceTo(this._startPos) + }); + } + + this._moving = false; + _dragging = false; + } + + }); + + /* + * @namespace LineUtil + * + * Various utility functions for polyine points processing, used by Leaflet internally to make polylines lightning-fast. + */ + +// Simplify polyline with vertex reduction and Douglas-Peucker simplification. +// Improves rendering performance dramatically by lessening the number of points to draw. + +// @function simplify(points: Point[], tolerance: Number): Point[] +// Dramatically reduces the number of points in a polyline while retaining +// its shape and returns a new array of simplified points, using the +// [Douglas-Peucker algorithm](http://en.wikipedia.org/wiki/Douglas-Peucker_algorithm). +// Used for a huge performance boost when processing/displaying Leaflet polylines for +// each zoom level and also reducing visual noise. tolerance affects the amount of +// simplification (lesser value means higher quality but slower and with more points). +// Also released as a separated micro-library [Simplify.js](http://mourner.github.com/simplify-js/). + function simplify(points, tolerance) { + if (!tolerance || !points.length) { + return points.slice(); + } + + var sqTolerance = tolerance * tolerance; + + // stage 1: vertex reduction + points = _reducePoints(points, sqTolerance); + + // stage 2: Douglas-Peucker simplification + points = _simplifyDP(points, sqTolerance); + + return points; + } + +// @function pointToSegmentDistance(p: Point, p1: Point, p2: Point): Number +// Returns the distance between point `p` and segment `p1` to `p2`. + function pointToSegmentDistance(p, p1, p2) { + return Math.sqrt(_sqClosestPointOnSegment(p, p1, p2, true)); + } + +// @function closestPointOnSegment(p: Point, p1: Point, p2: Point): Number +// Returns the closest point from a point `p` on a segment `p1` to `p2`. + function closestPointOnSegment(p, p1, p2) { + return _sqClosestPointOnSegment(p, p1, p2); + } + +// Douglas-Peucker simplification, see http://en.wikipedia.org/wiki/Douglas-Peucker_algorithm + function _simplifyDP(points, sqTolerance) { + + var len = points.length, + ArrayConstructor = typeof Uint8Array !== undefined + '' ? Uint8Array : Array, + markers = new ArrayConstructor(len); + + markers[0] = markers[len - 1] = 1; + + _simplifyDPStep(points, markers, sqTolerance, 0, len - 1); + + var i, + newPoints = []; + + for (i = 0; i < len; i++) { + if (markers[i]) { + newPoints.push(points[i]); + } + } + + return newPoints; + } + + function _simplifyDPStep(points, markers, sqTolerance, first, last) { + + var maxSqDist = 0, + index, i, sqDist; + + for (i = first + 1; i <= last - 1; i++) { + sqDist = _sqClosestPointOnSegment(points[i], points[first], points[last], true); + + if (sqDist > maxSqDist) { + index = i; + maxSqDist = sqDist; + } + } + + if (maxSqDist > sqTolerance) { + markers[index] = 1; + + _simplifyDPStep(points, markers, sqTolerance, first, index); + _simplifyDPStep(points, markers, sqTolerance, index, last); + } + } + +// reduce points that are too close to each other to a single point + function _reducePoints(points, sqTolerance) { + var reducedPoints = [points[0]]; + + for (var i = 1, prev = 0, len = points.length; i < len; i++) { + if (_sqDist(points[i], points[prev]) > sqTolerance) { + reducedPoints.push(points[i]); + prev = i; + } + } + if (prev < len - 1) { + reducedPoints.push(points[len - 1]); + } + return reducedPoints; + } + + var _lastCode; + +// @function clipSegment(a: Point, b: Point, bounds: Bounds, useLastCode?: Boolean, round?: Boolean): Point[]|Boolean +// Clips the segment a to b by rectangular bounds with the +// [Cohen-Sutherland algorithm](https://en.wikipedia.org/wiki/Cohen%E2%80%93Sutherland_algorithm) +// (modifying the segment points directly!). Used by Leaflet to only show polyline +// points that are on the screen or near, increasing performance. + function clipSegment(a, b, bounds, useLastCode, round) { + var codeA = useLastCode ? _lastCode : _getBitCode(a, bounds), + codeB = _getBitCode(b, bounds), + + codeOut, p, newCode; + + // save 2nd code to avoid calculating it on the next segment + _lastCode = codeB; + + while (true) { + // if a,b is inside the clip window (trivial accept) + if (!(codeA | codeB)) { + return [a, b]; + } + + // if a,b is outside the clip window (trivial reject) + if (codeA & codeB) { + return false; + } + + // other cases + codeOut = codeA || codeB; + p = _getEdgeIntersection(a, b, codeOut, bounds, round); + newCode = _getBitCode(p, bounds); + + if (codeOut === codeA) { + a = p; + codeA = newCode; + } else { + b = p; + codeB = newCode; + } + } + } + + function _getEdgeIntersection(a, b, code, bounds, round) { + var dx = b.x - a.x, + dy = b.y - a.y, + min = bounds.min, + max = bounds.max, + x, y; + + if (code & 8) { // top + x = a.x + dx * (max.y - a.y) / dy; + y = max.y; + + } else if (code & 4) { // bottom + x = a.x + dx * (min.y - a.y) / dy; + y = min.y; + + } else if (code & 2) { // right + x = max.x; + y = a.y + dy * (max.x - a.x) / dx; + + } else if (code & 1) { // left + x = min.x; + y = a.y + dy * (min.x - a.x) / dx; + } + + return new Point(x, y, round); + } + + function _getBitCode(p, bounds) { + var code = 0; + + if (p.x < bounds.min.x) { // left + code |= 1; + } else if (p.x > bounds.max.x) { // right + code |= 2; + } + + if (p.y < bounds.min.y) { // bottom + code |= 4; + } else if (p.y > bounds.max.y) { // top + code |= 8; + } + + return code; + } + +// square distance (to avoid unnecessary Math.sqrt calls) + function _sqDist(p1, p2) { + var dx = p2.x - p1.x, + dy = p2.y - p1.y; + return dx * dx + dy * dy; + } + +// return closest point on segment or distance to that point + function _sqClosestPointOnSegment(p, p1, p2, sqDist) { + var x = p1.x, + y = p1.y, + dx = p2.x - x, + dy = p2.y - y, + dot = dx * dx + dy * dy, + t; + + if (dot > 0) { + t = ((p.x - x) * dx + (p.y - y) * dy) / dot; + + if (t > 1) { + x = p2.x; + y = p2.y; + } else if (t > 0) { + x += dx * t; + y += dy * t; + } + } + + dx = p.x - x; + dy = p.y - y; + + return sqDist ? dx * dx + dy * dy : new Point(x, y); + } + + + function _flat(latlngs) { + // true if it's a flat array of latlngs; false if nested + return !isArray(latlngs[0]) || (typeof latlngs[0][0] !== 'object' && typeof latlngs[0][0] !== 'undefined'); + } + + + var LineUtil = (Object.freeze || Object)({ + simplify: simplify, + pointToSegmentDistance: pointToSegmentDistance, + closestPointOnSegment: closestPointOnSegment, + clipSegment: clipSegment, + _getEdgeIntersection: _getEdgeIntersection, + _getBitCode: _getBitCode, + _sqClosestPointOnSegment: _sqClosestPointOnSegment, + _flat: _flat + }); + + /* + * @namespace PolyUtil + * Various utility functions for polygon geometries. + */ + + /* @function clipPolygon(points: Point[], bounds: Bounds, round?: Boolean): Point[] + * Clips the polygon geometry defined by the given `points` by the given bounds (using the [Sutherland-Hodgeman algorithm](https://en.wikipedia.org/wiki/Sutherland%E2%80%93Hodgman_algorithm)). + * Used by Leaflet to only show polygon points that are on the screen or near, increasing + * performance. Note that polygon points needs different algorithm for clipping + * than polyline, so there's a seperate method for it. + */ + function clipPolygon(points, bounds, round) { + var clippedPoints, + edges = [1, 4, 2, 8], + i, j, k, + a, b, + len, edge, p; + + for (i = 0, len = points.length; i < len; i++) { + points[i]._code = _getBitCode(points[i], bounds); + } + + // for each edge (left, bottom, right, top) + for (k = 0; k < 4; k++) { + edge = edges[k]; + clippedPoints = []; + + for (i = 0, len = points.length, j = len - 1; i < len; j = i++) { + a = points[i]; + b = points[j]; + + // if a is inside the clip window + if (!(a._code & edge)) { + // if b is outside the clip window (a->b goes out of screen) + if (b._code & edge) { + p = _getEdgeIntersection(b, a, edge, bounds, round); + p._code = _getBitCode(p, bounds); + clippedPoints.push(p); + } + clippedPoints.push(a); + + // else if b is inside the clip window (a->b enters the screen) + } else if (!(b._code & edge)) { + p = _getEdgeIntersection(b, a, edge, bounds, round); + p._code = _getBitCode(p, bounds); + clippedPoints.push(p); + } + } + points = clippedPoints; + } + + return points; + } + + + var PolyUtil = (Object.freeze || Object)({ + clipPolygon: clipPolygon + }); + + /* + * @namespace Projection + * @section + * Leaflet comes with a set of already defined Projections out of the box: + * + * @projection L.Projection.LonLat + * + * Equirectangular, or Plate Carree projection — the most simple projection, + * mostly used by GIS enthusiasts. Directly maps `x` as longitude, and `y` as + * latitude. Also suitable for flat worlds, e.g. game maps. Used by the + * `EPSG:4326` and `Simple` CRS. + */ + + var LonLat = { + project: function (latlng) { + return new Point(latlng.lng, latlng.lat); + }, + + unproject: function (point) { + return new LatLng(point.y, point.x); + }, + + bounds: new Bounds([-180, -90], [180, 90]) + }; + + /* + * @namespace Projection + * @projection L.Projection.Mercator + * + * Elliptical Mercator projection — more complex than Spherical Mercator. Takes into account that Earth is a geoid, not a perfect sphere. Used by the EPSG:3395 CRS. + */ + + var Mercator = { + R: 6378137, + R_MINOR: 6356752.314245179, + + bounds: new Bounds([-20037508.34279, -15496570.73972], [20037508.34279, 18764656.23138]), + + project: function (latlng) { + var d = Math.PI / 180, + r = this.R, + y = latlng.lat * d, + tmp = this.R_MINOR / r, + e = Math.sqrt(1 - tmp * tmp), + con = e * Math.sin(y); + + var ts = Math.tan(Math.PI / 4 - y / 2) / Math.pow((1 - con) / (1 + con), e / 2); + y = -r * Math.log(Math.max(ts, 1E-10)); + + return new Point(latlng.lng * d * r, y); + }, + + unproject: function (point) { + var d = 180 / Math.PI, + r = this.R, + tmp = this.R_MINOR / r, + e = Math.sqrt(1 - tmp * tmp), + ts = Math.exp(-point.y / r), + phi = Math.PI / 2 - 2 * Math.atan(ts); + + for (var i = 0, dphi = 0.1, con; i < 15 && Math.abs(dphi) > 1e-7; i++) { + con = e * Math.sin(phi); + con = Math.pow((1 - con) / (1 + con), e / 2); + dphi = Math.PI / 2 - 2 * Math.atan(ts * con) - phi; + phi += dphi; + } + + return new LatLng(phi * d, point.x * d / r); + } + }; + + /* + * @class Projection + + * An object with methods for projecting geographical coordinates of the world onto + * a flat surface (and back). See [Map projection](http://en.wikipedia.org/wiki/Map_projection). + + * @property bounds: Bounds + * The bounds (specified in CRS units) where the projection is valid + + * @method project(latlng: LatLng): Point + * Projects geographical coordinates into a 2D point. + * Only accepts actual `L.LatLng` instances, not arrays. + + * @method unproject(point: Point): LatLng + * The inverse of `project`. Projects a 2D point into a geographical location. + * Only accepts actual `L.Point` instances, not arrays. + + */ + + + + + var index = (Object.freeze || Object)({ + LonLat: LonLat, + Mercator: Mercator, + SphericalMercator: SphericalMercator + }); + + /* + * @namespace CRS + * @crs L.CRS.EPSG3395 + * + * Rarely used by some commercial tile providers. Uses Elliptical Mercator projection. + */ + var EPSG3395 = extend({}, Earth, { + code: 'EPSG:3395', + projection: Mercator, + + transformation: (function () { + var scale = 0.5 / (Math.PI * Mercator.R); + return toTransformation(scale, 0.5, -scale, 0.5); + }()) + }); + + /* + * @namespace CRS + * @crs L.CRS.EPSG4326 + * + * A common CRS among GIS enthusiasts. Uses simple Equirectangular projection. + * + * Leaflet 1.0.x complies with the [TMS coordinate scheme for EPSG:4326](https://wiki.osgeo.org/wiki/Tile_Map_Service_Specification#global-geodetic), + * which is a breaking change from 0.7.x behaviour. If you are using a `TileLayer` + * with this CRS, ensure that there are two 256x256 pixel tiles covering the + * whole earth at zoom level zero, and that the tile coordinate origin is (-180,+90), + * or (-180,-90) for `TileLayer`s with [the `tms` option](#tilelayer-tms) set. + */ + + var EPSG4326 = extend({}, Earth, { + code: 'EPSG:4326', + projection: LonLat, + transformation: toTransformation(1 / 180, 1, -1 / 180, 0.5) + }); + + /* + * @namespace CRS + * @crs L.CRS.Simple + * + * A simple CRS that maps longitude and latitude into `x` and `y` directly. + * May be used for maps of flat surfaces (e.g. game maps). Note that the `y` + * axis should still be inverted (going from bottom to top). `distance()` returns + * simple euclidean distance. + */ + + var Simple = extend({}, CRS, { + projection: LonLat, + transformation: toTransformation(1, 0, -1, 0), + + scale: function (zoom) { + return Math.pow(2, zoom); + }, + + zoom: function (scale) { + return Math.log(scale) / Math.LN2; + }, + + distance: function (latlng1, latlng2) { + var dx = latlng2.lng - latlng1.lng, + dy = latlng2.lat - latlng1.lat; + + return Math.sqrt(dx * dx + dy * dy); + }, + + infinite: true + }); + + CRS.Earth = Earth; + CRS.EPSG3395 = EPSG3395; + CRS.EPSG3857 = EPSG3857; + CRS.EPSG900913 = EPSG900913; + CRS.EPSG4326 = EPSG4326; + CRS.Simple = Simple; + + /* + * @class Layer + * @inherits Evented + * @aka L.Layer + * @aka ILayer + * + * A set of methods from the Layer base class that all Leaflet layers use. + * Inherits all methods, options and events from `L.Evented`. + * + * @example + * + * ```js + * var layer = L.Marker(latlng).addTo(map); + * layer.addTo(map); + * layer.remove(); + * ``` + * + * @event add: Event + * Fired after the layer is added to a map + * + * @event remove: Event + * Fired after the layer is removed from a map + */ + + + var Layer = Evented.extend({ + + // Classes extending `L.Layer` will inherit the following options: + options: { + // @option pane: String = 'overlayPane' + // By default the layer will be added to the map's [overlay pane](#map-overlaypane). Overriding this option will cause the layer to be placed on another pane by default. + pane: 'overlayPane', + + // @option attribution: String = null + // String to be shown in the attribution control, describes the layer data, e.g. "© Mapbox". + attribution: null, + + bubblingMouseEvents: true + }, + + /* @section + * Classes extending `L.Layer` will inherit the following methods: + * + * @method addTo(map: Map): this + * Adds the layer to the given map + */ + addTo: function (map) { + map.addLayer(this); + return this; + }, + + // @method remove: this + // Removes the layer from the map it is currently active on. + remove: function () { + return this.removeFrom(this._map || this._mapToAdd); + }, + + // @method removeFrom(map: Map): this + // Removes the layer from the given map + removeFrom: function (obj) { + if (obj) { + obj.removeLayer(this); + } + return this; + }, + + // @method getPane(name? : String): HTMLElement + // Returns the `HTMLElement` representing the named pane on the map. If `name` is omitted, returns the pane for this layer. + getPane: function (name) { + return this._map.getPane(name ? (this.options[name] || name) : this.options.pane); + }, + + addInteractiveTarget: function (targetEl) { + this._map._targets[stamp(targetEl)] = this; + return this; + }, + + removeInteractiveTarget: function (targetEl) { + delete this._map._targets[stamp(targetEl)]; + return this; + }, + + // @method getAttribution: String + // Used by the `attribution control`, returns the [attribution option](#gridlayer-attribution). + getAttribution: function () { + return this.options.attribution; + }, + + _layerAdd: function (e) { + var map = e.target; + + // check in case layer gets added and then removed before the map is ready + if (!map.hasLayer(this)) { return; } + + this._map = map; + this._zoomAnimated = map._zoomAnimated; + + if (this.getEvents) { + var events = this.getEvents(); + map.on(events, this); + this.once('remove', function () { + map.off(events, this); + }, this); + } + + this.onAdd(map); + + if (this.getAttribution && map.attributionControl) { + map.attributionControl.addAttribution(this.getAttribution()); + } + + this.fire('add'); + map.fire('layeradd', {layer: this}); + } + }); + + /* @section Extension methods + * @uninheritable + * + * Every layer should extend from `L.Layer` and (re-)implement the following methods. + * + * @method onAdd(map: Map): this + * Should contain code that creates DOM elements for the layer, adds them to `map panes` where they should belong and puts listeners on relevant map events. Called on [`map.addLayer(layer)`](#map-addlayer). + * + * @method onRemove(map: Map): this + * Should contain all clean up code that removes the layer's elements from the DOM and removes listeners previously added in [`onAdd`](#layer-onadd). Called on [`map.removeLayer(layer)`](#map-removelayer). + * + * @method getEvents(): Object + * This optional method should return an object like `{ viewreset: this._reset }` for [`addEventListener`](#evented-addeventlistener). The event handlers in this object will be automatically added and removed from the map with your layer. + * + * @method getAttribution(): String + * This optional method should return a string containing HTML to be shown on the `Attribution control` whenever the layer is visible. + * + * @method beforeAdd(map: Map): this + * Optional method. Called on [`map.addLayer(layer)`](#map-addlayer), before the layer is added to the map, before events are initialized, without waiting until the map is in a usable state. Use for early initialization only. + */ + + + /* @namespace Map + * @section Layer events + * + * @event layeradd: LayerEvent + * Fired when a new layer is added to the map. + * + * @event layerremove: LayerEvent + * Fired when some layer is removed from the map + * + * @section Methods for Layers and Controls + */ + Map.include({ + // @method addLayer(layer: Layer): this + // Adds the given layer to the map + addLayer: function (layer) { + var id = stamp(layer); + if (this._layers[id]) { return this; } + this._layers[id] = layer; + + layer._mapToAdd = this; + + if (layer.beforeAdd) { + layer.beforeAdd(this); + } + + this.whenReady(layer._layerAdd, layer); + + return this; + }, + + // @method removeLayer(layer: Layer): this + // Removes the given layer from the map. + removeLayer: function (layer) { + var id = stamp(layer); + + if (!this._layers[id]) { return this; } + + if (this._loaded) { + layer.onRemove(this); + } + + if (layer.getAttribution && this.attributionControl) { + this.attributionControl.removeAttribution(layer.getAttribution()); + } + + delete this._layers[id]; + + if (this._loaded) { + this.fire('layerremove', {layer: layer}); + layer.fire('remove'); + } + + layer._map = layer._mapToAdd = null; + + return this; + }, + + // @method hasLayer(layer: Layer): Boolean + // Returns `true` if the given layer is currently added to the map + hasLayer: function (layer) { + return !!layer && (stamp(layer) in this._layers); + }, + + /* @method eachLayer(fn: Function, context?: Object): this + * Iterates over the layers of the map, optionally specifying context of the iterator function. + * ``` + * map.eachLayer(function(layer){ + * layer.bindPopup('Hello'); + * }); + * ``` + */ + eachLayer: function (method, context) { + for (var i in this._layers) { + method.call(context, this._layers[i]); + } + return this; + }, + + _addLayers: function (layers) { + layers = layers ? (isArray(layers) ? layers : [layers]) : []; + + for (var i = 0, len = layers.length; i < len; i++) { + this.addLayer(layers[i]); + } + }, + + _addZoomLimit: function (layer) { + if (isNaN(layer.options.maxZoom) || !isNaN(layer.options.minZoom)) { + this._zoomBoundLayers[stamp(layer)] = layer; + this._updateZoomLevels(); + } + }, + + _removeZoomLimit: function (layer) { + var id = stamp(layer); + + if (this._zoomBoundLayers[id]) { + delete this._zoomBoundLayers[id]; + this._updateZoomLevels(); + } + }, + + _updateZoomLevels: function () { + var minZoom = Infinity, + maxZoom = -Infinity, + oldZoomSpan = this._getZoomSpan(); + + for (var i in this._zoomBoundLayers) { + var options = this._zoomBoundLayers[i].options; + + minZoom = options.minZoom === undefined ? minZoom : Math.min(minZoom, options.minZoom); + maxZoom = options.maxZoom === undefined ? maxZoom : Math.max(maxZoom, options.maxZoom); + } + + this._layersMaxZoom = maxZoom === -Infinity ? undefined : maxZoom; + this._layersMinZoom = minZoom === Infinity ? undefined : minZoom; + + // @section Map state change events + // @event zoomlevelschange: Event + // Fired when the number of zoomlevels on the map is changed due + // to adding or removing a layer. + if (oldZoomSpan !== this._getZoomSpan()) { + this.fire('zoomlevelschange'); + } + + if (this.options.maxZoom === undefined && this._layersMaxZoom && this.getZoom() > this._layersMaxZoom) { + this.setZoom(this._layersMaxZoom); + } + if (this.options.minZoom === undefined && this._layersMinZoom && this.getZoom() < this._layersMinZoom) { + this.setZoom(this._layersMinZoom); + } + } + }); + + /* + * @class LayerGroup + * @aka L.LayerGroup + * @inherits Layer + * + * Used to group several layers and handle them as one. If you add it to the map, + * any layers added or removed from the group will be added/removed on the map as + * well. Extends `Layer`. + * + * @example + * + * ```js + * L.layerGroup([marker1, marker2]) + * .addLayer(polyline) + * .addTo(map); + * ``` + */ + + var LayerGroup = Layer.extend({ + + initialize: function (layers) { + this._layers = {}; + + var i, len; + + if (layers) { + for (i = 0, len = layers.length; i < len; i++) { + this.addLayer(layers[i]); + } + } + }, + + // @method addLayer(layer: Layer): this + // Adds the given layer to the group. + addLayer: function (layer) { + var id = this.getLayerId(layer); + + this._layers[id] = layer; + + if (this._map) { + this._map.addLayer(layer); + } + + return this; + }, + + // @method removeLayer(layer: Layer): this + // Removes the given layer from the group. + // @alternative + // @method removeLayer(id: Number): this + // Removes the layer with the given internal ID from the group. + removeLayer: function (layer) { + var id = layer in this._layers ? layer : this.getLayerId(layer); + + if (this._map && this._layers[id]) { + this._map.removeLayer(this._layers[id]); + } + + delete this._layers[id]; + + return this; + }, + + // @method hasLayer(layer: Layer): Boolean + // Returns `true` if the given layer is currently added to the group. + // @alternative + // @method hasLayer(id: Number): Boolean + // Returns `true` if the given internal ID is currently added to the group. + hasLayer: function (layer) { + return !!layer && (layer in this._layers || this.getLayerId(layer) in this._layers); + }, + + // @method clearLayers(): this + // Removes all the layers from the group. + clearLayers: function () { + for (var i in this._layers) { + this.removeLayer(this._layers[i]); + } + return this; + }, + + // @method invoke(methodName: String, …): this + // Calls `methodName` on every layer contained in this group, passing any + // additional parameters. Has no effect if the layers contained do not + // implement `methodName`. + invoke: function (methodName) { + var args = Array.prototype.slice.call(arguments, 1), + i, layer; + + for (i in this._layers) { + layer = this._layers[i]; + + if (layer[methodName]) { + layer[methodName].apply(layer, args); + } + } + + return this; + }, + + onAdd: function (map) { + for (var i in this._layers) { + map.addLayer(this._layers[i]); + } + }, + + onRemove: function (map) { + for (var i in this._layers) { + map.removeLayer(this._layers[i]); + } + }, + + // @method eachLayer(fn: Function, context?: Object): this + // Iterates over the layers of the group, optionally specifying context of the iterator function. + // ```js + // group.eachLayer(function (layer) { + // layer.bindPopup('Hello'); + // }); + // ``` + eachLayer: function (method, context) { + for (var i in this._layers) { + method.call(context, this._layers[i]); + } + return this; + }, + + // @method getLayer(id: Number): Layer + // Returns the layer with the given internal ID. + getLayer: function (id) { + return this._layers[id]; + }, + + // @method getLayers(): Layer[] + // Returns an array of all the layers added to the group. + getLayers: function () { + var layers = []; + + for (var i in this._layers) { + layers.push(this._layers[i]); + } + return layers; + }, + + // @method setZIndex(zIndex: Number): this + // Calls `setZIndex` on every layer contained in this group, passing the z-index. + setZIndex: function (zIndex) { + return this.invoke('setZIndex', zIndex); + }, + + // @method getLayerId(layer: Layer): Number + // Returns the internal ID for a layer + getLayerId: function (layer) { + return stamp(layer); + } + }); + + +// @factory L.layerGroup(layers: Layer[]) +// Create a layer group, optionally given an initial set of layers. + var layerGroup = function (layers) { + return new LayerGroup(layers); + }; + + /* + * @class FeatureGroup + * @aka L.FeatureGroup + * @inherits LayerGroup + * + * Extended `LayerGroup` that makes it easier to do the same thing to all its member layers: + * * [`bindPopup`](#layer-bindpopup) binds a popup to all of the layers at once (likewise with [`bindTooltip`](#layer-bindtooltip)) + * * Events are propagated to the `FeatureGroup`, so if the group has an event + * handler, it will handle events from any of the layers. This includes mouse events + * and custom events. + * * Has `layeradd` and `layerremove` events + * + * @example + * + * ```js + * L.featureGroup([marker1, marker2, polyline]) + * .bindPopup('Hello world!') + * .on('click', function() { alert('Clicked on a member of the group!'); }) + * .addTo(map); + * ``` + */ + + var FeatureGroup = LayerGroup.extend({ + + addLayer: function (layer) { + if (this.hasLayer(layer)) { + return this; + } + + layer.addEventParent(this); + + LayerGroup.prototype.addLayer.call(this, layer); + + // @event layeradd: LayerEvent + // Fired when a layer is added to this `FeatureGroup` + return this.fire('layeradd', {layer: layer}); + }, + + removeLayer: function (layer) { + if (!this.hasLayer(layer)) { + return this; + } + if (layer in this._layers) { + layer = this._layers[layer]; + } + + layer.removeEventParent(this); + + LayerGroup.prototype.removeLayer.call(this, layer); + + // @event layerremove: LayerEvent + // Fired when a layer is removed from this `FeatureGroup` + return this.fire('layerremove', {layer: layer}); + }, + + // @method setStyle(style: Path options): this + // Sets the given path options to each layer of the group that has a `setStyle` method. + setStyle: function (style) { + return this.invoke('setStyle', style); + }, + + // @method bringToFront(): this + // Brings the layer group to the top of all other layers + bringToFront: function () { + return this.invoke('bringToFront'); + }, + + // @method bringToBack(): this + // Brings the layer group to the top of all other layers + bringToBack: function () { + return this.invoke('bringToBack'); + }, + + // @method getBounds(): LatLngBounds + // Returns the LatLngBounds of the Feature Group (created from bounds and coordinates of its children). + getBounds: function () { + var bounds = new LatLngBounds(); + + for (var id in this._layers) { + var layer = this._layers[id]; + bounds.extend(layer.getBounds ? layer.getBounds() : layer.getLatLng()); + } + return bounds; + } + }); + +// @factory L.featureGroup(layers: Layer[]) +// Create a feature group, optionally given an initial set of layers. + var featureGroup = function (layers) { + return new FeatureGroup(layers); + }; + + /* + * @class Icon + * @aka L.Icon + * + * Represents an icon to provide when creating a marker. + * + * @example + * + * ```js + * var myIcon = L.icon({ + * iconUrl: 'my-icon.png', + * iconRetinaUrl: 'my-icon@2x.png', + * iconSize: [38, 95], + * iconAnchor: [22, 94], + * popupAnchor: [-3, -76], + * shadowUrl: 'my-icon-shadow.png', + * shadowRetinaUrl: 'my-icon-shadow@2x.png', + * shadowSize: [68, 95], + * shadowAnchor: [22, 94] + * }); + * + * L.marker([50.505, 30.57], {icon: myIcon}).addTo(map); + * ``` + * + * `L.Icon.Default` extends `L.Icon` and is the blue icon Leaflet uses for markers by default. + * + */ + + var Icon = Class.extend({ + + /* @section + * @aka Icon options + * + * @option iconUrl: String = null + * **(required)** The URL to the icon image (absolute or relative to your script path). + * + * @option iconRetinaUrl: String = null + * The URL to a retina sized version of the icon image (absolute or relative to your + * script path). Used for Retina screen devices. + * + * @option iconSize: Point = null + * Size of the icon image in pixels. + * + * @option iconAnchor: Point = null + * The coordinates of the "tip" of the icon (relative to its top left corner). The icon + * will be aligned so that this point is at the marker's geographical location. Centered + * by default if size is specified, also can be set in CSS with negative margins. + * + * @option popupAnchor: Point = null + * The coordinates of the point from which popups will "open", relative to the icon anchor. + * + * @option shadowUrl: String = null + * The URL to the icon shadow image. If not specified, no shadow image will be created. + * + * @option shadowRetinaUrl: String = null + * + * @option shadowSize: Point = null + * Size of the shadow image in pixels. + * + * @option shadowAnchor: Point = null + * The coordinates of the "tip" of the shadow (relative to its top left corner) (the same + * as iconAnchor if not specified). + * + * @option className: String = '' + * A custom class name to assign to both icon and shadow images. Empty by default. + */ + + initialize: function (options) { + setOptions(this, options); + }, + + // @method createIcon(oldIcon?: HTMLElement): HTMLElement + // Called internally when the icon has to be shown, returns a `` HTML element + // styled according to the options. + createIcon: function (oldIcon) { + return this._createIcon('icon', oldIcon); + }, + + // @method createShadow(oldIcon?: HTMLElement): HTMLElement + // As `createIcon`, but for the shadow beneath it. + createShadow: function (oldIcon) { + return this._createIcon('shadow', oldIcon); + }, + + _createIcon: function (name, oldIcon) { + var src = this._getIconUrl(name); + + if (!src) { + if (name === 'icon') { + throw new Error('iconUrl not set in Icon options (see the docs).'); + } + return null; + } + + var img = this._createImg(src, oldIcon && oldIcon.tagName === 'IMG' ? oldIcon : null); + this._setIconStyles(img, name); + + return img; + }, + + _setIconStyles: function (img, name) { + var options = this.options; + var sizeOption = options[name + 'Size']; + + if (typeof sizeOption === 'number') { + sizeOption = [sizeOption, sizeOption]; + } + + var size = toPoint(sizeOption), + anchor = toPoint(name === 'shadow' && options.shadowAnchor || options.iconAnchor || + size && size.divideBy(2, true)); + + img.className = 'leaflet-marker-' + name + ' ' + (options.className || ''); + + if (anchor) { + img.style.marginLeft = (-anchor.x) + 'px'; + img.style.marginTop = (-anchor.y) + 'px'; + } + + if (size) { + img.style.width = size.x + 'px'; + img.style.height = size.y + 'px'; + } + }, + + _createImg: function (src, el) { + el = el || document.createElement('img'); + el.src = src; + return el; + }, + + _getIconUrl: function (name) { + return retina && this.options[name + 'RetinaUrl'] || this.options[name + 'Url']; + } + }); + + +// @factory L.icon(options: Icon options) +// Creates an icon instance with the given options. + function icon(options) { + return new Icon(options); + } + + /* + * @miniclass Icon.Default (Icon) + * @aka L.Icon.Default + * @section + * + * A trivial subclass of `Icon`, represents the icon to use in `Marker`s when + * no icon is specified. Points to the blue marker image distributed with Leaflet + * releases. + * + * In order to customize the default icon, just change the properties of `L.Icon.Default.prototype.options` + * (which is a set of `Icon options`). + * + * If you want to _completely_ replace the default icon, override the + * `L.Marker.prototype.options.icon` with your own icon instead. + */ + + var IconDefault = Icon.extend({ + + options: { + iconUrl: 'marker-icon.png', + iconRetinaUrl: 'marker-icon-2x.png', + shadowUrl: 'marker-shadow.png', + iconSize: [25, 41], + iconAnchor: [12, 41], + popupAnchor: [1, -34], + tooltipAnchor: [16, -28], + shadowSize: [41, 41] + }, + + _getIconUrl: function (name) { + if (!IconDefault.imagePath) { // Deprecated, backwards-compatibility only + IconDefault.imagePath = this._detectIconPath(); + } + + // @option imagePath: String + // `Icon.Default` will try to auto-detect the absolute location of the + // blue icon images. If you are placing these images in a non-standard + // way, set this option to point to the right absolute path. + return (this.options.imagePath || IconDefault.imagePath) + Icon.prototype._getIconUrl.call(this, name); + }, + + _detectIconPath: function () { + var el = create$1('div', 'leaflet-default-icon-path', document.body); + var path = getStyle(el, 'background-image') || + getStyle(el, 'backgroundImage'); // IE8 + + document.body.removeChild(el); + + if (path === null || path.indexOf('url') !== 0) { + path = ''; + } else { + path = path.replace(/^url\([\"\']?/, '').replace(/marker-icon\.png[\"\']?\)$/, ''); + } + + return path; + } + }); + + /* + * L.Handler.MarkerDrag is used internally by L.Marker to make the markers draggable. + */ + + + /* @namespace Marker + * @section Interaction handlers + * + * Interaction handlers are properties of a marker instance that allow you to control interaction behavior in runtime, enabling or disabling certain features such as dragging (see `Handler` methods). Example: + * + * ```js + * marker.dragging.disable(); + * ``` + * + * @property dragging: Handler + * Marker dragging handler (by both mouse and touch). Only valid when the marker is on the map (Otherwise set [`marker.options.draggable`](#marker-draggable)). + */ + + var MarkerDrag = Handler.extend({ + initialize: function (marker) { + this._marker = marker; + }, + + addHooks: function () { + var icon = this._marker._icon; + + if (!this._draggable) { + this._draggable = new Draggable(icon, icon, true); + } + + this._draggable.on({ + dragstart: this._onDragStart, + drag: this._onDrag, + dragend: this._onDragEnd + }, this).enable(); + + addClass(icon, 'leaflet-marker-draggable'); + }, + + removeHooks: function () { + this._draggable.off({ + dragstart: this._onDragStart, + drag: this._onDrag, + dragend: this._onDragEnd + }, this).disable(); + + if (this._marker._icon) { + removeClass(this._marker._icon, 'leaflet-marker-draggable'); + } + }, + + moved: function () { + return this._draggable && this._draggable._moved; + }, + + _onDragStart: function () { + // @section Dragging events + // @event dragstart: Event + // Fired when the user starts dragging the marker. + + // @event movestart: Event + // Fired when the marker starts moving (because of dragging). + + this._oldLatLng = this._marker.getLatLng(); + this._marker + .closePopup() + .fire('movestart') + .fire('dragstart'); + }, + + _onDrag: function (e) { + var marker = this._marker, + shadow = marker._shadow, + iconPos = getPosition(marker._icon), + latlng = marker._map.layerPointToLatLng(iconPos); + + // update shadow position + if (shadow) { + setPosition(shadow, iconPos); + } + + marker._latlng = latlng; + e.latlng = latlng; + e.oldLatLng = this._oldLatLng; + + // @event drag: Event + // Fired repeatedly while the user drags the marker. + marker + .fire('move', e) + .fire('drag', e); + }, + + _onDragEnd: function (e) { + // @event dragend: DragEndEvent + // Fired when the user stops dragging the marker. + + // @event moveend: Event + // Fired when the marker stops moving (because of dragging). + delete this._oldLatLng; + this._marker + .fire('moveend') + .fire('dragend', e); + } + }); + + /* + * @class Marker + * @inherits Interactive layer + * @aka L.Marker + * L.Marker is used to display clickable/draggable icons on the map. Extends `Layer`. + * + * @example + * + * ```js + * L.marker([50.5, 30.5]).addTo(map); + * ``` + */ + + var Marker = Layer.extend({ + + // @section + // @aka Marker options + options: { + // @option icon: Icon = * + // Icon instance to use for rendering the marker. + // See [Icon documentation](#L.Icon) for details on how to customize the marker icon. + // If not specified, a common instance of `L.Icon.Default` is used. + icon: new IconDefault(), + + // Option inherited from "Interactive layer" abstract class + interactive: true, + + // @option draggable: Boolean = false + // Whether the marker is draggable with mouse/touch or not. + draggable: false, + + // @option keyboard: Boolean = true + // Whether the marker can be tabbed to with a keyboard and clicked by pressing enter. + keyboard: true, + + // @option title: String = '' + // Text for the browser tooltip that appear on marker hover (no tooltip by default). + title: '', + + // @option alt: String = '' + // Text for the `alt` attribute of the icon image (useful for accessibility). + alt: '', + + // @option zIndexOffset: Number = 0 + // By default, marker images zIndex is set automatically based on its latitude. Use this option if you want to put the marker on top of all others (or below), specifying a high value like `1000` (or high negative value, respectively). + zIndexOffset: 0, + + // @option opacity: Number = 1.0 + // The opacity of the marker. + opacity: 1, + + // @option riseOnHover: Boolean = false + // If `true`, the marker will get on top of others when you hover the mouse over it. + riseOnHover: false, + + // @option riseOffset: Number = 250 + // The z-index offset used for the `riseOnHover` feature. + riseOffset: 250, + + // @option pane: String = 'markerPane' + // `Map pane` where the markers icon will be added. + pane: 'markerPane', + + // @option bubblingMouseEvents: Boolean = false + // When `true`, a mouse event on this marker will trigger the same event on the map + // (unless [`L.DomEvent.stopPropagation`](#domevent-stoppropagation) is used). + bubblingMouseEvents: false + }, + + /* @section + * + * In addition to [shared layer methods](#Layer) like `addTo()` and `remove()` and [popup methods](#Popup) like bindPopup() you can also use the following methods: + */ + + initialize: function (latlng, options) { + setOptions(this, options); + this._latlng = toLatLng(latlng); + }, + + onAdd: function (map) { + this._zoomAnimated = this._zoomAnimated && map.options.markerZoomAnimation; + + if (this._zoomAnimated) { + map.on('zoomanim', this._animateZoom, this); + } + + this._initIcon(); + this.update(); + }, + + onRemove: function (map) { + if (this.dragging && this.dragging.enabled()) { + this.options.draggable = true; + this.dragging.removeHooks(); + } + delete this.dragging; + + if (this._zoomAnimated) { + map.off('zoomanim', this._animateZoom, this); + } + + this._removeIcon(); + this._removeShadow(); + }, + + getEvents: function () { + return { + zoom: this.update, + viewreset: this.update + }; + }, + + // @method getLatLng: LatLng + // Returns the current geographical position of the marker. + getLatLng: function () { + return this._latlng; + }, + + // @method setLatLng(latlng: LatLng): this + // Changes the marker position to the given point. + setLatLng: function (latlng) { + var oldLatLng = this._latlng; + this._latlng = toLatLng(latlng); + this.update(); + + // @event move: Event + // Fired when the marker is moved via [`setLatLng`](#marker-setlatlng) or by [dragging](#marker-dragging). Old and new coordinates are included in event arguments as `oldLatLng`, `latlng`. + return this.fire('move', {oldLatLng: oldLatLng, latlng: this._latlng}); + }, + + // @method setZIndexOffset(offset: Number): this + // Changes the [zIndex offset](#marker-zindexoffset) of the marker. + setZIndexOffset: function (offset) { + this.options.zIndexOffset = offset; + return this.update(); + }, + + // @method setIcon(icon: Icon): this + // Changes the marker icon. + setIcon: function (icon) { + + this.options.icon = icon; + + if (this._map) { + this._initIcon(); + this.update(); + } + + if (this._popup) { + this.bindPopup(this._popup, this._popup.options); + } + + return this; + }, + + getElement: function () { + return this._icon; + }, + + update: function () { + + if (this._icon) { + var pos = this._map.latLngToLayerPoint(this._latlng).round(); + this._setPos(pos); + } + + return this; + }, + + _initIcon: function () { + var options = this.options, + classToAdd = 'leaflet-zoom-' + (this._zoomAnimated ? 'animated' : 'hide'); + + var icon = options.icon.createIcon(this._icon), + addIcon = false; + + // if we're not reusing the icon, remove the old one and init new one + if (icon !== this._icon) { + if (this._icon) { + this._removeIcon(); + } + addIcon = true; + + if (options.title) { + icon.title = options.title; + } + if (options.alt) { + icon.alt = options.alt; + } + } + + addClass(icon, classToAdd); + + if (options.keyboard) { + icon.tabIndex = '0'; + } + + this._icon = icon; + + if (options.riseOnHover) { + this.on({ + mouseover: this._bringToFront, + mouseout: this._resetZIndex + }); + } + + var newShadow = options.icon.createShadow(this._shadow), + addShadow = false; + + if (newShadow !== this._shadow) { + this._removeShadow(); + addShadow = true; + } + + if (newShadow) { + addClass(newShadow, classToAdd); + newShadow.alt = ''; + } + this._shadow = newShadow; + + + if (options.opacity < 1) { + this._updateOpacity(); + } + + + if (addIcon) { + this.getPane().appendChild(this._icon); + } + this._initInteraction(); + if (newShadow && addShadow) { + this.getPane('shadowPane').appendChild(this._shadow); + } + }, + + _removeIcon: function () { + if (this.options.riseOnHover) { + this.off({ + mouseover: this._bringToFront, + mouseout: this._resetZIndex + }); + } + + remove(this._icon); + this.removeInteractiveTarget(this._icon); + + this._icon = null; + }, + + _removeShadow: function () { + if (this._shadow) { + remove(this._shadow); + } + this._shadow = null; + }, + + _setPos: function (pos) { + setPosition(this._icon, pos); + + if (this._shadow) { + setPosition(this._shadow, pos); + } + + this._zIndex = pos.y + this.options.zIndexOffset; + + this._resetZIndex(); + }, + + _updateZIndex: function (offset) { + this._icon.style.zIndex = this._zIndex + offset; + }, + + _animateZoom: function (opt) { + var pos = this._map._latLngToNewLayerPoint(this._latlng, opt.zoom, opt.center).round(); + + this._setPos(pos); + }, + + _initInteraction: function () { + + if (!this.options.interactive) { return; } + + addClass(this._icon, 'leaflet-interactive'); + + this.addInteractiveTarget(this._icon); + + if (MarkerDrag) { + var draggable = this.options.draggable; + if (this.dragging) { + draggable = this.dragging.enabled(); + this.dragging.disable(); + } + + this.dragging = new MarkerDrag(this); + + if (draggable) { + this.dragging.enable(); + } + } + }, + + // @method setOpacity(opacity: Number): this + // Changes the opacity of the marker. + setOpacity: function (opacity) { + this.options.opacity = opacity; + if (this._map) { + this._updateOpacity(); + } + + return this; + }, + + _updateOpacity: function () { + var opacity = this.options.opacity; + + setOpacity(this._icon, opacity); + + if (this._shadow) { + setOpacity(this._shadow, opacity); + } + }, + + _bringToFront: function () { + this._updateZIndex(this.options.riseOffset); + }, + + _resetZIndex: function () { + this._updateZIndex(0); + }, + + _getPopupAnchor: function () { + return this.options.icon.options.popupAnchor || [0, 0]; + }, + + _getTooltipAnchor: function () { + return this.options.icon.options.tooltipAnchor || [0, 0]; + } + }); + + +// factory L.marker(latlng: LatLng, options? : Marker options) + +// @factory L.marker(latlng: LatLng, options? : Marker options) +// Instantiates a Marker object given a geographical point and optionally an options object. + function marker(latlng, options) { + return new Marker(latlng, options); + } + + /* + * @class Path + * @aka L.Path + * @inherits Interactive layer + * + * An abstract class that contains options and constants shared between vector + * overlays (Polygon, Polyline, Circle). Do not use it directly. Extends `Layer`. + */ + + var Path = Layer.extend({ + + // @section + // @aka Path options + options: { + // @option stroke: Boolean = true + // Whether to draw stroke along the path. Set it to `false` to disable borders on polygons or circles. + stroke: true, + + // @option color: String = '#3388ff' + // Stroke color + color: '#3388ff', + + // @option weight: Number = 3 + // Stroke width in pixels + weight: 3, + + // @option opacity: Number = 1.0 + // Stroke opacity + opacity: 1, + + // @option lineCap: String= 'round' + // A string that defines [shape to be used at the end](https://developer.mozilla.org/docs/Web/SVG/Attribute/stroke-linecap) of the stroke. + lineCap: 'round', + + // @option lineJoin: String = 'round' + // A string that defines [shape to be used at the corners](https://developer.mozilla.org/docs/Web/SVG/Attribute/stroke-linejoin) of the stroke. + lineJoin: 'round', + + // @option dashArray: String = null + // A string that defines the stroke [dash pattern](https://developer.mozilla.org/docs/Web/SVG/Attribute/stroke-dasharray). Doesn't work on `Canvas`-powered layers in [some old browsers](https://developer.mozilla.org/docs/Web/API/CanvasRenderingContext2D/setLineDash#Browser_compatibility). + dashArray: null, + + // @option dashOffset: String = null + // A string that defines the [distance into the dash pattern to start the dash](https://developer.mozilla.org/docs/Web/SVG/Attribute/stroke-dashoffset). Doesn't work on `Canvas`-powered layers in [some old browsers](https://developer.mozilla.org/docs/Web/API/CanvasRenderingContext2D/setLineDash#Browser_compatibility). + dashOffset: null, + + // @option fill: Boolean = depends + // Whether to fill the path with color. Set it to `false` to disable filling on polygons or circles. + fill: false, + + // @option fillColor: String = * + // Fill color. Defaults to the value of the [`color`](#path-color) option + fillColor: null, + + // @option fillOpacity: Number = 0.2 + // Fill opacity. + fillOpacity: 0.2, + + // @option fillRule: String = 'evenodd' + // A string that defines [how the inside of a shape](https://developer.mozilla.org/docs/Web/SVG/Attribute/fill-rule) is determined. + fillRule: 'evenodd', + + // className: '', + + // Option inherited from "Interactive layer" abstract class + interactive: true, + + // @option bubblingMouseEvents: Boolean = true + // When `true`, a mouse event on this path will trigger the same event on the map + // (unless [`L.DomEvent.stopPropagation`](#domevent-stoppropagation) is used). + bubblingMouseEvents: true + }, + + beforeAdd: function (map) { + // Renderer is set here because we need to call renderer.getEvents + // before this.getEvents. + this._renderer = map.getRenderer(this); + }, + + onAdd: function () { + this._renderer._initPath(this); + this._reset(); + this._renderer._addPath(this); + }, + + onRemove: function () { + this._renderer._removePath(this); + }, + + // @method redraw(): this + // Redraws the layer. Sometimes useful after you changed the coordinates that the path uses. + redraw: function () { + if (this._map) { + this._renderer._updatePath(this); + } + return this; + }, + + // @method setStyle(style: Path options): this + // Changes the appearance of a Path based on the options in the `Path options` object. + setStyle: function (style) { + setOptions(this, style); + if (this._renderer) { + this._renderer._updateStyle(this); + } + return this; + }, + + // @method bringToFront(): this + // Brings the layer to the top of all path layers. + bringToFront: function () { + if (this._renderer) { + this._renderer._bringToFront(this); + } + return this; + }, + + // @method bringToBack(): this + // Brings the layer to the bottom of all path layers. + bringToBack: function () { + if (this._renderer) { + this._renderer._bringToBack(this); + } + return this; + }, + + getElement: function () { + return this._path; + }, + + _reset: function () { + // defined in child classes + this._project(); + this._update(); + }, + + _clickTolerance: function () { + // used when doing hit detection for Canvas layers + return (this.options.stroke ? this.options.weight / 2 : 0) + (touch ? 10 : 0); + } + }); + + /* + * @class CircleMarker + * @aka L.CircleMarker + * @inherits Path + * + * A circle of a fixed size with radius specified in pixels. Extends `Path`. + */ + + var CircleMarker = Path.extend({ + + // @section + // @aka CircleMarker options + options: { + fill: true, + + // @option radius: Number = 10 + // Radius of the circle marker, in pixels + radius: 10 + }, + + initialize: function (latlng, options) { + setOptions(this, options); + this._latlng = toLatLng(latlng); + this._radius = this.options.radius; + }, + + // @method setLatLng(latLng: LatLng): this + // Sets the position of a circle marker to a new location. + setLatLng: function (latlng) { + this._latlng = toLatLng(latlng); + this.redraw(); + return this.fire('move', {latlng: this._latlng}); + }, + + // @method getLatLng(): LatLng + // Returns the current geographical position of the circle marker + getLatLng: function () { + return this._latlng; + }, + + // @method setRadius(radius: Number): this + // Sets the radius of a circle marker. Units are in pixels. + setRadius: function (radius) { + this.options.radius = this._radius = radius; + return this.redraw(); + }, + + // @method getRadius(): Number + // Returns the current radius of the circle + getRadius: function () { + return this._radius; + }, + + setStyle : function (options) { + var radius = options && options.radius || this._radius; + Path.prototype.setStyle.call(this, options); + this.setRadius(radius); + return this; + }, + + _project: function () { + this._point = this._map.latLngToLayerPoint(this._latlng); + this._updateBounds(); + }, + + _updateBounds: function () { + var r = this._radius, + r2 = this._radiusY || r, + w = this._clickTolerance(), + p = [r + w, r2 + w]; + this._pxBounds = new Bounds(this._point.subtract(p), this._point.add(p)); + }, + + _update: function () { + if (this._map) { + this._updatePath(); + } + }, + + _updatePath: function () { + this._renderer._updateCircle(this); + }, + + _empty: function () { + return this._radius && !this._renderer._bounds.intersects(this._pxBounds); + }, + + // Needed by the `Canvas` renderer for interactivity + _containsPoint: function (p) { + return p.distanceTo(this._point) <= this._radius + this._clickTolerance(); + } + }); + + +// @factory L.circleMarker(latlng: LatLng, options?: CircleMarker options) +// Instantiates a circle marker object given a geographical point, and an optional options object. + function circleMarker(latlng, options) { + return new CircleMarker(latlng, options); + } + + /* + * @class Circle + * @aka L.Circle + * @inherits CircleMarker + * + * A class for drawing circle overlays on a map. Extends `CircleMarker`. + * + * It's an approximation and starts to diverge from a real circle closer to poles (due to projection distortion). + * + * @example + * + * ```js + * L.circle([50.5, 30.5], {radius: 200}).addTo(map); + * ``` + */ + + var Circle = CircleMarker.extend({ + + initialize: function (latlng, options, legacyOptions) { + if (typeof options === 'number') { + // Backwards compatibility with 0.7.x factory (latlng, radius, options?) + options = extend({}, legacyOptions, {radius: options}); + } + setOptions(this, options); + this._latlng = toLatLng(latlng); + + if (isNaN(this.options.radius)) { throw new Error('Circle radius cannot be NaN'); } + + // @section + // @aka Circle options + // @option radius: Number; Radius of the circle, in meters. + this._mRadius = this.options.radius; + }, + + // @method setRadius(radius: Number): this + // Sets the radius of a circle. Units are in meters. + setRadius: function (radius) { + this._mRadius = radius; + return this.redraw(); + }, + + // @method getRadius(): Number + // Returns the current radius of a circle. Units are in meters. + getRadius: function () { + return this._mRadius; + }, + + // @method getBounds(): LatLngBounds + // Returns the `LatLngBounds` of the path. + getBounds: function () { + var half = [this._radius, this._radiusY || this._radius]; + + return new LatLngBounds( + this._map.layerPointToLatLng(this._point.subtract(half)), + this._map.layerPointToLatLng(this._point.add(half))); + }, + + setStyle: Path.prototype.setStyle, + + _project: function () { + + var lng = this._latlng.lng, + lat = this._latlng.lat, + map = this._map, + crs = map.options.crs; + + if (crs.distance === Earth.distance) { + var d = Math.PI / 180, + latR = (this._mRadius / Earth.R) / d, + top = map.project([lat + latR, lng]), + bottom = map.project([lat - latR, lng]), + p = top.add(bottom).divideBy(2), + lat2 = map.unproject(p).lat, + lngR = Math.acos((Math.cos(latR * d) - Math.sin(lat * d) * Math.sin(lat2 * d)) / + (Math.cos(lat * d) * Math.cos(lat2 * d))) / d; + + if (isNaN(lngR) || lngR === 0) { + lngR = latR / Math.cos(Math.PI / 180 * lat); // Fallback for edge case, #2425 + } + + this._point = p.subtract(map.getPixelOrigin()); + this._radius = isNaN(lngR) ? 0 : Math.max(Math.round(p.x - map.project([lat2, lng - lngR]).x), 1); + this._radiusY = Math.max(Math.round(p.y - top.y), 1); + + } else { + var latlng2 = crs.unproject(crs.project(this._latlng).subtract([this._mRadius, 0])); + + this._point = map.latLngToLayerPoint(this._latlng); + this._radius = this._point.x - map.latLngToLayerPoint(latlng2).x; + } + + this._updateBounds(); + } + }); + +// @factory L.circle(latlng: LatLng, options?: Circle options) +// Instantiates a circle object given a geographical point, and an options object +// which contains the circle radius. +// @alternative +// @factory L.circle(latlng: LatLng, radius: Number, options?: Circle options) +// Obsolete way of instantiating a circle, for compatibility with 0.7.x code. +// Do not use in new applications or plugins. + function circle(latlng, options, legacyOptions) { + return new Circle(latlng, options, legacyOptions); + } + + /* + * @class Polyline + * @aka L.Polyline + * @inherits Path + * + * A class for drawing polyline overlays on a map. Extends `Path`. + * + * @example + * + * ```js + * // create a red polyline from an array of LatLng points + * var latlngs = [ + * [45.51, -122.68], + * [37.77, -122.43], + * [34.04, -118.2] + * ]; + * + * var polyline = L.polyline(latlngs, {color: 'red'}).addTo(map); + * + * // zoom the map to the polyline + * map.fitBounds(polyline.getBounds()); + * ``` + * + * You can also pass a multi-dimensional array to represent a `MultiPolyline` shape: + * + * ```js + * // create a red polyline from an array of arrays of LatLng points + * var latlngs = [ + * [[45.51, -122.68], + * [37.77, -122.43], + * [34.04, -118.2]], + * [[40.78, -73.91], + * [41.83, -87.62], + * [32.76, -96.72]] + * ]; + * ``` + */ + + + var Polyline = Path.extend({ + + // @section + // @aka Polyline options + options: { + // @option smoothFactor: Number = 1.0 + // How much to simplify the polyline on each zoom level. More means + // better performance and smoother look, and less means more accurate representation. + smoothFactor: 1.0, + + // @option noClip: Boolean = false + // Disable polyline clipping. + noClip: false + }, + + initialize: function (latlngs, options) { + setOptions(this, options); + this._setLatLngs(latlngs); + }, + + // @method getLatLngs(): LatLng[] + // Returns an array of the points in the path, or nested arrays of points in case of multi-polyline. + getLatLngs: function () { + return this._latlngs; + }, + + // @method setLatLngs(latlngs: LatLng[]): this + // Replaces all the points in the polyline with the given array of geographical points. + setLatLngs: function (latlngs) { + this._setLatLngs(latlngs); + return this.redraw(); + }, + + // @method isEmpty(): Boolean + // Returns `true` if the Polyline has no LatLngs. + isEmpty: function () { + return !this._latlngs.length; + }, + + closestLayerPoint: function (p) { + var minDistance = Infinity, + minPoint = null, + closest = _sqClosestPointOnSegment, + p1, p2; + + for (var j = 0, jLen = this._parts.length; j < jLen; j++) { + var points = this._parts[j]; + + for (var i = 1, len = points.length; i < len; i++) { + p1 = points[i - 1]; + p2 = points[i]; + + var sqDist = closest(p, p1, p2, true); + + if (sqDist < minDistance) { + minDistance = sqDist; + minPoint = closest(p, p1, p2); + } + } + } + if (minPoint) { + minPoint.distance = Math.sqrt(minDistance); + } + return minPoint; + }, + + // @method getCenter(): LatLng + // Returns the center ([centroid](http://en.wikipedia.org/wiki/Centroid)) of the polyline. + getCenter: function () { + // throws error when not yet added to map as this center calculation requires projected coordinates + if (!this._map) { + throw new Error('Must add layer to map before using getCenter()'); + } + + var i, halfDist, segDist, dist, p1, p2, ratio, + points = this._rings[0], + len = points.length; + + if (!len) { return null; } + + // polyline centroid algorithm; only uses the first ring if there are multiple + + for (i = 0, halfDist = 0; i < len - 1; i++) { + halfDist += points[i].distanceTo(points[i + 1]) / 2; + } + + // The line is so small in the current view that all points are on the same pixel. + if (halfDist === 0) { + return this._map.layerPointToLatLng(points[0]); + } + + for (i = 0, dist = 0; i < len - 1; i++) { + p1 = points[i]; + p2 = points[i + 1]; + segDist = p1.distanceTo(p2); + dist += segDist; + + if (dist > halfDist) { + ratio = (dist - halfDist) / segDist; + return this._map.layerPointToLatLng([ + p2.x - ratio * (p2.x - p1.x), + p2.y - ratio * (p2.y - p1.y) + ]); + } + } + }, + + // @method getBounds(): LatLngBounds + // Returns the `LatLngBounds` of the path. + getBounds: function () { + return this._bounds; + }, + + // @method addLatLng(latlng: LatLng, latlngs? LatLng[]): this + // Adds a given point to the polyline. By default, adds to the first ring of + // the polyline in case of a multi-polyline, but can be overridden by passing + // a specific ring as a LatLng array (that you can earlier access with [`getLatLngs`](#polyline-getlatlngs)). + addLatLng: function (latlng, latlngs) { + latlngs = latlngs || this._defaultShape(); + latlng = toLatLng(latlng); + latlngs.push(latlng); + this._bounds.extend(latlng); + return this.redraw(); + }, + + _setLatLngs: function (latlngs) { + this._bounds = new LatLngBounds(); + this._latlngs = this._convertLatLngs(latlngs); + }, + + _defaultShape: function () { + return _flat(this._latlngs) ? this._latlngs : this._latlngs[0]; + }, + + // recursively convert latlngs input into actual LatLng instances; calculate bounds along the way + _convertLatLngs: function (latlngs) { + var result = [], + flat = _flat(latlngs); + + for (var i = 0, len = latlngs.length; i < len; i++) { + if (flat) { + result[i] = toLatLng(latlngs[i]); + this._bounds.extend(result[i]); + } else { + result[i] = this._convertLatLngs(latlngs[i]); + } + } + + return result; + }, + + _project: function () { + var pxBounds = new Bounds(); + this._rings = []; + this._projectLatlngs(this._latlngs, this._rings, pxBounds); + + var w = this._clickTolerance(), + p = new Point(w, w); + + if (this._bounds.isValid() && pxBounds.isValid()) { + pxBounds.min._subtract(p); + pxBounds.max._add(p); + this._pxBounds = pxBounds; + } + }, + + // recursively turns latlngs into a set of rings with projected coordinates + _projectLatlngs: function (latlngs, result, projectedBounds) { + var flat = latlngs[0] instanceof LatLng, + len = latlngs.length, + i, ring; + + if (flat) { + ring = []; + for (i = 0; i < len; i++) { + ring[i] = this._map.latLngToLayerPoint(latlngs[i]); + projectedBounds.extend(ring[i]); + } + result.push(ring); + } else { + for (i = 0; i < len; i++) { + this._projectLatlngs(latlngs[i], result, projectedBounds); + } + } + }, + + // clip polyline by renderer bounds so that we have less to render for performance + _clipPoints: function () { + var bounds = this._renderer._bounds; + + this._parts = []; + if (!this._pxBounds || !this._pxBounds.intersects(bounds)) { + return; + } + + if (this.options.noClip) { + this._parts = this._rings; + return; + } + + var parts = this._parts, + i, j, k, len, len2, segment, points; + + for (i = 0, k = 0, len = this._rings.length; i < len; i++) { + points = this._rings[i]; + + for (j = 0, len2 = points.length; j < len2 - 1; j++) { + segment = clipSegment(points[j], points[j + 1], bounds, j, true); + + if (!segment) { continue; } + + parts[k] = parts[k] || []; + parts[k].push(segment[0]); + + // if segment goes out of screen, or it's the last one, it's the end of the line part + if ((segment[1] !== points[j + 1]) || (j === len2 - 2)) { + parts[k].push(segment[1]); + k++; + } + } + } + }, + + // simplify each clipped part of the polyline for performance + _simplifyPoints: function () { + var parts = this._parts, + tolerance = this.options.smoothFactor; + + for (var i = 0, len = parts.length; i < len; i++) { + parts[i] = simplify(parts[i], tolerance); + } + }, + + _update: function () { + if (!this._map) { return; } + + this._clipPoints(); + this._simplifyPoints(); + this._updatePath(); + }, + + _updatePath: function () { + this._renderer._updatePoly(this); + }, + + // Needed by the `Canvas` renderer for interactivity + _containsPoint: function (p, closed) { + var i, j, k, len, len2, part, + w = this._clickTolerance(); + + if (!this._pxBounds || !this._pxBounds.contains(p)) { return false; } + + // hit detection for polylines + for (i = 0, len = this._parts.length; i < len; i++) { + part = this._parts[i]; + + for (j = 0, len2 = part.length, k = len2 - 1; j < len2; k = j++) { + if (!closed && (j === 0)) { continue; } + + if (pointToSegmentDistance(p, part[k], part[j]) <= w) { + return true; + } + } + } + return false; + } + }); + +// @factory L.polyline(latlngs: LatLng[], options?: Polyline options) +// Instantiates a polyline object given an array of geographical points and +// optionally an options object. You can create a `Polyline` object with +// multiple separate lines (`MultiPolyline`) by passing an array of arrays +// of geographic points. + function polyline(latlngs, options) { + return new Polyline(latlngs, options); + } + + /* + * @class Polygon + * @aka L.Polygon + * @inherits Polyline + * + * A class for drawing polygon overlays on a map. Extends `Polyline`. + * + * Note that points you pass when creating a polygon shouldn't have an additional last point equal to the first one — it's better to filter out such points. + * + * + * @example + * + * ```js + * // create a red polygon from an array of LatLng points + * var latlngs = [[37, -109.05],[41, -109.03],[41, -102.05],[37, -102.04]]; + * + * var polygon = L.polygon(latlngs, {color: 'red'}).addTo(map); + * + * // zoom the map to the polygon + * map.fitBounds(polygon.getBounds()); + * ``` + * + * You can also pass an array of arrays of latlngs, with the first array representing the outer shape and the other arrays representing holes in the outer shape: + * + * ```js + * var latlngs = [ + * [[37, -109.05],[41, -109.03],[41, -102.05],[37, -102.04]], // outer ring + * [[37.29, -108.58],[40.71, -108.58],[40.71, -102.50],[37.29, -102.50]] // hole + * ]; + * ``` + * + * Additionally, you can pass a multi-dimensional array to represent a MultiPolygon shape. + * + * ```js + * var latlngs = [ + * [ // first polygon + * [[37, -109.05],[41, -109.03],[41, -102.05],[37, -102.04]], // outer ring + * [[37.29, -108.58],[40.71, -108.58],[40.71, -102.50],[37.29, -102.50]] // hole + * ], + * [ // second polygon + * [[41, -111.03],[45, -111.04],[45, -104.05],[41, -104.05]] + * ] + * ]; + * ``` + */ + + var Polygon = Polyline.extend({ + + options: { + fill: true + }, + + isEmpty: function () { + return !this._latlngs.length || !this._latlngs[0].length; + }, + + getCenter: function () { + // throws error when not yet added to map as this center calculation requires projected coordinates + if (!this._map) { + throw new Error('Must add layer to map before using getCenter()'); + } + + var i, j, p1, p2, f, area, x, y, center, + points = this._rings[0], + len = points.length; + + if (!len) { return null; } + + // polygon centroid algorithm; only uses the first ring if there are multiple + + area = x = y = 0; + + for (i = 0, j = len - 1; i < len; j = i++) { + p1 = points[i]; + p2 = points[j]; + + f = p1.y * p2.x - p2.y * p1.x; + x += (p1.x + p2.x) * f; + y += (p1.y + p2.y) * f; + area += f * 3; + } + + if (area === 0) { + // Polygon is so small that all points are on same pixel. + center = points[0]; + } else { + center = [x / area, y / area]; + } + return this._map.layerPointToLatLng(center); + }, + + _convertLatLngs: function (latlngs) { + var result = Polyline.prototype._convertLatLngs.call(this, latlngs), + len = result.length; + + // remove last point if it equals first one + if (len >= 2 && result[0] instanceof LatLng && result[0].equals(result[len - 1])) { + result.pop(); + } + return result; + }, + + _setLatLngs: function (latlngs) { + Polyline.prototype._setLatLngs.call(this, latlngs); + if (_flat(this._latlngs)) { + this._latlngs = [this._latlngs]; + } + }, + + _defaultShape: function () { + return _flat(this._latlngs[0]) ? this._latlngs[0] : this._latlngs[0][0]; + }, + + _clipPoints: function () { + // polygons need a different clipping algorithm so we redefine that + + var bounds = this._renderer._bounds, + w = this.options.weight, + p = new Point(w, w); + + // increase clip padding by stroke width to avoid stroke on clip edges + bounds = new Bounds(bounds.min.subtract(p), bounds.max.add(p)); + + this._parts = []; + if (!this._pxBounds || !this._pxBounds.intersects(bounds)) { + return; + } + + if (this.options.noClip) { + this._parts = this._rings; + return; + } + + for (var i = 0, len = this._rings.length, clipped; i < len; i++) { + clipped = clipPolygon(this._rings[i], bounds, true); + if (clipped.length) { + this._parts.push(clipped); + } + } + }, + + _updatePath: function () { + this._renderer._updatePoly(this, true); + }, + + // Needed by the `Canvas` renderer for interactivity + _containsPoint: function (p) { + var inside = false, + part, p1, p2, i, j, k, len, len2; + + if (!this._pxBounds.contains(p)) { return false; } + + // ray casting algorithm for detecting if point is in polygon + for (i = 0, len = this._parts.length; i < len; i++) { + part = this._parts[i]; + + for (j = 0, len2 = part.length, k = len2 - 1; j < len2; k = j++) { + p1 = part[j]; + p2 = part[k]; + + if (((p1.y > p.y) !== (p2.y > p.y)) && (p.x < (p2.x - p1.x) * (p.y - p1.y) / (p2.y - p1.y) + p1.x)) { + inside = !inside; + } + } + } + + // also check if it's on polygon stroke + return inside || Polyline.prototype._containsPoint.call(this, p, true); + } + + }); + + +// @factory L.polygon(latlngs: LatLng[], options?: Polyline options) + function polygon(latlngs, options) { + return new Polygon(latlngs, options); + } + + /* + * @class GeoJSON + * @aka L.GeoJSON + * @inherits FeatureGroup + * + * Represents a GeoJSON object or an array of GeoJSON objects. Allows you to parse + * GeoJSON data and display it on the map. Extends `FeatureGroup`. + * + * @example + * + * ```js + * L.geoJSON(data, { + * style: function (feature) { + * return {color: feature.properties.color}; + * } + * }).bindPopup(function (layer) { + * return layer.feature.properties.description; + * }).addTo(map); + * ``` + */ + + var GeoJSON = FeatureGroup.extend({ + + /* @section + * @aka GeoJSON options + * + * @option pointToLayer: Function = * + * A `Function` defining how GeoJSON points spawn Leaflet layers. It is internally + * called when data is added, passing the GeoJSON point feature and its `LatLng`. + * The default is to spawn a default `Marker`: + * ```js + * function(geoJsonPoint, latlng) { + * return L.marker(latlng); + * } + * ``` + * + * @option style: Function = * + * A `Function` defining the `Path options` for styling GeoJSON lines and polygons, + * called internally when data is added. + * The default value is to not override any defaults: + * ```js + * function (geoJsonFeature) { + * return {} + * } + * ``` + * + * @option onEachFeature: Function = * + * A `Function` that will be called once for each created `Feature`, after it has + * been created and styled. Useful for attaching events and popups to features. + * The default is to do nothing with the newly created layers: + * ```js + * function (feature, layer) {} + * ``` + * + * @option filter: Function = * + * A `Function` that will be used to decide whether to include a feature or not. + * The default is to include all features: + * ```js + * function (geoJsonFeature) { + * return true; + * } + * ``` + * Note: dynamically changing the `filter` option will have effect only on newly + * added data. It will _not_ re-evaluate already included features. + * + * @option coordsToLatLng: Function = * + * A `Function` that will be used for converting GeoJSON coordinates to `LatLng`s. + * The default is the `coordsToLatLng` static method. + */ + + initialize: function (geojson, options) { + setOptions(this, options); + + this._layers = {}; + + if (geojson) { + this.addData(geojson); + } + }, + + // @method addData( data ): this + // Adds a GeoJSON object to the layer. + addData: function (geojson) { + var features = isArray(geojson) ? geojson : geojson.features, + i, len, feature; + + if (features) { + for (i = 0, len = features.length; i < len; i++) { + // only add this if geometry or geometries are set and not null + feature = features[i]; + if (feature.geometries || feature.geometry || feature.features || feature.coordinates) { + this.addData(feature); + } + } + return this; + } + + var options = this.options; + + if (options.filter && !options.filter(geojson)) { return this; } + + var layer = geometryToLayer(geojson, options); + if (!layer) { + return this; + } + layer.feature = asFeature(geojson); + + layer.defaultOptions = layer.options; + this.resetStyle(layer); + + if (options.onEachFeature) { + options.onEachFeature(geojson, layer); + } + + return this.addLayer(layer); + }, + + // @method resetStyle( layer ): this + // Resets the given vector layer's style to the original GeoJSON style, useful for resetting style after hover events. + resetStyle: function (layer) { + // reset any custom styles + layer.options = extend({}, layer.defaultOptions); + this._setLayerStyle(layer, this.options.style); + return this; + }, + + // @method setStyle( style ): this + // Changes styles of GeoJSON vector layers with the given style function. + setStyle: function (style) { + return this.eachLayer(function (layer) { + this._setLayerStyle(layer, style); + }, this); + }, + + _setLayerStyle: function (layer, style) { + if (typeof style === 'function') { + style = style(layer.feature); + } + if (layer.setStyle) { + layer.setStyle(style); + } + } + }); + +// @section +// There are several static functions which can be called without instantiating L.GeoJSON: + +// @function geometryToLayer(featureData: Object, options?: GeoJSON options): Layer +// Creates a `Layer` from a given GeoJSON feature. Can use a custom +// [`pointToLayer`](#geojson-pointtolayer) and/or [`coordsToLatLng`](#geojson-coordstolatlng) +// functions if provided as options. + function geometryToLayer(geojson, options) { + + var geometry = geojson.type === 'Feature' ? geojson.geometry : geojson, + coords = geometry ? geometry.coordinates : null, + layers = [], + pointToLayer = options && options.pointToLayer, + _coordsToLatLng = options && options.coordsToLatLng || coordsToLatLng, + latlng, latlngs, i, len; + + if (!coords && !geometry) { + return null; + } + + switch (geometry.type) { + case 'Point': + latlng = _coordsToLatLng(coords); + return pointToLayer ? pointToLayer(geojson, latlng) : new Marker(latlng); + + case 'MultiPoint': + for (i = 0, len = coords.length; i < len; i++) { + latlng = _coordsToLatLng(coords[i]); + layers.push(pointToLayer ? pointToLayer(geojson, latlng) : new Marker(latlng)); + } + return new FeatureGroup(layers); + + case 'LineString': + case 'MultiLineString': + latlngs = coordsToLatLngs(coords, geometry.type === 'LineString' ? 0 : 1, _coordsToLatLng); + return new Polyline(latlngs, options); + + case 'Polygon': + case 'MultiPolygon': + latlngs = coordsToLatLngs(coords, geometry.type === 'Polygon' ? 1 : 2, _coordsToLatLng); + return new Polygon(latlngs, options); + + case 'GeometryCollection': + for (i = 0, len = geometry.geometries.length; i < len; i++) { + var layer = geometryToLayer({ + geometry: geometry.geometries[i], + type: 'Feature', + properties: geojson.properties + }, options); + + if (layer) { + layers.push(layer); + } + } + return new FeatureGroup(layers); + + default: + throw new Error('Invalid GeoJSON object.'); + } + } + +// @function coordsToLatLng(coords: Array): LatLng +// Creates a `LatLng` object from an array of 2 numbers (longitude, latitude) +// or 3 numbers (longitude, latitude, altitude) used in GeoJSON for points. + function coordsToLatLng(coords) { + return new LatLng(coords[1], coords[0], coords[2]); + } + +// @function coordsToLatLngs(coords: Array, levelsDeep?: Number, coordsToLatLng?: Function): Array +// Creates a multidimensional array of `LatLng`s from a GeoJSON coordinates array. +// `levelsDeep` specifies the nesting level (0 is for an array of points, 1 for an array of arrays of points, etc., 0 by default). +// Can use a custom [`coordsToLatLng`](#geojson-coordstolatlng) function. + function coordsToLatLngs(coords, levelsDeep, _coordsToLatLng) { + var latlngs = []; + + for (var i = 0, len = coords.length, latlng; i < len; i++) { + latlng = levelsDeep ? + coordsToLatLngs(coords[i], levelsDeep - 1, _coordsToLatLng) : + (_coordsToLatLng || coordsToLatLng)(coords[i]); + + latlngs.push(latlng); + } + + return latlngs; + } + +// @function latLngToCoords(latlng: LatLng, precision?: Number): Array +// Reverse of [`coordsToLatLng`](#geojson-coordstolatlng) + function latLngToCoords(latlng, precision) { + precision = typeof precision === 'number' ? precision : 6; + return latlng.alt !== undefined ? + [formatNum(latlng.lng, precision), formatNum(latlng.lat, precision), formatNum(latlng.alt, precision)] : + [formatNum(latlng.lng, precision), formatNum(latlng.lat, precision)]; + } + +// @function latLngsToCoords(latlngs: Array, levelsDeep?: Number, closed?: Boolean): Array +// Reverse of [`coordsToLatLngs`](#geojson-coordstolatlngs) +// `closed` determines whether the first point should be appended to the end of the array to close the feature, only used when `levelsDeep` is 0. False by default. + function latLngsToCoords(latlngs, levelsDeep, closed, precision) { + var coords = []; + + for (var i = 0, len = latlngs.length; i < len; i++) { + coords.push(levelsDeep ? + latLngsToCoords(latlngs[i], levelsDeep - 1, closed, precision) : + latLngToCoords(latlngs[i], precision)); + } + + if (!levelsDeep && closed) { + coords.push(coords[0]); + } + + return coords; + } + + function getFeature(layer, newGeometry) { + return layer.feature ? + extend({}, layer.feature, {geometry: newGeometry}) : + asFeature(newGeometry); + } + +// @function asFeature(geojson: Object): Object +// Normalize GeoJSON geometries/features into GeoJSON features. + function asFeature(geojson) { + if (geojson.type === 'Feature' || geojson.type === 'FeatureCollection') { + return geojson; + } + + return { + type: 'Feature', + properties: {}, + geometry: geojson + }; + } + + var PointToGeoJSON = { + toGeoJSON: function (precision) { + return getFeature(this, { + type: 'Point', + coordinates: latLngToCoords(this.getLatLng(), precision) + }); + } + }; + +// @namespace Marker +// @method toGeoJSON(): Object +// Returns a [`GeoJSON`](http://en.wikipedia.org/wiki/GeoJSON) representation of the marker (as a GeoJSON `Point` Feature). + Marker.include(PointToGeoJSON); + +// @namespace CircleMarker +// @method toGeoJSON(): Object +// Returns a [`GeoJSON`](http://en.wikipedia.org/wiki/GeoJSON) representation of the circle marker (as a GeoJSON `Point` Feature). + Circle.include(PointToGeoJSON); + CircleMarker.include(PointToGeoJSON); + + +// @namespace Polyline +// @method toGeoJSON(): Object +// Returns a [`GeoJSON`](http://en.wikipedia.org/wiki/GeoJSON) representation of the polyline (as a GeoJSON `LineString` or `MultiLineString` Feature). + Polyline.include({ + toGeoJSON: function (precision) { + var multi = !_flat(this._latlngs); + + var coords = latLngsToCoords(this._latlngs, multi ? 1 : 0, false, precision); + + return getFeature(this, { + type: (multi ? 'Multi' : '') + 'LineString', + coordinates: coords + }); + } + }); + +// @namespace Polygon +// @method toGeoJSON(): Object +// Returns a [`GeoJSON`](http://en.wikipedia.org/wiki/GeoJSON) representation of the polygon (as a GeoJSON `Polygon` or `MultiPolygon` Feature). + Polygon.include({ + toGeoJSON: function (precision) { + var holes = !_flat(this._latlngs), + multi = holes && !_flat(this._latlngs[0]); + + var coords = latLngsToCoords(this._latlngs, multi ? 2 : holes ? 1 : 0, true, precision); + + if (!holes) { + coords = [coords]; + } + + return getFeature(this, { + type: (multi ? 'Multi' : '') + 'Polygon', + coordinates: coords + }); + } + }); + + +// @namespace LayerGroup + LayerGroup.include({ + toMultiPoint: function (precision) { + var coords = []; + + this.eachLayer(function (layer) { + coords.push(layer.toGeoJSON(precision).geometry.coordinates); + }); + + return getFeature(this, { + type: 'MultiPoint', + coordinates: coords + }); + }, + + // @method toGeoJSON(): Object + // Returns a [`GeoJSON`](http://en.wikipedia.org/wiki/GeoJSON) representation of the layer group (as a GeoJSON `FeatureCollection`, `GeometryCollection`, or `MultiPoint`). + toGeoJSON: function (precision) { + + var type = this.feature && this.feature.geometry && this.feature.geometry.type; + + if (type === 'MultiPoint') { + return this.toMultiPoint(precision); + } + + var isGeometryCollection = type === 'GeometryCollection', + jsons = []; + + this.eachLayer(function (layer) { + if (layer.toGeoJSON) { + var json = layer.toGeoJSON(precision); + if (isGeometryCollection) { + jsons.push(json.geometry); + } else { + var feature = asFeature(json); + // Squash nested feature collections + if (feature.type === 'FeatureCollection') { + jsons.push.apply(jsons, feature.features); + } else { + jsons.push(feature); + } + } + } + }); + + if (isGeometryCollection) { + return getFeature(this, { + geometries: jsons, + type: 'GeometryCollection' + }); + } + + return { + type: 'FeatureCollection', + features: jsons + }; + } + }); + +// @namespace GeoJSON +// @factory L.geoJSON(geojson?: Object, options?: GeoJSON options) +// Creates a GeoJSON layer. Optionally accepts an object in +// [GeoJSON format](http://geojson.org/geojson-spec.html) to display on the map +// (you can alternatively add it later with `addData` method) and an `options` object. + function geoJSON(geojson, options) { + return new GeoJSON(geojson, options); + } + +// Backward compatibility. + var geoJson = geoJSON; + + /* + * @class ImageOverlay + * @aka L.ImageOverlay + * @inherits Interactive layer + * + * Used to load and display a single image over specific bounds of the map. Extends `Layer`. + * + * @example + * + * ```js + * var imageUrl = 'http://www.lib.utexas.edu/maps/historical/newark_nj_1922.jpg', + * imageBounds = [[40.712216, -74.22655], [40.773941, -74.12544]]; + * L.imageOverlay(imageUrl, imageBounds).addTo(map); + * ``` + */ + + var ImageOverlay = Layer.extend({ + + // @section + // @aka ImageOverlay options + options: { + // @option opacity: Number = 1.0 + // The opacity of the image overlay. + opacity: 1, + + // @option alt: String = '' + // Text for the `alt` attribute of the image (useful for accessibility). + alt: '', + + // @option interactive: Boolean = false + // If `true`, the image overlay will emit [mouse events](#interactive-layer) when clicked or hovered. + interactive: false, + + // @option crossOrigin: Boolean = false + // If true, the image will have its crossOrigin attribute set to ''. This is needed if you want to access image pixel data. + crossOrigin: false, + + // @option errorOverlayUrl: String = '' + // URL to the overlay image to show in place of the overlay that failed to load. + errorOverlayUrl: '', + + // @option zIndex: Number = 1 + // The explicit [zIndex](https://developer.mozilla.org/docs/Web/CSS/CSS_Positioning/Understanding_z_index) of the tile layer. + zIndex: 1, + + // @option className: String = '' + // A custom class name to assign to the image. Empty by default. + className: '', + }, + + initialize: function (url, bounds, options) { // (String, LatLngBounds, Object) + this._url = url; + this._bounds = toLatLngBounds(bounds); + + setOptions(this, options); + }, + + onAdd: function () { + if (!this._image) { + this._initImage(); + + if (this.options.opacity < 1) { + this._updateOpacity(); + } + } + + if (this.options.interactive) { + addClass(this._image, 'leaflet-interactive'); + this.addInteractiveTarget(this._image); + } + + this.getPane().appendChild(this._image); + this._reset(); + }, + + onRemove: function () { + remove(this._image); + if (this.options.interactive) { + this.removeInteractiveTarget(this._image); + } + }, + + // @method setOpacity(opacity: Number): this + // Sets the opacity of the overlay. + setOpacity: function (opacity) { + this.options.opacity = opacity; + + if (this._image) { + this._updateOpacity(); + } + return this; + }, + + setStyle: function (styleOpts) { + if (styleOpts.opacity) { + this.setOpacity(styleOpts.opacity); + } + return this; + }, + + // @method bringToFront(): this + // Brings the layer to the top of all overlays. + bringToFront: function () { + if (this._map) { + toFront(this._image); + } + return this; + }, + + // @method bringToBack(): this + // Brings the layer to the bottom of all overlays. + bringToBack: function () { + if (this._map) { + toBack(this._image); + } + return this; + }, + + // @method setUrl(url: String): this + // Changes the URL of the image. + setUrl: function (url) { + this._url = url; + + if (this._image) { + this._image.src = url; + } + return this; + }, + + // @method setBounds(bounds: LatLngBounds): this + // Update the bounds that this ImageOverlay covers + setBounds: function (bounds) { + this._bounds = bounds; + + if (this._map) { + this._reset(); + } + return this; + }, + + getEvents: function () { + var events = { + zoom: this._reset, + viewreset: this._reset + }; + + if (this._zoomAnimated) { + events.zoomanim = this._animateZoom; + } + + return events; + }, + + // @method: setZIndex(value: Number) : this + // Changes the [zIndex](#imageoverlay-zindex) of the image overlay. + setZIndex: function (value) { + this.options.zIndex = value; + this._updateZIndex(); + return this; + }, + + // @method getBounds(): LatLngBounds + // Get the bounds that this ImageOverlay covers + getBounds: function () { + return this._bounds; + }, + + // @method getElement(): HTMLElement + // Returns the instance of [`HTMLImageElement`](https://developer.mozilla.org/docs/Web/API/HTMLImageElement) + // used by this overlay. + getElement: function () { + return this._image; + }, + + _initImage: function () { + var img = this._image = create$1('img', + 'leaflet-image-layer ' + (this._zoomAnimated ? 'leaflet-zoom-animated' : '') + + (this.options.className || '')); + + img.onselectstart = falseFn; + img.onmousemove = falseFn; + + // @event load: Event + // Fired when the ImageOverlay layer has loaded its image + img.onload = bind(this.fire, this, 'load'); + img.onerror = bind(this._overlayOnError, this, 'error'); + + if (this.options.crossOrigin) { + img.crossOrigin = ''; + } + + if (this.options.zIndex) { + this._updateZIndex(); + } + + img.src = this._url; + img.alt = this.options.alt; + }, + + _animateZoom: function (e) { + var scale = this._map.getZoomScale(e.zoom), + offset = this._map._latLngBoundsToNewLayerBounds(this._bounds, e.zoom, e.center).min; + + setTransform(this._image, offset, scale); + }, + + _reset: function () { + var image = this._image, + bounds = new Bounds( + this._map.latLngToLayerPoint(this._bounds.getNorthWest()), + this._map.latLngToLayerPoint(this._bounds.getSouthEast())), + size = bounds.getSize(); + + setPosition(image, bounds.min); + + image.style.width = size.x + 'px'; + image.style.height = size.y + 'px'; + }, + + _updateOpacity: function () { + setOpacity(this._image, this.options.opacity); + }, + + _updateZIndex: function () { + if (this._image && this.options.zIndex !== undefined && this.options.zIndex !== null) { + this._image.style.zIndex = this.options.zIndex; + } + }, + + _overlayOnError: function () { + // @event error: Event + // Fired when the ImageOverlay layer has loaded its image + this.fire('error'); + + var errorUrl = this.options.errorOverlayUrl; + if (errorUrl && this._url !== errorUrl) { + this._url = errorUrl; + this._image.src = errorUrl; + } + } + }); + +// @factory L.imageOverlay(imageUrl: String, bounds: LatLngBounds, options?: ImageOverlay options) +// Instantiates an image overlay object given the URL of the image and the +// geographical bounds it is tied to. + var imageOverlay = function (url, bounds, options) { + return new ImageOverlay(url, bounds, options); + }; + + /* + * @class VideoOverlay + * @aka L.VideoOverlay + * @inherits ImageOverlay + * + * Used to load and display a video player over specific bounds of the map. Extends `ImageOverlay`. + * + * A video overlay uses the [`