Gps Tracker
https://www.websmithing.com/gpstracker/updatelocation.php
-
Tracking is Off
Tracking is On
- Tracking has now started.
- Tracking has now stopped.
Please restart tracking to change the time interval.
Cannot reach the website that you have entered. Did you spell it correctly? Does your website use http or https?
Please fill in both fields. User names cannot have a space.
Please go into Settings > Apps > All and enable Google Play Services.
-
user name
upload website
- How often to update website. Updating more often wears down the battery faster. Update every:
+ Update website every:
1 minute
5 minutes
- 15 minutes
- 30 minutes
- 1 hour
diff --git a/phoneClients/android/build.gradle b/phoneClients/android/build.gradle
index 4c8f6d8..c722660 100644
--- a/phoneClients/android/build.gradle
+++ b/phoneClients/android/build.gradle
@@ -5,7 +5,7 @@
mavenCentral()
}
dependencies {
- classpath 'com.android.tools.build:gradle:0.11.+'
+ classpath 'com.android.tools.build:gradle:0.12.2'
}
}
diff --git a/phoneClients/android/build/intermediates/dex-cache/cache.xml b/phoneClients/android/build/intermediates/dex-cache/cache.xml
index 2aa96cb..63b6e3c 100644
--- a/phoneClients/android/build/intermediates/dex-cache/cache.xml
+++ b/phoneClients/android/build/intermediates/dex-cache/cache.xml
@@ -2,28 +2,40 @@
+
+
+
+
-
-
+ revision="20.0.0"
+ sha1="8694693636ab36910c8100ce527c258ff3ebece0"/>
diff --git a/servers/php/README.md b/servers/php/README.md
index 9d7391d..70d5df8 100644
--- a/servers/php/README.md
+++ b/servers/php/README.md
@@ -1,16 +1,98 @@
-The php version of GpsTracker is now updated to v3 of google maps, jquery and leaflet. The database file is in the mysql directory and this needs to be installed from the command line, please read the quick start guide to see how to do that:
+Gps Tracker v3.2.4
+-------------
-https://www.websmithing.com/2014/01/20/quick-start-guide-for-gpstracker-3/#phpserver
+##### Google Map Gps Cell Phone Tracker
-If you want to turn on the adsense advertisement in the map, then change line 8 of google.js to enabled:
- var adsense_status = 'enabled';
+**Just a quick note, Aug 26, 2014**
-and add your adsense channel number and publisher id to line 206 and 207:
+There is a responsive front-end using bootstrap in the bootstrap branch. This will be brought into the master branch as soon as I am done making it. You are free to use it now. I am using it on websmithing.com as my test pages and they are working fine.
- user_adsense.channelNumber = "YOUR_CHANNEL_NUMBER";
- user_adsense.publisherID = "YOUR_PUBLISHER_ID";
+The dark theme is displaymap.php and the light theme is displaymap-light.php.
-https://github.com/nickfox/GpsTracker/blob/master/servers/php/javascript/leaflet-plugins/google.js
+If you want to use that branch, you need to add this stored procedure to your database:
+DROP PROCEDURE IF EXISTS prcGetAllRoutesForMap;
+DELIMITER //
+CREATE PROCEDURE prcGetAllRoutesForMap()
+BEGIN
+ SELECT distinct(sessionId), max(gpsTime) gpsTime,
+ CONCAT('{ "latitude":"', CAST(latitude AS CHAR),'", "longitude":"', CAST(longitude AS CHAR), '", "speed":"', CAST(speed AS CHAR), '", "direction":"', CAST(direction AS CHAR), '", "distance":"', CAST(distance AS CHAR), '", "locationMethod":"', locationMethod, '", "gpsTime":"', DATE_FORMAT(gpsTime, '%b %e %Y %h:%i%p'), '", "phoneNumber":"', phoneNumber, '", "sessionID":"', CAST(sessionID AS CHAR), '", "accuracy":"', CAST(accuracy AS CHAR), '", "extraInfo":"', extraInfo, '" }') json
+ FROM gpslocations
+ WHERE sessionID != '0' && CHAR_LENGTH(sessionID) != 0
+ GROUP BY sessionID;
+END//
+DELIMITER ;
+
+*************
+
+This project allows you to track cell phones periodically. For instance every minute or every five minutes. You can watch the cell phone being tracked in real time using google maps and you can store and reload routes easily.
+
+You have the choice of two server stacks. Either using asp.net and sql server or using php and mysql. Both are now in the same download but you only need to use one.
+
+If you need help, please go to:
+
+https://www.websmithing.com/gps-tracker/
+
+Here is a quick start guide to help you set up Gps Tracker:
+
+https://www.websmithing.com/2014/01/20/quick-start-guide-for-gpstracker-3/
+
+*************
+
+#### Changelog
+
+*************
+
+Jun 12, 2014 - v3.2.4
+
+Fixed android intervals not working above one minute and removed adsense ads publisher id.
+
+*************
+
+Jun 6, 2014 - v3.2.3
+
+This version fixes the zoom buttons on the google map which was causing the app to freeze and updates leaflet to version 0.7.3. This version is now free from all known errors.
+
+*************
+
+May 5, 2014 - v3.2.2
+
+This version fixes the total distance traveled in the android app.
+
+*************
+
+May 1, 2014 - v3.2.1
+
+This version changes the UI of the android app from two buttons (Save and Track) to one button (Track).
+
+
+
+
+and the android version is now in Google Play:
+
+
+https://play.google.com/store/apps/details?id=com.websmithing.gpstracker
+
+*************
+
+Apr 20, 2014 - v3.2.0
+
+Some pretty major changes here. The android app now has google play location services running in a background service. Also updates will restart if phone is rebooted. The servers are both now using json and jquery. Xml is gone forever. Both servers now support multiple map types. It's currently set up for google maps, bing maps and OpenStreetMaps.
+
+
+*************
+
+Feb 11, 2014 - v3.1.0
+
+Version 3.1.0 updates the php server code from google maps 2 to 3 and now uses jquery. There was a bug in the delete route stored procedure (the sessionID parameter was to small and was causing the delete to fail silently).
+
+*************
+
+Jan 14, 2014 - v3.0.0
+
+Version 3.0.0 of Gps Tracker is complete. This was a very big change from the last version. It's been updated with ios, android, windowsPhone and java me/j2me phone clients and the servers have all been updated as well. Please let me know how it works, good or bad and create an issue if you find a problem. Thanks and enjoy the software, Nick.
+
+ps. I will be writing tutorials and documentation shortly.
+
diff --git a/servers/php/css/styles.css b/servers/php/css/styles.css
new file mode 100644
index 0000000..27143ad
--- /dev/null
+++ b/servers/php/css/styles.css
@@ -0,0 +1,104 @@
+body {
+ /* background-color: #ccc; */
+}
+.container {
+ border: 1px solid #aaa;
+ margin-top: 10px;
+}
+.routeSelectDark {
+ background-color: #ccc;
+}
+.col-sm-2 {
+ height: 50px;
+}
+.col-sm-3 {
+ height: 50px;
+}
+.col-sm-6 {
+ height: 50px;
+}
+.col-sm-8 {
+ height: 50px;
+}
+.col-sm-10 {
+ height: 50px;
+}
+#toplogo {
+ padding-top: 10px;
+}
+#messages {
+ padding-top: 19px;
+ text-align: right;
+ margin: 0px 0px 10px 0px;
+}
+#routeSelect {
+ width: 100%;
+ color: #000;
+ margin-bottom:5px;
+ /* background-color: #ccc; */
+}
+#autorefresh {
+ width: 100%;
+ margin-bottom:5px;
+}
+#delete {
+ width: 100%;
+ margin-bottom:5px;
+}
+#refresh {
+ width: 100%;
+ margin-bottom:5px;
+}
+#viewall {
+ width: 100%;
+ margin-bottom:5px;
+}
+.deletediv {
+ text-align: center;
+}
+.autorefreshdiv {
+ text-align: center;
+}
+.refreshdiv {
+ text-align: center;
+}
+.viewalldiv {
+ text-align: center;
+}
+@media only screen and (min-width : 768px) {
+ .container {
+ margin-top: 25px;
+ }
+
+ .deletediv {
+ text-align: left;
+ }
+ .autorefreshdiv {
+ text-align: center;
+ }
+
+ .refreshdiv {
+ text-align: center;
+ }
+
+ .viewalldiv {
+ text-align: right;
+ }
+}
+#map-canvas {
+ border: 1px solid #000;
+ width: 100%;
+ height: 460px;
+}
+#mapdiv {
+ height: 475px;
+}
+#selectdiv {
+ margin-bottom: 10px;
+}
+#halimage {
+ vertical-align:middle;
+ width: 37px;
+ height: 37px;
+ margin-bottom: 5px;
+}
diff --git a/servers/php/css/styles_old.css b/servers/php/css/styles_old.css
new file mode 100644
index 0000000..a003888
--- /dev/null
+++ b/servers/php/css/styles_old.css
@@ -0,0 +1,252 @@
+* {
+ margin: 0;
+ padding: 0;
+}
+#gpstracker {
+ position:relative;
+ top: 10px;
+ width:97%;
+ border:1px solid #909090;
+ margin: 0 auto;
+ padding: 0;
+ /*background-color:#CCEDFF;*/
+ background-color:rgba(0,0,0,0.8);
+ font-family: helvetica, arial, sans-serif;
+ font-size: 15px;
+}
+
+#map-canvas {
+ top: 15px;
+ width: 95%;
+ height:400px;
+ /*background-color:#FFF !important;*/
+ background-color:rgba(0,0,0,0.5);
+ border:1px solid #909090;
+ overflow: hidden !important;
+ padding: 0px !important;
+ margin: 0 auto;
+ word-wrap: normal;
+}
+
+#map-canvas img {
+ background-color: transparent !important;
+ max-width: 99999px !important; /* Some themes have max-width/max-height set, some phones can't accept max-width: auto. */
+ max-height: none !important;
+ visibility: inherit !important;
+ padding: 0px !important;
+ margin: 0px 20px 0px 0px !important;
+ box-shadow: none !important; /* 2012 adds box shadow to all images */
+ border-radius: 0px !important; /* 2012 adds border-radius to all images */
+}
+
+#hal-image {
+ width:37px;
+ height:37px;
+ vertical-align:middle;
+ margin-bottom:5px;
+}
+
+#gps_info_table {
+ border-spacing: 0;
+ border-collapse: separate;
+}
+
+#gps_info_table td {
+ padding: 0;
+ text-align: right;
+}
+
+#messages {
+ position:relative;
+ top: 7px;
+ width:95%;
+ margin: 0 auto;
+ margin-bottom:7px;
+}
+
+#top-messages {
+ display:table;
+ float:right;
+ text-align:right;
+ margin-top:10px;
+ width:50%;
+ min-width:50%;
+ color:#33ffcc !important;
+}
+
+#selectRoute {
+ position:relative;
+ left: 0px;
+ top: 0px;
+ width: 100%;
+}
+
+#timer-countdown {
+ color:#F00;
+}
+
+#top-label {
+ color:#33ff66;
+ margin-right:3px;
+}
+
+.bottom-label {
+ color:#33ff66;
+ margin-right:3px;
+}
+
+#autorefresh {
+ position:relative;
+ float: right;
+ right: 17px;
+ width: 110px;
+ height:37px;
+ margin-top: 25px;
+ border:1px;
+ border-style:solid;
+ border-color:#555;
+ color:#000 !important;
+ background:#33ffcc !important;
+}
+
+#refresh {
+ position:relative;
+ float: right;
+ width: 110px;
+ height:37px;
+ margin-top: 25px;
+ border:1px;
+ border-style:solid;
+ border-color:#555;
+ color:#000;
+ background:#33ffcc !important;
+}
+
+#delete {
+ position:relative;
+ float: right;
+ right: 33px;
+ width: 110px;
+ height:37px;
+ margin-top: 25px;
+ border:1px;
+ border-style:solid;
+ border-color:#555;
+ color:#000;
+ background:#33ffcc !important;
+}
+
+#remaining-time {
+ position:relative;
+ float:left;
+ width: 400px;
+ margin-top:3px;
+ /* background:#33ffcc; */
+}
+
+/* Minimum width of 600 pixels for desktop */
+@media screen and (min-width: 600px) {
+ #bottom-messages {
+ width:95%;
+ margin: 0 auto;
+ }
+ #time-messages {
+ position:relative;
+ float:left;
+ top: 20px;
+ max-width:250px;
+ }
+
+ #gpstracker {
+ height:525px;
+ }
+}
+
+/* Maximum width of 599 pixels for phones */
+@media screen and (max-width: 599px) {
+ #time-messages {
+ position:relative;
+ float:left;
+ left: 10px;
+ top: 20px;
+ max-width:250px;
+ }
+
+ #refresh {
+ clear:both;
+ margin-top: 37px !important;
+ }
+
+ #autorefresh {
+ clear:left;
+ margin-top: 37px !important;
+ }
+
+ #gpstracker {
+ height:527px;
+ }
+}
+
+
+/*
+* {
+ margin: 0;
+ padding: 0;
+}
+body {
+ font-family: arial, helvetica, sans-serif;
+ font-size: 17px;
+}
+#test {
+ position:absolute;
+ left: 750px;
+ top: 50px;
+ width:400px;
+}
+#messages {
+ position:absolute;
+ left: 10px;
+ top: 10px;
+ width:950px;
+}
+#map {
+ position:absolute;
+ left: 10px;
+ top: 40px;
+ width:700px;
+ height:450px;
+ border:1px;
+ border-style:solid;
+ background-color:#FFF;
+}
+#selectRoute {
+ position:absolute;
+ left: 10px;
+ top: 510px;
+ width: 600px;
+}
+#selectRefresh {
+ position:absolute;
+ left: 10px;
+ top: 550px;
+ width: 250px;
+}
+#selectZoomLevel {
+ position:absolute;
+ left: 360px;
+ top: 550px;
+ width: 250px;
+}
+#delete {
+ position:absolute;
+ left: 637px;
+ top: 510px;
+ width: 75px;
+}
+#refresh {
+ position:absolute;
+ left: 637px;
+ top: 550px;
+ width: 75px;
+}
+*/
diff --git a/servers/php/dbconnect.php b/servers/php/dbconnect.php
index 573b384..b88f346 100644
--- a/servers/php/dbconnect.php
+++ b/servers/php/dbconnect.php
@@ -1,15 +1,10 @@
PDO::ERRMODE_EXCEPTION,
+ PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_NUM);
-$mysqli = new mysqli($dbhost, $dbuser, $dbpass, $dbname);
+$pdo = new PDO('mysql:host=localhost;dbname=gpstracker;charset=utf8', $dbuser, $dbpass, $params);
-if (mysqli_connect_errno()) {
- echo "Connection failed: " . mysqli_connect_error();
- exit();
-}
-
-?>
+?>
\ No newline at end of file
diff --git a/servers/php/deleteroute.php b/servers/php/deleteroute.php
index bc195e1..2484af5 100644
--- a/servers/php/deleteroute.php
+++ b/servers/php/deleteroute.php
@@ -1,15 +1,9 @@
prepare('CALL prcDeleteRoute(:sessionID)');
+ $stmt->execute(array(':sessionID' => $sessionid));
- isset($_GET['sessionID']) ? $sessionID = $_GET['sessionID'] : $sessionID = '0';
- isset($_GET['phoneNumber']) ? $phoneNumber = $_GET['phoneNumber'] : $phoneNumber = '0';
-
- $query = 'CALL prcDeleteRoute(\'' . $sessionID . '\',\'' . $phoneNumber . '\')';
-
- // execute query
- if (!$mysqli->multi_query($query)) {
- die('$mysqli->multi_query: ' . $mysqli->error);
- }
-
- $mysqli->close();
?>
diff --git a/servers/php/displaymap-blue.php b/servers/php/displaymap-blue.php
new file mode 100644
index 0000000..9485109
--- /dev/null
+++ b/servers/php/displaymap-blue.php
@@ -0,0 +1,59 @@
+
+
+
+
+ Gps Tracker
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
GpsTracker
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/servers/php/displaymap-dark.php b/servers/php/displaymap-dark.php
new file mode 100644
index 0000000..a244e71
--- /dev/null
+++ b/servers/php/displaymap-dark.php
@@ -0,0 +1,59 @@
+
+
+
+
+ Gps Tracker
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
GpsTracker
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/servers/php/displaymap.php b/servers/php/displaymap.php
index d28edac..329bb9b 100644
--- a/servers/php/displaymap.php
+++ b/servers/php/displaymap.php
@@ -1,114 +1,59 @@
-
-
+
+
- Google Map GPS Cell Phone Tracker
+
+ Gps Tracker
-
-
+
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
-
- GpsTracker
-
-
-
-
-
- Auto Refresh - Off
- Auto Refresh - 1 minute
- Auto Refresh - 2 minutes
- Auto Refresh - 3 minutes
- Auto Refresh - 5 minutes
- Auto Refresh - 10 minutes
-
-
-
- Zoom Level - 1
- Zoom Level - 2
- Zoom Level - 3
- Zoom Level - 4
- Zoom Level - 5
- Zoom Level - 6
- Zoom Level - 7
- Zoom Level - 8
- Zoom Level - 9
- Zoom Level - 10
- Zoom Level - 11
- Zoom Level - 12
- Zoom Level - 13
- Zoom Level - 14
- Zoom Level - 15
- Zoom Level - 16
- Zoom Level - 17
-
-
-
-
-
- <== after you select a route below, check out the different map providers here. I'm using google maps, bing maps and OpenStreetMaps but there are many more providers.
-
+
+
+
+
+
GpsTracker
+
+
+
+
+
+
+
-
-
-
-
-
+
\ No newline at end of file
diff --git a/servers/php/getallroutesformap.php b/servers/php/getallroutesformap.php
new file mode 100644
index 0000000..ef30fe6
--- /dev/null
+++ b/servers/php/getallroutesformap.php
@@ -0,0 +1,21 @@
+prepare('CALL prcGetAllRoutesForMap();');
+ $stmt->execute();
+
+ $json = '{ "locations": [';
+
+ foreach ($stmt as $row) {
+ $json .= $row[2];
+ $json .= ',';
+ }
+
+ $json = rtrim($json, ",");
+ $json .= '] }';
+
+ header('Content-Type: application/json');
+ echo $json;
+
+?>
\ No newline at end of file
diff --git a/servers/php/getgpslocations.php b/servers/php/getgpslocations.php
deleted file mode 100644
index 93a69e4..0000000
--- a/servers/php/getgpslocations.php
+++ /dev/null
@@ -1,36 +0,0 @@
-multi_query($query)) {
-
- do { // build our json array
- if ($result = $mysqli->store_result()) {
- while ($row = $result->fetch_row()) {
- $json .= $row[0];
- $json .= ',';
- }
- $result->close();
- }
- } while ($mysqli->more_results() && $mysqli->next_result());
- }
- else {
- die('error: ' . $mysqli->error);
- }
-
- $json = rtrim($json, ",");
- $json .= '] }';
-
- header('Content-Type: application/json');
- echo $json;
-
- $mysqli->close();
-?>
diff --git a/servers/php/getrouteformap.php b/servers/php/getrouteformap.php
index 93a69e4..dee74ae 100644
--- a/servers/php/getrouteformap.php
+++ b/servers/php/getrouteformap.php
@@ -1,29 +1,16 @@
prepare('CALL prcGetRouteForMap(:sessionID)');
+ $stmt->execute(array(':sessionID' => $sessionid));
$json = '{ "locations": [';
- // execute query
- if ($mysqli->multi_query($query)) {
-
- do { // build our json array
- if ($result = $mysqli->store_result()) {
- while ($row = $result->fetch_row()) {
- $json .= $row[0];
- $json .= ',';
- }
- $result->close();
- }
- } while ($mysqli->more_results() && $mysqli->next_result());
- }
- else {
- die('error: ' . $mysqli->error);
+ foreach ($stmt as $row) {
+ $json .= $row[0];
+ $json .= ',';
}
$json = rtrim($json, ",");
@@ -32,5 +19,4 @@
header('Content-Type: application/json');
echo $json;
- $mysqli->close();
-?>
+?>
\ No newline at end of file
diff --git a/servers/php/getroutes.php b/servers/php/getroutes.php
index ab79814..2eb8460 100644
--- a/servers/php/getroutes.php
+++ b/servers/php/getroutes.php
@@ -1,32 +1,19 @@
prepare('CALL prcGetRoutes();');
+ $stmt->execute();
+
$json = '{ "routes": [';
- // execute query
- if ($mysqli->multi_query($query)) {
-
- do { // build our json array
- if ($result = $mysqli->store_result()) {
- while ($row = $result->fetch_row()) {
- $json .= $row[0];
- $json .= ',';
- }
- $result->close();
- }
- } while ($mysqli->more_results() && $mysqli->next_result());
+ foreach ($stmt as $row) {
+ $json .= $row[0];
+ $json .= ',';
}
- else {
- die('error: ' . $mysqli->error);
- }
-
+
$json = rtrim($json, ",");
$json .= '] }';
header('Content-Type: application/json');
echo $json;
-
- $mysqli->close();
?>
diff --git a/servers/php/images/beepphone.jpg b/servers/php/images/beepphone.jpg
new file mode 100644
index 0000000..0f979fc
--- /dev/null
+++ b/servers/php/images/beepphone.jpg
Binary files differ
diff --git a/servers/php/images/blue_circle.png b/servers/php/images/blue_circle.png
new file mode 100644
index 0000000..b922e48
--- /dev/null
+++ b/servers/php/images/blue_circle.png
Binary files differ
diff --git a/servers/php/images/coolgreen2_small.png b/servers/php/images/coolgreen2_small.png
new file mode 100644
index 0000000..5e002b4
--- /dev/null
+++ b/servers/php/images/coolgreen2_small.png
Binary files differ
diff --git a/servers/php/images/deleteroute.jpg b/servers/php/images/deleteroute.jpg
new file mode 100644
index 0000000..14ab9ab
--- /dev/null
+++ b/servers/php/images/deleteroute.jpg
Binary files differ
diff --git a/servers/php/images/download.jpg b/servers/php/images/download.jpg
new file mode 100644
index 0000000..34aa06f
--- /dev/null
+++ b/servers/php/images/download.jpg
Binary files differ
diff --git a/servers/php/images/findphone.jpg b/servers/php/images/findphone.jpg
new file mode 100644
index 0000000..ccd83e1
--- /dev/null
+++ b/servers/php/images/findphone.jpg
Binary files differ
diff --git a/servers/php/images/gpstracker-man-37.png b/servers/php/images/gpstracker-man-37.png
new file mode 100644
index 0000000..af9e814
--- /dev/null
+++ b/servers/php/images/gpstracker-man-37.png
Binary files differ
diff --git a/servers/php/images/gpstracker-man-blue-37.png b/servers/php/images/gpstracker-man-blue-37.png
new file mode 100644
index 0000000..d1ce958
--- /dev/null
+++ b/servers/php/images/gpstracker-man-blue-37.png
Binary files differ
diff --git a/servers/php/images/gpstracker-man-blue.png b/servers/php/images/gpstracker-man-blue.png
new file mode 100644
index 0000000..6d3f785
--- /dev/null
+++ b/servers/php/images/gpstracker-man-blue.png
Binary files differ
diff --git a/servers/php/images/gpstracker-man.png b/servers/php/images/gpstracker-man.png
new file mode 100644
index 0000000..eb79cdb
--- /dev/null
+++ b/servers/php/images/gpstracker-man.png
Binary files differ
diff --git a/servers/php/images/gpstracker_49x49.png b/servers/php/images/gpstracker_49x49.png
new file mode 100644
index 0000000..04b3a6e
--- /dev/null
+++ b/servers/php/images/gpstracker_49x49.png
Binary files differ
diff --git a/servers/php/images/info.jpg b/servers/php/images/info.jpg
new file mode 100644
index 0000000..18b357a
--- /dev/null
+++ b/servers/php/images/info.jpg
Binary files differ
diff --git a/servers/php/images/matrix.jpg b/servers/php/images/matrix.jpg
new file mode 100644
index 0000000..73d8d68
--- /dev/null
+++ b/servers/php/images/matrix.jpg
Binary files differ
diff --git a/servers/php/images/purple_circle.png b/servers/php/images/purple_circle.png
new file mode 100644
index 0000000..209ba12
--- /dev/null
+++ b/servers/php/images/purple_circle.png
Binary files differ
diff --git a/servers/php/images/refresh.jpg b/servers/php/images/refresh.jpg
new file mode 100644
index 0000000..f48604a
--- /dev/null
+++ b/servers/php/images/refresh.jpg
Binary files differ
diff --git a/servers/php/images/start.jpg b/servers/php/images/start.jpg
new file mode 100644
index 0000000..643606c
--- /dev/null
+++ b/servers/php/images/start.jpg
Binary files differ
diff --git a/servers/php/images/stop_dark.jpg b/servers/php/images/stop_dark.jpg
new file mode 100644
index 0000000..9b2d522
--- /dev/null
+++ b/servers/php/images/stop_dark.jpg
Binary files differ
diff --git a/servers/php/images/stop_green.jpg b/servers/php/images/stop_green.jpg
new file mode 100644
index 0000000..00aaa99
--- /dev/null
+++ b/servers/php/images/stop_green.jpg
Binary files differ
diff --git a/servers/php/javascript/leaflet-0.7.3/images/layers-2x.png b/servers/php/javascript/leaflet-0.7.3/images/layers-2x.png
deleted file mode 100644
index a2cf7f9..0000000
--- a/servers/php/javascript/leaflet-0.7.3/images/layers-2x.png
+++ /dev/null
Binary files differ
diff --git a/servers/php/javascript/leaflet-0.7.3/images/layers.png b/servers/php/javascript/leaflet-0.7.3/images/layers.png
deleted file mode 100644
index bca0a0e..0000000
--- a/servers/php/javascript/leaflet-0.7.3/images/layers.png
+++ /dev/null
Binary files differ
diff --git a/servers/php/javascript/leaflet-0.7.3/images/marker-icon-2x.png b/servers/php/javascript/leaflet-0.7.3/images/marker-icon-2x.png
deleted file mode 100644
index 0015b64..0000000
--- a/servers/php/javascript/leaflet-0.7.3/images/marker-icon-2x.png
+++ /dev/null
Binary files differ
diff --git a/servers/php/javascript/leaflet-0.7.3/images/marker-icon.png b/servers/php/javascript/leaflet-0.7.3/images/marker-icon.png
deleted file mode 100644
index e2e9f75..0000000
--- a/servers/php/javascript/leaflet-0.7.3/images/marker-icon.png
+++ /dev/null
Binary files differ
diff --git a/servers/php/javascript/leaflet-0.7.3/images/marker-shadow.png b/servers/php/javascript/leaflet-0.7.3/images/marker-shadow.png
deleted file mode 100644
index d1e773c..0000000
--- a/servers/php/javascript/leaflet-0.7.3/images/marker-shadow.png
+++ /dev/null
Binary files differ
diff --git a/servers/php/javascript/leaflet-0.7.3/leaflet-src.js b/servers/php/javascript/leaflet-0.7.3/leaflet-src.js
deleted file mode 100644
index 4947090..0000000
--- a/servers/php/javascript/leaflet-0.7.3/leaflet-src.js
+++ /dev/null
@@ -1,9180 +0,0 @@
-/*
- Leaflet, a JavaScript library for mobile-friendly interactive maps. http://leafletjs.com
- (c) 2010-2013, Vladimir Agafonkin
- (c) 2010-2011, CloudMade
-*/
-(function (window, document, undefined) {
-var oldL = window.L,
- L = {};
-
-L.version = '0.7.3';
-
-// define Leaflet for Node module pattern loaders, including Browserify
-if (typeof module === 'object' && typeof module.exports === 'object') {
- module.exports = L;
-
-// define Leaflet as an AMD module
-} else if (typeof define === 'function' && define.amd) {
- define(L);
-}
-
-// define Leaflet as a global L variable, saving the original L to restore later if needed
-
-L.noConflict = function () {
- window.L = oldL;
- return this;
-};
-
-window.L = L;
-
-
-/*
- * L.Util contains various utility functions used throughout Leaflet code.
- */
-
-L.Util = {
- extend: function (dest) { // (Object[, Object, ...]) ->
- var sources = Array.prototype.slice.call(arguments, 1),
- i, j, len, src;
-
- for (j = 0, len = sources.length; j < len; j++) {
- src = sources[j] || {};
- for (i in src) {
- if (src.hasOwnProperty(i)) {
- dest[i] = src[i];
- }
- }
- }
- return dest;
- },
-
- bind: function (fn, obj) { // (Function, Object) -> Function
- var args = arguments.length > 2 ? Array.prototype.slice.call(arguments, 2) : null;
- return function () {
- return fn.apply(obj, args || arguments);
- };
- },
-
- stamp: (function () {
- var lastId = 0,
- key = '_leaflet_id';
- return function (obj) {
- obj[key] = obj[key] || ++lastId;
- return obj[key];
- };
- }()),
-
- invokeEach: function (obj, method, context) {
- var i, args;
-
- if (typeof obj === 'object') {
- args = Array.prototype.slice.call(arguments, 3);
-
- for (i in obj) {
- method.apply(context, [i, obj[i]].concat(args));
- }
- return true;
- }
-
- return false;
- },
-
- limitExecByInterval: function (fn, time, context) {
- var lock, execOnUnlock;
-
- return function wrapperFn() {
- var args = arguments;
-
- if (lock) {
- execOnUnlock = true;
- return;
- }
-
- lock = true;
-
- setTimeout(function () {
- lock = false;
-
- if (execOnUnlock) {
- wrapperFn.apply(context, args);
- execOnUnlock = false;
- }
- }, time);
-
- fn.apply(context, args);
- };
- },
-
- falseFn: function () {
- return false;
- },
-
- formatNum: function (num, digits) {
- var pow = Math.pow(10, digits || 5);
- return Math.round(num * pow) / pow;
- },
-
- trim: function (str) {
- return str.trim ? str.trim() : str.replace(/^\s+|\s+$/g, '');
- },
-
- splitWords: function (str) {
- return L.Util.trim(str).split(/\s+/);
- },
-
- setOptions: function (obj, options) {
- obj.options = L.extend({}, obj.options, options);
- return obj.options;
- },
-
- getParamString: function (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('&');
- },
- template: function (str, data) {
- return str.replace(/\{ *([\w_]+) *\}/g, 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;
- });
- },
-
- isArray: Array.isArray || function (obj) {
- return (Object.prototype.toString.call(obj) === '[object Array]');
- },
-
- emptyImageUrl: 'data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs='
-};
-
-(function () {
-
- // inspired by http://paulirish.com/2011/requestanimationframe-for-smart-animating/
-
- function getPrefixed(name) {
- var i, fn,
- prefixes = ['webkit', 'moz', 'o', 'ms'];
-
- for (i = 0; i < prefixes.length && !fn; i++) {
- fn = window[prefixes[i] + name];
- }
-
- return fn;
- }
-
- var lastTime = 0;
-
- 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); };
-
-
- L.Util.requestAnimFrame = function (fn, context, immediate, element) {
- fn = L.bind(fn, context);
-
- if (immediate && requestFn === timeoutDefer) {
- fn();
- } else {
- return requestFn.call(window, fn, element);
- }
- };
-
- L.Util.cancelAnimFrame = function (id) {
- if (id) {
- cancelFn.call(window, id);
- }
- };
-
-}());
-
-// shortcuts for most used utility functions
-L.extend = L.Util.extend;
-L.bind = L.Util.bind;
-L.stamp = L.Util.stamp;
-L.setOptions = L.Util.setOptions;
-
-
-/*
- * L.Class powers the OOP facilities of the library.
- * Thanks to John Resig and Dean Edwards for inspiration!
- */
-
-L.Class = function () {};
-
-L.Class.extend = function (props) {
-
- // extended class with the new prototype
- var NewClass = function () {
-
- // call the constructor
- if (this.initialize) {
- this.initialize.apply(this, arguments);
- }
-
- // call all constructor hooks
- if (this._initHooks) {
- this.callInitHooks();
- }
- };
-
- // instantiate class without calling constructor
- var F = function () {};
- F.prototype = this.prototype;
-
- var proto = new F();
- proto.constructor = NewClass;
-
- NewClass.prototype = proto;
-
- //inherit parent's statics
- for (var i in this) {
- if (this.hasOwnProperty(i) && i !== 'prototype') {
- NewClass[i] = this[i];
- }
- }
-
- // mix static properties into the class
- if (props.statics) {
- L.extend(NewClass, props.statics);
- delete props.statics;
- }
-
- // mix includes into the prototype
- if (props.includes) {
- L.Util.extend.apply(null, [proto].concat(props.includes));
- delete props.includes;
- }
-
- // merge options
- if (props.options && proto.options) {
- props.options = L.extend({}, proto.options, props.options);
- }
-
- // mix given properties into the prototype
- L.extend(proto, props);
-
- proto._initHooks = [];
-
- var parent = this;
- // jshint camelcase: false
- NewClass.__super__ = parent.prototype;
-
- // add method for calling all hooks
- proto.callInitHooks = function () {
-
- if (this._initHooksCalled) { return; }
-
- if (parent.prototype.callInitHooks) {
- parent.prototype.callInitHooks.call(this);
- }
-
- this._initHooksCalled = true;
-
- for (var i = 0, len = proto._initHooks.length; i < len; i++) {
- proto._initHooks[i].call(this);
- }
- };
-
- return NewClass;
-};
-
-
-// method for adding properties to prototype
-L.Class.include = function (props) {
- L.extend(this.prototype, props);
-};
-
-// merge new default options to the Class
-L.Class.mergeOptions = function (options) {
- L.extend(this.prototype.options, options);
-};
-
-// add a constructor hook
-L.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);
-};
-
-
-/*
- * L.Mixin.Events is used to add custom events functionality to Leaflet classes.
- */
-
-var eventsKey = '_leaflet_events';
-
-L.Mixin = {};
-
-L.Mixin.Events = {
-
- addEventListener: function (types, fn, context) { // (String, Function[, Object]) or (Object[, Object])
-
- // types can be a map of types/handlers
- if (L.Util.invokeEach(types, this.addEventListener, this, fn, context)) { return this; }
-
- var events = this[eventsKey] = this[eventsKey] || {},
- contextId = context && context !== this && L.stamp(context),
- i, len, event, type, indexKey, indexLenKey, typeIndex;
-
- // types can be a string of space-separated words
- types = L.Util.splitWords(types);
-
- for (i = 0, len = types.length; i < len; i++) {
- event = {
- action: fn,
- context: context || this
- };
- type = types[i];
-
- if (contextId) {
- // store listeners of a particular context in a separate hash (if it has an id)
- // gives a major performance boost when removing thousands of map layers
-
- indexKey = type + '_idx';
- indexLenKey = indexKey + '_len';
-
- typeIndex = events[indexKey] = events[indexKey] || {};
-
- if (!typeIndex[contextId]) {
- typeIndex[contextId] = [];
-
- // keep track of the number of keys in the index to quickly check if it's empty
- events[indexLenKey] = (events[indexLenKey] || 0) + 1;
- }
-
- typeIndex[contextId].push(event);
-
-
- } else {
- events[type] = events[type] || [];
- events[type].push(event);
- }
- }
-
- return this;
- },
-
- hasEventListeners: function (type) { // (String) -> Boolean
- var events = this[eventsKey];
- return !!events && ((type in events && events[type].length > 0) ||
- (type + '_idx' in events && events[type + '_idx_len'] > 0));
- },
-
- removeEventListener: function (types, fn, context) { // ([String, Function, Object]) or (Object[, Object])
-
- if (!this[eventsKey]) {
- return this;
- }
-
- if (!types) {
- return this.clearAllEventListeners();
- }
-
- if (L.Util.invokeEach(types, this.removeEventListener, this, fn, context)) { return this; }
-
- var events = this[eventsKey],
- contextId = context && context !== this && L.stamp(context),
- i, len, type, listeners, j, indexKey, indexLenKey, typeIndex, removed;
-
- types = L.Util.splitWords(types);
-
- for (i = 0, len = types.length; i < len; i++) {
- type = types[i];
- indexKey = type + '_idx';
- indexLenKey = indexKey + '_len';
-
- typeIndex = events[indexKey];
-
- if (!fn) {
- // clear all listeners for a type if function isn't specified
- delete events[type];
- delete events[indexKey];
- delete events[indexLenKey];
-
- } else {
- listeners = contextId && typeIndex ? typeIndex[contextId] : events[type];
-
- if (listeners) {
- for (j = listeners.length - 1; j >= 0; j--) {
- if ((listeners[j].action === fn) && (!context || (listeners[j].context === context))) {
- removed = listeners.splice(j, 1);
- // set the old action to a no-op, because it is possible
- // that the listener is being iterated over as part of a dispatch
- removed[0].action = L.Util.falseFn;
- }
- }
-
- if (context && typeIndex && (listeners.length === 0)) {
- delete typeIndex[contextId];
- events[indexLenKey]--;
- }
- }
- }
- }
-
- return this;
- },
-
- clearAllEventListeners: function () {
- delete this[eventsKey];
- return this;
- },
-
- fireEvent: function (type, data) { // (String[, Object])
- if (!this.hasEventListeners(type)) {
- return this;
- }
-
- var event = L.Util.extend({}, data, { type: type, target: this });
-
- var events = this[eventsKey],
- listeners, i, len, typeIndex, contextId;
-
- if (events[type]) {
- // make sure adding/removing listeners inside other listeners won't cause infinite loop
- listeners = events[type].slice();
-
- for (i = 0, len = listeners.length; i < len; i++) {
- listeners[i].action.call(listeners[i].context, event);
- }
- }
-
- // fire event for the context-indexed listeners as well
- typeIndex = events[type + '_idx'];
-
- for (contextId in typeIndex) {
- listeners = typeIndex[contextId].slice();
-
- if (listeners) {
- for (i = 0, len = listeners.length; i < len; i++) {
- listeners[i].action.call(listeners[i].context, event);
- }
- }
- }
-
- return this;
- },
-
- addOneTimeEventListener: function (types, fn, context) {
-
- if (L.Util.invokeEach(types, this.addOneTimeEventListener, this, fn, context)) { return this; }
-
- var handler = L.bind(function () {
- this
- .removeEventListener(types, fn, context)
- .removeEventListener(types, handler, context);
- }, this);
-
- return this
- .addEventListener(types, fn, context)
- .addEventListener(types, handler, context);
- }
-};
-
-L.Mixin.Events.on = L.Mixin.Events.addEventListener;
-L.Mixin.Events.off = L.Mixin.Events.removeEventListener;
-L.Mixin.Events.once = L.Mixin.Events.addOneTimeEventListener;
-L.Mixin.Events.fire = L.Mixin.Events.fireEvent;
-
-
-/*
- * L.Browser handles different browser and feature detections for internal Leaflet use.
- */
-
-(function () {
-
- var ie = 'ActiveXObject' in window,
- ielt9 = ie && !document.addEventListener,
-
- // terrible browser detection to work around Safari / iOS / Android browser bugs
- ua = navigator.userAgent.toLowerCase(),
- webkit = ua.indexOf('webkit') !== -1,
- chrome = ua.indexOf('chrome') !== -1,
- phantomjs = ua.indexOf('phantom') !== -1,
- android = ua.indexOf('android') !== -1,
- android23 = ua.search('android [23]') !== -1,
- gecko = ua.indexOf('gecko') !== -1,
-
- mobile = typeof orientation !== undefined + '',
- msPointer = window.navigator && window.navigator.msPointerEnabled &&
- window.navigator.msMaxTouchPoints && !window.PointerEvent,
- pointer = (window.PointerEvent && window.navigator.pointerEnabled && window.navigator.maxTouchPoints) ||
- msPointer,
- retina = ('devicePixelRatio' in window && window.devicePixelRatio > 1) ||
- ('matchMedia' in window && window.matchMedia('(min-resolution:144dpi)') &&
- window.matchMedia('(min-resolution:144dpi)').matches),
-
- doc = document.documentElement,
- ie3d = ie && ('transition' in doc.style),
- webkit3d = ('WebKitCSSMatrix' in window) && ('m11' in new window.WebKitCSSMatrix()) && !android23,
- gecko3d = 'MozPerspective' in doc.style,
- opera3d = 'OTransition' in doc.style,
- any3d = !window.L_DISABLE_3D && (ie3d || webkit3d || gecko3d || opera3d) && !phantomjs;
-
-
- // PhantomJS has 'ontouchstart' in document.documentElement, but doesn't actually support touch.
- // https://github.com/Leaflet/Leaflet/pull/1434#issuecomment-13843151
-
- var touch = !window.L_NO_TOUCH && !phantomjs && (function () {
-
- var startName = 'ontouchstart';
-
- // IE10+ (We simulate these into touch* events in L.DomEvent and L.DomEvent.Pointer) or WebKit, etc.
- if (pointer || (startName in doc)) {
- return true;
- }
-
- // Firefox/Gecko
- var div = document.createElement('div'),
- supported = false;
-
- if (!div.setAttribute) {
- return false;
- }
- div.setAttribute(startName, 'return;');
-
- if (typeof div[startName] === 'function') {
- supported = true;
- }
-
- div.removeAttribute(startName);
- div = null;
-
- return supported;
- }());
-
-
- L.Browser = {
- ie: ie,
- ielt9: ielt9,
- webkit: webkit,
- gecko: gecko && !webkit && !window.opera && !ie,
-
- android: android,
- android23: android23,
-
- chrome: chrome,
-
- ie3d: ie3d,
- webkit3d: webkit3d,
- gecko3d: gecko3d,
- opera3d: opera3d,
- any3d: any3d,
-
- mobile: mobile,
- mobileWebkit: mobile && webkit,
- mobileWebkit3d: mobile && webkit3d,
- mobileOpera: mobile && window.opera,
-
- touch: touch,
- msPointer: msPointer,
- pointer: pointer,
-
- retina: retina
- };
-
-}());
-
-
-/*
- * L.Point represents a point with x and y coordinates.
- */
-
-L.Point = function (/*Number*/ x, /*Number*/ y, /*Boolean*/ round) {
- this.x = (round ? Math.round(x) : x);
- this.y = (round ? Math.round(y) : y);
-};
-
-L.Point.prototype = {
-
- clone: function () {
- return new L.Point(this.x, this.y);
- },
-
- // non-destructive, returns a new point
- add: function (point) {
- return this.clone()._add(L.point(point));
- },
-
- // destructive, used directly for performance in situations where it's safe to modify existing point
- _add: function (point) {
- this.x += point.x;
- this.y += point.y;
- return this;
- },
-
- subtract: function (point) {
- return this.clone()._subtract(L.point(point));
- },
-
- _subtract: function (point) {
- this.x -= point.x;
- this.y -= point.y;
- return this;
- },
-
- divideBy: function (num) {
- return this.clone()._divideBy(num);
- },
-
- _divideBy: function (num) {
- this.x /= num;
- this.y /= num;
- return this;
- },
-
- multiplyBy: function (num) {
- return this.clone()._multiplyBy(num);
- },
-
- _multiplyBy: function (num) {
- this.x *= num;
- this.y *= num;
- return this;
- },
-
- round: function () {
- return this.clone()._round();
- },
-
- _round: function () {
- this.x = Math.round(this.x);
- this.y = Math.round(this.y);
- return this;
- },
-
- floor: function () {
- return this.clone()._floor();
- },
-
- _floor: function () {
- this.x = Math.floor(this.x);
- this.y = Math.floor(this.y);
- return this;
- },
-
- distanceTo: function (point) {
- point = L.point(point);
-
- var x = point.x - this.x,
- y = point.y - this.y;
-
- return Math.sqrt(x * x + y * y);
- },
-
- equals: function (point) {
- point = L.point(point);
-
- return point.x === this.x &&
- point.y === this.y;
- },
-
- contains: function (point) {
- point = L.point(point);
-
- return Math.abs(point.x) <= Math.abs(this.x) &&
- Math.abs(point.y) <= Math.abs(this.y);
- },
-
- toString: function () {
- return 'Point(' +
- L.Util.formatNum(this.x) + ', ' +
- L.Util.formatNum(this.y) + ')';
- }
-};
-
-L.point = function (x, y, round) {
- if (x instanceof L.Point) {
- return x;
- }
- if (L.Util.isArray(x)) {
- return new L.Point(x[0], x[1]);
- }
- if (x === undefined || x === null) {
- return x;
- }
- return new L.Point(x, y, round);
-};
-
-
-/*
- * L.Bounds represents a rectangular area on the screen in pixel coordinates.
- */
-
-L.Bounds = function (a, b) { //(Point, Point) or Point[]
- if (!a) { return; }
-
- var points = b ? [a, b] : a;
-
- for (var i = 0, len = points.length; i < len; i++) {
- this.extend(points[i]);
- }
-};
-
-L.Bounds.prototype = {
- // extend the bounds to contain the given point
- extend: function (point) { // (Point)
- point = L.point(point);
-
- 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;
- },
-
- getCenter: function (round) { // (Boolean) -> Point
- return new L.Point(
- (this.min.x + this.max.x) / 2,
- (this.min.y + this.max.y) / 2, round);
- },
-
- getBottomLeft: function () { // -> Point
- return new L.Point(this.min.x, this.max.y);
- },
-
- getTopRight: function () { // -> Point
- return new L.Point(this.max.x, this.min.y);
- },
-
- getSize: function () {
- return this.max.subtract(this.min);
- },
-
- contains: function (obj) { // (Bounds) or (Point) -> Boolean
- var min, max;
-
- if (typeof obj[0] === 'number' || obj instanceof L.Point) {
- obj = L.point(obj);
- } else {
- obj = L.bounds(obj);
- }
-
- if (obj instanceof L.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);
- },
-
- intersects: function (bounds) { // (Bounds) -> Boolean
- bounds = L.bounds(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;
- },
-
- isValid: function () {
- return !!(this.min && this.max);
- }
-};
-
-L.bounds = function (a, b) { // (Bounds) or (Point, Point) or (Point[])
- if (!a || a instanceof L.Bounds) {
- return a;
- }
- return new L.Bounds(a, b);
-};
-
-
-/*
- * L.Transformation is an utility class to perform simple point transformations through a 2d-matrix.
- */
-
-L.Transformation = function (a, b, c, d) {
- this._a = a;
- this._b = b;
- this._c = c;
- this._d = d;
-};
-
-L.Transformation.prototype = {
- 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;
- },
-
- untransform: function (point, scale) {
- scale = scale || 1;
- return new L.Point(
- (point.x / scale - this._b) / this._a,
- (point.y / scale - this._d) / this._c);
- }
-};
-
-
-/*
- * L.DomUtil contains various utility functions for working with DOM.
- */
-
-L.DomUtil = {
- get: function (id) {
- return (typeof id === 'string' ? document.getElementById(id) : id);
- },
-
- getStyle: function (el, style) {
-
- var value = el.style[style];
-
- if (!value && el.currentStyle) {
- value = 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;
- },
-
- getViewportOffset: function (element) {
-
- var top = 0,
- left = 0,
- el = element,
- docBody = document.body,
- docEl = document.documentElement,
- pos;
-
- do {
- top += el.offsetTop || 0;
- left += el.offsetLeft || 0;
-
- //add borders
- top += parseInt(L.DomUtil.getStyle(el, 'borderTopWidth'), 10) || 0;
- left += parseInt(L.DomUtil.getStyle(el, 'borderLeftWidth'), 10) || 0;
-
- pos = L.DomUtil.getStyle(el, 'position');
-
- if (el.offsetParent === docBody && pos === 'absolute') { break; }
-
- if (pos === 'fixed') {
- top += docBody.scrollTop || docEl.scrollTop || 0;
- left += docBody.scrollLeft || docEl.scrollLeft || 0;
- break;
- }
-
- if (pos === 'relative' && !el.offsetLeft) {
- var width = L.DomUtil.getStyle(el, 'width'),
- maxWidth = L.DomUtil.getStyle(el, 'max-width'),
- r = el.getBoundingClientRect();
-
- if (width !== 'none' || maxWidth !== 'none') {
- left += r.left + el.clientLeft;
- }
-
- //calculate full y offset since we're breaking out of the loop
- top += r.top + (docBody.scrollTop || docEl.scrollTop || 0);
-
- break;
- }
-
- el = el.offsetParent;
-
- } while (el);
-
- el = element;
-
- do {
- if (el === docBody) { break; }
-
- top -= el.scrollTop || 0;
- left -= el.scrollLeft || 0;
-
- el = el.parentNode;
- } while (el);
-
- return new L.Point(left, top);
- },
-
- documentIsLtr: function () {
- if (!L.DomUtil._docIsLtrCached) {
- L.DomUtil._docIsLtrCached = true;
- L.DomUtil._docIsLtr = L.DomUtil.getStyle(document.body, 'direction') === 'ltr';
- }
- return L.DomUtil._docIsLtr;
- },
-
- create: function (tagName, className, container) {
-
- var el = document.createElement(tagName);
- el.className = className;
-
- if (container) {
- container.appendChild(el);
- }
-
- return el;
- },
-
- hasClass: function (el, name) {
- if (el.classList !== undefined) {
- return el.classList.contains(name);
- }
- var className = L.DomUtil._getClass(el);
- return className.length > 0 && new RegExp('(^|\\s)' + name + '(\\s|$)').test(className);
- },
-
- addClass: function (el, name) {
- if (el.classList !== undefined) {
- var classes = L.Util.splitWords(name);
- for (var i = 0, len = classes.length; i < len; i++) {
- el.classList.add(classes[i]);
- }
- } else if (!L.DomUtil.hasClass(el, name)) {
- var className = L.DomUtil._getClass(el);
- L.DomUtil._setClass(el, (className ? className + ' ' : '') + name);
- }
- },
-
- removeClass: function (el, name) {
- if (el.classList !== undefined) {
- el.classList.remove(name);
- } else {
- L.DomUtil._setClass(el, L.Util.trim((' ' + L.DomUtil._getClass(el) + ' ').replace(' ' + name + ' ', ' ')));
- }
- },
-
- _setClass: function (el, name) {
- if (el.className.baseVal === undefined) {
- el.className = name;
- } else {
- // in case of SVG element
- el.className.baseVal = name;
- }
- },
-
- _getClass: function (el) {
- return el.className.baseVal === undefined ? el.className : el.className.baseVal;
- },
-
- setOpacity: function (el, value) {
-
- if ('opacity' in el.style) {
- el.style.opacity = value;
-
- } else if ('filter' in el.style) {
-
- 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 + ')';
- }
- }
- },
-
- testProp: function (props) {
-
- var style = document.documentElement.style;
-
- for (var i = 0; i < props.length; i++) {
- if (props[i] in style) {
- return props[i];
- }
- }
- return false;
- },
-
- getTranslateString: function (point) {
- // on WebKit browsers (Chrome/Safari/iOS Safari/Android) using translate3d instead of translate
- // makes animation smoother as it ensures HW accel is used. Firefox 13 doesn't care
- // (same speed either way), Opera 12 doesn't support translate3d
-
- var is3d = L.Browser.webkit3d,
- open = 'translate' + (is3d ? '3d' : '') + '(',
- close = (is3d ? ',0' : '') + ')';
-
- return open + point.x + 'px,' + point.y + 'px' + close;
- },
-
- getScaleString: function (scale, origin) {
-
- var preTranslateStr = L.DomUtil.getTranslateString(origin.add(origin.multiplyBy(-1 * scale))),
- scaleStr = ' scale(' + scale + ') ';
-
- return preTranslateStr + scaleStr;
- },
-
- setPosition: function (el, point, disable3D) { // (HTMLElement, Point[, Boolean])
-
- // jshint camelcase: false
- el._leaflet_pos = point;
-
- if (!disable3D && L.Browser.any3d) {
- el.style[L.DomUtil.TRANSFORM] = L.DomUtil.getTranslateString(point);
- } else {
- el.style.left = point.x + 'px';
- el.style.top = point.y + 'px';
- }
- },
-
- getPosition: function (el) {
- // this method is only used for elements previously positioned using setPosition,
- // so it's safe to cache the position for performance
-
- // jshint camelcase: false
- return el._leaflet_pos;
- }
-};
-
-
-// prefix style property names
-
-L.DomUtil.TRANSFORM = L.DomUtil.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
-
-L.DomUtil.TRANSITION = L.DomUtil.testProp(
- ['webkitTransition', 'transition', 'OTransition', 'MozTransition', 'msTransition']);
-
-L.DomUtil.TRANSITION_END =
- L.DomUtil.TRANSITION === 'webkitTransition' || L.DomUtil.TRANSITION === 'OTransition' ?
- L.DomUtil.TRANSITION + 'End' : 'transitionend';
-
-(function () {
- if ('onselectstart' in document) {
- L.extend(L.DomUtil, {
- disableTextSelection: function () {
- L.DomEvent.on(window, 'selectstart', L.DomEvent.preventDefault);
- },
-
- enableTextSelection: function () {
- L.DomEvent.off(window, 'selectstart', L.DomEvent.preventDefault);
- }
- });
- } else {
- var userSelectProperty = L.DomUtil.testProp(
- ['userSelect', 'WebkitUserSelect', 'OUserSelect', 'MozUserSelect', 'msUserSelect']);
-
- L.extend(L.DomUtil, {
- disableTextSelection: function () {
- if (userSelectProperty) {
- var style = document.documentElement.style;
- this._userSelect = style[userSelectProperty];
- style[userSelectProperty] = 'none';
- }
- },
-
- enableTextSelection: function () {
- if (userSelectProperty) {
- document.documentElement.style[userSelectProperty] = this._userSelect;
- delete this._userSelect;
- }
- }
- });
- }
-
- L.extend(L.DomUtil, {
- disableImageDrag: function () {
- L.DomEvent.on(window, 'dragstart', L.DomEvent.preventDefault);
- },
-
- enableImageDrag: function () {
- L.DomEvent.off(window, 'dragstart', L.DomEvent.preventDefault);
- }
- });
-})();
-
-
-/*
- * L.LatLng represents a geographical point with latitude and longitude coordinates.
- */
-
-L.LatLng = function (lat, lng, alt) { // (Number, Number, Number)
- lat = parseFloat(lat);
- lng = parseFloat(lng);
-
- if (isNaN(lat) || isNaN(lng)) {
- throw new Error('Invalid LatLng object: (' + lat + ', ' + lng + ')');
- }
-
- this.lat = lat;
- this.lng = lng;
-
- if (alt !== undefined) {
- this.alt = parseFloat(alt);
- }
-};
-
-L.extend(L.LatLng, {
- DEG_TO_RAD: Math.PI / 180,
- RAD_TO_DEG: 180 / Math.PI,
- MAX_MARGIN: 1.0E-9 // max margin of error for the "equals" check
-});
-
-L.LatLng.prototype = {
- equals: function (obj) { // (LatLng) -> Boolean
- if (!obj) { return false; }
-
- obj = L.latLng(obj);
-
- var margin = Math.max(
- Math.abs(this.lat - obj.lat),
- Math.abs(this.lng - obj.lng));
-
- return margin <= L.LatLng.MAX_MARGIN;
- },
-
- toString: function (precision) { // (Number) -> String
- return 'LatLng(' +
- L.Util.formatNum(this.lat, precision) + ', ' +
- L.Util.formatNum(this.lng, precision) + ')';
- },
-
- // Haversine distance formula, see http://en.wikipedia.org/wiki/Haversine_formula
- // TODO move to projection code, LatLng shouldn't know about Earth
- distanceTo: function (other) { // (LatLng) -> Number
- other = L.latLng(other);
-
- var R = 6378137, // earth radius in meters
- d2r = L.LatLng.DEG_TO_RAD,
- dLat = (other.lat - this.lat) * d2r,
- dLon = (other.lng - this.lng) * d2r,
- lat1 = this.lat * d2r,
- lat2 = other.lat * d2r,
- sin1 = Math.sin(dLat / 2),
- sin2 = Math.sin(dLon / 2);
-
- var a = sin1 * sin1 + sin2 * sin2 * Math.cos(lat1) * Math.cos(lat2);
-
- return R * 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
- },
-
- wrap: function (a, b) { // (Number, Number) -> LatLng
- var lng = this.lng;
-
- a = a || -180;
- b = b || 180;
-
- lng = (lng + b) % (b - a) + (lng < a || lng === b ? b : a);
-
- return new L.LatLng(this.lat, lng);
- }
-};
-
-L.latLng = function (a, b) { // (LatLng) or ([Number, Number]) or (Number, Number)
- if (a instanceof L.LatLng) {
- return a;
- }
- if (L.Util.isArray(a)) {
- if (typeof a[0] === 'number' || typeof a[0] === 'string') {
- return new L.LatLng(a[0], a[1], a[2]);
- } else {
- return null;
- }
- }
- if (a === undefined || a === null) {
- return a;
- }
- if (typeof a === 'object' && 'lat' in a) {
- return new L.LatLng(a.lat, 'lng' in a ? a.lng : a.lon);
- }
- if (b === undefined) {
- return null;
- }
- return new L.LatLng(a, b);
-};
-
-
-
-/*
- * L.LatLngBounds represents a rectangular area on the map in geographical coordinates.
- */
-
-L.LatLngBounds = function (southWest, northEast) { // (LatLng, LatLng) or (LatLng[])
- if (!southWest) { return; }
-
- var latlngs = northEast ? [southWest, northEast] : southWest;
-
- for (var i = 0, len = latlngs.length; i < len; i++) {
- this.extend(latlngs[i]);
- }
-};
-
-L.LatLngBounds.prototype = {
- // extend the bounds to contain the given point or bounds
- extend: function (obj) { // (LatLng) or (LatLngBounds)
- if (!obj) { return this; }
-
- var latLng = L.latLng(obj);
- if (latLng !== null) {
- obj = latLng;
- } else {
- obj = L.latLngBounds(obj);
- }
-
- if (obj instanceof L.LatLng) {
- if (!this._southWest && !this._northEast) {
- this._southWest = new L.LatLng(obj.lat, obj.lng);
- this._northEast = new L.LatLng(obj.lat, obj.lng);
- } else {
- this._southWest.lat = Math.min(obj.lat, this._southWest.lat);
- this._southWest.lng = Math.min(obj.lng, this._southWest.lng);
-
- this._northEast.lat = Math.max(obj.lat, this._northEast.lat);
- this._northEast.lng = Math.max(obj.lng, this._northEast.lng);
- }
- } else if (obj instanceof L.LatLngBounds) {
- this.extend(obj._southWest);
- this.extend(obj._northEast);
- }
- return this;
- },
-
- // extend the bounds by a percentage
- pad: function (bufferRatio) { // (Number) -> LatLngBounds
- 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 L.LatLngBounds(
- new L.LatLng(sw.lat - heightBuffer, sw.lng - widthBuffer),
- new L.LatLng(ne.lat + heightBuffer, ne.lng + widthBuffer));
- },
-
- getCenter: function () { // -> LatLng
- return new L.LatLng(
- (this._southWest.lat + this._northEast.lat) / 2,
- (this._southWest.lng + this._northEast.lng) / 2);
- },
-
- getSouthWest: function () {
- return this._southWest;
- },
-
- getNorthEast: function () {
- return this._northEast;
- },
-
- getNorthWest: function () {
- return new L.LatLng(this.getNorth(), this.getWest());
- },
-
- getSouthEast: function () {
- return new L.LatLng(this.getSouth(), this.getEast());
- },
-
- getWest: function () {
- return this._southWest.lng;
- },
-
- getSouth: function () {
- return this._southWest.lat;
- },
-
- getEast: function () {
- return this._northEast.lng;
- },
-
- getNorth: function () {
- return this._northEast.lat;
- },
-
- contains: function (obj) { // (LatLngBounds) or (LatLng) -> Boolean
- if (typeof obj[0] === 'number' || obj instanceof L.LatLng) {
- obj = L.latLng(obj);
- } else {
- obj = L.latLngBounds(obj);
- }
-
- var sw = this._southWest,
- ne = this._northEast,
- sw2, ne2;
-
- if (obj instanceof L.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);
- },
-
- intersects: function (bounds) { // (LatLngBounds)
- bounds = L.latLngBounds(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;
- },
-
- toBBoxString: function () {
- return [this.getWest(), this.getSouth(), this.getEast(), this.getNorth()].join(',');
- },
-
- equals: function (bounds) { // (LatLngBounds)
- if (!bounds) { return false; }
-
- bounds = L.latLngBounds(bounds);
-
- return this._southWest.equals(bounds.getSouthWest()) &&
- this._northEast.equals(bounds.getNorthEast());
- },
-
- isValid: function () {
- return !!(this._southWest && this._northEast);
- }
-};
-
-//TODO International date line?
-
-L.latLngBounds = function (a, b) { // (LatLngBounds) or (LatLng, LatLng)
- if (!a || a instanceof L.LatLngBounds) {
- return a;
- }
- return new L.LatLngBounds(a, b);
-};
-
-
-/*
- * L.Projection contains various geographical projections used by CRS classes.
- */
-
-L.Projection = {};
-
-
-/*
- * Spherical Mercator is the most popular map projection, used by EPSG:3857 CRS used by default.
- */
-
-L.Projection.SphericalMercator = {
- MAX_LATITUDE: 85.0511287798,
-
- project: function (latlng) { // (LatLng) -> Point
- var d = L.LatLng.DEG_TO_RAD,
- max = this.MAX_LATITUDE,
- lat = Math.max(Math.min(max, latlng.lat), -max),
- x = latlng.lng * d,
- y = lat * d;
-
- y = Math.log(Math.tan((Math.PI / 4) + (y / 2)));
-
- return new L.Point(x, y);
- },
-
- unproject: function (point) { // (Point, Boolean) -> LatLng
- var d = L.LatLng.RAD_TO_DEG,
- lng = point.x * d,
- lat = (2 * Math.atan(Math.exp(point.y)) - (Math.PI / 2)) * d;
-
- return new L.LatLng(lat, lng);
- }
-};
-
-
-/*
- * Simple equirectangular (Plate Carree) projection, used by CRS like EPSG:4326 and Simple.
- */
-
-L.Projection.LonLat = {
- project: function (latlng) {
- return new L.Point(latlng.lng, latlng.lat);
- },
-
- unproject: function (point) {
- return new L.LatLng(point.y, point.x);
- }
-};
-
-
-/*
- * L.CRS is a base object for all defined CRS (Coordinate Reference Systems) in Leaflet.
- */
-
-L.CRS = {
- latLngToPoint: function (latlng, zoom) { // (LatLng, Number) -> Point
- var projectedPoint = this.projection.project(latlng),
- scale = this.scale(zoom);
-
- return this.transformation._transform(projectedPoint, scale);
- },
-
- pointToLatLng: function (point, zoom) { // (Point, Number[, Boolean]) -> LatLng
- var scale = this.scale(zoom),
- untransformedPoint = this.transformation.untransform(point, scale);
-
- return this.projection.unproject(untransformedPoint);
- },
-
- project: function (latlng) {
- return this.projection.project(latlng);
- },
-
- scale: function (zoom) {
- return 256 * Math.pow(2, zoom);
- },
-
- getSize: function (zoom) {
- var s = this.scale(zoom);
- return L.point(s, s);
- }
-};
-
-
-/*
- * A simple CRS that can be used for flat non-Earth maps like panoramas or game maps.
- */
-
-L.CRS.Simple = L.extend({}, L.CRS, {
- projection: L.Projection.LonLat,
- transformation: new L.Transformation(1, 0, -1, 0),
-
- scale: function (zoom) {
- return Math.pow(2, zoom);
- }
-});
-
-
-/*
- * L.CRS.EPSG3857 (Spherical Mercator) is the most common CRS for web mapping
- * and is used by Leaflet by default.
- */
-
-L.CRS.EPSG3857 = L.extend({}, L.CRS, {
- code: 'EPSG:3857',
-
- projection: L.Projection.SphericalMercator,
- transformation: new L.Transformation(0.5 / Math.PI, 0.5, -0.5 / Math.PI, 0.5),
-
- project: function (latlng) { // (LatLng) -> Point
- var projectedPoint = this.projection.project(latlng),
- earthRadius = 6378137;
- return projectedPoint.multiplyBy(earthRadius);
- }
-});
-
-L.CRS.EPSG900913 = L.extend({}, L.CRS.EPSG3857, {
- code: 'EPSG:900913'
-});
-
-
-/*
- * L.CRS.EPSG4326 is a CRS popular among advanced GIS specialists.
- */
-
-L.CRS.EPSG4326 = L.extend({}, L.CRS, {
- code: 'EPSG:4326',
-
- projection: L.Projection.LonLat,
- transformation: new L.Transformation(1 / 360, 0.5, -1 / 360, 0.5)
-});
-
-
-/*
- * L.Map is the central class of the API - it is used to create a map.
- */
-
-L.Map = L.Class.extend({
-
- includes: L.Mixin.Events,
-
- options: {
- crs: L.CRS.EPSG3857,
-
- /*
- center: LatLng,
- zoom: Number,
- layers: Array,
- */
-
- fadeAnimation: L.DomUtil.TRANSITION && !L.Browser.android23,
- trackResize: true,
- markerZoomAnimation: L.DomUtil.TRANSITION && L.Browser.any3d
- },
-
- initialize: function (id, options) { // (HTMLElement or String, Object)
- options = L.setOptions(this, options);
-
-
- this._initContainer(id);
- this._initLayout();
-
- // hack for https://github.com/Leaflet/Leaflet/issues/1980
- this._onResize = L.bind(this._onResize, this);
-
- this._initEvents();
-
- if (options.maxBounds) {
- this.setMaxBounds(options.maxBounds);
- }
-
- if (options.center && options.zoom !== undefined) {
- this.setView(L.latLng(options.center), options.zoom, {reset: true});
- }
-
- this._handlers = [];
-
- this._layers = {};
- this._zoomBoundLayers = {};
- this._tileLayersNum = 0;
-
- this.callInitHooks();
-
- this._addLayers(options.layers);
- },
-
-
- // public methods that modify map state
-
- // replaced by animation-powered implementation in Map.PanAnimation.js
- setView: function (center, zoom) {
- zoom = zoom === undefined ? this.getZoom() : zoom;
- this._resetView(L.latLng(center), this._limitZoom(zoom));
- return this;
- },
-
- setZoom: function (zoom, options) {
- if (!this._loaded) {
- this._zoom = this._limitZoom(zoom);
- return this;
- }
- return this.setView(this.getCenter(), zoom, {zoom: options});
- },
-
- zoomIn: function (delta, options) {
- return this.setZoom(this._zoom + (delta || 1), options);
- },
-
- zoomOut: function (delta, options) {
- return this.setZoom(this._zoom - (delta || 1), options);
- },
-
- setZoomAround: function (latlng, zoom, options) {
- var scale = this.getZoomScale(zoom),
- viewHalf = this.getSize().divideBy(2),
- containerPoint = latlng instanceof L.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});
- },
-
- fitBounds: function (bounds, options) {
-
- options = options || {};
- bounds = bounds.getBounds ? bounds.getBounds() : L.latLngBounds(bounds);
-
- var paddingTL = L.point(options.paddingTopLeft || options.padding || [0, 0]),
- paddingBR = L.point(options.paddingBottomRight || options.padding || [0, 0]),
-
- zoom = this.getBoundsZoom(bounds, false, paddingTL.add(paddingBR)),
- 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);
-
- zoom = options && options.maxZoom ? Math.min(options.maxZoom, zoom) : zoom;
-
- return this.setView(center, zoom, options);
- },
-
- fitWorld: function (options) {
- return this.fitBounds([[-90, -180], [90, 180]], options);
- },
-
- panTo: function (center, options) { // (LatLng)
- return this.setView(center, this._zoom, {pan: options});
- },
-
- panBy: function (offset) { // (Point)
- // replaced with animated panBy in Map.PanAnimation.js
- this.fire('movestart');
-
- this._rawPanBy(L.point(offset));
-
- this.fire('move');
- return this.fire('moveend');
- },
-
- setMaxBounds: function (bounds) {
- bounds = L.latLngBounds(bounds);
-
- this.options.maxBounds = bounds;
-
- if (!bounds) {
- return this.off('moveend', this._panInsideMaxBounds, this);
- }
-
- if (this._loaded) {
- this._panInsideMaxBounds();
- }
-
- return this.on('moveend', this._panInsideMaxBounds, this);
- },
-
- panInsideBounds: function (bounds, options) {
- var center = this.getCenter(),
- newCenter = this._limitCenter(center, this._zoom, bounds);
-
- if (center.equals(newCenter)) { return this; }
-
- return this.panTo(newCenter, options);
- },
-
- addLayer: function (layer) {
- // TODO method is too big, refactor
-
- var id = L.stamp(layer);
-
- if (this._layers[id]) { return this; }
-
- this._layers[id] = layer;
-
- // TODO getMaxZoom, getMinZoom in ILayer (instead of options)
- if (layer.options && (!isNaN(layer.options.maxZoom) || !isNaN(layer.options.minZoom))) {
- this._zoomBoundLayers[id] = layer;
- this._updateZoomLevels();
- }
-
- // TODO looks ugly, refactor!!!
- if (this.options.zoomAnimation && L.TileLayer && (layer instanceof L.TileLayer)) {
- this._tileLayersNum++;
- this._tileLayersToLoad++;
- layer.on('load', this._onTileLayerLoad, this);
- }
-
- if (this._loaded) {
- this._layerAdd(layer);
- }
-
- return this;
- },
-
- removeLayer: function (layer) {
- var id = L.stamp(layer);
-
- if (!this._layers[id]) { return this; }
-
- if (this._loaded) {
- layer.onRemove(this);
- }
-
- delete this._layers[id];
-
- if (this._loaded) {
- this.fire('layerremove', {layer: layer});
- }
-
- if (this._zoomBoundLayers[id]) {
- delete this._zoomBoundLayers[id];
- this._updateZoomLevels();
- }
-
- // TODO looks ugly, refactor
- if (this.options.zoomAnimation && L.TileLayer && (layer instanceof L.TileLayer)) {
- this._tileLayersNum--;
- this._tileLayersToLoad--;
- layer.off('load', this._onTileLayerLoad, this);
- }
-
- return this;
- },
-
- hasLayer: function (layer) {
- if (!layer) { return false; }
-
- return (L.stamp(layer) in this._layers);
- },
-
- eachLayer: function (method, context) {
- for (var i in this._layers) {
- method.call(context, this._layers[i]);
- }
- return this;
- },
-
- invalidateSize: function (options) {
- if (!this._loaded) { return this; }
-
- options = L.extend({
- animate: false,
- pan: true
- }, options === true ? {animate: true} : options);
-
- var oldSize = this.getSize();
- this._sizeChanged = true;
- this._initialCenter = 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(L.bind(this.fire, this, 'moveend'), 200);
- } else {
- this.fire('moveend');
- }
- }
-
- return this.fire('resize', {
- oldSize: oldSize,
- newSize: newSize
- });
- },
-
- // TODO handler.addTo
- 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;
- },
-
- remove: function () {
- if (this._loaded) {
- this.fire('unload');
- }
-
- this._initEvents('off');
-
- try {
- // throws error in IE6-8
- delete this._container._leaflet;
- } catch (e) {
- this._container._leaflet = undefined;
- }
-
- this._clearPanes();
- if (this._clearControlPos) {
- this._clearControlPos();
- }
-
- this._clearHandlers();
-
- return this;
- },
-
-
- // public methods for getting map state
-
- getCenter: function () { // (Boolean) -> LatLng
- this._checkIfLoaded();
-
- if (this._initialCenter && !this._moved()) {
- return this._initialCenter;
- }
- return this.layerPointToLatLng(this._getCenterLayerPoint());
- },
-
- getZoom: function () {
- return this._zoom;
- },
-
- getBounds: function () {
- var bounds = this.getPixelBounds(),
- sw = this.unproject(bounds.getBottomLeft()),
- ne = this.unproject(bounds.getTopRight());
-
- return new L.LatLngBounds(sw, ne);
- },
-
- getMinZoom: function () {
- return this.options.minZoom === undefined ?
- (this._layersMinZoom === undefined ? 0 : this._layersMinZoom) :
- this.options.minZoom;
- },
-
- getMaxZoom: function () {
- return this.options.maxZoom === undefined ?
- (this._layersMaxZoom === undefined ? Infinity : this._layersMaxZoom) :
- this.options.maxZoom;
- },
-
- getBoundsZoom: function (bounds, inside, padding) { // (LatLngBounds[, Boolean, Point]) -> Number
- bounds = L.latLngBounds(bounds);
-
- var zoom = this.getMinZoom() - (inside ? 1 : 0),
- maxZoom = this.getMaxZoom(),
- size = this.getSize(),
-
- nw = bounds.getNorthWest(),
- se = bounds.getSouthEast(),
-
- zoomNotFound = true,
- boundsSize;
-
- padding = L.point(padding || [0, 0]);
-
- do {
- zoom++;
- boundsSize = this.project(se, zoom).subtract(this.project(nw, zoom)).add(padding);
- zoomNotFound = !inside ? size.contains(boundsSize) : boundsSize.x < size.x || boundsSize.y < size.y;
-
- } while (zoomNotFound && zoom <= maxZoom);
-
- if (zoomNotFound && inside) {
- return null;
- }
-
- return inside ? zoom : zoom - 1;
- },
-
- getSize: function () {
- if (!this._size || this._sizeChanged) {
- this._size = new L.Point(
- this._container.clientWidth,
- this._container.clientHeight);
-
- this._sizeChanged = false;
- }
- return this._size.clone();
- },
-
- getPixelBounds: function () {
- var topLeftPoint = this._getTopLeftPoint();
- return new L.Bounds(topLeftPoint, topLeftPoint.add(this.getSize()));
- },
-
- getPixelOrigin: function () {
- this._checkIfLoaded();
- return this._initialTopLeftPoint;
- },
-
- getPanes: function () {
- return this._panes;
- },
-
- getContainer: function () {
- return this._container;
- },
-
-
- // TODO replace with universal implementation after refactoring projections
-
- getZoomScale: function (toZoom) {
- var crs = this.options.crs;
- return crs.scale(toZoom) / crs.scale(this._zoom);
- },
-
- getScaleZoom: function (scale) {
- return this._zoom + (Math.log(scale) / Math.LN2);
- },
-
-
- // conversion methods
-
- project: function (latlng, zoom) { // (LatLng[, Number]) -> Point
- zoom = zoom === undefined ? this._zoom : zoom;
- return this.options.crs.latLngToPoint(L.latLng(latlng), zoom);
- },
-
- unproject: function (point, zoom) { // (Point[, Number]) -> LatLng
- zoom = zoom === undefined ? this._zoom : zoom;
- return this.options.crs.pointToLatLng(L.point(point), zoom);
- },
-
- layerPointToLatLng: function (point) { // (Point)
- var projectedPoint = L.point(point).add(this.getPixelOrigin());
- return this.unproject(projectedPoint);
- },
-
- latLngToLayerPoint: function (latlng) { // (LatLng)
- var projectedPoint = this.project(L.latLng(latlng))._round();
- return projectedPoint._subtract(this.getPixelOrigin());
- },
-
- containerPointToLayerPoint: function (point) { // (Point)
- return L.point(point).subtract(this._getMapPanePos());
- },
-
- layerPointToContainerPoint: function (point) { // (Point)
- return L.point(point).add(this._getMapPanePos());
- },
-
- containerPointToLatLng: function (point) {
- var layerPoint = this.containerPointToLayerPoint(L.point(point));
- return this.layerPointToLatLng(layerPoint);
- },
-
- latLngToContainerPoint: function (latlng) {
- return this.layerPointToContainerPoint(this.latLngToLayerPoint(L.latLng(latlng)));
- },
-
- mouseEventToContainerPoint: function (e) { // (MouseEvent)
- return L.DomEvent.getMousePosition(e, this._container);
- },
-
- mouseEventToLayerPoint: function (e) { // (MouseEvent)
- return this.containerPointToLayerPoint(this.mouseEventToContainerPoint(e));
- },
-
- mouseEventToLatLng: function (e) { // (MouseEvent)
- return this.layerPointToLatLng(this.mouseEventToLayerPoint(e));
- },
-
-
- // map initialization methods
-
- _initContainer: function (id) {
- var container = this._container = L.DomUtil.get(id);
-
- if (!container) {
- throw new Error('Map container not found.');
- } else if (container._leaflet) {
- throw new Error('Map container is already initialized.');
- }
-
- container._leaflet = true;
- },
-
- _initLayout: function () {
- var container = this._container;
-
- L.DomUtil.addClass(container, 'leaflet-container' +
- (L.Browser.touch ? ' leaflet-touch' : '') +
- (L.Browser.retina ? ' leaflet-retina' : '') +
- (L.Browser.ielt9 ? ' leaflet-oldie' : '') +
- (this.options.fadeAnimation ? ' leaflet-fade-anim' : ''));
-
- var position = L.DomUtil.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._mapPane = panes.mapPane = this._createPane('leaflet-map-pane', this._container);
-
- this._tilePane = panes.tilePane = this._createPane('leaflet-tile-pane', this._mapPane);
- panes.objectsPane = this._createPane('leaflet-objects-pane', this._mapPane);
- panes.shadowPane = this._createPane('leaflet-shadow-pane');
- panes.overlayPane = this._createPane('leaflet-overlay-pane');
- panes.markerPane = this._createPane('leaflet-marker-pane');
- panes.popupPane = this._createPane('leaflet-popup-pane');
-
- var zoomHide = ' leaflet-zoom-hide';
-
- if (!this.options.markerZoomAnimation) {
- L.DomUtil.addClass(panes.markerPane, zoomHide);
- L.DomUtil.addClass(panes.shadowPane, zoomHide);
- L.DomUtil.addClass(panes.popupPane, zoomHide);
- }
- },
-
- _createPane: function (className, container) {
- return L.DomUtil.create('div', className, container || this._panes.objectsPane);
- },
-
- _clearPanes: function () {
- this._container.removeChild(this._mapPane);
- },
-
- _addLayers: function (layers) {
- layers = layers ? (L.Util.isArray(layers) ? layers : [layers]) : [];
-
- for (var i = 0, len = layers.length; i < len; i++) {
- this.addLayer(layers[i]);
- }
- },
-
-
- // private methods that modify map state
-
- _resetView: function (center, zoom, preserveMapOffset, afterZoomAnim) {
-
- var zoomChanged = (this._zoom !== zoom);
-
- if (!afterZoomAnim) {
- this.fire('movestart');
-
- if (zoomChanged) {
- this.fire('zoomstart');
- }
- }
-
- this._zoom = zoom;
- this._initialCenter = center;
-
- this._initialTopLeftPoint = this._getNewTopLeftPoint(center);
-
- if (!preserveMapOffset) {
- L.DomUtil.setPosition(this._mapPane, new L.Point(0, 0));
- } else {
- this._initialTopLeftPoint._add(this._getMapPanePos());
- }
-
- this._tileLayersToLoad = this._tileLayersNum;
-
- var loading = !this._loaded;
- this._loaded = true;
-
- this.fire('viewreset', {hard: !preserveMapOffset});
-
- if (loading) {
- this.fire('load');
- this.eachLayer(this._layerAdd, this);
- }
-
- this.fire('move');
-
- if (zoomChanged || afterZoomAnim) {
- this.fire('zoomend');
- }
-
- this.fire('moveend', {hard: !preserveMapOffset});
- },
-
- _rawPanBy: function (offset) {
- L.DomUtil.setPosition(this._mapPane, this._getMapPanePos().subtract(offset));
- },
-
- _getZoomSpan: function () {
- return this.getMaxZoom() - this.getMinZoom();
- },
-
- _updateZoomLevels: function () {
- var i,
- minZoom = Infinity,
- maxZoom = -Infinity,
- oldZoomSpan = this._getZoomSpan();
-
- for (i in this._zoomBoundLayers) {
- var layer = this._zoomBoundLayers[i];
- if (!isNaN(layer.options.minZoom)) {
- minZoom = Math.min(minZoom, layer.options.minZoom);
- }
- if (!isNaN(layer.options.maxZoom)) {
- maxZoom = Math.max(maxZoom, layer.options.maxZoom);
- }
- }
-
- if (i === undefined) { // we have no tilelayers
- this._layersMaxZoom = this._layersMinZoom = undefined;
- } else {
- this._layersMaxZoom = maxZoom;
- this._layersMinZoom = minZoom;
- }
-
- if (oldZoomSpan !== this._getZoomSpan()) {
- this.fire('zoomlevelschange');
- }
- },
-
- _panInsideMaxBounds: function () {
- this.panInsideBounds(this.options.maxBounds);
- },
-
- _checkIfLoaded: function () {
- if (!this._loaded) {
- throw new Error('Set map center and zoom first.');
- }
- },
-
- // map events
-
- _initEvents: function (onOff) {
- if (!L.DomEvent) { return; }
-
- onOff = onOff || 'on';
-
- L.DomEvent[onOff](this._container, 'click', this._onMouseClick, this);
-
- var events = ['dblclick', 'mousedown', 'mouseup', 'mouseenter',
- 'mouseleave', 'mousemove', 'contextmenu'],
- i, len;
-
- for (i = 0, len = events.length; i < len; i++) {
- L.DomEvent[onOff](this._container, events[i], this._fireMouseEvent, this);
- }
-
- if (this.options.trackResize) {
- L.DomEvent[onOff](window, 'resize', this._onResize, this);
- }
- },
-
- _onResize: function () {
- L.Util.cancelAnimFrame(this._resizeRequest);
- this._resizeRequest = L.Util.requestAnimFrame(
- function () { this.invalidateSize({debounceMoveend: true}); }, this, false, this._container);
- },
-
- _onMouseClick: function (e) {
- if (!this._loaded || (!e._simulated &&
- ((this.dragging && this.dragging.moved()) ||
- (this.boxZoom && this.boxZoom.moved()))) ||
- L.DomEvent._skipped(e)) { return; }
-
- this.fire('preclick');
- this._fireMouseEvent(e);
- },
-
- _fireMouseEvent: function (e) {
- if (!this._loaded || L.DomEvent._skipped(e)) { return; }
-
- var type = e.type;
-
- type = (type === 'mouseenter' ? 'mouseover' : (type === 'mouseleave' ? 'mouseout' : type));
-
- if (!this.hasEventListeners(type)) { return; }
-
- if (type === 'contextmenu') {
- L.DomEvent.preventDefault(e);
- }
-
- var containerPoint = this.mouseEventToContainerPoint(e),
- layerPoint = this.containerPointToLayerPoint(containerPoint),
- latlng = this.layerPointToLatLng(layerPoint);
-
- this.fire(type, {
- latlng: latlng,
- layerPoint: layerPoint,
- containerPoint: containerPoint,
- originalEvent: e
- });
- },
-
- _onTileLayerLoad: function () {
- this._tileLayersToLoad--;
- if (this._tileLayersNum && !this._tileLayersToLoad) {
- this.fire('tilelayersload');
- }
- },
-
- _clearHandlers: function () {
- for (var i = 0, len = this._handlers.length; i < len; i++) {
- this._handlers[i].disable();
- }
- },
-
- whenReady: function (callback, context) {
- if (this._loaded) {
- callback.call(context || this, this);
- } else {
- this.on('load', callback, context);
- }
- return this;
- },
-
- _layerAdd: function (layer) {
- layer.onAdd(this);
- this.fire('layeradd', {layer: layer});
- },
-
-
- // private methods for getting map state
-
- _getMapPanePos: function () {
- return L.DomUtil.getPosition(this._mapPane);
- },
-
- _moved: function () {
- var pos = this._getMapPanePos();
- return pos && !pos.equals([0, 0]);
- },
-
- _getTopLeftPoint: function () {
- return this.getPixelOrigin().subtract(this._getMapPanePos());
- },
-
- _getNewTopLeftPoint: function (center, zoom) {
- var viewHalf = this.getSize()._divideBy(2);
- // TODO round on display, not calculation to increase precision?
- return this.project(center, zoom)._subtract(viewHalf)._round();
- },
-
- _latLngToNewLayerPoint: function (latlng, newZoom, newCenter) {
- var topLeft = this._getNewTopLeftPoint(newCenter, newZoom).add(this._getMapPanePos());
- return this.project(latlng, newZoom)._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 L.Bounds(centerPoint.subtract(viewHalf), centerPoint.add(viewHalf)),
- offset = this._getBoundsOffset(viewBounds, bounds, zoom);
-
- 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 L.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 nwOffset = this.project(maxBounds.getNorthWest(), zoom).subtract(pxBounds.min),
- seOffset = this.project(maxBounds.getSouthEast(), zoom).subtract(pxBounds.max),
-
- dx = this._rebound(nwOffset.x, -seOffset.x),
- dy = this._rebound(nwOffset.y, -seOffset.y);
-
- return new L.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();
-
- return Math.max(min, Math.min(max, zoom));
- }
-});
-
-L.map = function (id, options) {
- return new L.Map(id, options);
-};
-
-
-/*
- * Mercator projection that takes into account that the Earth is not a perfect sphere.
- * Less popular than spherical mercator; used by projections like EPSG:3395.
- */
-
-L.Projection.Mercator = {
- MAX_LATITUDE: 85.0840591556,
-
- R_MINOR: 6356752.314245179,
- R_MAJOR: 6378137,
-
- project: function (latlng) { // (LatLng) -> Point
- var d = L.LatLng.DEG_TO_RAD,
- max = this.MAX_LATITUDE,
- lat = Math.max(Math.min(max, latlng.lat), -max),
- r = this.R_MAJOR,
- r2 = this.R_MINOR,
- x = latlng.lng * d * r,
- y = lat * d,
- tmp = r2 / r,
- eccent = Math.sqrt(1.0 - tmp * tmp),
- con = eccent * Math.sin(y);
-
- con = Math.pow((1 - con) / (1 + con), eccent * 0.5);
-
- var ts = Math.tan(0.5 * ((Math.PI * 0.5) - y)) / con;
- y = -r * Math.log(ts);
-
- return new L.Point(x, y);
- },
-
- unproject: function (point) { // (Point, Boolean) -> LatLng
- var d = L.LatLng.RAD_TO_DEG,
- r = this.R_MAJOR,
- r2 = this.R_MINOR,
- lng = point.x * d / r,
- tmp = r2 / r,
- eccent = Math.sqrt(1 - (tmp * tmp)),
- ts = Math.exp(- point.y / r),
- phi = (Math.PI / 2) - 2 * Math.atan(ts),
- numIter = 15,
- tol = 1e-7,
- i = numIter,
- dphi = 0.1,
- con;
-
- while ((Math.abs(dphi) > tol) && (--i > 0)) {
- con = eccent * Math.sin(phi);
- dphi = (Math.PI / 2) - 2 * Math.atan(ts *
- Math.pow((1.0 - con) / (1.0 + con), 0.5 * eccent)) - phi;
- phi += dphi;
- }
-
- return new L.LatLng(phi * d, lng);
- }
-};
-
-
-
-L.CRS.EPSG3395 = L.extend({}, L.CRS, {
- code: 'EPSG:3395',
-
- projection: L.Projection.Mercator,
-
- transformation: (function () {
- var m = L.Projection.Mercator,
- r = m.R_MAJOR,
- scale = 0.5 / (Math.PI * r);
-
- return new L.Transformation(scale, 0.5, -scale, 0.5);
- }())
-});
-
-
-/*
- * L.TileLayer is used for standard xyz-numbered tile layers.
- */
-
-L.TileLayer = L.Class.extend({
- includes: L.Mixin.Events,
-
- options: {
- minZoom: 0,
- maxZoom: 18,
- tileSize: 256,
- subdomains: 'abc',
- errorTileUrl: '',
- attribution: '',
- zoomOffset: 0,
- opacity: 1,
- /*
- maxNativeZoom: null,
- zIndex: null,
- tms: false,
- continuousWorld: false,
- noWrap: false,
- zoomReverse: false,
- detectRetina: false,
- reuseTiles: false,
- bounds: false,
- */
- unloadInvisibleTiles: L.Browser.mobile,
- updateWhenIdle: L.Browser.mobile
- },
-
- initialize: function (url, options) {
- options = L.setOptions(this, options);
-
- // detecting retina displays, adjusting tileSize and zoom levels
- if (options.detectRetina && L.Browser.retina && options.maxZoom > 0) {
-
- options.tileSize = Math.floor(options.tileSize / 2);
- options.zoomOffset++;
-
- if (options.minZoom > 0) {
- options.minZoom--;
- }
- this.options.maxZoom--;
- }
-
- if (options.bounds) {
- options.bounds = L.latLngBounds(options.bounds);
- }
-
- this._url = url;
-
- var subdomains = this.options.subdomains;
-
- if (typeof subdomains === 'string') {
- this.options.subdomains = subdomains.split('');
- }
- },
-
- onAdd: function (map) {
- this._map = map;
- this._animated = map._zoomAnimated;
-
- // create a container div for tiles
- this._initContainer();
-
- // set up events
- map.on({
- 'viewreset': this._reset,
- 'moveend': this._update
- }, this);
-
- if (this._animated) {
- map.on({
- 'zoomanim': this._animateZoom,
- 'zoomend': this._endZoomAnim
- }, this);
- }
-
- if (!this.options.updateWhenIdle) {
- this._limitedUpdate = L.Util.limitExecByInterval(this._update, 150, this);
- map.on('move', this._limitedUpdate, this);
- }
-
- this._reset();
- this._update();
- },
-
- addTo: function (map) {
- map.addLayer(this);
- return this;
- },
-
- onRemove: function (map) {
- this._container.parentNode.removeChild(this._container);
-
- map.off({
- 'viewreset': this._reset,
- 'moveend': this._update
- }, this);
-
- if (this._animated) {
- map.off({
- 'zoomanim': this._animateZoom,
- 'zoomend': this._endZoomAnim
- }, this);
- }
-
- if (!this.options.updateWhenIdle) {
- map.off('move', this._limitedUpdate, this);
- }
-
- this._container = null;
- this._map = null;
- },
-
- bringToFront: function () {
- var pane = this._map._panes.tilePane;
-
- if (this._container) {
- pane.appendChild(this._container);
- this._setAutoZIndex(pane, Math.max);
- }
-
- return this;
- },
-
- bringToBack: function () {
- var pane = this._map._panes.tilePane;
-
- if (this._container) {
- pane.insertBefore(this._container, pane.firstChild);
- this._setAutoZIndex(pane, Math.min);
- }
-
- return this;
- },
-
- getAttribution: function () {
- return this.options.attribution;
- },
-
- getContainer: function () {
- return this._container;
- },
-
- setOpacity: function (opacity) {
- this.options.opacity = opacity;
-
- if (this._map) {
- this._updateOpacity();
- }
-
- return this;
- },
-
- setZIndex: function (zIndex) {
- this.options.zIndex = zIndex;
- this._updateZIndex();
-
- return this;
- },
-
- setUrl: function (url, noRedraw) {
- this._url = url;
-
- if (!noRedraw) {
- this.redraw();
- }
-
- return this;
- },
-
- redraw: function () {
- if (this._map) {
- this._reset({hard: true});
- this._update();
- }
- return this;
- },
-
- _updateZIndex: function () {
- if (this._container && this.options.zIndex !== undefined) {
- this._container.style.zIndex = this.options.zIndex;
- }
- },
-
- _setAutoZIndex: function (pane, compare) {
-
- var layers = pane.children,
- edgeZIndex = -compare(Infinity, -Infinity), // -Infinity for max, Infinity for min
- zIndex, i, len;
-
- for (i = 0, len = layers.length; i < len; i++) {
-
- if (layers[i] !== this._container) {
- zIndex = parseInt(layers[i].style.zIndex, 10);
-
- if (!isNaN(zIndex)) {
- edgeZIndex = compare(edgeZIndex, zIndex);
- }
- }
- }
-
- this.options.zIndex = this._container.style.zIndex =
- (isFinite(edgeZIndex) ? edgeZIndex : 0) + compare(1, -1);
- },
-
- _updateOpacity: function () {
- var i,
- tiles = this._tiles;
-
- if (L.Browser.ielt9) {
- for (i in tiles) {
- L.DomUtil.setOpacity(tiles[i], this.options.opacity);
- }
- } else {
- L.DomUtil.setOpacity(this._container, this.options.opacity);
- }
- },
-
- _initContainer: function () {
- var tilePane = this._map._panes.tilePane;
-
- if (!this._container) {
- this._container = L.DomUtil.create('div', 'leaflet-layer');
-
- this._updateZIndex();
-
- if (this._animated) {
- var className = 'leaflet-tile-container';
-
- this._bgBuffer = L.DomUtil.create('div', className, this._container);
- this._tileContainer = L.DomUtil.create('div', className, this._container);
-
- } else {
- this._tileContainer = this._container;
- }
-
- tilePane.appendChild(this._container);
-
- if (this.options.opacity < 1) {
- this._updateOpacity();
- }
- }
- },
-
- _reset: function (e) {
- for (var key in this._tiles) {
- this.fire('tileunload', {tile: this._tiles[key]});
- }
-
- this._tiles = {};
- this._tilesToLoad = 0;
-
- if (this.options.reuseTiles) {
- this._unusedTiles = [];
- }
-
- this._tileContainer.innerHTML = '';
-
- if (this._animated && e && e.hard) {
- this._clearBgBuffer();
- }
-
- this._initContainer();
- },
-
- _getTileSize: function () {
- var map = this._map,
- zoom = map.getZoom() + this.options.zoomOffset,
- zoomN = this.options.maxNativeZoom,
- tileSize = this.options.tileSize;
-
- if (zoomN && zoom > zoomN) {
- tileSize = Math.round(map.getZoomScale(zoom) / map.getZoomScale(zoomN) * tileSize);
- }
-
- return tileSize;
- },
-
- _update: function () {
-
- if (!this._map) { return; }
-
- var map = this._map,
- bounds = map.getPixelBounds(),
- zoom = map.getZoom(),
- tileSize = this._getTileSize();
-
- if (zoom > this.options.maxZoom || zoom < this.options.minZoom) {
- return;
- }
-
- var tileBounds = L.bounds(
- bounds.min.divideBy(tileSize)._floor(),
- bounds.max.divideBy(tileSize)._floor());
-
- this._addTilesFromCenterOut(tileBounds);
-
- if (this.options.unloadInvisibleTiles || this.options.reuseTiles) {
- this._removeOtherTiles(tileBounds);
- }
- },
-
- _addTilesFromCenterOut: function (bounds) {
- var queue = [],
- center = bounds.getCenter();
-
- var j, i, point;
-
- for (j = bounds.min.y; j <= bounds.max.y; j++) {
- for (i = bounds.min.x; i <= bounds.max.x; i++) {
- point = new L.Point(i, j);
-
- if (this._tileShouldBeLoaded(point)) {
- queue.push(point);
- }
- }
- }
-
- var tilesToLoad = queue.length;
-
- if (tilesToLoad === 0) { return; }
-
- // load tiles in order of their distance to center
- queue.sort(function (a, b) {
- return a.distanceTo(center) - b.distanceTo(center);
- });
-
- var fragment = document.createDocumentFragment();
-
- // if its the first batch of tiles to load
- if (!this._tilesToLoad) {
- this.fire('loading');
- }
-
- this._tilesToLoad += tilesToLoad;
-
- for (i = 0; i < tilesToLoad; i++) {
- this._addTile(queue[i], fragment);
- }
-
- this._tileContainer.appendChild(fragment);
- },
-
- _tileShouldBeLoaded: function (tilePoint) {
- if ((tilePoint.x + ':' + tilePoint.y) in this._tiles) {
- return false; // already loaded
- }
-
- var options = this.options;
-
- if (!options.continuousWorld) {
- var limit = this._getWrapTileNum();
-
- // don't load if exceeds world bounds
- if ((options.noWrap && (tilePoint.x < 0 || tilePoint.x >= limit.x)) ||
- tilePoint.y < 0 || tilePoint.y >= limit.y) { return false; }
- }
-
- if (options.bounds) {
- var tileSize = options.tileSize,
- nwPoint = tilePoint.multiplyBy(tileSize),
- sePoint = nwPoint.add([tileSize, tileSize]),
- nw = this._map.unproject(nwPoint),
- se = this._map.unproject(sePoint);
-
- // TODO temporary hack, will be removed after refactoring projections
- // https://github.com/Leaflet/Leaflet/issues/1618
- if (!options.continuousWorld && !options.noWrap) {
- nw = nw.wrap();
- se = se.wrap();
- }
-
- if (!options.bounds.intersects([nw, se])) { return false; }
- }
-
- return true;
- },
-
- _removeOtherTiles: function (bounds) {
- var kArr, x, y, key;
-
- for (key in this._tiles) {
- kArr = key.split(':');
- x = parseInt(kArr[0], 10);
- y = parseInt(kArr[1], 10);
-
- // remove tile if it's out of bounds
- if (x < bounds.min.x || x > bounds.max.x || y < bounds.min.y || y > bounds.max.y) {
- this._removeTile(key);
- }
- }
- },
-
- _removeTile: function (key) {
- var tile = this._tiles[key];
-
- this.fire('tileunload', {tile: tile, url: tile.src});
-
- if (this.options.reuseTiles) {
- L.DomUtil.removeClass(tile, 'leaflet-tile-loaded');
- this._unusedTiles.push(tile);
-
- } else if (tile.parentNode === this._tileContainer) {
- this._tileContainer.removeChild(tile);
- }
-
- // for https://github.com/CloudMade/Leaflet/issues/137
- if (!L.Browser.android) {
- tile.onload = null;
- tile.src = L.Util.emptyImageUrl;
- }
-
- delete this._tiles[key];
- },
-
- _addTile: function (tilePoint, container) {
- var tilePos = this._getTilePos(tilePoint);
-
- // get unused tile - or create a new tile
- var tile = this._getTile();
-
- /*
- Chrome 20 layouts much faster with top/left (verify with timeline, frames)
- Android 4 browser has display issues with top/left and requires transform instead
- (other browsers don't currently care) - see debug/hacks/jitter.html for an example
- */
- L.DomUtil.setPosition(tile, tilePos, L.Browser.chrome);
-
- this._tiles[tilePoint.x + ':' + tilePoint.y] = tile;
-
- this._loadTile(tile, tilePoint);
-
- if (tile.parentNode !== this._tileContainer) {
- container.appendChild(tile);
- }
- },
-
- _getZoomForUrl: function () {
-
- var options = this.options,
- zoom = this._map.getZoom();
-
- if (options.zoomReverse) {
- zoom = options.maxZoom - zoom;
- }
-
- zoom += options.zoomOffset;
-
- return options.maxNativeZoom ? Math.min(zoom, options.maxNativeZoom) : zoom;
- },
-
- _getTilePos: function (tilePoint) {
- var origin = this._map.getPixelOrigin(),
- tileSize = this._getTileSize();
-
- return tilePoint.multiplyBy(tileSize).subtract(origin);
- },
-
- // image-specific code (override to implement e.g. Canvas or SVG tile layer)
-
- getTileUrl: function (tilePoint) {
- return L.Util.template(this._url, L.extend({
- s: this._getSubdomain(tilePoint),
- z: tilePoint.z,
- x: tilePoint.x,
- y: tilePoint.y
- }, this.options));
- },
-
- _getWrapTileNum: function () {
- var crs = this._map.options.crs,
- size = crs.getSize(this._map.getZoom());
- return size.divideBy(this._getTileSize())._floor();
- },
-
- _adjustTilePoint: function (tilePoint) {
-
- var limit = this._getWrapTileNum();
-
- // wrap tile coordinates
- if (!this.options.continuousWorld && !this.options.noWrap) {
- tilePoint.x = ((tilePoint.x % limit.x) + limit.x) % limit.x;
- }
-
- if (this.options.tms) {
- tilePoint.y = limit.y - tilePoint.y - 1;
- }
-
- tilePoint.z = this._getZoomForUrl();
- },
-
- _getSubdomain: function (tilePoint) {
- var index = Math.abs(tilePoint.x + tilePoint.y) % this.options.subdomains.length;
- return this.options.subdomains[index];
- },
-
- _getTile: function () {
- if (this.options.reuseTiles && this._unusedTiles.length > 0) {
- var tile = this._unusedTiles.pop();
- this._resetTile(tile);
- return tile;
- }
- return this._createTile();
- },
-
- // Override if data stored on a tile needs to be cleaned up before reuse
- _resetTile: function (/*tile*/) {},
-
- _createTile: function () {
- var tile = L.DomUtil.create('img', 'leaflet-tile');
- tile.style.width = tile.style.height = this._getTileSize() + 'px';
- tile.galleryimg = 'no';
-
- tile.onselectstart = tile.onmousemove = L.Util.falseFn;
-
- if (L.Browser.ielt9 && this.options.opacity !== undefined) {
- L.DomUtil.setOpacity(tile, this.options.opacity);
- }
- // without this hack, tiles disappear after zoom on Chrome for Android
- // https://github.com/Leaflet/Leaflet/issues/2078
- if (L.Browser.mobileWebkit3d) {
- tile.style.WebkitBackfaceVisibility = 'hidden';
- }
- return tile;
- },
-
- _loadTile: function (tile, tilePoint) {
- tile._layer = this;
- tile.onload = this._tileOnLoad;
- tile.onerror = this._tileOnError;
-
- this._adjustTilePoint(tilePoint);
- tile.src = this.getTileUrl(tilePoint);
-
- this.fire('tileloadstart', {
- tile: tile,
- url: tile.src
- });
- },
-
- _tileLoaded: function () {
- this._tilesToLoad--;
-
- if (this._animated) {
- L.DomUtil.addClass(this._tileContainer, 'leaflet-zoom-animated');
- }
-
- if (!this._tilesToLoad) {
- this.fire('load');
-
- if (this._animated) {
- // clear scaled tiles after all new tiles are loaded (for performance)
- clearTimeout(this._clearBgBufferTimer);
- this._clearBgBufferTimer = setTimeout(L.bind(this._clearBgBuffer, this), 500);
- }
- }
- },
-
- _tileOnLoad: function () {
- var layer = this._layer;
-
- //Only if we are loading an actual image
- if (this.src !== L.Util.emptyImageUrl) {
- L.DomUtil.addClass(this, 'leaflet-tile-loaded');
-
- layer.fire('tileload', {
- tile: this,
- url: this.src
- });
- }
-
- layer._tileLoaded();
- },
-
- _tileOnError: function () {
- var layer = this._layer;
-
- layer.fire('tileerror', {
- tile: this,
- url: this.src
- });
-
- var newUrl = layer.options.errorTileUrl;
- if (newUrl) {
- this.src = newUrl;
- }
-
- layer._tileLoaded();
- }
-});
-
-L.tileLayer = function (url, options) {
- return new L.TileLayer(url, options);
-};
-
-
-/*
- * L.TileLayer.WMS is used for putting WMS tile layers on the map.
- */
-
-L.TileLayer.WMS = L.TileLayer.extend({
-
- defaultWmsParams: {
- service: 'WMS',
- request: 'GetMap',
- version: '1.1.1',
- layers: '',
- styles: '',
- format: 'image/jpeg',
- transparent: false
- },
-
- initialize: function (url, options) { // (String, Object)
-
- this._url = url;
-
- var wmsParams = L.extend({}, this.defaultWmsParams),
- tileSize = options.tileSize || this.options.tileSize;
-
- if (options.detectRetina && L.Browser.retina) {
- wmsParams.width = wmsParams.height = tileSize * 2;
- } else {
- wmsParams.width = wmsParams.height = tileSize;
- }
-
- for (var i in options) {
- // all keys that are not TileLayer options go to WMS params
- if (!this.options.hasOwnProperty(i) && i !== 'crs') {
- wmsParams[i] = options[i];
- }
- }
-
- this.wmsParams = wmsParams;
-
- L.setOptions(this, options);
- },
-
- onAdd: function (map) {
-
- this._crs = this.options.crs || map.options.crs;
-
- this._wmsVersion = parseFloat(this.wmsParams.version);
-
- var projectionKey = this._wmsVersion >= 1.3 ? 'crs' : 'srs';
- this.wmsParams[projectionKey] = this._crs.code;
-
- L.TileLayer.prototype.onAdd.call(this, map);
- },
-
- getTileUrl: function (tilePoint) { // (Point, Number) -> String
-
- var map = this._map,
- tileSize = this.options.tileSize,
-
- nwPoint = tilePoint.multiplyBy(tileSize),
- sePoint = nwPoint.add([tileSize, tileSize]),
-
- nw = this._crs.project(map.unproject(nwPoint, tilePoint.z)),
- se = this._crs.project(map.unproject(sePoint, tilePoint.z)),
- bbox = this._wmsVersion >= 1.3 && this._crs === L.CRS.EPSG4326 ?
- [se.y, nw.x, nw.y, se.x].join(',') :
- [nw.x, se.y, se.x, nw.y].join(','),
-
- url = L.Util.template(this._url, {s: this._getSubdomain(tilePoint)});
-
- return url + L.Util.getParamString(this.wmsParams, url, true) + '&BBOX=' + bbox;
- },
-
- setParams: function (params, noRedraw) {
-
- L.extend(this.wmsParams, params);
-
- if (!noRedraw) {
- this.redraw();
- }
-
- return this;
- }
-});
-
-L.tileLayer.wms = function (url, options) {
- return new L.TileLayer.WMS(url, options);
-};
-
-
-/*
- * L.TileLayer.Canvas is a class that you can use as a base for creating
- * dynamically drawn Canvas-based tile layers.
- */
-
-L.TileLayer.Canvas = L.TileLayer.extend({
- options: {
- async: false
- },
-
- initialize: function (options) {
- L.setOptions(this, options);
- },
-
- redraw: function () {
- if (this._map) {
- this._reset({hard: true});
- this._update();
- }
-
- for (var i in this._tiles) {
- this._redrawTile(this._tiles[i]);
- }
- return this;
- },
-
- _redrawTile: function (tile) {
- this.drawTile(tile, tile._tilePoint, this._map._zoom);
- },
-
- _createTile: function () {
- var tile = L.DomUtil.create('canvas', 'leaflet-tile');
- tile.width = tile.height = this.options.tileSize;
- tile.onselectstart = tile.onmousemove = L.Util.falseFn;
- return tile;
- },
-
- _loadTile: function (tile, tilePoint) {
- tile._layer = this;
- tile._tilePoint = tilePoint;
-
- this._redrawTile(tile);
-
- if (!this.options.async) {
- this.tileDrawn(tile);
- }
- },
-
- drawTile: function (/*tile, tilePoint*/) {
- // override with rendering code
- },
-
- tileDrawn: function (tile) {
- this._tileOnLoad.call(tile);
- }
-});
-
-
-L.tileLayer.canvas = function (options) {
- return new L.TileLayer.Canvas(options);
-};
-
-
-/*
- * L.ImageOverlay is used to overlay images over the map (to specific geographical bounds).
- */
-
-L.ImageOverlay = L.Class.extend({
- includes: L.Mixin.Events,
-
- options: {
- opacity: 1
- },
-
- initialize: function (url, bounds, options) { // (String, LatLngBounds, Object)
- this._url = url;
- this._bounds = L.latLngBounds(bounds);
-
- L.setOptions(this, options);
- },
-
- onAdd: function (map) {
- this._map = map;
-
- if (!this._image) {
- this._initImage();
- }
-
- map._panes.overlayPane.appendChild(this._image);
-
- map.on('viewreset', this._reset, this);
-
- if (map.options.zoomAnimation && L.Browser.any3d) {
- map.on('zoomanim', this._animateZoom, this);
- }
-
- this._reset();
- },
-
- onRemove: function (map) {
- map.getPanes().overlayPane.removeChild(this._image);
-
- map.off('viewreset', this._reset, this);
-
- if (map.options.zoomAnimation) {
- map.off('zoomanim', this._animateZoom, this);
- }
- },
-
- addTo: function (map) {
- map.addLayer(this);
- return this;
- },
-
- setOpacity: function (opacity) {
- this.options.opacity = opacity;
- this._updateOpacity();
- return this;
- },
-
- // TODO remove bringToFront/bringToBack duplication from TileLayer/Path
- bringToFront: function () {
- if (this._image) {
- this._map._panes.overlayPane.appendChild(this._image);
- }
- return this;
- },
-
- bringToBack: function () {
- var pane = this._map._panes.overlayPane;
- if (this._image) {
- pane.insertBefore(this._image, pane.firstChild);
- }
- return this;
- },
-
- setUrl: function (url) {
- this._url = url;
- this._image.src = this._url;
- },
-
- getAttribution: function () {
- return this.options.attribution;
- },
-
- _initImage: function () {
- this._image = L.DomUtil.create('img', 'leaflet-image-layer');
-
- if (this._map.options.zoomAnimation && L.Browser.any3d) {
- L.DomUtil.addClass(this._image, 'leaflet-zoom-animated');
- } else {
- L.DomUtil.addClass(this._image, 'leaflet-zoom-hide');
- }
-
- this._updateOpacity();
-
- //TODO createImage util method to remove duplication
- L.extend(this._image, {
- galleryimg: 'no',
- onselectstart: L.Util.falseFn,
- onmousemove: L.Util.falseFn,
- onload: L.bind(this._onImageLoad, this),
- src: this._url
- });
- },
-
- _animateZoom: function (e) {
- var map = this._map,
- image = this._image,
- scale = map.getZoomScale(e.zoom),
- nw = this._bounds.getNorthWest(),
- se = this._bounds.getSouthEast(),
-
- topLeft = map._latLngToNewLayerPoint(nw, e.zoom, e.center),
- size = map._latLngToNewLayerPoint(se, e.zoom, e.center)._subtract(topLeft),
- origin = topLeft._add(size._multiplyBy((1 / 2) * (1 - 1 / scale)));
-
- image.style[L.DomUtil.TRANSFORM] =
- L.DomUtil.getTranslateString(origin) + ' scale(' + scale + ') ';
- },
-
- _reset: function () {
- var image = this._image,
- topLeft = this._map.latLngToLayerPoint(this._bounds.getNorthWest()),
- size = this._map.latLngToLayerPoint(this._bounds.getSouthEast())._subtract(topLeft);
-
- L.DomUtil.setPosition(image, topLeft);
-
- image.style.width = size.x + 'px';
- image.style.height = size.y + 'px';
- },
-
- _onImageLoad: function () {
- this.fire('load');
- },
-
- _updateOpacity: function () {
- L.DomUtil.setOpacity(this._image, this.options.opacity);
- }
-});
-
-L.imageOverlay = function (url, bounds, options) {
- return new L.ImageOverlay(url, bounds, options);
-};
-
-
-/*
- * L.Icon is an image-based icon class that you can use with L.Marker for custom markers.
- */
-
-L.Icon = L.Class.extend({
- options: {
- /*
- iconUrl: (String) (required)
- iconRetinaUrl: (String) (optional, used for retina devices if detected)
- iconSize: (Point) (can be set through CSS)
- iconAnchor: (Point) (centered by default, can be set in CSS with negative margins)
- popupAnchor: (Point) (if not specified, popup opens in the anchor point)
- shadowUrl: (String) (no shadow by default)
- shadowRetinaUrl: (String) (optional, used for retina devices if detected)
- shadowSize: (Point)
- shadowAnchor: (Point)
- */
- className: ''
- },
-
- initialize: function (options) {
- L.setOptions(this, options);
- },
-
- createIcon: function (oldIcon) {
- return this._createIcon('icon', oldIcon);
- },
-
- 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;
- if (!oldIcon || oldIcon.tagName !== 'IMG') {
- img = this._createImg(src);
- } else {
- img = this._createImg(src, oldIcon);
- }
- this._setIconStyles(img, name);
-
- return img;
- },
-
- _setIconStyles: function (img, name) {
- var options = this.options,
- size = L.point(options[name + 'Size']),
- anchor;
-
- if (name === 'shadow') {
- anchor = L.point(options.shadowAnchor || options.iconAnchor);
- } else {
- anchor = L.point(options.iconAnchor);
- }
-
- if (!anchor && size) {
- anchor = 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) {
- if (L.Browser.retina && this.options[name + 'RetinaUrl']) {
- return this.options[name + 'RetinaUrl'];
- }
- return this.options[name + 'Url'];
- }
-});
-
-L.icon = function (options) {
- return new L.Icon(options);
-};
-
-
-/*
- * L.Icon.Default is the blue marker icon used by default in Leaflet.
- */
-
-L.Icon.Default = L.Icon.extend({
-
- options: {
- iconSize: [25, 41],
- iconAnchor: [12, 41],
- popupAnchor: [1, -34],
-
- shadowSize: [41, 41]
- },
-
- _getIconUrl: function (name) {
- var key = name + 'Url';
-
- if (this.options[key]) {
- return this.options[key];
- }
-
- if (L.Browser.retina && name === 'icon') {
- name += '-2x';
- }
-
- var path = L.Icon.Default.imagePath;
-
- if (!path) {
- throw new Error('Couldn\'t autodetect L.Icon.Default.imagePath, set it manually.');
- }
-
- return path + '/marker-' + name + '.png';
- }
-});
-
-L.Icon.Default.imagePath = (function () {
- var scripts = document.getElementsByTagName('script'),
- leafletRe = /[\/^]leaflet[\-\._]?([\w\-\._]*)\.js\??/;
-
- var i, len, src, matches, path;
-
- for (i = 0, len = scripts.length; i < len; i++) {
- src = scripts[i].src;
- matches = src.match(leafletRe);
-
- if (matches) {
- path = src.split(leafletRe)[0];
- return (path ? path + '/' : '') + 'images';
- }
- }
-}());
-
-
-/*
- * L.Marker is used to display clickable/draggable icons on the map.
- */
-
-L.Marker = L.Class.extend({
-
- includes: L.Mixin.Events,
-
- options: {
- icon: new L.Icon.Default(),
- title: '',
- alt: '',
- clickable: true,
- draggable: false,
- keyboard: true,
- zIndexOffset: 0,
- opacity: 1,
- riseOnHover: false,
- riseOffset: 250
- },
-
- initialize: function (latlng, options) {
- L.setOptions(this, options);
- this._latlng = L.latLng(latlng);
- },
-
- onAdd: function (map) {
- this._map = map;
-
- map.on('viewreset', this.update, this);
-
- this._initIcon();
- this.update();
- this.fire('add');
-
- if (map.options.zoomAnimation && map.options.markerZoomAnimation) {
- map.on('zoomanim', this._animateZoom, this);
- }
- },
-
- addTo: function (map) {
- map.addLayer(this);
- return this;
- },
-
- onRemove: function (map) {
- if (this.dragging) {
- this.dragging.disable();
- }
-
- this._removeIcon();
- this._removeShadow();
-
- this.fire('remove');
-
- map.off({
- 'viewreset': this.update,
- 'zoomanim': this._animateZoom
- }, this);
-
- this._map = null;
- },
-
- getLatLng: function () {
- return this._latlng;
- },
-
- setLatLng: function (latlng) {
- this._latlng = L.latLng(latlng);
-
- this.update();
-
- return this.fire('move', { latlng: this._latlng });
- },
-
- setZIndexOffset: function (offset) {
- this.options.zIndexOffset = offset;
- this.update();
-
- return this;
- },
-
- setIcon: function (icon) {
-
- this.options.icon = icon;
-
- if (this._map) {
- this._initIcon();
- this.update();
- }
-
- if (this._popup) {
- this.bindPopup(this._popup);
- }
-
- return this;
- },
-
- update: function () {
- if (this._icon) {
- var pos = this._map.latLngToLayerPoint(this._latlng).round();
- this._setPos(pos);
- }
-
- return this;
- },
-
- _initIcon: function () {
- var options = this.options,
- map = this._map,
- animation = (map.options.zoomAnimation && map.options.markerZoomAnimation),
- classToAdd = animation ? 'leaflet-zoom-animated' : 'leaflet-zoom-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;
- }
- }
-
- L.DomUtil.addClass(icon, classToAdd);
-
- if (options.keyboard) {
- icon.tabIndex = '0';
- }
-
- this._icon = icon;
-
- this._initInteraction();
-
- if (options.riseOnHover) {
- L.DomEvent
- .on(icon, 'mouseover', this._bringToFront, this)
- .on(icon, 'mouseout', this._resetZIndex, this);
- }
-
- var newShadow = options.icon.createShadow(this._shadow),
- addShadow = false;
-
- if (newShadow !== this._shadow) {
- this._removeShadow();
- addShadow = true;
- }
-
- if (newShadow) {
- L.DomUtil.addClass(newShadow, classToAdd);
- }
- this._shadow = newShadow;
-
-
- if (options.opacity < 1) {
- this._updateOpacity();
- }
-
-
- var panes = this._map._panes;
-
- if (addIcon) {
- panes.markerPane.appendChild(this._icon);
- }
-
- if (newShadow && addShadow) {
- panes.shadowPane.appendChild(this._shadow);
- }
- },
-
- _removeIcon: function () {
- if (this.options.riseOnHover) {
- L.DomEvent
- .off(this._icon, 'mouseover', this._bringToFront)
- .off(this._icon, 'mouseout', this._resetZIndex);
- }
-
- this._map._panes.markerPane.removeChild(this._icon);
-
- this._icon = null;
- },
-
- _removeShadow: function () {
- if (this._shadow) {
- this._map._panes.shadowPane.removeChild(this._shadow);
- }
- this._shadow = null;
- },
-
- _setPos: function (pos) {
- L.DomUtil.setPosition(this._icon, pos);
-
- if (this._shadow) {
- L.DomUtil.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.clickable) { return; }
-
- // TODO refactor into something shared with Map/Path/etc. to DRY it up
-
- var icon = this._icon,
- events = ['dblclick', 'mousedown', 'mouseover', 'mouseout', 'contextmenu'];
-
- L.DomUtil.addClass(icon, 'leaflet-clickable');
- L.DomEvent.on(icon, 'click', this._onMouseClick, this);
- L.DomEvent.on(icon, 'keypress', this._onKeyPress, this);
-
- for (var i = 0; i < events.length; i++) {
- L.DomEvent.on(icon, events[i], this._fireMouseEvent, this);
- }
-
- if (L.Handler.MarkerDrag) {
- this.dragging = new L.Handler.MarkerDrag(this);
-
- if (this.options.draggable) {
- this.dragging.enable();
- }
- }
- },
-
- _onMouseClick: function (e) {
- var wasDragged = this.dragging && this.dragging.moved();
-
- if (this.hasEventListeners(e.type) || wasDragged) {
- L.DomEvent.stopPropagation(e);
- }
-
- if (wasDragged) { return; }
-
- if ((!this.dragging || !this.dragging._enabled) && this._map.dragging && this._map.dragging.moved()) { return; }
-
- this.fire(e.type, {
- originalEvent: e,
- latlng: this._latlng
- });
- },
-
- _onKeyPress: function (e) {
- if (e.keyCode === 13) {
- this.fire('click', {
- originalEvent: e,
- latlng: this._latlng
- });
- }
- },
-
- _fireMouseEvent: function (e) {
-
- this.fire(e.type, {
- originalEvent: e,
- latlng: this._latlng
- });
-
- // TODO proper custom event propagation
- // this line will always be called if marker is in a FeatureGroup
- if (e.type === 'contextmenu' && this.hasEventListeners(e.type)) {
- L.DomEvent.preventDefault(e);
- }
- if (e.type !== 'mousedown') {
- L.DomEvent.stopPropagation(e);
- } else {
- L.DomEvent.preventDefault(e);
- }
- },
-
- setOpacity: function (opacity) {
- this.options.opacity = opacity;
- if (this._map) {
- this._updateOpacity();
- }
-
- return this;
- },
-
- _updateOpacity: function () {
- L.DomUtil.setOpacity(this._icon, this.options.opacity);
- if (this._shadow) {
- L.DomUtil.setOpacity(this._shadow, this.options.opacity);
- }
- },
-
- _bringToFront: function () {
- this._updateZIndex(this.options.riseOffset);
- },
-
- _resetZIndex: function () {
- this._updateZIndex(0);
- }
-});
-
-L.marker = function (latlng, options) {
- return new L.Marker(latlng, options);
-};
-
-
-/*
- * L.DivIcon is a lightweight HTML-based icon class (as opposed to the image-based L.Icon)
- * to use with L.Marker.
- */
-
-L.DivIcon = L.Icon.extend({
- options: {
- iconSize: [12, 12], // also can be set through CSS
- /*
- iconAnchor: (Point)
- popupAnchor: (Point)
- html: (String)
- bgPos: (Point)
- */
- className: 'leaflet-div-icon',
- html: false
- },
-
- createIcon: function (oldIcon) {
- var div = (oldIcon && oldIcon.tagName === 'DIV') ? oldIcon : document.createElement('div'),
- options = this.options;
-
- if (options.html !== false) {
- div.innerHTML = options.html;
- } else {
- div.innerHTML = '';
- }
-
- if (options.bgPos) {
- div.style.backgroundPosition =
- (-options.bgPos.x) + 'px ' + (-options.bgPos.y) + 'px';
- }
-
- this._setIconStyles(div, 'icon');
- return div;
- },
-
- createShadow: function () {
- return null;
- }
-});
-
-L.divIcon = function (options) {
- return new L.DivIcon(options);
-};
-
-
-/*
- * L.Popup is used for displaying popups on the map.
- */
-
-L.Map.mergeOptions({
- closePopupOnClick: true
-});
-
-L.Popup = L.Class.extend({
- includes: L.Mixin.Events,
-
- options: {
- minWidth: 50,
- maxWidth: 300,
- // maxHeight: null,
- autoPan: true,
- closeButton: true,
- offset: [0, 7],
- autoPanPadding: [5, 5],
- // autoPanPaddingTopLeft: null,
- // autoPanPaddingBottomRight: null,
- keepInView: false,
- className: '',
- zoomAnimation: true
- },
-
- initialize: function (options, source) {
- L.setOptions(this, options);
-
- this._source = source;
- this._animated = L.Browser.any3d && this.options.zoomAnimation;
- this._isOpen = false;
- },
-
- onAdd: function (map) {
- this._map = map;
-
- if (!this._container) {
- this._initLayout();
- }
-
- var animFade = map.options.fadeAnimation;
-
- if (animFade) {
- L.DomUtil.setOpacity(this._container, 0);
- }
- map._panes.popupPane.appendChild(this._container);
-
- map.on(this._getEvents(), this);
-
- this.update();
-
- if (animFade) {
- L.DomUtil.setOpacity(this._container, 1);
- }
-
- this.fire('open');
-
- map.fire('popupopen', {popup: this});
-
- if (this._source) {
- this._source.fire('popupopen', {popup: this});
- }
- },
-
- addTo: function (map) {
- map.addLayer(this);
- return this;
- },
-
- openOn: function (map) {
- map.openPopup(this);
- return this;
- },
-
- onRemove: function (map) {
- map._panes.popupPane.removeChild(this._container);
-
- L.Util.falseFn(this._container.offsetWidth); // force reflow
-
- map.off(this._getEvents(), this);
-
- if (map.options.fadeAnimation) {
- L.DomUtil.setOpacity(this._container, 0);
- }
-
- this._map = null;
-
- this.fire('close');
-
- map.fire('popupclose', {popup: this});
-
- if (this._source) {
- this._source.fire('popupclose', {popup: this});
- }
- },
-
- getLatLng: function () {
- return this._latlng;
- },
-
- setLatLng: function (latlng) {
- this._latlng = L.latLng(latlng);
- if (this._map) {
- this._updatePosition();
- this._adjustPan();
- }
- return this;
- },
-
- getContent: function () {
- return this._content;
- },
-
- setContent: function (content) {
- this._content = content;
- this.update();
- return this;
- },
-
- update: function () {
- if (!this._map) { return; }
-
- this._container.style.visibility = 'hidden';
-
- this._updateContent();
- this._updateLayout();
- this._updatePosition();
-
- this._container.style.visibility = '';
-
- this._adjustPan();
- },
-
- _getEvents: function () {
- var events = {
- viewreset: this._updatePosition
- };
-
- if (this._animated) {
- events.zoomanim = this._zoomAnimation;
- }
- if ('closeOnClick' in this.options ? this.options.closeOnClick : this._map.options.closePopupOnClick) {
- events.preclick = this._close;
- }
- if (this.options.keepInView) {
- events.moveend = this._adjustPan;
- }
-
- return events;
- },
-
- _close: function () {
- if (this._map) {
- this._map.closePopup(this);
- }
- },
-
- _initLayout: function () {
- var prefix = 'leaflet-popup',
- containerClass = prefix + ' ' + this.options.className + ' leaflet-zoom-' +
- (this._animated ? 'animated' : 'hide'),
- container = this._container = L.DomUtil.create('div', containerClass),
- closeButton;
-
- if (this.options.closeButton) {
- closeButton = this._closeButton =
- L.DomUtil.create('a', prefix + '-close-button', container);
- closeButton.href = '#close';
- closeButton.innerHTML = '×';
- L.DomEvent.disableClickPropagation(closeButton);
-
- L.DomEvent.on(closeButton, 'click', this._onCloseButtonClick, this);
- }
-
- var wrapper = this._wrapper =
- L.DomUtil.create('div', prefix + '-content-wrapper', container);
- L.DomEvent.disableClickPropagation(wrapper);
-
- this._contentNode = L.DomUtil.create('div', prefix + '-content', wrapper);
-
- L.DomEvent.disableScrollPropagation(this._contentNode);
- L.DomEvent.on(wrapper, 'contextmenu', L.DomEvent.stopPropagation);
-
- this._tipContainer = L.DomUtil.create('div', prefix + '-tip-container', container);
- this._tip = L.DomUtil.create('div', prefix + '-tip', this._tipContainer);
- },
-
- _updateContent: function () {
- if (!this._content) { return; }
-
- if (typeof this._content === 'string') {
- this._contentNode.innerHTML = this._content;
- } else {
- while (this._contentNode.hasChildNodes()) {
- this._contentNode.removeChild(this._contentNode.firstChild);
- }
- this._contentNode.appendChild(this._content);
- }
- this.fire('contentupdate');
- },
-
- _updateLayout: function () {
- var container = this._contentNode,
- style = container.style;
-
- style.width = '';
- style.whiteSpace = 'nowrap';
-
- var width = container.offsetWidth;
- width = Math.min(width, this.options.maxWidth);
- width = Math.max(width, this.options.minWidth);
-
- style.width = (width + 1) + 'px';
- style.whiteSpace = '';
-
- style.height = '';
-
- var height = container.offsetHeight,
- maxHeight = this.options.maxHeight,
- scrolledClass = 'leaflet-popup-scrolled';
-
- if (maxHeight && height > maxHeight) {
- style.height = maxHeight + 'px';
- L.DomUtil.addClass(container, scrolledClass);
- } else {
- L.DomUtil.removeClass(container, scrolledClass);
- }
-
- this._containerWidth = this._container.offsetWidth;
- },
-
- _updatePosition: function () {
- if (!this._map) { return; }
-
- var pos = this._map.latLngToLayerPoint(this._latlng),
- animated = this._animated,
- offset = L.point(this.options.offset);
-
- if (animated) {
- L.DomUtil.setPosition(this._container, pos);
- }
-
- this._containerBottom = -offset.y - (animated ? 0 : pos.y);
- this._containerLeft = -Math.round(this._containerWidth / 2) + offset.x + (animated ? 0 : pos.x);
-
- // bottom position the popup in case the height of the popup changes (images loading etc)
- this._container.style.bottom = this._containerBottom + 'px';
- this._container.style.left = this._containerLeft + 'px';
- },
-
- _zoomAnimation: function (opt) {
- var pos = this._map._latLngToNewLayerPoint(this._latlng, opt.zoom, opt.center);
-
- L.DomUtil.setPosition(this._container, pos);
- },
-
- _adjustPan: function () {
- if (!this.options.autoPan) { return; }
-
- var map = this._map,
- containerHeight = this._container.offsetHeight,
- containerWidth = this._containerWidth,
-
- layerPos = new L.Point(this._containerLeft, -containerHeight - this._containerBottom);
-
- if (this._animated) {
- layerPos._add(L.DomUtil.getPosition(this._container));
- }
-
- var containerPos = map.layerPointToContainerPoint(layerPos),
- padding = L.point(this.options.autoPanPadding),
- paddingTL = L.point(this.options.autoPanPaddingTopLeft || padding),
- paddingBR = L.point(this.options.autoPanPaddingBottomRight || padding),
- size = map.getSize(),
- dx = 0,
- dy = 0;
-
- if (containerPos.x + containerWidth + paddingBR.x > size.x) { // right
- dx = containerPos.x + containerWidth - size.x + paddingBR.x;
- }
- if (containerPos.x - dx - paddingTL.x < 0) { // left
- dx = containerPos.x - paddingTL.x;
- }
- if (containerPos.y + containerHeight + paddingBR.y > size.y) { // bottom
- dy = containerPos.y + containerHeight - size.y + paddingBR.y;
- }
- if (containerPos.y - dy - paddingTL.y < 0) { // top
- dy = containerPos.y - paddingTL.y;
- }
-
- if (dx || dy) {
- map
- .fire('autopanstart')
- .panBy([dx, dy]);
- }
- },
-
- _onCloseButtonClick: function (e) {
- this._close();
- L.DomEvent.stop(e);
- }
-});
-
-L.popup = function (options, source) {
- return new L.Popup(options, source);
-};
-
-
-L.Map.include({
- openPopup: function (popup, latlng, options) { // (Popup) or (String || HTMLElement, LatLng[, Object])
- this.closePopup();
-
- if (!(popup instanceof L.Popup)) {
- var content = popup;
-
- popup = new L.Popup(options)
- .setLatLng(latlng)
- .setContent(content);
- }
- popup._isOpen = true;
-
- this._popup = popup;
- return this.addLayer(popup);
- },
-
- closePopup: function (popup) {
- if (!popup || popup === this._popup) {
- popup = this._popup;
- this._popup = null;
- }
- if (popup) {
- this.removeLayer(popup);
- popup._isOpen = false;
- }
- return this;
- }
-});
-
-
-/*
- * Popup extension to L.Marker, adding popup-related methods.
- */
-
-L.Marker.include({
- openPopup: function () {
- if (this._popup && this._map && !this._map.hasLayer(this._popup)) {
- this._popup.setLatLng(this._latlng);
- this._map.openPopup(this._popup);
- }
-
- return this;
- },
-
- closePopup: function () {
- if (this._popup) {
- this._popup._close();
- }
- return this;
- },
-
- togglePopup: function () {
- if (this._popup) {
- if (this._popup._isOpen) {
- this.closePopup();
- } else {
- this.openPopup();
- }
- }
- return this;
- },
-
- bindPopup: function (content, options) {
- var anchor = L.point(this.options.icon.options.popupAnchor || [0, 0]);
-
- anchor = anchor.add(L.Popup.prototype.options.offset);
-
- if (options && options.offset) {
- anchor = anchor.add(options.offset);
- }
-
- options = L.extend({offset: anchor}, options);
-
- if (!this._popupHandlersAdded) {
- this
- .on('click', this.togglePopup, this)
- .on('remove', this.closePopup, this)
- .on('move', this._movePopup, this);
- this._popupHandlersAdded = true;
- }
-
- if (content instanceof L.Popup) {
- L.setOptions(content, options);
- this._popup = content;
- } else {
- this._popup = new L.Popup(options, this)
- .setContent(content);
- }
-
- return this;
- },
-
- setPopupContent: function (content) {
- if (this._popup) {
- this._popup.setContent(content);
- }
- return this;
- },
-
- unbindPopup: function () {
- if (this._popup) {
- this._popup = null;
- this
- .off('click', this.togglePopup, this)
- .off('remove', this.closePopup, this)
- .off('move', this._movePopup, this);
- this._popupHandlersAdded = false;
- }
- return this;
- },
-
- getPopup: function () {
- return this._popup;
- },
-
- _movePopup: function (e) {
- this._popup.setLatLng(e.latlng);
- }
-});
-
-
-/*
- * L.LayerGroup is a class to combine several layers into one so that
- * you can manipulate the group (e.g. add/remove it) as one layer.
- */
-
-L.LayerGroup = L.Class.extend({
- initialize: function (layers) {
- this._layers = {};
-
- var i, len;
-
- if (layers) {
- for (i = 0, len = layers.length; i < len; i++) {
- this.addLayer(layers[i]);
- }
- }
- },
-
- addLayer: function (layer) {
- var id = this.getLayerId(layer);
-
- this._layers[id] = layer;
-
- if (this._map) {
- this._map.addLayer(layer);
- }
-
- return this;
- },
-
- 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;
- },
-
- hasLayer: function (layer) {
- if (!layer) { return false; }
-
- return (layer in this._layers || this.getLayerId(layer) in this._layers);
- },
-
- clearLayers: function () {
- this.eachLayer(this.removeLayer, this);
- return this;
- },
-
- 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) {
- this._map = map;
- this.eachLayer(map.addLayer, map);
- },
-
- onRemove: function (map) {
- this.eachLayer(map.removeLayer, map);
- this._map = null;
- },
-
- addTo: function (map) {
- map.addLayer(this);
- return this;
- },
-
- eachLayer: function (method, context) {
- for (var i in this._layers) {
- method.call(context, this._layers[i]);
- }
- return this;
- },
-
- getLayer: function (id) {
- return this._layers[id];
- },
-
- getLayers: function () {
- var layers = [];
-
- for (var i in this._layers) {
- layers.push(this._layers[i]);
- }
- return layers;
- },
-
- setZIndex: function (zIndex) {
- return this.invoke('setZIndex', zIndex);
- },
-
- getLayerId: function (layer) {
- return L.stamp(layer);
- }
-});
-
-L.layerGroup = function (layers) {
- return new L.LayerGroup(layers);
-};
-
-
-/*
- * L.FeatureGroup extends L.LayerGroup by introducing mouse events and additional methods
- * shared between a group of interactive layers (like vectors or markers).
- */
-
-L.FeatureGroup = L.LayerGroup.extend({
- includes: L.Mixin.Events,
-
- statics: {
- EVENTS: 'click dblclick mouseover mouseout mousemove contextmenu popupopen popupclose'
- },
-
- addLayer: function (layer) {
- if (this.hasLayer(layer)) {
- return this;
- }
-
- if ('on' in layer) {
- layer.on(L.FeatureGroup.EVENTS, this._propagateEvent, this);
- }
-
- L.LayerGroup.prototype.addLayer.call(this, layer);
-
- if (this._popupContent && layer.bindPopup) {
- layer.bindPopup(this._popupContent, this._popupOptions);
- }
-
- 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.off(L.FeatureGroup.EVENTS, this._propagateEvent, this);
-
- L.LayerGroup.prototype.removeLayer.call(this, layer);
-
- if (this._popupContent) {
- this.invoke('unbindPopup');
- }
-
- return this.fire('layerremove', {layer: layer});
- },
-
- bindPopup: function (content, options) {
- this._popupContent = content;
- this._popupOptions = options;
- return this.invoke('bindPopup', content, options);
- },
-
- openPopup: function (latlng) {
- // open popup on the first layer
- for (var id in this._layers) {
- this._layers[id].openPopup(latlng);
- break;
- }
- return this;
- },
-
- setStyle: function (style) {
- return this.invoke('setStyle', style);
- },
-
- bringToFront: function () {
- return this.invoke('bringToFront');
- },
-
- bringToBack: function () {
- return this.invoke('bringToBack');
- },
-
- getBounds: function () {
- var bounds = new L.LatLngBounds();
-
- this.eachLayer(function (layer) {
- bounds.extend(layer instanceof L.Marker ? layer.getLatLng() : layer.getBounds());
- });
-
- return bounds;
- },
-
- _propagateEvent: function (e) {
- e = L.extend({
- layer: e.target,
- target: this
- }, e);
- this.fire(e.type, e);
- }
-});
-
-L.featureGroup = function (layers) {
- return new L.FeatureGroup(layers);
-};
-
-
-/*
- * L.Path is a base class for rendering vector paths on a map. Inherited by Polyline, Circle, etc.
- */
-
-L.Path = L.Class.extend({
- includes: [L.Mixin.Events],
-
- statics: {
- // how much to extend the clip area around the map view
- // (relative to its size, e.g. 0.5 is half the screen in each direction)
- // set it so that SVG element doesn't exceed 1280px (vectors flicker on dragend if it is)
- CLIP_PADDING: (function () {
- var max = L.Browser.mobile ? 1280 : 2000,
- target = (max / Math.max(window.outerWidth, window.outerHeight) - 1) / 2;
- return Math.max(0, Math.min(0.5, target));
- })()
- },
-
- options: {
- stroke: true,
- color: '#0033ff',
- dashArray: null,
- lineCap: null,
- lineJoin: null,
- weight: 5,
- opacity: 0.5,
-
- fill: false,
- fillColor: null, //same as color by default
- fillOpacity: 0.2,
-
- clickable: true
- },
-
- initialize: function (options) {
- L.setOptions(this, options);
- },
-
- onAdd: function (map) {
- this._map = map;
-
- if (!this._container) {
- this._initElements();
- this._initEvents();
- }
-
- this.projectLatlngs();
- this._updatePath();
-
- if (this._container) {
- this._map._pathRoot.appendChild(this._container);
- }
-
- this.fire('add');
-
- map.on({
- 'viewreset': this.projectLatlngs,
- 'moveend': this._updatePath
- }, this);
- },
-
- addTo: function (map) {
- map.addLayer(this);
- return this;
- },
-
- onRemove: function (map) {
- map._pathRoot.removeChild(this._container);
-
- // Need to fire remove event before we set _map to null as the event hooks might need the object
- this.fire('remove');
- this._map = null;
-
- if (L.Browser.vml) {
- this._container = null;
- this._stroke = null;
- this._fill = null;
- }
-
- map.off({
- 'viewreset': this.projectLatlngs,
- 'moveend': this._updatePath
- }, this);
- },
-
- projectLatlngs: function () {
- // do all projection stuff here
- },
-
- setStyle: function (style) {
- L.setOptions(this, style);
-
- if (this._container) {
- this._updateStyle();
- }
-
- return this;
- },
-
- redraw: function () {
- if (this._map) {
- this.projectLatlngs();
- this._updatePath();
- }
- return this;
- }
-});
-
-L.Map.include({
- _updatePathViewport: function () {
- var p = L.Path.CLIP_PADDING,
- size = this.getSize(),
- panePos = L.DomUtil.getPosition(this._mapPane),
- min = panePos.multiplyBy(-1)._subtract(size.multiplyBy(p)._round()),
- max = min.add(size.multiplyBy(1 + p * 2)._round());
-
- this._pathViewport = new L.Bounds(min, max);
- }
-});
-
-
-/*
- * Extends L.Path with SVG-specific rendering code.
- */
-
-L.Path.SVG_NS = 'http://www.w3.org/2000/svg';
-
-L.Browser.svg = !!(document.createElementNS && document.createElementNS(L.Path.SVG_NS, 'svg').createSVGRect);
-
-L.Path = L.Path.extend({
- statics: {
- SVG: L.Browser.svg
- },
-
- bringToFront: function () {
- var root = this._map._pathRoot,
- path = this._container;
-
- if (path && root.lastChild !== path) {
- root.appendChild(path);
- }
- return this;
- },
-
- bringToBack: function () {
- var root = this._map._pathRoot,
- path = this._container,
- first = root.firstChild;
-
- if (path && first !== path) {
- root.insertBefore(path, first);
- }
- return this;
- },
-
- getPathString: function () {
- // form path string here
- },
-
- _createElement: function (name) {
- return document.createElementNS(L.Path.SVG_NS, name);
- },
-
- _initElements: function () {
- this._map._initPathRoot();
- this._initPath();
- this._initStyle();
- },
-
- _initPath: function () {
- this._container = this._createElement('g');
-
- this._path = this._createElement('path');
-
- if (this.options.className) {
- L.DomUtil.addClass(this._path, this.options.className);
- }
-
- this._container.appendChild(this._path);
- },
-
- _initStyle: function () {
- if (this.options.stroke) {
- this._path.setAttribute('stroke-linejoin', 'round');
- this._path.setAttribute('stroke-linecap', 'round');
- }
- if (this.options.fill) {
- this._path.setAttribute('fill-rule', 'evenodd');
- }
- if (this.options.pointerEvents) {
- this._path.setAttribute('pointer-events', this.options.pointerEvents);
- }
- if (!this.options.clickable && !this.options.pointerEvents) {
- this._path.setAttribute('pointer-events', 'none');
- }
- this._updateStyle();
- },
-
- _updateStyle: function () {
- if (this.options.stroke) {
- this._path.setAttribute('stroke', this.options.color);
- this._path.setAttribute('stroke-opacity', this.options.opacity);
- this._path.setAttribute('stroke-width', this.options.weight);
- if (this.options.dashArray) {
- this._path.setAttribute('stroke-dasharray', this.options.dashArray);
- } else {
- this._path.removeAttribute('stroke-dasharray');
- }
- if (this.options.lineCap) {
- this._path.setAttribute('stroke-linecap', this.options.lineCap);
- }
- if (this.options.lineJoin) {
- this._path.setAttribute('stroke-linejoin', this.options.lineJoin);
- }
- } else {
- this._path.setAttribute('stroke', 'none');
- }
- if (this.options.fill) {
- this._path.setAttribute('fill', this.options.fillColor || this.options.color);
- this._path.setAttribute('fill-opacity', this.options.fillOpacity);
- } else {
- this._path.setAttribute('fill', 'none');
- }
- },
-
- _updatePath: function () {
- var str = this.getPathString();
- if (!str) {
- // fix webkit empty string parsing bug
- str = 'M0 0';
- }
- this._path.setAttribute('d', str);
- },
-
- // TODO remove duplication with L.Map
- _initEvents: function () {
- if (this.options.clickable) {
- if (L.Browser.svg || !L.Browser.vml) {
- L.DomUtil.addClass(this._path, 'leaflet-clickable');
- }
-
- L.DomEvent.on(this._container, 'click', this._onMouseClick, this);
-
- var events = ['dblclick', 'mousedown', 'mouseover',
- 'mouseout', 'mousemove', 'contextmenu'];
- for (var i = 0; i < events.length; i++) {
- L.DomEvent.on(this._container, events[i], this._fireMouseEvent, this);
- }
- }
- },
-
- _onMouseClick: function (e) {
- if (this._map.dragging && this._map.dragging.moved()) { return; }
-
- this._fireMouseEvent(e);
- },
-
- _fireMouseEvent: function (e) {
- if (!this.hasEventListeners(e.type)) { return; }
-
- var map = this._map,
- containerPoint = map.mouseEventToContainerPoint(e),
- layerPoint = map.containerPointToLayerPoint(containerPoint),
- latlng = map.layerPointToLatLng(layerPoint);
-
- this.fire(e.type, {
- latlng: latlng,
- layerPoint: layerPoint,
- containerPoint: containerPoint,
- originalEvent: e
- });
-
- if (e.type === 'contextmenu') {
- L.DomEvent.preventDefault(e);
- }
- if (e.type !== 'mousemove') {
- L.DomEvent.stopPropagation(e);
- }
- }
-});
-
-L.Map.include({
- _initPathRoot: function () {
- if (!this._pathRoot) {
- this._pathRoot = L.Path.prototype._createElement('svg');
- this._panes.overlayPane.appendChild(this._pathRoot);
-
- if (this.options.zoomAnimation && L.Browser.any3d) {
- L.DomUtil.addClass(this._pathRoot, 'leaflet-zoom-animated');
-
- this.on({
- 'zoomanim': this._animatePathZoom,
- 'zoomend': this._endPathZoom
- });
- } else {
- L.DomUtil.addClass(this._pathRoot, 'leaflet-zoom-hide');
- }
-
- this.on('moveend', this._updateSvgViewport);
- this._updateSvgViewport();
- }
- },
-
- _animatePathZoom: function (e) {
- var scale = this.getZoomScale(e.zoom),
- offset = this._getCenterOffset(e.center)._multiplyBy(-scale)._add(this._pathViewport.min);
-
- this._pathRoot.style[L.DomUtil.TRANSFORM] =
- L.DomUtil.getTranslateString(offset) + ' scale(' + scale + ') ';
-
- this._pathZooming = true;
- },
-
- _endPathZoom: function () {
- this._pathZooming = false;
- },
-
- _updateSvgViewport: function () {
-
- if (this._pathZooming) {
- // Do not update SVGs while a zoom animation is going on otherwise the animation will break.
- // When the zoom animation ends we will be updated again anyway
- // This fixes the case where you do a momentum move and zoom while the move is still ongoing.
- return;
- }
-
- this._updatePathViewport();
-
- var vp = this._pathViewport,
- min = vp.min,
- max = vp.max,
- width = max.x - min.x,
- height = max.y - min.y,
- root = this._pathRoot,
- pane = this._panes.overlayPane;
-
- // Hack to make flicker on drag end on mobile webkit less irritating
- if (L.Browser.mobileWebkit) {
- pane.removeChild(root);
- }
-
- L.DomUtil.setPosition(root, min);
- root.setAttribute('width', width);
- root.setAttribute('height', height);
- root.setAttribute('viewBox', [min.x, min.y, width, height].join(' '));
-
- if (L.Browser.mobileWebkit) {
- pane.appendChild(root);
- }
- }
-});
-
-
-/*
- * Popup extension to L.Path (polylines, polygons, circles), adding popup-related methods.
- */
-
-L.Path.include({
-
- bindPopup: function (content, options) {
-
- if (content instanceof L.Popup) {
- this._popup = content;
- } else {
- if (!this._popup || options) {
- this._popup = new L.Popup(options, this);
- }
- this._popup.setContent(content);
- }
-
- if (!this._popupHandlersAdded) {
- this
- .on('click', this._openPopup, this)
- .on('remove', this.closePopup, this);
-
- this._popupHandlersAdded = true;
- }
-
- return this;
- },
-
- unbindPopup: function () {
- if (this._popup) {
- this._popup = null;
- this
- .off('click', this._openPopup)
- .off('remove', this.closePopup);
-
- this._popupHandlersAdded = false;
- }
- return this;
- },
-
- openPopup: function (latlng) {
-
- if (this._popup) {
- // open the popup from one of the path's points if not specified
- latlng = latlng || this._latlng ||
- this._latlngs[Math.floor(this._latlngs.length / 2)];
-
- this._openPopup({latlng: latlng});
- }
-
- return this;
- },
-
- closePopup: function () {
- if (this._popup) {
- this._popup._close();
- }
- return this;
- },
-
- _openPopup: function (e) {
- this._popup.setLatLng(e.latlng);
- this._map.openPopup(this._popup);
- }
-});
-
-
-/*
- * Vector rendering for IE6-8 through VML.
- * Thanks to Dmitry Baranovsky and his Raphael library for inspiration!
- */
-
-L.Browser.vml = !L.Browser.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;
- }
-}());
-
-L.Path = L.Browser.svg || !L.Browser.vml ? L.Path : L.Path.extend({
- statics: {
- VML: true,
- CLIP_PADDING: 0.02
- },
-
- _createElement: (function () {
- try {
- document.namespaces.add('lvml', 'urn:schemas-microsoft-com:vml');
- return function (name) {
- return document.createElement('');
- };
- } catch (e) {
- return function (name) {
- return document.createElement(
- '<' + name + ' xmlns="urn:schemas-microsoft.com:vml" class="lvml">');
- };
- }
- }()),
-
- _initPath: function () {
- var container = this._container = this._createElement('shape');
-
- L.DomUtil.addClass(container, 'leaflet-vml-shape' +
- (this.options.className ? ' ' + this.options.className : ''));
-
- if (this.options.clickable) {
- L.DomUtil.addClass(container, 'leaflet-clickable');
- }
-
- container.coordsize = '1 1';
-
- this._path = this._createElement('path');
- container.appendChild(this._path);
-
- this._map._pathRoot.appendChild(container);
- },
-
- _initStyle: function () {
- this._updateStyle();
- },
-
- _updateStyle: function () {
- var stroke = this._stroke,
- fill = this._fill,
- options = this.options,
- container = this._container;
-
- container.stroked = options.stroke;
- container.filled = options.fill;
-
- if (options.stroke) {
- if (!stroke) {
- stroke = this._stroke = this._createElement('stroke');
- stroke.endcap = 'round';
- container.appendChild(stroke);
- }
- stroke.weight = options.weight + 'px';
- stroke.color = options.color;
- stroke.opacity = options.opacity;
-
- if (options.dashArray) {
- stroke.dashStyle = L.Util.isArray(options.dashArray) ?
- options.dashArray.join(' ') :
- options.dashArray.replace(/( *, *)/g, ' ');
- } else {
- stroke.dashStyle = '';
- }
- if (options.lineCap) {
- stroke.endcap = options.lineCap.replace('butt', 'flat');
- }
- if (options.lineJoin) {
- stroke.joinstyle = options.lineJoin;
- }
-
- } else if (stroke) {
- container.removeChild(stroke);
- this._stroke = null;
- }
-
- if (options.fill) {
- if (!fill) {
- fill = this._fill = this._createElement('fill');
- container.appendChild(fill);
- }
- fill.color = options.fillColor || options.color;
- fill.opacity = options.fillOpacity;
-
- } else if (fill) {
- container.removeChild(fill);
- this._fill = null;
- }
- },
-
- _updatePath: function () {
- var style = this._container.style;
-
- style.display = 'none';
- this._path.v = this.getPathString() + ' '; // the space fixes IE empty path string bug
- style.display = '';
- }
-});
-
-L.Map.include(L.Browser.svg || !L.Browser.vml ? {} : {
- _initPathRoot: function () {
- if (this._pathRoot) { return; }
-
- var root = this._pathRoot = document.createElement('div');
- root.className = 'leaflet-vml-container';
- this._panes.overlayPane.appendChild(root);
-
- this.on('moveend', this._updatePathViewport);
- this._updatePathViewport();
- }
-});
-
-
-/*
- * Vector rendering for all browsers that support canvas.
- */
-
-L.Browser.canvas = (function () {
- return !!document.createElement('canvas').getContext;
-}());
-
-L.Path = (L.Path.SVG && !window.L_PREFER_CANVAS) || !L.Browser.canvas ? L.Path : L.Path.extend({
- statics: {
- //CLIP_PADDING: 0.02, // not sure if there's a need to set it to a small value
- CANVAS: true,
- SVG: false
- },
-
- redraw: function () {
- if (this._map) {
- this.projectLatlngs();
- this._requestUpdate();
- }
- return this;
- },
-
- setStyle: function (style) {
- L.setOptions(this, style);
-
- if (this._map) {
- this._updateStyle();
- this._requestUpdate();
- }
- return this;
- },
-
- onRemove: function (map) {
- map
- .off('viewreset', this.projectLatlngs, this)
- .off('moveend', this._updatePath, this);
-
- if (this.options.clickable) {
- this._map.off('click', this._onClick, this);
- this._map.off('mousemove', this._onMouseMove, this);
- }
-
- this._requestUpdate();
-
- this.fire('remove');
- this._map = null;
- },
-
- _requestUpdate: function () {
- if (this._map && !L.Path._updateRequest) {
- L.Path._updateRequest = L.Util.requestAnimFrame(this._fireMapMoveEnd, this._map);
- }
- },
-
- _fireMapMoveEnd: function () {
- L.Path._updateRequest = null;
- this.fire('moveend');
- },
-
- _initElements: function () {
- this._map._initPathRoot();
- this._ctx = this._map._canvasCtx;
- },
-
- _updateStyle: function () {
- var options = this.options;
-
- if (options.stroke) {
- this._ctx.lineWidth = options.weight;
- this._ctx.strokeStyle = options.color;
- }
- if (options.fill) {
- this._ctx.fillStyle = options.fillColor || options.color;
- }
- },
-
- _drawPath: function () {
- var i, j, len, len2, point, drawMethod;
-
- this._ctx.beginPath();
-
- for (i = 0, len = this._parts.length; i < len; i++) {
- for (j = 0, len2 = this._parts[i].length; j < len2; j++) {
- point = this._parts[i][j];
- drawMethod = (j === 0 ? 'move' : 'line') + 'To';
-
- this._ctx[drawMethod](point.x, point.y);
- }
- // TODO refactor ugly hack
- if (this instanceof L.Polygon) {
- this._ctx.closePath();
- }
- }
- },
-
- _checkIfEmpty: function () {
- return !this._parts.length;
- },
-
- _updatePath: function () {
- if (this._checkIfEmpty()) { return; }
-
- var ctx = this._ctx,
- options = this.options;
-
- this._drawPath();
- ctx.save();
- this._updateStyle();
-
- if (options.fill) {
- ctx.globalAlpha = options.fillOpacity;
- ctx.fill();
- }
-
- if (options.stroke) {
- ctx.globalAlpha = options.opacity;
- ctx.stroke();
- }
-
- ctx.restore();
-
- // TODO optimization: 1 fill/stroke for all features with equal style instead of 1 for each feature
- },
-
- _initEvents: function () {
- if (this.options.clickable) {
- // TODO dblclick
- this._map.on('mousemove', this._onMouseMove, this);
- this._map.on('click', this._onClick, this);
- }
- },
-
- _onClick: function (e) {
- if (this._containsPoint(e.layerPoint)) {
- this.fire('click', e);
- }
- },
-
- _onMouseMove: function (e) {
- if (!this._map || this._map._animatingZoom) { return; }
-
- // TODO don't do on each move
- if (this._containsPoint(e.layerPoint)) {
- this._ctx.canvas.style.cursor = 'pointer';
- this._mouseInside = true;
- this.fire('mouseover', e);
-
- } else if (this._mouseInside) {
- this._ctx.canvas.style.cursor = '';
- this._mouseInside = false;
- this.fire('mouseout', e);
- }
- }
-});
-
-L.Map.include((L.Path.SVG && !window.L_PREFER_CANVAS) || !L.Browser.canvas ? {} : {
- _initPathRoot: function () {
- var root = this._pathRoot,
- ctx;
-
- if (!root) {
- root = this._pathRoot = document.createElement('canvas');
- root.style.position = 'absolute';
- ctx = this._canvasCtx = root.getContext('2d');
-
- ctx.lineCap = 'round';
- ctx.lineJoin = 'round';
-
- this._panes.overlayPane.appendChild(root);
-
- if (this.options.zoomAnimation) {
- this._pathRoot.className = 'leaflet-zoom-animated';
- this.on('zoomanim', this._animatePathZoom);
- this.on('zoomend', this._endPathZoom);
- }
- this.on('moveend', this._updateCanvasViewport);
- this._updateCanvasViewport();
- }
- },
-
- _updateCanvasViewport: function () {
- // don't redraw while zooming. See _updateSvgViewport for more details
- if (this._pathZooming) { return; }
- this._updatePathViewport();
-
- var vp = this._pathViewport,
- min = vp.min,
- size = vp.max.subtract(min),
- root = this._pathRoot;
-
- //TODO check if this works properly on mobile webkit
- L.DomUtil.setPosition(root, min);
- root.width = size.x;
- root.height = size.y;
- root.getContext('2d').translate(-min.x, -min.y);
- }
-});
-
-
-/*
- * L.LineUtil contains different utility functions for line segments
- * and polylines (clipping, simplification, distances, etc.)
- */
-
-/*jshint bitwise:false */ // allow bitwise operations for this file
-
-L.LineUtil = {
-
- // Simplify polyline with vertex reduction and Douglas-Peucker simplification.
- // Improves rendering performance dramatically by lessening the number of points to draw.
-
- simplify: function (/*Point[]*/ points, /*Number*/ tolerance) {
- if (!tolerance || !points.length) {
- return points.slice();
- }
-
- var sqTolerance = tolerance * tolerance;
-
- // stage 1: vertex reduction
- points = this._reducePoints(points, sqTolerance);
-
- // stage 2: Douglas-Peucker simplification
- points = this._simplifyDP(points, sqTolerance);
-
- return points;
- },
-
- // distance from a point to a segment between two points
- pointToSegmentDistance: function (/*Point*/ p, /*Point*/ p1, /*Point*/ p2) {
- return Math.sqrt(this._sqClosestPointOnSegment(p, p1, p2, true));
- },
-
- closestPointOnSegment: function (/*Point*/ p, /*Point*/ p1, /*Point*/ p2) {
- return this._sqClosestPointOnSegment(p, p1, p2);
- },
-
- // Douglas-Peucker simplification, see http://en.wikipedia.org/wiki/Douglas-Peucker_algorithm
- _simplifyDP: function (points, sqTolerance) {
-
- var len = points.length,
- ArrayConstructor = typeof Uint8Array !== undefined + '' ? Uint8Array : Array,
- markers = new ArrayConstructor(len);
-
- markers[0] = markers[len - 1] = 1;
-
- this._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;
- },
-
- _simplifyDPStep: function (points, markers, sqTolerance, first, last) {
-
- var maxSqDist = 0,
- index, i, sqDist;
-
- for (i = first + 1; i <= last - 1; i++) {
- sqDist = this._sqClosestPointOnSegment(points[i], points[first], points[last], true);
-
- if (sqDist > maxSqDist) {
- index = i;
- maxSqDist = sqDist;
- }
- }
-
- if (maxSqDist > sqTolerance) {
- markers[index] = 1;
-
- this._simplifyDPStep(points, markers, sqTolerance, first, index);
- this._simplifyDPStep(points, markers, sqTolerance, index, last);
- }
- },
-
- // reduce points that are too close to each other to a single point
- _reducePoints: function (points, sqTolerance) {
- var reducedPoints = [points[0]];
-
- for (var i = 1, prev = 0, len = points.length; i < len; i++) {
- if (this._sqDist(points[i], points[prev]) > sqTolerance) {
- reducedPoints.push(points[i]);
- prev = i;
- }
- }
- if (prev < len - 1) {
- reducedPoints.push(points[len - 1]);
- }
- return reducedPoints;
- },
-
- // Cohen-Sutherland line clipping algorithm.
- // Used to avoid rendering parts of a polyline that are not currently visible.
-
- clipSegment: function (a, b, bounds, useLastCode) {
- var codeA = useLastCode ? this._lastCode : this._getBitCode(a, bounds),
- codeB = this._getBitCode(b, bounds),
-
- codeOut, p, newCode;
-
- // save 2nd code to avoid calculating it on the next segment
- this._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)
- } else if (codeA & codeB) {
- return false;
- // other cases
- } else {
- codeOut = codeA || codeB;
- p = this._getEdgeIntersection(a, b, codeOut, bounds);
- newCode = this._getBitCode(p, bounds);
-
- if (codeOut === codeA) {
- a = p;
- codeA = newCode;
- } else {
- b = p;
- codeB = newCode;
- }
- }
- }
- },
-
- _getEdgeIntersection: function (a, b, code, bounds) {
- var dx = b.x - a.x,
- dy = b.y - a.y,
- min = bounds.min,
- max = bounds.max;
-
- if (code & 8) { // top
- return new L.Point(a.x + dx * (max.y - a.y) / dy, max.y);
- } else if (code & 4) { // bottom
- return new L.Point(a.x + dx * (min.y - a.y) / dy, min.y);
- } else if (code & 2) { // right
- return new L.Point(max.x, a.y + dy * (max.x - a.x) / dx);
- } else if (code & 1) { // left
- return new L.Point(min.x, a.y + dy * (min.x - a.x) / dx);
- }
- },
-
- _getBitCode: function (/*Point*/ 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)
- _sqDist: function (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
- _sqClosestPointOnSegment: function (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 L.Point(x, y);
- }
-};
-
-
-/*
- * L.Polyline is used to display polylines on a map.
- */
-
-L.Polyline = L.Path.extend({
- initialize: function (latlngs, options) {
- L.Path.prototype.initialize.call(this, options);
-
- this._latlngs = this._convertLatLngs(latlngs);
- },
-
- options: {
- // how much to simplify the polyline on each zoom level
- // more = better performance and smoother look, less = more accurate
- smoothFactor: 1.0,
- noClip: false
- },
-
- projectLatlngs: function () {
- this._originalPoints = [];
-
- for (var i = 0, len = this._latlngs.length; i < len; i++) {
- this._originalPoints[i] = this._map.latLngToLayerPoint(this._latlngs[i]);
- }
- },
-
- getPathString: function () {
- for (var i = 0, len = this._parts.length, str = ''; i < len; i++) {
- str += this._getPathPartStr(this._parts[i]);
- }
- return str;
- },
-
- getLatLngs: function () {
- return this._latlngs;
- },
-
- setLatLngs: function (latlngs) {
- this._latlngs = this._convertLatLngs(latlngs);
- return this.redraw();
- },
-
- addLatLng: function (latlng) {
- this._latlngs.push(L.latLng(latlng));
- return this.redraw();
- },
-
- spliceLatLngs: function () { // (Number index, Number howMany)
- var removed = [].splice.apply(this._latlngs, arguments);
- this._convertLatLngs(this._latlngs, true);
- this.redraw();
- return removed;
- },
-
- closestLayerPoint: function (p) {
- var minDistance = Infinity, parts = this._parts, p1, p2, minPoint = null;
-
- for (var j = 0, jLen = parts.length; j < jLen; j++) {
- var points = parts[j];
- for (var i = 1, len = points.length; i < len; i++) {
- p1 = points[i - 1];
- p2 = points[i];
- var sqDist = L.LineUtil._sqClosestPointOnSegment(p, p1, p2, true);
- if (sqDist < minDistance) {
- minDistance = sqDist;
- minPoint = L.LineUtil._sqClosestPointOnSegment(p, p1, p2);
- }
- }
- }
- if (minPoint) {
- minPoint.distance = Math.sqrt(minDistance);
- }
- return minPoint;
- },
-
- getBounds: function () {
- return new L.LatLngBounds(this.getLatLngs());
- },
-
- _convertLatLngs: function (latlngs, overwrite) {
- var i, len, target = overwrite ? latlngs : [];
-
- for (i = 0, len = latlngs.length; i < len; i++) {
- if (L.Util.isArray(latlngs[i]) && typeof latlngs[i][0] !== 'number') {
- return;
- }
- target[i] = L.latLng(latlngs[i]);
- }
- return target;
- },
-
- _initEvents: function () {
- L.Path.prototype._initEvents.call(this);
- },
-
- _getPathPartStr: function (points) {
- var round = L.Path.VML;
-
- for (var j = 0, len2 = points.length, str = '', p; j < len2; j++) {
- p = points[j];
- if (round) {
- p._round();
- }
- str += (j ? 'L' : 'M') + p.x + ' ' + p.y;
- }
- return str;
- },
-
- _clipPoints: function () {
- var points = this._originalPoints,
- len = points.length,
- i, k, segment;
-
- if (this.options.noClip) {
- this._parts = [points];
- return;
- }
-
- this._parts = [];
-
- var parts = this._parts,
- vp = this._map._pathViewport,
- lu = L.LineUtil;
-
- for (i = 0, k = 0; i < len - 1; i++) {
- segment = lu.clipSegment(points[i], points[i + 1], vp, i);
- 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[i + 1]) || (i === len - 2)) {
- parts[k].push(segment[1]);
- k++;
- }
- }
- },
-
- // simplify each clipped part of the polyline
- _simplifyPoints: function () {
- var parts = this._parts,
- lu = L.LineUtil;
-
- for (var i = 0, len = parts.length; i < len; i++) {
- parts[i] = lu.simplify(parts[i], this.options.smoothFactor);
- }
- },
-
- _updatePath: function () {
- if (!this._map) { return; }
-
- this._clipPoints();
- this._simplifyPoints();
-
- L.Path.prototype._updatePath.call(this);
- }
-});
-
-L.polyline = function (latlngs, options) {
- return new L.Polyline(latlngs, options);
-};
-
-
-/*
- * L.PolyUtil contains utility functions for polygons (clipping, etc.).
- */
-
-/*jshint bitwise:false */ // allow bitwise operations here
-
-L.PolyUtil = {};
-
-/*
- * Sutherland-Hodgeman polygon clipping algorithm.
- * Used to avoid rendering parts of a polygon that are not currently visible.
- */
-L.PolyUtil.clipPolygon = function (points, bounds) {
- var clippedPoints,
- edges = [1, 4, 2, 8],
- i, j, k,
- a, b,
- len, edge, p,
- lu = L.LineUtil;
-
- for (i = 0, len = points.length; i < len; i++) {
- points[i]._code = lu._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 = lu._getEdgeIntersection(b, a, edge, bounds);
- p._code = lu._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 = lu._getEdgeIntersection(b, a, edge, bounds);
- p._code = lu._getBitCode(p, bounds);
- clippedPoints.push(p);
- }
- }
- points = clippedPoints;
- }
-
- return points;
-};
-
-
-/*
- * L.Polygon is used to display polygons on a map.
- */
-
-L.Polygon = L.Polyline.extend({
- options: {
- fill: true
- },
-
- initialize: function (latlngs, options) {
- L.Polyline.prototype.initialize.call(this, latlngs, options);
- this._initWithHoles(latlngs);
- },
-
- _initWithHoles: function (latlngs) {
- var i, len, hole;
- if (latlngs && L.Util.isArray(latlngs[0]) && (typeof latlngs[0][0] !== 'number')) {
- this._latlngs = this._convertLatLngs(latlngs[0]);
- this._holes = latlngs.slice(1);
-
- for (i = 0, len = this._holes.length; i < len; i++) {
- hole = this._holes[i] = this._convertLatLngs(this._holes[i]);
- if (hole[0].equals(hole[hole.length - 1])) {
- hole.pop();
- }
- }
- }
-
- // filter out last point if its equal to the first one
- latlngs = this._latlngs;
-
- if (latlngs.length >= 2 && latlngs[0].equals(latlngs[latlngs.length - 1])) {
- latlngs.pop();
- }
- },
-
- projectLatlngs: function () {
- L.Polyline.prototype.projectLatlngs.call(this);
-
- // project polygon holes points
- // TODO move this logic to Polyline to get rid of duplication
- this._holePoints = [];
-
- if (!this._holes) { return; }
-
- var i, j, len, len2;
-
- for (i = 0, len = this._holes.length; i < len; i++) {
- this._holePoints[i] = [];
-
- for (j = 0, len2 = this._holes[i].length; j < len2; j++) {
- this._holePoints[i][j] = this._map.latLngToLayerPoint(this._holes[i][j]);
- }
- }
- },
-
- setLatLngs: function (latlngs) {
- if (latlngs && L.Util.isArray(latlngs[0]) && (typeof latlngs[0][0] !== 'number')) {
- this._initWithHoles(latlngs);
- return this.redraw();
- } else {
- return L.Polyline.prototype.setLatLngs.call(this, latlngs);
- }
- },
-
- _clipPoints: function () {
- var points = this._originalPoints,
- newParts = [];
-
- this._parts = [points].concat(this._holePoints);
-
- if (this.options.noClip) { return; }
-
- for (var i = 0, len = this._parts.length; i < len; i++) {
- var clipped = L.PolyUtil.clipPolygon(this._parts[i], this._map._pathViewport);
- if (clipped.length) {
- newParts.push(clipped);
- }
- }
-
- this._parts = newParts;
- },
-
- _getPathPartStr: function (points) {
- var str = L.Polyline.prototype._getPathPartStr.call(this, points);
- return str + (L.Browser.svg ? 'z' : 'x');
- }
-});
-
-L.polygon = function (latlngs, options) {
- return new L.Polygon(latlngs, options);
-};
-
-
-/*
- * Contains L.MultiPolyline and L.MultiPolygon layers.
- */
-
-(function () {
- function createMulti(Klass) {
-
- return L.FeatureGroup.extend({
-
- initialize: function (latlngs, options) {
- this._layers = {};
- this._options = options;
- this.setLatLngs(latlngs);
- },
-
- setLatLngs: function (latlngs) {
- var i = 0,
- len = latlngs.length;
-
- this.eachLayer(function (layer) {
- if (i < len) {
- layer.setLatLngs(latlngs[i++]);
- } else {
- this.removeLayer(layer);
- }
- }, this);
-
- while (i < len) {
- this.addLayer(new Klass(latlngs[i++], this._options));
- }
-
- return this;
- },
-
- getLatLngs: function () {
- var latlngs = [];
-
- this.eachLayer(function (layer) {
- latlngs.push(layer.getLatLngs());
- });
-
- return latlngs;
- }
- });
- }
-
- L.MultiPolyline = createMulti(L.Polyline);
- L.MultiPolygon = createMulti(L.Polygon);
-
- L.multiPolyline = function (latlngs, options) {
- return new L.MultiPolyline(latlngs, options);
- };
-
- L.multiPolygon = function (latlngs, options) {
- return new L.MultiPolygon(latlngs, options);
- };
-}());
-
-
-/*
- * L.Rectangle extends Polygon and creates a rectangle when passed a LatLngBounds object.
- */
-
-L.Rectangle = L.Polygon.extend({
- initialize: function (latLngBounds, options) {
- L.Polygon.prototype.initialize.call(this, this._boundsToLatLngs(latLngBounds), options);
- },
-
- setBounds: function (latLngBounds) {
- this.setLatLngs(this._boundsToLatLngs(latLngBounds));
- },
-
- _boundsToLatLngs: function (latLngBounds) {
- latLngBounds = L.latLngBounds(latLngBounds);
- return [
- latLngBounds.getSouthWest(),
- latLngBounds.getNorthWest(),
- latLngBounds.getNorthEast(),
- latLngBounds.getSouthEast()
- ];
- }
-});
-
-L.rectangle = function (latLngBounds, options) {
- return new L.Rectangle(latLngBounds, options);
-};
-
-
-/*
- * L.Circle is a circle overlay (with a certain radius in meters).
- */
-
-L.Circle = L.Path.extend({
- initialize: function (latlng, radius, options) {
- L.Path.prototype.initialize.call(this, options);
-
- this._latlng = L.latLng(latlng);
- this._mRadius = radius;
- },
-
- options: {
- fill: true
- },
-
- setLatLng: function (latlng) {
- this._latlng = L.latLng(latlng);
- return this.redraw();
- },
-
- setRadius: function (radius) {
- this._mRadius = radius;
- return this.redraw();
- },
-
- projectLatlngs: function () {
- var lngRadius = this._getLngRadius(),
- latlng = this._latlng,
- pointLeft = this._map.latLngToLayerPoint([latlng.lat, latlng.lng - lngRadius]);
-
- this._point = this._map.latLngToLayerPoint(latlng);
- this._radius = Math.max(this._point.x - pointLeft.x, 1);
- },
-
- getBounds: function () {
- var lngRadius = this._getLngRadius(),
- latRadius = (this._mRadius / 40075017) * 360,
- latlng = this._latlng;
-
- return new L.LatLngBounds(
- [latlng.lat - latRadius, latlng.lng - lngRadius],
- [latlng.lat + latRadius, latlng.lng + lngRadius]);
- },
-
- getLatLng: function () {
- return this._latlng;
- },
-
- getPathString: function () {
- var p = this._point,
- r = this._radius;
-
- if (this._checkIfEmpty()) {
- return '';
- }
-
- if (L.Browser.svg) {
- return 'M' + p.x + ',' + (p.y - r) +
- 'A' + r + ',' + r + ',0,1,1,' +
- (p.x - 0.1) + ',' + (p.y - r) + ' z';
- } else {
- p._round();
- r = Math.round(r);
- return 'AL ' + p.x + ',' + p.y + ' ' + r + ',' + r + ' 0,' + (65535 * 360);
- }
- },
-
- getRadius: function () {
- return this._mRadius;
- },
-
- // TODO Earth hardcoded, move into projection code!
-
- _getLatRadius: function () {
- return (this._mRadius / 40075017) * 360;
- },
-
- _getLngRadius: function () {
- return this._getLatRadius() / Math.cos(L.LatLng.DEG_TO_RAD * this._latlng.lat);
- },
-
- _checkIfEmpty: function () {
- if (!this._map) {
- return false;
- }
- var vp = this._map._pathViewport,
- r = this._radius,
- p = this._point;
-
- return p.x - r > vp.max.x || p.y - r > vp.max.y ||
- p.x + r < vp.min.x || p.y + r < vp.min.y;
- }
-});
-
-L.circle = function (latlng, radius, options) {
- return new L.Circle(latlng, radius, options);
-};
-
-
-/*
- * L.CircleMarker is a circle overlay with a permanent pixel radius.
- */
-
-L.CircleMarker = L.Circle.extend({
- options: {
- radius: 10,
- weight: 2
- },
-
- initialize: function (latlng, options) {
- L.Circle.prototype.initialize.call(this, latlng, null, options);
- this._radius = this.options.radius;
- },
-
- projectLatlngs: function () {
- this._point = this._map.latLngToLayerPoint(this._latlng);
- },
-
- _updateStyle : function () {
- L.Circle.prototype._updateStyle.call(this);
- this.setRadius(this.options.radius);
- },
-
- setLatLng: function (latlng) {
- L.Circle.prototype.setLatLng.call(this, latlng);
- if (this._popup && this._popup._isOpen) {
- this._popup.setLatLng(latlng);
- }
- return this;
- },
-
- setRadius: function (radius) {
- this.options.radius = this._radius = radius;
- return this.redraw();
- },
-
- getRadius: function () {
- return this._radius;
- }
-});
-
-L.circleMarker = function (latlng, options) {
- return new L.CircleMarker(latlng, options);
-};
-
-
-/*
- * Extends L.Polyline to be able to manually detect clicks on Canvas-rendered polylines.
- */
-
-L.Polyline.include(!L.Path.CANVAS ? {} : {
- _containsPoint: function (p, closed) {
- var i, j, k, len, len2, dist, part,
- w = this.options.weight / 2;
-
- if (L.Browser.touch) {
- w += 10; // polyline click tolerance on touch devices
- }
-
- 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;
- }
-
- dist = L.LineUtil.pointToSegmentDistance(p, part[k], part[j]);
-
- if (dist <= w) {
- return true;
- }
- }
- }
- return false;
- }
-});
-
-
-/*
- * Extends L.Polygon to be able to manually detect clicks on Canvas-rendered polygons.
- */
-
-L.Polygon.include(!L.Path.CANVAS ? {} : {
- _containsPoint: function (p) {
- var inside = false,
- part, p1, p2,
- i, j, k,
- len, len2;
-
- // TODO optimization: check if within bounds first
-
- if (L.Polyline.prototype._containsPoint.call(this, p, true)) {
- // click on polygon border
- return true;
- }
-
- // 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;
- }
- }
- }
-
- return inside;
- }
-});
-
-
-/*
- * Extends L.Circle with Canvas-specific code.
- */
-
-L.Circle.include(!L.Path.CANVAS ? {} : {
- _drawPath: function () {
- var p = this._point;
- this._ctx.beginPath();
- this._ctx.arc(p.x, p.y, this._radius, 0, Math.PI * 2, false);
- },
-
- _containsPoint: function (p) {
- var center = this._point,
- w2 = this.options.stroke ? this.options.weight / 2 : 0;
-
- return (p.distanceTo(center) <= this._radius + w2);
- }
-});
-
-
-/*
- * CircleMarker canvas specific drawing parts.
- */
-
-L.CircleMarker.include(!L.Path.CANVAS ? {} : {
- _updateStyle: function () {
- L.Path.prototype._updateStyle.call(this);
- }
-});
-
-
-/*
- * L.GeoJSON turns any GeoJSON data into a Leaflet layer.
- */
-
-L.GeoJSON = L.FeatureGroup.extend({
-
- initialize: function (geojson, options) {
- L.setOptions(this, options);
-
- this._layers = {};
-
- if (geojson) {
- this.addData(geojson);
- }
- },
-
- addData: function (geojson) {
- var features = L.Util.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(features[i]);
- }
- }
- return this;
- }
-
- var options = this.options;
-
- if (options.filter && !options.filter(geojson)) { return; }
-
- var layer = L.GeoJSON.geometryToLayer(geojson, options.pointToLayer, options.coordsToLatLng, options);
- layer.feature = L.GeoJSON.asFeature(geojson);
-
- layer.defaultOptions = layer.options;
- this.resetStyle(layer);
-
- if (options.onEachFeature) {
- options.onEachFeature(geojson, layer);
- }
-
- return this.addLayer(layer);
- },
-
- resetStyle: function (layer) {
- var style = this.options.style;
- if (style) {
- // reset any custom styles
- L.Util.extend(layer.options, layer.defaultOptions);
-
- this._setLayerStyle(layer, style);
- }
- },
-
- setStyle: function (style) {
- 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);
- }
- }
-});
-
-L.extend(L.GeoJSON, {
- geometryToLayer: function (geojson, pointToLayer, coordsToLatLng, vectorOptions) {
- var geometry = geojson.type === 'Feature' ? geojson.geometry : geojson,
- coords = geometry.coordinates,
- layers = [],
- latlng, latlngs, i, len;
-
- coordsToLatLng = coordsToLatLng || this.coordsToLatLng;
-
- switch (geometry.type) {
- case 'Point':
- latlng = coordsToLatLng(coords);
- return pointToLayer ? pointToLayer(geojson, latlng) : new L.Marker(latlng);
-
- case 'MultiPoint':
- for (i = 0, len = coords.length; i < len; i++) {
- latlng = coordsToLatLng(coords[i]);
- layers.push(pointToLayer ? pointToLayer(geojson, latlng) : new L.Marker(latlng));
- }
- return new L.FeatureGroup(layers);
-
- case 'LineString':
- latlngs = this.coordsToLatLngs(coords, 0, coordsToLatLng);
- return new L.Polyline(latlngs, vectorOptions);
-
- case 'Polygon':
- if (coords.length === 2 && !coords[1].length) {
- throw new Error('Invalid GeoJSON object.');
- }
- latlngs = this.coordsToLatLngs(coords, 1, coordsToLatLng);
- return new L.Polygon(latlngs, vectorOptions);
-
- case 'MultiLineString':
- latlngs = this.coordsToLatLngs(coords, 1, coordsToLatLng);
- return new L.MultiPolyline(latlngs, vectorOptions);
-
- case 'MultiPolygon':
- latlngs = this.coordsToLatLngs(coords, 2, coordsToLatLng);
- return new L.MultiPolygon(latlngs, vectorOptions);
-
- case 'GeometryCollection':
- for (i = 0, len = geometry.geometries.length; i < len; i++) {
-
- layers.push(this.geometryToLayer({
- geometry: geometry.geometries[i],
- type: 'Feature',
- properties: geojson.properties
- }, pointToLayer, coordsToLatLng, vectorOptions));
- }
- return new L.FeatureGroup(layers);
-
- default:
- throw new Error('Invalid GeoJSON object.');
- }
- },
-
- coordsToLatLng: function (coords) { // (Array[, Boolean]) -> LatLng
- return new L.LatLng(coords[1], coords[0], coords[2]);
- },
-
- coordsToLatLngs: function (coords, levelsDeep, coordsToLatLng) { // (Array[, Number, Function]) -> Array
- var latlng, i, len,
- latlngs = [];
-
- for (i = 0, len = coords.length; i < len; i++) {
- latlng = levelsDeep ?
- this.coordsToLatLngs(coords[i], levelsDeep - 1, coordsToLatLng) :
- (coordsToLatLng || this.coordsToLatLng)(coords[i]);
-
- latlngs.push(latlng);
- }
-
- return latlngs;
- },
-
- latLngToCoords: function (latlng) {
- var coords = [latlng.lng, latlng.lat];
-
- if (latlng.alt !== undefined) {
- coords.push(latlng.alt);
- }
- return coords;
- },
-
- latLngsToCoords: function (latLngs) {
- var coords = [];
-
- for (var i = 0, len = latLngs.length; i < len; i++) {
- coords.push(L.GeoJSON.latLngToCoords(latLngs[i]));
- }
-
- return coords;
- },
-
- getFeature: function (layer, newGeometry) {
- return layer.feature ? L.extend({}, layer.feature, {geometry: newGeometry}) : L.GeoJSON.asFeature(newGeometry);
- },
-
- asFeature: function (geoJSON) {
- if (geoJSON.type === 'Feature') {
- return geoJSON;
- }
-
- return {
- type: 'Feature',
- properties: {},
- geometry: geoJSON
- };
- }
-});
-
-var PointToGeoJSON = {
- toGeoJSON: function () {
- return L.GeoJSON.getFeature(this, {
- type: 'Point',
- coordinates: L.GeoJSON.latLngToCoords(this.getLatLng())
- });
- }
-};
-
-L.Marker.include(PointToGeoJSON);
-L.Circle.include(PointToGeoJSON);
-L.CircleMarker.include(PointToGeoJSON);
-
-L.Polyline.include({
- toGeoJSON: function () {
- return L.GeoJSON.getFeature(this, {
- type: 'LineString',
- coordinates: L.GeoJSON.latLngsToCoords(this.getLatLngs())
- });
- }
-});
-
-L.Polygon.include({
- toGeoJSON: function () {
- var coords = [L.GeoJSON.latLngsToCoords(this.getLatLngs())],
- i, len, hole;
-
- coords[0].push(coords[0][0]);
-
- if (this._holes) {
- for (i = 0, len = this._holes.length; i < len; i++) {
- hole = L.GeoJSON.latLngsToCoords(this._holes[i]);
- hole.push(hole[0]);
- coords.push(hole);
- }
- }
-
- return L.GeoJSON.getFeature(this, {
- type: 'Polygon',
- coordinates: coords
- });
- }
-});
-
-(function () {
- function multiToGeoJSON(type) {
- return function () {
- var coords = [];
-
- this.eachLayer(function (layer) {
- coords.push(layer.toGeoJSON().geometry.coordinates);
- });
-
- return L.GeoJSON.getFeature(this, {
- type: type,
- coordinates: coords
- });
- };
- }
-
- L.MultiPolyline.include({toGeoJSON: multiToGeoJSON('MultiLineString')});
- L.MultiPolygon.include({toGeoJSON: multiToGeoJSON('MultiPolygon')});
-
- L.LayerGroup.include({
- toGeoJSON: function () {
-
- var geometry = this.feature && this.feature.geometry,
- jsons = [],
- json;
-
- if (geometry && geometry.type === 'MultiPoint') {
- return multiToGeoJSON('MultiPoint').call(this);
- }
-
- var isGeometryCollection = geometry && geometry.type === 'GeometryCollection';
-
- this.eachLayer(function (layer) {
- if (layer.toGeoJSON) {
- json = layer.toGeoJSON();
- jsons.push(isGeometryCollection ? json.geometry : L.GeoJSON.asFeature(json));
- }
- });
-
- if (isGeometryCollection) {
- return L.GeoJSON.getFeature(this, {
- geometries: jsons,
- type: 'GeometryCollection'
- });
- }
-
- return {
- type: 'FeatureCollection',
- features: jsons
- };
- }
- });
-}());
-
-L.geoJson = function (geojson, options) {
- return new L.GeoJSON(geojson, options);
-};
-
-
-/*
- * L.DomEvent contains functions for working with DOM events.
- */
-
-L.DomEvent = {
- /* inspired by John Resig, Dean Edwards and YUI addEvent implementations */
- addListener: function (obj, type, fn, context) { // (HTMLElement, String, Function[, Object])
-
- var id = L.stamp(fn),
- key = '_leaflet_' + type + id,
- handler, originalHandler, newType;
-
- if (obj[key]) { return this; }
-
- handler = function (e) {
- return fn.call(context || obj, e || L.DomEvent._getEvent());
- };
-
- if (L.Browser.pointer && type.indexOf('touch') === 0) {
- return this.addPointerListener(obj, type, handler, id);
- }
- if (L.Browser.touch && (type === 'dblclick') && this.addDoubleTapListener) {
- this.addDoubleTapListener(obj, handler, id);
- }
-
- if ('addEventListener' in obj) {
-
- if (type === 'mousewheel') {
- obj.addEventListener('DOMMouseScroll', handler, false);
- obj.addEventListener(type, handler, false);
-
- } else if ((type === 'mouseenter') || (type === 'mouseleave')) {
-
- originalHandler = handler;
- newType = (type === 'mouseenter' ? 'mouseover' : 'mouseout');
-
- handler = function (e) {
- if (!L.DomEvent._checkMouse(obj, e)) { return; }
- return originalHandler(e);
- };
-
- obj.addEventListener(newType, handler, false);
-
- } else if (type === 'click' && L.Browser.android) {
- originalHandler = handler;
- handler = function (e) {
- return L.DomEvent._filterClick(e, originalHandler);
- };
-
- obj.addEventListener(type, handler, false);
- } else {
- obj.addEventListener(type, handler, false);
- }
-
- } else if ('attachEvent' in obj) {
- obj.attachEvent('on' + type, handler);
- }
-
- obj[key] = handler;
-
- return this;
- },
-
- removeListener: function (obj, type, fn) { // (HTMLElement, String, Function)
-
- var id = L.stamp(fn),
- key = '_leaflet_' + type + id,
- handler = obj[key];
-
- if (!handler) { return this; }
-
- if (L.Browser.pointer && type.indexOf('touch') === 0) {
- this.removePointerListener(obj, type, id);
- } else if (L.Browser.touch && (type === 'dblclick') && this.removeDoubleTapListener) {
- this.removeDoubleTapListener(obj, id);
-
- } else if ('removeEventListener' in obj) {
-
- if (type === 'mousewheel') {
- obj.removeEventListener('DOMMouseScroll', handler, false);
- obj.removeEventListener(type, handler, false);
-
- } else if ((type === 'mouseenter') || (type === 'mouseleave')) {
- obj.removeEventListener((type === 'mouseenter' ? 'mouseover' : 'mouseout'), handler, false);
- } else {
- obj.removeEventListener(type, handler, false);
- }
- } else if ('detachEvent' in obj) {
- obj.detachEvent('on' + type, handler);
- }
-
- obj[key] = null;
-
- return this;
- },
-
- stopPropagation: function (e) {
-
- if (e.stopPropagation) {
- e.stopPropagation();
- } else {
- e.cancelBubble = true;
- }
- L.DomEvent._skipped(e);
-
- return this;
- },
-
- disableScrollPropagation: function (el) {
- var stop = L.DomEvent.stopPropagation;
-
- return L.DomEvent
- .on(el, 'mousewheel', stop)
- .on(el, 'MozMousePixelScroll', stop);
- },
-
- disableClickPropagation: function (el) {
- var stop = L.DomEvent.stopPropagation;
-
- for (var i = L.Draggable.START.length - 1; i >= 0; i--) {
- L.DomEvent.on(el, L.Draggable.START[i], stop);
- }
-
- return L.DomEvent
- .on(el, 'click', L.DomEvent._fakeStop)
- .on(el, 'dblclick', stop);
- },
-
- preventDefault: function (e) {
-
- if (e.preventDefault) {
- e.preventDefault();
- } else {
- e.returnValue = false;
- }
- return this;
- },
-
- stop: function (e) {
- return L.DomEvent
- .preventDefault(e)
- .stopPropagation(e);
- },
-
- getMousePosition: function (e, container) {
- if (!container) {
- return new L.Point(e.clientX, e.clientY);
- }
-
- var rect = container.getBoundingClientRect();
-
- return new L.Point(
- e.clientX - rect.left - container.clientLeft,
- e.clientY - rect.top - container.clientTop);
- },
-
- getWheelDelta: function (e) {
-
- var delta = 0;
-
- if (e.wheelDelta) {
- delta = e.wheelDelta / 120;
- }
- if (e.detail) {
- delta = -e.detail / 3;
- }
- return delta;
- },
-
- _skipEvents: {},
-
- _fakeStop: function (e) {
- // fakes stopPropagation by setting a special event flag, checked/reset with L.DomEvent._skipped(e)
- L.DomEvent._skipEvents[e.type] = true;
- },
-
- _skipped: function (e) {
- var skipped = this._skipEvents[e.type];
- // reset when checking, as it's only used in map container and propagates outside of the map
- this._skipEvents[e.type] = false;
- return skipped;
- },
-
- // check if element really left/entered the event target (for mouseenter/mouseleave)
- _checkMouse: function (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);
- },
-
- _getEvent: function () { // evil magic for IE
- /*jshint noarg:false */
- var e = window.event;
- if (!e) {
- var caller = arguments.callee.caller;
- while (caller) {
- e = caller['arguments'][0];
- if (e && window.Event === e.constructor) {
- break;
- }
- caller = caller.caller;
- }
- }
- return e;
- },
-
- // this is a horrible workaround for a bug in Android where a single touch triggers two click events
- _filterClick: function (e, handler) {
- var timeStamp = (e.timeStamp || e.originalEvent.timeStamp),
- elapsed = L.DomEvent._lastClick && (timeStamp - L.DomEvent._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)) {
- L.DomEvent.stop(e);
- return;
- }
- L.DomEvent._lastClick = timeStamp;
-
- return handler(e);
- }
-};
-
-L.DomEvent.on = L.DomEvent.addListener;
-L.DomEvent.off = L.DomEvent.removeListener;
-
-
-/*
- * L.Draggable allows you to add dragging capabilities to any element. Supports mobile devices too.
- */
-
-L.Draggable = L.Class.extend({
- includes: L.Mixin.Events,
-
- statics: {
- START: L.Browser.touch ? ['touchstart', 'mousedown'] : ['mousedown'],
- END: {
- mousedown: 'mouseup',
- touchstart: 'touchend',
- pointerdown: 'touchend',
- MSPointerDown: 'touchend'
- },
- MOVE: {
- mousedown: 'mousemove',
- touchstart: 'touchmove',
- pointerdown: 'touchmove',
- MSPointerDown: 'touchmove'
- }
- },
-
- initialize: function (element, dragStartTarget) {
- this._element = element;
- this._dragStartTarget = dragStartTarget || element;
- },
-
- enable: function () {
- if (this._enabled) { return; }
-
- for (var i = L.Draggable.START.length - 1; i >= 0; i--) {
- L.DomEvent.on(this._dragStartTarget, L.Draggable.START[i], this._onDown, this);
- }
-
- this._enabled = true;
- },
-
- disable: function () {
- if (!this._enabled) { return; }
-
- for (var i = L.Draggable.START.length - 1; i >= 0; i--) {
- L.DomEvent.off(this._dragStartTarget, L.Draggable.START[i], this._onDown, this);
- }
-
- this._enabled = false;
- this._moved = false;
- },
-
- _onDown: function (e) {
- this._moved = false;
-
- if (e.shiftKey || ((e.which !== 1) && (e.button !== 1) && !e.touches)) { return; }
-
- L.DomEvent.stopPropagation(e);
-
- if (L.Draggable._disabled) { return; }
-
- L.DomUtil.disableImageDrag();
- L.DomUtil.disableTextSelection();
-
- if (this._moving) { return; }
-
- var first = e.touches ? e.touches[0] : e;
-
- this._startPoint = new L.Point(first.clientX, first.clientY);
- this._startPos = this._newPos = L.DomUtil.getPosition(this._element);
-
- L.DomEvent
- .on(document, L.Draggable.MOVE[e.type], this._onMove, this)
- .on(document, L.Draggable.END[e.type], this._onUp, this);
- },
-
- _onMove: function (e) {
- if (e.touches && e.touches.length > 1) {
- this._moved = true;
- return;
- }
-
- var first = (e.touches && e.touches.length === 1 ? e.touches[0] : e),
- newPoint = new L.Point(first.clientX, first.clientY),
- offset = newPoint.subtract(this._startPoint);
-
- if (!offset.x && !offset.y) { return; }
- if (L.Browser.touch && Math.abs(offset.x) + Math.abs(offset.y) < 3) { return; }
-
- L.DomEvent.preventDefault(e);
-
- if (!this._moved) {
- this.fire('dragstart');
-
- this._moved = true;
- this._startPos = L.DomUtil.getPosition(this._element).subtract(offset);
-
- L.DomUtil.addClass(document.body, 'leaflet-dragging');
- this._lastTarget = e.target || e.srcElement;
- L.DomUtil.addClass(this._lastTarget, 'leaflet-drag-target');
- }
-
- this._newPos = this._startPos.add(offset);
- this._moving = true;
-
- L.Util.cancelAnimFrame(this._animRequest);
- this._animRequest = L.Util.requestAnimFrame(this._updatePosition, this, true, this._dragStartTarget);
- },
-
- _updatePosition: function () {
- this.fire('predrag');
- L.DomUtil.setPosition(this._element, this._newPos);
- this.fire('drag');
- },
-
- _onUp: function () {
- L.DomUtil.removeClass(document.body, 'leaflet-dragging');
-
- if (this._lastTarget) {
- L.DomUtil.removeClass(this._lastTarget, 'leaflet-drag-target');
- this._lastTarget = null;
- }
-
- for (var i in L.Draggable.MOVE) {
- L.DomEvent
- .off(document, L.Draggable.MOVE[i], this._onMove)
- .off(document, L.Draggable.END[i], this._onUp);
- }
-
- L.DomUtil.enableImageDrag();
- L.DomUtil.enableTextSelection();
-
- if (this._moved && this._moving) {
- // ensure drag is not fired after dragend
- L.Util.cancelAnimFrame(this._animRequest);
-
- this.fire('dragend', {
- distance: this._newPos.distanceTo(this._startPos)
- });
- }
-
- this._moving = false;
- }
-});
-
-
-/*
- 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.
-*/
-
-L.Handler = L.Class.extend({
- initialize: function (map) {
- this._map = map;
- },
-
- enable: function () {
- if (this._enabled) { return; }
-
- this._enabled = true;
- this.addHooks();
- },
-
- disable: function () {
- if (!this._enabled) { return; }
-
- this._enabled = false;
- this.removeHooks();
- },
-
- enabled: function () {
- return !!this._enabled;
- }
-});
-
-
-/*
- * L.Handler.MapDrag is used to make the map draggable (with panning inertia), enabled by default.
- */
-
-L.Map.mergeOptions({
- dragging: true,
-
- inertia: !L.Browser.android23,
- inertiaDeceleration: 3400, // px/s^2
- inertiaMaxSpeed: Infinity, // px/s
- inertiaThreshold: L.Browser.touch ? 32 : 18, // ms
- easeLinearity: 0.25,
-
- // TODO refactor, move to CRS
- worldCopyJump: false
-});
-
-L.Map.Drag = L.Handler.extend({
- addHooks: function () {
- if (!this._draggable) {
- var map = this._map;
-
- this._draggable = new L.Draggable(map._mapPane, map._container);
-
- this._draggable.on({
- 'dragstart': this._onDragStart,
- 'drag': this._onDrag,
- 'dragend': this._onDragEnd
- }, this);
-
- if (map.options.worldCopyJump) {
- this._draggable.on('predrag', this._onPreDrag, this);
- map.on('viewreset', this._onViewReset, this);
-
- map.whenReady(this._onViewReset, this);
- }
- }
- this._draggable.enable();
- },
-
- removeHooks: function () {
- this._draggable.disable();
- },
-
- moved: function () {
- return this._draggable && this._draggable._moved;
- },
-
- _onDragStart: function () {
- var map = this._map;
-
- if (map._panAnim) {
- map._panAnim.stop();
- }
-
- map
- .fire('movestart')
- .fire('dragstart');
-
- if (map.options.inertia) {
- this._positions = [];
- this._times = [];
- }
- },
-
- _onDrag: function () {
- if (this._map.options.inertia) {
- var time = this._lastTime = +new Date(),
- pos = this._lastPos = this._draggable._newPos;
-
- this._positions.push(pos);
- this._times.push(time);
-
- if (time - this._times[0] > 200) {
- this._positions.shift();
- this._times.shift();
- }
- }
-
- this._map
- .fire('move')
- .fire('drag');
- },
-
- _onViewReset: function () {
- // TODO fix hardcoded Earth values
- var pxCenter = this._map.getSize()._divideBy(2),
- pxWorldCenter = this._map.latLngToLayerPoint([0, 0]);
-
- this._initialWorldOffset = pxWorldCenter.subtract(pxCenter).x;
- this._worldWidth = this._map.project([0, 180]).x;
- },
-
- _onPreDrag: function () {
- // TODO refactor to be able to adjust map pane position after zoom
- var worldWidth = this._worldWidth,
- halfWidth = Math.round(worldWidth / 2),
- dx = this._initialWorldOffset,
- x = this._draggable._newPos.x,
- newX1 = (x - halfWidth + dx) % worldWidth + halfWidth - dx,
- newX2 = (x + halfWidth + dx) % worldWidth - halfWidth - dx,
- newX = Math.abs(newX1 + dx) < Math.abs(newX2 + dx) ? newX1 : newX2;
-
- this._draggable._newPos.x = newX;
- },
-
- _onDragEnd: function (e) {
- var map = this._map,
- options = map.options,
- delay = +new Date() - this._lastTime,
-
- noInertia = !options.inertia || delay > options.inertiaThreshold || !this._positions[0];
-
- map.fire('dragend', e);
-
- if (noInertia) {
- map.fire('moveend');
-
- } else {
-
- var direction = this._lastPos.subtract(this._positions[0]),
- duration = (this._lastTime + delay - this._times[0]) / 1000,
- ease = options.easeLinearity,
-
- speedVector = direction.multiplyBy(ease / duration),
- speed = speedVector.distanceTo([0, 0]),
-
- limitedSpeed = Math.min(options.inertiaMaxSpeed, speed),
- limitedSpeedVector = speedVector.multiplyBy(limitedSpeed / speed),
-
- decelerationDuration = limitedSpeed / (options.inertiaDeceleration * ease),
- offset = limitedSpeedVector.multiplyBy(-decelerationDuration / 2).round();
-
- if (!offset.x || !offset.y) {
- map.fire('moveend');
-
- } else {
- offset = map._limitOffset(offset, map.options.maxBounds);
-
- L.Util.requestAnimFrame(function () {
- map.panBy(offset, {
- duration: decelerationDuration,
- easeLinearity: ease,
- noMoveStart: true
- });
- });
- }
- }
- }
-});
-
-L.Map.addInitHook('addHandler', 'dragging', L.Map.Drag);
-
-
-/*
- * L.Handler.DoubleClickZoom is used to handle double-click zoom on the map, enabled by default.
- */
-
-L.Map.mergeOptions({
- doubleClickZoom: true
-});
-
-L.Map.DoubleClickZoom = L.Handler.extend({
- addHooks: function () {
- this._map.on('dblclick', this._onDoubleClick, this);
- },
-
- removeHooks: function () {
- this._map.off('dblclick', this._onDoubleClick, this);
- },
-
- _onDoubleClick: function (e) {
- var map = this._map,
- zoom = map.getZoom() + (e.originalEvent.shiftKey ? -1 : 1);
-
- if (map.options.doubleClickZoom === 'center') {
- map.setZoom(zoom);
- } else {
- map.setZoomAround(e.containerPoint, zoom);
- }
- }
-});
-
-L.Map.addInitHook('addHandler', 'doubleClickZoom', L.Map.DoubleClickZoom);
-
-
-/*
- * L.Handler.ScrollWheelZoom is used by L.Map to enable mouse scroll wheel zoom on the map.
- */
-
-L.Map.mergeOptions({
- scrollWheelZoom: true
-});
-
-L.Map.ScrollWheelZoom = L.Handler.extend({
- addHooks: function () {
- L.DomEvent.on(this._map._container, 'mousewheel', this._onWheelScroll, this);
- L.DomEvent.on(this._map._container, 'MozMousePixelScroll', L.DomEvent.preventDefault);
- this._delta = 0;
- },
-
- removeHooks: function () {
- L.DomEvent.off(this._map._container, 'mousewheel', this._onWheelScroll);
- L.DomEvent.off(this._map._container, 'MozMousePixelScroll', L.DomEvent.preventDefault);
- },
-
- _onWheelScroll: function (e) {
- var delta = L.DomEvent.getWheelDelta(e);
-
- this._delta += delta;
- this._lastMousePos = this._map.mouseEventToContainerPoint(e);
-
- if (!this._startTime) {
- this._startTime = +new Date();
- }
-
- var left = Math.max(40 - (+new Date() - this._startTime), 0);
-
- clearTimeout(this._timer);
- this._timer = setTimeout(L.bind(this._performZoom, this), left);
-
- L.DomEvent.preventDefault(e);
- L.DomEvent.stopPropagation(e);
- },
-
- _performZoom: function () {
- var map = this._map,
- delta = this._delta,
- zoom = map.getZoom();
-
- delta = delta > 0 ? Math.ceil(delta) : Math.floor(delta);
- delta = Math.max(Math.min(delta, 4), -4);
- delta = map._limitZoom(zoom + delta) - zoom;
-
- this._delta = 0;
- this._startTime = null;
-
- if (!delta) { return; }
-
- if (map.options.scrollWheelZoom === 'center') {
- map.setZoom(zoom + delta);
- } else {
- map.setZoomAround(this._lastMousePos, zoom + delta);
- }
- }
-});
-
-L.Map.addInitHook('addHandler', 'scrollWheelZoom', L.Map.ScrollWheelZoom);
-
-
-/*
- * Extends the event handling code with double tap support for mobile browsers.
- */
-
-L.extend(L.DomEvent, {
-
- _touchstart: L.Browser.msPointer ? 'MSPointerDown' : L.Browser.pointer ? 'pointerdown' : 'touchstart',
- _touchend: L.Browser.msPointer ? 'MSPointerUp' : L.Browser.pointer ? 'pointerup' : 'touchend',
-
- // inspired by Zepto touch code by Thomas Fuchs
- addDoubleTapListener: function (obj, handler, id) {
- var last,
- doubleTap = false,
- delay = 250,
- touch,
- pre = '_leaflet_',
- touchstart = this._touchstart,
- touchend = this._touchend,
- trackedTouches = [];
-
- function onTouchStart(e) {
- var count;
-
- if (L.Browser.pointer) {
- trackedTouches.push(e.pointerId);
- count = trackedTouches.length;
- } else {
- count = e.touches.length;
- }
- if (count > 1) {
- return;
- }
-
- var now = Date.now(),
- delta = now - (last || now);
-
- touch = e.touches ? e.touches[0] : e;
- doubleTap = (delta > 0 && delta <= delay);
- last = now;
- }
-
- function onTouchEnd(e) {
- if (L.Browser.pointer) {
- var idx = trackedTouches.indexOf(e.pointerId);
- if (idx === -1) {
- return;
- }
- trackedTouches.splice(idx, 1);
- }
-
- if (doubleTap) {
- if (L.Browser.pointer) {
- // work around .type being readonly with MSPointer* events
- var newTouch = { },
- prop;
-
- // jshint forin:false
- for (var i in touch) {
- prop = touch[i];
- if (typeof prop === 'function') {
- newTouch[i] = prop.bind(touch);
- } else {
- newTouch[i] = prop;
- }
- }
- touch = newTouch;
- }
- touch.type = 'dblclick';
- handler(touch);
- last = null;
- }
- }
- obj[pre + touchstart + id] = onTouchStart;
- obj[pre + touchend + id] = onTouchEnd;
-
- // on pointer we need to listen on the document, otherwise a drag starting on the map and moving off screen
- // will not come through to us, so we will lose track of how many touches are ongoing
- var endElement = L.Browser.pointer ? document.documentElement : obj;
-
- obj.addEventListener(touchstart, onTouchStart, false);
- endElement.addEventListener(touchend, onTouchEnd, false);
-
- if (L.Browser.pointer) {
- endElement.addEventListener(L.DomEvent.POINTER_CANCEL, onTouchEnd, false);
- }
-
- return this;
- },
-
- removeDoubleTapListener: function (obj, id) {
- var pre = '_leaflet_';
-
- obj.removeEventListener(this._touchstart, obj[pre + this._touchstart + id], false);
- (L.Browser.pointer ? document.documentElement : obj).removeEventListener(
- this._touchend, obj[pre + this._touchend + id], false);
-
- if (L.Browser.pointer) {
- document.documentElement.removeEventListener(L.DomEvent.POINTER_CANCEL, obj[pre + this._touchend + id],
- false);
- }
-
- return this;
- }
-});
-
-
-/*
- * Extends L.DomEvent to provide touch support for Internet Explorer and Windows-based devices.
- */
-
-L.extend(L.DomEvent, {
-
- //static
- POINTER_DOWN: L.Browser.msPointer ? 'MSPointerDown' : 'pointerdown',
- POINTER_MOVE: L.Browser.msPointer ? 'MSPointerMove' : 'pointermove',
- POINTER_UP: L.Browser.msPointer ? 'MSPointerUp' : 'pointerup',
- POINTER_CANCEL: L.Browser.msPointer ? 'MSPointerCancel' : 'pointercancel',
-
- _pointers: [],
- _pointerDocumentListener: false,
-
- // Provides a touch events wrapper for (ms)pointer events.
- // Based on changes by veproza https://github.com/CloudMade/Leaflet/pull/1019
- //ref http://www.w3.org/TR/pointerevents/ https://www.w3.org/Bugs/Public/show_bug.cgi?id=22890
-
- addPointerListener: function (obj, type, handler, id) {
-
- switch (type) {
- case 'touchstart':
- return this.addPointerListenerStart(obj, type, handler, id);
- case 'touchend':
- return this.addPointerListenerEnd(obj, type, handler, id);
- case 'touchmove':
- return this.addPointerListenerMove(obj, type, handler, id);
- default:
- throw 'Unknown touch event type';
- }
- },
-
- addPointerListenerStart: function (obj, type, handler, id) {
- var pre = '_leaflet_',
- pointers = this._pointers;
-
- var cb = function (e) {
-
- L.DomEvent.preventDefault(e);
-
- var alreadyInArray = false;
- for (var i = 0; i < pointers.length; i++) {
- if (pointers[i].pointerId === e.pointerId) {
- alreadyInArray = true;
- break;
- }
- }
- if (!alreadyInArray) {
- pointers.push(e);
- }
-
- e.touches = pointers.slice();
- e.changedTouches = [e];
-
- handler(e);
- };
-
- obj[pre + 'touchstart' + id] = cb;
- obj.addEventListener(this.POINTER_DOWN, cb, false);
-
- // need to also listen for end events to keep the _pointers list accurate
- // this needs to be on the body and never go away
- if (!this._pointerDocumentListener) {
- var internalCb = function (e) {
- for (var i = 0; i < pointers.length; i++) {
- if (pointers[i].pointerId === e.pointerId) {
- pointers.splice(i, 1);
- break;
- }
- }
- };
- //We listen on the documentElement as any drags that end by moving the touch off the screen get fired there
- document.documentElement.addEventListener(this.POINTER_UP, internalCb, false);
- document.documentElement.addEventListener(this.POINTER_CANCEL, internalCb, false);
-
- this._pointerDocumentListener = true;
- }
-
- return this;
- },
-
- addPointerListenerMove: function (obj, type, handler, id) {
- var pre = '_leaflet_',
- touches = this._pointers;
-
- function cb(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; }
-
- for (var i = 0; i < touches.length; i++) {
- if (touches[i].pointerId === e.pointerId) {
- touches[i] = e;
- break;
- }
- }
-
- e.touches = touches.slice();
- e.changedTouches = [e];
-
- handler(e);
- }
-
- obj[pre + 'touchmove' + id] = cb;
- obj.addEventListener(this.POINTER_MOVE, cb, false);
-
- return this;
- },
-
- addPointerListenerEnd: function (obj, type, handler, id) {
- var pre = '_leaflet_',
- touches = this._pointers;
-
- var cb = function (e) {
- for (var i = 0; i < touches.length; i++) {
- if (touches[i].pointerId === e.pointerId) {
- touches.splice(i, 1);
- break;
- }
- }
-
- e.touches = touches.slice();
- e.changedTouches = [e];
-
- handler(e);
- };
-
- obj[pre + 'touchend' + id] = cb;
- obj.addEventListener(this.POINTER_UP, cb, false);
- obj.addEventListener(this.POINTER_CANCEL, cb, false);
-
- return this;
- },
-
- removePointerListener: function (obj, type, id) {
- var pre = '_leaflet_',
- cb = obj[pre + type + id];
-
- switch (type) {
- case 'touchstart':
- obj.removeEventListener(this.POINTER_DOWN, cb, false);
- break;
- case 'touchmove':
- obj.removeEventListener(this.POINTER_MOVE, cb, false);
- break;
- case 'touchend':
- obj.removeEventListener(this.POINTER_UP, cb, false);
- obj.removeEventListener(this.POINTER_CANCEL, cb, false);
- break;
- }
-
- return this;
- }
-});
-
-
-/*
- * L.Handler.TouchZoom is used by L.Map to add pinch zoom on supported mobile browsers.
- */
-
-L.Map.mergeOptions({
- touchZoom: L.Browser.touch && !L.Browser.android23,
- bounceAtZoomLimits: true
-});
-
-L.Map.TouchZoom = L.Handler.extend({
- addHooks: function () {
- L.DomEvent.on(this._map._container, 'touchstart', this._onTouchStart, this);
- },
-
- removeHooks: function () {
- L.DomEvent.off(this._map._container, 'touchstart', this._onTouchStart, this);
- },
-
- _onTouchStart: function (e) {
- var map = this._map;
-
- if (!e.touches || e.touches.length !== 2 || map._animatingZoom || this._zooming) { return; }
-
- var p1 = map.mouseEventToLayerPoint(e.touches[0]),
- p2 = map.mouseEventToLayerPoint(e.touches[1]),
- viewCenter = map._getCenterLayerPoint();
-
- this._startCenter = p1.add(p2)._divideBy(2);
- this._startDist = p1.distanceTo(p2);
-
- this._moved = false;
- this._zooming = true;
-
- this._centerOffset = viewCenter.subtract(this._startCenter);
-
- if (map._panAnim) {
- map._panAnim.stop();
- }
-
- L.DomEvent
- .on(document, 'touchmove', this._onTouchMove, this)
- .on(document, 'touchend', this._onTouchEnd, this);
-
- L.DomEvent.preventDefault(e);
- },
-
- _onTouchMove: function (e) {
- var map = this._map;
-
- if (!e.touches || e.touches.length !== 2 || !this._zooming) { return; }
-
- var p1 = map.mouseEventToLayerPoint(e.touches[0]),
- p2 = map.mouseEventToLayerPoint(e.touches[1]);
-
- this._scale = p1.distanceTo(p2) / this._startDist;
- this._delta = p1._add(p2)._divideBy(2)._subtract(this._startCenter);
-
- if (this._scale === 1) { return; }
-
- if (!map.options.bounceAtZoomLimits) {
- if ((map.getZoom() === map.getMinZoom() && this._scale < 1) ||
- (map.getZoom() === map.getMaxZoom() && this._scale > 1)) { return; }
- }
-
- if (!this._moved) {
- L.DomUtil.addClass(map._mapPane, 'leaflet-touching');
-
- map
- .fire('movestart')
- .fire('zoomstart');
-
- this._moved = true;
- }
-
- L.Util.cancelAnimFrame(this._animRequest);
- this._animRequest = L.Util.requestAnimFrame(
- this._updateOnMove, this, true, this._map._container);
-
- L.DomEvent.preventDefault(e);
- },
-
- _updateOnMove: function () {
- var map = this._map,
- origin = this._getScaleOrigin(),
- center = map.layerPointToLatLng(origin),
- zoom = map.getScaleZoom(this._scale);
-
- map._animateZoom(center, zoom, this._startCenter, this._scale, this._delta, false, true);
- },
-
- _onTouchEnd: function () {
- if (!this._moved || !this._zooming) {
- this._zooming = false;
- return;
- }
-
- var map = this._map;
-
- this._zooming = false;
- L.DomUtil.removeClass(map._mapPane, 'leaflet-touching');
- L.Util.cancelAnimFrame(this._animRequest);
-
- L.DomEvent
- .off(document, 'touchmove', this._onTouchMove)
- .off(document, 'touchend', this._onTouchEnd);
-
- var origin = this._getScaleOrigin(),
- center = map.layerPointToLatLng(origin),
-
- oldZoom = map.getZoom(),
- floatZoomDelta = map.getScaleZoom(this._scale) - oldZoom,
- roundZoomDelta = (floatZoomDelta > 0 ?
- Math.ceil(floatZoomDelta) : Math.floor(floatZoomDelta)),
-
- zoom = map._limitZoom(oldZoom + roundZoomDelta),
- scale = map.getZoomScale(zoom) / this._scale;
-
- map._animateZoom(center, zoom, origin, scale);
- },
-
- _getScaleOrigin: function () {
- var centerOffset = this._centerOffset.subtract(this._delta).divideBy(this._scale);
- return this._startCenter.add(centerOffset);
- }
-});
-
-L.Map.addInitHook('addHandler', 'touchZoom', L.Map.TouchZoom);
-
-
-/*
- * L.Map.Tap is used to enable mobile hacks like quick taps and long hold.
- */
-
-L.Map.mergeOptions({
- tap: true,
- tapTolerance: 15
-});
-
-L.Map.Tap = L.Handler.extend({
- addHooks: function () {
- L.DomEvent.on(this._map._container, 'touchstart', this._onDown, this);
- },
-
- removeHooks: function () {
- L.DomEvent.off(this._map._container, 'touchstart', this._onDown, this);
- },
-
- _onDown: function (e) {
- if (!e.touches) { return; }
-
- L.DomEvent.preventDefault(e);
-
- this._fireClick = true;
-
- // don't simulate click or track longpress if more than 1 touch
- if (e.touches.length > 1) {
- this._fireClick = false;
- clearTimeout(this._holdTimeout);
- return;
- }
-
- var first = e.touches[0],
- el = first.target;
-
- this._startPos = this._newPos = new L.Point(first.clientX, first.clientY);
-
- // if touching a link, highlight it
- if (el.tagName && el.tagName.toLowerCase() === 'a') {
- L.DomUtil.addClass(el, 'leaflet-active');
- }
-
- // simulate long hold but setting a timeout
- this._holdTimeout = setTimeout(L.bind(function () {
- if (this._isTapValid()) {
- this._fireClick = false;
- this._onUp();
- this._simulateEvent('contextmenu', first);
- }
- }, this), 1000);
-
- L.DomEvent
- .on(document, 'touchmove', this._onMove, this)
- .on(document, 'touchend', this._onUp, this);
- },
-
- _onUp: function (e) {
- clearTimeout(this._holdTimeout);
-
- L.DomEvent
- .off(document, 'touchmove', this._onMove, this)
- .off(document, 'touchend', this._onUp, this);
-
- if (this._fireClick && e && e.changedTouches) {
-
- var first = e.changedTouches[0],
- el = first.target;
-
- if (el && el.tagName && el.tagName.toLowerCase() === 'a') {
- L.DomUtil.removeClass(el, 'leaflet-active');
- }
-
- // simulate click if the touch didn't move too much
- if (this._isTapValid()) {
- this._simulateEvent('click', first);
- }
- }
- },
-
- _isTapValid: function () {
- return this._newPos.distanceTo(this._startPos) <= this._map.options.tapTolerance;
- },
-
- _onMove: function (e) {
- var first = e.touches[0];
- this._newPos = new L.Point(first.clientX, first.clientY);
- },
-
- _simulateEvent: function (type, e) {
- var simulatedEvent = document.createEvent('MouseEvents');
-
- simulatedEvent._simulated = true;
- e.target._simulatedClick = true;
-
- simulatedEvent.initMouseEvent(
- type, true, true, window, 1,
- e.screenX, e.screenY,
- e.clientX, e.clientY,
- false, false, false, false, 0, null);
-
- e.target.dispatchEvent(simulatedEvent);
- }
-});
-
-if (L.Browser.touch && !L.Browser.pointer) {
- L.Map.addInitHook('addHandler', 'tap', L.Map.Tap);
-}
-
-
-/*
- * L.Handler.ShiftDragZoom is used to add shift-drag zoom interaction to the map
- * (zoom to a selected bounding box), enabled by default.
- */
-
-L.Map.mergeOptions({
- boxZoom: true
-});
-
-L.Map.BoxZoom = L.Handler.extend({
- initialize: function (map) {
- this._map = map;
- this._container = map._container;
- this._pane = map._panes.overlayPane;
- this._moved = false;
- },
-
- addHooks: function () {
- L.DomEvent.on(this._container, 'mousedown', this._onMouseDown, this);
- },
-
- removeHooks: function () {
- L.DomEvent.off(this._container, 'mousedown', this._onMouseDown);
- this._moved = false;
- },
-
- moved: function () {
- return this._moved;
- },
-
- _onMouseDown: function (e) {
- this._moved = false;
-
- if (!e.shiftKey || ((e.which !== 1) && (e.button !== 1))) { return false; }
-
- L.DomUtil.disableTextSelection();
- L.DomUtil.disableImageDrag();
-
- this._startLayerPoint = this._map.mouseEventToLayerPoint(e);
-
- L.DomEvent
- .on(document, 'mousemove', this._onMouseMove, this)
- .on(document, 'mouseup', this._onMouseUp, this)
- .on(document, 'keydown', this._onKeyDown, this);
- },
-
- _onMouseMove: function (e) {
- if (!this._moved) {
- this._box = L.DomUtil.create('div', 'leaflet-zoom-box', this._pane);
- L.DomUtil.setPosition(this._box, this._startLayerPoint);
-
- //TODO refactor: move cursor to styles
- this._container.style.cursor = 'crosshair';
- this._map.fire('boxzoomstart');
- }
-
- var startPoint = this._startLayerPoint,
- box = this._box,
-
- layerPoint = this._map.mouseEventToLayerPoint(e),
- offset = layerPoint.subtract(startPoint),
-
- newPos = new L.Point(
- Math.min(layerPoint.x, startPoint.x),
- Math.min(layerPoint.y, startPoint.y));
-
- L.DomUtil.setPosition(box, newPos);
-
- this._moved = true;
-
- // TODO refactor: remove hardcoded 4 pixels
- box.style.width = (Math.max(0, Math.abs(offset.x) - 4)) + 'px';
- box.style.height = (Math.max(0, Math.abs(offset.y) - 4)) + 'px';
- },
-
- _finish: function () {
- if (this._moved) {
- this._pane.removeChild(this._box);
- this._container.style.cursor = '';
- }
-
- L.DomUtil.enableTextSelection();
- L.DomUtil.enableImageDrag();
-
- L.DomEvent
- .off(document, 'mousemove', this._onMouseMove)
- .off(document, 'mouseup', this._onMouseUp)
- .off(document, 'keydown', this._onKeyDown);
- },
-
- _onMouseUp: function (e) {
-
- this._finish();
-
- var map = this._map,
- layerPoint = map.mouseEventToLayerPoint(e);
-
- if (this._startLayerPoint.equals(layerPoint)) { return; }
-
- var bounds = new L.LatLngBounds(
- map.layerPointToLatLng(this._startLayerPoint),
- map.layerPointToLatLng(layerPoint));
-
- map.fitBounds(bounds);
-
- map.fire('boxzoomend', {
- boxZoomBounds: bounds
- });
- },
-
- _onKeyDown: function (e) {
- if (e.keyCode === 27) {
- this._finish();
- }
- }
-});
-
-L.Map.addInitHook('addHandler', 'boxZoom', L.Map.BoxZoom);
-
-
-/*
- * L.Map.Keyboard is handling keyboard interaction with the map, enabled by default.
- */
-
-L.Map.mergeOptions({
- keyboard: true,
- keyboardPanOffset: 80,
- keyboardZoomOffset: 1
-});
-
-L.Map.Keyboard = L.Handler.extend({
-
- keyCodes: {
- left: [37],
- right: [39],
- down: [40],
- up: [38],
- zoomIn: [187, 107, 61, 171],
- zoomOut: [189, 109, 173]
- },
-
- initialize: function (map) {
- this._map = map;
-
- this._setPanOffset(map.options.keyboardPanOffset);
- this._setZoomOffset(map.options.keyboardZoomOffset);
- },
-
- addHooks: function () {
- var container = this._map._container;
-
- // make the container focusable by tabbing
- if (container.tabIndex === -1) {
- container.tabIndex = '0';
- }
-
- L.DomEvent
- .on(container, 'focus', this._onFocus, this)
- .on(container, 'blur', this._onBlur, this)
- .on(container, 'mousedown', this._onMouseDown, this);
-
- this._map
- .on('focus', this._addHooks, this)
- .on('blur', this._removeHooks, this);
- },
-
- removeHooks: function () {
- this._removeHooks();
-
- var container = this._map._container;
-
- L.DomEvent
- .off(container, 'focus', this._onFocus, this)
- .off(container, 'blur', this._onBlur, this)
- .off(container, 'mousedown', this._onMouseDown, this);
-
- this._map
- .off('focus', this._addHooks, this)
- .off('blur', this._removeHooks, this);
- },
-
- _onMouseDown: function () {
- if (this._focused) { return; }
-
- var body = document.body,
- docEl = document.documentElement,
- top = body.scrollTop || docEl.scrollTop,
- left = body.scrollLeft || docEl.scrollLeft;
-
- this._map._container.focus();
-
- window.scrollTo(left, top);
- },
-
- _onFocus: function () {
- this._focused = true;
- this._map.fire('focus');
- },
-
- _onBlur: function () {
- this._focused = false;
- this._map.fire('blur');
- },
-
- _setPanOffset: function (pan) {
- var keys = this._panKeys = {},
- codes = this.keyCodes,
- i, len;
-
- for (i = 0, len = codes.left.length; i < len; i++) {
- keys[codes.left[i]] = [-1 * pan, 0];
- }
- for (i = 0, len = codes.right.length; i < len; i++) {
- keys[codes.right[i]] = [pan, 0];
- }
- for (i = 0, len = codes.down.length; i < len; i++) {
- keys[codes.down[i]] = [0, pan];
- }
- for (i = 0, len = codes.up.length; i < len; i++) {
- keys[codes.up[i]] = [0, -1 * pan];
- }
- },
-
- _setZoomOffset: function (zoom) {
- var keys = this._zoomKeys = {},
- codes = this.keyCodes,
- i, len;
-
- for (i = 0, len = codes.zoomIn.length; i < len; i++) {
- keys[codes.zoomIn[i]] = zoom;
- }
- for (i = 0, len = codes.zoomOut.length; i < len; i++) {
- keys[codes.zoomOut[i]] = -zoom;
- }
- },
-
- _addHooks: function () {
- L.DomEvent.on(document, 'keydown', this._onKeyDown, this);
- },
-
- _removeHooks: function () {
- L.DomEvent.off(document, 'keydown', this._onKeyDown, this);
- },
-
- _onKeyDown: function (e) {
- var key = e.keyCode,
- map = this._map;
-
- if (key in this._panKeys) {
-
- if (map._panAnim && map._panAnim._inProgress) { return; }
-
- map.panBy(this._panKeys[key]);
-
- if (map.options.maxBounds) {
- map.panInsideBounds(map.options.maxBounds);
- }
-
- } else if (key in this._zoomKeys) {
- map.setZoom(map.getZoom() + this._zoomKeys[key]);
-
- } else {
- return;
- }
-
- L.DomEvent.stop(e);
- }
-});
-
-L.Map.addInitHook('addHandler', 'keyboard', L.Map.Keyboard);
-
-
-/*
- * L.Handler.MarkerDrag is used internally by L.Marker to make the markers draggable.
- */
-
-L.Handler.MarkerDrag = L.Handler.extend({
- initialize: function (marker) {
- this._marker = marker;
- },
-
- addHooks: function () {
- var icon = this._marker._icon;
- if (!this._draggable) {
- this._draggable = new L.Draggable(icon, icon);
- }
-
- this._draggable
- .on('dragstart', this._onDragStart, this)
- .on('drag', this._onDrag, this)
- .on('dragend', this._onDragEnd, this);
- this._draggable.enable();
- L.DomUtil.addClass(this._marker._icon, 'leaflet-marker-draggable');
- },
-
- removeHooks: function () {
- this._draggable
- .off('dragstart', this._onDragStart, this)
- .off('drag', this._onDrag, this)
- .off('dragend', this._onDragEnd, this);
-
- this._draggable.disable();
- L.DomUtil.removeClass(this._marker._icon, 'leaflet-marker-draggable');
- },
-
- moved: function () {
- return this._draggable && this._draggable._moved;
- },
-
- _onDragStart: function () {
- this._marker
- .closePopup()
- .fire('movestart')
- .fire('dragstart');
- },
-
- _onDrag: function () {
- var marker = this._marker,
- shadow = marker._shadow,
- iconPos = L.DomUtil.getPosition(marker._icon),
- latlng = marker._map.layerPointToLatLng(iconPos);
-
- // update shadow position
- if (shadow) {
- L.DomUtil.setPosition(shadow, iconPos);
- }
-
- marker._latlng = latlng;
-
- marker
- .fire('move', {latlng: latlng})
- .fire('drag');
- },
-
- _onDragEnd: function (e) {
- this._marker
- .fire('moveend')
- .fire('dragend', e);
- }
-});
-
-
-/*
- * L.Control is a base class for implementing map controls. Handles positioning.
- * All other controls extend from this class.
- */
-
-L.Control = L.Class.extend({
- options: {
- position: 'topright'
- },
-
- initialize: function (options) {
- L.setOptions(this, options);
- },
-
- getPosition: function () {
- return this.options.position;
- },
-
- setPosition: function (position) {
- var map = this._map;
-
- if (map) {
- map.removeControl(this);
- }
-
- this.options.position = position;
-
- if (map) {
- map.addControl(this);
- }
-
- return this;
- },
-
- getContainer: function () {
- return this._container;
- },
-
- addTo: function (map) {
- this._map = map;
-
- var container = this._container = this.onAdd(map),
- pos = this.getPosition(),
- corner = map._controlCorners[pos];
-
- L.DomUtil.addClass(container, 'leaflet-control');
-
- if (pos.indexOf('bottom') !== -1) {
- corner.insertBefore(container, corner.firstChild);
- } else {
- corner.appendChild(container);
- }
-
- return this;
- },
-
- removeFrom: function (map) {
- var pos = this.getPosition(),
- corner = map._controlCorners[pos];
-
- corner.removeChild(this._container);
- this._map = null;
-
- if (this.onRemove) {
- this.onRemove(map);
- }
-
- return this;
- },
-
- _refocusOnMap: function () {
- if (this._map) {
- this._map.getContainer().focus();
- }
- }
-});
-
-L.control = function (options) {
- return new L.Control(options);
-};
-
-
-// adds control-related methods to L.Map
-
-L.Map.include({
- addControl: function (control) {
- control.addTo(this);
- return this;
- },
-
- removeControl: function (control) {
- control.removeFrom(this);
- return this;
- },
-
- _initControlPos: function () {
- var corners = this._controlCorners = {},
- l = 'leaflet-',
- container = this._controlContainer =
- L.DomUtil.create('div', l + 'control-container', this._container);
-
- function createCorner(vSide, hSide) {
- var className = l + vSide + ' ' + l + hSide;
-
- corners[vSide + hSide] = L.DomUtil.create('div', className, container);
- }
-
- createCorner('top', 'left');
- createCorner('top', 'right');
- createCorner('bottom', 'left');
- createCorner('bottom', 'right');
- },
-
- _clearControlPos: function () {
- this._container.removeChild(this._controlContainer);
- }
-});
-
-
-/*
- * L.Control.Zoom is used for the default zoom buttons on the map.
- */
-
-L.Control.Zoom = L.Control.extend({
- options: {
- position: 'topleft',
- zoomInText: '+',
- zoomInTitle: 'Zoom in',
- zoomOutText: '-',
- zoomOutTitle: 'Zoom out'
- },
-
- onAdd: function (map) {
- var zoomName = 'leaflet-control-zoom',
- container = L.DomUtil.create('div', zoomName + ' leaflet-bar');
-
- this._map = map;
-
- this._zoomInButton = this._createButton(
- this.options.zoomInText, this.options.zoomInTitle,
- zoomName + '-in', container, this._zoomIn, this);
- this._zoomOutButton = this._createButton(
- this.options.zoomOutText, this.options.zoomOutTitle,
- zoomName + '-out', container, this._zoomOut, this);
-
- this._updateDisabled();
- map.on('zoomend zoomlevelschange', this._updateDisabled, this);
-
- return container;
- },
-
- onRemove: function (map) {
- map.off('zoomend zoomlevelschange', this._updateDisabled, this);
- },
-
- _zoomIn: function (e) {
- this._map.zoomIn(e.shiftKey ? 3 : 1);
- },
-
- _zoomOut: function (e) {
- this._map.zoomOut(e.shiftKey ? 3 : 1);
- },
-
- _createButton: function (html, title, className, container, fn, context) {
- var link = L.DomUtil.create('a', className, container);
- link.innerHTML = html;
- link.href = '#';
- link.title = title;
-
- var stop = L.DomEvent.stopPropagation;
-
- L.DomEvent
- .on(link, 'click', stop)
- .on(link, 'mousedown', stop)
- .on(link, 'dblclick', stop)
- .on(link, 'click', L.DomEvent.preventDefault)
- .on(link, 'click', fn, context)
- .on(link, 'click', this._refocusOnMap, context);
-
- return link;
- },
-
- _updateDisabled: function () {
- var map = this._map,
- className = 'leaflet-disabled';
-
- L.DomUtil.removeClass(this._zoomInButton, className);
- L.DomUtil.removeClass(this._zoomOutButton, className);
-
- if (map._zoom === map.getMinZoom()) {
- L.DomUtil.addClass(this._zoomOutButton, className);
- }
- if (map._zoom === map.getMaxZoom()) {
- L.DomUtil.addClass(this._zoomInButton, className);
- }
- }
-});
-
-L.Map.mergeOptions({
- zoomControl: true
-});
-
-L.Map.addInitHook(function () {
- if (this.options.zoomControl) {
- this.zoomControl = new L.Control.Zoom();
- this.addControl(this.zoomControl);
- }
-});
-
-L.control.zoom = function (options) {
- return new L.Control.Zoom(options);
-};
-
-
-
-/*
- * L.Control.Attribution is used for displaying attribution on the map (added by default).
- */
-
-L.Control.Attribution = L.Control.extend({
- options: {
- position: 'bottomright',
- prefix: 'Leaflet '
- },
-
- initialize: function (options) {
- L.setOptions(this, options);
-
- this._attributions = {};
- },
-
- onAdd: function (map) {
- this._container = L.DomUtil.create('div', 'leaflet-control-attribution');
- L.DomEvent.disableClickPropagation(this._container);
-
- for (var i in map._layers) {
- if (map._layers[i].getAttribution) {
- this.addAttribution(map._layers[i].getAttribution());
- }
- }
-
- map
- .on('layeradd', this._onLayerAdd, this)
- .on('layerremove', this._onLayerRemove, this);
-
- this._update();
-
- return this._container;
- },
-
- onRemove: function (map) {
- map
- .off('layeradd', this._onLayerAdd)
- .off('layerremove', this._onLayerRemove);
-
- },
-
- setPrefix: function (prefix) {
- this.options.prefix = prefix;
- this._update();
- return this;
- },
-
- addAttribution: function (text) {
- if (!text) { return; }
-
- if (!this._attributions[text]) {
- this._attributions[text] = 0;
- }
- this._attributions[text]++;
-
- this._update();
-
- return this;
- },
-
- removeAttribution: function (text) {
- if (!text) { return; }
-
- 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(' | ');
- },
-
- _onLayerAdd: function (e) {
- if (e.layer.getAttribution) {
- this.addAttribution(e.layer.getAttribution());
- }
- },
-
- _onLayerRemove: function (e) {
- if (e.layer.getAttribution) {
- this.removeAttribution(e.layer.getAttribution());
- }
- }
-});
-
-L.Map.mergeOptions({
- attributionControl: true
-});
-
-L.Map.addInitHook(function () {
- if (this.options.attributionControl) {
- this.attributionControl = (new L.Control.Attribution()).addTo(this);
- }
-});
-
-L.control.attribution = function (options) {
- return new L.Control.Attribution(options);
-};
-
-
-/*
- * L.Control.Scale is used for displaying metric/imperial scale on the map.
- */
-
-L.Control.Scale = L.Control.extend({
- options: {
- position: 'bottomleft',
- maxWidth: 100,
- metric: true,
- imperial: true,
- updateWhenIdle: false
- },
-
- onAdd: function (map) {
- this._map = map;
-
- var className = 'leaflet-control-scale',
- container = L.DomUtil.create('div', className),
- options = this.options;
-
- this._addScales(options, className, 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 = L.DomUtil.create('div', className + '-line', container);
- }
- if (options.imperial) {
- this._iScale = L.DomUtil.create('div', className + '-line', container);
- }
- },
-
- _update: function () {
- var bounds = this._map.getBounds(),
- centerLat = bounds.getCenter().lat,
- halfWorldMeters = 6378137 * Math.PI * Math.cos(centerLat * Math.PI / 180),
- dist = halfWorldMeters * (bounds.getNorthEast().lng - bounds.getSouthWest().lng) / 180,
-
- size = this._map.getSize(),
- options = this.options,
- maxMeters = 0;
-
- if (size.x > 0) {
- maxMeters = dist * (options.maxWidth / size.x);
- }
-
- this._updateScales(options, maxMeters);
- },
-
- _updateScales: function (options, maxMeters) {
- if (options.metric && maxMeters) {
- this._updateMetric(maxMeters);
- }
-
- if (options.imperial && maxMeters) {
- this._updateImperial(maxMeters);
- }
- },
-
- _updateMetric: function (maxMeters) {
- var meters = this._getRoundNum(maxMeters);
-
- this._mScale.style.width = this._getScaleWidth(meters / maxMeters) + 'px';
- this._mScale.innerHTML = meters < 1000 ? meters + ' m' : (meters / 1000) + ' km';
- },
-
- _updateImperial: function (maxMeters) {
- var maxFeet = maxMeters * 3.2808399,
- scale = this._iScale,
- maxMiles, miles, feet;
-
- if (maxFeet > 5280) {
- maxMiles = maxFeet / 5280;
- miles = this._getRoundNum(maxMiles);
-
- scale.style.width = this._getScaleWidth(miles / maxMiles) + 'px';
- scale.innerHTML = miles + ' mi';
-
- } else {
- feet = this._getRoundNum(maxFeet);
-
- scale.style.width = this._getScaleWidth(feet / maxFeet) + 'px';
- scale.innerHTML = feet + ' ft';
- }
- },
-
- _getScaleWidth: function (ratio) {
- return Math.round(this.options.maxWidth * ratio) - 10;
- },
-
- _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;
- }
-});
-
-L.control.scale = function (options) {
- return new L.Control.Scale(options);
-};
-
-
-/*
- * L.Control.Layers is a control to allow users to switch between different layers on the map.
- */
-
-L.Control.Layers = L.Control.extend({
- options: {
- collapsed: true,
- position: 'topright',
- autoZIndex: true
- },
-
- initialize: function (baseLayers, overlays, options) {
- L.setOptions(this, options);
-
- 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();
-
- map
- .on('layeradd', this._onLayerChange, this)
- .on('layerremove', this._onLayerChange, this);
-
- return this._container;
- },
-
- onRemove: function (map) {
- map
- .off('layeradd', this._onLayerChange, this)
- .off('layerremove', this._onLayerChange, this);
- },
-
- addBaseLayer: function (layer, name) {
- this._addLayer(layer, name);
- this._update();
- return this;
- },
-
- addOverlay: function (layer, name) {
- this._addLayer(layer, name, true);
- this._update();
- return this;
- },
-
- removeLayer: function (layer) {
- var id = L.stamp(layer);
- delete this._layers[id];
- this._update();
- return this;
- },
-
- _initLayout: function () {
- var className = 'leaflet-control-layers',
- container = this._container = L.DomUtil.create('div', className);
-
- //Makes this work on IE10 Touch devices by stopping it from firing a mouseout event when the touch is released
- container.setAttribute('aria-haspopup', true);
-
- if (!L.Browser.touch) {
- L.DomEvent
- .disableClickPropagation(container)
- .disableScrollPropagation(container);
- } else {
- L.DomEvent.on(container, 'click', L.DomEvent.stopPropagation);
- }
-
- var form = this._form = L.DomUtil.create('form', className + '-list');
-
- if (this.options.collapsed) {
- if (!L.Browser.android) {
- L.DomEvent
- .on(container, 'mouseover', this._expand, this)
- .on(container, 'mouseout', this._collapse, this);
- }
- var link = this._layersLink = L.DomUtil.create('a', className + '-toggle', container);
- link.href = '#';
- link.title = 'Layers';
-
- if (L.Browser.touch) {
- L.DomEvent
- .on(link, 'click', L.DomEvent.stop)
- .on(link, 'click', this._expand, this);
- }
- else {
- L.DomEvent.on(link, 'focus', this._expand, this);
- }
- //Work around for Firefox android issue https://github.com/Leaflet/Leaflet/issues/2033
- L.DomEvent.on(form, 'click', function () {
- setTimeout(L.bind(this._onInputClick, this), 0);
- }, this);
-
- this._map.on('click', this._collapse, this);
- // TODO keyboard accessibility
- } else {
- this._expand();
- }
-
- this._baseLayersList = L.DomUtil.create('div', className + '-base', form);
- this._separator = L.DomUtil.create('div', className + '-separator', form);
- this._overlaysList = L.DomUtil.create('div', className + '-overlays', form);
-
- container.appendChild(form);
- },
-
- _addLayer: function (layer, name, overlay) {
- var id = L.stamp(layer);
-
- this._layers[id] = {
- layer: layer,
- name: name,
- overlay: overlay
- };
-
- if (this.options.autoZIndex && layer.setZIndex) {
- this._lastZIndex++;
- layer.setZIndex(this._lastZIndex);
- }
- },
-
- _update: function () {
- if (!this._container) {
- return;
- }
-
- this._baseLayersList.innerHTML = '';
- this._overlaysList.innerHTML = '';
-
- var baseLayersPresent = false,
- overlaysPresent = false,
- i, obj;
-
- for (i in this._layers) {
- obj = this._layers[i];
- this._addItem(obj);
- overlaysPresent = overlaysPresent || obj.overlay;
- baseLayersPresent = baseLayersPresent || !obj.overlay;
- }
-
- this._separator.style.display = overlaysPresent && baseLayersPresent ? '' : 'none';
- },
-
- _onLayerChange: function (e) {
- var obj = this._layers[L.stamp(e.layer)];
-
- if (!obj) { return; }
-
- if (!this._handlingClick) {
- this._update();
- }
-
- var type = obj.overlay ?
- (e.type === 'layeradd' ? 'overlayadd' : 'overlayremove') :
- (e.type === 'layeradd' ? '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'),
- input,
- checked = this._map.hasLayer(obj.layer);
-
- 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);
- }
-
- input.layerId = L.stamp(obj.layer);
-
- L.DomEvent.on(input, 'click', this._onInputClick, this);
-
- var name = document.createElement('span');
- name.innerHTML = ' ' + obj.name;
-
- label.appendChild(input);
- label.appendChild(name);
-
- var container = obj.overlay ? this._overlaysList : this._baseLayersList;
- container.appendChild(label);
-
- return label;
- },
-
- _onInputClick: function () {
- var i, input, obj,
- inputs = this._form.getElementsByTagName('input'),
- inputsLen = inputs.length;
-
- this._handlingClick = true;
-
- for (i = 0; i < inputsLen; i++) {
- input = inputs[i];
- obj = this._layers[input.layerId];
-
- if (input.checked && !this._map.hasLayer(obj.layer)) {
- this._map.addLayer(obj.layer);
-
- } else if (!input.checked && this._map.hasLayer(obj.layer)) {
- this._map.removeLayer(obj.layer);
- }
- }
-
- this._handlingClick = false;
-
- this._refocusOnMap();
- },
-
- _expand: function () {
- L.DomUtil.addClass(this._container, 'leaflet-control-layers-expanded');
- },
-
- _collapse: function () {
- this._container.className = this._container.className.replace(' leaflet-control-layers-expanded', '');
- }
-});
-
-L.control.layers = function (baseLayers, overlays, options) {
- return new L.Control.Layers(baseLayers, overlays, options);
-};
-
-
-/*
- * L.PosAnimation is used by Leaflet internally for pan animations.
- */
-
-L.PosAnimation = L.Class.extend({
- includes: L.Mixin.Events,
-
- run: function (el, newPos, duration, easeLinearity) { // (HTMLElement, Point[, Number, Number])
- this.stop();
-
- this._el = el;
- this._inProgress = true;
- this._newPos = newPos;
-
- this.fire('start');
-
- el.style[L.DomUtil.TRANSITION] = 'all ' + (duration || 0.25) +
- 's cubic-bezier(0,0,' + (easeLinearity || 0.5) + ',1)';
-
- L.DomEvent.on(el, L.DomUtil.TRANSITION_END, this._onTransitionEnd, this);
- L.DomUtil.setPosition(el, newPos);
-
- // toggle reflow, Chrome flickers for some reason if you don't do this
- L.Util.falseFn(el.offsetWidth);
-
- // there's no native way to track value updates of transitioned properties, so we imitate this
- this._stepTimer = setInterval(L.bind(this._onStep, this), 50);
- },
-
- stop: function () {
- if (!this._inProgress) { return; }
-
- // if we just removed the transition property, the element would jump to its final position,
- // so we need to make it stay at the current position
-
- L.DomUtil.setPosition(this._el, this._getPos());
- this._onTransitionEnd();
- L.Util.falseFn(this._el.offsetWidth); // force reflow in case we are about to start a new animation
- },
-
- _onStep: function () {
- var stepPos = this._getPos();
- if (!stepPos) {
- this._onTransitionEnd();
- return;
- }
- // jshint camelcase: false
- // make L.DomUtil.getPosition return intermediate position value during animation
- this._el._leaflet_pos = stepPos;
-
- this.fire('step');
- },
-
- // you can't easily get intermediate values of properties animated with CSS3 Transitions,
- // we need to parse computed style (in case of transform it returns matrix string)
-
- _transformRe: /([-+]?(?:\d*\.)?\d+)\D*, ([-+]?(?:\d*\.)?\d+)\D*\)/,
-
- _getPos: function () {
- var left, top, matches,
- el = this._el,
- style = window.getComputedStyle(el);
-
- if (L.Browser.any3d) {
- matches = style[L.DomUtil.TRANSFORM].match(this._transformRe);
- if (!matches) { return; }
- left = parseFloat(matches[1]);
- top = parseFloat(matches[2]);
- } else {
- left = parseFloat(style.left);
- top = parseFloat(style.top);
- }
-
- return new L.Point(left, top, true);
- },
-
- _onTransitionEnd: function () {
- L.DomEvent.off(this._el, L.DomUtil.TRANSITION_END, this._onTransitionEnd, this);
-
- if (!this._inProgress) { return; }
- this._inProgress = false;
-
- this._el.style[L.DomUtil.TRANSITION] = '';
-
- // jshint camelcase: false
- // make sure L.DomUtil.getPosition returns the final position value after animation
- this._el._leaflet_pos = this._newPos;
-
- clearInterval(this._stepTimer);
-
- this.fire('step').fire('end');
- }
-
-});
-
-
-/*
- * Extends L.Map to handle panning animations.
- */
-
-L.Map.include({
-
- setView: function (center, zoom, options) {
-
- zoom = zoom === undefined ? this._zoom : this._limitZoom(zoom);
- center = this._limitCenter(L.latLng(center), zoom, this.options.maxBounds);
- options = options || {};
-
- if (this._panAnim) {
- this._panAnim.stop();
- }
-
- if (this._loaded && !options.reset && options !== true) {
-
- if (options.animate !== undefined) {
- options.zoom = L.extend({animate: options.animate}, options.zoom);
- options.pan = L.extend({animate: options.animate}, options.pan);
- }
-
- // try animating pan or zoom
- var animated = (this._zoom !== zoom) ?
- this._tryAnimatedZoom && this._tryAnimatedZoom(center, zoom, options.zoom) :
- this._tryAnimatedPan(center, options.pan);
-
- if (animated) {
- // 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;
- },
-
- panBy: function (offset, options) {
- offset = L.point(offset).round();
- options = options || {};
-
- if (!offset.x && !offset.y) {
- return this;
- }
-
- if (!this._panAnim) {
- this._panAnim = new L.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) {
- L.DomUtil.addClass(this._mapPane, 'leaflet-pan-anim');
-
- var newPos = this._getMapPanePos().subtract(offset);
- this._panAnim.run(this._mapPane, newPos, options.duration || 0.25, options.easeLinearity);
- } else {
- this._rawPanBy(offset);
- this.fire('move').fire('moveend');
- }
-
- return this;
- },
-
- _onPanTransitionStep: function () {
- this.fire('move');
- },
-
- _onPanTransitionEnd: function () {
- L.DomUtil.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;
- }
-});
-
-
-/*
- * L.PosAnimation fallback implementation that powers Leaflet pan animations
- * in browsers that don't support CSS3 Transitions.
- */
-
-L.PosAnimation = L.DomUtil.TRANSITION ? L.PosAnimation : L.PosAnimation.extend({
-
- run: function (el, newPos, duration, easeLinearity) { // (HTMLElement, Point[, Number, Number])
- 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 = L.DomUtil.getPosition(el);
- this._offset = newPos.subtract(this._startPos);
- this._startTime = +new Date();
-
- this.fire('start');
-
- this._animate();
- },
-
- stop: function () {
- if (!this._inProgress) { return; }
-
- this._step();
- this._complete();
- },
-
- _animate: function () {
- // animation loop
- this._animId = L.Util.requestAnimFrame(this._animate, this);
- this._step();
- },
-
- _step: function () {
- var elapsed = (+new Date()) - this._startTime,
- duration = this._duration * 1000;
-
- if (elapsed < duration) {
- this._runFrame(this._easeOut(elapsed / duration));
- } else {
- this._runFrame(1);
- this._complete();
- }
- },
-
- _runFrame: function (progress) {
- var pos = this._startPos.add(this._offset.multiplyBy(progress));
- L.DomUtil.setPosition(this._el, pos);
-
- this.fire('step');
- },
-
- _complete: function () {
- L.Util.cancelAnimFrame(this._animId);
-
- this._inProgress = false;
- this.fire('end');
- },
-
- _easeOut: function (t) {
- return 1 - Math.pow(1 - t, this._easeOutPower);
- }
-});
-
-
-/*
- * Extends L.Map to handle zoom animations.
- */
-
-L.Map.mergeOptions({
- zoomAnimation: true,
- zoomAnimationThreshold: 4
-});
-
-if (L.DomUtil.TRANSITION) {
-
- L.Map.addInitHook(function () {
- // don't animate on browsers without hardware-accelerated transitions or old Android/Opera
- this._zoomAnimated = this.options.zoomAnimation && L.DomUtil.TRANSITION &&
- L.Browser.any3d && !L.Browser.android23 && !L.Browser.mobileOpera;
-
- // 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) {
- L.DomEvent.on(this._mapPane, L.DomUtil.TRANSITION_END, this._catchTransitionEnd, this);
- }
- });
-}
-
-L.Map.include(!L.DomUtil.TRANSITION ? {} : {
-
- _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),
- origin = this._getCenterLayerPoint()._add(offset);
-
- // 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; }
-
- this
- .fire('movestart')
- .fire('zoomstart');
-
- this._animateZoom(center, zoom, origin, scale, null, true);
-
- return true;
- },
-
- _animateZoom: function (center, zoom, origin, scale, delta, backwards, forTouchZoom) {
-
- if (!forTouchZoom) {
- this._animatingZoom = true;
- }
-
- // put transform transition on all layers with leaflet-zoom-animated class
- L.DomUtil.addClass(this._mapPane, 'leaflet-zoom-anim');
-
- // remember what center/zoom to set after animation
- this._animateToCenter = center;
- this._animateToZoom = zoom;
-
- // disable any dragging during animation
- if (L.Draggable) {
- L.Draggable._disabled = true;
- }
-
- L.Util.requestAnimFrame(function () {
- this.fire('zoomanim', {
- center: center,
- zoom: zoom,
- origin: origin,
- scale: scale,
- delta: delta,
- backwards: backwards
- });
- }, this);
- },
-
- _onZoomTransitionEnd: function () {
-
- this._animatingZoom = false;
-
- L.DomUtil.removeClass(this._mapPane, 'leaflet-zoom-anim');
-
- this._resetView(this._animateToCenter, this._animateToZoom, true, true);
-
- if (L.Draggable) {
- L.Draggable._disabled = false;
- }
- }
-});
-
-
-/*
- Zoom animation logic for L.TileLayer.
-*/
-
-L.TileLayer.include({
- _animateZoom: function (e) {
- if (!this._animating) {
- this._animating = true;
- this._prepareBgBuffer();
- }
-
- var bg = this._bgBuffer,
- transform = L.DomUtil.TRANSFORM,
- initialTransform = e.delta ? L.DomUtil.getTranslateString(e.delta) : bg.style[transform],
- scaleStr = L.DomUtil.getScaleString(e.scale, e.origin);
-
- bg.style[transform] = e.backwards ?
- scaleStr + ' ' + initialTransform :
- initialTransform + ' ' + scaleStr;
- },
-
- _endZoomAnim: function () {
- var front = this._tileContainer,
- bg = this._bgBuffer;
-
- front.style.visibility = '';
- front.parentNode.appendChild(front); // Bring to fore
-
- // force reflow
- L.Util.falseFn(bg.offsetWidth);
-
- this._animating = false;
- },
-
- _clearBgBuffer: function () {
- var map = this._map;
-
- if (map && !map._animatingZoom && !map.touchZoom._zooming) {
- this._bgBuffer.innerHTML = '';
- this._bgBuffer.style[L.DomUtil.TRANSFORM] = '';
- }
- },
-
- _prepareBgBuffer: function () {
-
- var front = this._tileContainer,
- bg = this._bgBuffer;
-
- // if foreground layer doesn't have many tiles but bg layer does,
- // keep the existing bg layer and just zoom it some more
-
- var bgLoaded = this._getLoadedTilesPercentage(bg),
- frontLoaded = this._getLoadedTilesPercentage(front);
-
- if (bg && bgLoaded > 0.5 && frontLoaded < 0.5) {
-
- front.style.visibility = 'hidden';
- this._stopLoadingImages(front);
- return;
- }
-
- // prepare the buffer to become the front tile pane
- bg.style.visibility = 'hidden';
- bg.style[L.DomUtil.TRANSFORM] = '';
-
- // switch out the current layer to be the new bg layer (and vice-versa)
- this._tileContainer = bg;
- bg = this._bgBuffer = front;
-
- this._stopLoadingImages(bg);
-
- //prevent bg buffer from clearing right after zoom
- clearTimeout(this._clearBgBufferTimer);
- },
-
- _getLoadedTilesPercentage: function (container) {
- var tiles = container.getElementsByTagName('img'),
- i, len, count = 0;
-
- for (i = 0, len = tiles.length; i < len; i++) {
- if (tiles[i].complete) {
- count++;
- }
- }
- return count / len;
- },
-
- // stops loading all tiles in the background layer
- _stopLoadingImages: function (container) {
- var tiles = Array.prototype.slice.call(container.getElementsByTagName('img')),
- i, len, tile;
-
- for (i = 0, len = tiles.length; i < len; i++) {
- tile = tiles[i];
-
- if (!tile.complete) {
- tile.onload = L.Util.falseFn;
- tile.onerror = L.Util.falseFn;
- tile.src = L.Util.emptyImageUrl;
-
- tile.parentNode.removeChild(tile);
- }
- }
- }
-});
-
-
-/*
- * Provides L.Map with convenient shortcuts for using browser geolocation features.
- */
-
-L.Map.include({
- _defaultLocateOptions: {
- watch: false,
- setView: false,
- maxZoom: Infinity,
- timeout: 10000,
- maximumAge: 0,
- enableHighAccuracy: false
- },
-
- locate: function (/*Object*/ options) {
-
- options = this._locateOptions = L.extend(this._defaultLocateOptions, options);
-
- if (!navigator.geolocation) {
- this._handleGeolocationError({
- code: 0,
- message: 'Geolocation not supported.'
- });
- return this;
- }
-
- var onResponse = L.bind(this._handleGeolocationResponse, this),
- onError = L.bind(this._handleGeolocationError, this);
-
- if (options.watch) {
- this._locationWatchId =
- navigator.geolocation.watchPosition(onResponse, onError, options);
- } else {
- navigator.geolocation.getCurrentPosition(onResponse, onError, options);
- }
- return this;
- },
-
- stopLocate: function () {
- if (navigator.geolocation) {
- 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();
- }
-
- this.fire('locationerror', {
- code: c,
- message: 'Geolocation error: ' + message + '.'
- });
- },
-
- _handleGeolocationResponse: function (pos) {
- var lat = pos.coords.latitude,
- lng = pos.coords.longitude,
- latlng = new L.LatLng(lat, lng),
-
- latAccuracy = 180 * pos.coords.accuracy / 40075017,
- lngAccuracy = latAccuracy / Math.cos(L.LatLng.DEG_TO_RAD * lat),
-
- bounds = L.latLngBounds(
- [lat - latAccuracy, lng - lngAccuracy],
- [lat + latAccuracy, lng + lngAccuracy]),
-
- options = this._locateOptions;
-
- if (options.setView) {
- var zoom = Math.min(this.getBoundsZoom(bounds), options.maxZoom);
- this.setView(latlng, 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];
- }
- }
-
- this.fire('locationfound', data);
- }
-});
-
-
-}(window, document));
\ No newline at end of file
diff --git a/servers/php/javascript/leaflet-0.7.3/leaflet.css b/servers/php/javascript/leaflet-0.7.3/leaflet.css
deleted file mode 100644
index ac0cd17..0000000
--- a/servers/php/javascript/leaflet-0.7.3/leaflet.css
+++ /dev/null
@@ -1,478 +0,0 @@
-/* required styles */
-
-.leaflet-map-pane,
-.leaflet-tile,
-.leaflet-marker-icon,
-.leaflet-marker-shadow,
-.leaflet-tile-pane,
-.leaflet-tile-container,
-.leaflet-overlay-pane,
-.leaflet-shadow-pane,
-.leaflet-marker-pane,
-.leaflet-popup-pane,
-.leaflet-overlay-pane svg,
-.leaflet-zoom-box,
-.leaflet-image-layer,
-.leaflet-layer {
- position: absolute;
- left: 0;
- top: 0;
- }
-.leaflet-container {
- overflow: hidden;
- -ms-touch-action: none;
- }
-.leaflet-tile,
-.leaflet-marker-icon,
-.leaflet-marker-shadow {
- -webkit-user-select: none;
- -moz-user-select: none;
- user-select: none;
- -webkit-user-drag: none;
- }
-.leaflet-marker-icon,
-.leaflet-marker-shadow {
- display: block;
- }
-/* map is broken in FF if you have max-width: 100% on tiles */
-.leaflet-container img {
- max-width: none !important;
- }
-/* stupid Android 2 doesn't understand "max-width: none" properly */
-.leaflet-container img.leaflet-image-layer {
- max-width: 15000px !important;
- }
-.leaflet-tile {
- filter: inherit;
- visibility: hidden;
- }
-.leaflet-tile-loaded {
- visibility: inherit;
- }
-.leaflet-zoom-box {
- width: 0;
- height: 0;
- }
-/* workaround for https://bugzilla.mozilla.org/show_bug.cgi?id=888319 */
-.leaflet-overlay-pane svg {
- -moz-user-select: none;
- }
-
-.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-vml-shape {
- width: 1px;
- height: 1px;
- }
-.lvml {
- behavior: url(#default#VML);
- display: inline-block;
- position: absolute;
- }
-
-
-/* control positioning */
-
-.leaflet-control {
- position: relative;
- z-index: 7;
- pointer-events: auto;
- }
-.leaflet-top,
-.leaflet-bottom {
- position: absolute;
- z-index: 1000;
- pointer-events: none;
- }
-.leaflet-top {
- top: 0;
- }
-.leaflet-right {
- right: 0;
- }
-.leaflet-bottom {
- bottom: 0;
- }
-.leaflet-left {
- left: 0;
- }
-.leaflet-control {
- float: left;
- clear: both;
- }
-.leaflet-right .leaflet-control {
- float: right;
- }
-.leaflet-top .leaflet-control {
- margin-top: 10px;
- }
-.leaflet-bottom .leaflet-control {
- margin-bottom: 10px;
- }
-.leaflet-left .leaflet-control {
- margin-left: 10px;
- }
-.leaflet-right .leaflet-control {
- margin-right: 10px;
- }
-
-
-/* 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,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-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;
- }
-
-
-/* cursors */
-
-.leaflet-clickable {
- cursor: pointer;
- }
-.leaflet-container {
- cursor: -webkit-grab;
- cursor: -moz-grab;
- }
-.leaflet-popup-pane,
-.leaflet-control {
- cursor: auto;
- }
-.leaflet-dragging .leaflet-container,
-.leaflet-dragging .leaflet-clickable {
- cursor: move;
- cursor: -webkit-grabbing;
- cursor: -moz-grabbing;
- }
-
-
-/* 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-bar a:hover {
- background-color: #f4f4f4;
- }
-.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-bar a.leaflet-disabled {
- cursor: default;
- background-color: #f4f4f4;
- color: #bbb;
- }
-
-.leaflet-touch .leaflet-bar a {
- width: 30px;
- height: 30px;
- line-height: 30px;
- }
-
-
-/* zoom control */
-
-.leaflet-control-zoom-in,
-.leaflet-control-zoom-out {
- 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 5px rgba(0,0,0,0.4);
- background: #fff;
- border-radius: 5px;
- }
-.leaflet-control-layers-toggle {
- background-image: url(images/layers.png);
- width: 36px;
- height: 36px;
- }
-.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;
- }
-.leaflet-control-layers .leaflet-control-layers-list,
-.leaflet-control-layers-expanded .leaflet-control-layers-toggle {
- display: none;
- }
-.leaflet-control-layers-expanded .leaflet-control-layers-list {
- display: block;
- position: relative;
- }
-.leaflet-control-layers-expanded {
- padding: 6px 10px 6px 6px;
- color: #333;
- background: #fff;
- }
-.leaflet-control-layers-selector {
- margin-top: 2px;
- position: relative;
- top: 1px;
- }
-.leaflet-control-layers label {
- display: block;
- }
-.leaflet-control-layers-separator {
- height: 0;
- border-top: 1px solid #ddd;
- margin: 5px -10px 5px -6px;
- }
-
-
-/* attribution and scale controls */
-
-.leaflet-container .leaflet-control-attribution {
- 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-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;
- line-height: 1.1;
- padding: 2px 5px 1px;
- font-size: 11px;
- white-space: nowrap;
- overflow: hidden;
- -moz-box-sizing: content-box;
- box-sizing: content-box;
-
- background: #fff;
- background: rgba(255, 255, 255, 0.5);
- }
-.leaflet-control-scale-line:not(:first-child) {
- border-top: 2px solid #777;
- border-bottom: none;
- margin-top: -2px;
- }
-.leaflet-control-scale-line:not(:first-child):not(:last-child) {
- border-bottom: 2px solid #777;
- }
-
-.leaflet-touch .leaflet-control-attribution,
-.leaflet-touch .leaflet-control-layers,
-.leaflet-touch .leaflet-bar {
- box-shadow: none;
- }
-.leaflet-touch .leaflet-control-layers,
-.leaflet-touch .leaflet-bar {
- border: 2px solid rgba(0,0,0,0.2);
- background-clip: padding-box;
- }
-
-
-/* popup */
-
-.leaflet-popup {
- position: absolute;
- text-align: center;
- }
-.leaflet-popup-content-wrapper {
- padding: 1px;
- text-align: left;
- border-radius: 12px;
- }
-.leaflet-popup-content {
- 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;
- overflow: hidden;
- }
-.leaflet-popup-tip {
- width: 17px;
- height: 17px;
- padding: 1px;
-
- margin: -10px auto 0;
-
- -webkit-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;
-
- 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 4px 0 0;
- text-align: center;
- width: 18px;
- height: 14px;
- font: 16px/14px Tahoma, Verdana, sans-serif;
- color: #c3c3c3;
- text-decoration: none;
- font-weight: bold;
- background: transparent;
- }
-.leaflet-container a.leaflet-popup-close-button:hover {
- color: #999;
- }
-.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;
-
- -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-oldie .leaflet-popup-tip-container {
- margin-top: -1px;
- }
-
-.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;
- }
diff --git a/servers/php/javascript/leaflet-0.7.3/leaflet.js b/servers/php/javascript/leaflet-0.7.3/leaflet.js
deleted file mode 100644
index 03434b7..0000000
--- a/servers/php/javascript/leaflet-0.7.3/leaflet.js
+++ /dev/null
@@ -1,9 +0,0 @@
-/*
- Leaflet, a JavaScript library for mobile-friendly interactive maps. http://leafletjs.com
- (c) 2010-2013, Vladimir Agafonkin
- (c) 2010-2011, CloudMade
-*/
-!function(t,e,i){var n=t.L,o={};o.version="0.7.3","object"==typeof module&&"object"==typeof module.exports?module.exports=o:"function"==typeof define&&define.amd&&define(o),o.noConflict=function(){return t.L=n,this},t.L=o,o.Util={extend:function(t){var e,i,n,o,s=Array.prototype.slice.call(arguments,1);for(i=0,n=s.length;n>i;i++){o=s[i]||{};for(e in o)o.hasOwnProperty(e)&&(t[e]=o[e])}return t},bind:function(t,e){var i=arguments.length>2?Array.prototype.slice.call(arguments,2):null;return function(){return t.apply(e,i||arguments)}},stamp:function(){var t=0,e="_leaflet_id";return function(i){return i[e]=i[e]||++t,i[e]}}(),invokeEach:function(t,e,i){var n,o;if("object"==typeof t){o=Array.prototype.slice.call(arguments,3);for(n in t)e.apply(i,[n,t[n]].concat(o));return!0}return!1},limitExecByInterval:function(t,e,i){var n,o;return function s(){var a=arguments;return n?void(o=!0):(n=!0,setTimeout(function(){n=!1,o&&(s.apply(i,a),o=!1)},e),void t.apply(i,a))}},falseFn:function(){return!1},formatNum:function(t,e){var i=Math.pow(10,e||5);return Math.round(t*i)/i},trim:function(t){return t.trim?t.trim():t.replace(/^\s+|\s+$/g,"")},splitWords:function(t){return o.Util.trim(t).split(/\s+/)},setOptions:function(t,e){return t.options=o.extend({},t.options,e),t.options},getParamString:function(t,e,i){var n=[];for(var o in t)n.push(encodeURIComponent(i?o.toUpperCase():o)+"="+encodeURIComponent(t[o]));return(e&&-1!==e.indexOf("?")?"&":"?")+n.join("&")},template:function(t,e){return t.replace(/\{ *([\w_]+) *\}/g,function(t,n){var o=e[n];if(o===i)throw new Error("No value provided for variable "+t);return"function"==typeof o&&(o=o(e)),o})},isArray:Array.isArray||function(t){return"[object Array]"===Object.prototype.toString.call(t)},emptyImageUrl:"data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs="},function(){function e(e){var i,n,o=["webkit","moz","o","ms"];for(i=0;it;t++)n._initHooks[t].call(this)}},e},o.Class.include=function(t){o.extend(this.prototype,t)},o.Class.mergeOptions=function(t){o.extend(this.prototype.options,t)},o.Class.addInitHook=function(t){var e=Array.prototype.slice.call(arguments,1),i="function"==typeof t?t:function(){this[t].apply(this,e)};this.prototype._initHooks=this.prototype._initHooks||[],this.prototype._initHooks.push(i)};var s="_leaflet_events";o.Mixin={},o.Mixin.Events={addEventListener:function(t,e,i){if(o.Util.invokeEach(t,this.addEventListener,this,e,i))return this;var n,a,r,h,l,u,c,d=this[s]=this[s]||{},p=i&&i!==this&&o.stamp(i);for(t=o.Util.splitWords(t),n=0,a=t.length;a>n;n++)r={action:e,context:i||this},h=t[n],p?(l=h+"_idx",u=l+"_len",c=d[l]=d[l]||{},c[p]||(c[p]=[],d[u]=(d[u]||0)+1),c[p].push(r)):(d[h]=d[h]||[],d[h].push(r));return this},hasEventListeners:function(t){var e=this[s];return!!e&&(t in e&&e[t].length>0||t+"_idx"in e&&e[t+"_idx_len"]>0)},removeEventListener:function(t,e,i){if(!this[s])return this;if(!t)return this.clearAllEventListeners();if(o.Util.invokeEach(t,this.removeEventListener,this,e,i))return this;var n,a,r,h,l,u,c,d,p,_=this[s],m=i&&i!==this&&o.stamp(i);for(t=o.Util.splitWords(t),n=0,a=t.length;a>n;n++)if(r=t[n],u=r+"_idx",c=u+"_len",d=_[u],e){if(h=m&&d?d[m]:_[r]){for(l=h.length-1;l>=0;l--)h[l].action!==e||i&&h[l].context!==i||(p=h.splice(l,1),p[0].action=o.Util.falseFn);i&&d&&0===h.length&&(delete d[m],_[c]--)}}else delete _[r],delete _[u],delete _[c];return this},clearAllEventListeners:function(){return delete this[s],this},fireEvent:function(t,e){if(!this.hasEventListeners(t))return this;var i,n,a,r,h,l=o.Util.extend({},e,{type:t,target:this}),u=this[s];if(u[t])for(i=u[t].slice(),n=0,a=i.length;a>n;n++)i[n].action.call(i[n].context,l);r=u[t+"_idx"];for(h in r)if(i=r[h].slice())for(n=0,a=i.length;a>n;n++)i[n].action.call(i[n].context,l);return this},addOneTimeEventListener:function(t,e,i){if(o.Util.invokeEach(t,this.addOneTimeEventListener,this,e,i))return this;var n=o.bind(function(){this.removeEventListener(t,e,i).removeEventListener(t,n,i)},this);return this.addEventListener(t,e,i).addEventListener(t,n,i)}},o.Mixin.Events.on=o.Mixin.Events.addEventListener,o.Mixin.Events.off=o.Mixin.Events.removeEventListener,o.Mixin.Events.once=o.Mixin.Events.addOneTimeEventListener,o.Mixin.Events.fire=o.Mixin.Events.fireEvent,function(){var n="ActiveXObject"in t,s=n&&!e.addEventListener,a=navigator.userAgent.toLowerCase(),r=-1!==a.indexOf("webkit"),h=-1!==a.indexOf("chrome"),l=-1!==a.indexOf("phantom"),u=-1!==a.indexOf("android"),c=-1!==a.search("android [23]"),d=-1!==a.indexOf("gecko"),p=typeof orientation!=i+"",_=t.navigator&&t.navigator.msPointerEnabled&&t.navigator.msMaxTouchPoints&&!t.PointerEvent,m=t.PointerEvent&&t.navigator.pointerEnabled&&t.navigator.maxTouchPoints||_,f="devicePixelRatio"in t&&t.devicePixelRatio>1||"matchMedia"in t&&t.matchMedia("(min-resolution:144dpi)")&&t.matchMedia("(min-resolution:144dpi)").matches,g=e.documentElement,v=n&&"transition"in g.style,y="WebKitCSSMatrix"in t&&"m11"in new t.WebKitCSSMatrix&&!c,P="MozPerspective"in g.style,L="OTransition"in g.style,x=!t.L_DISABLE_3D&&(v||y||P||L)&&!l,w=!t.L_NO_TOUCH&&!l&&function(){var t="ontouchstart";if(m||t in g)return!0;var i=e.createElement("div"),n=!1;return i.setAttribute?(i.setAttribute(t,"return;"),"function"==typeof i[t]&&(n=!0),i.removeAttribute(t),i=null,n):!1}();o.Browser={ie:n,ielt9:s,webkit:r,gecko:d&&!r&&!t.opera&&!n,android:u,android23:c,chrome:h,ie3d:v,webkit3d:y,gecko3d:P,opera3d:L,any3d:x,mobile:p,mobileWebkit:p&&r,mobileWebkit3d:p&&y,mobileOpera:p&&t.opera,touch:w,msPointer:_,pointer:m,retina:f}}(),o.Point=function(t,e,i){this.x=i?Math.round(t):t,this.y=i?Math.round(e):e},o.Point.prototype={clone:function(){return new o.Point(this.x,this.y)},add:function(t){return this.clone()._add(o.point(t))},_add:function(t){return this.x+=t.x,this.y+=t.y,this},subtract:function(t){return this.clone()._subtract(o.point(t))},_subtract:function(t){return this.x-=t.x,this.y-=t.y,this},divideBy:function(t){return this.clone()._divideBy(t)},_divideBy:function(t){return this.x/=t,this.y/=t,this},multiplyBy:function(t){return this.clone()._multiplyBy(t)},_multiplyBy:function(t){return this.x*=t,this.y*=t,this},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},distanceTo:function(t){t=o.point(t);var e=t.x-this.x,i=t.y-this.y;return Math.sqrt(e*e+i*i)},equals:function(t){return t=o.point(t),t.x===this.x&&t.y===this.y},contains:function(t){return t=o.point(t),Math.abs(t.x)<=Math.abs(this.x)&&Math.abs(t.y)<=Math.abs(this.y)},toString:function(){return"Point("+o.Util.formatNum(this.x)+", "+o.Util.formatNum(this.y)+")"}},o.point=function(t,e,n){return t instanceof o.Point?t:o.Util.isArray(t)?new o.Point(t[0],t[1]):t===i||null===t?t:new o.Point(t,e,n)},o.Bounds=function(t,e){if(t)for(var i=e?[t,e]:t,n=0,o=i.length;o>n;n++)this.extend(i[n])},o.Bounds.prototype={extend:function(t){return t=o.point(t),this.min||this.max?(this.min.x=Math.min(t.x,this.min.x),this.max.x=Math.max(t.x,this.max.x),this.min.y=Math.min(t.y,this.min.y),this.max.y=Math.max(t.y,this.max.y)):(this.min=t.clone(),this.max=t.clone()),this},getCenter:function(t){return new o.Point((this.min.x+this.max.x)/2,(this.min.y+this.max.y)/2,t)},getBottomLeft:function(){return new o.Point(this.min.x,this.max.y)},getTopRight:function(){return new o.Point(this.max.x,this.min.y)},getSize:function(){return this.max.subtract(this.min)},contains:function(t){var e,i;return t="number"==typeof t[0]||t instanceof o.Point?o.point(t):o.bounds(t),t instanceof o.Bounds?(e=t.min,i=t.max):e=i=t,e.x>=this.min.x&&i.x<=this.max.x&&e.y>=this.min.y&&i.y<=this.max.y},intersects:function(t){t=o.bounds(t);var e=this.min,i=this.max,n=t.min,s=t.max,a=s.x>=e.x&&n.x<=i.x,r=s.y>=e.y&&n.y<=i.y;return a&&r},isValid:function(){return!(!this.min||!this.max)}},o.bounds=function(t,e){return!t||t instanceof o.Bounds?t:new o.Bounds(t,e)},o.Transformation=function(t,e,i,n){this._a=t,this._b=e,this._c=i,this._d=n},o.Transformation.prototype={transform:function(t,e){return this._transform(t.clone(),e)},_transform:function(t,e){return e=e||1,t.x=e*(this._a*t.x+this._b),t.y=e*(this._c*t.y+this._d),t},untransform:function(t,e){return e=e||1,new o.Point((t.x/e-this._b)/this._a,(t.y/e-this._d)/this._c)}},o.DomUtil={get:function(t){return"string"==typeof t?e.getElementById(t):t},getStyle:function(t,i){var n=t.style[i];if(!n&&t.currentStyle&&(n=t.currentStyle[i]),(!n||"auto"===n)&&e.defaultView){var o=e.defaultView.getComputedStyle(t,null);n=o?o[i]:null}return"auto"===n?null:n},getViewportOffset:function(t){var i,n=0,s=0,a=t,r=e.body,h=e.documentElement;do{if(n+=a.offsetTop||0,s+=a.offsetLeft||0,n+=parseInt(o.DomUtil.getStyle(a,"borderTopWidth"),10)||0,s+=parseInt(o.DomUtil.getStyle(a,"borderLeftWidth"),10)||0,i=o.DomUtil.getStyle(a,"position"),a.offsetParent===r&&"absolute"===i)break;if("fixed"===i){n+=r.scrollTop||h.scrollTop||0,s+=r.scrollLeft||h.scrollLeft||0;break}if("relative"===i&&!a.offsetLeft){var l=o.DomUtil.getStyle(a,"width"),u=o.DomUtil.getStyle(a,"max-width"),c=a.getBoundingClientRect();("none"!==l||"none"!==u)&&(s+=c.left+a.clientLeft),n+=c.top+(r.scrollTop||h.scrollTop||0);break}a=a.offsetParent}while(a);a=t;do{if(a===r)break;n-=a.scrollTop||0,s-=a.scrollLeft||0,a=a.parentNode}while(a);return new o.Point(s,n)},documentIsLtr:function(){return o.DomUtil._docIsLtrCached||(o.DomUtil._docIsLtrCached=!0,o.DomUtil._docIsLtr="ltr"===o.DomUtil.getStyle(e.body,"direction")),o.DomUtil._docIsLtr},create:function(t,i,n){var o=e.createElement(t);return o.className=i,n&&n.appendChild(o),o},hasClass:function(t,e){if(t.classList!==i)return t.classList.contains(e);var n=o.DomUtil._getClass(t);return n.length>0&&new RegExp("(^|\\s)"+e+"(\\s|$)").test(n)},addClass:function(t,e){if(t.classList!==i)for(var n=o.Util.splitWords(e),s=0,a=n.length;a>s;s++)t.classList.add(n[s]);else if(!o.DomUtil.hasClass(t,e)){var r=o.DomUtil._getClass(t);o.DomUtil._setClass(t,(r?r+" ":"")+e)}},removeClass:function(t,e){t.classList!==i?t.classList.remove(e):o.DomUtil._setClass(t,o.Util.trim((" "+o.DomUtil._getClass(t)+" ").replace(" "+e+" "," ")))},_setClass:function(t,e){t.className.baseVal===i?t.className=e:t.className.baseVal=e},_getClass:function(t){return t.className.baseVal===i?t.className:t.className.baseVal},setOpacity:function(t,e){if("opacity"in t.style)t.style.opacity=e;else if("filter"in t.style){var i=!1,n="DXImageTransform.Microsoft.Alpha";try{i=t.filters.item(n)}catch(o){if(1===e)return}e=Math.round(100*e),i?(i.Enabled=100!==e,i.Opacity=e):t.style.filter+=" progid:"+n+"(opacity="+e+")"}},testProp:function(t){for(var i=e.documentElement.style,n=0;ni||i===e?e:t),new o.LatLng(this.lat,i)}},o.latLng=function(t,e){return t instanceof o.LatLng?t:o.Util.isArray(t)?"number"==typeof t[0]||"string"==typeof t[0]?new o.LatLng(t[0],t[1],t[2]):null:t===i||null===t?t:"object"==typeof t&&"lat"in t?new o.LatLng(t.lat,"lng"in t?t.lng:t.lon):e===i?null:new o.LatLng(t,e)},o.LatLngBounds=function(t,e){if(t)for(var i=e?[t,e]:t,n=0,o=i.length;o>n;n++)this.extend(i[n])},o.LatLngBounds.prototype={extend:function(t){if(!t)return this;var e=o.latLng(t);return t=null!==e?e:o.latLngBounds(t),t instanceof o.LatLng?this._southWest||this._northEast?(this._southWest.lat=Math.min(t.lat,this._southWest.lat),this._southWest.lng=Math.min(t.lng,this._southWest.lng),this._northEast.lat=Math.max(t.lat,this._northEast.lat),this._northEast.lng=Math.max(t.lng,this._northEast.lng)):(this._southWest=new o.LatLng(t.lat,t.lng),this._northEast=new o.LatLng(t.lat,t.lng)):t instanceof o.LatLngBounds&&(this.extend(t._southWest),this.extend(t._northEast)),this},pad:function(t){var e=this._southWest,i=this._northEast,n=Math.abs(e.lat-i.lat)*t,s=Math.abs(e.lng-i.lng)*t;return new o.LatLngBounds(new o.LatLng(e.lat-n,e.lng-s),new o.LatLng(i.lat+n,i.lng+s))},getCenter:function(){return new o.LatLng((this._southWest.lat+this._northEast.lat)/2,(this._southWest.lng+this._northEast.lng)/2)},getSouthWest:function(){return this._southWest},getNorthEast:function(){return this._northEast},getNorthWest:function(){return new o.LatLng(this.getNorth(),this.getWest())},getSouthEast:function(){return new o.LatLng(this.getSouth(),this.getEast())},getWest:function(){return this._southWest.lng},getSouth:function(){return this._southWest.lat},getEast:function(){return this._northEast.lng},getNorth:function(){return this._northEast.lat},contains:function(t){t="number"==typeof t[0]||t instanceof o.LatLng?o.latLng(t):o.latLngBounds(t);var e,i,n=this._southWest,s=this._northEast;return t instanceof o.LatLngBounds?(e=t.getSouthWest(),i=t.getNorthEast()):e=i=t,e.lat>=n.lat&&i.lat<=s.lat&&e.lng>=n.lng&&i.lng<=s.lng},intersects:function(t){t=o.latLngBounds(t);var e=this._southWest,i=this._northEast,n=t.getSouthWest(),s=t.getNorthEast(),a=s.lat>=e.lat&&n.lat<=i.lat,r=s.lng>=e.lng&&n.lng<=i.lng;return a&&r},toBBoxString:function(){return[this.getWest(),this.getSouth(),this.getEast(),this.getNorth()].join(",")},equals:function(t){return t?(t=o.latLngBounds(t),this._southWest.equals(t.getSouthWest())&&this._northEast.equals(t.getNorthEast())):!1},isValid:function(){return!(!this._southWest||!this._northEast)}},o.latLngBounds=function(t,e){return!t||t instanceof o.LatLngBounds?t:new o.LatLngBounds(t,e)},o.Projection={},o.Projection.SphericalMercator={MAX_LATITUDE:85.0511287798,project:function(t){var e=o.LatLng.DEG_TO_RAD,i=this.MAX_LATITUDE,n=Math.max(Math.min(i,t.lat),-i),s=t.lng*e,a=n*e;return a=Math.log(Math.tan(Math.PI/4+a/2)),new o.Point(s,a)},unproject:function(t){var e=o.LatLng.RAD_TO_DEG,i=t.x*e,n=(2*Math.atan(Math.exp(t.y))-Math.PI/2)*e;return new o.LatLng(n,i)}},o.Projection.LonLat={project:function(t){return new o.Point(t.lng,t.lat)},unproject:function(t){return new o.LatLng(t.y,t.x)}},o.CRS={latLngToPoint:function(t,e){var i=this.projection.project(t),n=this.scale(e);return this.transformation._transform(i,n)},pointToLatLng:function(t,e){var i=this.scale(e),n=this.transformation.untransform(t,i);return this.projection.unproject(n)},project:function(t){return this.projection.project(t)},scale:function(t){return 256*Math.pow(2,t)},getSize:function(t){var e=this.scale(t);return o.point(e,e)}},o.CRS.Simple=o.extend({},o.CRS,{projection:o.Projection.LonLat,transformation:new o.Transformation(1,0,-1,0),scale:function(t){return Math.pow(2,t)}}),o.CRS.EPSG3857=o.extend({},o.CRS,{code:"EPSG:3857",projection:o.Projection.SphericalMercator,transformation:new o.Transformation(.5/Math.PI,.5,-.5/Math.PI,.5),project:function(t){var e=this.projection.project(t),i=6378137;return e.multiplyBy(i)}}),o.CRS.EPSG900913=o.extend({},o.CRS.EPSG3857,{code:"EPSG:900913"}),o.CRS.EPSG4326=o.extend({},o.CRS,{code:"EPSG:4326",projection:o.Projection.LonLat,transformation:new o.Transformation(1/360,.5,-1/360,.5)}),o.Map=o.Class.extend({includes:o.Mixin.Events,options:{crs:o.CRS.EPSG3857,fadeAnimation:o.DomUtil.TRANSITION&&!o.Browser.android23,trackResize:!0,markerZoomAnimation:o.DomUtil.TRANSITION&&o.Browser.any3d},initialize:function(t,e){e=o.setOptions(this,e),this._initContainer(t),this._initLayout(),this._onResize=o.bind(this._onResize,this),this._initEvents(),e.maxBounds&&this.setMaxBounds(e.maxBounds),e.center&&e.zoom!==i&&this.setView(o.latLng(e.center),e.zoom,{reset:!0}),this._handlers=[],this._layers={},this._zoomBoundLayers={},this._tileLayersNum=0,this.callInitHooks(),this._addLayers(e.layers)},setView:function(t,e){return e=e===i?this.getZoom():e,this._resetView(o.latLng(t),this._limitZoom(e)),this},setZoom:function(t,e){return this._loaded?this.setView(this.getCenter(),t,{zoom:e}):(this._zoom=this._limitZoom(t),this)},zoomIn:function(t,e){return this.setZoom(this._zoom+(t||1),e)},zoomOut:function(t,e){return this.setZoom(this._zoom-(t||1),e)},setZoomAround:function(t,e,i){var n=this.getZoomScale(e),s=this.getSize().divideBy(2),a=t instanceof o.Point?t:this.latLngToContainerPoint(t),r=a.subtract(s).multiplyBy(1-1/n),h=this.containerPointToLatLng(s.add(r));return this.setView(h,e,{zoom:i})},fitBounds:function(t,e){e=e||{},t=t.getBounds?t.getBounds():o.latLngBounds(t);var i=o.point(e.paddingTopLeft||e.padding||[0,0]),n=o.point(e.paddingBottomRight||e.padding||[0,0]),s=this.getBoundsZoom(t,!1,i.add(n)),a=n.subtract(i).divideBy(2),r=this.project(t.getSouthWest(),s),h=this.project(t.getNorthEast(),s),l=this.unproject(r.add(h).divideBy(2).add(a),s);return s=e&&e.maxZoom?Math.min(e.maxZoom,s):s,this.setView(l,s,e)},fitWorld:function(t){return this.fitBounds([[-90,-180],[90,180]],t)},panTo:function(t,e){return this.setView(t,this._zoom,{pan:e})},panBy:function(t){return this.fire("movestart"),this._rawPanBy(o.point(t)),this.fire("move"),this.fire("moveend")},setMaxBounds:function(t){return t=o.latLngBounds(t),this.options.maxBounds=t,t?(this._loaded&&this._panInsideMaxBounds(),this.on("moveend",this._panInsideMaxBounds,this)):this.off("moveend",this._panInsideMaxBounds,this)},panInsideBounds:function(t,e){var i=this.getCenter(),n=this._limitCenter(i,this._zoom,t);return i.equals(n)?this:this.panTo(n,e)},addLayer:function(t){var e=o.stamp(t);return this._layers[e]?this:(this._layers[e]=t,!t.options||isNaN(t.options.maxZoom)&&isNaN(t.options.minZoom)||(this._zoomBoundLayers[e]=t,this._updateZoomLevels()),this.options.zoomAnimation&&o.TileLayer&&t instanceof o.TileLayer&&(this._tileLayersNum++,this._tileLayersToLoad++,t.on("load",this._onTileLayerLoad,this)),this._loaded&&this._layerAdd(t),this)},removeLayer:function(t){var e=o.stamp(t);return this._layers[e]?(this._loaded&&t.onRemove(this),delete this._layers[e],this._loaded&&this.fire("layerremove",{layer:t}),this._zoomBoundLayers[e]&&(delete this._zoomBoundLayers[e],this._updateZoomLevels()),this.options.zoomAnimation&&o.TileLayer&&t instanceof o.TileLayer&&(this._tileLayersNum--,this._tileLayersToLoad--,t.off("load",this._onTileLayerLoad,this)),this):this},hasLayer:function(t){return t?o.stamp(t)in this._layers:!1},eachLayer:function(t,e){for(var i in this._layers)t.call(e,this._layers[i]);return this},invalidateSize:function(t){if(!this._loaded)return this;t=o.extend({animate:!1,pan:!0},t===!0?{animate:!0}:t);var e=this.getSize();this._sizeChanged=!0,this._initialCenter=null;var i=this.getSize(),n=e.divideBy(2).round(),s=i.divideBy(2).round(),a=n.subtract(s);return a.x||a.y?(t.animate&&t.pan?this.panBy(a):(t.pan&&this._rawPanBy(a),this.fire("move"),t.debounceMoveend?(clearTimeout(this._sizeTimer),this._sizeTimer=setTimeout(o.bind(this.fire,this,"moveend"),200)):this.fire("moveend")),this.fire("resize",{oldSize:e,newSize:i})):this},addHandler:function(t,e){if(!e)return this;var i=this[t]=new e(this);return this._handlers.push(i),this.options[t]&&i.enable(),this},remove:function(){this._loaded&&this.fire("unload"),this._initEvents("off");try{delete this._container._leaflet}catch(t){this._container._leaflet=i}return this._clearPanes(),this._clearControlPos&&this._clearControlPos(),this._clearHandlers(),this},getCenter:function(){return this._checkIfLoaded(),this._initialCenter&&!this._moved()?this._initialCenter:this.layerPointToLatLng(this._getCenterLayerPoint())},getZoom:function(){return this._zoom},getBounds:function(){var t=this.getPixelBounds(),e=this.unproject(t.getBottomLeft()),i=this.unproject(t.getTopRight());return new o.LatLngBounds(e,i)},getMinZoom:function(){return this.options.minZoom===i?this._layersMinZoom===i?0:this._layersMinZoom:this.options.minZoom},getMaxZoom:function(){return this.options.maxZoom===i?this._layersMaxZoom===i?1/0:this._layersMaxZoom:this.options.maxZoom},getBoundsZoom:function(t,e,i){t=o.latLngBounds(t);var n,s=this.getMinZoom()-(e?1:0),a=this.getMaxZoom(),r=this.getSize(),h=t.getNorthWest(),l=t.getSouthEast(),u=!0;i=o.point(i||[0,0]);do s++,n=this.project(l,s).subtract(this.project(h,s)).add(i),u=e?n.x=s);return u&&e?null:e?s:s-1},getSize:function(){return(!this._size||this._sizeChanged)&&(this._size=new o.Point(this._container.clientWidth,this._container.clientHeight),this._sizeChanged=!1),this._size.clone()},getPixelBounds:function(){var t=this._getTopLeftPoint();return new o.Bounds(t,t.add(this.getSize()))},getPixelOrigin:function(){return this._checkIfLoaded(),this._initialTopLeftPoint},getPanes:function(){return this._panes},getContainer:function(){return this._container},getZoomScale:function(t){var e=this.options.crs;return e.scale(t)/e.scale(this._zoom)},getScaleZoom:function(t){return this._zoom+Math.log(t)/Math.LN2},project:function(t,e){return e=e===i?this._zoom:e,this.options.crs.latLngToPoint(o.latLng(t),e)},unproject:function(t,e){return e=e===i?this._zoom:e,this.options.crs.pointToLatLng(o.point(t),e)},layerPointToLatLng:function(t){var e=o.point(t).add(this.getPixelOrigin());return this.unproject(e)},latLngToLayerPoint:function(t){var e=this.project(o.latLng(t))._round();return e._subtract(this.getPixelOrigin())},containerPointToLayerPoint:function(t){return o.point(t).subtract(this._getMapPanePos())},layerPointToContainerPoint:function(t){return o.point(t).add(this._getMapPanePos())},containerPointToLatLng:function(t){var e=this.containerPointToLayerPoint(o.point(t));return this.layerPointToLatLng(e)},latLngToContainerPoint:function(t){return this.layerPointToContainerPoint(this.latLngToLayerPoint(o.latLng(t)))},mouseEventToContainerPoint:function(t){return o.DomEvent.getMousePosition(t,this._container)},mouseEventToLayerPoint:function(t){return this.containerPointToLayerPoint(this.mouseEventToContainerPoint(t))},mouseEventToLatLng:function(t){return this.layerPointToLatLng(this.mouseEventToLayerPoint(t))},_initContainer:function(t){var e=this._container=o.DomUtil.get(t);if(!e)throw new Error("Map container not found.");if(e._leaflet)throw new Error("Map container is already initialized.");e._leaflet=!0},_initLayout:function(){var t=this._container;o.DomUtil.addClass(t,"leaflet-container"+(o.Browser.touch?" leaflet-touch":"")+(o.Browser.retina?" leaflet-retina":"")+(o.Browser.ielt9?" leaflet-oldie":"")+(this.options.fadeAnimation?" leaflet-fade-anim":""));var e=o.DomUtil.getStyle(t,"position");"absolute"!==e&&"relative"!==e&&"fixed"!==e&&(t.style.position="relative"),this._initPanes(),this._initControlPos&&this._initControlPos()},_initPanes:function(){var t=this._panes={};this._mapPane=t.mapPane=this._createPane("leaflet-map-pane",this._container),this._tilePane=t.tilePane=this._createPane("leaflet-tile-pane",this._mapPane),t.objectsPane=this._createPane("leaflet-objects-pane",this._mapPane),t.shadowPane=this._createPane("leaflet-shadow-pane"),t.overlayPane=this._createPane("leaflet-overlay-pane"),t.markerPane=this._createPane("leaflet-marker-pane"),t.popupPane=this._createPane("leaflet-popup-pane");var e=" leaflet-zoom-hide";this.options.markerZoomAnimation||(o.DomUtil.addClass(t.markerPane,e),o.DomUtil.addClass(t.shadowPane,e),o.DomUtil.addClass(t.popupPane,e))},_createPane:function(t,e){return o.DomUtil.create("div",t,e||this._panes.objectsPane)},_clearPanes:function(){this._container.removeChild(this._mapPane)},_addLayers:function(t){t=t?o.Util.isArray(t)?t:[t]:[];for(var e=0,i=t.length;i>e;e++)this.addLayer(t[e])},_resetView:function(t,e,i,n){var s=this._zoom!==e;n||(this.fire("movestart"),s&&this.fire("zoomstart")),this._zoom=e,this._initialCenter=t,this._initialTopLeftPoint=this._getNewTopLeftPoint(t),i?this._initialTopLeftPoint._add(this._getMapPanePos()):o.DomUtil.setPosition(this._mapPane,new o.Point(0,0)),this._tileLayersToLoad=this._tileLayersNum;var a=!this._loaded;this._loaded=!0,this.fire("viewreset",{hard:!i}),a&&(this.fire("load"),this.eachLayer(this._layerAdd,this)),this.fire("move"),(s||n)&&this.fire("zoomend"),this.fire("moveend",{hard:!i})},_rawPanBy:function(t){o.DomUtil.setPosition(this._mapPane,this._getMapPanePos().subtract(t))},_getZoomSpan:function(){return this.getMaxZoom()-this.getMinZoom()},_updateZoomLevels:function(){var t,e=1/0,n=-1/0,o=this._getZoomSpan();for(t in this._zoomBoundLayers){var s=this._zoomBoundLayers[t];isNaN(s.options.minZoom)||(e=Math.min(e,s.options.minZoom)),isNaN(s.options.maxZoom)||(n=Math.max(n,s.options.maxZoom))}t===i?this._layersMaxZoom=this._layersMinZoom=i:(this._layersMaxZoom=n,this._layersMinZoom=e),o!==this._getZoomSpan()&&this.fire("zoomlevelschange")},_panInsideMaxBounds:function(){this.panInsideBounds(this.options.maxBounds)},_checkIfLoaded:function(){if(!this._loaded)throw new Error("Set map center and zoom first.")},_initEvents:function(e){if(o.DomEvent){e=e||"on",o.DomEvent[e](this._container,"click",this._onMouseClick,this);var i,n,s=["dblclick","mousedown","mouseup","mouseenter","mouseleave","mousemove","contextmenu"];for(i=0,n=s.length;n>i;i++)o.DomEvent[e](this._container,s[i],this._fireMouseEvent,this);this.options.trackResize&&o.DomEvent[e](t,"resize",this._onResize,this)}},_onResize:function(){o.Util.cancelAnimFrame(this._resizeRequest),this._resizeRequest=o.Util.requestAnimFrame(function(){this.invalidateSize({debounceMoveend:!0})},this,!1,this._container)},_onMouseClick:function(t){!this._loaded||!t._simulated&&(this.dragging&&this.dragging.moved()||this.boxZoom&&this.boxZoom.moved())||o.DomEvent._skipped(t)||(this.fire("preclick"),this._fireMouseEvent(t))},_fireMouseEvent:function(t){if(this._loaded&&!o.DomEvent._skipped(t)){var e=t.type;if(e="mouseenter"===e?"mouseover":"mouseleave"===e?"mouseout":e,this.hasEventListeners(e)){"contextmenu"===e&&o.DomEvent.preventDefault(t);var i=this.mouseEventToContainerPoint(t),n=this.containerPointToLayerPoint(i),s=this.layerPointToLatLng(n);this.fire(e,{latlng:s,layerPoint:n,containerPoint:i,originalEvent:t})}}},_onTileLayerLoad:function(){this._tileLayersToLoad--,this._tileLayersNum&&!this._tileLayersToLoad&&this.fire("tilelayersload")},_clearHandlers:function(){for(var t=0,e=this._handlers.length;e>t;t++)this._handlers[t].disable()},whenReady:function(t,e){return this._loaded?t.call(e||this,this):this.on("load",t,e),this},_layerAdd:function(t){t.onAdd(this),this.fire("layeradd",{layer:t})},_getMapPanePos:function(){return o.DomUtil.getPosition(this._mapPane)},_moved:function(){var t=this._getMapPanePos();return t&&!t.equals([0,0])},_getTopLeftPoint:function(){return this.getPixelOrigin().subtract(this._getMapPanePos())},_getNewTopLeftPoint:function(t,e){var i=this.getSize()._divideBy(2);return this.project(t,e)._subtract(i)._round()},_latLngToNewLayerPoint:function(t,e,i){var n=this._getNewTopLeftPoint(i,e).add(this._getMapPanePos());return this.project(t,e)._subtract(n)},_getCenterLayerPoint:function(){return this.containerPointToLayerPoint(this.getSize()._divideBy(2))},_getCenterOffset:function(t){return this.latLngToLayerPoint(t).subtract(this._getCenterLayerPoint())},_limitCenter:function(t,e,i){if(!i)return t;var n=this.project(t,e),s=this.getSize().divideBy(2),a=new o.Bounds(n.subtract(s),n.add(s)),r=this._getBoundsOffset(a,i,e);return this.unproject(n.add(r),e)},_limitOffset:function(t,e){if(!e)return t;var i=this.getPixelBounds(),n=new o.Bounds(i.min.add(t),i.max.add(t));return t.add(this._getBoundsOffset(n,e))},_getBoundsOffset:function(t,e,i){var n=this.project(e.getNorthWest(),i).subtract(t.min),s=this.project(e.getSouthEast(),i).subtract(t.max),a=this._rebound(n.x,-s.x),r=this._rebound(n.y,-s.y);return new o.Point(a,r)},_rebound:function(t,e){return t+e>0?Math.round(t-e)/2:Math.max(0,Math.ceil(t))-Math.max(0,Math.floor(e))},_limitZoom:function(t){var e=this.getMinZoom(),i=this.getMaxZoom();return Math.max(e,Math.min(i,t))}}),o.map=function(t,e){return new o.Map(t,e)},o.Projection.Mercator={MAX_LATITUDE:85.0840591556,R_MINOR:6356752.314245179,R_MAJOR:6378137,project:function(t){var e=o.LatLng.DEG_TO_RAD,i=this.MAX_LATITUDE,n=Math.max(Math.min(i,t.lat),-i),s=this.R_MAJOR,a=this.R_MINOR,r=t.lng*e*s,h=n*e,l=a/s,u=Math.sqrt(1-l*l),c=u*Math.sin(h);c=Math.pow((1-c)/(1+c),.5*u);var d=Math.tan(.5*(.5*Math.PI-h))/c;return h=-s*Math.log(d),new o.Point(r,h)},unproject:function(t){for(var e,i=o.LatLng.RAD_TO_DEG,n=this.R_MAJOR,s=this.R_MINOR,a=t.x*i/n,r=s/n,h=Math.sqrt(1-r*r),l=Math.exp(-t.y/n),u=Math.PI/2-2*Math.atan(l),c=15,d=1e-7,p=c,_=.1;Math.abs(_)>d&&--p>0;)e=h*Math.sin(u),_=Math.PI/2-2*Math.atan(l*Math.pow((1-e)/(1+e),.5*h))-u,u+=_;
-return new o.LatLng(u*i,a)}},o.CRS.EPSG3395=o.extend({},o.CRS,{code:"EPSG:3395",projection:o.Projection.Mercator,transformation:function(){var t=o.Projection.Mercator,e=t.R_MAJOR,i=.5/(Math.PI*e);return new o.Transformation(i,.5,-i,.5)}()}),o.TileLayer=o.Class.extend({includes:o.Mixin.Events,options:{minZoom:0,maxZoom:18,tileSize:256,subdomains:"abc",errorTileUrl:"",attribution:"",zoomOffset:0,opacity:1,unloadInvisibleTiles:o.Browser.mobile,updateWhenIdle:o.Browser.mobile},initialize:function(t,e){e=o.setOptions(this,e),e.detectRetina&&o.Browser.retina&&e.maxZoom>0&&(e.tileSize=Math.floor(e.tileSize/2),e.zoomOffset++,e.minZoom>0&&e.minZoom--,this.options.maxZoom--),e.bounds&&(e.bounds=o.latLngBounds(e.bounds)),this._url=t;var i=this.options.subdomains;"string"==typeof i&&(this.options.subdomains=i.split(""))},onAdd:function(t){this._map=t,this._animated=t._zoomAnimated,this._initContainer(),t.on({viewreset:this._reset,moveend:this._update},this),this._animated&&t.on({zoomanim:this._animateZoom,zoomend:this._endZoomAnim},this),this.options.updateWhenIdle||(this._limitedUpdate=o.Util.limitExecByInterval(this._update,150,this),t.on("move",this._limitedUpdate,this)),this._reset(),this._update()},addTo:function(t){return t.addLayer(this),this},onRemove:function(t){this._container.parentNode.removeChild(this._container),t.off({viewreset:this._reset,moveend:this._update},this),this._animated&&t.off({zoomanim:this._animateZoom,zoomend:this._endZoomAnim},this),this.options.updateWhenIdle||t.off("move",this._limitedUpdate,this),this._container=null,this._map=null},bringToFront:function(){var t=this._map._panes.tilePane;return this._container&&(t.appendChild(this._container),this._setAutoZIndex(t,Math.max)),this},bringToBack:function(){var t=this._map._panes.tilePane;return this._container&&(t.insertBefore(this._container,t.firstChild),this._setAutoZIndex(t,Math.min)),this},getAttribution:function(){return this.options.attribution},getContainer:function(){return this._container},setOpacity:function(t){return this.options.opacity=t,this._map&&this._updateOpacity(),this},setZIndex:function(t){return this.options.zIndex=t,this._updateZIndex(),this},setUrl:function(t,e){return this._url=t,e||this.redraw(),this},redraw:function(){return this._map&&(this._reset({hard:!0}),this._update()),this},_updateZIndex:function(){this._container&&this.options.zIndex!==i&&(this._container.style.zIndex=this.options.zIndex)},_setAutoZIndex:function(t,e){var i,n,o,s=t.children,a=-e(1/0,-1/0);for(n=0,o=s.length;o>n;n++)s[n]!==this._container&&(i=parseInt(s[n].style.zIndex,10),isNaN(i)||(a=e(a,i)));this.options.zIndex=this._container.style.zIndex=(isFinite(a)?a:0)+e(1,-1)},_updateOpacity:function(){var t,e=this._tiles;if(o.Browser.ielt9)for(t in e)o.DomUtil.setOpacity(e[t],this.options.opacity);else o.DomUtil.setOpacity(this._container,this.options.opacity)},_initContainer:function(){var t=this._map._panes.tilePane;if(!this._container){if(this._container=o.DomUtil.create("div","leaflet-layer"),this._updateZIndex(),this._animated){var e="leaflet-tile-container";this._bgBuffer=o.DomUtil.create("div",e,this._container),this._tileContainer=o.DomUtil.create("div",e,this._container)}else this._tileContainer=this._container;t.appendChild(this._container),this.options.opacity<1&&this._updateOpacity()}},_reset:function(t){for(var e in this._tiles)this.fire("tileunload",{tile:this._tiles[e]});this._tiles={},this._tilesToLoad=0,this.options.reuseTiles&&(this._unusedTiles=[]),this._tileContainer.innerHTML="",this._animated&&t&&t.hard&&this._clearBgBuffer(),this._initContainer()},_getTileSize:function(){var t=this._map,e=t.getZoom()+this.options.zoomOffset,i=this.options.maxNativeZoom,n=this.options.tileSize;return i&&e>i&&(n=Math.round(t.getZoomScale(e)/t.getZoomScale(i)*n)),n},_update:function(){if(this._map){var t=this._map,e=t.getPixelBounds(),i=t.getZoom(),n=this._getTileSize();if(!(i>this.options.maxZoom||in;n++)this._addTile(a[n],l);this._tileContainer.appendChild(l)}},_tileShouldBeLoaded:function(t){if(t.x+":"+t.y in this._tiles)return!1;var e=this.options;if(!e.continuousWorld){var i=this._getWrapTileNum();if(e.noWrap&&(t.x<0||t.x>=i.x)||t.y<0||t.y>=i.y)return!1}if(e.bounds){var n=e.tileSize,o=t.multiplyBy(n),s=o.add([n,n]),a=this._map.unproject(o),r=this._map.unproject(s);if(e.continuousWorld||e.noWrap||(a=a.wrap(),r=r.wrap()),!e.bounds.intersects([a,r]))return!1}return!0},_removeOtherTiles:function(t){var e,i,n,o;for(o in this._tiles)e=o.split(":"),i=parseInt(e[0],10),n=parseInt(e[1],10),(it.max.x||nt.max.y)&&this._removeTile(o)},_removeTile:function(t){var e=this._tiles[t];this.fire("tileunload",{tile:e,url:e.src}),this.options.reuseTiles?(o.DomUtil.removeClass(e,"leaflet-tile-loaded"),this._unusedTiles.push(e)):e.parentNode===this._tileContainer&&this._tileContainer.removeChild(e),o.Browser.android||(e.onload=null,e.src=o.Util.emptyImageUrl),delete this._tiles[t]},_addTile:function(t,e){var i=this._getTilePos(t),n=this._getTile();o.DomUtil.setPosition(n,i,o.Browser.chrome),this._tiles[t.x+":"+t.y]=n,this._loadTile(n,t),n.parentNode!==this._tileContainer&&e.appendChild(n)},_getZoomForUrl:function(){var t=this.options,e=this._map.getZoom();return t.zoomReverse&&(e=t.maxZoom-e),e+=t.zoomOffset,t.maxNativeZoom?Math.min(e,t.maxNativeZoom):e},_getTilePos:function(t){var e=this._map.getPixelOrigin(),i=this._getTileSize();return t.multiplyBy(i).subtract(e)},getTileUrl:function(t){return o.Util.template(this._url,o.extend({s:this._getSubdomain(t),z:t.z,x:t.x,y:t.y},this.options))},_getWrapTileNum:function(){var t=this._map.options.crs,e=t.getSize(this._map.getZoom());return e.divideBy(this._getTileSize())._floor()},_adjustTilePoint:function(t){var e=this._getWrapTileNum();this.options.continuousWorld||this.options.noWrap||(t.x=(t.x%e.x+e.x)%e.x),this.options.tms&&(t.y=e.y-t.y-1),t.z=this._getZoomForUrl()},_getSubdomain:function(t){var e=Math.abs(t.x+t.y)%this.options.subdomains.length;return this.options.subdomains[e]},_getTile:function(){if(this.options.reuseTiles&&this._unusedTiles.length>0){var t=this._unusedTiles.pop();return this._resetTile(t),t}return this._createTile()},_resetTile:function(){},_createTile:function(){var t=o.DomUtil.create("img","leaflet-tile");return t.style.width=t.style.height=this._getTileSize()+"px",t.galleryimg="no",t.onselectstart=t.onmousemove=o.Util.falseFn,o.Browser.ielt9&&this.options.opacity!==i&&o.DomUtil.setOpacity(t,this.options.opacity),o.Browser.mobileWebkit3d&&(t.style.WebkitBackfaceVisibility="hidden"),t},_loadTile:function(t,e){t._layer=this,t.onload=this._tileOnLoad,t.onerror=this._tileOnError,this._adjustTilePoint(e),t.src=this.getTileUrl(e),this.fire("tileloadstart",{tile:t,url:t.src})},_tileLoaded:function(){this._tilesToLoad--,this._animated&&o.DomUtil.addClass(this._tileContainer,"leaflet-zoom-animated"),this._tilesToLoad||(this.fire("load"),this._animated&&(clearTimeout(this._clearBgBufferTimer),this._clearBgBufferTimer=setTimeout(o.bind(this._clearBgBuffer,this),500)))},_tileOnLoad:function(){var t=this._layer;this.src!==o.Util.emptyImageUrl&&(o.DomUtil.addClass(this,"leaflet-tile-loaded"),t.fire("tileload",{tile:this,url:this.src})),t._tileLoaded()},_tileOnError:function(){var t=this._layer;t.fire("tileerror",{tile:this,url:this.src});var e=t.options.errorTileUrl;e&&(this.src=e),t._tileLoaded()}}),o.tileLayer=function(t,e){return new o.TileLayer(t,e)},o.TileLayer.WMS=o.TileLayer.extend({defaultWmsParams:{service:"WMS",request:"GetMap",version:"1.1.1",layers:"",styles:"",format:"image/jpeg",transparent:!1},initialize:function(t,e){this._url=t;var i=o.extend({},this.defaultWmsParams),n=e.tileSize||this.options.tileSize;i.width=i.height=e.detectRetina&&o.Browser.retina?2*n:n;for(var s in e)this.options.hasOwnProperty(s)||"crs"===s||(i[s]=e[s]);this.wmsParams=i,o.setOptions(this,e)},onAdd:function(t){this._crs=this.options.crs||t.options.crs,this._wmsVersion=parseFloat(this.wmsParams.version);var e=this._wmsVersion>=1.3?"crs":"srs";this.wmsParams[e]=this._crs.code,o.TileLayer.prototype.onAdd.call(this,t)},getTileUrl:function(t){var e=this._map,i=this.options.tileSize,n=t.multiplyBy(i),s=n.add([i,i]),a=this._crs.project(e.unproject(n,t.z)),r=this._crs.project(e.unproject(s,t.z)),h=this._wmsVersion>=1.3&&this._crs===o.CRS.EPSG4326?[r.y,a.x,a.y,r.x].join(","):[a.x,r.y,r.x,a.y].join(","),l=o.Util.template(this._url,{s:this._getSubdomain(t)});return l+o.Util.getParamString(this.wmsParams,l,!0)+"&BBOX="+h},setParams:function(t,e){return o.extend(this.wmsParams,t),e||this.redraw(),this}}),o.tileLayer.wms=function(t,e){return new o.TileLayer.WMS(t,e)},o.TileLayer.Canvas=o.TileLayer.extend({options:{async:!1},initialize:function(t){o.setOptions(this,t)},redraw:function(){this._map&&(this._reset({hard:!0}),this._update());for(var t in this._tiles)this._redrawTile(this._tiles[t]);return this},_redrawTile:function(t){this.drawTile(t,t._tilePoint,this._map._zoom)},_createTile:function(){var t=o.DomUtil.create("canvas","leaflet-tile");return t.width=t.height=this.options.tileSize,t.onselectstart=t.onmousemove=o.Util.falseFn,t},_loadTile:function(t,e){t._layer=this,t._tilePoint=e,this._redrawTile(t),this.options.async||this.tileDrawn(t)},drawTile:function(){},tileDrawn:function(t){this._tileOnLoad.call(t)}}),o.tileLayer.canvas=function(t){return new o.TileLayer.Canvas(t)},o.ImageOverlay=o.Class.extend({includes:o.Mixin.Events,options:{opacity:1},initialize:function(t,e,i){this._url=t,this._bounds=o.latLngBounds(e),o.setOptions(this,i)},onAdd:function(t){this._map=t,this._image||this._initImage(),t._panes.overlayPane.appendChild(this._image),t.on("viewreset",this._reset,this),t.options.zoomAnimation&&o.Browser.any3d&&t.on("zoomanim",this._animateZoom,this),this._reset()},onRemove:function(t){t.getPanes().overlayPane.removeChild(this._image),t.off("viewreset",this._reset,this),t.options.zoomAnimation&&t.off("zoomanim",this._animateZoom,this)},addTo:function(t){return t.addLayer(this),this},setOpacity:function(t){return this.options.opacity=t,this._updateOpacity(),this},bringToFront:function(){return this._image&&this._map._panes.overlayPane.appendChild(this._image),this},bringToBack:function(){var t=this._map._panes.overlayPane;return this._image&&t.insertBefore(this._image,t.firstChild),this},setUrl:function(t){this._url=t,this._image.src=this._url},getAttribution:function(){return this.options.attribution},_initImage:function(){this._image=o.DomUtil.create("img","leaflet-image-layer"),this._map.options.zoomAnimation&&o.Browser.any3d?o.DomUtil.addClass(this._image,"leaflet-zoom-animated"):o.DomUtil.addClass(this._image,"leaflet-zoom-hide"),this._updateOpacity(),o.extend(this._image,{galleryimg:"no",onselectstart:o.Util.falseFn,onmousemove:o.Util.falseFn,onload:o.bind(this._onImageLoad,this),src:this._url})},_animateZoom:function(t){var e=this._map,i=this._image,n=e.getZoomScale(t.zoom),s=this._bounds.getNorthWest(),a=this._bounds.getSouthEast(),r=e._latLngToNewLayerPoint(s,t.zoom,t.center),h=e._latLngToNewLayerPoint(a,t.zoom,t.center)._subtract(r),l=r._add(h._multiplyBy(.5*(1-1/n)));i.style[o.DomUtil.TRANSFORM]=o.DomUtil.getTranslateString(l)+" scale("+n+") "},_reset:function(){var t=this._image,e=this._map.latLngToLayerPoint(this._bounds.getNorthWest()),i=this._map.latLngToLayerPoint(this._bounds.getSouthEast())._subtract(e);o.DomUtil.setPosition(t,e),t.style.width=i.x+"px",t.style.height=i.y+"px"},_onImageLoad:function(){this.fire("load")},_updateOpacity:function(){o.DomUtil.setOpacity(this._image,this.options.opacity)}}),o.imageOverlay=function(t,e,i){return new o.ImageOverlay(t,e,i)},o.Icon=o.Class.extend({options:{className:""},initialize:function(t){o.setOptions(this,t)},createIcon:function(t){return this._createIcon("icon",t)},createShadow:function(t){return this._createIcon("shadow",t)},_createIcon:function(t,e){var i=this._getIconUrl(t);if(!i){if("icon"===t)throw new Error("iconUrl not set in Icon options (see the docs).");return null}var n;return n=e&&"IMG"===e.tagName?this._createImg(i,e):this._createImg(i),this._setIconStyles(n,t),n},_setIconStyles:function(t,e){var i,n=this.options,s=o.point(n[e+"Size"]);i=o.point("shadow"===e?n.shadowAnchor||n.iconAnchor:n.iconAnchor),!i&&s&&(i=s.divideBy(2,!0)),t.className="leaflet-marker-"+e+" "+n.className,i&&(t.style.marginLeft=-i.x+"px",t.style.marginTop=-i.y+"px"),s&&(t.style.width=s.x+"px",t.style.height=s.y+"px")},_createImg:function(t,i){return i=i||e.createElement("img"),i.src=t,i},_getIconUrl:function(t){return o.Browser.retina&&this.options[t+"RetinaUrl"]?this.options[t+"RetinaUrl"]:this.options[t+"Url"]}}),o.icon=function(t){return new o.Icon(t)},o.Icon.Default=o.Icon.extend({options:{iconSize:[25,41],iconAnchor:[12,41],popupAnchor:[1,-34],shadowSize:[41,41]},_getIconUrl:function(t){var e=t+"Url";if(this.options[e])return this.options[e];o.Browser.retina&&"icon"===t&&(t+="-2x");var i=o.Icon.Default.imagePath;if(!i)throw new Error("Couldn't autodetect L.Icon.Default.imagePath, set it manually.");return i+"/marker-"+t+".png"}}),o.Icon.Default.imagePath=function(){var t,i,n,o,s,a=e.getElementsByTagName("script"),r=/[\/^]leaflet[\-\._]?([\w\-\._]*)\.js\??/;for(t=0,i=a.length;i>t;t++)if(n=a[t].src,o=n.match(r))return s=n.split(r)[0],(s?s+"/":"")+"images"}(),o.Marker=o.Class.extend({includes:o.Mixin.Events,options:{icon:new o.Icon.Default,title:"",alt:"",clickable:!0,draggable:!1,keyboard:!0,zIndexOffset:0,opacity:1,riseOnHover:!1,riseOffset:250},initialize:function(t,e){o.setOptions(this,e),this._latlng=o.latLng(t)},onAdd:function(t){this._map=t,t.on("viewreset",this.update,this),this._initIcon(),this.update(),this.fire("add"),t.options.zoomAnimation&&t.options.markerZoomAnimation&&t.on("zoomanim",this._animateZoom,this)},addTo:function(t){return t.addLayer(this),this},onRemove:function(t){this.dragging&&this.dragging.disable(),this._removeIcon(),this._removeShadow(),this.fire("remove"),t.off({viewreset:this.update,zoomanim:this._animateZoom},this),this._map=null},getLatLng:function(){return this._latlng},setLatLng:function(t){return this._latlng=o.latLng(t),this.update(),this.fire("move",{latlng:this._latlng})},setZIndexOffset:function(t){return this.options.zIndexOffset=t,this.update(),this},setIcon:function(t){return this.options.icon=t,this._map&&(this._initIcon(),this.update()),this._popup&&this.bindPopup(this._popup),this},update:function(){if(this._icon){var t=this._map.latLngToLayerPoint(this._latlng).round();this._setPos(t)}return this},_initIcon:function(){var t=this.options,e=this._map,i=e.options.zoomAnimation&&e.options.markerZoomAnimation,n=i?"leaflet-zoom-animated":"leaflet-zoom-hide",s=t.icon.createIcon(this._icon),a=!1;s!==this._icon&&(this._icon&&this._removeIcon(),a=!0,t.title&&(s.title=t.title),t.alt&&(s.alt=t.alt)),o.DomUtil.addClass(s,n),t.keyboard&&(s.tabIndex="0"),this._icon=s,this._initInteraction(),t.riseOnHover&&o.DomEvent.on(s,"mouseover",this._bringToFront,this).on(s,"mouseout",this._resetZIndex,this);var r=t.icon.createShadow(this._shadow),h=!1;r!==this._shadow&&(this._removeShadow(),h=!0),r&&o.DomUtil.addClass(r,n),this._shadow=r,t.opacity<1&&this._updateOpacity();var l=this._map._panes;a&&l.markerPane.appendChild(this._icon),r&&h&&l.shadowPane.appendChild(this._shadow)},_removeIcon:function(){this.options.riseOnHover&&o.DomEvent.off(this._icon,"mouseover",this._bringToFront).off(this._icon,"mouseout",this._resetZIndex),this._map._panes.markerPane.removeChild(this._icon),this._icon=null},_removeShadow:function(){this._shadow&&this._map._panes.shadowPane.removeChild(this._shadow),this._shadow=null},_setPos:function(t){o.DomUtil.setPosition(this._icon,t),this._shadow&&o.DomUtil.setPosition(this._shadow,t),this._zIndex=t.y+this.options.zIndexOffset,this._resetZIndex()},_updateZIndex:function(t){this._icon.style.zIndex=this._zIndex+t},_animateZoom:function(t){var e=this._map._latLngToNewLayerPoint(this._latlng,t.zoom,t.center).round();this._setPos(e)},_initInteraction:function(){if(this.options.clickable){var t=this._icon,e=["dblclick","mousedown","mouseover","mouseout","contextmenu"];o.DomUtil.addClass(t,"leaflet-clickable"),o.DomEvent.on(t,"click",this._onMouseClick,this),o.DomEvent.on(t,"keypress",this._onKeyPress,this);for(var i=0;is?(e.height=s+"px",o.DomUtil.addClass(t,a)):o.DomUtil.removeClass(t,a),this._containerWidth=this._container.offsetWidth},_updatePosition:function(){if(this._map){var t=this._map.latLngToLayerPoint(this._latlng),e=this._animated,i=o.point(this.options.offset);e&&o.DomUtil.setPosition(this._container,t),this._containerBottom=-i.y-(e?0:t.y),this._containerLeft=-Math.round(this._containerWidth/2)+i.x+(e?0:t.x),this._container.style.bottom=this._containerBottom+"px",this._container.style.left=this._containerLeft+"px"}},_zoomAnimation:function(t){var e=this._map._latLngToNewLayerPoint(this._latlng,t.zoom,t.center);o.DomUtil.setPosition(this._container,e)},_adjustPan:function(){if(this.options.autoPan){var t=this._map,e=this._container.offsetHeight,i=this._containerWidth,n=new o.Point(this._containerLeft,-e-this._containerBottom);this._animated&&n._add(o.DomUtil.getPosition(this._container));var s=t.layerPointToContainerPoint(n),a=o.point(this.options.autoPanPadding),r=o.point(this.options.autoPanPaddingTopLeft||a),h=o.point(this.options.autoPanPaddingBottomRight||a),l=t.getSize(),u=0,c=0;s.x+i+h.x>l.x&&(u=s.x+i-l.x+h.x),s.x-u-r.x<0&&(u=s.x-r.x),s.y+e+h.y>l.y&&(c=s.y+e-l.y+h.y),s.y-c-r.y<0&&(c=s.y-r.y),(u||c)&&t.fire("autopanstart").panBy([u,c])}},_onCloseButtonClick:function(t){this._close(),o.DomEvent.stop(t)}}),o.popup=function(t,e){return new o.Popup(t,e)},o.Map.include({openPopup:function(t,e,i){if(this.closePopup(),!(t instanceof o.Popup)){var n=t;t=new o.Popup(i).setLatLng(e).setContent(n)}return t._isOpen=!0,this._popup=t,this.addLayer(t)},closePopup:function(t){return t&&t!==this._popup||(t=this._popup,this._popup=null),t&&(this.removeLayer(t),t._isOpen=!1),this}}),o.Marker.include({openPopup:function(){return this._popup&&this._map&&!this._map.hasLayer(this._popup)&&(this._popup.setLatLng(this._latlng),this._map.openPopup(this._popup)),this},closePopup:function(){return this._popup&&this._popup._close(),this},togglePopup:function(){return this._popup&&(this._popup._isOpen?this.closePopup():this.openPopup()),this},bindPopup:function(t,e){var i=o.point(this.options.icon.options.popupAnchor||[0,0]);return i=i.add(o.Popup.prototype.options.offset),e&&e.offset&&(i=i.add(e.offset)),e=o.extend({offset:i},e),this._popupHandlersAdded||(this.on("click",this.togglePopup,this).on("remove",this.closePopup,this).on("move",this._movePopup,this),this._popupHandlersAdded=!0),t instanceof o.Popup?(o.setOptions(t,e),this._popup=t):this._popup=new o.Popup(e,this).setContent(t),this},setPopupContent:function(t){return this._popup&&this._popup.setContent(t),this},unbindPopup:function(){return this._popup&&(this._popup=null,this.off("click",this.togglePopup,this).off("remove",this.closePopup,this).off("move",this._movePopup,this),this._popupHandlersAdded=!1),this},getPopup:function(){return this._popup},_movePopup:function(t){this._popup.setLatLng(t.latlng)}}),o.LayerGroup=o.Class.extend({initialize:function(t){this._layers={};var e,i;if(t)for(e=0,i=t.length;i>e;e++)this.addLayer(t[e])},addLayer:function(t){var e=this.getLayerId(t);return this._layers[e]=t,this._map&&this._map.addLayer(t),this},removeLayer:function(t){var e=t in this._layers?t:this.getLayerId(t);return this._map&&this._layers[e]&&this._map.removeLayer(this._layers[e]),delete this._layers[e],this},hasLayer:function(t){return t?t in this._layers||this.getLayerId(t)in this._layers:!1},clearLayers:function(){return this.eachLayer(this.removeLayer,this),this},invoke:function(t){var e,i,n=Array.prototype.slice.call(arguments,1);for(e in this._layers)i=this._layers[e],i[t]&&i[t].apply(i,n);return this},onAdd:function(t){this._map=t,this.eachLayer(t.addLayer,t)},onRemove:function(t){this.eachLayer(t.removeLayer,t),this._map=null},addTo:function(t){return t.addLayer(this),this},eachLayer:function(t,e){for(var i in this._layers)t.call(e,this._layers[i]);return this},getLayer:function(t){return this._layers[t]},getLayers:function(){var t=[];for(var e in this._layers)t.push(this._layers[e]);return t},setZIndex:function(t){return this.invoke("setZIndex",t)},getLayerId:function(t){return o.stamp(t)}}),o.layerGroup=function(t){return new o.LayerGroup(t)},o.FeatureGroup=o.LayerGroup.extend({includes:o.Mixin.Events,statics:{EVENTS:"click dblclick mouseover mouseout mousemove contextmenu popupopen popupclose"},addLayer:function(t){return this.hasLayer(t)?this:("on"in t&&t.on(o.FeatureGroup.EVENTS,this._propagateEvent,this),o.LayerGroup.prototype.addLayer.call(this,t),this._popupContent&&t.bindPopup&&t.bindPopup(this._popupContent,this._popupOptions),this.fire("layeradd",{layer:t}))},removeLayer:function(t){return this.hasLayer(t)?(t in this._layers&&(t=this._layers[t]),t.off(o.FeatureGroup.EVENTS,this._propagateEvent,this),o.LayerGroup.prototype.removeLayer.call(this,t),this._popupContent&&this.invoke("unbindPopup"),this.fire("layerremove",{layer:t})):this},bindPopup:function(t,e){return this._popupContent=t,this._popupOptions=e,this.invoke("bindPopup",t,e)},openPopup:function(t){for(var e in this._layers){this._layers[e].openPopup(t);break}return this},setStyle:function(t){return this.invoke("setStyle",t)},bringToFront:function(){return this.invoke("bringToFront")},bringToBack:function(){return this.invoke("bringToBack")},getBounds:function(){var t=new o.LatLngBounds;return this.eachLayer(function(e){t.extend(e instanceof o.Marker?e.getLatLng():e.getBounds())}),t},_propagateEvent:function(t){t=o.extend({layer:t.target,target:this},t),this.fire(t.type,t)}}),o.featureGroup=function(t){return new o.FeatureGroup(t)},o.Path=o.Class.extend({includes:[o.Mixin.Events],statics:{CLIP_PADDING:function(){var e=o.Browser.mobile?1280:2e3,i=(e/Math.max(t.outerWidth,t.outerHeight)-1)/2;return Math.max(0,Math.min(.5,i))}()},options:{stroke:!0,color:"#0033ff",dashArray:null,lineCap:null,lineJoin:null,weight:5,opacity:.5,fill:!1,fillColor:null,fillOpacity:.2,clickable:!0},initialize:function(t){o.setOptions(this,t)},onAdd:function(t){this._map=t,this._container||(this._initElements(),this._initEvents()),this.projectLatlngs(),this._updatePath(),this._container&&this._map._pathRoot.appendChild(this._container),this.fire("add"),t.on({viewreset:this.projectLatlngs,moveend:this._updatePath},this)},addTo:function(t){return t.addLayer(this),this},onRemove:function(t){t._pathRoot.removeChild(this._container),this.fire("remove"),this._map=null,o.Browser.vml&&(this._container=null,this._stroke=null,this._fill=null),t.off({viewreset:this.projectLatlngs,moveend:this._updatePath},this)},projectLatlngs:function(){},setStyle:function(t){return o.setOptions(this,t),this._container&&this._updateStyle(),this},redraw:function(){return this._map&&(this.projectLatlngs(),this._updatePath()),this}}),o.Map.include({_updatePathViewport:function(){var t=o.Path.CLIP_PADDING,e=this.getSize(),i=o.DomUtil.getPosition(this._mapPane),n=i.multiplyBy(-1)._subtract(e.multiplyBy(t)._round()),s=n.add(e.multiplyBy(1+2*t)._round());this._pathViewport=new o.Bounds(n,s)}}),o.Path.SVG_NS="http://www.w3.org/2000/svg",o.Browser.svg=!(!e.createElementNS||!e.createElementNS(o.Path.SVG_NS,"svg").createSVGRect),o.Path=o.Path.extend({statics:{SVG:o.Browser.svg},bringToFront:function(){var t=this._map._pathRoot,e=this._container;return e&&t.lastChild!==e&&t.appendChild(e),this},bringToBack:function(){var t=this._map._pathRoot,e=this._container,i=t.firstChild;return e&&i!==e&&t.insertBefore(e,i),this},getPathString:function(){},_createElement:function(t){return e.createElementNS(o.Path.SVG_NS,t)},_initElements:function(){this._map._initPathRoot(),this._initPath(),this._initStyle()},_initPath:function(){this._container=this._createElement("g"),this._path=this._createElement("path"),this.options.className&&o.DomUtil.addClass(this._path,this.options.className),this._container.appendChild(this._path)},_initStyle:function(){this.options.stroke&&(this._path.setAttribute("stroke-linejoin","round"),this._path.setAttribute("stroke-linecap","round")),this.options.fill&&this._path.setAttribute("fill-rule","evenodd"),this.options.pointerEvents&&this._path.setAttribute("pointer-events",this.options.pointerEvents),this.options.clickable||this.options.pointerEvents||this._path.setAttribute("pointer-events","none"),this._updateStyle()},_updateStyle:function(){this.options.stroke?(this._path.setAttribute("stroke",this.options.color),this._path.setAttribute("stroke-opacity",this.options.opacity),this._path.setAttribute("stroke-width",this.options.weight),this.options.dashArray?this._path.setAttribute("stroke-dasharray",this.options.dashArray):this._path.removeAttribute("stroke-dasharray"),this.options.lineCap&&this._path.setAttribute("stroke-linecap",this.options.lineCap),this.options.lineJoin&&this._path.setAttribute("stroke-linejoin",this.options.lineJoin)):this._path.setAttribute("stroke","none"),this.options.fill?(this._path.setAttribute("fill",this.options.fillColor||this.options.color),this._path.setAttribute("fill-opacity",this.options.fillOpacity)):this._path.setAttribute("fill","none")},_updatePath:function(){var t=this.getPathString();t||(t="M0 0"),this._path.setAttribute("d",t)},_initEvents:function(){if(this.options.clickable){(o.Browser.svg||!o.Browser.vml)&&o.DomUtil.addClass(this._path,"leaflet-clickable"),o.DomEvent.on(this._container,"click",this._onMouseClick,this);for(var t=["dblclick","mousedown","mouseover","mouseout","mousemove","contextmenu"],e=0;e ';var i=t.firstChild;return i.style.behavior="url(#default#VML)",i&&"object"==typeof i.adj}catch(n){return!1}}(),o.Path=o.Browser.svg||!o.Browser.vml?o.Path:o.Path.extend({statics:{VML:!0,CLIP_PADDING:.02},_createElement:function(){try{return e.namespaces.add("lvml","urn:schemas-microsoft-com:vml"),function(t){return e.createElement("')}}catch(t){return function(t){return e.createElement("<"+t+' xmlns="urn:schemas-microsoft.com:vml" class="lvml">')}}}(),_initPath:function(){var t=this._container=this._createElement("shape");o.DomUtil.addClass(t,"leaflet-vml-shape"+(this.options.className?" "+this.options.className:"")),this.options.clickable&&o.DomUtil.addClass(t,"leaflet-clickable"),t.coordsize="1 1",this._path=this._createElement("path"),t.appendChild(this._path),this._map._pathRoot.appendChild(t)},_initStyle:function(){this._updateStyle()},_updateStyle:function(){var t=this._stroke,e=this._fill,i=this.options,n=this._container;n.stroked=i.stroke,n.filled=i.fill,i.stroke?(t||(t=this._stroke=this._createElement("stroke"),t.endcap="round",n.appendChild(t)),t.weight=i.weight+"px",t.color=i.color,t.opacity=i.opacity,t.dashStyle=i.dashArray?o.Util.isArray(i.dashArray)?i.dashArray.join(" "):i.dashArray.replace(/( *, *)/g," "):"",i.lineCap&&(t.endcap=i.lineCap.replace("butt","flat")),i.lineJoin&&(t.joinstyle=i.lineJoin)):t&&(n.removeChild(t),this._stroke=null),i.fill?(e||(e=this._fill=this._createElement("fill"),n.appendChild(e)),e.color=i.fillColor||i.color,e.opacity=i.fillOpacity):e&&(n.removeChild(e),this._fill=null)},_updatePath:function(){var t=this._container.style;t.display="none",this._path.v=this.getPathString()+" ",t.display=""}}),o.Map.include(o.Browser.svg||!o.Browser.vml?{}:{_initPathRoot:function(){if(!this._pathRoot){var t=this._pathRoot=e.createElement("div");t.className="leaflet-vml-container",this._panes.overlayPane.appendChild(t),this.on("moveend",this._updatePathViewport),this._updatePathViewport()}}}),o.Browser.canvas=function(){return!!e.createElement("canvas").getContext}(),o.Path=o.Path.SVG&&!t.L_PREFER_CANVAS||!o.Browser.canvas?o.Path:o.Path.extend({statics:{CANVAS:!0,SVG:!1},redraw:function(){return this._map&&(this.projectLatlngs(),this._requestUpdate()),this},setStyle:function(t){return o.setOptions(this,t),this._map&&(this._updateStyle(),this._requestUpdate()),this},onRemove:function(t){t.off("viewreset",this.projectLatlngs,this).off("moveend",this._updatePath,this),this.options.clickable&&(this._map.off("click",this._onClick,this),this._map.off("mousemove",this._onMouseMove,this)),this._requestUpdate(),this.fire("remove"),this._map=null},_requestUpdate:function(){this._map&&!o.Path._updateRequest&&(o.Path._updateRequest=o.Util.requestAnimFrame(this._fireMapMoveEnd,this._map))},_fireMapMoveEnd:function(){o.Path._updateRequest=null,this.fire("moveend")},_initElements:function(){this._map._initPathRoot(),this._ctx=this._map._canvasCtx},_updateStyle:function(){var t=this.options;t.stroke&&(this._ctx.lineWidth=t.weight,this._ctx.strokeStyle=t.color),t.fill&&(this._ctx.fillStyle=t.fillColor||t.color)},_drawPath:function(){var t,e,i,n,s,a;for(this._ctx.beginPath(),t=0,i=this._parts.length;i>t;t++){for(e=0,n=this._parts[t].length;n>e;e++)s=this._parts[t][e],a=(0===e?"move":"line")+"To",this._ctx[a](s.x,s.y);this instanceof o.Polygon&&this._ctx.closePath()}},_checkIfEmpty:function(){return!this._parts.length},_updatePath:function(){if(!this._checkIfEmpty()){var t=this._ctx,e=this.options;this._drawPath(),t.save(),this._updateStyle(),e.fill&&(t.globalAlpha=e.fillOpacity,t.fill()),e.stroke&&(t.globalAlpha=e.opacity,t.stroke()),t.restore()}},_initEvents:function(){this.options.clickable&&(this._map.on("mousemove",this._onMouseMove,this),this._map.on("click",this._onClick,this))},_onClick:function(t){this._containsPoint(t.layerPoint)&&this.fire("click",t)},_onMouseMove:function(t){this._map&&!this._map._animatingZoom&&(this._containsPoint(t.layerPoint)?(this._ctx.canvas.style.cursor="pointer",this._mouseInside=!0,this.fire("mouseover",t)):this._mouseInside&&(this._ctx.canvas.style.cursor="",this._mouseInside=!1,this.fire("mouseout",t)))}}),o.Map.include(o.Path.SVG&&!t.L_PREFER_CANVAS||!o.Browser.canvas?{}:{_initPathRoot:function(){var t,i=this._pathRoot;i||(i=this._pathRoot=e.createElement("canvas"),i.style.position="absolute",t=this._canvasCtx=i.getContext("2d"),t.lineCap="round",t.lineJoin="round",this._panes.overlayPane.appendChild(i),this.options.zoomAnimation&&(this._pathRoot.className="leaflet-zoom-animated",this.on("zoomanim",this._animatePathZoom),this.on("zoomend",this._endPathZoom)),this.on("moveend",this._updateCanvasViewport),this._updateCanvasViewport())},_updateCanvasViewport:function(){if(!this._pathZooming){this._updatePathViewport();var t=this._pathViewport,e=t.min,i=t.max.subtract(e),n=this._pathRoot;o.DomUtil.setPosition(n,e),n.width=i.x,n.height=i.y,n.getContext("2d").translate(-e.x,-e.y)}}}),o.LineUtil={simplify:function(t,e){if(!e||!t.length)return t.slice();var i=e*e;return t=this._reducePoints(t,i),t=this._simplifyDP(t,i)},pointToSegmentDistance:function(t,e,i){return Math.sqrt(this._sqClosestPointOnSegment(t,e,i,!0))},closestPointOnSegment:function(t,e,i){return this._sqClosestPointOnSegment(t,e,i)},_simplifyDP:function(t,e){var n=t.length,o=typeof Uint8Array!=i+""?Uint8Array:Array,s=new o(n);s[0]=s[n-1]=1,this._simplifyDPStep(t,s,e,0,n-1);var a,r=[];for(a=0;n>a;a++)s[a]&&r.push(t[a]);return r},_simplifyDPStep:function(t,e,i,n,o){var s,a,r,h=0;for(a=n+1;o-1>=a;a++)r=this._sqClosestPointOnSegment(t[a],t[n],t[o],!0),r>h&&(s=a,h=r);h>i&&(e[s]=1,this._simplifyDPStep(t,e,i,n,s),this._simplifyDPStep(t,e,i,s,o))},_reducePoints:function(t,e){for(var i=[t[0]],n=1,o=0,s=t.length;s>n;n++)this._sqDist(t[n],t[o])>e&&(i.push(t[n]),o=n);return s-1>o&&i.push(t[s-1]),i},clipSegment:function(t,e,i,n){var o,s,a,r=n?this._lastCode:this._getBitCode(t,i),h=this._getBitCode(e,i);for(this._lastCode=h;;){if(!(r|h))return[t,e];if(r&h)return!1;o=r||h,s=this._getEdgeIntersection(t,e,o,i),a=this._getBitCode(s,i),o===r?(t=s,r=a):(e=s,h=a)}},_getEdgeIntersection:function(t,e,i,n){var s=e.x-t.x,a=e.y-t.y,r=n.min,h=n.max;return 8&i?new o.Point(t.x+s*(h.y-t.y)/a,h.y):4&i?new o.Point(t.x+s*(r.y-t.y)/a,r.y):2&i?new o.Point(h.x,t.y+a*(h.x-t.x)/s):1&i?new o.Point(r.x,t.y+a*(r.x-t.x)/s):void 0},_getBitCode:function(t,e){var i=0;return t.xe.max.x&&(i|=2),t.ye.max.y&&(i|=8),i},_sqDist:function(t,e){var i=e.x-t.x,n=e.y-t.y;return i*i+n*n},_sqClosestPointOnSegment:function(t,e,i,n){var s,a=e.x,r=e.y,h=i.x-a,l=i.y-r,u=h*h+l*l;return u>0&&(s=((t.x-a)*h+(t.y-r)*l)/u,s>1?(a=i.x,r=i.y):s>0&&(a+=h*s,r+=l*s)),h=t.x-a,l=t.y-r,n?h*h+l*l:new o.Point(a,r)}},o.Polyline=o.Path.extend({initialize:function(t,e){o.Path.prototype.initialize.call(this,e),this._latlngs=this._convertLatLngs(t)},options:{smoothFactor:1,noClip:!1},projectLatlngs:function(){this._originalPoints=[];for(var t=0,e=this._latlngs.length;e>t;t++)this._originalPoints[t]=this._map.latLngToLayerPoint(this._latlngs[t])},getPathString:function(){for(var t=0,e=this._parts.length,i="";e>t;t++)i+=this._getPathPartStr(this._parts[t]);return i},getLatLngs:function(){return this._latlngs},setLatLngs:function(t){return this._latlngs=this._convertLatLngs(t),this.redraw()},addLatLng:function(t){return this._latlngs.push(o.latLng(t)),this.redraw()},spliceLatLngs:function(){var t=[].splice.apply(this._latlngs,arguments);return this._convertLatLngs(this._latlngs,!0),this.redraw(),t},closestLayerPoint:function(t){for(var e,i,n=1/0,s=this._parts,a=null,r=0,h=s.length;h>r;r++)for(var l=s[r],u=1,c=l.length;c>u;u++){e=l[u-1],i=l[u];var d=o.LineUtil._sqClosestPointOnSegment(t,e,i,!0);n>d&&(n=d,a=o.LineUtil._sqClosestPointOnSegment(t,e,i))}return a&&(a.distance=Math.sqrt(n)),a},getBounds:function(){return new o.LatLngBounds(this.getLatLngs())},_convertLatLngs:function(t,e){var i,n,s=e?t:[];for(i=0,n=t.length;n>i;i++){if(o.Util.isArray(t[i])&&"number"!=typeof t[i][0])return;s[i]=o.latLng(t[i])}return s},_initEvents:function(){o.Path.prototype._initEvents.call(this)},_getPathPartStr:function(t){for(var e,i=o.Path.VML,n=0,s=t.length,a="";s>n;n++)e=t[n],i&&e._round(),a+=(n?"L":"M")+e.x+" "+e.y;return a},_clipPoints:function(){var t,e,i,n=this._originalPoints,s=n.length;if(this.options.noClip)return void(this._parts=[n]);this._parts=[];var a=this._parts,r=this._map._pathViewport,h=o.LineUtil;for(t=0,e=0;s-1>t;t++)i=h.clipSegment(n[t],n[t+1],r,t),i&&(a[e]=a[e]||[],a[e].push(i[0]),(i[1]!==n[t+1]||t===s-2)&&(a[e].push(i[1]),e++))},_simplifyPoints:function(){for(var t=this._parts,e=o.LineUtil,i=0,n=t.length;n>i;i++)t[i]=e.simplify(t[i],this.options.smoothFactor)},_updatePath:function(){this._map&&(this._clipPoints(),this._simplifyPoints(),o.Path.prototype._updatePath.call(this))}}),o.polyline=function(t,e){return new o.Polyline(t,e)},o.PolyUtil={},o.PolyUtil.clipPolygon=function(t,e){var i,n,s,a,r,h,l,u,c,d=[1,4,2,8],p=o.LineUtil;for(n=0,l=t.length;l>n;n++)t[n]._code=p._getBitCode(t[n],e);for(a=0;4>a;a++){for(u=d[a],i=[],n=0,l=t.length,s=l-1;l>n;s=n++)r=t[n],h=t[s],r._code&u?h._code&u||(c=p._getEdgeIntersection(h,r,u,e),c._code=p._getBitCode(c,e),i.push(c)):(h._code&u&&(c=p._getEdgeIntersection(h,r,u,e),c._code=p._getBitCode(c,e),i.push(c)),i.push(r));t=i}return t},o.Polygon=o.Polyline.extend({options:{fill:!0},initialize:function(t,e){o.Polyline.prototype.initialize.call(this,t,e),this._initWithHoles(t)},_initWithHoles:function(t){var e,i,n;if(t&&o.Util.isArray(t[0])&&"number"!=typeof t[0][0])for(this._latlngs=this._convertLatLngs(t[0]),this._holes=t.slice(1),e=0,i=this._holes.length;i>e;e++)n=this._holes[e]=this._convertLatLngs(this._holes[e]),n[0].equals(n[n.length-1])&&n.pop();t=this._latlngs,t.length>=2&&t[0].equals(t[t.length-1])&&t.pop()},projectLatlngs:function(){if(o.Polyline.prototype.projectLatlngs.call(this),this._holePoints=[],this._holes){var t,e,i,n;for(t=0,i=this._holes.length;i>t;t++)for(this._holePoints[t]=[],e=0,n=this._holes[t].length;n>e;e++)this._holePoints[t][e]=this._map.latLngToLayerPoint(this._holes[t][e])}},setLatLngs:function(t){return t&&o.Util.isArray(t[0])&&"number"!=typeof t[0][0]?(this._initWithHoles(t),this.redraw()):o.Polyline.prototype.setLatLngs.call(this,t)},_clipPoints:function(){var t=this._originalPoints,e=[];if(this._parts=[t].concat(this._holePoints),!this.options.noClip){for(var i=0,n=this._parts.length;n>i;i++){var s=o.PolyUtil.clipPolygon(this._parts[i],this._map._pathViewport);s.length&&e.push(s)}this._parts=e}},_getPathPartStr:function(t){var e=o.Polyline.prototype._getPathPartStr.call(this,t);return e+(o.Browser.svg?"z":"x")}}),o.polygon=function(t,e){return new o.Polygon(t,e)},function(){function t(t){return o.FeatureGroup.extend({initialize:function(t,e){this._layers={},this._options=e,this.setLatLngs(t)},setLatLngs:function(e){var i=0,n=e.length;for(this.eachLayer(function(t){n>i?t.setLatLngs(e[i++]):this.removeLayer(t)},this);n>i;)this.addLayer(new t(e[i++],this._options));return this},getLatLngs:function(){var t=[];return this.eachLayer(function(e){t.push(e.getLatLngs())}),t}})}o.MultiPolyline=t(o.Polyline),o.MultiPolygon=t(o.Polygon),o.multiPolyline=function(t,e){return new o.MultiPolyline(t,e)},o.multiPolygon=function(t,e){return new o.MultiPolygon(t,e)}}(),o.Rectangle=o.Polygon.extend({initialize:function(t,e){o.Polygon.prototype.initialize.call(this,this._boundsToLatLngs(t),e)},setBounds:function(t){this.setLatLngs(this._boundsToLatLngs(t))},_boundsToLatLngs:function(t){return t=o.latLngBounds(t),[t.getSouthWest(),t.getNorthWest(),t.getNorthEast(),t.getSouthEast()]}}),o.rectangle=function(t,e){return new o.Rectangle(t,e)},o.Circle=o.Path.extend({initialize:function(t,e,i){o.Path.prototype.initialize.call(this,i),this._latlng=o.latLng(t),this._mRadius=e},options:{fill:!0},setLatLng:function(t){return this._latlng=o.latLng(t),this.redraw()},setRadius:function(t){return this._mRadius=t,this.redraw()},projectLatlngs:function(){var t=this._getLngRadius(),e=this._latlng,i=this._map.latLngToLayerPoint([e.lat,e.lng-t]);this._point=this._map.latLngToLayerPoint(e),this._radius=Math.max(this._point.x-i.x,1)},getBounds:function(){var t=this._getLngRadius(),e=this._mRadius/40075017*360,i=this._latlng;return new o.LatLngBounds([i.lat-e,i.lng-t],[i.lat+e,i.lng+t])},getLatLng:function(){return this._latlng},getPathString:function(){var t=this._point,e=this._radius;return this._checkIfEmpty()?"":o.Browser.svg?"M"+t.x+","+(t.y-e)+"A"+e+","+e+",0,1,1,"+(t.x-.1)+","+(t.y-e)+" z":(t._round(),e=Math.round(e),"AL "+t.x+","+t.y+" "+e+","+e+" 0,23592600")},getRadius:function(){return this._mRadius},_getLatRadius:function(){return this._mRadius/40075017*360},_getLngRadius:function(){return this._getLatRadius()/Math.cos(o.LatLng.DEG_TO_RAD*this._latlng.lat)},_checkIfEmpty:function(){if(!this._map)return!1;var t=this._map._pathViewport,e=this._radius,i=this._point;return i.x-e>t.max.x||i.y-e>t.max.y||i.x+ei;i++)for(l=this._parts[i],n=0,r=l.length,s=r-1;r>n;s=n++)if((e||0!==n)&&(h=o.LineUtil.pointToSegmentDistance(t,l[s],l[n]),u>=h))return!0;return!1}}:{}),o.Polygon.include(o.Path.CANVAS?{_containsPoint:function(t){var e,i,n,s,a,r,h,l,u=!1;if(o.Polyline.prototype._containsPoint.call(this,t,!0))return!0;for(s=0,h=this._parts.length;h>s;s++)for(e=this._parts[s],a=0,l=e.length,r=l-1;l>a;r=a++)i=e[a],n=e[r],i.y>t.y!=n.y>t.y&&t.x<(n.x-i.x)*(t.y-i.y)/(n.y-i.y)+i.x&&(u=!u);return u}}:{}),o.Circle.include(o.Path.CANVAS?{_drawPath:function(){var t=this._point;this._ctx.beginPath(),this._ctx.arc(t.x,t.y,this._radius,0,2*Math.PI,!1)},_containsPoint:function(t){var e=this._point,i=this.options.stroke?this.options.weight/2:0;return t.distanceTo(e)<=this._radius+i}}:{}),o.CircleMarker.include(o.Path.CANVAS?{_updateStyle:function(){o.Path.prototype._updateStyle.call(this)}}:{}),o.GeoJSON=o.FeatureGroup.extend({initialize:function(t,e){o.setOptions(this,e),this._layers={},t&&this.addData(t)},addData:function(t){var e,i,n,s=o.Util.isArray(t)?t:t.features;if(s){for(e=0,i=s.length;i>e;e++)n=s[e],(n.geometries||n.geometry||n.features||n.coordinates)&&this.addData(s[e]);return this}var a=this.options;if(!a.filter||a.filter(t)){var r=o.GeoJSON.geometryToLayer(t,a.pointToLayer,a.coordsToLatLng,a);return r.feature=o.GeoJSON.asFeature(t),r.defaultOptions=r.options,this.resetStyle(r),a.onEachFeature&&a.onEachFeature(t,r),this.addLayer(r)}},resetStyle:function(t){var e=this.options.style;e&&(o.Util.extend(t.options,t.defaultOptions),this._setLayerStyle(t,e))},setStyle:function(t){this.eachLayer(function(e){this._setLayerStyle(e,t)},this)},_setLayerStyle:function(t,e){"function"==typeof e&&(e=e(t.feature)),t.setStyle&&t.setStyle(e)}}),o.extend(o.GeoJSON,{geometryToLayer:function(t,e,i,n){var s,a,r,h,l="Feature"===t.type?t.geometry:t,u=l.coordinates,c=[];switch(i=i||this.coordsToLatLng,l.type){case"Point":return s=i(u),e?e(t,s):new o.Marker(s);case"MultiPoint":for(r=0,h=u.length;h>r;r++)s=i(u[r]),c.push(e?e(t,s):new o.Marker(s));return new o.FeatureGroup(c);case"LineString":return a=this.coordsToLatLngs(u,0,i),new o.Polyline(a,n);case"Polygon":if(2===u.length&&!u[1].length)throw new Error("Invalid GeoJSON object.");return a=this.coordsToLatLngs(u,1,i),new o.Polygon(a,n);case"MultiLineString":return a=this.coordsToLatLngs(u,1,i),new o.MultiPolyline(a,n);case"MultiPolygon":return a=this.coordsToLatLngs(u,2,i),new o.MultiPolygon(a,n);case"GeometryCollection":for(r=0,h=l.geometries.length;h>r;r++)c.push(this.geometryToLayer({geometry:l.geometries[r],type:"Feature",properties:t.properties},e,i,n));return new o.FeatureGroup(c);default:throw new Error("Invalid GeoJSON object.")}},coordsToLatLng:function(t){return new o.LatLng(t[1],t[0],t[2])},coordsToLatLngs:function(t,e,i){var n,o,s,a=[];for(o=0,s=t.length;s>o;o++)n=e?this.coordsToLatLngs(t[o],e-1,i):(i||this.coordsToLatLng)(t[o]),a.push(n);return a},latLngToCoords:function(t){var e=[t.lng,t.lat];return t.alt!==i&&e.push(t.alt),e},latLngsToCoords:function(t){for(var e=[],i=0,n=t.length;n>i;i++)e.push(o.GeoJSON.latLngToCoords(t[i]));return e},getFeature:function(t,e){return t.feature?o.extend({},t.feature,{geometry:e}):o.GeoJSON.asFeature(e)},asFeature:function(t){return"Feature"===t.type?t:{type:"Feature",properties:{},geometry:t}}});var a={toGeoJSON:function(){return o.GeoJSON.getFeature(this,{type:"Point",coordinates:o.GeoJSON.latLngToCoords(this.getLatLng())})}};o.Marker.include(a),o.Circle.include(a),o.CircleMarker.include(a),o.Polyline.include({toGeoJSON:function(){return o.GeoJSON.getFeature(this,{type:"LineString",coordinates:o.GeoJSON.latLngsToCoords(this.getLatLngs())})}}),o.Polygon.include({toGeoJSON:function(){var t,e,i,n=[o.GeoJSON.latLngsToCoords(this.getLatLngs())];if(n[0].push(n[0][0]),this._holes)for(t=0,e=this._holes.length;e>t;t++)i=o.GeoJSON.latLngsToCoords(this._holes[t]),i.push(i[0]),n.push(i);return o.GeoJSON.getFeature(this,{type:"Polygon",coordinates:n})}}),function(){function t(t){return function(){var e=[];return this.eachLayer(function(t){e.push(t.toGeoJSON().geometry.coordinates)}),o.GeoJSON.getFeature(this,{type:t,coordinates:e})}}o.MultiPolyline.include({toGeoJSON:t("MultiLineString")}),o.MultiPolygon.include({toGeoJSON:t("MultiPolygon")}),o.LayerGroup.include({toGeoJSON:function(){var e,i=this.feature&&this.feature.geometry,n=[];if(i&&"MultiPoint"===i.type)return t("MultiPoint").call(this);var s=i&&"GeometryCollection"===i.type;return this.eachLayer(function(t){t.toGeoJSON&&(e=t.toGeoJSON(),n.push(s?e.geometry:o.GeoJSON.asFeature(e)))}),s?o.GeoJSON.getFeature(this,{geometries:n,type:"GeometryCollection"}):{type:"FeatureCollection",features:n}}})}(),o.geoJson=function(t,e){return new o.GeoJSON(t,e)},o.DomEvent={addListener:function(t,e,i,n){var s,a,r,h=o.stamp(i),l="_leaflet_"+e+h;return t[l]?this:(s=function(e){return i.call(n||t,e||o.DomEvent._getEvent())},o.Browser.pointer&&0===e.indexOf("touch")?this.addPointerListener(t,e,s,h):(o.Browser.touch&&"dblclick"===e&&this.addDoubleTapListener&&this.addDoubleTapListener(t,s,h),"addEventListener"in t?"mousewheel"===e?(t.addEventListener("DOMMouseScroll",s,!1),t.addEventListener(e,s,!1)):"mouseenter"===e||"mouseleave"===e?(a=s,r="mouseenter"===e?"mouseover":"mouseout",s=function(e){return o.DomEvent._checkMouse(t,e)?a(e):void 0},t.addEventListener(r,s,!1)):"click"===e&&o.Browser.android?(a=s,s=function(t){return o.DomEvent._filterClick(t,a)},t.addEventListener(e,s,!1)):t.addEventListener(e,s,!1):"attachEvent"in t&&t.attachEvent("on"+e,s),t[l]=s,this))},removeListener:function(t,e,i){var n=o.stamp(i),s="_leaflet_"+e+n,a=t[s];return a?(o.Browser.pointer&&0===e.indexOf("touch")?this.removePointerListener(t,e,n):o.Browser.touch&&"dblclick"===e&&this.removeDoubleTapListener?this.removeDoubleTapListener(t,n):"removeEventListener"in t?"mousewheel"===e?(t.removeEventListener("DOMMouseScroll",a,!1),t.removeEventListener(e,a,!1)):"mouseenter"===e||"mouseleave"===e?t.removeEventListener("mouseenter"===e?"mouseover":"mouseout",a,!1):t.removeEventListener(e,a,!1):"detachEvent"in t&&t.detachEvent("on"+e,a),t[s]=null,this):this},stopPropagation:function(t){return t.stopPropagation?t.stopPropagation():t.cancelBubble=!0,o.DomEvent._skipped(t),this},disableScrollPropagation:function(t){var e=o.DomEvent.stopPropagation;return o.DomEvent.on(t,"mousewheel",e).on(t,"MozMousePixelScroll",e)},disableClickPropagation:function(t){for(var e=o.DomEvent.stopPropagation,i=o.Draggable.START.length-1;i>=0;i--)o.DomEvent.on(t,o.Draggable.START[i],e);return o.DomEvent.on(t,"click",o.DomEvent._fakeStop).on(t,"dblclick",e)},preventDefault:function(t){return t.preventDefault?t.preventDefault():t.returnValue=!1,this},stop:function(t){return o.DomEvent.preventDefault(t).stopPropagation(t)},getMousePosition:function(t,e){if(!e)return new o.Point(t.clientX,t.clientY);var i=e.getBoundingClientRect();return new o.Point(t.clientX-i.left-e.clientLeft,t.clientY-i.top-e.clientTop)},getWheelDelta:function(t){var e=0;return t.wheelDelta&&(e=t.wheelDelta/120),t.detail&&(e=-t.detail/3),e},_skipEvents:{},_fakeStop:function(t){o.DomEvent._skipEvents[t.type]=!0},_skipped:function(t){var e=this._skipEvents[t.type];return this._skipEvents[t.type]=!1,e},_checkMouse:function(t,e){var i=e.relatedTarget;if(!i)return!0;try{for(;i&&i!==t;)i=i.parentNode}catch(n){return!1}return i!==t},_getEvent:function(){var e=t.event;if(!e)for(var i=arguments.callee.caller;i&&(e=i.arguments[0],!e||t.Event!==e.constructor);)i=i.caller;return e},_filterClick:function(t,e){var i=t.timeStamp||t.originalEvent.timeStamp,n=o.DomEvent._lastClick&&i-o.DomEvent._lastClick;return n&&n>100&&500>n||t.target._simulatedClick&&!t._simulated?void o.DomEvent.stop(t):(o.DomEvent._lastClick=i,e(t))}},o.DomEvent.on=o.DomEvent.addListener,o.DomEvent.off=o.DomEvent.removeListener,o.Draggable=o.Class.extend({includes:o.Mixin.Events,statics:{START:o.Browser.touch?["touchstart","mousedown"]:["mousedown"],END:{mousedown:"mouseup",touchstart:"touchend",pointerdown:"touchend",MSPointerDown:"touchend"},MOVE:{mousedown:"mousemove",touchstart:"touchmove",pointerdown:"touchmove",MSPointerDown:"touchmove"}},initialize:function(t,e){this._element=t,this._dragStartTarget=e||t},enable:function(){if(!this._enabled){for(var t=o.Draggable.START.length-1;t>=0;t--)o.DomEvent.on(this._dragStartTarget,o.Draggable.START[t],this._onDown,this);this._enabled=!0}},disable:function(){if(this._enabled){for(var t=o.Draggable.START.length-1;t>=0;t--)o.DomEvent.off(this._dragStartTarget,o.Draggable.START[t],this._onDown,this);this._enabled=!1,this._moved=!1}},_onDown:function(t){if(this._moved=!1,!(t.shiftKey||1!==t.which&&1!==t.button&&!t.touches||(o.DomEvent.stopPropagation(t),o.Draggable._disabled||(o.DomUtil.disableImageDrag(),o.DomUtil.disableTextSelection(),this._moving)))){var i=t.touches?t.touches[0]:t;this._startPoint=new o.Point(i.clientX,i.clientY),this._startPos=this._newPos=o.DomUtil.getPosition(this._element),o.DomEvent.on(e,o.Draggable.MOVE[t.type],this._onMove,this).on(e,o.Draggable.END[t.type],this._onUp,this)}},_onMove:function(t){if(t.touches&&t.touches.length>1)return void(this._moved=!0);var i=t.touches&&1===t.touches.length?t.touches[0]:t,n=new o.Point(i.clientX,i.clientY),s=n.subtract(this._startPoint);(s.x||s.y)&&(o.Browser.touch&&Math.abs(s.x)+Math.abs(s.y)<3||(o.DomEvent.preventDefault(t),this._moved||(this.fire("dragstart"),this._moved=!0,this._startPos=o.DomUtil.getPosition(this._element).subtract(s),o.DomUtil.addClass(e.body,"leaflet-dragging"),this._lastTarget=t.target||t.srcElement,o.DomUtil.addClass(this._lastTarget,"leaflet-drag-target")),this._newPos=this._startPos.add(s),this._moving=!0,o.Util.cancelAnimFrame(this._animRequest),this._animRequest=o.Util.requestAnimFrame(this._updatePosition,this,!0,this._dragStartTarget)))},_updatePosition:function(){this.fire("predrag"),o.DomUtil.setPosition(this._element,this._newPos),this.fire("drag")},_onUp:function(){o.DomUtil.removeClass(e.body,"leaflet-dragging"),this._lastTarget&&(o.DomUtil.removeClass(this._lastTarget,"leaflet-drag-target"),this._lastTarget=null);for(var t in o.Draggable.MOVE)o.DomEvent.off(e,o.Draggable.MOVE[t],this._onMove).off(e,o.Draggable.END[t],this._onUp);o.DomUtil.enableImageDrag(),o.DomUtil.enableTextSelection(),this._moved&&this._moving&&(o.Util.cancelAnimFrame(this._animRequest),this.fire("dragend",{distance:this._newPos.distanceTo(this._startPos)})),this._moving=!1}}),o.Handler=o.Class.extend({initialize:function(t){this._map=t},enable:function(){this._enabled||(this._enabled=!0,this.addHooks())},disable:function(){this._enabled&&(this._enabled=!1,this.removeHooks())},enabled:function(){return!!this._enabled}}),o.Map.mergeOptions({dragging:!0,inertia:!o.Browser.android23,inertiaDeceleration:3400,inertiaMaxSpeed:1/0,inertiaThreshold:o.Browser.touch?32:18,easeLinearity:.25,worldCopyJump:!1}),o.Map.Drag=o.Handler.extend({addHooks:function(){if(!this._draggable){var t=this._map;this._draggable=new o.Draggable(t._mapPane,t._container),this._draggable.on({dragstart:this._onDragStart,drag:this._onDrag,dragend:this._onDragEnd},this),t.options.worldCopyJump&&(this._draggable.on("predrag",this._onPreDrag,this),t.on("viewreset",this._onViewReset,this),t.whenReady(this._onViewReset,this))}this._draggable.enable()},removeHooks:function(){this._draggable.disable()},moved:function(){return this._draggable&&this._draggable._moved},_onDragStart:function(){var t=this._map;t._panAnim&&t._panAnim.stop(),t.fire("movestart").fire("dragstart"),t.options.inertia&&(this._positions=[],this._times=[])},_onDrag:function(){if(this._map.options.inertia){var t=this._lastTime=+new Date,e=this._lastPos=this._draggable._newPos;this._positions.push(e),this._times.push(t),t-this._times[0]>200&&(this._positions.shift(),this._times.shift())}this._map.fire("move").fire("drag")},_onViewReset:function(){var t=this._map.getSize()._divideBy(2),e=this._map.latLngToLayerPoint([0,0]);this._initialWorldOffset=e.subtract(t).x,this._worldWidth=this._map.project([0,180]).x},_onPreDrag:function(){var t=this._worldWidth,e=Math.round(t/2),i=this._initialWorldOffset,n=this._draggable._newPos.x,o=(n-e+i)%t+e-i,s=(n+e+i)%t-e-i,a=Math.abs(o+i)i.inertiaThreshold||!this._positions[0];if(e.fire("dragend",t),s)e.fire("moveend");else{var a=this._lastPos.subtract(this._positions[0]),r=(this._lastTime+n-this._times[0])/1e3,h=i.easeLinearity,l=a.multiplyBy(h/r),u=l.distanceTo([0,0]),c=Math.min(i.inertiaMaxSpeed,u),d=l.multiplyBy(c/u),p=c/(i.inertiaDeceleration*h),_=d.multiplyBy(-p/2).round();_.x&&_.y?(_=e._limitOffset(_,e.options.maxBounds),o.Util.requestAnimFrame(function(){e.panBy(_,{duration:p,easeLinearity:h,noMoveStart:!0})})):e.fire("moveend")}}}),o.Map.addInitHook("addHandler","dragging",o.Map.Drag),o.Map.mergeOptions({doubleClickZoom:!0}),o.Map.DoubleClickZoom=o.Handler.extend({addHooks:function(){this._map.on("dblclick",this._onDoubleClick,this)},removeHooks:function(){this._map.off("dblclick",this._onDoubleClick,this)},_onDoubleClick:function(t){var e=this._map,i=e.getZoom()+(t.originalEvent.shiftKey?-1:1);"center"===e.options.doubleClickZoom?e.setZoom(i):e.setZoomAround(t.containerPoint,i)}}),o.Map.addInitHook("addHandler","doubleClickZoom",o.Map.DoubleClickZoom),o.Map.mergeOptions({scrollWheelZoom:!0}),o.Map.ScrollWheelZoom=o.Handler.extend({addHooks:function(){o.DomEvent.on(this._map._container,"mousewheel",this._onWheelScroll,this),o.DomEvent.on(this._map._container,"MozMousePixelScroll",o.DomEvent.preventDefault),this._delta=0},removeHooks:function(){o.DomEvent.off(this._map._container,"mousewheel",this._onWheelScroll),o.DomEvent.off(this._map._container,"MozMousePixelScroll",o.DomEvent.preventDefault)},_onWheelScroll:function(t){var e=o.DomEvent.getWheelDelta(t);this._delta+=e,this._lastMousePos=this._map.mouseEventToContainerPoint(t),this._startTime||(this._startTime=+new Date);var i=Math.max(40-(+new Date-this._startTime),0);clearTimeout(this._timer),this._timer=setTimeout(o.bind(this._performZoom,this),i),o.DomEvent.preventDefault(t),o.DomEvent.stopPropagation(t)},_performZoom:function(){var t=this._map,e=this._delta,i=t.getZoom();e=e>0?Math.ceil(e):Math.floor(e),e=Math.max(Math.min(e,4),-4),e=t._limitZoom(i+e)-i,this._delta=0,this._startTime=null,e&&("center"===t.options.scrollWheelZoom?t.setZoom(i+e):t.setZoomAround(this._lastMousePos,i+e))}}),o.Map.addInitHook("addHandler","scrollWheelZoom",o.Map.ScrollWheelZoom),o.extend(o.DomEvent,{_touchstart:o.Browser.msPointer?"MSPointerDown":o.Browser.pointer?"pointerdown":"touchstart",_touchend:o.Browser.msPointer?"MSPointerUp":o.Browser.pointer?"pointerup":"touchend",addDoubleTapListener:function(t,i,n){function s(t){var e;if(o.Browser.pointer?(_.push(t.pointerId),e=_.length):e=t.touches.length,!(e>1)){var i=Date.now(),n=i-(r||i);h=t.touches?t.touches[0]:t,l=n>0&&u>=n,r=i}}function a(t){if(o.Browser.pointer){var e=_.indexOf(t.pointerId);if(-1===e)return;_.splice(e,1)}if(l){if(o.Browser.pointer){var n,s={};for(var a in h)n=h[a],s[a]="function"==typeof n?n.bind(h):n;h=s}h.type="dblclick",i(h),r=null}}var r,h,l=!1,u=250,c="_leaflet_",d=this._touchstart,p=this._touchend,_=[];t[c+d+n]=s,t[c+p+n]=a;var m=o.Browser.pointer?e.documentElement:t;return t.addEventListener(d,s,!1),m.addEventListener(p,a,!1),o.Browser.pointer&&m.addEventListener(o.DomEvent.POINTER_CANCEL,a,!1),this},removeDoubleTapListener:function(t,i){var n="_leaflet_";return t.removeEventListener(this._touchstart,t[n+this._touchstart+i],!1),(o.Browser.pointer?e.documentElement:t).removeEventListener(this._touchend,t[n+this._touchend+i],!1),o.Browser.pointer&&e.documentElement.removeEventListener(o.DomEvent.POINTER_CANCEL,t[n+this._touchend+i],!1),this}}),o.extend(o.DomEvent,{POINTER_DOWN:o.Browser.msPointer?"MSPointerDown":"pointerdown",POINTER_MOVE:o.Browser.msPointer?"MSPointerMove":"pointermove",POINTER_UP:o.Browser.msPointer?"MSPointerUp":"pointerup",POINTER_CANCEL:o.Browser.msPointer?"MSPointerCancel":"pointercancel",_pointers:[],_pointerDocumentListener:!1,addPointerListener:function(t,e,i,n){switch(e){case"touchstart":return this.addPointerListenerStart(t,e,i,n);case"touchend":return this.addPointerListenerEnd(t,e,i,n);case"touchmove":return this.addPointerListenerMove(t,e,i,n);default:throw"Unknown touch event type"}},addPointerListenerStart:function(t,i,n,s){var a="_leaflet_",r=this._pointers,h=function(t){o.DomEvent.preventDefault(t);for(var e=!1,i=0;i1))&&(this._moved||(o.DomUtil.addClass(e._mapPane,"leaflet-touching"),e.fire("movestart").fire("zoomstart"),this._moved=!0),o.Util.cancelAnimFrame(this._animRequest),this._animRequest=o.Util.requestAnimFrame(this._updateOnMove,this,!0,this._map._container),o.DomEvent.preventDefault(t))}},_updateOnMove:function(){var t=this._map,e=this._getScaleOrigin(),i=t.layerPointToLatLng(e),n=t.getScaleZoom(this._scale);t._animateZoom(i,n,this._startCenter,this._scale,this._delta,!1,!0)},_onTouchEnd:function(){if(!this._moved||!this._zooming)return void(this._zooming=!1);var t=this._map;this._zooming=!1,o.DomUtil.removeClass(t._mapPane,"leaflet-touching"),o.Util.cancelAnimFrame(this._animRequest),o.DomEvent.off(e,"touchmove",this._onTouchMove).off(e,"touchend",this._onTouchEnd);var i=this._getScaleOrigin(),n=t.layerPointToLatLng(i),s=t.getZoom(),a=t.getScaleZoom(this._scale)-s,r=a>0?Math.ceil(a):Math.floor(a),h=t._limitZoom(s+r),l=t.getZoomScale(h)/this._scale;t._animateZoom(n,h,i,l)},_getScaleOrigin:function(){var t=this._centerOffset.subtract(this._delta).divideBy(this._scale);return this._startCenter.add(t)}}),o.Map.addInitHook("addHandler","touchZoom",o.Map.TouchZoom),o.Map.mergeOptions({tap:!0,tapTolerance:15}),o.Map.Tap=o.Handler.extend({addHooks:function(){o.DomEvent.on(this._map._container,"touchstart",this._onDown,this)},removeHooks:function(){o.DomEvent.off(this._map._container,"touchstart",this._onDown,this)},_onDown:function(t){if(t.touches){if(o.DomEvent.preventDefault(t),this._fireClick=!0,t.touches.length>1)return this._fireClick=!1,void clearTimeout(this._holdTimeout);var i=t.touches[0],n=i.target;this._startPos=this._newPos=new o.Point(i.clientX,i.clientY),n.tagName&&"a"===n.tagName.toLowerCase()&&o.DomUtil.addClass(n,"leaflet-active"),this._holdTimeout=setTimeout(o.bind(function(){this._isTapValid()&&(this._fireClick=!1,this._onUp(),this._simulateEvent("contextmenu",i))},this),1e3),o.DomEvent.on(e,"touchmove",this._onMove,this).on(e,"touchend",this._onUp,this)}},_onUp:function(t){if(clearTimeout(this._holdTimeout),o.DomEvent.off(e,"touchmove",this._onMove,this).off(e,"touchend",this._onUp,this),this._fireClick&&t&&t.changedTouches){var i=t.changedTouches[0],n=i.target;n&&n.tagName&&"a"===n.tagName.toLowerCase()&&o.DomUtil.removeClass(n,"leaflet-active"),this._isTapValid()&&this._simulateEvent("click",i)}},_isTapValid:function(){return this._newPos.distanceTo(this._startPos)<=this._map.options.tapTolerance},_onMove:function(t){var e=t.touches[0];this._newPos=new o.Point(e.clientX,e.clientY)},_simulateEvent:function(i,n){var o=e.createEvent("MouseEvents");o._simulated=!0,n.target._simulatedClick=!0,o.initMouseEvent(i,!0,!0,t,1,n.screenX,n.screenY,n.clientX,n.clientY,!1,!1,!1,!1,0,null),n.target.dispatchEvent(o)}}),o.Browser.touch&&!o.Browser.pointer&&o.Map.addInitHook("addHandler","tap",o.Map.Tap),o.Map.mergeOptions({boxZoom:!0}),o.Map.BoxZoom=o.Handler.extend({initialize:function(t){this._map=t,this._container=t._container,this._pane=t._panes.overlayPane,this._moved=!1},addHooks:function(){o.DomEvent.on(this._container,"mousedown",this._onMouseDown,this)},removeHooks:function(){o.DomEvent.off(this._container,"mousedown",this._onMouseDown),this._moved=!1},moved:function(){return this._moved},_onMouseDown:function(t){return this._moved=!1,!t.shiftKey||1!==t.which&&1!==t.button?!1:(o.DomUtil.disableTextSelection(),o.DomUtil.disableImageDrag(),this._startLayerPoint=this._map.mouseEventToLayerPoint(t),void o.DomEvent.on(e,"mousemove",this._onMouseMove,this).on(e,"mouseup",this._onMouseUp,this).on(e,"keydown",this._onKeyDown,this))},_onMouseMove:function(t){this._moved||(this._box=o.DomUtil.create("div","leaflet-zoom-box",this._pane),o.DomUtil.setPosition(this._box,this._startLayerPoint),this._container.style.cursor="crosshair",this._map.fire("boxzoomstart"));var e=this._startLayerPoint,i=this._box,n=this._map.mouseEventToLayerPoint(t),s=n.subtract(e),a=new o.Point(Math.min(n.x,e.x),Math.min(n.y,e.y));o.DomUtil.setPosition(i,a),this._moved=!0,i.style.width=Math.max(0,Math.abs(s.x)-4)+"px",i.style.height=Math.max(0,Math.abs(s.y)-4)+"px"},_finish:function(){this._moved&&(this._pane.removeChild(this._box),this._container.style.cursor=""),o.DomUtil.enableTextSelection(),o.DomUtil.enableImageDrag(),o.DomEvent.off(e,"mousemove",this._onMouseMove).off(e,"mouseup",this._onMouseUp).off(e,"keydown",this._onKeyDown)},_onMouseUp:function(t){this._finish();var e=this._map,i=e.mouseEventToLayerPoint(t);if(!this._startLayerPoint.equals(i)){var n=new o.LatLngBounds(e.layerPointToLatLng(this._startLayerPoint),e.layerPointToLatLng(i));e.fitBounds(n),e.fire("boxzoomend",{boxZoomBounds:n})}},_onKeyDown:function(t){27===t.keyCode&&this._finish()}}),o.Map.addInitHook("addHandler","boxZoom",o.Map.BoxZoom),o.Map.mergeOptions({keyboard:!0,keyboardPanOffset:80,keyboardZoomOffset:1}),o.Map.Keyboard=o.Handler.extend({keyCodes:{left:[37],right:[39],down:[40],up:[38],zoomIn:[187,107,61,171],zoomOut:[189,109,173]},initialize:function(t){this._map=t,this._setPanOffset(t.options.keyboardPanOffset),this._setZoomOffset(t.options.keyboardZoomOffset)},addHooks:function(){var t=this._map._container;-1===t.tabIndex&&(t.tabIndex="0"),o.DomEvent.on(t,"focus",this._onFocus,this).on(t,"blur",this._onBlur,this).on(t,"mousedown",this._onMouseDown,this),this._map.on("focus",this._addHooks,this).on("blur",this._removeHooks,this)},removeHooks:function(){this._removeHooks();var t=this._map._container;o.DomEvent.off(t,"focus",this._onFocus,this).off(t,"blur",this._onBlur,this).off(t,"mousedown",this._onMouseDown,this),this._map.off("focus",this._addHooks,this).off("blur",this._removeHooks,this)},_onMouseDown:function(){if(!this._focused){var i=e.body,n=e.documentElement,o=i.scrollTop||n.scrollTop,s=i.scrollLeft||n.scrollLeft;this._map._container.focus(),t.scrollTo(s,o)}},_onFocus:function(){this._focused=!0,this._map.fire("focus")},_onBlur:function(){this._focused=!1,this._map.fire("blur")},_setPanOffset:function(t){var e,i,n=this._panKeys={},o=this.keyCodes;for(e=0,i=o.left.length;i>e;e++)n[o.left[e]]=[-1*t,0];for(e=0,i=o.right.length;i>e;e++)n[o.right[e]]=[t,0];for(e=0,i=o.down.length;i>e;e++)n[o.down[e]]=[0,t];for(e=0,i=o.up.length;i>e;e++)n[o.up[e]]=[0,-1*t]},_setZoomOffset:function(t){var e,i,n=this._zoomKeys={},o=this.keyCodes;for(e=0,i=o.zoomIn.length;i>e;e++)n[o.zoomIn[e]]=t;for(e=0,i=o.zoomOut.length;i>e;e++)n[o.zoomOut[e]]=-t},_addHooks:function(){o.DomEvent.on(e,"keydown",this._onKeyDown,this)},_removeHooks:function(){o.DomEvent.off(e,"keydown",this._onKeyDown,this)},_onKeyDown:function(t){var e=t.keyCode,i=this._map;if(e in this._panKeys){if(i._panAnim&&i._panAnim._inProgress)return;i.panBy(this._panKeys[e]),i.options.maxBounds&&i.panInsideBounds(i.options.maxBounds)}else{if(!(e in this._zoomKeys))return;i.setZoom(i.getZoom()+this._zoomKeys[e])}o.DomEvent.stop(t)}}),o.Map.addInitHook("addHandler","keyboard",o.Map.Keyboard),o.Handler.MarkerDrag=o.Handler.extend({initialize:function(t){this._marker=t},addHooks:function(){var t=this._marker._icon;this._draggable||(this._draggable=new o.Draggable(t,t)),this._draggable.on("dragstart",this._onDragStart,this).on("drag",this._onDrag,this).on("dragend",this._onDragEnd,this),this._draggable.enable(),o.DomUtil.addClass(this._marker._icon,"leaflet-marker-draggable")},removeHooks:function(){this._draggable.off("dragstart",this._onDragStart,this).off("drag",this._onDrag,this).off("dragend",this._onDragEnd,this),this._draggable.disable(),o.DomUtil.removeClass(this._marker._icon,"leaflet-marker-draggable")},moved:function(){return this._draggable&&this._draggable._moved},_onDragStart:function(){this._marker.closePopup().fire("movestart").fire("dragstart")},_onDrag:function(){var t=this._marker,e=t._shadow,i=o.DomUtil.getPosition(t._icon),n=t._map.layerPointToLatLng(i);e&&o.DomUtil.setPosition(e,i),t._latlng=n,t.fire("move",{latlng:n}).fire("drag")},_onDragEnd:function(t){this._marker.fire("moveend").fire("dragend",t)}}),o.Control=o.Class.extend({options:{position:"topright"},initialize:function(t){o.setOptions(this,t)},getPosition:function(){return this.options.position},setPosition:function(t){var e=this._map;return e&&e.removeControl(this),this.options.position=t,e&&e.addControl(this),this},getContainer:function(){return this._container},addTo:function(t){this._map=t;var e=this._container=this.onAdd(t),i=this.getPosition(),n=t._controlCorners[i];return o.DomUtil.addClass(e,"leaflet-control"),-1!==i.indexOf("bottom")?n.insertBefore(e,n.firstChild):n.appendChild(e),this},removeFrom:function(t){var e=this.getPosition(),i=t._controlCorners[e];return i.removeChild(this._container),this._map=null,this.onRemove&&this.onRemove(t),this},_refocusOnMap:function(){this._map&&this._map.getContainer().focus()}}),o.control=function(t){return new o.Control(t)},o.Map.include({addControl:function(t){return t.addTo(this),this},removeControl:function(t){return t.removeFrom(this),this},_initControlPos:function(){function t(t,s){var a=i+t+" "+i+s;e[t+s]=o.DomUtil.create("div",a,n)}var e=this._controlCorners={},i="leaflet-",n=this._controlContainer=o.DomUtil.create("div",i+"control-container",this._container);t("top","left"),t("top","right"),t("bottom","left"),t("bottom","right")},_clearControlPos:function(){this._container.removeChild(this._controlContainer)}}),o.Control.Zoom=o.Control.extend({options:{position:"topleft",zoomInText:"+",zoomInTitle:"Zoom in",zoomOutText:"-",zoomOutTitle:"Zoom out"},onAdd:function(t){var e="leaflet-control-zoom",i=o.DomUtil.create("div",e+" leaflet-bar");return this._map=t,this._zoomInButton=this._createButton(this.options.zoomInText,this.options.zoomInTitle,e+"-in",i,this._zoomIn,this),this._zoomOutButton=this._createButton(this.options.zoomOutText,this.options.zoomOutTitle,e+"-out",i,this._zoomOut,this),this._updateDisabled(),t.on("zoomend zoomlevelschange",this._updateDisabled,this),i},onRemove:function(t){t.off("zoomend zoomlevelschange",this._updateDisabled,this)},_zoomIn:function(t){this._map.zoomIn(t.shiftKey?3:1)},_zoomOut:function(t){this._map.zoomOut(t.shiftKey?3:1)},_createButton:function(t,e,i,n,s,a){var r=o.DomUtil.create("a",i,n);r.innerHTML=t,r.href="#",r.title=e;var h=o.DomEvent.stopPropagation;return o.DomEvent.on(r,"click",h).on(r,"mousedown",h).on(r,"dblclick",h).on(r,"click",o.DomEvent.preventDefault).on(r,"click",s,a).on(r,"click",this._refocusOnMap,a),r},_updateDisabled:function(){var t=this._map,e="leaflet-disabled";o.DomUtil.removeClass(this._zoomInButton,e),o.DomUtil.removeClass(this._zoomOutButton,e),t._zoom===t.getMinZoom()&&o.DomUtil.addClass(this._zoomOutButton,e),t._zoom===t.getMaxZoom()&&o.DomUtil.addClass(this._zoomInButton,e)}}),o.Map.mergeOptions({zoomControl:!0}),o.Map.addInitHook(function(){this.options.zoomControl&&(this.zoomControl=new o.Control.Zoom,this.addControl(this.zoomControl))}),o.control.zoom=function(t){return new o.Control.Zoom(t)},o.Control.Attribution=o.Control.extend({options:{position:"bottomright",prefix:'Leaflet '},initialize:function(t){o.setOptions(this,t),this._attributions={}},onAdd:function(t){this._container=o.DomUtil.create("div","leaflet-control-attribution"),o.DomEvent.disableClickPropagation(this._container);for(var e in t._layers)t._layers[e].getAttribution&&this.addAttribution(t._layers[e].getAttribution());return t.on("layeradd",this._onLayerAdd,this).on("layerremove",this._onLayerRemove,this),this._update(),this._container},onRemove:function(t){t.off("layeradd",this._onLayerAdd).off("layerremove",this._onLayerRemove)},setPrefix:function(t){return this.options.prefix=t,this._update(),this},addAttribution:function(t){return t?(this._attributions[t]||(this._attributions[t]=0),this._attributions[t]++,this._update(),this):void 0},removeAttribution:function(t){return t?(this._attributions[t]&&(this._attributions[t]--,this._update()),this):void 0},_update:function(){if(this._map){var t=[];for(var e in this._attributions)this._attributions[e]&&t.push(e);var i=[];this.options.prefix&&i.push(this.options.prefix),t.length&&i.push(t.join(", ")),this._container.innerHTML=i.join(" | ")}},_onLayerAdd:function(t){t.layer.getAttribution&&this.addAttribution(t.layer.getAttribution())},_onLayerRemove:function(t){t.layer.getAttribution&&this.removeAttribution(t.layer.getAttribution())}}),o.Map.mergeOptions({attributionControl:!0}),o.Map.addInitHook(function(){this.options.attributionControl&&(this.attributionControl=(new o.Control.Attribution).addTo(this))}),o.control.attribution=function(t){return new o.Control.Attribution(t)},o.Control.Scale=o.Control.extend({options:{position:"bottomleft",maxWidth:100,metric:!0,imperial:!0,updateWhenIdle:!1},onAdd:function(t){this._map=t;var e="leaflet-control-scale",i=o.DomUtil.create("div",e),n=this.options;return this._addScales(n,e,i),t.on(n.updateWhenIdle?"moveend":"move",this._update,this),t.whenReady(this._update,this),i},onRemove:function(t){t.off(this.options.updateWhenIdle?"moveend":"move",this._update,this)},_addScales:function(t,e,i){t.metric&&(this._mScale=o.DomUtil.create("div",e+"-line",i)),t.imperial&&(this._iScale=o.DomUtil.create("div",e+"-line",i))},_update:function(){var t=this._map.getBounds(),e=t.getCenter().lat,i=6378137*Math.PI*Math.cos(e*Math.PI/180),n=i*(t.getNorthEast().lng-t.getSouthWest().lng)/180,o=this._map.getSize(),s=this.options,a=0;o.x>0&&(a=n*(s.maxWidth/o.x)),this._updateScales(s,a)},_updateScales:function(t,e){t.metric&&e&&this._updateMetric(e),t.imperial&&e&&this._updateImperial(e)},_updateMetric:function(t){var e=this._getRoundNum(t);this._mScale.style.width=this._getScaleWidth(e/t)+"px",this._mScale.innerHTML=1e3>e?e+" m":e/1e3+" km"},_updateImperial:function(t){var e,i,n,o=3.2808399*t,s=this._iScale;o>5280?(e=o/5280,i=this._getRoundNum(e),s.style.width=this._getScaleWidth(i/e)+"px",s.innerHTML=i+" mi"):(n=this._getRoundNum(o),s.style.width=this._getScaleWidth(n/o)+"px",s.innerHTML=n+" ft")},_getScaleWidth:function(t){return Math.round(this.options.maxWidth*t)-10},_getRoundNum:function(t){var e=Math.pow(10,(Math.floor(t)+"").length-1),i=t/e;return i=i>=10?10:i>=5?5:i>=3?3:i>=2?2:1,e*i}}),o.control.scale=function(t){return new o.Control.Scale(t)},o.Control.Layers=o.Control.extend({options:{collapsed:!0,position:"topright",autoZIndex:!0},initialize:function(t,e,i){o.setOptions(this,i),this._layers={},this._lastZIndex=0,this._handlingClick=!1;for(var n in t)this._addLayer(t[n],n);for(n in e)this._addLayer(e[n],n,!0)},onAdd:function(t){return this._initLayout(),this._update(),t.on("layeradd",this._onLayerChange,this).on("layerremove",this._onLayerChange,this),this._container},onRemove:function(t){t.off("layeradd",this._onLayerChange,this).off("layerremove",this._onLayerChange,this)},addBaseLayer:function(t,e){return this._addLayer(t,e),this._update(),this},addOverlay:function(t,e){return this._addLayer(t,e,!0),this._update(),this},removeLayer:function(t){var e=o.stamp(t);return delete this._layers[e],this._update(),this},_initLayout:function(){var t="leaflet-control-layers",e=this._container=o.DomUtil.create("div",t);e.setAttribute("aria-haspopup",!0),o.Browser.touch?o.DomEvent.on(e,"click",o.DomEvent.stopPropagation):o.DomEvent.disableClickPropagation(e).disableScrollPropagation(e);var i=this._form=o.DomUtil.create("form",t+"-list");if(this.options.collapsed){o.Browser.android||o.DomEvent.on(e,"mouseover",this._expand,this).on(e,"mouseout",this._collapse,this);var n=this._layersLink=o.DomUtil.create("a",t+"-toggle",e);n.href="#",n.title="Layers",o.Browser.touch?o.DomEvent.on(n,"click",o.DomEvent.stop).on(n,"click",this._expand,this):o.DomEvent.on(n,"focus",this._expand,this),o.DomEvent.on(i,"click",function(){setTimeout(o.bind(this._onInputClick,this),0)},this),this._map.on("click",this._collapse,this)}else this._expand();this._baseLayersList=o.DomUtil.create("div",t+"-base",i),this._separator=o.DomUtil.create("div",t+"-separator",i),this._overlaysList=o.DomUtil.create("div",t+"-overlays",i),e.appendChild(i)},_addLayer:function(t,e,i){var n=o.stamp(t);this._layers[n]={layer:t,name:e,overlay:i},this.options.autoZIndex&&t.setZIndex&&(this._lastZIndex++,t.setZIndex(this._lastZIndex))},_update:function(){if(this._container){this._baseLayersList.innerHTML="",this._overlaysList.innerHTML="";var t,e,i=!1,n=!1;for(t in this._layers)e=this._layers[t],this._addItem(e),n=n||e.overlay,i=i||!e.overlay;this._separator.style.display=n&&i?"":"none"}},_onLayerChange:function(t){var e=this._layers[o.stamp(t.layer)];if(e){this._handlingClick||this._update();var i=e.overlay?"layeradd"===t.type?"overlayadd":"overlayremove":"layeradd"===t.type?"baselayerchange":null;i&&this._map.fire(i,e)}},_createRadioElement:function(t,i){var n=' ";var o=e.createElement("div");return o.innerHTML=n,o.firstChild},_addItem:function(t){var i,n=e.createElement("label"),s=this._map.hasLayer(t.layer);t.overlay?(i=e.createElement("input"),i.type="checkbox",i.className="leaflet-control-layers-selector",i.defaultChecked=s):i=this._createRadioElement("leaflet-base-layers",s),i.layerId=o.stamp(t.layer),o.DomEvent.on(i,"click",this._onInputClick,this);var a=e.createElement("span");a.innerHTML=" "+t.name,n.appendChild(i),n.appendChild(a);var r=t.overlay?this._overlaysList:this._baseLayersList;return r.appendChild(n),n},_onInputClick:function(){var t,e,i,n=this._form.getElementsByTagName("input"),o=n.length;for(this._handlingClick=!0,t=0;o>t;t++)e=n[t],i=this._layers[e.layerId],e.checked&&!this._map.hasLayer(i.layer)?this._map.addLayer(i.layer):!e.checked&&this._map.hasLayer(i.layer)&&this._map.removeLayer(i.layer);this._handlingClick=!1,this._refocusOnMap()},_expand:function(){o.DomUtil.addClass(this._container,"leaflet-control-layers-expanded")},_collapse:function(){this._container.className=this._container.className.replace(" leaflet-control-layers-expanded","")}}),o.control.layers=function(t,e,i){return new o.Control.Layers(t,e,i)},o.PosAnimation=o.Class.extend({includes:o.Mixin.Events,run:function(t,e,i,n){this.stop(),this._el=t,this._inProgress=!0,this._newPos=e,this.fire("start"),t.style[o.DomUtil.TRANSITION]="all "+(i||.25)+"s cubic-bezier(0,0,"+(n||.5)+",1)",o.DomEvent.on(t,o.DomUtil.TRANSITION_END,this._onTransitionEnd,this),o.DomUtil.setPosition(t,e),o.Util.falseFn(t.offsetWidth),this._stepTimer=setInterval(o.bind(this._onStep,this),50)},stop:function(){this._inProgress&&(o.DomUtil.setPosition(this._el,this._getPos()),this._onTransitionEnd(),o.Util.falseFn(this._el.offsetWidth))},_onStep:function(){var t=this._getPos();return t?(this._el._leaflet_pos=t,void this.fire("step")):void this._onTransitionEnd()},_transformRe:/([-+]?(?:\d*\.)?\d+)\D*, ([-+]?(?:\d*\.)?\d+)\D*\)/,_getPos:function(){var e,i,n,s=this._el,a=t.getComputedStyle(s);if(o.Browser.any3d){if(n=a[o.DomUtil.TRANSFORM].match(this._transformRe),!n)return;e=parseFloat(n[1]),i=parseFloat(n[2])}else e=parseFloat(a.left),i=parseFloat(a.top);return new o.Point(e,i,!0)},_onTransitionEnd:function(){o.DomEvent.off(this._el,o.DomUtil.TRANSITION_END,this._onTransitionEnd,this),this._inProgress&&(this._inProgress=!1,this._el.style[o.DomUtil.TRANSITION]="",this._el._leaflet_pos=this._newPos,clearInterval(this._stepTimer),this.fire("step").fire("end"))}}),o.Map.include({setView:function(t,e,n){if(e=e===i?this._zoom:this._limitZoom(e),t=this._limitCenter(o.latLng(t),e,this.options.maxBounds),n=n||{},this._panAnim&&this._panAnim.stop(),this._loaded&&!n.reset&&n!==!0){n.animate!==i&&(n.zoom=o.extend({animate:n.animate},n.zoom),n.pan=o.extend({animate:n.animate},n.pan));var s=this._zoom!==e?this._tryAnimatedZoom&&this._tryAnimatedZoom(t,e,n.zoom):this._tryAnimatedPan(t,n.pan);if(s)return clearTimeout(this._sizeTimer),this}return this._resetView(t,e),this},panBy:function(t,e){if(t=o.point(t).round(),e=e||{},!t.x&&!t.y)return this;if(this._panAnim||(this._panAnim=new o.PosAnimation,this._panAnim.on({step:this._onPanTransitionStep,end:this._onPanTransitionEnd},this)),e.noMoveStart||this.fire("movestart"),e.animate!==!1){o.DomUtil.addClass(this._mapPane,"leaflet-pan-anim");var i=this._getMapPanePos().subtract(t);this._panAnim.run(this._mapPane,i,e.duration||.25,e.easeLinearity)}else this._rawPanBy(t),this.fire("move").fire("moveend");return this},_onPanTransitionStep:function(){this.fire("move")},_onPanTransitionEnd:function(){o.DomUtil.removeClass(this._mapPane,"leaflet-pan-anim"),this.fire("moveend")},_tryAnimatedPan:function(t,e){var i=this._getCenterOffset(t)._floor();return(e&&e.animate)===!0||this.getSize().contains(i)?(this.panBy(i,e),!0):!1}}),o.PosAnimation=o.DomUtil.TRANSITION?o.PosAnimation:o.PosAnimation.extend({run:function(t,e,i,n){this.stop(),this._el=t,this._inProgress=!0,this._duration=i||.25,this._easeOutPower=1/Math.max(n||.5,.2),this._startPos=o.DomUtil.getPosition(t),this._offset=e.subtract(this._startPos),this._startTime=+new Date,this.fire("start"),this._animate()},stop:function(){this._inProgress&&(this._step(),this._complete())},_animate:function(){this._animId=o.Util.requestAnimFrame(this._animate,this),this._step()},_step:function(){var t=+new Date-this._startTime,e=1e3*this._duration;e>t?this._runFrame(this._easeOut(t/e)):(this._runFrame(1),this._complete())},_runFrame:function(t){var e=this._startPos.add(this._offset.multiplyBy(t));o.DomUtil.setPosition(this._el,e),this.fire("step")},_complete:function(){o.Util.cancelAnimFrame(this._animId),this._inProgress=!1,this.fire("end")},_easeOut:function(t){return 1-Math.pow(1-t,this._easeOutPower)}}),o.Map.mergeOptions({zoomAnimation:!0,zoomAnimationThreshold:4}),o.DomUtil.TRANSITION&&o.Map.addInitHook(function(){this._zoomAnimated=this.options.zoomAnimation&&o.DomUtil.TRANSITION&&o.Browser.any3d&&!o.Browser.android23&&!o.Browser.mobileOpera,this._zoomAnimated&&o.DomEvent.on(this._mapPane,o.DomUtil.TRANSITION_END,this._catchTransitionEnd,this)}),o.Map.include(o.DomUtil.TRANSITION?{_catchTransitionEnd:function(t){this._animatingZoom&&t.propertyName.indexOf("transform")>=0&&this._onZoomTransitionEnd()},_nothingToAnimate:function(){return!this._container.getElementsByClassName("leaflet-zoom-animated").length},_tryAnimatedZoom:function(t,e,i){if(this._animatingZoom)return!0;if(i=i||{},!this._zoomAnimated||i.animate===!1||this._nothingToAnimate()||Math.abs(e-this._zoom)>this.options.zoomAnimationThreshold)return!1;var n=this.getZoomScale(e),o=this._getCenterOffset(t)._divideBy(1-1/n),s=this._getCenterLayerPoint()._add(o);return i.animate===!0||this.getSize().contains(o)?(this.fire("movestart").fire("zoomstart"),this._animateZoom(t,e,s,n,null,!0),!0):!1},_animateZoom:function(t,e,i,n,s,a,r){r||(this._animatingZoom=!0),o.DomUtil.addClass(this._mapPane,"leaflet-zoom-anim"),this._animateToCenter=t,this._animateToZoom=e,o.Draggable&&(o.Draggable._disabled=!0),o.Util.requestAnimFrame(function(){this.fire("zoomanim",{center:t,zoom:e,origin:i,scale:n,delta:s,backwards:a})},this)},_onZoomTransitionEnd:function(){this._animatingZoom=!1,o.DomUtil.removeClass(this._mapPane,"leaflet-zoom-anim"),this._resetView(this._animateToCenter,this._animateToZoom,!0,!0),o.Draggable&&(o.Draggable._disabled=!1)}}:{}),o.TileLayer.include({_animateZoom:function(t){this._animating||(this._animating=!0,this._prepareBgBuffer());var e=this._bgBuffer,i=o.DomUtil.TRANSFORM,n=t.delta?o.DomUtil.getTranslateString(t.delta):e.style[i],s=o.DomUtil.getScaleString(t.scale,t.origin);e.style[i]=t.backwards?s+" "+n:n+" "+s},_endZoomAnim:function(){var t=this._tileContainer,e=this._bgBuffer;t.style.visibility="",t.parentNode.appendChild(t),o.Util.falseFn(e.offsetWidth),this._animating=!1},_clearBgBuffer:function(){var t=this._map;!t||t._animatingZoom||t.touchZoom._zooming||(this._bgBuffer.innerHTML="",this._bgBuffer.style[o.DomUtil.TRANSFORM]="")},_prepareBgBuffer:function(){var t=this._tileContainer,e=this._bgBuffer,i=this._getLoadedTilesPercentage(e),n=this._getLoadedTilesPercentage(t);return e&&i>.5&&.5>n?(t.style.visibility="hidden",void this._stopLoadingImages(t)):(e.style.visibility="hidden",e.style[o.DomUtil.TRANSFORM]="",this._tileContainer=e,e=this._bgBuffer=t,this._stopLoadingImages(e),void clearTimeout(this._clearBgBufferTimer))},_getLoadedTilesPercentage:function(t){var e,i,n=t.getElementsByTagName("img"),o=0;for(e=0,i=n.length;i>e;e++)n[e].complete&&o++;return o/i},_stopLoadingImages:function(t){var e,i,n,s=Array.prototype.slice.call(t.getElementsByTagName("img"));for(e=0,i=s.length;i>e;e++)n=s[e],n.complete||(n.onload=o.Util.falseFn,n.onerror=o.Util.falseFn,n.src=o.Util.emptyImageUrl,n.parentNode.removeChild(n))}}),o.Map.include({_defaultLocateOptions:{watch:!1,setView:!1,maxZoom:1/0,timeout:1e4,maximumAge:0,enableHighAccuracy:!1},locate:function(t){if(t=this._locateOptions=o.extend(this._defaultLocateOptions,t),!navigator.geolocation)return this._handleGeolocationError({code:0,message:"Geolocation not supported."}),this;var e=o.bind(this._handleGeolocationResponse,this),i=o.bind(this._handleGeolocationError,this);return t.watch?this._locationWatchId=navigator.geolocation.watchPosition(e,i,t):navigator.geolocation.getCurrentPosition(e,i,t),this},stopLocate:function(){return navigator.geolocation&&navigator.geolocation.clearWatch(this._locationWatchId),this._locateOptions&&(this._locateOptions.setView=!1),this},_handleGeolocationError:function(t){var e=t.code,i=t.message||(1===e?"permission denied":2===e?"position unavailable":"timeout");this._locateOptions.setView&&!this._loaded&&this.fitWorld(),this.fire("locationerror",{code:e,message:"Geolocation error: "+i+"."})},_handleGeolocationResponse:function(t){var e=t.coords.latitude,i=t.coords.longitude,n=new o.LatLng(e,i),s=180*t.coords.accuracy/40075017,a=s/Math.cos(o.LatLng.DEG_TO_RAD*e),r=o.latLngBounds([e-s,i-a],[e+s,i+a]),h=this._locateOptions;if(h.setView){var l=Math.min(this.getBoundsZoom(r),h.maxZoom);this.setView(n,l)}var u={latlng:n,bounds:r,timestamp:t.timestamp};for(var c in t.coords)"number"==typeof t.coords[c]&&(u[c]=t.coords[c]);this.fire("locationfound",u)}})}(window,document);
\ No newline at end of file
diff --git a/servers/php/javascript/leaflet-plugins/LICENSE b/servers/php/javascript/leaflet-plugins/LICENSE
deleted file mode 100644
index f371b7d..0000000
--- a/servers/php/javascript/leaflet-plugins/LICENSE
+++ /dev/null
@@ -1,22 +0,0 @@
-Copyright (c) 2011-2012, Pavel Shramov
-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.
\ No newline at end of file
diff --git a/servers/php/javascript/leaflet-plugins/bing.js b/servers/php/javascript/leaflet-plugins/bing.js
deleted file mode 100644
index 9c1d7aa..0000000
--- a/servers/php/javascript/leaflet-plugins/bing.js
+++ /dev/null
@@ -1,131 +0,0 @@
-/*
-Copyright (c) 2011-2012, Pavel Shramov
-All rights reserved.
-https://github.com/robertharm/leaflet-plugins
-License in same folder as this file
-*/
-
-L.BingLayer = L.TileLayer.extend({
- options: {
- subdomains: [0, 1, 2, 3],
- type: 'Road',
- attribution: 'Bing',
- culture: ''
- },
-
- initialize: function(key, options) {
- L.Util.setOptions(this, options);
-
- this._key = key;
- this._url = null;
- this.meta = {};
- this.loadMetadata();
- },
-
- tile2quad: function(x, y, z) {
- var quad = '';
- for (var i = z; i > 0; i--) {
- var digit = 0;
- var mask = 1 << (i - 1);
- if ((x & mask) != 0) digit += 1;
- if ((y & mask) != 0) digit += 2;
- quad = quad + digit;
- }
- return quad;
- },
-
- getTileUrl: function(p, z) {
- var z = this._getZoomForUrl();
- var subdomains = this.options.subdomains,
- s = this.options.subdomains[Math.abs((p.x + p.y) % subdomains.length)];
- return this._url.replace('{subdomain}', s)
- .replace('{quadkey}', this.tile2quad(p.x, p.y, z))
- .replace('{culture}', this.options.culture);
- },
-
- loadMetadata: function() {
- var _this = this;
- var cbid = '_bing_metadata_' + L.Util.stamp(this);
- window[cbid] = function (meta) {
- _this.meta = meta;
- window[cbid] = undefined;
- var e = document.getElementById(cbid);
- e.parentNode.removeChild(e);
- if (meta.errorDetails) {
- if (window.console) console.log("Leaflet Bing Plugin Error - Got metadata: " + meta.errorDetails);
- return;
- }
- _this.initMetadata();
- };
- var url = document.location.protocol + "//dev.virtualearth.net/REST/v1/Imagery/Metadata/" + this.options.type + "?include=ImageryProviders&jsonp=" + cbid +
- "&key=" + this._key + "&UriScheme=" + document.location.protocol.slice(0, -1);
- var script = document.createElement("script");
- script.type = "text/javascript";
- script.src = url;
- script.id = cbid;
- document.getElementsByTagName("head")[0].appendChild(script);
- },
-
- initMetadata: function() {
- var r = this.meta.resourceSets[0].resources[0];
- this.options.subdomains = r.imageUrlSubdomains;
- this._url = r.imageUrl;
- this._providers = [];
- if (r.imageryProviders) {
- for (var i = 0; i < r.imageryProviders.length; i++) {
- var p = r.imageryProviders[i];
- for (var j = 0; j < p.coverageAreas.length; j++) {
- var c = p.coverageAreas[j];
- var coverage = {zoomMin: c.zoomMin, zoomMax: c.zoomMax, active: false};
- var bounds = new L.LatLngBounds(
- new L.LatLng(c.bbox[0]+0.01, c.bbox[1]+0.01),
- new L.LatLng(c.bbox[2]-0.01, c.bbox[3]-0.01)
- );
- coverage.bounds = bounds;
- coverage.attrib = p.attribution;
- this._providers.push(coverage);
- }
- }
- }
- this._update();
- },
-
- _update: function() {
- if (this._url == null || !this._map) return;
- this._update_attribution();
- L.TileLayer.prototype._update.apply(this, []);
- },
-
- _update_attribution: function() {
- var bounds = this._map.getBounds();
- var zoom = this._map.getZoom();
- for (var i = 0; i < this._providers.length; i++) {
- var p = this._providers[i];
- if ((zoom <= p.zoomMax && zoom >= p.zoomMin) &&
- bounds.intersects(p.bounds)) {
- if (!p.active && this._map.attributionControl)
- this._map.attributionControl.addAttribution(p.attrib);
- p.active = true;
- } else {
- if (p.active && this._map.attributionControl)
- this._map.attributionControl.removeAttribution(p.attrib);
- p.active = false;
- }
- }
- },
-
- onRemove: function(map) {
- for (var i = 0; i < this._providers.length; i++) {
- var p = this._providers[i];
- if (p.active && this._map.attributionControl) {
- this._map.attributionControl.removeAttribution(p.attrib);
- p.active = false;
- }
- }
- L.TileLayer.prototype.onRemove.apply(this, [map]);
- }
-});
-
-L.bingLayer = function (key, options) {
- return new L.BingLayer(key, options);
-};
\ No newline at end of file
diff --git a/servers/php/javascript/leaflet-plugins/google.js b/servers/php/javascript/leaflet-plugins/google.js
deleted file mode 100644
index 8d267b0..0000000
--- a/servers/php/javascript/leaflet-plugins/google.js
+++ /dev/null
@@ -1,242 +0,0 @@
-/*
-Copyright (c) 2011-2012, Pavel Shramov
-All rights reserved.
-https://github.com/robertharm/leaflet-plugins
-License in same folder as this file
-*/
-
-var adsense_status = 'disabled';
-
-L.Google = L.Class.extend( {
- includes: L.Mixin.Events,
-
- options: {
- minZoom: 0,
- maxZoom: 18,
- tileSize: 256,
- subdomains: 'abc',
- errorTileUrl: '',
- attribution: '',
- opacity: 1,
- continuousWorld: false,
- noWrap: false,
- mapOptions: {
- backgroundColor: '#dddddd'
- }
- },
-
- // Possible types: SATELLITE, ROADMAP, HYBRID, TERRAIN
- initialize: function(type, options) {
- L.Util.setOptions(this, options);
-
- this._ready = google.maps.Map !== undefined;
- if (!this._ready) L.Google.asyncWait.push(this);
-
- this._type = type || 'ROADMAP';
- },
-
- onAdd: function(map, insertAtTheBottom) {
- this._map = map;
- this._insertAtTheBottom = insertAtTheBottom;
-
- // create a container div for tiles
- this._initContainer();
- this._initMapObject();
-
- if (adsense_status == 'enabled') {
- this._initAdSense();
- }
-
- // set up events
- map.on('viewreset', this._resetCallback, this);
-
- this._limitedUpdate = L.Util.limitExecByInterval(this._update, 150, this);
- map.on('move', this._update, this);
-
- map.on('zoomanim', this._handleZoomAnim, this);
-
- //20px instead of 1em to avoid a slight overlap with google's attribution
- map._controlCorners.bottomright.style.marginBottom = '20px';
-
- this._reset();
- this._update();
- },
-
- onRemove: function(map) {
- this._map._container.removeChild(this._container);
- this._map.off('viewreset', this._resetCallback, this);
- this._map.off('move', this._update, this);
- this._map.off('zoomanim', this._handleZoomAnim, this);
- map._controlCorners.bottomright.style.marginBottom = '0em';
- },
-
- getAttribution: function() {
- return this.options.attribution;
- },
-
- setOpacity: function(opacity) {
- this.options.opacity = opacity;
- if (opacity < 1) {
- L.DomUtil.setOpacity(this._container, opacity);
- }
- },
-
- setElementSize: function(e, size) {
- e.style.width = size.x + 'px';
- e.style.height = size.y + 'px';
- },
-
- _initContainer: function() {
- var tilePane = this._map._container,
- first = tilePane.firstChild;
-
- if (!this._container) {
- this._container = L.DomUtil.create('div', 'leaflet-google-layer leaflet-top leaflet-left');
- this._container.id = '_GMapContainer_' + L.Util.stamp(this);
- this._container.style.zIndex = 'auto';
- }
-
- tilePane.insertBefore(this._container, first);
-
- this.setOpacity(this.options.opacity);
- this.setElementSize(this._container, this._map.getSize());
- },
-
- _initMapObject: function() {
- if (!this._ready) return;
- this._google_center = new google.maps.LatLng(0, 0);
- var map = new google.maps.Map(this._container, {
- center: this._google_center,
- zoom: 0,
- tilt: 0,
- mapTypeId: google.maps.MapTypeId[this._type],
- disableDefaultUI: true,
- keyboardShortcuts: false,
- draggable: false,
- disableDoubleClickZoom: true,
- scrollwheel: false,
- streetViewControl: false,
- styles: this.options.mapOptions.styles,
- backgroundColor: this.options.mapOptions.backgroundColor
- });
-
- var _this = this;
- this._reposition = google.maps.event.addListenerOnce(map, 'center_changed',
- function() { _this.onReposition(); });
- this._google = map;
-
- google.maps.event.addListenerOnce(map, 'idle',
- function() { _this._checkZoomLevels(); });
-
- //Reporting that map-object was initialized.
- this.fire('MapObjectInitialized', { mapObject: map });
- },
-
- _checkZoomLevels: function() {
- //setting the zoom level on the Google map may result in a different zoom level than the one requested
- //(it won't go beyond the level for which they have data).
- // verify and make sure the zoom levels on both Leaflet and Google maps are consistent
- if (this._google.getZoom() !== this._map.getZoom()) {
- //zoom levels are out of sync. Set the leaflet zoom level to match the google one
- this._map.setZoom( this._google.getZoom() );
- }
-
- // this._resetCallback(this._container);
- },
-
- _resetCallback: function(e) {
- this._reset(e.hard);
- },
-
- _reset: function(clearOldContainer) {
- this._initContainer();
- },
-
- _update: function(e) {
- if (!this._google) return;
- this._resize();
-
- var center = e && e.latlng ? e.latlng : this._map.getCenter();
- var _center = new google.maps.LatLng(center.lat, center.lng);
-
- this._google.setCenter(_center);
- this._google.setZoom(Math.round(this._map.getZoom()));
-
- this._checkZoomLevels();
- },
-
- _resize: function() {
- var size = this._map.getSize();
- if (this._container.style.width === size.x &&
- this._container.style.height === size.y)
- return;
- this.setElementSize(this._container, size);
- this.onReposition();
- },
-
-
- _handleZoomAnim: function (e) {
- var center = e.center;
- var _center = new google.maps.LatLng(center.lat, center.lng);
-
- this._google.setCenter(_center);
- this._google.setZoom(Math.round(e.zoom));
- },
-
-
- onReposition: function() {
- if (!this._google) return;
- google.maps.event.trigger(this._google, 'resize');
- },
-
- _initAdSense: function() {
- var adUnitDiv = document.createElement('div');
- adUnitDiv.className = "leaflet-control";
- adUnitDiv.style.margin = "0";
- adUnitDiv.style.clear = "none";
- var user_adsense = new String;
- user_adsense.format = google.maps.adsense.AdFormat["HALF_BANNER"];
- user_adsense.position = google.maps.ControlPosition["TOP_CENTER"];
- user_adsense.cposition = "TOP_CENTER";
- user_adsense.backgroundColor = "#c4d4f3";
- user_adsense.borderColor = "#aaa";
- user_adsense.titleColor = "#0000cc";
- user_adsense.textColor = "#000000";
- user_adsense.urlColor = "#009900";
- user_adsense.channelNumber = "YOUR_CHANNEL_NUMBER";
- user_adsense.publisherID = "YOUR_PUBLISHER_ID";
-
- var adUnitOptions = {
- format: user_adsense.format,
- position: user_adsense.position,
- backgroundColor: user_adsense.backgroundColor,
- borderColor: user_adsense.borderColor,
- titleColor: user_adsense.titleColor,
- textColor: user_adsense.textColor,
- urlColor: user_adsense.urlColor,
- map: this._google,
- visible: true,
- channelNumber: user_adsense.channelNumber,
- publisherId: user_adsense.publisherID
- }
- //info: dont load ads on minimaps
- var size = this._map.getSize();
- if (size.x > 150) {
- this._adUnit = new google.maps.adsense.AdUnit(adUnitDiv, adUnitOptions);
- }
- }
-});
-
-L.Google.asyncWait = [];
-L.Google.asyncInitialize = function() {
- var i;
- for (i = 0; i < L.Google.asyncWait.length; i++) {
- var o = L.Google.asyncWait[i];
- o._ready = true;
- if (o._container) {
- o._initMapObject();
- o._update();
- }
- }
- L.Google.asyncWait = [];
-};
diff --git a/servers/php/javascript/maps.js b/servers/php/javascript/maps.js
deleted file mode 100644
index 4e1f663..0000000
--- a/servers/php/javascript/maps.js
+++ /dev/null
@@ -1,328 +0,0 @@
-
-function loadRoutes(json) {
- if (json.length == 0) {
- showMessage('There are no routes available to view.');
- map.innerHTML = '';
- }
- else {
- // create the first option of the dropdown box
- var option = document.createElement('option');
- option.setAttribute('value', '0');
- option.innerHTML = 'Select Route...';
- routeSelect.appendChild(option);
-
- // iterate through the routes and load them into the dropdwon box.
- $(json.routes).each(function(key, value){
- var option = document.createElement('option');
- option.setAttribute('value', '?sessionID=' + $(this).attr('sessionID')
- + '&phoneNumber=' + $(this).attr('phoneNumber'));
-
- var shortSessionID = $(this).attr('sessionID').substring(0,5);
- option.innerHTML = $(this).attr('phoneNumber') + "-" + shortSessionID + " " + $(this).attr('times');
- routeSelect.appendChild(option);
- });
-
- // need to reset this for firefox
- routeSelect.selectedIndex = 0;
-
- hideWaitImage();
- showMessage('Please select a route below. ');
- }
-}
-
-// this will get the map and route, the route is selected from the dropdown box
-function getRouteForMap() {
- if (hasMap()) {
- showWaitImage('Getting map...');
- var url = 'getrouteformap.php' + routeSelect.options[routeSelect.selectedIndex].value;
-
- //console.log("testing route: " + routeSelect.options[routeSelect.selectedIndex].value);
-
- $.ajax({
- url: url,
- type: 'GET',
- dataType: 'json',
- success: function(data) {
- //console.log("success getRouteForMap");
- loadGPSLocations(data);
- },
- error: function (xhr, status, errorThrown) {
- console.log("responseText: " + xhr.responseText);
- console.log("status: " + xhr.status);
- console.log("errorThrown: " + errorThrown);
- }
- });
- }
- else {
- alert("Please select a route before trying to refresh map.");
- }
-}
-
-// check to see if we have a map loaded, don't want to autorefresh or delete without it
-function hasMap() {
- if (routeSelect.selectedIndex == 0) { // means no map
- return false;
- }
- else {
- return true;
- }
-}
-
-function loadGPSLocations(json) {
- if (json.length == 0) {
- showMessage('There is no tracking data to view.');
- map.innerHTML = '';
- }
- else {
- hideWaitImage();
-
- // make sure we only create map object once
- if (map.id == 'map') {
- // use leaflet (http://leafletjs.com/) to create our map and map layers
- map = new L.map('map');
-
- var openStreetMapsURL = ('https:' == document.location.protocol ? 'https://' : 'http://') +
- '{s}.tile.openstreetmap.org/{z}/{x}/{y}.png';
- var openStreetMapsLayer = new L.TileLayer(openStreetMapsURL,
- {attribution:'©2014 OpenStreetMap contributors'});
-
- // need to get your own bing maps key, http://www.microsoft.com/maps/create-a-bing-maps-key.aspx
- var bingMapsLayer = new L.BingLayer("AnH1IKGCBwAiBWfYAHMtIfIhMVybHFx2GxsReNP5W0z6P8kRa67_QwhM4PglI9yL");
- var googleMapsLayer = new L.Google('ROADMAP');
-
- // this fixes the zoom buttons from freezing
- // https://github.com/shramov/leaflet-plugins/issues/62
- L.polyline([[0, 0], ]).addTo(map);
-
- // this sets which map layer will first be displayed, go ahead and change it to bingMapsLayer or openStreetMapsLayer to see
- map.addLayer(googleMapsLayer);
-
- // this is the switcher control to switch between map types (upper right hand corner of map)
- map.addControl(new L.Control.Layers({
- 'Bing Maps':bingMapsLayer,
- 'Google Maps':googleMapsLayer,
- 'OpenStreetMaps':openStreetMapsLayer
- }, {}));
- }
-
- var finalLocation = false;
- var counter = 0;
-
- // iterate through the locations and create map markers for each location
- $(json.locations).each(function(key, value){
- counter++;
-
- // want to set the map center on the last location
- if (counter == $(json.locations).length) {
- map.setView(new L.LatLng($(this).attr('latitude'),$(this).attr('longitude')), zoomLevel);
- finalLocation = true;
- }
-
- var marker = createMarker(
- $(this).attr('latitude'),
- $(this).attr('longitude'),
- $(this).attr('speed'),
- $(this).attr('direction'),
- $(this).attr('distance'),
- $(this).attr('locationMethod'),
- $(this).attr('gpsTime'),
- $(this).attr('phoneNumber'),
- $(this).attr('sessionID'),
- $(this).attr('accuracy'),
- $(this).attr('extraInfo'),
- map, finalLocation);
- });
- }
-
- // display route name above map
- showMessage(routeSelect.options[routeSelect.selectedIndex].innerHTML);
-}
-
-function createMarker(latitude, longitude, speed, direction, distance, locationMethod, gpsTime,
- phoneNumber, sessionID, accuracy, extraInfo, map, finalLocation) {
- var iconUrl;
-
- if (finalLocation) {
- iconUrl = 'images/coolred_small.png';
- } else {
- iconUrl = 'images/coolblue_small.png';
- }
-
- var markerIcon = new L.Icon({
- iconUrl: iconUrl,
- shadowUrl: 'images/coolshadow_small.png',
- iconSize: [12, 20],
- shadowSize: [22, 20],
- iconAnchor: [6, 20],
- shadowAnchor: [6, 20],
- popupAnchor: [-3, -76]
- });
-
- var lastMarker = "";
-
- // when a user clicks on last marker, let them know it's final one
- if (finalLocation) {
- lastMarker = " Final location ";
- }
-
- // convert from meters to feet
- accuracy = parseInt(accuracy * 3.28);
-
- var popupWindowText = ""
- + " "
- + " " + lastMarker
- + " Speed: " + speed + " mph "
- + "Distance: " + distance + " mi "
- + "Time: " + gpsTime + " "
- + "Method: " + locationMethod + " "
- + "Phone #: " + phoneNumber + " "
- + "Session ID: " + sessionID + " "
- + "Accuracy: " + accuracy + " ft "
- + "Extra Info: " + extraInfo + "
";
-
- L.marker(new L.LatLng(latitude, longitude), {icon: markerIcon}).bindPopup(popupWindowText).addTo(map);
-}
-
-// this chooses the proper image for our litte compass in the popup window
-function getCompassImage(azimuth) {
- if ((azimuth >= 337 && azimuth <= 360) || (azimuth >= 0 && azimuth < 23))
- return "compassN";
- if (azimuth >= 23 && azimuth < 68)
- return "compassNE";
- if (azimuth >= 68 && azimuth < 113)
- return "compassE";
- if (azimuth >= 113 && azimuth < 158)
- return "compassSE";
- if (azimuth >= 158 && azimuth < 203)
- return "compassS";
- if (azimuth >= 203 && azimuth < 248)
- return "compassSW";
- if (azimuth >= 248 && azimuth < 293)
- return "compassW";
- if (azimuth >= 293 && azimuth < 337)
- return "compassNW";
-
- return "";
-}
-
-function deleteRoute() {
- if (hasMap()) {
-
- // comment out these two lines to get delete working
- var answer = confirm("Disabled here on test website, this works fine.");
- return false;
-
- var answer = confirm("This will permanently delete this route\n from the database. Do you want to delete?");
- if (answer){
- showWaitImage('Deleting route...');
- var url = 'deleteroute.php' + routeSelect.options[routeSelect.selectedIndex].value;
-
- $.ajax({
- url: url,
- type: 'GET',
- success: function() {
- deleteRouteResponse();
- }
- });
- }
- else {
- return false;
- }
- }
- else {
- alert("Please select a route before trying to delete.");
- }
-}
-
-function deleteRouteResponse() {
- map.innerHTML = '';
- routeSelect.length = 0;
-
- $.ajax({
- url: 'getroutes.php',
- type: 'GET',
- success: function(data) {
- loadRoutes(data);
- }
- });
-}
-
-// auto refresh the map. there are 3 transitions (shown below). transitions happen when a user
-// selects an option in the auto refresh dropdown box. an interval is an amount of time in between
-// refreshes of the map. for instance, auto refresh once a minute. in the method below, the 3 numbers
-// in the code show where the 3 transitions are handled. setInterval turns on a timer that calls
-// the getRouteForMap() method every so many seconds based on the value of newInterval.
-// clearInterval turns off the timer. if newInterval is 5, then the value passed to setInterval is
-// 5000 milliseconds or 5 seconds.
-function autoRefresh() {
- /*
- 1) going from off to any interval
- 2) going from any interval to off
- 3) going from one interval to another
- */
-
- if (hasMap()) {
- newInterval = refreshSelect.options[refreshSelect.selectedIndex].value;
-
- if (currentInterval > 0) { // currently running at an interval
-
- if (newInterval > 0) { // moving to another interval (3)
- clearInterval(intervalID);
- intervalID = setInterval("getRouteForMap();", newInterval * 1000);
- currentInterval = newInterval;
- }
- else { // we are turning off (2)
- clearInterval(intervalID);
- newInterval = 0;
- currentInterval = 0;
- }
- }
- else { // off and going to an interval (1)
- intervalID = setInterval("getRouteForMap();", newInterval * 1000);
- currentInterval = newInterval;
- }
-
- // show what auto refresh action was taken and after 5 seconds, display the route name again
- showMessage(refreshSelect.options[refreshSelect.selectedIndex].innerHTML);
- setTimeout('showRouteName();', 5000);
- }
- else {
- alert("Please select a route before trying to refresh map.");
- refreshSelect.selectedIndex = 0;
- }
-}
-
-function changeZoomLevel() {
- if (hasMap()) {
- zoomLevel = zoomLevelSelect.selectedIndex + 1;
-
- getRouteForMap();
-
- // show what zoom level action was taken and after 5 seconds, display the route name again
- showMessage(zoomLevelSelect.options[zoomLevelSelect.selectedIndex].innerHTML);
- setTimeout('showRouteName();', 5000);
- }
- else {
- alert("Please select a route before selecting zoom level.");
- zoomLevelSelect.selectedIndex = zoomLevel - 1;
- }
-}
-
-function showMessage(message) {
- messages.innerHTML = 'GpsTracker: ' + message + ' ';
-}
-
-function showRouteName() {
- showMessage(routeSelect.options[routeSelect.selectedIndex].innerHTML);
-}
-
-function showWaitImage(theMessage) {
- map.innerHTML = ' ';
- showMessage(theMessage);
-}
-
-function hideWaitImage() {
- map.innerHTML = '';
- messages.innerHTML = 'GpsTracker';
-}
-
diff --git a/servers/php/js/leaflet-0.7.3/images/layers-2x.png b/servers/php/js/leaflet-0.7.3/images/layers-2x.png
new file mode 100644
index 0000000..a2cf7f9
--- /dev/null
+++ b/servers/php/js/leaflet-0.7.3/images/layers-2x.png
Binary files differ
diff --git a/servers/php/js/leaflet-0.7.3/images/layers.png b/servers/php/js/leaflet-0.7.3/images/layers.png
new file mode 100644
index 0000000..bca0a0e
--- /dev/null
+++ b/servers/php/js/leaflet-0.7.3/images/layers.png
Binary files differ
diff --git a/servers/php/js/leaflet-0.7.3/images/marker-icon-2x.png b/servers/php/js/leaflet-0.7.3/images/marker-icon-2x.png
new file mode 100644
index 0000000..0015b64
--- /dev/null
+++ b/servers/php/js/leaflet-0.7.3/images/marker-icon-2x.png
Binary files differ
diff --git a/servers/php/js/leaflet-0.7.3/images/marker-icon.png b/servers/php/js/leaflet-0.7.3/images/marker-icon.png
new file mode 100644
index 0000000..e2e9f75
--- /dev/null
+++ b/servers/php/js/leaflet-0.7.3/images/marker-icon.png
Binary files differ
diff --git a/servers/php/js/leaflet-0.7.3/images/marker-shadow.png b/servers/php/js/leaflet-0.7.3/images/marker-shadow.png
new file mode 100644
index 0000000..d1e773c
--- /dev/null
+++ b/servers/php/js/leaflet-0.7.3/images/marker-shadow.png
Binary files differ
diff --git a/servers/php/js/leaflet-0.7.3/leaflet-src.js b/servers/php/js/leaflet-0.7.3/leaflet-src.js
new file mode 100644
index 0000000..4947090
--- /dev/null
+++ b/servers/php/js/leaflet-0.7.3/leaflet-src.js
@@ -0,0 +1,9180 @@
+/*
+ Leaflet, a JavaScript library for mobile-friendly interactive maps. http://leafletjs.com
+ (c) 2010-2013, Vladimir Agafonkin
+ (c) 2010-2011, CloudMade
+*/
+(function (window, document, undefined) {
+var oldL = window.L,
+ L = {};
+
+L.version = '0.7.3';
+
+// define Leaflet for Node module pattern loaders, including Browserify
+if (typeof module === 'object' && typeof module.exports === 'object') {
+ module.exports = L;
+
+// define Leaflet as an AMD module
+} else if (typeof define === 'function' && define.amd) {
+ define(L);
+}
+
+// define Leaflet as a global L variable, saving the original L to restore later if needed
+
+L.noConflict = function () {
+ window.L = oldL;
+ return this;
+};
+
+window.L = L;
+
+
+/*
+ * L.Util contains various utility functions used throughout Leaflet code.
+ */
+
+L.Util = {
+ extend: function (dest) { // (Object[, Object, ...]) ->
+ var sources = Array.prototype.slice.call(arguments, 1),
+ i, j, len, src;
+
+ for (j = 0, len = sources.length; j < len; j++) {
+ src = sources[j] || {};
+ for (i in src) {
+ if (src.hasOwnProperty(i)) {
+ dest[i] = src[i];
+ }
+ }
+ }
+ return dest;
+ },
+
+ bind: function (fn, obj) { // (Function, Object) -> Function
+ var args = arguments.length > 2 ? Array.prototype.slice.call(arguments, 2) : null;
+ return function () {
+ return fn.apply(obj, args || arguments);
+ };
+ },
+
+ stamp: (function () {
+ var lastId = 0,
+ key = '_leaflet_id';
+ return function (obj) {
+ obj[key] = obj[key] || ++lastId;
+ return obj[key];
+ };
+ }()),
+
+ invokeEach: function (obj, method, context) {
+ var i, args;
+
+ if (typeof obj === 'object') {
+ args = Array.prototype.slice.call(arguments, 3);
+
+ for (i in obj) {
+ method.apply(context, [i, obj[i]].concat(args));
+ }
+ return true;
+ }
+
+ return false;
+ },
+
+ limitExecByInterval: function (fn, time, context) {
+ var lock, execOnUnlock;
+
+ return function wrapperFn() {
+ var args = arguments;
+
+ if (lock) {
+ execOnUnlock = true;
+ return;
+ }
+
+ lock = true;
+
+ setTimeout(function () {
+ lock = false;
+
+ if (execOnUnlock) {
+ wrapperFn.apply(context, args);
+ execOnUnlock = false;
+ }
+ }, time);
+
+ fn.apply(context, args);
+ };
+ },
+
+ falseFn: function () {
+ return false;
+ },
+
+ formatNum: function (num, digits) {
+ var pow = Math.pow(10, digits || 5);
+ return Math.round(num * pow) / pow;
+ },
+
+ trim: function (str) {
+ return str.trim ? str.trim() : str.replace(/^\s+|\s+$/g, '');
+ },
+
+ splitWords: function (str) {
+ return L.Util.trim(str).split(/\s+/);
+ },
+
+ setOptions: function (obj, options) {
+ obj.options = L.extend({}, obj.options, options);
+ return obj.options;
+ },
+
+ getParamString: function (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('&');
+ },
+ template: function (str, data) {
+ return str.replace(/\{ *([\w_]+) *\}/g, 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;
+ });
+ },
+
+ isArray: Array.isArray || function (obj) {
+ return (Object.prototype.toString.call(obj) === '[object Array]');
+ },
+
+ emptyImageUrl: 'data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs='
+};
+
+(function () {
+
+ // inspired by http://paulirish.com/2011/requestanimationframe-for-smart-animating/
+
+ function getPrefixed(name) {
+ var i, fn,
+ prefixes = ['webkit', 'moz', 'o', 'ms'];
+
+ for (i = 0; i < prefixes.length && !fn; i++) {
+ fn = window[prefixes[i] + name];
+ }
+
+ return fn;
+ }
+
+ var lastTime = 0;
+
+ 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); };
+
+
+ L.Util.requestAnimFrame = function (fn, context, immediate, element) {
+ fn = L.bind(fn, context);
+
+ if (immediate && requestFn === timeoutDefer) {
+ fn();
+ } else {
+ return requestFn.call(window, fn, element);
+ }
+ };
+
+ L.Util.cancelAnimFrame = function (id) {
+ if (id) {
+ cancelFn.call(window, id);
+ }
+ };
+
+}());
+
+// shortcuts for most used utility functions
+L.extend = L.Util.extend;
+L.bind = L.Util.bind;
+L.stamp = L.Util.stamp;
+L.setOptions = L.Util.setOptions;
+
+
+/*
+ * L.Class powers the OOP facilities of the library.
+ * Thanks to John Resig and Dean Edwards for inspiration!
+ */
+
+L.Class = function () {};
+
+L.Class.extend = function (props) {
+
+ // extended class with the new prototype
+ var NewClass = function () {
+
+ // call the constructor
+ if (this.initialize) {
+ this.initialize.apply(this, arguments);
+ }
+
+ // call all constructor hooks
+ if (this._initHooks) {
+ this.callInitHooks();
+ }
+ };
+
+ // instantiate class without calling constructor
+ var F = function () {};
+ F.prototype = this.prototype;
+
+ var proto = new F();
+ proto.constructor = NewClass;
+
+ NewClass.prototype = proto;
+
+ //inherit parent's statics
+ for (var i in this) {
+ if (this.hasOwnProperty(i) && i !== 'prototype') {
+ NewClass[i] = this[i];
+ }
+ }
+
+ // mix static properties into the class
+ if (props.statics) {
+ L.extend(NewClass, props.statics);
+ delete props.statics;
+ }
+
+ // mix includes into the prototype
+ if (props.includes) {
+ L.Util.extend.apply(null, [proto].concat(props.includes));
+ delete props.includes;
+ }
+
+ // merge options
+ if (props.options && proto.options) {
+ props.options = L.extend({}, proto.options, props.options);
+ }
+
+ // mix given properties into the prototype
+ L.extend(proto, props);
+
+ proto._initHooks = [];
+
+ var parent = this;
+ // jshint camelcase: false
+ NewClass.__super__ = parent.prototype;
+
+ // add method for calling all hooks
+ proto.callInitHooks = function () {
+
+ if (this._initHooksCalled) { return; }
+
+ if (parent.prototype.callInitHooks) {
+ parent.prototype.callInitHooks.call(this);
+ }
+
+ this._initHooksCalled = true;
+
+ for (var i = 0, len = proto._initHooks.length; i < len; i++) {
+ proto._initHooks[i].call(this);
+ }
+ };
+
+ return NewClass;
+};
+
+
+// method for adding properties to prototype
+L.Class.include = function (props) {
+ L.extend(this.prototype, props);
+};
+
+// merge new default options to the Class
+L.Class.mergeOptions = function (options) {
+ L.extend(this.prototype.options, options);
+};
+
+// add a constructor hook
+L.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);
+};
+
+
+/*
+ * L.Mixin.Events is used to add custom events functionality to Leaflet classes.
+ */
+
+var eventsKey = '_leaflet_events';
+
+L.Mixin = {};
+
+L.Mixin.Events = {
+
+ addEventListener: function (types, fn, context) { // (String, Function[, Object]) or (Object[, Object])
+
+ // types can be a map of types/handlers
+ if (L.Util.invokeEach(types, this.addEventListener, this, fn, context)) { return this; }
+
+ var events = this[eventsKey] = this[eventsKey] || {},
+ contextId = context && context !== this && L.stamp(context),
+ i, len, event, type, indexKey, indexLenKey, typeIndex;
+
+ // types can be a string of space-separated words
+ types = L.Util.splitWords(types);
+
+ for (i = 0, len = types.length; i < len; i++) {
+ event = {
+ action: fn,
+ context: context || this
+ };
+ type = types[i];
+
+ if (contextId) {
+ // store listeners of a particular context in a separate hash (if it has an id)
+ // gives a major performance boost when removing thousands of map layers
+
+ indexKey = type + '_idx';
+ indexLenKey = indexKey + '_len';
+
+ typeIndex = events[indexKey] = events[indexKey] || {};
+
+ if (!typeIndex[contextId]) {
+ typeIndex[contextId] = [];
+
+ // keep track of the number of keys in the index to quickly check if it's empty
+ events[indexLenKey] = (events[indexLenKey] || 0) + 1;
+ }
+
+ typeIndex[contextId].push(event);
+
+
+ } else {
+ events[type] = events[type] || [];
+ events[type].push(event);
+ }
+ }
+
+ return this;
+ },
+
+ hasEventListeners: function (type) { // (String) -> Boolean
+ var events = this[eventsKey];
+ return !!events && ((type in events && events[type].length > 0) ||
+ (type + '_idx' in events && events[type + '_idx_len'] > 0));
+ },
+
+ removeEventListener: function (types, fn, context) { // ([String, Function, Object]) or (Object[, Object])
+
+ if (!this[eventsKey]) {
+ return this;
+ }
+
+ if (!types) {
+ return this.clearAllEventListeners();
+ }
+
+ if (L.Util.invokeEach(types, this.removeEventListener, this, fn, context)) { return this; }
+
+ var events = this[eventsKey],
+ contextId = context && context !== this && L.stamp(context),
+ i, len, type, listeners, j, indexKey, indexLenKey, typeIndex, removed;
+
+ types = L.Util.splitWords(types);
+
+ for (i = 0, len = types.length; i < len; i++) {
+ type = types[i];
+ indexKey = type + '_idx';
+ indexLenKey = indexKey + '_len';
+
+ typeIndex = events[indexKey];
+
+ if (!fn) {
+ // clear all listeners for a type if function isn't specified
+ delete events[type];
+ delete events[indexKey];
+ delete events[indexLenKey];
+
+ } else {
+ listeners = contextId && typeIndex ? typeIndex[contextId] : events[type];
+
+ if (listeners) {
+ for (j = listeners.length - 1; j >= 0; j--) {
+ if ((listeners[j].action === fn) && (!context || (listeners[j].context === context))) {
+ removed = listeners.splice(j, 1);
+ // set the old action to a no-op, because it is possible
+ // that the listener is being iterated over as part of a dispatch
+ removed[0].action = L.Util.falseFn;
+ }
+ }
+
+ if (context && typeIndex && (listeners.length === 0)) {
+ delete typeIndex[contextId];
+ events[indexLenKey]--;
+ }
+ }
+ }
+ }
+
+ return this;
+ },
+
+ clearAllEventListeners: function () {
+ delete this[eventsKey];
+ return this;
+ },
+
+ fireEvent: function (type, data) { // (String[, Object])
+ if (!this.hasEventListeners(type)) {
+ return this;
+ }
+
+ var event = L.Util.extend({}, data, { type: type, target: this });
+
+ var events = this[eventsKey],
+ listeners, i, len, typeIndex, contextId;
+
+ if (events[type]) {
+ // make sure adding/removing listeners inside other listeners won't cause infinite loop
+ listeners = events[type].slice();
+
+ for (i = 0, len = listeners.length; i < len; i++) {
+ listeners[i].action.call(listeners[i].context, event);
+ }
+ }
+
+ // fire event for the context-indexed listeners as well
+ typeIndex = events[type + '_idx'];
+
+ for (contextId in typeIndex) {
+ listeners = typeIndex[contextId].slice();
+
+ if (listeners) {
+ for (i = 0, len = listeners.length; i < len; i++) {
+ listeners[i].action.call(listeners[i].context, event);
+ }
+ }
+ }
+
+ return this;
+ },
+
+ addOneTimeEventListener: function (types, fn, context) {
+
+ if (L.Util.invokeEach(types, this.addOneTimeEventListener, this, fn, context)) { return this; }
+
+ var handler = L.bind(function () {
+ this
+ .removeEventListener(types, fn, context)
+ .removeEventListener(types, handler, context);
+ }, this);
+
+ return this
+ .addEventListener(types, fn, context)
+ .addEventListener(types, handler, context);
+ }
+};
+
+L.Mixin.Events.on = L.Mixin.Events.addEventListener;
+L.Mixin.Events.off = L.Mixin.Events.removeEventListener;
+L.Mixin.Events.once = L.Mixin.Events.addOneTimeEventListener;
+L.Mixin.Events.fire = L.Mixin.Events.fireEvent;
+
+
+/*
+ * L.Browser handles different browser and feature detections for internal Leaflet use.
+ */
+
+(function () {
+
+ var ie = 'ActiveXObject' in window,
+ ielt9 = ie && !document.addEventListener,
+
+ // terrible browser detection to work around Safari / iOS / Android browser bugs
+ ua = navigator.userAgent.toLowerCase(),
+ webkit = ua.indexOf('webkit') !== -1,
+ chrome = ua.indexOf('chrome') !== -1,
+ phantomjs = ua.indexOf('phantom') !== -1,
+ android = ua.indexOf('android') !== -1,
+ android23 = ua.search('android [23]') !== -1,
+ gecko = ua.indexOf('gecko') !== -1,
+
+ mobile = typeof orientation !== undefined + '',
+ msPointer = window.navigator && window.navigator.msPointerEnabled &&
+ window.navigator.msMaxTouchPoints && !window.PointerEvent,
+ pointer = (window.PointerEvent && window.navigator.pointerEnabled && window.navigator.maxTouchPoints) ||
+ msPointer,
+ retina = ('devicePixelRatio' in window && window.devicePixelRatio > 1) ||
+ ('matchMedia' in window && window.matchMedia('(min-resolution:144dpi)') &&
+ window.matchMedia('(min-resolution:144dpi)').matches),
+
+ doc = document.documentElement,
+ ie3d = ie && ('transition' in doc.style),
+ webkit3d = ('WebKitCSSMatrix' in window) && ('m11' in new window.WebKitCSSMatrix()) && !android23,
+ gecko3d = 'MozPerspective' in doc.style,
+ opera3d = 'OTransition' in doc.style,
+ any3d = !window.L_DISABLE_3D && (ie3d || webkit3d || gecko3d || opera3d) && !phantomjs;
+
+
+ // PhantomJS has 'ontouchstart' in document.documentElement, but doesn't actually support touch.
+ // https://github.com/Leaflet/Leaflet/pull/1434#issuecomment-13843151
+
+ var touch = !window.L_NO_TOUCH && !phantomjs && (function () {
+
+ var startName = 'ontouchstart';
+
+ // IE10+ (We simulate these into touch* events in L.DomEvent and L.DomEvent.Pointer) or WebKit, etc.
+ if (pointer || (startName in doc)) {
+ return true;
+ }
+
+ // Firefox/Gecko
+ var div = document.createElement('div'),
+ supported = false;
+
+ if (!div.setAttribute) {
+ return false;
+ }
+ div.setAttribute(startName, 'return;');
+
+ if (typeof div[startName] === 'function') {
+ supported = true;
+ }
+
+ div.removeAttribute(startName);
+ div = null;
+
+ return supported;
+ }());
+
+
+ L.Browser = {
+ ie: ie,
+ ielt9: ielt9,
+ webkit: webkit,
+ gecko: gecko && !webkit && !window.opera && !ie,
+
+ android: android,
+ android23: android23,
+
+ chrome: chrome,
+
+ ie3d: ie3d,
+ webkit3d: webkit3d,
+ gecko3d: gecko3d,
+ opera3d: opera3d,
+ any3d: any3d,
+
+ mobile: mobile,
+ mobileWebkit: mobile && webkit,
+ mobileWebkit3d: mobile && webkit3d,
+ mobileOpera: mobile && window.opera,
+
+ touch: touch,
+ msPointer: msPointer,
+ pointer: pointer,
+
+ retina: retina
+ };
+
+}());
+
+
+/*
+ * L.Point represents a point with x and y coordinates.
+ */
+
+L.Point = function (/*Number*/ x, /*Number*/ y, /*Boolean*/ round) {
+ this.x = (round ? Math.round(x) : x);
+ this.y = (round ? Math.round(y) : y);
+};
+
+L.Point.prototype = {
+
+ clone: function () {
+ return new L.Point(this.x, this.y);
+ },
+
+ // non-destructive, returns a new point
+ add: function (point) {
+ return this.clone()._add(L.point(point));
+ },
+
+ // destructive, used directly for performance in situations where it's safe to modify existing point
+ _add: function (point) {
+ this.x += point.x;
+ this.y += point.y;
+ return this;
+ },
+
+ subtract: function (point) {
+ return this.clone()._subtract(L.point(point));
+ },
+
+ _subtract: function (point) {
+ this.x -= point.x;
+ this.y -= point.y;
+ return this;
+ },
+
+ divideBy: function (num) {
+ return this.clone()._divideBy(num);
+ },
+
+ _divideBy: function (num) {
+ this.x /= num;
+ this.y /= num;
+ return this;
+ },
+
+ multiplyBy: function (num) {
+ return this.clone()._multiplyBy(num);
+ },
+
+ _multiplyBy: function (num) {
+ this.x *= num;
+ this.y *= num;
+ return this;
+ },
+
+ round: function () {
+ return this.clone()._round();
+ },
+
+ _round: function () {
+ this.x = Math.round(this.x);
+ this.y = Math.round(this.y);
+ return this;
+ },
+
+ floor: function () {
+ return this.clone()._floor();
+ },
+
+ _floor: function () {
+ this.x = Math.floor(this.x);
+ this.y = Math.floor(this.y);
+ return this;
+ },
+
+ distanceTo: function (point) {
+ point = L.point(point);
+
+ var x = point.x - this.x,
+ y = point.y - this.y;
+
+ return Math.sqrt(x * x + y * y);
+ },
+
+ equals: function (point) {
+ point = L.point(point);
+
+ return point.x === this.x &&
+ point.y === this.y;
+ },
+
+ contains: function (point) {
+ point = L.point(point);
+
+ return Math.abs(point.x) <= Math.abs(this.x) &&
+ Math.abs(point.y) <= Math.abs(this.y);
+ },
+
+ toString: function () {
+ return 'Point(' +
+ L.Util.formatNum(this.x) + ', ' +
+ L.Util.formatNum(this.y) + ')';
+ }
+};
+
+L.point = function (x, y, round) {
+ if (x instanceof L.Point) {
+ return x;
+ }
+ if (L.Util.isArray(x)) {
+ return new L.Point(x[0], x[1]);
+ }
+ if (x === undefined || x === null) {
+ return x;
+ }
+ return new L.Point(x, y, round);
+};
+
+
+/*
+ * L.Bounds represents a rectangular area on the screen in pixel coordinates.
+ */
+
+L.Bounds = function (a, b) { //(Point, Point) or Point[]
+ if (!a) { return; }
+
+ var points = b ? [a, b] : a;
+
+ for (var i = 0, len = points.length; i < len; i++) {
+ this.extend(points[i]);
+ }
+};
+
+L.Bounds.prototype = {
+ // extend the bounds to contain the given point
+ extend: function (point) { // (Point)
+ point = L.point(point);
+
+ 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;
+ },
+
+ getCenter: function (round) { // (Boolean) -> Point
+ return new L.Point(
+ (this.min.x + this.max.x) / 2,
+ (this.min.y + this.max.y) / 2, round);
+ },
+
+ getBottomLeft: function () { // -> Point
+ return new L.Point(this.min.x, this.max.y);
+ },
+
+ getTopRight: function () { // -> Point
+ return new L.Point(this.max.x, this.min.y);
+ },
+
+ getSize: function () {
+ return this.max.subtract(this.min);
+ },
+
+ contains: function (obj) { // (Bounds) or (Point) -> Boolean
+ var min, max;
+
+ if (typeof obj[0] === 'number' || obj instanceof L.Point) {
+ obj = L.point(obj);
+ } else {
+ obj = L.bounds(obj);
+ }
+
+ if (obj instanceof L.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);
+ },
+
+ intersects: function (bounds) { // (Bounds) -> Boolean
+ bounds = L.bounds(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;
+ },
+
+ isValid: function () {
+ return !!(this.min && this.max);
+ }
+};
+
+L.bounds = function (a, b) { // (Bounds) or (Point, Point) or (Point[])
+ if (!a || a instanceof L.Bounds) {
+ return a;
+ }
+ return new L.Bounds(a, b);
+};
+
+
+/*
+ * L.Transformation is an utility class to perform simple point transformations through a 2d-matrix.
+ */
+
+L.Transformation = function (a, b, c, d) {
+ this._a = a;
+ this._b = b;
+ this._c = c;
+ this._d = d;
+};
+
+L.Transformation.prototype = {
+ 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;
+ },
+
+ untransform: function (point, scale) {
+ scale = scale || 1;
+ return new L.Point(
+ (point.x / scale - this._b) / this._a,
+ (point.y / scale - this._d) / this._c);
+ }
+};
+
+
+/*
+ * L.DomUtil contains various utility functions for working with DOM.
+ */
+
+L.DomUtil = {
+ get: function (id) {
+ return (typeof id === 'string' ? document.getElementById(id) : id);
+ },
+
+ getStyle: function (el, style) {
+
+ var value = el.style[style];
+
+ if (!value && el.currentStyle) {
+ value = 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;
+ },
+
+ getViewportOffset: function (element) {
+
+ var top = 0,
+ left = 0,
+ el = element,
+ docBody = document.body,
+ docEl = document.documentElement,
+ pos;
+
+ do {
+ top += el.offsetTop || 0;
+ left += el.offsetLeft || 0;
+
+ //add borders
+ top += parseInt(L.DomUtil.getStyle(el, 'borderTopWidth'), 10) || 0;
+ left += parseInt(L.DomUtil.getStyle(el, 'borderLeftWidth'), 10) || 0;
+
+ pos = L.DomUtil.getStyle(el, 'position');
+
+ if (el.offsetParent === docBody && pos === 'absolute') { break; }
+
+ if (pos === 'fixed') {
+ top += docBody.scrollTop || docEl.scrollTop || 0;
+ left += docBody.scrollLeft || docEl.scrollLeft || 0;
+ break;
+ }
+
+ if (pos === 'relative' && !el.offsetLeft) {
+ var width = L.DomUtil.getStyle(el, 'width'),
+ maxWidth = L.DomUtil.getStyle(el, 'max-width'),
+ r = el.getBoundingClientRect();
+
+ if (width !== 'none' || maxWidth !== 'none') {
+ left += r.left + el.clientLeft;
+ }
+
+ //calculate full y offset since we're breaking out of the loop
+ top += r.top + (docBody.scrollTop || docEl.scrollTop || 0);
+
+ break;
+ }
+
+ el = el.offsetParent;
+
+ } while (el);
+
+ el = element;
+
+ do {
+ if (el === docBody) { break; }
+
+ top -= el.scrollTop || 0;
+ left -= el.scrollLeft || 0;
+
+ el = el.parentNode;
+ } while (el);
+
+ return new L.Point(left, top);
+ },
+
+ documentIsLtr: function () {
+ if (!L.DomUtil._docIsLtrCached) {
+ L.DomUtil._docIsLtrCached = true;
+ L.DomUtil._docIsLtr = L.DomUtil.getStyle(document.body, 'direction') === 'ltr';
+ }
+ return L.DomUtil._docIsLtr;
+ },
+
+ create: function (tagName, className, container) {
+
+ var el = document.createElement(tagName);
+ el.className = className;
+
+ if (container) {
+ container.appendChild(el);
+ }
+
+ return el;
+ },
+
+ hasClass: function (el, name) {
+ if (el.classList !== undefined) {
+ return el.classList.contains(name);
+ }
+ var className = L.DomUtil._getClass(el);
+ return className.length > 0 && new RegExp('(^|\\s)' + name + '(\\s|$)').test(className);
+ },
+
+ addClass: function (el, name) {
+ if (el.classList !== undefined) {
+ var classes = L.Util.splitWords(name);
+ for (var i = 0, len = classes.length; i < len; i++) {
+ el.classList.add(classes[i]);
+ }
+ } else if (!L.DomUtil.hasClass(el, name)) {
+ var className = L.DomUtil._getClass(el);
+ L.DomUtil._setClass(el, (className ? className + ' ' : '') + name);
+ }
+ },
+
+ removeClass: function (el, name) {
+ if (el.classList !== undefined) {
+ el.classList.remove(name);
+ } else {
+ L.DomUtil._setClass(el, L.Util.trim((' ' + L.DomUtil._getClass(el) + ' ').replace(' ' + name + ' ', ' ')));
+ }
+ },
+
+ _setClass: function (el, name) {
+ if (el.className.baseVal === undefined) {
+ el.className = name;
+ } else {
+ // in case of SVG element
+ el.className.baseVal = name;
+ }
+ },
+
+ _getClass: function (el) {
+ return el.className.baseVal === undefined ? el.className : el.className.baseVal;
+ },
+
+ setOpacity: function (el, value) {
+
+ if ('opacity' in el.style) {
+ el.style.opacity = value;
+
+ } else if ('filter' in el.style) {
+
+ 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 + ')';
+ }
+ }
+ },
+
+ testProp: function (props) {
+
+ var style = document.documentElement.style;
+
+ for (var i = 0; i < props.length; i++) {
+ if (props[i] in style) {
+ return props[i];
+ }
+ }
+ return false;
+ },
+
+ getTranslateString: function (point) {
+ // on WebKit browsers (Chrome/Safari/iOS Safari/Android) using translate3d instead of translate
+ // makes animation smoother as it ensures HW accel is used. Firefox 13 doesn't care
+ // (same speed either way), Opera 12 doesn't support translate3d
+
+ var is3d = L.Browser.webkit3d,
+ open = 'translate' + (is3d ? '3d' : '') + '(',
+ close = (is3d ? ',0' : '') + ')';
+
+ return open + point.x + 'px,' + point.y + 'px' + close;
+ },
+
+ getScaleString: function (scale, origin) {
+
+ var preTranslateStr = L.DomUtil.getTranslateString(origin.add(origin.multiplyBy(-1 * scale))),
+ scaleStr = ' scale(' + scale + ') ';
+
+ return preTranslateStr + scaleStr;
+ },
+
+ setPosition: function (el, point, disable3D) { // (HTMLElement, Point[, Boolean])
+
+ // jshint camelcase: false
+ el._leaflet_pos = point;
+
+ if (!disable3D && L.Browser.any3d) {
+ el.style[L.DomUtil.TRANSFORM] = L.DomUtil.getTranslateString(point);
+ } else {
+ el.style.left = point.x + 'px';
+ el.style.top = point.y + 'px';
+ }
+ },
+
+ getPosition: function (el) {
+ // this method is only used for elements previously positioned using setPosition,
+ // so it's safe to cache the position for performance
+
+ // jshint camelcase: false
+ return el._leaflet_pos;
+ }
+};
+
+
+// prefix style property names
+
+L.DomUtil.TRANSFORM = L.DomUtil.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
+
+L.DomUtil.TRANSITION = L.DomUtil.testProp(
+ ['webkitTransition', 'transition', 'OTransition', 'MozTransition', 'msTransition']);
+
+L.DomUtil.TRANSITION_END =
+ L.DomUtil.TRANSITION === 'webkitTransition' || L.DomUtil.TRANSITION === 'OTransition' ?
+ L.DomUtil.TRANSITION + 'End' : 'transitionend';
+
+(function () {
+ if ('onselectstart' in document) {
+ L.extend(L.DomUtil, {
+ disableTextSelection: function () {
+ L.DomEvent.on(window, 'selectstart', L.DomEvent.preventDefault);
+ },
+
+ enableTextSelection: function () {
+ L.DomEvent.off(window, 'selectstart', L.DomEvent.preventDefault);
+ }
+ });
+ } else {
+ var userSelectProperty = L.DomUtil.testProp(
+ ['userSelect', 'WebkitUserSelect', 'OUserSelect', 'MozUserSelect', 'msUserSelect']);
+
+ L.extend(L.DomUtil, {
+ disableTextSelection: function () {
+ if (userSelectProperty) {
+ var style = document.documentElement.style;
+ this._userSelect = style[userSelectProperty];
+ style[userSelectProperty] = 'none';
+ }
+ },
+
+ enableTextSelection: function () {
+ if (userSelectProperty) {
+ document.documentElement.style[userSelectProperty] = this._userSelect;
+ delete this._userSelect;
+ }
+ }
+ });
+ }
+
+ L.extend(L.DomUtil, {
+ disableImageDrag: function () {
+ L.DomEvent.on(window, 'dragstart', L.DomEvent.preventDefault);
+ },
+
+ enableImageDrag: function () {
+ L.DomEvent.off(window, 'dragstart', L.DomEvent.preventDefault);
+ }
+ });
+})();
+
+
+/*
+ * L.LatLng represents a geographical point with latitude and longitude coordinates.
+ */
+
+L.LatLng = function (lat, lng, alt) { // (Number, Number, Number)
+ lat = parseFloat(lat);
+ lng = parseFloat(lng);
+
+ if (isNaN(lat) || isNaN(lng)) {
+ throw new Error('Invalid LatLng object: (' + lat + ', ' + lng + ')');
+ }
+
+ this.lat = lat;
+ this.lng = lng;
+
+ if (alt !== undefined) {
+ this.alt = parseFloat(alt);
+ }
+};
+
+L.extend(L.LatLng, {
+ DEG_TO_RAD: Math.PI / 180,
+ RAD_TO_DEG: 180 / Math.PI,
+ MAX_MARGIN: 1.0E-9 // max margin of error for the "equals" check
+});
+
+L.LatLng.prototype = {
+ equals: function (obj) { // (LatLng) -> Boolean
+ if (!obj) { return false; }
+
+ obj = L.latLng(obj);
+
+ var margin = Math.max(
+ Math.abs(this.lat - obj.lat),
+ Math.abs(this.lng - obj.lng));
+
+ return margin <= L.LatLng.MAX_MARGIN;
+ },
+
+ toString: function (precision) { // (Number) -> String
+ return 'LatLng(' +
+ L.Util.formatNum(this.lat, precision) + ', ' +
+ L.Util.formatNum(this.lng, precision) + ')';
+ },
+
+ // Haversine distance formula, see http://en.wikipedia.org/wiki/Haversine_formula
+ // TODO move to projection code, LatLng shouldn't know about Earth
+ distanceTo: function (other) { // (LatLng) -> Number
+ other = L.latLng(other);
+
+ var R = 6378137, // earth radius in meters
+ d2r = L.LatLng.DEG_TO_RAD,
+ dLat = (other.lat - this.lat) * d2r,
+ dLon = (other.lng - this.lng) * d2r,
+ lat1 = this.lat * d2r,
+ lat2 = other.lat * d2r,
+ sin1 = Math.sin(dLat / 2),
+ sin2 = Math.sin(dLon / 2);
+
+ var a = sin1 * sin1 + sin2 * sin2 * Math.cos(lat1) * Math.cos(lat2);
+
+ return R * 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
+ },
+
+ wrap: function (a, b) { // (Number, Number) -> LatLng
+ var lng = this.lng;
+
+ a = a || -180;
+ b = b || 180;
+
+ lng = (lng + b) % (b - a) + (lng < a || lng === b ? b : a);
+
+ return new L.LatLng(this.lat, lng);
+ }
+};
+
+L.latLng = function (a, b) { // (LatLng) or ([Number, Number]) or (Number, Number)
+ if (a instanceof L.LatLng) {
+ return a;
+ }
+ if (L.Util.isArray(a)) {
+ if (typeof a[0] === 'number' || typeof a[0] === 'string') {
+ return new L.LatLng(a[0], a[1], a[2]);
+ } else {
+ return null;
+ }
+ }
+ if (a === undefined || a === null) {
+ return a;
+ }
+ if (typeof a === 'object' && 'lat' in a) {
+ return new L.LatLng(a.lat, 'lng' in a ? a.lng : a.lon);
+ }
+ if (b === undefined) {
+ return null;
+ }
+ return new L.LatLng(a, b);
+};
+
+
+
+/*
+ * L.LatLngBounds represents a rectangular area on the map in geographical coordinates.
+ */
+
+L.LatLngBounds = function (southWest, northEast) { // (LatLng, LatLng) or (LatLng[])
+ if (!southWest) { return; }
+
+ var latlngs = northEast ? [southWest, northEast] : southWest;
+
+ for (var i = 0, len = latlngs.length; i < len; i++) {
+ this.extend(latlngs[i]);
+ }
+};
+
+L.LatLngBounds.prototype = {
+ // extend the bounds to contain the given point or bounds
+ extend: function (obj) { // (LatLng) or (LatLngBounds)
+ if (!obj) { return this; }
+
+ var latLng = L.latLng(obj);
+ if (latLng !== null) {
+ obj = latLng;
+ } else {
+ obj = L.latLngBounds(obj);
+ }
+
+ if (obj instanceof L.LatLng) {
+ if (!this._southWest && !this._northEast) {
+ this._southWest = new L.LatLng(obj.lat, obj.lng);
+ this._northEast = new L.LatLng(obj.lat, obj.lng);
+ } else {
+ this._southWest.lat = Math.min(obj.lat, this._southWest.lat);
+ this._southWest.lng = Math.min(obj.lng, this._southWest.lng);
+
+ this._northEast.lat = Math.max(obj.lat, this._northEast.lat);
+ this._northEast.lng = Math.max(obj.lng, this._northEast.lng);
+ }
+ } else if (obj instanceof L.LatLngBounds) {
+ this.extend(obj._southWest);
+ this.extend(obj._northEast);
+ }
+ return this;
+ },
+
+ // extend the bounds by a percentage
+ pad: function (bufferRatio) { // (Number) -> LatLngBounds
+ 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 L.LatLngBounds(
+ new L.LatLng(sw.lat - heightBuffer, sw.lng - widthBuffer),
+ new L.LatLng(ne.lat + heightBuffer, ne.lng + widthBuffer));
+ },
+
+ getCenter: function () { // -> LatLng
+ return new L.LatLng(
+ (this._southWest.lat + this._northEast.lat) / 2,
+ (this._southWest.lng + this._northEast.lng) / 2);
+ },
+
+ getSouthWest: function () {
+ return this._southWest;
+ },
+
+ getNorthEast: function () {
+ return this._northEast;
+ },
+
+ getNorthWest: function () {
+ return new L.LatLng(this.getNorth(), this.getWest());
+ },
+
+ getSouthEast: function () {
+ return new L.LatLng(this.getSouth(), this.getEast());
+ },
+
+ getWest: function () {
+ return this._southWest.lng;
+ },
+
+ getSouth: function () {
+ return this._southWest.lat;
+ },
+
+ getEast: function () {
+ return this._northEast.lng;
+ },
+
+ getNorth: function () {
+ return this._northEast.lat;
+ },
+
+ contains: function (obj) { // (LatLngBounds) or (LatLng) -> Boolean
+ if (typeof obj[0] === 'number' || obj instanceof L.LatLng) {
+ obj = L.latLng(obj);
+ } else {
+ obj = L.latLngBounds(obj);
+ }
+
+ var sw = this._southWest,
+ ne = this._northEast,
+ sw2, ne2;
+
+ if (obj instanceof L.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);
+ },
+
+ intersects: function (bounds) { // (LatLngBounds)
+ bounds = L.latLngBounds(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;
+ },
+
+ toBBoxString: function () {
+ return [this.getWest(), this.getSouth(), this.getEast(), this.getNorth()].join(',');
+ },
+
+ equals: function (bounds) { // (LatLngBounds)
+ if (!bounds) { return false; }
+
+ bounds = L.latLngBounds(bounds);
+
+ return this._southWest.equals(bounds.getSouthWest()) &&
+ this._northEast.equals(bounds.getNorthEast());
+ },
+
+ isValid: function () {
+ return !!(this._southWest && this._northEast);
+ }
+};
+
+//TODO International date line?
+
+L.latLngBounds = function (a, b) { // (LatLngBounds) or (LatLng, LatLng)
+ if (!a || a instanceof L.LatLngBounds) {
+ return a;
+ }
+ return new L.LatLngBounds(a, b);
+};
+
+
+/*
+ * L.Projection contains various geographical projections used by CRS classes.
+ */
+
+L.Projection = {};
+
+
+/*
+ * Spherical Mercator is the most popular map projection, used by EPSG:3857 CRS used by default.
+ */
+
+L.Projection.SphericalMercator = {
+ MAX_LATITUDE: 85.0511287798,
+
+ project: function (latlng) { // (LatLng) -> Point
+ var d = L.LatLng.DEG_TO_RAD,
+ max = this.MAX_LATITUDE,
+ lat = Math.max(Math.min(max, latlng.lat), -max),
+ x = latlng.lng * d,
+ y = lat * d;
+
+ y = Math.log(Math.tan((Math.PI / 4) + (y / 2)));
+
+ return new L.Point(x, y);
+ },
+
+ unproject: function (point) { // (Point, Boolean) -> LatLng
+ var d = L.LatLng.RAD_TO_DEG,
+ lng = point.x * d,
+ lat = (2 * Math.atan(Math.exp(point.y)) - (Math.PI / 2)) * d;
+
+ return new L.LatLng(lat, lng);
+ }
+};
+
+
+/*
+ * Simple equirectangular (Plate Carree) projection, used by CRS like EPSG:4326 and Simple.
+ */
+
+L.Projection.LonLat = {
+ project: function (latlng) {
+ return new L.Point(latlng.lng, latlng.lat);
+ },
+
+ unproject: function (point) {
+ return new L.LatLng(point.y, point.x);
+ }
+};
+
+
+/*
+ * L.CRS is a base object for all defined CRS (Coordinate Reference Systems) in Leaflet.
+ */
+
+L.CRS = {
+ latLngToPoint: function (latlng, zoom) { // (LatLng, Number) -> Point
+ var projectedPoint = this.projection.project(latlng),
+ scale = this.scale(zoom);
+
+ return this.transformation._transform(projectedPoint, scale);
+ },
+
+ pointToLatLng: function (point, zoom) { // (Point, Number[, Boolean]) -> LatLng
+ var scale = this.scale(zoom),
+ untransformedPoint = this.transformation.untransform(point, scale);
+
+ return this.projection.unproject(untransformedPoint);
+ },
+
+ project: function (latlng) {
+ return this.projection.project(latlng);
+ },
+
+ scale: function (zoom) {
+ return 256 * Math.pow(2, zoom);
+ },
+
+ getSize: function (zoom) {
+ var s = this.scale(zoom);
+ return L.point(s, s);
+ }
+};
+
+
+/*
+ * A simple CRS that can be used for flat non-Earth maps like panoramas or game maps.
+ */
+
+L.CRS.Simple = L.extend({}, L.CRS, {
+ projection: L.Projection.LonLat,
+ transformation: new L.Transformation(1, 0, -1, 0),
+
+ scale: function (zoom) {
+ return Math.pow(2, zoom);
+ }
+});
+
+
+/*
+ * L.CRS.EPSG3857 (Spherical Mercator) is the most common CRS for web mapping
+ * and is used by Leaflet by default.
+ */
+
+L.CRS.EPSG3857 = L.extend({}, L.CRS, {
+ code: 'EPSG:3857',
+
+ projection: L.Projection.SphericalMercator,
+ transformation: new L.Transformation(0.5 / Math.PI, 0.5, -0.5 / Math.PI, 0.5),
+
+ project: function (latlng) { // (LatLng) -> Point
+ var projectedPoint = this.projection.project(latlng),
+ earthRadius = 6378137;
+ return projectedPoint.multiplyBy(earthRadius);
+ }
+});
+
+L.CRS.EPSG900913 = L.extend({}, L.CRS.EPSG3857, {
+ code: 'EPSG:900913'
+});
+
+
+/*
+ * L.CRS.EPSG4326 is a CRS popular among advanced GIS specialists.
+ */
+
+L.CRS.EPSG4326 = L.extend({}, L.CRS, {
+ code: 'EPSG:4326',
+
+ projection: L.Projection.LonLat,
+ transformation: new L.Transformation(1 / 360, 0.5, -1 / 360, 0.5)
+});
+
+
+/*
+ * L.Map is the central class of the API - it is used to create a map.
+ */
+
+L.Map = L.Class.extend({
+
+ includes: L.Mixin.Events,
+
+ options: {
+ crs: L.CRS.EPSG3857,
+
+ /*
+ center: LatLng,
+ zoom: Number,
+ layers: Array,
+ */
+
+ fadeAnimation: L.DomUtil.TRANSITION && !L.Browser.android23,
+ trackResize: true,
+ markerZoomAnimation: L.DomUtil.TRANSITION && L.Browser.any3d
+ },
+
+ initialize: function (id, options) { // (HTMLElement or String, Object)
+ options = L.setOptions(this, options);
+
+
+ this._initContainer(id);
+ this._initLayout();
+
+ // hack for https://github.com/Leaflet/Leaflet/issues/1980
+ this._onResize = L.bind(this._onResize, this);
+
+ this._initEvents();
+
+ if (options.maxBounds) {
+ this.setMaxBounds(options.maxBounds);
+ }
+
+ if (options.center && options.zoom !== undefined) {
+ this.setView(L.latLng(options.center), options.zoom, {reset: true});
+ }
+
+ this._handlers = [];
+
+ this._layers = {};
+ this._zoomBoundLayers = {};
+ this._tileLayersNum = 0;
+
+ this.callInitHooks();
+
+ this._addLayers(options.layers);
+ },
+
+
+ // public methods that modify map state
+
+ // replaced by animation-powered implementation in Map.PanAnimation.js
+ setView: function (center, zoom) {
+ zoom = zoom === undefined ? this.getZoom() : zoom;
+ this._resetView(L.latLng(center), this._limitZoom(zoom));
+ return this;
+ },
+
+ setZoom: function (zoom, options) {
+ if (!this._loaded) {
+ this._zoom = this._limitZoom(zoom);
+ return this;
+ }
+ return this.setView(this.getCenter(), zoom, {zoom: options});
+ },
+
+ zoomIn: function (delta, options) {
+ return this.setZoom(this._zoom + (delta || 1), options);
+ },
+
+ zoomOut: function (delta, options) {
+ return this.setZoom(this._zoom - (delta || 1), options);
+ },
+
+ setZoomAround: function (latlng, zoom, options) {
+ var scale = this.getZoomScale(zoom),
+ viewHalf = this.getSize().divideBy(2),
+ containerPoint = latlng instanceof L.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});
+ },
+
+ fitBounds: function (bounds, options) {
+
+ options = options || {};
+ bounds = bounds.getBounds ? bounds.getBounds() : L.latLngBounds(bounds);
+
+ var paddingTL = L.point(options.paddingTopLeft || options.padding || [0, 0]),
+ paddingBR = L.point(options.paddingBottomRight || options.padding || [0, 0]),
+
+ zoom = this.getBoundsZoom(bounds, false, paddingTL.add(paddingBR)),
+ 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);
+
+ zoom = options && options.maxZoom ? Math.min(options.maxZoom, zoom) : zoom;
+
+ return this.setView(center, zoom, options);
+ },
+
+ fitWorld: function (options) {
+ return this.fitBounds([[-90, -180], [90, 180]], options);
+ },
+
+ panTo: function (center, options) { // (LatLng)
+ return this.setView(center, this._zoom, {pan: options});
+ },
+
+ panBy: function (offset) { // (Point)
+ // replaced with animated panBy in Map.PanAnimation.js
+ this.fire('movestart');
+
+ this._rawPanBy(L.point(offset));
+
+ this.fire('move');
+ return this.fire('moveend');
+ },
+
+ setMaxBounds: function (bounds) {
+ bounds = L.latLngBounds(bounds);
+
+ this.options.maxBounds = bounds;
+
+ if (!bounds) {
+ return this.off('moveend', this._panInsideMaxBounds, this);
+ }
+
+ if (this._loaded) {
+ this._panInsideMaxBounds();
+ }
+
+ return this.on('moveend', this._panInsideMaxBounds, this);
+ },
+
+ panInsideBounds: function (bounds, options) {
+ var center = this.getCenter(),
+ newCenter = this._limitCenter(center, this._zoom, bounds);
+
+ if (center.equals(newCenter)) { return this; }
+
+ return this.panTo(newCenter, options);
+ },
+
+ addLayer: function (layer) {
+ // TODO method is too big, refactor
+
+ var id = L.stamp(layer);
+
+ if (this._layers[id]) { return this; }
+
+ this._layers[id] = layer;
+
+ // TODO getMaxZoom, getMinZoom in ILayer (instead of options)
+ if (layer.options && (!isNaN(layer.options.maxZoom) || !isNaN(layer.options.minZoom))) {
+ this._zoomBoundLayers[id] = layer;
+ this._updateZoomLevels();
+ }
+
+ // TODO looks ugly, refactor!!!
+ if (this.options.zoomAnimation && L.TileLayer && (layer instanceof L.TileLayer)) {
+ this._tileLayersNum++;
+ this._tileLayersToLoad++;
+ layer.on('load', this._onTileLayerLoad, this);
+ }
+
+ if (this._loaded) {
+ this._layerAdd(layer);
+ }
+
+ return this;
+ },
+
+ removeLayer: function (layer) {
+ var id = L.stamp(layer);
+
+ if (!this._layers[id]) { return this; }
+
+ if (this._loaded) {
+ layer.onRemove(this);
+ }
+
+ delete this._layers[id];
+
+ if (this._loaded) {
+ this.fire('layerremove', {layer: layer});
+ }
+
+ if (this._zoomBoundLayers[id]) {
+ delete this._zoomBoundLayers[id];
+ this._updateZoomLevels();
+ }
+
+ // TODO looks ugly, refactor
+ if (this.options.zoomAnimation && L.TileLayer && (layer instanceof L.TileLayer)) {
+ this._tileLayersNum--;
+ this._tileLayersToLoad--;
+ layer.off('load', this._onTileLayerLoad, this);
+ }
+
+ return this;
+ },
+
+ hasLayer: function (layer) {
+ if (!layer) { return false; }
+
+ return (L.stamp(layer) in this._layers);
+ },
+
+ eachLayer: function (method, context) {
+ for (var i in this._layers) {
+ method.call(context, this._layers[i]);
+ }
+ return this;
+ },
+
+ invalidateSize: function (options) {
+ if (!this._loaded) { return this; }
+
+ options = L.extend({
+ animate: false,
+ pan: true
+ }, options === true ? {animate: true} : options);
+
+ var oldSize = this.getSize();
+ this._sizeChanged = true;
+ this._initialCenter = 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(L.bind(this.fire, this, 'moveend'), 200);
+ } else {
+ this.fire('moveend');
+ }
+ }
+
+ return this.fire('resize', {
+ oldSize: oldSize,
+ newSize: newSize
+ });
+ },
+
+ // TODO handler.addTo
+ 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;
+ },
+
+ remove: function () {
+ if (this._loaded) {
+ this.fire('unload');
+ }
+
+ this._initEvents('off');
+
+ try {
+ // throws error in IE6-8
+ delete this._container._leaflet;
+ } catch (e) {
+ this._container._leaflet = undefined;
+ }
+
+ this._clearPanes();
+ if (this._clearControlPos) {
+ this._clearControlPos();
+ }
+
+ this._clearHandlers();
+
+ return this;
+ },
+
+
+ // public methods for getting map state
+
+ getCenter: function () { // (Boolean) -> LatLng
+ this._checkIfLoaded();
+
+ if (this._initialCenter && !this._moved()) {
+ return this._initialCenter;
+ }
+ return this.layerPointToLatLng(this._getCenterLayerPoint());
+ },
+
+ getZoom: function () {
+ return this._zoom;
+ },
+
+ getBounds: function () {
+ var bounds = this.getPixelBounds(),
+ sw = this.unproject(bounds.getBottomLeft()),
+ ne = this.unproject(bounds.getTopRight());
+
+ return new L.LatLngBounds(sw, ne);
+ },
+
+ getMinZoom: function () {
+ return this.options.minZoom === undefined ?
+ (this._layersMinZoom === undefined ? 0 : this._layersMinZoom) :
+ this.options.minZoom;
+ },
+
+ getMaxZoom: function () {
+ return this.options.maxZoom === undefined ?
+ (this._layersMaxZoom === undefined ? Infinity : this._layersMaxZoom) :
+ this.options.maxZoom;
+ },
+
+ getBoundsZoom: function (bounds, inside, padding) { // (LatLngBounds[, Boolean, Point]) -> Number
+ bounds = L.latLngBounds(bounds);
+
+ var zoom = this.getMinZoom() - (inside ? 1 : 0),
+ maxZoom = this.getMaxZoom(),
+ size = this.getSize(),
+
+ nw = bounds.getNorthWest(),
+ se = bounds.getSouthEast(),
+
+ zoomNotFound = true,
+ boundsSize;
+
+ padding = L.point(padding || [0, 0]);
+
+ do {
+ zoom++;
+ boundsSize = this.project(se, zoom).subtract(this.project(nw, zoom)).add(padding);
+ zoomNotFound = !inside ? size.contains(boundsSize) : boundsSize.x < size.x || boundsSize.y < size.y;
+
+ } while (zoomNotFound && zoom <= maxZoom);
+
+ if (zoomNotFound && inside) {
+ return null;
+ }
+
+ return inside ? zoom : zoom - 1;
+ },
+
+ getSize: function () {
+ if (!this._size || this._sizeChanged) {
+ this._size = new L.Point(
+ this._container.clientWidth,
+ this._container.clientHeight);
+
+ this._sizeChanged = false;
+ }
+ return this._size.clone();
+ },
+
+ getPixelBounds: function () {
+ var topLeftPoint = this._getTopLeftPoint();
+ return new L.Bounds(topLeftPoint, topLeftPoint.add(this.getSize()));
+ },
+
+ getPixelOrigin: function () {
+ this._checkIfLoaded();
+ return this._initialTopLeftPoint;
+ },
+
+ getPanes: function () {
+ return this._panes;
+ },
+
+ getContainer: function () {
+ return this._container;
+ },
+
+
+ // TODO replace with universal implementation after refactoring projections
+
+ getZoomScale: function (toZoom) {
+ var crs = this.options.crs;
+ return crs.scale(toZoom) / crs.scale(this._zoom);
+ },
+
+ getScaleZoom: function (scale) {
+ return this._zoom + (Math.log(scale) / Math.LN2);
+ },
+
+
+ // conversion methods
+
+ project: function (latlng, zoom) { // (LatLng[, Number]) -> Point
+ zoom = zoom === undefined ? this._zoom : zoom;
+ return this.options.crs.latLngToPoint(L.latLng(latlng), zoom);
+ },
+
+ unproject: function (point, zoom) { // (Point[, Number]) -> LatLng
+ zoom = zoom === undefined ? this._zoom : zoom;
+ return this.options.crs.pointToLatLng(L.point(point), zoom);
+ },
+
+ layerPointToLatLng: function (point) { // (Point)
+ var projectedPoint = L.point(point).add(this.getPixelOrigin());
+ return this.unproject(projectedPoint);
+ },
+
+ latLngToLayerPoint: function (latlng) { // (LatLng)
+ var projectedPoint = this.project(L.latLng(latlng))._round();
+ return projectedPoint._subtract(this.getPixelOrigin());
+ },
+
+ containerPointToLayerPoint: function (point) { // (Point)
+ return L.point(point).subtract(this._getMapPanePos());
+ },
+
+ layerPointToContainerPoint: function (point) { // (Point)
+ return L.point(point).add(this._getMapPanePos());
+ },
+
+ containerPointToLatLng: function (point) {
+ var layerPoint = this.containerPointToLayerPoint(L.point(point));
+ return this.layerPointToLatLng(layerPoint);
+ },
+
+ latLngToContainerPoint: function (latlng) {
+ return this.layerPointToContainerPoint(this.latLngToLayerPoint(L.latLng(latlng)));
+ },
+
+ mouseEventToContainerPoint: function (e) { // (MouseEvent)
+ return L.DomEvent.getMousePosition(e, this._container);
+ },
+
+ mouseEventToLayerPoint: function (e) { // (MouseEvent)
+ return this.containerPointToLayerPoint(this.mouseEventToContainerPoint(e));
+ },
+
+ mouseEventToLatLng: function (e) { // (MouseEvent)
+ return this.layerPointToLatLng(this.mouseEventToLayerPoint(e));
+ },
+
+
+ // map initialization methods
+
+ _initContainer: function (id) {
+ var container = this._container = L.DomUtil.get(id);
+
+ if (!container) {
+ throw new Error('Map container not found.');
+ } else if (container._leaflet) {
+ throw new Error('Map container is already initialized.');
+ }
+
+ container._leaflet = true;
+ },
+
+ _initLayout: function () {
+ var container = this._container;
+
+ L.DomUtil.addClass(container, 'leaflet-container' +
+ (L.Browser.touch ? ' leaflet-touch' : '') +
+ (L.Browser.retina ? ' leaflet-retina' : '') +
+ (L.Browser.ielt9 ? ' leaflet-oldie' : '') +
+ (this.options.fadeAnimation ? ' leaflet-fade-anim' : ''));
+
+ var position = L.DomUtil.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._mapPane = panes.mapPane = this._createPane('leaflet-map-pane', this._container);
+
+ this._tilePane = panes.tilePane = this._createPane('leaflet-tile-pane', this._mapPane);
+ panes.objectsPane = this._createPane('leaflet-objects-pane', this._mapPane);
+ panes.shadowPane = this._createPane('leaflet-shadow-pane');
+ panes.overlayPane = this._createPane('leaflet-overlay-pane');
+ panes.markerPane = this._createPane('leaflet-marker-pane');
+ panes.popupPane = this._createPane('leaflet-popup-pane');
+
+ var zoomHide = ' leaflet-zoom-hide';
+
+ if (!this.options.markerZoomAnimation) {
+ L.DomUtil.addClass(panes.markerPane, zoomHide);
+ L.DomUtil.addClass(panes.shadowPane, zoomHide);
+ L.DomUtil.addClass(panes.popupPane, zoomHide);
+ }
+ },
+
+ _createPane: function (className, container) {
+ return L.DomUtil.create('div', className, container || this._panes.objectsPane);
+ },
+
+ _clearPanes: function () {
+ this._container.removeChild(this._mapPane);
+ },
+
+ _addLayers: function (layers) {
+ layers = layers ? (L.Util.isArray(layers) ? layers : [layers]) : [];
+
+ for (var i = 0, len = layers.length; i < len; i++) {
+ this.addLayer(layers[i]);
+ }
+ },
+
+
+ // private methods that modify map state
+
+ _resetView: function (center, zoom, preserveMapOffset, afterZoomAnim) {
+
+ var zoomChanged = (this._zoom !== zoom);
+
+ if (!afterZoomAnim) {
+ this.fire('movestart');
+
+ if (zoomChanged) {
+ this.fire('zoomstart');
+ }
+ }
+
+ this._zoom = zoom;
+ this._initialCenter = center;
+
+ this._initialTopLeftPoint = this._getNewTopLeftPoint(center);
+
+ if (!preserveMapOffset) {
+ L.DomUtil.setPosition(this._mapPane, new L.Point(0, 0));
+ } else {
+ this._initialTopLeftPoint._add(this._getMapPanePos());
+ }
+
+ this._tileLayersToLoad = this._tileLayersNum;
+
+ var loading = !this._loaded;
+ this._loaded = true;
+
+ this.fire('viewreset', {hard: !preserveMapOffset});
+
+ if (loading) {
+ this.fire('load');
+ this.eachLayer(this._layerAdd, this);
+ }
+
+ this.fire('move');
+
+ if (zoomChanged || afterZoomAnim) {
+ this.fire('zoomend');
+ }
+
+ this.fire('moveend', {hard: !preserveMapOffset});
+ },
+
+ _rawPanBy: function (offset) {
+ L.DomUtil.setPosition(this._mapPane, this._getMapPanePos().subtract(offset));
+ },
+
+ _getZoomSpan: function () {
+ return this.getMaxZoom() - this.getMinZoom();
+ },
+
+ _updateZoomLevels: function () {
+ var i,
+ minZoom = Infinity,
+ maxZoom = -Infinity,
+ oldZoomSpan = this._getZoomSpan();
+
+ for (i in this._zoomBoundLayers) {
+ var layer = this._zoomBoundLayers[i];
+ if (!isNaN(layer.options.minZoom)) {
+ minZoom = Math.min(minZoom, layer.options.minZoom);
+ }
+ if (!isNaN(layer.options.maxZoom)) {
+ maxZoom = Math.max(maxZoom, layer.options.maxZoom);
+ }
+ }
+
+ if (i === undefined) { // we have no tilelayers
+ this._layersMaxZoom = this._layersMinZoom = undefined;
+ } else {
+ this._layersMaxZoom = maxZoom;
+ this._layersMinZoom = minZoom;
+ }
+
+ if (oldZoomSpan !== this._getZoomSpan()) {
+ this.fire('zoomlevelschange');
+ }
+ },
+
+ _panInsideMaxBounds: function () {
+ this.panInsideBounds(this.options.maxBounds);
+ },
+
+ _checkIfLoaded: function () {
+ if (!this._loaded) {
+ throw new Error('Set map center and zoom first.');
+ }
+ },
+
+ // map events
+
+ _initEvents: function (onOff) {
+ if (!L.DomEvent) { return; }
+
+ onOff = onOff || 'on';
+
+ L.DomEvent[onOff](this._container, 'click', this._onMouseClick, this);
+
+ var events = ['dblclick', 'mousedown', 'mouseup', 'mouseenter',
+ 'mouseleave', 'mousemove', 'contextmenu'],
+ i, len;
+
+ for (i = 0, len = events.length; i < len; i++) {
+ L.DomEvent[onOff](this._container, events[i], this._fireMouseEvent, this);
+ }
+
+ if (this.options.trackResize) {
+ L.DomEvent[onOff](window, 'resize', this._onResize, this);
+ }
+ },
+
+ _onResize: function () {
+ L.Util.cancelAnimFrame(this._resizeRequest);
+ this._resizeRequest = L.Util.requestAnimFrame(
+ function () { this.invalidateSize({debounceMoveend: true}); }, this, false, this._container);
+ },
+
+ _onMouseClick: function (e) {
+ if (!this._loaded || (!e._simulated &&
+ ((this.dragging && this.dragging.moved()) ||
+ (this.boxZoom && this.boxZoom.moved()))) ||
+ L.DomEvent._skipped(e)) { return; }
+
+ this.fire('preclick');
+ this._fireMouseEvent(e);
+ },
+
+ _fireMouseEvent: function (e) {
+ if (!this._loaded || L.DomEvent._skipped(e)) { return; }
+
+ var type = e.type;
+
+ type = (type === 'mouseenter' ? 'mouseover' : (type === 'mouseleave' ? 'mouseout' : type));
+
+ if (!this.hasEventListeners(type)) { return; }
+
+ if (type === 'contextmenu') {
+ L.DomEvent.preventDefault(e);
+ }
+
+ var containerPoint = this.mouseEventToContainerPoint(e),
+ layerPoint = this.containerPointToLayerPoint(containerPoint),
+ latlng = this.layerPointToLatLng(layerPoint);
+
+ this.fire(type, {
+ latlng: latlng,
+ layerPoint: layerPoint,
+ containerPoint: containerPoint,
+ originalEvent: e
+ });
+ },
+
+ _onTileLayerLoad: function () {
+ this._tileLayersToLoad--;
+ if (this._tileLayersNum && !this._tileLayersToLoad) {
+ this.fire('tilelayersload');
+ }
+ },
+
+ _clearHandlers: function () {
+ for (var i = 0, len = this._handlers.length; i < len; i++) {
+ this._handlers[i].disable();
+ }
+ },
+
+ whenReady: function (callback, context) {
+ if (this._loaded) {
+ callback.call(context || this, this);
+ } else {
+ this.on('load', callback, context);
+ }
+ return this;
+ },
+
+ _layerAdd: function (layer) {
+ layer.onAdd(this);
+ this.fire('layeradd', {layer: layer});
+ },
+
+
+ // private methods for getting map state
+
+ _getMapPanePos: function () {
+ return L.DomUtil.getPosition(this._mapPane);
+ },
+
+ _moved: function () {
+ var pos = this._getMapPanePos();
+ return pos && !pos.equals([0, 0]);
+ },
+
+ _getTopLeftPoint: function () {
+ return this.getPixelOrigin().subtract(this._getMapPanePos());
+ },
+
+ _getNewTopLeftPoint: function (center, zoom) {
+ var viewHalf = this.getSize()._divideBy(2);
+ // TODO round on display, not calculation to increase precision?
+ return this.project(center, zoom)._subtract(viewHalf)._round();
+ },
+
+ _latLngToNewLayerPoint: function (latlng, newZoom, newCenter) {
+ var topLeft = this._getNewTopLeftPoint(newCenter, newZoom).add(this._getMapPanePos());
+ return this.project(latlng, newZoom)._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 L.Bounds(centerPoint.subtract(viewHalf), centerPoint.add(viewHalf)),
+ offset = this._getBoundsOffset(viewBounds, bounds, zoom);
+
+ 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 L.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 nwOffset = this.project(maxBounds.getNorthWest(), zoom).subtract(pxBounds.min),
+ seOffset = this.project(maxBounds.getSouthEast(), zoom).subtract(pxBounds.max),
+
+ dx = this._rebound(nwOffset.x, -seOffset.x),
+ dy = this._rebound(nwOffset.y, -seOffset.y);
+
+ return new L.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();
+
+ return Math.max(min, Math.min(max, zoom));
+ }
+});
+
+L.map = function (id, options) {
+ return new L.Map(id, options);
+};
+
+
+/*
+ * Mercator projection that takes into account that the Earth is not a perfect sphere.
+ * Less popular than spherical mercator; used by projections like EPSG:3395.
+ */
+
+L.Projection.Mercator = {
+ MAX_LATITUDE: 85.0840591556,
+
+ R_MINOR: 6356752.314245179,
+ R_MAJOR: 6378137,
+
+ project: function (latlng) { // (LatLng) -> Point
+ var d = L.LatLng.DEG_TO_RAD,
+ max = this.MAX_LATITUDE,
+ lat = Math.max(Math.min(max, latlng.lat), -max),
+ r = this.R_MAJOR,
+ r2 = this.R_MINOR,
+ x = latlng.lng * d * r,
+ y = lat * d,
+ tmp = r2 / r,
+ eccent = Math.sqrt(1.0 - tmp * tmp),
+ con = eccent * Math.sin(y);
+
+ con = Math.pow((1 - con) / (1 + con), eccent * 0.5);
+
+ var ts = Math.tan(0.5 * ((Math.PI * 0.5) - y)) / con;
+ y = -r * Math.log(ts);
+
+ return new L.Point(x, y);
+ },
+
+ unproject: function (point) { // (Point, Boolean) -> LatLng
+ var d = L.LatLng.RAD_TO_DEG,
+ r = this.R_MAJOR,
+ r2 = this.R_MINOR,
+ lng = point.x * d / r,
+ tmp = r2 / r,
+ eccent = Math.sqrt(1 - (tmp * tmp)),
+ ts = Math.exp(- point.y / r),
+ phi = (Math.PI / 2) - 2 * Math.atan(ts),
+ numIter = 15,
+ tol = 1e-7,
+ i = numIter,
+ dphi = 0.1,
+ con;
+
+ while ((Math.abs(dphi) > tol) && (--i > 0)) {
+ con = eccent * Math.sin(phi);
+ dphi = (Math.PI / 2) - 2 * Math.atan(ts *
+ Math.pow((1.0 - con) / (1.0 + con), 0.5 * eccent)) - phi;
+ phi += dphi;
+ }
+
+ return new L.LatLng(phi * d, lng);
+ }
+};
+
+
+
+L.CRS.EPSG3395 = L.extend({}, L.CRS, {
+ code: 'EPSG:3395',
+
+ projection: L.Projection.Mercator,
+
+ transformation: (function () {
+ var m = L.Projection.Mercator,
+ r = m.R_MAJOR,
+ scale = 0.5 / (Math.PI * r);
+
+ return new L.Transformation(scale, 0.5, -scale, 0.5);
+ }())
+});
+
+
+/*
+ * L.TileLayer is used for standard xyz-numbered tile layers.
+ */
+
+L.TileLayer = L.Class.extend({
+ includes: L.Mixin.Events,
+
+ options: {
+ minZoom: 0,
+ maxZoom: 18,
+ tileSize: 256,
+ subdomains: 'abc',
+ errorTileUrl: '',
+ attribution: '',
+ zoomOffset: 0,
+ opacity: 1,
+ /*
+ maxNativeZoom: null,
+ zIndex: null,
+ tms: false,
+ continuousWorld: false,
+ noWrap: false,
+ zoomReverse: false,
+ detectRetina: false,
+ reuseTiles: false,
+ bounds: false,
+ */
+ unloadInvisibleTiles: L.Browser.mobile,
+ updateWhenIdle: L.Browser.mobile
+ },
+
+ initialize: function (url, options) {
+ options = L.setOptions(this, options);
+
+ // detecting retina displays, adjusting tileSize and zoom levels
+ if (options.detectRetina && L.Browser.retina && options.maxZoom > 0) {
+
+ options.tileSize = Math.floor(options.tileSize / 2);
+ options.zoomOffset++;
+
+ if (options.minZoom > 0) {
+ options.minZoom--;
+ }
+ this.options.maxZoom--;
+ }
+
+ if (options.bounds) {
+ options.bounds = L.latLngBounds(options.bounds);
+ }
+
+ this._url = url;
+
+ var subdomains = this.options.subdomains;
+
+ if (typeof subdomains === 'string') {
+ this.options.subdomains = subdomains.split('');
+ }
+ },
+
+ onAdd: function (map) {
+ this._map = map;
+ this._animated = map._zoomAnimated;
+
+ // create a container div for tiles
+ this._initContainer();
+
+ // set up events
+ map.on({
+ 'viewreset': this._reset,
+ 'moveend': this._update
+ }, this);
+
+ if (this._animated) {
+ map.on({
+ 'zoomanim': this._animateZoom,
+ 'zoomend': this._endZoomAnim
+ }, this);
+ }
+
+ if (!this.options.updateWhenIdle) {
+ this._limitedUpdate = L.Util.limitExecByInterval(this._update, 150, this);
+ map.on('move', this._limitedUpdate, this);
+ }
+
+ this._reset();
+ this._update();
+ },
+
+ addTo: function (map) {
+ map.addLayer(this);
+ return this;
+ },
+
+ onRemove: function (map) {
+ this._container.parentNode.removeChild(this._container);
+
+ map.off({
+ 'viewreset': this._reset,
+ 'moveend': this._update
+ }, this);
+
+ if (this._animated) {
+ map.off({
+ 'zoomanim': this._animateZoom,
+ 'zoomend': this._endZoomAnim
+ }, this);
+ }
+
+ if (!this.options.updateWhenIdle) {
+ map.off('move', this._limitedUpdate, this);
+ }
+
+ this._container = null;
+ this._map = null;
+ },
+
+ bringToFront: function () {
+ var pane = this._map._panes.tilePane;
+
+ if (this._container) {
+ pane.appendChild(this._container);
+ this._setAutoZIndex(pane, Math.max);
+ }
+
+ return this;
+ },
+
+ bringToBack: function () {
+ var pane = this._map._panes.tilePane;
+
+ if (this._container) {
+ pane.insertBefore(this._container, pane.firstChild);
+ this._setAutoZIndex(pane, Math.min);
+ }
+
+ return this;
+ },
+
+ getAttribution: function () {
+ return this.options.attribution;
+ },
+
+ getContainer: function () {
+ return this._container;
+ },
+
+ setOpacity: function (opacity) {
+ this.options.opacity = opacity;
+
+ if (this._map) {
+ this._updateOpacity();
+ }
+
+ return this;
+ },
+
+ setZIndex: function (zIndex) {
+ this.options.zIndex = zIndex;
+ this._updateZIndex();
+
+ return this;
+ },
+
+ setUrl: function (url, noRedraw) {
+ this._url = url;
+
+ if (!noRedraw) {
+ this.redraw();
+ }
+
+ return this;
+ },
+
+ redraw: function () {
+ if (this._map) {
+ this._reset({hard: true});
+ this._update();
+ }
+ return this;
+ },
+
+ _updateZIndex: function () {
+ if (this._container && this.options.zIndex !== undefined) {
+ this._container.style.zIndex = this.options.zIndex;
+ }
+ },
+
+ _setAutoZIndex: function (pane, compare) {
+
+ var layers = pane.children,
+ edgeZIndex = -compare(Infinity, -Infinity), // -Infinity for max, Infinity for min
+ zIndex, i, len;
+
+ for (i = 0, len = layers.length; i < len; i++) {
+
+ if (layers[i] !== this._container) {
+ zIndex = parseInt(layers[i].style.zIndex, 10);
+
+ if (!isNaN(zIndex)) {
+ edgeZIndex = compare(edgeZIndex, zIndex);
+ }
+ }
+ }
+
+ this.options.zIndex = this._container.style.zIndex =
+ (isFinite(edgeZIndex) ? edgeZIndex : 0) + compare(1, -1);
+ },
+
+ _updateOpacity: function () {
+ var i,
+ tiles = this._tiles;
+
+ if (L.Browser.ielt9) {
+ for (i in tiles) {
+ L.DomUtil.setOpacity(tiles[i], this.options.opacity);
+ }
+ } else {
+ L.DomUtil.setOpacity(this._container, this.options.opacity);
+ }
+ },
+
+ _initContainer: function () {
+ var tilePane = this._map._panes.tilePane;
+
+ if (!this._container) {
+ this._container = L.DomUtil.create('div', 'leaflet-layer');
+
+ this._updateZIndex();
+
+ if (this._animated) {
+ var className = 'leaflet-tile-container';
+
+ this._bgBuffer = L.DomUtil.create('div', className, this._container);
+ this._tileContainer = L.DomUtil.create('div', className, this._container);
+
+ } else {
+ this._tileContainer = this._container;
+ }
+
+ tilePane.appendChild(this._container);
+
+ if (this.options.opacity < 1) {
+ this._updateOpacity();
+ }
+ }
+ },
+
+ _reset: function (e) {
+ for (var key in this._tiles) {
+ this.fire('tileunload', {tile: this._tiles[key]});
+ }
+
+ this._tiles = {};
+ this._tilesToLoad = 0;
+
+ if (this.options.reuseTiles) {
+ this._unusedTiles = [];
+ }
+
+ this._tileContainer.innerHTML = '';
+
+ if (this._animated && e && e.hard) {
+ this._clearBgBuffer();
+ }
+
+ this._initContainer();
+ },
+
+ _getTileSize: function () {
+ var map = this._map,
+ zoom = map.getZoom() + this.options.zoomOffset,
+ zoomN = this.options.maxNativeZoom,
+ tileSize = this.options.tileSize;
+
+ if (zoomN && zoom > zoomN) {
+ tileSize = Math.round(map.getZoomScale(zoom) / map.getZoomScale(zoomN) * tileSize);
+ }
+
+ return tileSize;
+ },
+
+ _update: function () {
+
+ if (!this._map) { return; }
+
+ var map = this._map,
+ bounds = map.getPixelBounds(),
+ zoom = map.getZoom(),
+ tileSize = this._getTileSize();
+
+ if (zoom > this.options.maxZoom || zoom < this.options.minZoom) {
+ return;
+ }
+
+ var tileBounds = L.bounds(
+ bounds.min.divideBy(tileSize)._floor(),
+ bounds.max.divideBy(tileSize)._floor());
+
+ this._addTilesFromCenterOut(tileBounds);
+
+ if (this.options.unloadInvisibleTiles || this.options.reuseTiles) {
+ this._removeOtherTiles(tileBounds);
+ }
+ },
+
+ _addTilesFromCenterOut: function (bounds) {
+ var queue = [],
+ center = bounds.getCenter();
+
+ var j, i, point;
+
+ for (j = bounds.min.y; j <= bounds.max.y; j++) {
+ for (i = bounds.min.x; i <= bounds.max.x; i++) {
+ point = new L.Point(i, j);
+
+ if (this._tileShouldBeLoaded(point)) {
+ queue.push(point);
+ }
+ }
+ }
+
+ var tilesToLoad = queue.length;
+
+ if (tilesToLoad === 0) { return; }
+
+ // load tiles in order of their distance to center
+ queue.sort(function (a, b) {
+ return a.distanceTo(center) - b.distanceTo(center);
+ });
+
+ var fragment = document.createDocumentFragment();
+
+ // if its the first batch of tiles to load
+ if (!this._tilesToLoad) {
+ this.fire('loading');
+ }
+
+ this._tilesToLoad += tilesToLoad;
+
+ for (i = 0; i < tilesToLoad; i++) {
+ this._addTile(queue[i], fragment);
+ }
+
+ this._tileContainer.appendChild(fragment);
+ },
+
+ _tileShouldBeLoaded: function (tilePoint) {
+ if ((tilePoint.x + ':' + tilePoint.y) in this._tiles) {
+ return false; // already loaded
+ }
+
+ var options = this.options;
+
+ if (!options.continuousWorld) {
+ var limit = this._getWrapTileNum();
+
+ // don't load if exceeds world bounds
+ if ((options.noWrap && (tilePoint.x < 0 || tilePoint.x >= limit.x)) ||
+ tilePoint.y < 0 || tilePoint.y >= limit.y) { return false; }
+ }
+
+ if (options.bounds) {
+ var tileSize = options.tileSize,
+ nwPoint = tilePoint.multiplyBy(tileSize),
+ sePoint = nwPoint.add([tileSize, tileSize]),
+ nw = this._map.unproject(nwPoint),
+ se = this._map.unproject(sePoint);
+
+ // TODO temporary hack, will be removed after refactoring projections
+ // https://github.com/Leaflet/Leaflet/issues/1618
+ if (!options.continuousWorld && !options.noWrap) {
+ nw = nw.wrap();
+ se = se.wrap();
+ }
+
+ if (!options.bounds.intersects([nw, se])) { return false; }
+ }
+
+ return true;
+ },
+
+ _removeOtherTiles: function (bounds) {
+ var kArr, x, y, key;
+
+ for (key in this._tiles) {
+ kArr = key.split(':');
+ x = parseInt(kArr[0], 10);
+ y = parseInt(kArr[1], 10);
+
+ // remove tile if it's out of bounds
+ if (x < bounds.min.x || x > bounds.max.x || y < bounds.min.y || y > bounds.max.y) {
+ this._removeTile(key);
+ }
+ }
+ },
+
+ _removeTile: function (key) {
+ var tile = this._tiles[key];
+
+ this.fire('tileunload', {tile: tile, url: tile.src});
+
+ if (this.options.reuseTiles) {
+ L.DomUtil.removeClass(tile, 'leaflet-tile-loaded');
+ this._unusedTiles.push(tile);
+
+ } else if (tile.parentNode === this._tileContainer) {
+ this._tileContainer.removeChild(tile);
+ }
+
+ // for https://github.com/CloudMade/Leaflet/issues/137
+ if (!L.Browser.android) {
+ tile.onload = null;
+ tile.src = L.Util.emptyImageUrl;
+ }
+
+ delete this._tiles[key];
+ },
+
+ _addTile: function (tilePoint, container) {
+ var tilePos = this._getTilePos(tilePoint);
+
+ // get unused tile - or create a new tile
+ var tile = this._getTile();
+
+ /*
+ Chrome 20 layouts much faster with top/left (verify with timeline, frames)
+ Android 4 browser has display issues with top/left and requires transform instead
+ (other browsers don't currently care) - see debug/hacks/jitter.html for an example
+ */
+ L.DomUtil.setPosition(tile, tilePos, L.Browser.chrome);
+
+ this._tiles[tilePoint.x + ':' + tilePoint.y] = tile;
+
+ this._loadTile(tile, tilePoint);
+
+ if (tile.parentNode !== this._tileContainer) {
+ container.appendChild(tile);
+ }
+ },
+
+ _getZoomForUrl: function () {
+
+ var options = this.options,
+ zoom = this._map.getZoom();
+
+ if (options.zoomReverse) {
+ zoom = options.maxZoom - zoom;
+ }
+
+ zoom += options.zoomOffset;
+
+ return options.maxNativeZoom ? Math.min(zoom, options.maxNativeZoom) : zoom;
+ },
+
+ _getTilePos: function (tilePoint) {
+ var origin = this._map.getPixelOrigin(),
+ tileSize = this._getTileSize();
+
+ return tilePoint.multiplyBy(tileSize).subtract(origin);
+ },
+
+ // image-specific code (override to implement e.g. Canvas or SVG tile layer)
+
+ getTileUrl: function (tilePoint) {
+ return L.Util.template(this._url, L.extend({
+ s: this._getSubdomain(tilePoint),
+ z: tilePoint.z,
+ x: tilePoint.x,
+ y: tilePoint.y
+ }, this.options));
+ },
+
+ _getWrapTileNum: function () {
+ var crs = this._map.options.crs,
+ size = crs.getSize(this._map.getZoom());
+ return size.divideBy(this._getTileSize())._floor();
+ },
+
+ _adjustTilePoint: function (tilePoint) {
+
+ var limit = this._getWrapTileNum();
+
+ // wrap tile coordinates
+ if (!this.options.continuousWorld && !this.options.noWrap) {
+ tilePoint.x = ((tilePoint.x % limit.x) + limit.x) % limit.x;
+ }
+
+ if (this.options.tms) {
+ tilePoint.y = limit.y - tilePoint.y - 1;
+ }
+
+ tilePoint.z = this._getZoomForUrl();
+ },
+
+ _getSubdomain: function (tilePoint) {
+ var index = Math.abs(tilePoint.x + tilePoint.y) % this.options.subdomains.length;
+ return this.options.subdomains[index];
+ },
+
+ _getTile: function () {
+ if (this.options.reuseTiles && this._unusedTiles.length > 0) {
+ var tile = this._unusedTiles.pop();
+ this._resetTile(tile);
+ return tile;
+ }
+ return this._createTile();
+ },
+
+ // Override if data stored on a tile needs to be cleaned up before reuse
+ _resetTile: function (/*tile*/) {},
+
+ _createTile: function () {
+ var tile = L.DomUtil.create('img', 'leaflet-tile');
+ tile.style.width = tile.style.height = this._getTileSize() + 'px';
+ tile.galleryimg = 'no';
+
+ tile.onselectstart = tile.onmousemove = L.Util.falseFn;
+
+ if (L.Browser.ielt9 && this.options.opacity !== undefined) {
+ L.DomUtil.setOpacity(tile, this.options.opacity);
+ }
+ // without this hack, tiles disappear after zoom on Chrome for Android
+ // https://github.com/Leaflet/Leaflet/issues/2078
+ if (L.Browser.mobileWebkit3d) {
+ tile.style.WebkitBackfaceVisibility = 'hidden';
+ }
+ return tile;
+ },
+
+ _loadTile: function (tile, tilePoint) {
+ tile._layer = this;
+ tile.onload = this._tileOnLoad;
+ tile.onerror = this._tileOnError;
+
+ this._adjustTilePoint(tilePoint);
+ tile.src = this.getTileUrl(tilePoint);
+
+ this.fire('tileloadstart', {
+ tile: tile,
+ url: tile.src
+ });
+ },
+
+ _tileLoaded: function () {
+ this._tilesToLoad--;
+
+ if (this._animated) {
+ L.DomUtil.addClass(this._tileContainer, 'leaflet-zoom-animated');
+ }
+
+ if (!this._tilesToLoad) {
+ this.fire('load');
+
+ if (this._animated) {
+ // clear scaled tiles after all new tiles are loaded (for performance)
+ clearTimeout(this._clearBgBufferTimer);
+ this._clearBgBufferTimer = setTimeout(L.bind(this._clearBgBuffer, this), 500);
+ }
+ }
+ },
+
+ _tileOnLoad: function () {
+ var layer = this._layer;
+
+ //Only if we are loading an actual image
+ if (this.src !== L.Util.emptyImageUrl) {
+ L.DomUtil.addClass(this, 'leaflet-tile-loaded');
+
+ layer.fire('tileload', {
+ tile: this,
+ url: this.src
+ });
+ }
+
+ layer._tileLoaded();
+ },
+
+ _tileOnError: function () {
+ var layer = this._layer;
+
+ layer.fire('tileerror', {
+ tile: this,
+ url: this.src
+ });
+
+ var newUrl = layer.options.errorTileUrl;
+ if (newUrl) {
+ this.src = newUrl;
+ }
+
+ layer._tileLoaded();
+ }
+});
+
+L.tileLayer = function (url, options) {
+ return new L.TileLayer(url, options);
+};
+
+
+/*
+ * L.TileLayer.WMS is used for putting WMS tile layers on the map.
+ */
+
+L.TileLayer.WMS = L.TileLayer.extend({
+
+ defaultWmsParams: {
+ service: 'WMS',
+ request: 'GetMap',
+ version: '1.1.1',
+ layers: '',
+ styles: '',
+ format: 'image/jpeg',
+ transparent: false
+ },
+
+ initialize: function (url, options) { // (String, Object)
+
+ this._url = url;
+
+ var wmsParams = L.extend({}, this.defaultWmsParams),
+ tileSize = options.tileSize || this.options.tileSize;
+
+ if (options.detectRetina && L.Browser.retina) {
+ wmsParams.width = wmsParams.height = tileSize * 2;
+ } else {
+ wmsParams.width = wmsParams.height = tileSize;
+ }
+
+ for (var i in options) {
+ // all keys that are not TileLayer options go to WMS params
+ if (!this.options.hasOwnProperty(i) && i !== 'crs') {
+ wmsParams[i] = options[i];
+ }
+ }
+
+ this.wmsParams = wmsParams;
+
+ L.setOptions(this, options);
+ },
+
+ onAdd: function (map) {
+
+ this._crs = this.options.crs || map.options.crs;
+
+ this._wmsVersion = parseFloat(this.wmsParams.version);
+
+ var projectionKey = this._wmsVersion >= 1.3 ? 'crs' : 'srs';
+ this.wmsParams[projectionKey] = this._crs.code;
+
+ L.TileLayer.prototype.onAdd.call(this, map);
+ },
+
+ getTileUrl: function (tilePoint) { // (Point, Number) -> String
+
+ var map = this._map,
+ tileSize = this.options.tileSize,
+
+ nwPoint = tilePoint.multiplyBy(tileSize),
+ sePoint = nwPoint.add([tileSize, tileSize]),
+
+ nw = this._crs.project(map.unproject(nwPoint, tilePoint.z)),
+ se = this._crs.project(map.unproject(sePoint, tilePoint.z)),
+ bbox = this._wmsVersion >= 1.3 && this._crs === L.CRS.EPSG4326 ?
+ [se.y, nw.x, nw.y, se.x].join(',') :
+ [nw.x, se.y, se.x, nw.y].join(','),
+
+ url = L.Util.template(this._url, {s: this._getSubdomain(tilePoint)});
+
+ return url + L.Util.getParamString(this.wmsParams, url, true) + '&BBOX=' + bbox;
+ },
+
+ setParams: function (params, noRedraw) {
+
+ L.extend(this.wmsParams, params);
+
+ if (!noRedraw) {
+ this.redraw();
+ }
+
+ return this;
+ }
+});
+
+L.tileLayer.wms = function (url, options) {
+ return new L.TileLayer.WMS(url, options);
+};
+
+
+/*
+ * L.TileLayer.Canvas is a class that you can use as a base for creating
+ * dynamically drawn Canvas-based tile layers.
+ */
+
+L.TileLayer.Canvas = L.TileLayer.extend({
+ options: {
+ async: false
+ },
+
+ initialize: function (options) {
+ L.setOptions(this, options);
+ },
+
+ redraw: function () {
+ if (this._map) {
+ this._reset({hard: true});
+ this._update();
+ }
+
+ for (var i in this._tiles) {
+ this._redrawTile(this._tiles[i]);
+ }
+ return this;
+ },
+
+ _redrawTile: function (tile) {
+ this.drawTile(tile, tile._tilePoint, this._map._zoom);
+ },
+
+ _createTile: function () {
+ var tile = L.DomUtil.create('canvas', 'leaflet-tile');
+ tile.width = tile.height = this.options.tileSize;
+ tile.onselectstart = tile.onmousemove = L.Util.falseFn;
+ return tile;
+ },
+
+ _loadTile: function (tile, tilePoint) {
+ tile._layer = this;
+ tile._tilePoint = tilePoint;
+
+ this._redrawTile(tile);
+
+ if (!this.options.async) {
+ this.tileDrawn(tile);
+ }
+ },
+
+ drawTile: function (/*tile, tilePoint*/) {
+ // override with rendering code
+ },
+
+ tileDrawn: function (tile) {
+ this._tileOnLoad.call(tile);
+ }
+});
+
+
+L.tileLayer.canvas = function (options) {
+ return new L.TileLayer.Canvas(options);
+};
+
+
+/*
+ * L.ImageOverlay is used to overlay images over the map (to specific geographical bounds).
+ */
+
+L.ImageOverlay = L.Class.extend({
+ includes: L.Mixin.Events,
+
+ options: {
+ opacity: 1
+ },
+
+ initialize: function (url, bounds, options) { // (String, LatLngBounds, Object)
+ this._url = url;
+ this._bounds = L.latLngBounds(bounds);
+
+ L.setOptions(this, options);
+ },
+
+ onAdd: function (map) {
+ this._map = map;
+
+ if (!this._image) {
+ this._initImage();
+ }
+
+ map._panes.overlayPane.appendChild(this._image);
+
+ map.on('viewreset', this._reset, this);
+
+ if (map.options.zoomAnimation && L.Browser.any3d) {
+ map.on('zoomanim', this._animateZoom, this);
+ }
+
+ this._reset();
+ },
+
+ onRemove: function (map) {
+ map.getPanes().overlayPane.removeChild(this._image);
+
+ map.off('viewreset', this._reset, this);
+
+ if (map.options.zoomAnimation) {
+ map.off('zoomanim', this._animateZoom, this);
+ }
+ },
+
+ addTo: function (map) {
+ map.addLayer(this);
+ return this;
+ },
+
+ setOpacity: function (opacity) {
+ this.options.opacity = opacity;
+ this._updateOpacity();
+ return this;
+ },
+
+ // TODO remove bringToFront/bringToBack duplication from TileLayer/Path
+ bringToFront: function () {
+ if (this._image) {
+ this._map._panes.overlayPane.appendChild(this._image);
+ }
+ return this;
+ },
+
+ bringToBack: function () {
+ var pane = this._map._panes.overlayPane;
+ if (this._image) {
+ pane.insertBefore(this._image, pane.firstChild);
+ }
+ return this;
+ },
+
+ setUrl: function (url) {
+ this._url = url;
+ this._image.src = this._url;
+ },
+
+ getAttribution: function () {
+ return this.options.attribution;
+ },
+
+ _initImage: function () {
+ this._image = L.DomUtil.create('img', 'leaflet-image-layer');
+
+ if (this._map.options.zoomAnimation && L.Browser.any3d) {
+ L.DomUtil.addClass(this._image, 'leaflet-zoom-animated');
+ } else {
+ L.DomUtil.addClass(this._image, 'leaflet-zoom-hide');
+ }
+
+ this._updateOpacity();
+
+ //TODO createImage util method to remove duplication
+ L.extend(this._image, {
+ galleryimg: 'no',
+ onselectstart: L.Util.falseFn,
+ onmousemove: L.Util.falseFn,
+ onload: L.bind(this._onImageLoad, this),
+ src: this._url
+ });
+ },
+
+ _animateZoom: function (e) {
+ var map = this._map,
+ image = this._image,
+ scale = map.getZoomScale(e.zoom),
+ nw = this._bounds.getNorthWest(),
+ se = this._bounds.getSouthEast(),
+
+ topLeft = map._latLngToNewLayerPoint(nw, e.zoom, e.center),
+ size = map._latLngToNewLayerPoint(se, e.zoom, e.center)._subtract(topLeft),
+ origin = topLeft._add(size._multiplyBy((1 / 2) * (1 - 1 / scale)));
+
+ image.style[L.DomUtil.TRANSFORM] =
+ L.DomUtil.getTranslateString(origin) + ' scale(' + scale + ') ';
+ },
+
+ _reset: function () {
+ var image = this._image,
+ topLeft = this._map.latLngToLayerPoint(this._bounds.getNorthWest()),
+ size = this._map.latLngToLayerPoint(this._bounds.getSouthEast())._subtract(topLeft);
+
+ L.DomUtil.setPosition(image, topLeft);
+
+ image.style.width = size.x + 'px';
+ image.style.height = size.y + 'px';
+ },
+
+ _onImageLoad: function () {
+ this.fire('load');
+ },
+
+ _updateOpacity: function () {
+ L.DomUtil.setOpacity(this._image, this.options.opacity);
+ }
+});
+
+L.imageOverlay = function (url, bounds, options) {
+ return new L.ImageOverlay(url, bounds, options);
+};
+
+
+/*
+ * L.Icon is an image-based icon class that you can use with L.Marker for custom markers.
+ */
+
+L.Icon = L.Class.extend({
+ options: {
+ /*
+ iconUrl: (String) (required)
+ iconRetinaUrl: (String) (optional, used for retina devices if detected)
+ iconSize: (Point) (can be set through CSS)
+ iconAnchor: (Point) (centered by default, can be set in CSS with negative margins)
+ popupAnchor: (Point) (if not specified, popup opens in the anchor point)
+ shadowUrl: (String) (no shadow by default)
+ shadowRetinaUrl: (String) (optional, used for retina devices if detected)
+ shadowSize: (Point)
+ shadowAnchor: (Point)
+ */
+ className: ''
+ },
+
+ initialize: function (options) {
+ L.setOptions(this, options);
+ },
+
+ createIcon: function (oldIcon) {
+ return this._createIcon('icon', oldIcon);
+ },
+
+ 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;
+ if (!oldIcon || oldIcon.tagName !== 'IMG') {
+ img = this._createImg(src);
+ } else {
+ img = this._createImg(src, oldIcon);
+ }
+ this._setIconStyles(img, name);
+
+ return img;
+ },
+
+ _setIconStyles: function (img, name) {
+ var options = this.options,
+ size = L.point(options[name + 'Size']),
+ anchor;
+
+ if (name === 'shadow') {
+ anchor = L.point(options.shadowAnchor || options.iconAnchor);
+ } else {
+ anchor = L.point(options.iconAnchor);
+ }
+
+ if (!anchor && size) {
+ anchor = 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) {
+ if (L.Browser.retina && this.options[name + 'RetinaUrl']) {
+ return this.options[name + 'RetinaUrl'];
+ }
+ return this.options[name + 'Url'];
+ }
+});
+
+L.icon = function (options) {
+ return new L.Icon(options);
+};
+
+
+/*
+ * L.Icon.Default is the blue marker icon used by default in Leaflet.
+ */
+
+L.Icon.Default = L.Icon.extend({
+
+ options: {
+ iconSize: [25, 41],
+ iconAnchor: [12, 41],
+ popupAnchor: [1, -34],
+
+ shadowSize: [41, 41]
+ },
+
+ _getIconUrl: function (name) {
+ var key = name + 'Url';
+
+ if (this.options[key]) {
+ return this.options[key];
+ }
+
+ if (L.Browser.retina && name === 'icon') {
+ name += '-2x';
+ }
+
+ var path = L.Icon.Default.imagePath;
+
+ if (!path) {
+ throw new Error('Couldn\'t autodetect L.Icon.Default.imagePath, set it manually.');
+ }
+
+ return path + '/marker-' + name + '.png';
+ }
+});
+
+L.Icon.Default.imagePath = (function () {
+ var scripts = document.getElementsByTagName('script'),
+ leafletRe = /[\/^]leaflet[\-\._]?([\w\-\._]*)\.js\??/;
+
+ var i, len, src, matches, path;
+
+ for (i = 0, len = scripts.length; i < len; i++) {
+ src = scripts[i].src;
+ matches = src.match(leafletRe);
+
+ if (matches) {
+ path = src.split(leafletRe)[0];
+ return (path ? path + '/' : '') + 'images';
+ }
+ }
+}());
+
+
+/*
+ * L.Marker is used to display clickable/draggable icons on the map.
+ */
+
+L.Marker = L.Class.extend({
+
+ includes: L.Mixin.Events,
+
+ options: {
+ icon: new L.Icon.Default(),
+ title: '',
+ alt: '',
+ clickable: true,
+ draggable: false,
+ keyboard: true,
+ zIndexOffset: 0,
+ opacity: 1,
+ riseOnHover: false,
+ riseOffset: 250
+ },
+
+ initialize: function (latlng, options) {
+ L.setOptions(this, options);
+ this._latlng = L.latLng(latlng);
+ },
+
+ onAdd: function (map) {
+ this._map = map;
+
+ map.on('viewreset', this.update, this);
+
+ this._initIcon();
+ this.update();
+ this.fire('add');
+
+ if (map.options.zoomAnimation && map.options.markerZoomAnimation) {
+ map.on('zoomanim', this._animateZoom, this);
+ }
+ },
+
+ addTo: function (map) {
+ map.addLayer(this);
+ return this;
+ },
+
+ onRemove: function (map) {
+ if (this.dragging) {
+ this.dragging.disable();
+ }
+
+ this._removeIcon();
+ this._removeShadow();
+
+ this.fire('remove');
+
+ map.off({
+ 'viewreset': this.update,
+ 'zoomanim': this._animateZoom
+ }, this);
+
+ this._map = null;
+ },
+
+ getLatLng: function () {
+ return this._latlng;
+ },
+
+ setLatLng: function (latlng) {
+ this._latlng = L.latLng(latlng);
+
+ this.update();
+
+ return this.fire('move', { latlng: this._latlng });
+ },
+
+ setZIndexOffset: function (offset) {
+ this.options.zIndexOffset = offset;
+ this.update();
+
+ return this;
+ },
+
+ setIcon: function (icon) {
+
+ this.options.icon = icon;
+
+ if (this._map) {
+ this._initIcon();
+ this.update();
+ }
+
+ if (this._popup) {
+ this.bindPopup(this._popup);
+ }
+
+ return this;
+ },
+
+ update: function () {
+ if (this._icon) {
+ var pos = this._map.latLngToLayerPoint(this._latlng).round();
+ this._setPos(pos);
+ }
+
+ return this;
+ },
+
+ _initIcon: function () {
+ var options = this.options,
+ map = this._map,
+ animation = (map.options.zoomAnimation && map.options.markerZoomAnimation),
+ classToAdd = animation ? 'leaflet-zoom-animated' : 'leaflet-zoom-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;
+ }
+ }
+
+ L.DomUtil.addClass(icon, classToAdd);
+
+ if (options.keyboard) {
+ icon.tabIndex = '0';
+ }
+
+ this._icon = icon;
+
+ this._initInteraction();
+
+ if (options.riseOnHover) {
+ L.DomEvent
+ .on(icon, 'mouseover', this._bringToFront, this)
+ .on(icon, 'mouseout', this._resetZIndex, this);
+ }
+
+ var newShadow = options.icon.createShadow(this._shadow),
+ addShadow = false;
+
+ if (newShadow !== this._shadow) {
+ this._removeShadow();
+ addShadow = true;
+ }
+
+ if (newShadow) {
+ L.DomUtil.addClass(newShadow, classToAdd);
+ }
+ this._shadow = newShadow;
+
+
+ if (options.opacity < 1) {
+ this._updateOpacity();
+ }
+
+
+ var panes = this._map._panes;
+
+ if (addIcon) {
+ panes.markerPane.appendChild(this._icon);
+ }
+
+ if (newShadow && addShadow) {
+ panes.shadowPane.appendChild(this._shadow);
+ }
+ },
+
+ _removeIcon: function () {
+ if (this.options.riseOnHover) {
+ L.DomEvent
+ .off(this._icon, 'mouseover', this._bringToFront)
+ .off(this._icon, 'mouseout', this._resetZIndex);
+ }
+
+ this._map._panes.markerPane.removeChild(this._icon);
+
+ this._icon = null;
+ },
+
+ _removeShadow: function () {
+ if (this._shadow) {
+ this._map._panes.shadowPane.removeChild(this._shadow);
+ }
+ this._shadow = null;
+ },
+
+ _setPos: function (pos) {
+ L.DomUtil.setPosition(this._icon, pos);
+
+ if (this._shadow) {
+ L.DomUtil.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.clickable) { return; }
+
+ // TODO refactor into something shared with Map/Path/etc. to DRY it up
+
+ var icon = this._icon,
+ events = ['dblclick', 'mousedown', 'mouseover', 'mouseout', 'contextmenu'];
+
+ L.DomUtil.addClass(icon, 'leaflet-clickable');
+ L.DomEvent.on(icon, 'click', this._onMouseClick, this);
+ L.DomEvent.on(icon, 'keypress', this._onKeyPress, this);
+
+ for (var i = 0; i < events.length; i++) {
+ L.DomEvent.on(icon, events[i], this._fireMouseEvent, this);
+ }
+
+ if (L.Handler.MarkerDrag) {
+ this.dragging = new L.Handler.MarkerDrag(this);
+
+ if (this.options.draggable) {
+ this.dragging.enable();
+ }
+ }
+ },
+
+ _onMouseClick: function (e) {
+ var wasDragged = this.dragging && this.dragging.moved();
+
+ if (this.hasEventListeners(e.type) || wasDragged) {
+ L.DomEvent.stopPropagation(e);
+ }
+
+ if (wasDragged) { return; }
+
+ if ((!this.dragging || !this.dragging._enabled) && this._map.dragging && this._map.dragging.moved()) { return; }
+
+ this.fire(e.type, {
+ originalEvent: e,
+ latlng: this._latlng
+ });
+ },
+
+ _onKeyPress: function (e) {
+ if (e.keyCode === 13) {
+ this.fire('click', {
+ originalEvent: e,
+ latlng: this._latlng
+ });
+ }
+ },
+
+ _fireMouseEvent: function (e) {
+
+ this.fire(e.type, {
+ originalEvent: e,
+ latlng: this._latlng
+ });
+
+ // TODO proper custom event propagation
+ // this line will always be called if marker is in a FeatureGroup
+ if (e.type === 'contextmenu' && this.hasEventListeners(e.type)) {
+ L.DomEvent.preventDefault(e);
+ }
+ if (e.type !== 'mousedown') {
+ L.DomEvent.stopPropagation(e);
+ } else {
+ L.DomEvent.preventDefault(e);
+ }
+ },
+
+ setOpacity: function (opacity) {
+ this.options.opacity = opacity;
+ if (this._map) {
+ this._updateOpacity();
+ }
+
+ return this;
+ },
+
+ _updateOpacity: function () {
+ L.DomUtil.setOpacity(this._icon, this.options.opacity);
+ if (this._shadow) {
+ L.DomUtil.setOpacity(this._shadow, this.options.opacity);
+ }
+ },
+
+ _bringToFront: function () {
+ this._updateZIndex(this.options.riseOffset);
+ },
+
+ _resetZIndex: function () {
+ this._updateZIndex(0);
+ }
+});
+
+L.marker = function (latlng, options) {
+ return new L.Marker(latlng, options);
+};
+
+
+/*
+ * L.DivIcon is a lightweight HTML-based icon class (as opposed to the image-based L.Icon)
+ * to use with L.Marker.
+ */
+
+L.DivIcon = L.Icon.extend({
+ options: {
+ iconSize: [12, 12], // also can be set through CSS
+ /*
+ iconAnchor: (Point)
+ popupAnchor: (Point)
+ html: (String)
+ bgPos: (Point)
+ */
+ className: 'leaflet-div-icon',
+ html: false
+ },
+
+ createIcon: function (oldIcon) {
+ var div = (oldIcon && oldIcon.tagName === 'DIV') ? oldIcon : document.createElement('div'),
+ options = this.options;
+
+ if (options.html !== false) {
+ div.innerHTML = options.html;
+ } else {
+ div.innerHTML = '';
+ }
+
+ if (options.bgPos) {
+ div.style.backgroundPosition =
+ (-options.bgPos.x) + 'px ' + (-options.bgPos.y) + 'px';
+ }
+
+ this._setIconStyles(div, 'icon');
+ return div;
+ },
+
+ createShadow: function () {
+ return null;
+ }
+});
+
+L.divIcon = function (options) {
+ return new L.DivIcon(options);
+};
+
+
+/*
+ * L.Popup is used for displaying popups on the map.
+ */
+
+L.Map.mergeOptions({
+ closePopupOnClick: true
+});
+
+L.Popup = L.Class.extend({
+ includes: L.Mixin.Events,
+
+ options: {
+ minWidth: 50,
+ maxWidth: 300,
+ // maxHeight: null,
+ autoPan: true,
+ closeButton: true,
+ offset: [0, 7],
+ autoPanPadding: [5, 5],
+ // autoPanPaddingTopLeft: null,
+ // autoPanPaddingBottomRight: null,
+ keepInView: false,
+ className: '',
+ zoomAnimation: true
+ },
+
+ initialize: function (options, source) {
+ L.setOptions(this, options);
+
+ this._source = source;
+ this._animated = L.Browser.any3d && this.options.zoomAnimation;
+ this._isOpen = false;
+ },
+
+ onAdd: function (map) {
+ this._map = map;
+
+ if (!this._container) {
+ this._initLayout();
+ }
+
+ var animFade = map.options.fadeAnimation;
+
+ if (animFade) {
+ L.DomUtil.setOpacity(this._container, 0);
+ }
+ map._panes.popupPane.appendChild(this._container);
+
+ map.on(this._getEvents(), this);
+
+ this.update();
+
+ if (animFade) {
+ L.DomUtil.setOpacity(this._container, 1);
+ }
+
+ this.fire('open');
+
+ map.fire('popupopen', {popup: this});
+
+ if (this._source) {
+ this._source.fire('popupopen', {popup: this});
+ }
+ },
+
+ addTo: function (map) {
+ map.addLayer(this);
+ return this;
+ },
+
+ openOn: function (map) {
+ map.openPopup(this);
+ return this;
+ },
+
+ onRemove: function (map) {
+ map._panes.popupPane.removeChild(this._container);
+
+ L.Util.falseFn(this._container.offsetWidth); // force reflow
+
+ map.off(this._getEvents(), this);
+
+ if (map.options.fadeAnimation) {
+ L.DomUtil.setOpacity(this._container, 0);
+ }
+
+ this._map = null;
+
+ this.fire('close');
+
+ map.fire('popupclose', {popup: this});
+
+ if (this._source) {
+ this._source.fire('popupclose', {popup: this});
+ }
+ },
+
+ getLatLng: function () {
+ return this._latlng;
+ },
+
+ setLatLng: function (latlng) {
+ this._latlng = L.latLng(latlng);
+ if (this._map) {
+ this._updatePosition();
+ this._adjustPan();
+ }
+ return this;
+ },
+
+ getContent: function () {
+ return this._content;
+ },
+
+ setContent: function (content) {
+ this._content = content;
+ this.update();
+ return this;
+ },
+
+ update: function () {
+ if (!this._map) { return; }
+
+ this._container.style.visibility = 'hidden';
+
+ this._updateContent();
+ this._updateLayout();
+ this._updatePosition();
+
+ this._container.style.visibility = '';
+
+ this._adjustPan();
+ },
+
+ _getEvents: function () {
+ var events = {
+ viewreset: this._updatePosition
+ };
+
+ if (this._animated) {
+ events.zoomanim = this._zoomAnimation;
+ }
+ if ('closeOnClick' in this.options ? this.options.closeOnClick : this._map.options.closePopupOnClick) {
+ events.preclick = this._close;
+ }
+ if (this.options.keepInView) {
+ events.moveend = this._adjustPan;
+ }
+
+ return events;
+ },
+
+ _close: function () {
+ if (this._map) {
+ this._map.closePopup(this);
+ }
+ },
+
+ _initLayout: function () {
+ var prefix = 'leaflet-popup',
+ containerClass = prefix + ' ' + this.options.className + ' leaflet-zoom-' +
+ (this._animated ? 'animated' : 'hide'),
+ container = this._container = L.DomUtil.create('div', containerClass),
+ closeButton;
+
+ if (this.options.closeButton) {
+ closeButton = this._closeButton =
+ L.DomUtil.create('a', prefix + '-close-button', container);
+ closeButton.href = '#close';
+ closeButton.innerHTML = '×';
+ L.DomEvent.disableClickPropagation(closeButton);
+
+ L.DomEvent.on(closeButton, 'click', this._onCloseButtonClick, this);
+ }
+
+ var wrapper = this._wrapper =
+ L.DomUtil.create('div', prefix + '-content-wrapper', container);
+ L.DomEvent.disableClickPropagation(wrapper);
+
+ this._contentNode = L.DomUtil.create('div', prefix + '-content', wrapper);
+
+ L.DomEvent.disableScrollPropagation(this._contentNode);
+ L.DomEvent.on(wrapper, 'contextmenu', L.DomEvent.stopPropagation);
+
+ this._tipContainer = L.DomUtil.create('div', prefix + '-tip-container', container);
+ this._tip = L.DomUtil.create('div', prefix + '-tip', this._tipContainer);
+ },
+
+ _updateContent: function () {
+ if (!this._content) { return; }
+
+ if (typeof this._content === 'string') {
+ this._contentNode.innerHTML = this._content;
+ } else {
+ while (this._contentNode.hasChildNodes()) {
+ this._contentNode.removeChild(this._contentNode.firstChild);
+ }
+ this._contentNode.appendChild(this._content);
+ }
+ this.fire('contentupdate');
+ },
+
+ _updateLayout: function () {
+ var container = this._contentNode,
+ style = container.style;
+
+ style.width = '';
+ style.whiteSpace = 'nowrap';
+
+ var width = container.offsetWidth;
+ width = Math.min(width, this.options.maxWidth);
+ width = Math.max(width, this.options.minWidth);
+
+ style.width = (width + 1) + 'px';
+ style.whiteSpace = '';
+
+ style.height = '';
+
+ var height = container.offsetHeight,
+ maxHeight = this.options.maxHeight,
+ scrolledClass = 'leaflet-popup-scrolled';
+
+ if (maxHeight && height > maxHeight) {
+ style.height = maxHeight + 'px';
+ L.DomUtil.addClass(container, scrolledClass);
+ } else {
+ L.DomUtil.removeClass(container, scrolledClass);
+ }
+
+ this._containerWidth = this._container.offsetWidth;
+ },
+
+ _updatePosition: function () {
+ if (!this._map) { return; }
+
+ var pos = this._map.latLngToLayerPoint(this._latlng),
+ animated = this._animated,
+ offset = L.point(this.options.offset);
+
+ if (animated) {
+ L.DomUtil.setPosition(this._container, pos);
+ }
+
+ this._containerBottom = -offset.y - (animated ? 0 : pos.y);
+ this._containerLeft = -Math.round(this._containerWidth / 2) + offset.x + (animated ? 0 : pos.x);
+
+ // bottom position the popup in case the height of the popup changes (images loading etc)
+ this._container.style.bottom = this._containerBottom + 'px';
+ this._container.style.left = this._containerLeft + 'px';
+ },
+
+ _zoomAnimation: function (opt) {
+ var pos = this._map._latLngToNewLayerPoint(this._latlng, opt.zoom, opt.center);
+
+ L.DomUtil.setPosition(this._container, pos);
+ },
+
+ _adjustPan: function () {
+ if (!this.options.autoPan) { return; }
+
+ var map = this._map,
+ containerHeight = this._container.offsetHeight,
+ containerWidth = this._containerWidth,
+
+ layerPos = new L.Point(this._containerLeft, -containerHeight - this._containerBottom);
+
+ if (this._animated) {
+ layerPos._add(L.DomUtil.getPosition(this._container));
+ }
+
+ var containerPos = map.layerPointToContainerPoint(layerPos),
+ padding = L.point(this.options.autoPanPadding),
+ paddingTL = L.point(this.options.autoPanPaddingTopLeft || padding),
+ paddingBR = L.point(this.options.autoPanPaddingBottomRight || padding),
+ size = map.getSize(),
+ dx = 0,
+ dy = 0;
+
+ if (containerPos.x + containerWidth + paddingBR.x > size.x) { // right
+ dx = containerPos.x + containerWidth - size.x + paddingBR.x;
+ }
+ if (containerPos.x - dx - paddingTL.x < 0) { // left
+ dx = containerPos.x - paddingTL.x;
+ }
+ if (containerPos.y + containerHeight + paddingBR.y > size.y) { // bottom
+ dy = containerPos.y + containerHeight - size.y + paddingBR.y;
+ }
+ if (containerPos.y - dy - paddingTL.y < 0) { // top
+ dy = containerPos.y - paddingTL.y;
+ }
+
+ if (dx || dy) {
+ map
+ .fire('autopanstart')
+ .panBy([dx, dy]);
+ }
+ },
+
+ _onCloseButtonClick: function (e) {
+ this._close();
+ L.DomEvent.stop(e);
+ }
+});
+
+L.popup = function (options, source) {
+ return new L.Popup(options, source);
+};
+
+
+L.Map.include({
+ openPopup: function (popup, latlng, options) { // (Popup) or (String || HTMLElement, LatLng[, Object])
+ this.closePopup();
+
+ if (!(popup instanceof L.Popup)) {
+ var content = popup;
+
+ popup = new L.Popup(options)
+ .setLatLng(latlng)
+ .setContent(content);
+ }
+ popup._isOpen = true;
+
+ this._popup = popup;
+ return this.addLayer(popup);
+ },
+
+ closePopup: function (popup) {
+ if (!popup || popup === this._popup) {
+ popup = this._popup;
+ this._popup = null;
+ }
+ if (popup) {
+ this.removeLayer(popup);
+ popup._isOpen = false;
+ }
+ return this;
+ }
+});
+
+
+/*
+ * Popup extension to L.Marker, adding popup-related methods.
+ */
+
+L.Marker.include({
+ openPopup: function () {
+ if (this._popup && this._map && !this._map.hasLayer(this._popup)) {
+ this._popup.setLatLng(this._latlng);
+ this._map.openPopup(this._popup);
+ }
+
+ return this;
+ },
+
+ closePopup: function () {
+ if (this._popup) {
+ this._popup._close();
+ }
+ return this;
+ },
+
+ togglePopup: function () {
+ if (this._popup) {
+ if (this._popup._isOpen) {
+ this.closePopup();
+ } else {
+ this.openPopup();
+ }
+ }
+ return this;
+ },
+
+ bindPopup: function (content, options) {
+ var anchor = L.point(this.options.icon.options.popupAnchor || [0, 0]);
+
+ anchor = anchor.add(L.Popup.prototype.options.offset);
+
+ if (options && options.offset) {
+ anchor = anchor.add(options.offset);
+ }
+
+ options = L.extend({offset: anchor}, options);
+
+ if (!this._popupHandlersAdded) {
+ this
+ .on('click', this.togglePopup, this)
+ .on('remove', this.closePopup, this)
+ .on('move', this._movePopup, this);
+ this._popupHandlersAdded = true;
+ }
+
+ if (content instanceof L.Popup) {
+ L.setOptions(content, options);
+ this._popup = content;
+ } else {
+ this._popup = new L.Popup(options, this)
+ .setContent(content);
+ }
+
+ return this;
+ },
+
+ setPopupContent: function (content) {
+ if (this._popup) {
+ this._popup.setContent(content);
+ }
+ return this;
+ },
+
+ unbindPopup: function () {
+ if (this._popup) {
+ this._popup = null;
+ this
+ .off('click', this.togglePopup, this)
+ .off('remove', this.closePopup, this)
+ .off('move', this._movePopup, this);
+ this._popupHandlersAdded = false;
+ }
+ return this;
+ },
+
+ getPopup: function () {
+ return this._popup;
+ },
+
+ _movePopup: function (e) {
+ this._popup.setLatLng(e.latlng);
+ }
+});
+
+
+/*
+ * L.LayerGroup is a class to combine several layers into one so that
+ * you can manipulate the group (e.g. add/remove it) as one layer.
+ */
+
+L.LayerGroup = L.Class.extend({
+ initialize: function (layers) {
+ this._layers = {};
+
+ var i, len;
+
+ if (layers) {
+ for (i = 0, len = layers.length; i < len; i++) {
+ this.addLayer(layers[i]);
+ }
+ }
+ },
+
+ addLayer: function (layer) {
+ var id = this.getLayerId(layer);
+
+ this._layers[id] = layer;
+
+ if (this._map) {
+ this._map.addLayer(layer);
+ }
+
+ return this;
+ },
+
+ 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;
+ },
+
+ hasLayer: function (layer) {
+ if (!layer) { return false; }
+
+ return (layer in this._layers || this.getLayerId(layer) in this._layers);
+ },
+
+ clearLayers: function () {
+ this.eachLayer(this.removeLayer, this);
+ return this;
+ },
+
+ 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) {
+ this._map = map;
+ this.eachLayer(map.addLayer, map);
+ },
+
+ onRemove: function (map) {
+ this.eachLayer(map.removeLayer, map);
+ this._map = null;
+ },
+
+ addTo: function (map) {
+ map.addLayer(this);
+ return this;
+ },
+
+ eachLayer: function (method, context) {
+ for (var i in this._layers) {
+ method.call(context, this._layers[i]);
+ }
+ return this;
+ },
+
+ getLayer: function (id) {
+ return this._layers[id];
+ },
+
+ getLayers: function () {
+ var layers = [];
+
+ for (var i in this._layers) {
+ layers.push(this._layers[i]);
+ }
+ return layers;
+ },
+
+ setZIndex: function (zIndex) {
+ return this.invoke('setZIndex', zIndex);
+ },
+
+ getLayerId: function (layer) {
+ return L.stamp(layer);
+ }
+});
+
+L.layerGroup = function (layers) {
+ return new L.LayerGroup(layers);
+};
+
+
+/*
+ * L.FeatureGroup extends L.LayerGroup by introducing mouse events and additional methods
+ * shared between a group of interactive layers (like vectors or markers).
+ */
+
+L.FeatureGroup = L.LayerGroup.extend({
+ includes: L.Mixin.Events,
+
+ statics: {
+ EVENTS: 'click dblclick mouseover mouseout mousemove contextmenu popupopen popupclose'
+ },
+
+ addLayer: function (layer) {
+ if (this.hasLayer(layer)) {
+ return this;
+ }
+
+ if ('on' in layer) {
+ layer.on(L.FeatureGroup.EVENTS, this._propagateEvent, this);
+ }
+
+ L.LayerGroup.prototype.addLayer.call(this, layer);
+
+ if (this._popupContent && layer.bindPopup) {
+ layer.bindPopup(this._popupContent, this._popupOptions);
+ }
+
+ 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.off(L.FeatureGroup.EVENTS, this._propagateEvent, this);
+
+ L.LayerGroup.prototype.removeLayer.call(this, layer);
+
+ if (this._popupContent) {
+ this.invoke('unbindPopup');
+ }
+
+ return this.fire('layerremove', {layer: layer});
+ },
+
+ bindPopup: function (content, options) {
+ this._popupContent = content;
+ this._popupOptions = options;
+ return this.invoke('bindPopup', content, options);
+ },
+
+ openPopup: function (latlng) {
+ // open popup on the first layer
+ for (var id in this._layers) {
+ this._layers[id].openPopup(latlng);
+ break;
+ }
+ return this;
+ },
+
+ setStyle: function (style) {
+ return this.invoke('setStyle', style);
+ },
+
+ bringToFront: function () {
+ return this.invoke('bringToFront');
+ },
+
+ bringToBack: function () {
+ return this.invoke('bringToBack');
+ },
+
+ getBounds: function () {
+ var bounds = new L.LatLngBounds();
+
+ this.eachLayer(function (layer) {
+ bounds.extend(layer instanceof L.Marker ? layer.getLatLng() : layer.getBounds());
+ });
+
+ return bounds;
+ },
+
+ _propagateEvent: function (e) {
+ e = L.extend({
+ layer: e.target,
+ target: this
+ }, e);
+ this.fire(e.type, e);
+ }
+});
+
+L.featureGroup = function (layers) {
+ return new L.FeatureGroup(layers);
+};
+
+
+/*
+ * L.Path is a base class for rendering vector paths on a map. Inherited by Polyline, Circle, etc.
+ */
+
+L.Path = L.Class.extend({
+ includes: [L.Mixin.Events],
+
+ statics: {
+ // how much to extend the clip area around the map view
+ // (relative to its size, e.g. 0.5 is half the screen in each direction)
+ // set it so that SVG element doesn't exceed 1280px (vectors flicker on dragend if it is)
+ CLIP_PADDING: (function () {
+ var max = L.Browser.mobile ? 1280 : 2000,
+ target = (max / Math.max(window.outerWidth, window.outerHeight) - 1) / 2;
+ return Math.max(0, Math.min(0.5, target));
+ })()
+ },
+
+ options: {
+ stroke: true,
+ color: '#0033ff',
+ dashArray: null,
+ lineCap: null,
+ lineJoin: null,
+ weight: 5,
+ opacity: 0.5,
+
+ fill: false,
+ fillColor: null, //same as color by default
+ fillOpacity: 0.2,
+
+ clickable: true
+ },
+
+ initialize: function (options) {
+ L.setOptions(this, options);
+ },
+
+ onAdd: function (map) {
+ this._map = map;
+
+ if (!this._container) {
+ this._initElements();
+ this._initEvents();
+ }
+
+ this.projectLatlngs();
+ this._updatePath();
+
+ if (this._container) {
+ this._map._pathRoot.appendChild(this._container);
+ }
+
+ this.fire('add');
+
+ map.on({
+ 'viewreset': this.projectLatlngs,
+ 'moveend': this._updatePath
+ }, this);
+ },
+
+ addTo: function (map) {
+ map.addLayer(this);
+ return this;
+ },
+
+ onRemove: function (map) {
+ map._pathRoot.removeChild(this._container);
+
+ // Need to fire remove event before we set _map to null as the event hooks might need the object
+ this.fire('remove');
+ this._map = null;
+
+ if (L.Browser.vml) {
+ this._container = null;
+ this._stroke = null;
+ this._fill = null;
+ }
+
+ map.off({
+ 'viewreset': this.projectLatlngs,
+ 'moveend': this._updatePath
+ }, this);
+ },
+
+ projectLatlngs: function () {
+ // do all projection stuff here
+ },
+
+ setStyle: function (style) {
+ L.setOptions(this, style);
+
+ if (this._container) {
+ this._updateStyle();
+ }
+
+ return this;
+ },
+
+ redraw: function () {
+ if (this._map) {
+ this.projectLatlngs();
+ this._updatePath();
+ }
+ return this;
+ }
+});
+
+L.Map.include({
+ _updatePathViewport: function () {
+ var p = L.Path.CLIP_PADDING,
+ size = this.getSize(),
+ panePos = L.DomUtil.getPosition(this._mapPane),
+ min = panePos.multiplyBy(-1)._subtract(size.multiplyBy(p)._round()),
+ max = min.add(size.multiplyBy(1 + p * 2)._round());
+
+ this._pathViewport = new L.Bounds(min, max);
+ }
+});
+
+
+/*
+ * Extends L.Path with SVG-specific rendering code.
+ */
+
+L.Path.SVG_NS = 'http://www.w3.org/2000/svg';
+
+L.Browser.svg = !!(document.createElementNS && document.createElementNS(L.Path.SVG_NS, 'svg').createSVGRect);
+
+L.Path = L.Path.extend({
+ statics: {
+ SVG: L.Browser.svg
+ },
+
+ bringToFront: function () {
+ var root = this._map._pathRoot,
+ path = this._container;
+
+ if (path && root.lastChild !== path) {
+ root.appendChild(path);
+ }
+ return this;
+ },
+
+ bringToBack: function () {
+ var root = this._map._pathRoot,
+ path = this._container,
+ first = root.firstChild;
+
+ if (path && first !== path) {
+ root.insertBefore(path, first);
+ }
+ return this;
+ },
+
+ getPathString: function () {
+ // form path string here
+ },
+
+ _createElement: function (name) {
+ return document.createElementNS(L.Path.SVG_NS, name);
+ },
+
+ _initElements: function () {
+ this._map._initPathRoot();
+ this._initPath();
+ this._initStyle();
+ },
+
+ _initPath: function () {
+ this._container = this._createElement('g');
+
+ this._path = this._createElement('path');
+
+ if (this.options.className) {
+ L.DomUtil.addClass(this._path, this.options.className);
+ }
+
+ this._container.appendChild(this._path);
+ },
+
+ _initStyle: function () {
+ if (this.options.stroke) {
+ this._path.setAttribute('stroke-linejoin', 'round');
+ this._path.setAttribute('stroke-linecap', 'round');
+ }
+ if (this.options.fill) {
+ this._path.setAttribute('fill-rule', 'evenodd');
+ }
+ if (this.options.pointerEvents) {
+ this._path.setAttribute('pointer-events', this.options.pointerEvents);
+ }
+ if (!this.options.clickable && !this.options.pointerEvents) {
+ this._path.setAttribute('pointer-events', 'none');
+ }
+ this._updateStyle();
+ },
+
+ _updateStyle: function () {
+ if (this.options.stroke) {
+ this._path.setAttribute('stroke', this.options.color);
+ this._path.setAttribute('stroke-opacity', this.options.opacity);
+ this._path.setAttribute('stroke-width', this.options.weight);
+ if (this.options.dashArray) {
+ this._path.setAttribute('stroke-dasharray', this.options.dashArray);
+ } else {
+ this._path.removeAttribute('stroke-dasharray');
+ }
+ if (this.options.lineCap) {
+ this._path.setAttribute('stroke-linecap', this.options.lineCap);
+ }
+ if (this.options.lineJoin) {
+ this._path.setAttribute('stroke-linejoin', this.options.lineJoin);
+ }
+ } else {
+ this._path.setAttribute('stroke', 'none');
+ }
+ if (this.options.fill) {
+ this._path.setAttribute('fill', this.options.fillColor || this.options.color);
+ this._path.setAttribute('fill-opacity', this.options.fillOpacity);
+ } else {
+ this._path.setAttribute('fill', 'none');
+ }
+ },
+
+ _updatePath: function () {
+ var str = this.getPathString();
+ if (!str) {
+ // fix webkit empty string parsing bug
+ str = 'M0 0';
+ }
+ this._path.setAttribute('d', str);
+ },
+
+ // TODO remove duplication with L.Map
+ _initEvents: function () {
+ if (this.options.clickable) {
+ if (L.Browser.svg || !L.Browser.vml) {
+ L.DomUtil.addClass(this._path, 'leaflet-clickable');
+ }
+
+ L.DomEvent.on(this._container, 'click', this._onMouseClick, this);
+
+ var events = ['dblclick', 'mousedown', 'mouseover',
+ 'mouseout', 'mousemove', 'contextmenu'];
+ for (var i = 0; i < events.length; i++) {
+ L.DomEvent.on(this._container, events[i], this._fireMouseEvent, this);
+ }
+ }
+ },
+
+ _onMouseClick: function (e) {
+ if (this._map.dragging && this._map.dragging.moved()) { return; }
+
+ this._fireMouseEvent(e);
+ },
+
+ _fireMouseEvent: function (e) {
+ if (!this.hasEventListeners(e.type)) { return; }
+
+ var map = this._map,
+ containerPoint = map.mouseEventToContainerPoint(e),
+ layerPoint = map.containerPointToLayerPoint(containerPoint),
+ latlng = map.layerPointToLatLng(layerPoint);
+
+ this.fire(e.type, {
+ latlng: latlng,
+ layerPoint: layerPoint,
+ containerPoint: containerPoint,
+ originalEvent: e
+ });
+
+ if (e.type === 'contextmenu') {
+ L.DomEvent.preventDefault(e);
+ }
+ if (e.type !== 'mousemove') {
+ L.DomEvent.stopPropagation(e);
+ }
+ }
+});
+
+L.Map.include({
+ _initPathRoot: function () {
+ if (!this._pathRoot) {
+ this._pathRoot = L.Path.prototype._createElement('svg');
+ this._panes.overlayPane.appendChild(this._pathRoot);
+
+ if (this.options.zoomAnimation && L.Browser.any3d) {
+ L.DomUtil.addClass(this._pathRoot, 'leaflet-zoom-animated');
+
+ this.on({
+ 'zoomanim': this._animatePathZoom,
+ 'zoomend': this._endPathZoom
+ });
+ } else {
+ L.DomUtil.addClass(this._pathRoot, 'leaflet-zoom-hide');
+ }
+
+ this.on('moveend', this._updateSvgViewport);
+ this._updateSvgViewport();
+ }
+ },
+
+ _animatePathZoom: function (e) {
+ var scale = this.getZoomScale(e.zoom),
+ offset = this._getCenterOffset(e.center)._multiplyBy(-scale)._add(this._pathViewport.min);
+
+ this._pathRoot.style[L.DomUtil.TRANSFORM] =
+ L.DomUtil.getTranslateString(offset) + ' scale(' + scale + ') ';
+
+ this._pathZooming = true;
+ },
+
+ _endPathZoom: function () {
+ this._pathZooming = false;
+ },
+
+ _updateSvgViewport: function () {
+
+ if (this._pathZooming) {
+ // Do not update SVGs while a zoom animation is going on otherwise the animation will break.
+ // When the zoom animation ends we will be updated again anyway
+ // This fixes the case where you do a momentum move and zoom while the move is still ongoing.
+ return;
+ }
+
+ this._updatePathViewport();
+
+ var vp = this._pathViewport,
+ min = vp.min,
+ max = vp.max,
+ width = max.x - min.x,
+ height = max.y - min.y,
+ root = this._pathRoot,
+ pane = this._panes.overlayPane;
+
+ // Hack to make flicker on drag end on mobile webkit less irritating
+ if (L.Browser.mobileWebkit) {
+ pane.removeChild(root);
+ }
+
+ L.DomUtil.setPosition(root, min);
+ root.setAttribute('width', width);
+ root.setAttribute('height', height);
+ root.setAttribute('viewBox', [min.x, min.y, width, height].join(' '));
+
+ if (L.Browser.mobileWebkit) {
+ pane.appendChild(root);
+ }
+ }
+});
+
+
+/*
+ * Popup extension to L.Path (polylines, polygons, circles), adding popup-related methods.
+ */
+
+L.Path.include({
+
+ bindPopup: function (content, options) {
+
+ if (content instanceof L.Popup) {
+ this._popup = content;
+ } else {
+ if (!this._popup || options) {
+ this._popup = new L.Popup(options, this);
+ }
+ this._popup.setContent(content);
+ }
+
+ if (!this._popupHandlersAdded) {
+ this
+ .on('click', this._openPopup, this)
+ .on('remove', this.closePopup, this);
+
+ this._popupHandlersAdded = true;
+ }
+
+ return this;
+ },
+
+ unbindPopup: function () {
+ if (this._popup) {
+ this._popup = null;
+ this
+ .off('click', this._openPopup)
+ .off('remove', this.closePopup);
+
+ this._popupHandlersAdded = false;
+ }
+ return this;
+ },
+
+ openPopup: function (latlng) {
+
+ if (this._popup) {
+ // open the popup from one of the path's points if not specified
+ latlng = latlng || this._latlng ||
+ this._latlngs[Math.floor(this._latlngs.length / 2)];
+
+ this._openPopup({latlng: latlng});
+ }
+
+ return this;
+ },
+
+ closePopup: function () {
+ if (this._popup) {
+ this._popup._close();
+ }
+ return this;
+ },
+
+ _openPopup: function (e) {
+ this._popup.setLatLng(e.latlng);
+ this._map.openPopup(this._popup);
+ }
+});
+
+
+/*
+ * Vector rendering for IE6-8 through VML.
+ * Thanks to Dmitry Baranovsky and his Raphael library for inspiration!
+ */
+
+L.Browser.vml = !L.Browser.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;
+ }
+}());
+
+L.Path = L.Browser.svg || !L.Browser.vml ? L.Path : L.Path.extend({
+ statics: {
+ VML: true,
+ CLIP_PADDING: 0.02
+ },
+
+ _createElement: (function () {
+ try {
+ document.namespaces.add('lvml', 'urn:schemas-microsoft-com:vml');
+ return function (name) {
+ return document.createElement('');
+ };
+ } catch (e) {
+ return function (name) {
+ return document.createElement(
+ '<' + name + ' xmlns="urn:schemas-microsoft.com:vml" class="lvml">');
+ };
+ }
+ }()),
+
+ _initPath: function () {
+ var container = this._container = this._createElement('shape');
+
+ L.DomUtil.addClass(container, 'leaflet-vml-shape' +
+ (this.options.className ? ' ' + this.options.className : ''));
+
+ if (this.options.clickable) {
+ L.DomUtil.addClass(container, 'leaflet-clickable');
+ }
+
+ container.coordsize = '1 1';
+
+ this._path = this._createElement('path');
+ container.appendChild(this._path);
+
+ this._map._pathRoot.appendChild(container);
+ },
+
+ _initStyle: function () {
+ this._updateStyle();
+ },
+
+ _updateStyle: function () {
+ var stroke = this._stroke,
+ fill = this._fill,
+ options = this.options,
+ container = this._container;
+
+ container.stroked = options.stroke;
+ container.filled = options.fill;
+
+ if (options.stroke) {
+ if (!stroke) {
+ stroke = this._stroke = this._createElement('stroke');
+ stroke.endcap = 'round';
+ container.appendChild(stroke);
+ }
+ stroke.weight = options.weight + 'px';
+ stroke.color = options.color;
+ stroke.opacity = options.opacity;
+
+ if (options.dashArray) {
+ stroke.dashStyle = L.Util.isArray(options.dashArray) ?
+ options.dashArray.join(' ') :
+ options.dashArray.replace(/( *, *)/g, ' ');
+ } else {
+ stroke.dashStyle = '';
+ }
+ if (options.lineCap) {
+ stroke.endcap = options.lineCap.replace('butt', 'flat');
+ }
+ if (options.lineJoin) {
+ stroke.joinstyle = options.lineJoin;
+ }
+
+ } else if (stroke) {
+ container.removeChild(stroke);
+ this._stroke = null;
+ }
+
+ if (options.fill) {
+ if (!fill) {
+ fill = this._fill = this._createElement('fill');
+ container.appendChild(fill);
+ }
+ fill.color = options.fillColor || options.color;
+ fill.opacity = options.fillOpacity;
+
+ } else if (fill) {
+ container.removeChild(fill);
+ this._fill = null;
+ }
+ },
+
+ _updatePath: function () {
+ var style = this._container.style;
+
+ style.display = 'none';
+ this._path.v = this.getPathString() + ' '; // the space fixes IE empty path string bug
+ style.display = '';
+ }
+});
+
+L.Map.include(L.Browser.svg || !L.Browser.vml ? {} : {
+ _initPathRoot: function () {
+ if (this._pathRoot) { return; }
+
+ var root = this._pathRoot = document.createElement('div');
+ root.className = 'leaflet-vml-container';
+ this._panes.overlayPane.appendChild(root);
+
+ this.on('moveend', this._updatePathViewport);
+ this._updatePathViewport();
+ }
+});
+
+
+/*
+ * Vector rendering for all browsers that support canvas.
+ */
+
+L.Browser.canvas = (function () {
+ return !!document.createElement('canvas').getContext;
+}());
+
+L.Path = (L.Path.SVG && !window.L_PREFER_CANVAS) || !L.Browser.canvas ? L.Path : L.Path.extend({
+ statics: {
+ //CLIP_PADDING: 0.02, // not sure if there's a need to set it to a small value
+ CANVAS: true,
+ SVG: false
+ },
+
+ redraw: function () {
+ if (this._map) {
+ this.projectLatlngs();
+ this._requestUpdate();
+ }
+ return this;
+ },
+
+ setStyle: function (style) {
+ L.setOptions(this, style);
+
+ if (this._map) {
+ this._updateStyle();
+ this._requestUpdate();
+ }
+ return this;
+ },
+
+ onRemove: function (map) {
+ map
+ .off('viewreset', this.projectLatlngs, this)
+ .off('moveend', this._updatePath, this);
+
+ if (this.options.clickable) {
+ this._map.off('click', this._onClick, this);
+ this._map.off('mousemove', this._onMouseMove, this);
+ }
+
+ this._requestUpdate();
+
+ this.fire('remove');
+ this._map = null;
+ },
+
+ _requestUpdate: function () {
+ if (this._map && !L.Path._updateRequest) {
+ L.Path._updateRequest = L.Util.requestAnimFrame(this._fireMapMoveEnd, this._map);
+ }
+ },
+
+ _fireMapMoveEnd: function () {
+ L.Path._updateRequest = null;
+ this.fire('moveend');
+ },
+
+ _initElements: function () {
+ this._map._initPathRoot();
+ this._ctx = this._map._canvasCtx;
+ },
+
+ _updateStyle: function () {
+ var options = this.options;
+
+ if (options.stroke) {
+ this._ctx.lineWidth = options.weight;
+ this._ctx.strokeStyle = options.color;
+ }
+ if (options.fill) {
+ this._ctx.fillStyle = options.fillColor || options.color;
+ }
+ },
+
+ _drawPath: function () {
+ var i, j, len, len2, point, drawMethod;
+
+ this._ctx.beginPath();
+
+ for (i = 0, len = this._parts.length; i < len; i++) {
+ for (j = 0, len2 = this._parts[i].length; j < len2; j++) {
+ point = this._parts[i][j];
+ drawMethod = (j === 0 ? 'move' : 'line') + 'To';
+
+ this._ctx[drawMethod](point.x, point.y);
+ }
+ // TODO refactor ugly hack
+ if (this instanceof L.Polygon) {
+ this._ctx.closePath();
+ }
+ }
+ },
+
+ _checkIfEmpty: function () {
+ return !this._parts.length;
+ },
+
+ _updatePath: function () {
+ if (this._checkIfEmpty()) { return; }
+
+ var ctx = this._ctx,
+ options = this.options;
+
+ this._drawPath();
+ ctx.save();
+ this._updateStyle();
+
+ if (options.fill) {
+ ctx.globalAlpha = options.fillOpacity;
+ ctx.fill();
+ }
+
+ if (options.stroke) {
+ ctx.globalAlpha = options.opacity;
+ ctx.stroke();
+ }
+
+ ctx.restore();
+
+ // TODO optimization: 1 fill/stroke for all features with equal style instead of 1 for each feature
+ },
+
+ _initEvents: function () {
+ if (this.options.clickable) {
+ // TODO dblclick
+ this._map.on('mousemove', this._onMouseMove, this);
+ this._map.on('click', this._onClick, this);
+ }
+ },
+
+ _onClick: function (e) {
+ if (this._containsPoint(e.layerPoint)) {
+ this.fire('click', e);
+ }
+ },
+
+ _onMouseMove: function (e) {
+ if (!this._map || this._map._animatingZoom) { return; }
+
+ // TODO don't do on each move
+ if (this._containsPoint(e.layerPoint)) {
+ this._ctx.canvas.style.cursor = 'pointer';
+ this._mouseInside = true;
+ this.fire('mouseover', e);
+
+ } else if (this._mouseInside) {
+ this._ctx.canvas.style.cursor = '';
+ this._mouseInside = false;
+ this.fire('mouseout', e);
+ }
+ }
+});
+
+L.Map.include((L.Path.SVG && !window.L_PREFER_CANVAS) || !L.Browser.canvas ? {} : {
+ _initPathRoot: function () {
+ var root = this._pathRoot,
+ ctx;
+
+ if (!root) {
+ root = this._pathRoot = document.createElement('canvas');
+ root.style.position = 'absolute';
+ ctx = this._canvasCtx = root.getContext('2d');
+
+ ctx.lineCap = 'round';
+ ctx.lineJoin = 'round';
+
+ this._panes.overlayPane.appendChild(root);
+
+ if (this.options.zoomAnimation) {
+ this._pathRoot.className = 'leaflet-zoom-animated';
+ this.on('zoomanim', this._animatePathZoom);
+ this.on('zoomend', this._endPathZoom);
+ }
+ this.on('moveend', this._updateCanvasViewport);
+ this._updateCanvasViewport();
+ }
+ },
+
+ _updateCanvasViewport: function () {
+ // don't redraw while zooming. See _updateSvgViewport for more details
+ if (this._pathZooming) { return; }
+ this._updatePathViewport();
+
+ var vp = this._pathViewport,
+ min = vp.min,
+ size = vp.max.subtract(min),
+ root = this._pathRoot;
+
+ //TODO check if this works properly on mobile webkit
+ L.DomUtil.setPosition(root, min);
+ root.width = size.x;
+ root.height = size.y;
+ root.getContext('2d').translate(-min.x, -min.y);
+ }
+});
+
+
+/*
+ * L.LineUtil contains different utility functions for line segments
+ * and polylines (clipping, simplification, distances, etc.)
+ */
+
+/*jshint bitwise:false */ // allow bitwise operations for this file
+
+L.LineUtil = {
+
+ // Simplify polyline with vertex reduction and Douglas-Peucker simplification.
+ // Improves rendering performance dramatically by lessening the number of points to draw.
+
+ simplify: function (/*Point[]*/ points, /*Number*/ tolerance) {
+ if (!tolerance || !points.length) {
+ return points.slice();
+ }
+
+ var sqTolerance = tolerance * tolerance;
+
+ // stage 1: vertex reduction
+ points = this._reducePoints(points, sqTolerance);
+
+ // stage 2: Douglas-Peucker simplification
+ points = this._simplifyDP(points, sqTolerance);
+
+ return points;
+ },
+
+ // distance from a point to a segment between two points
+ pointToSegmentDistance: function (/*Point*/ p, /*Point*/ p1, /*Point*/ p2) {
+ return Math.sqrt(this._sqClosestPointOnSegment(p, p1, p2, true));
+ },
+
+ closestPointOnSegment: function (/*Point*/ p, /*Point*/ p1, /*Point*/ p2) {
+ return this._sqClosestPointOnSegment(p, p1, p2);
+ },
+
+ // Douglas-Peucker simplification, see http://en.wikipedia.org/wiki/Douglas-Peucker_algorithm
+ _simplifyDP: function (points, sqTolerance) {
+
+ var len = points.length,
+ ArrayConstructor = typeof Uint8Array !== undefined + '' ? Uint8Array : Array,
+ markers = new ArrayConstructor(len);
+
+ markers[0] = markers[len - 1] = 1;
+
+ this._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;
+ },
+
+ _simplifyDPStep: function (points, markers, sqTolerance, first, last) {
+
+ var maxSqDist = 0,
+ index, i, sqDist;
+
+ for (i = first + 1; i <= last - 1; i++) {
+ sqDist = this._sqClosestPointOnSegment(points[i], points[first], points[last], true);
+
+ if (sqDist > maxSqDist) {
+ index = i;
+ maxSqDist = sqDist;
+ }
+ }
+
+ if (maxSqDist > sqTolerance) {
+ markers[index] = 1;
+
+ this._simplifyDPStep(points, markers, sqTolerance, first, index);
+ this._simplifyDPStep(points, markers, sqTolerance, index, last);
+ }
+ },
+
+ // reduce points that are too close to each other to a single point
+ _reducePoints: function (points, sqTolerance) {
+ var reducedPoints = [points[0]];
+
+ for (var i = 1, prev = 0, len = points.length; i < len; i++) {
+ if (this._sqDist(points[i], points[prev]) > sqTolerance) {
+ reducedPoints.push(points[i]);
+ prev = i;
+ }
+ }
+ if (prev < len - 1) {
+ reducedPoints.push(points[len - 1]);
+ }
+ return reducedPoints;
+ },
+
+ // Cohen-Sutherland line clipping algorithm.
+ // Used to avoid rendering parts of a polyline that are not currently visible.
+
+ clipSegment: function (a, b, bounds, useLastCode) {
+ var codeA = useLastCode ? this._lastCode : this._getBitCode(a, bounds),
+ codeB = this._getBitCode(b, bounds),
+
+ codeOut, p, newCode;
+
+ // save 2nd code to avoid calculating it on the next segment
+ this._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)
+ } else if (codeA & codeB) {
+ return false;
+ // other cases
+ } else {
+ codeOut = codeA || codeB;
+ p = this._getEdgeIntersection(a, b, codeOut, bounds);
+ newCode = this._getBitCode(p, bounds);
+
+ if (codeOut === codeA) {
+ a = p;
+ codeA = newCode;
+ } else {
+ b = p;
+ codeB = newCode;
+ }
+ }
+ }
+ },
+
+ _getEdgeIntersection: function (a, b, code, bounds) {
+ var dx = b.x - a.x,
+ dy = b.y - a.y,
+ min = bounds.min,
+ max = bounds.max;
+
+ if (code & 8) { // top
+ return new L.Point(a.x + dx * (max.y - a.y) / dy, max.y);
+ } else if (code & 4) { // bottom
+ return new L.Point(a.x + dx * (min.y - a.y) / dy, min.y);
+ } else if (code & 2) { // right
+ return new L.Point(max.x, a.y + dy * (max.x - a.x) / dx);
+ } else if (code & 1) { // left
+ return new L.Point(min.x, a.y + dy * (min.x - a.x) / dx);
+ }
+ },
+
+ _getBitCode: function (/*Point*/ 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)
+ _sqDist: function (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
+ _sqClosestPointOnSegment: function (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 L.Point(x, y);
+ }
+};
+
+
+/*
+ * L.Polyline is used to display polylines on a map.
+ */
+
+L.Polyline = L.Path.extend({
+ initialize: function (latlngs, options) {
+ L.Path.prototype.initialize.call(this, options);
+
+ this._latlngs = this._convertLatLngs(latlngs);
+ },
+
+ options: {
+ // how much to simplify the polyline on each zoom level
+ // more = better performance and smoother look, less = more accurate
+ smoothFactor: 1.0,
+ noClip: false
+ },
+
+ projectLatlngs: function () {
+ this._originalPoints = [];
+
+ for (var i = 0, len = this._latlngs.length; i < len; i++) {
+ this._originalPoints[i] = this._map.latLngToLayerPoint(this._latlngs[i]);
+ }
+ },
+
+ getPathString: function () {
+ for (var i = 0, len = this._parts.length, str = ''; i < len; i++) {
+ str += this._getPathPartStr(this._parts[i]);
+ }
+ return str;
+ },
+
+ getLatLngs: function () {
+ return this._latlngs;
+ },
+
+ setLatLngs: function (latlngs) {
+ this._latlngs = this._convertLatLngs(latlngs);
+ return this.redraw();
+ },
+
+ addLatLng: function (latlng) {
+ this._latlngs.push(L.latLng(latlng));
+ return this.redraw();
+ },
+
+ spliceLatLngs: function () { // (Number index, Number howMany)
+ var removed = [].splice.apply(this._latlngs, arguments);
+ this._convertLatLngs(this._latlngs, true);
+ this.redraw();
+ return removed;
+ },
+
+ closestLayerPoint: function (p) {
+ var minDistance = Infinity, parts = this._parts, p1, p2, minPoint = null;
+
+ for (var j = 0, jLen = parts.length; j < jLen; j++) {
+ var points = parts[j];
+ for (var i = 1, len = points.length; i < len; i++) {
+ p1 = points[i - 1];
+ p2 = points[i];
+ var sqDist = L.LineUtil._sqClosestPointOnSegment(p, p1, p2, true);
+ if (sqDist < minDistance) {
+ minDistance = sqDist;
+ minPoint = L.LineUtil._sqClosestPointOnSegment(p, p1, p2);
+ }
+ }
+ }
+ if (minPoint) {
+ minPoint.distance = Math.sqrt(minDistance);
+ }
+ return minPoint;
+ },
+
+ getBounds: function () {
+ return new L.LatLngBounds(this.getLatLngs());
+ },
+
+ _convertLatLngs: function (latlngs, overwrite) {
+ var i, len, target = overwrite ? latlngs : [];
+
+ for (i = 0, len = latlngs.length; i < len; i++) {
+ if (L.Util.isArray(latlngs[i]) && typeof latlngs[i][0] !== 'number') {
+ return;
+ }
+ target[i] = L.latLng(latlngs[i]);
+ }
+ return target;
+ },
+
+ _initEvents: function () {
+ L.Path.prototype._initEvents.call(this);
+ },
+
+ _getPathPartStr: function (points) {
+ var round = L.Path.VML;
+
+ for (var j = 0, len2 = points.length, str = '', p; j < len2; j++) {
+ p = points[j];
+ if (round) {
+ p._round();
+ }
+ str += (j ? 'L' : 'M') + p.x + ' ' + p.y;
+ }
+ return str;
+ },
+
+ _clipPoints: function () {
+ var points = this._originalPoints,
+ len = points.length,
+ i, k, segment;
+
+ if (this.options.noClip) {
+ this._parts = [points];
+ return;
+ }
+
+ this._parts = [];
+
+ var parts = this._parts,
+ vp = this._map._pathViewport,
+ lu = L.LineUtil;
+
+ for (i = 0, k = 0; i < len - 1; i++) {
+ segment = lu.clipSegment(points[i], points[i + 1], vp, i);
+ 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[i + 1]) || (i === len - 2)) {
+ parts[k].push(segment[1]);
+ k++;
+ }
+ }
+ },
+
+ // simplify each clipped part of the polyline
+ _simplifyPoints: function () {
+ var parts = this._parts,
+ lu = L.LineUtil;
+
+ for (var i = 0, len = parts.length; i < len; i++) {
+ parts[i] = lu.simplify(parts[i], this.options.smoothFactor);
+ }
+ },
+
+ _updatePath: function () {
+ if (!this._map) { return; }
+
+ this._clipPoints();
+ this._simplifyPoints();
+
+ L.Path.prototype._updatePath.call(this);
+ }
+});
+
+L.polyline = function (latlngs, options) {
+ return new L.Polyline(latlngs, options);
+};
+
+
+/*
+ * L.PolyUtil contains utility functions for polygons (clipping, etc.).
+ */
+
+/*jshint bitwise:false */ // allow bitwise operations here
+
+L.PolyUtil = {};
+
+/*
+ * Sutherland-Hodgeman polygon clipping algorithm.
+ * Used to avoid rendering parts of a polygon that are not currently visible.
+ */
+L.PolyUtil.clipPolygon = function (points, bounds) {
+ var clippedPoints,
+ edges = [1, 4, 2, 8],
+ i, j, k,
+ a, b,
+ len, edge, p,
+ lu = L.LineUtil;
+
+ for (i = 0, len = points.length; i < len; i++) {
+ points[i]._code = lu._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 = lu._getEdgeIntersection(b, a, edge, bounds);
+ p._code = lu._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 = lu._getEdgeIntersection(b, a, edge, bounds);
+ p._code = lu._getBitCode(p, bounds);
+ clippedPoints.push(p);
+ }
+ }
+ points = clippedPoints;
+ }
+
+ return points;
+};
+
+
+/*
+ * L.Polygon is used to display polygons on a map.
+ */
+
+L.Polygon = L.Polyline.extend({
+ options: {
+ fill: true
+ },
+
+ initialize: function (latlngs, options) {
+ L.Polyline.prototype.initialize.call(this, latlngs, options);
+ this._initWithHoles(latlngs);
+ },
+
+ _initWithHoles: function (latlngs) {
+ var i, len, hole;
+ if (latlngs && L.Util.isArray(latlngs[0]) && (typeof latlngs[0][0] !== 'number')) {
+ this._latlngs = this._convertLatLngs(latlngs[0]);
+ this._holes = latlngs.slice(1);
+
+ for (i = 0, len = this._holes.length; i < len; i++) {
+ hole = this._holes[i] = this._convertLatLngs(this._holes[i]);
+ if (hole[0].equals(hole[hole.length - 1])) {
+ hole.pop();
+ }
+ }
+ }
+
+ // filter out last point if its equal to the first one
+ latlngs = this._latlngs;
+
+ if (latlngs.length >= 2 && latlngs[0].equals(latlngs[latlngs.length - 1])) {
+ latlngs.pop();
+ }
+ },
+
+ projectLatlngs: function () {
+ L.Polyline.prototype.projectLatlngs.call(this);
+
+ // project polygon holes points
+ // TODO move this logic to Polyline to get rid of duplication
+ this._holePoints = [];
+
+ if (!this._holes) { return; }
+
+ var i, j, len, len2;
+
+ for (i = 0, len = this._holes.length; i < len; i++) {
+ this._holePoints[i] = [];
+
+ for (j = 0, len2 = this._holes[i].length; j < len2; j++) {
+ this._holePoints[i][j] = this._map.latLngToLayerPoint(this._holes[i][j]);
+ }
+ }
+ },
+
+ setLatLngs: function (latlngs) {
+ if (latlngs && L.Util.isArray(latlngs[0]) && (typeof latlngs[0][0] !== 'number')) {
+ this._initWithHoles(latlngs);
+ return this.redraw();
+ } else {
+ return L.Polyline.prototype.setLatLngs.call(this, latlngs);
+ }
+ },
+
+ _clipPoints: function () {
+ var points = this._originalPoints,
+ newParts = [];
+
+ this._parts = [points].concat(this._holePoints);
+
+ if (this.options.noClip) { return; }
+
+ for (var i = 0, len = this._parts.length; i < len; i++) {
+ var clipped = L.PolyUtil.clipPolygon(this._parts[i], this._map._pathViewport);
+ if (clipped.length) {
+ newParts.push(clipped);
+ }
+ }
+
+ this._parts = newParts;
+ },
+
+ _getPathPartStr: function (points) {
+ var str = L.Polyline.prototype._getPathPartStr.call(this, points);
+ return str + (L.Browser.svg ? 'z' : 'x');
+ }
+});
+
+L.polygon = function (latlngs, options) {
+ return new L.Polygon(latlngs, options);
+};
+
+
+/*
+ * Contains L.MultiPolyline and L.MultiPolygon layers.
+ */
+
+(function () {
+ function createMulti(Klass) {
+
+ return L.FeatureGroup.extend({
+
+ initialize: function (latlngs, options) {
+ this._layers = {};
+ this._options = options;
+ this.setLatLngs(latlngs);
+ },
+
+ setLatLngs: function (latlngs) {
+ var i = 0,
+ len = latlngs.length;
+
+ this.eachLayer(function (layer) {
+ if (i < len) {
+ layer.setLatLngs(latlngs[i++]);
+ } else {
+ this.removeLayer(layer);
+ }
+ }, this);
+
+ while (i < len) {
+ this.addLayer(new Klass(latlngs[i++], this._options));
+ }
+
+ return this;
+ },
+
+ getLatLngs: function () {
+ var latlngs = [];
+
+ this.eachLayer(function (layer) {
+ latlngs.push(layer.getLatLngs());
+ });
+
+ return latlngs;
+ }
+ });
+ }
+
+ L.MultiPolyline = createMulti(L.Polyline);
+ L.MultiPolygon = createMulti(L.Polygon);
+
+ L.multiPolyline = function (latlngs, options) {
+ return new L.MultiPolyline(latlngs, options);
+ };
+
+ L.multiPolygon = function (latlngs, options) {
+ return new L.MultiPolygon(latlngs, options);
+ };
+}());
+
+
+/*
+ * L.Rectangle extends Polygon and creates a rectangle when passed a LatLngBounds object.
+ */
+
+L.Rectangle = L.Polygon.extend({
+ initialize: function (latLngBounds, options) {
+ L.Polygon.prototype.initialize.call(this, this._boundsToLatLngs(latLngBounds), options);
+ },
+
+ setBounds: function (latLngBounds) {
+ this.setLatLngs(this._boundsToLatLngs(latLngBounds));
+ },
+
+ _boundsToLatLngs: function (latLngBounds) {
+ latLngBounds = L.latLngBounds(latLngBounds);
+ return [
+ latLngBounds.getSouthWest(),
+ latLngBounds.getNorthWest(),
+ latLngBounds.getNorthEast(),
+ latLngBounds.getSouthEast()
+ ];
+ }
+});
+
+L.rectangle = function (latLngBounds, options) {
+ return new L.Rectangle(latLngBounds, options);
+};
+
+
+/*
+ * L.Circle is a circle overlay (with a certain radius in meters).
+ */
+
+L.Circle = L.Path.extend({
+ initialize: function (latlng, radius, options) {
+ L.Path.prototype.initialize.call(this, options);
+
+ this._latlng = L.latLng(latlng);
+ this._mRadius = radius;
+ },
+
+ options: {
+ fill: true
+ },
+
+ setLatLng: function (latlng) {
+ this._latlng = L.latLng(latlng);
+ return this.redraw();
+ },
+
+ setRadius: function (radius) {
+ this._mRadius = radius;
+ return this.redraw();
+ },
+
+ projectLatlngs: function () {
+ var lngRadius = this._getLngRadius(),
+ latlng = this._latlng,
+ pointLeft = this._map.latLngToLayerPoint([latlng.lat, latlng.lng - lngRadius]);
+
+ this._point = this._map.latLngToLayerPoint(latlng);
+ this._radius = Math.max(this._point.x - pointLeft.x, 1);
+ },
+
+ getBounds: function () {
+ var lngRadius = this._getLngRadius(),
+ latRadius = (this._mRadius / 40075017) * 360,
+ latlng = this._latlng;
+
+ return new L.LatLngBounds(
+ [latlng.lat - latRadius, latlng.lng - lngRadius],
+ [latlng.lat + latRadius, latlng.lng + lngRadius]);
+ },
+
+ getLatLng: function () {
+ return this._latlng;
+ },
+
+ getPathString: function () {
+ var p = this._point,
+ r = this._radius;
+
+ if (this._checkIfEmpty()) {
+ return '';
+ }
+
+ if (L.Browser.svg) {
+ return 'M' + p.x + ',' + (p.y - r) +
+ 'A' + r + ',' + r + ',0,1,1,' +
+ (p.x - 0.1) + ',' + (p.y - r) + ' z';
+ } else {
+ p._round();
+ r = Math.round(r);
+ return 'AL ' + p.x + ',' + p.y + ' ' + r + ',' + r + ' 0,' + (65535 * 360);
+ }
+ },
+
+ getRadius: function () {
+ return this._mRadius;
+ },
+
+ // TODO Earth hardcoded, move into projection code!
+
+ _getLatRadius: function () {
+ return (this._mRadius / 40075017) * 360;
+ },
+
+ _getLngRadius: function () {
+ return this._getLatRadius() / Math.cos(L.LatLng.DEG_TO_RAD * this._latlng.lat);
+ },
+
+ _checkIfEmpty: function () {
+ if (!this._map) {
+ return false;
+ }
+ var vp = this._map._pathViewport,
+ r = this._radius,
+ p = this._point;
+
+ return p.x - r > vp.max.x || p.y - r > vp.max.y ||
+ p.x + r < vp.min.x || p.y + r < vp.min.y;
+ }
+});
+
+L.circle = function (latlng, radius, options) {
+ return new L.Circle(latlng, radius, options);
+};
+
+
+/*
+ * L.CircleMarker is a circle overlay with a permanent pixel radius.
+ */
+
+L.CircleMarker = L.Circle.extend({
+ options: {
+ radius: 10,
+ weight: 2
+ },
+
+ initialize: function (latlng, options) {
+ L.Circle.prototype.initialize.call(this, latlng, null, options);
+ this._radius = this.options.radius;
+ },
+
+ projectLatlngs: function () {
+ this._point = this._map.latLngToLayerPoint(this._latlng);
+ },
+
+ _updateStyle : function () {
+ L.Circle.prototype._updateStyle.call(this);
+ this.setRadius(this.options.radius);
+ },
+
+ setLatLng: function (latlng) {
+ L.Circle.prototype.setLatLng.call(this, latlng);
+ if (this._popup && this._popup._isOpen) {
+ this._popup.setLatLng(latlng);
+ }
+ return this;
+ },
+
+ setRadius: function (radius) {
+ this.options.radius = this._radius = radius;
+ return this.redraw();
+ },
+
+ getRadius: function () {
+ return this._radius;
+ }
+});
+
+L.circleMarker = function (latlng, options) {
+ return new L.CircleMarker(latlng, options);
+};
+
+
+/*
+ * Extends L.Polyline to be able to manually detect clicks on Canvas-rendered polylines.
+ */
+
+L.Polyline.include(!L.Path.CANVAS ? {} : {
+ _containsPoint: function (p, closed) {
+ var i, j, k, len, len2, dist, part,
+ w = this.options.weight / 2;
+
+ if (L.Browser.touch) {
+ w += 10; // polyline click tolerance on touch devices
+ }
+
+ 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;
+ }
+
+ dist = L.LineUtil.pointToSegmentDistance(p, part[k], part[j]);
+
+ if (dist <= w) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+});
+
+
+/*
+ * Extends L.Polygon to be able to manually detect clicks on Canvas-rendered polygons.
+ */
+
+L.Polygon.include(!L.Path.CANVAS ? {} : {
+ _containsPoint: function (p) {
+ var inside = false,
+ part, p1, p2,
+ i, j, k,
+ len, len2;
+
+ // TODO optimization: check if within bounds first
+
+ if (L.Polyline.prototype._containsPoint.call(this, p, true)) {
+ // click on polygon border
+ return true;
+ }
+
+ // 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;
+ }
+ }
+ }
+
+ return inside;
+ }
+});
+
+
+/*
+ * Extends L.Circle with Canvas-specific code.
+ */
+
+L.Circle.include(!L.Path.CANVAS ? {} : {
+ _drawPath: function () {
+ var p = this._point;
+ this._ctx.beginPath();
+ this._ctx.arc(p.x, p.y, this._radius, 0, Math.PI * 2, false);
+ },
+
+ _containsPoint: function (p) {
+ var center = this._point,
+ w2 = this.options.stroke ? this.options.weight / 2 : 0;
+
+ return (p.distanceTo(center) <= this._radius + w2);
+ }
+});
+
+
+/*
+ * CircleMarker canvas specific drawing parts.
+ */
+
+L.CircleMarker.include(!L.Path.CANVAS ? {} : {
+ _updateStyle: function () {
+ L.Path.prototype._updateStyle.call(this);
+ }
+});
+
+
+/*
+ * L.GeoJSON turns any GeoJSON data into a Leaflet layer.
+ */
+
+L.GeoJSON = L.FeatureGroup.extend({
+
+ initialize: function (geojson, options) {
+ L.setOptions(this, options);
+
+ this._layers = {};
+
+ if (geojson) {
+ this.addData(geojson);
+ }
+ },
+
+ addData: function (geojson) {
+ var features = L.Util.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(features[i]);
+ }
+ }
+ return this;
+ }
+
+ var options = this.options;
+
+ if (options.filter && !options.filter(geojson)) { return; }
+
+ var layer = L.GeoJSON.geometryToLayer(geojson, options.pointToLayer, options.coordsToLatLng, options);
+ layer.feature = L.GeoJSON.asFeature(geojson);
+
+ layer.defaultOptions = layer.options;
+ this.resetStyle(layer);
+
+ if (options.onEachFeature) {
+ options.onEachFeature(geojson, layer);
+ }
+
+ return this.addLayer(layer);
+ },
+
+ resetStyle: function (layer) {
+ var style = this.options.style;
+ if (style) {
+ // reset any custom styles
+ L.Util.extend(layer.options, layer.defaultOptions);
+
+ this._setLayerStyle(layer, style);
+ }
+ },
+
+ setStyle: function (style) {
+ 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);
+ }
+ }
+});
+
+L.extend(L.GeoJSON, {
+ geometryToLayer: function (geojson, pointToLayer, coordsToLatLng, vectorOptions) {
+ var geometry = geojson.type === 'Feature' ? geojson.geometry : geojson,
+ coords = geometry.coordinates,
+ layers = [],
+ latlng, latlngs, i, len;
+
+ coordsToLatLng = coordsToLatLng || this.coordsToLatLng;
+
+ switch (geometry.type) {
+ case 'Point':
+ latlng = coordsToLatLng(coords);
+ return pointToLayer ? pointToLayer(geojson, latlng) : new L.Marker(latlng);
+
+ case 'MultiPoint':
+ for (i = 0, len = coords.length; i < len; i++) {
+ latlng = coordsToLatLng(coords[i]);
+ layers.push(pointToLayer ? pointToLayer(geojson, latlng) : new L.Marker(latlng));
+ }
+ return new L.FeatureGroup(layers);
+
+ case 'LineString':
+ latlngs = this.coordsToLatLngs(coords, 0, coordsToLatLng);
+ return new L.Polyline(latlngs, vectorOptions);
+
+ case 'Polygon':
+ if (coords.length === 2 && !coords[1].length) {
+ throw new Error('Invalid GeoJSON object.');
+ }
+ latlngs = this.coordsToLatLngs(coords, 1, coordsToLatLng);
+ return new L.Polygon(latlngs, vectorOptions);
+
+ case 'MultiLineString':
+ latlngs = this.coordsToLatLngs(coords, 1, coordsToLatLng);
+ return new L.MultiPolyline(latlngs, vectorOptions);
+
+ case 'MultiPolygon':
+ latlngs = this.coordsToLatLngs(coords, 2, coordsToLatLng);
+ return new L.MultiPolygon(latlngs, vectorOptions);
+
+ case 'GeometryCollection':
+ for (i = 0, len = geometry.geometries.length; i < len; i++) {
+
+ layers.push(this.geometryToLayer({
+ geometry: geometry.geometries[i],
+ type: 'Feature',
+ properties: geojson.properties
+ }, pointToLayer, coordsToLatLng, vectorOptions));
+ }
+ return new L.FeatureGroup(layers);
+
+ default:
+ throw new Error('Invalid GeoJSON object.');
+ }
+ },
+
+ coordsToLatLng: function (coords) { // (Array[, Boolean]) -> LatLng
+ return new L.LatLng(coords[1], coords[0], coords[2]);
+ },
+
+ coordsToLatLngs: function (coords, levelsDeep, coordsToLatLng) { // (Array[, Number, Function]) -> Array
+ var latlng, i, len,
+ latlngs = [];
+
+ for (i = 0, len = coords.length; i < len; i++) {
+ latlng = levelsDeep ?
+ this.coordsToLatLngs(coords[i], levelsDeep - 1, coordsToLatLng) :
+ (coordsToLatLng || this.coordsToLatLng)(coords[i]);
+
+ latlngs.push(latlng);
+ }
+
+ return latlngs;
+ },
+
+ latLngToCoords: function (latlng) {
+ var coords = [latlng.lng, latlng.lat];
+
+ if (latlng.alt !== undefined) {
+ coords.push(latlng.alt);
+ }
+ return coords;
+ },
+
+ latLngsToCoords: function (latLngs) {
+ var coords = [];
+
+ for (var i = 0, len = latLngs.length; i < len; i++) {
+ coords.push(L.GeoJSON.latLngToCoords(latLngs[i]));
+ }
+
+ return coords;
+ },
+
+ getFeature: function (layer, newGeometry) {
+ return layer.feature ? L.extend({}, layer.feature, {geometry: newGeometry}) : L.GeoJSON.asFeature(newGeometry);
+ },
+
+ asFeature: function (geoJSON) {
+ if (geoJSON.type === 'Feature') {
+ return geoJSON;
+ }
+
+ return {
+ type: 'Feature',
+ properties: {},
+ geometry: geoJSON
+ };
+ }
+});
+
+var PointToGeoJSON = {
+ toGeoJSON: function () {
+ return L.GeoJSON.getFeature(this, {
+ type: 'Point',
+ coordinates: L.GeoJSON.latLngToCoords(this.getLatLng())
+ });
+ }
+};
+
+L.Marker.include(PointToGeoJSON);
+L.Circle.include(PointToGeoJSON);
+L.CircleMarker.include(PointToGeoJSON);
+
+L.Polyline.include({
+ toGeoJSON: function () {
+ return L.GeoJSON.getFeature(this, {
+ type: 'LineString',
+ coordinates: L.GeoJSON.latLngsToCoords(this.getLatLngs())
+ });
+ }
+});
+
+L.Polygon.include({
+ toGeoJSON: function () {
+ var coords = [L.GeoJSON.latLngsToCoords(this.getLatLngs())],
+ i, len, hole;
+
+ coords[0].push(coords[0][0]);
+
+ if (this._holes) {
+ for (i = 0, len = this._holes.length; i < len; i++) {
+ hole = L.GeoJSON.latLngsToCoords(this._holes[i]);
+ hole.push(hole[0]);
+ coords.push(hole);
+ }
+ }
+
+ return L.GeoJSON.getFeature(this, {
+ type: 'Polygon',
+ coordinates: coords
+ });
+ }
+});
+
+(function () {
+ function multiToGeoJSON(type) {
+ return function () {
+ var coords = [];
+
+ this.eachLayer(function (layer) {
+ coords.push(layer.toGeoJSON().geometry.coordinates);
+ });
+
+ return L.GeoJSON.getFeature(this, {
+ type: type,
+ coordinates: coords
+ });
+ };
+ }
+
+ L.MultiPolyline.include({toGeoJSON: multiToGeoJSON('MultiLineString')});
+ L.MultiPolygon.include({toGeoJSON: multiToGeoJSON('MultiPolygon')});
+
+ L.LayerGroup.include({
+ toGeoJSON: function () {
+
+ var geometry = this.feature && this.feature.geometry,
+ jsons = [],
+ json;
+
+ if (geometry && geometry.type === 'MultiPoint') {
+ return multiToGeoJSON('MultiPoint').call(this);
+ }
+
+ var isGeometryCollection = geometry && geometry.type === 'GeometryCollection';
+
+ this.eachLayer(function (layer) {
+ if (layer.toGeoJSON) {
+ json = layer.toGeoJSON();
+ jsons.push(isGeometryCollection ? json.geometry : L.GeoJSON.asFeature(json));
+ }
+ });
+
+ if (isGeometryCollection) {
+ return L.GeoJSON.getFeature(this, {
+ geometries: jsons,
+ type: 'GeometryCollection'
+ });
+ }
+
+ return {
+ type: 'FeatureCollection',
+ features: jsons
+ };
+ }
+ });
+}());
+
+L.geoJson = function (geojson, options) {
+ return new L.GeoJSON(geojson, options);
+};
+
+
+/*
+ * L.DomEvent contains functions for working with DOM events.
+ */
+
+L.DomEvent = {
+ /* inspired by John Resig, Dean Edwards and YUI addEvent implementations */
+ addListener: function (obj, type, fn, context) { // (HTMLElement, String, Function[, Object])
+
+ var id = L.stamp(fn),
+ key = '_leaflet_' + type + id,
+ handler, originalHandler, newType;
+
+ if (obj[key]) { return this; }
+
+ handler = function (e) {
+ return fn.call(context || obj, e || L.DomEvent._getEvent());
+ };
+
+ if (L.Browser.pointer && type.indexOf('touch') === 0) {
+ return this.addPointerListener(obj, type, handler, id);
+ }
+ if (L.Browser.touch && (type === 'dblclick') && this.addDoubleTapListener) {
+ this.addDoubleTapListener(obj, handler, id);
+ }
+
+ if ('addEventListener' in obj) {
+
+ if (type === 'mousewheel') {
+ obj.addEventListener('DOMMouseScroll', handler, false);
+ obj.addEventListener(type, handler, false);
+
+ } else if ((type === 'mouseenter') || (type === 'mouseleave')) {
+
+ originalHandler = handler;
+ newType = (type === 'mouseenter' ? 'mouseover' : 'mouseout');
+
+ handler = function (e) {
+ if (!L.DomEvent._checkMouse(obj, e)) { return; }
+ return originalHandler(e);
+ };
+
+ obj.addEventListener(newType, handler, false);
+
+ } else if (type === 'click' && L.Browser.android) {
+ originalHandler = handler;
+ handler = function (e) {
+ return L.DomEvent._filterClick(e, originalHandler);
+ };
+
+ obj.addEventListener(type, handler, false);
+ } else {
+ obj.addEventListener(type, handler, false);
+ }
+
+ } else if ('attachEvent' in obj) {
+ obj.attachEvent('on' + type, handler);
+ }
+
+ obj[key] = handler;
+
+ return this;
+ },
+
+ removeListener: function (obj, type, fn) { // (HTMLElement, String, Function)
+
+ var id = L.stamp(fn),
+ key = '_leaflet_' + type + id,
+ handler = obj[key];
+
+ if (!handler) { return this; }
+
+ if (L.Browser.pointer && type.indexOf('touch') === 0) {
+ this.removePointerListener(obj, type, id);
+ } else if (L.Browser.touch && (type === 'dblclick') && this.removeDoubleTapListener) {
+ this.removeDoubleTapListener(obj, id);
+
+ } else if ('removeEventListener' in obj) {
+
+ if (type === 'mousewheel') {
+ obj.removeEventListener('DOMMouseScroll', handler, false);
+ obj.removeEventListener(type, handler, false);
+
+ } else if ((type === 'mouseenter') || (type === 'mouseleave')) {
+ obj.removeEventListener((type === 'mouseenter' ? 'mouseover' : 'mouseout'), handler, false);
+ } else {
+ obj.removeEventListener(type, handler, false);
+ }
+ } else if ('detachEvent' in obj) {
+ obj.detachEvent('on' + type, handler);
+ }
+
+ obj[key] = null;
+
+ return this;
+ },
+
+ stopPropagation: function (e) {
+
+ if (e.stopPropagation) {
+ e.stopPropagation();
+ } else {
+ e.cancelBubble = true;
+ }
+ L.DomEvent._skipped(e);
+
+ return this;
+ },
+
+ disableScrollPropagation: function (el) {
+ var stop = L.DomEvent.stopPropagation;
+
+ return L.DomEvent
+ .on(el, 'mousewheel', stop)
+ .on(el, 'MozMousePixelScroll', stop);
+ },
+
+ disableClickPropagation: function (el) {
+ var stop = L.DomEvent.stopPropagation;
+
+ for (var i = L.Draggable.START.length - 1; i >= 0; i--) {
+ L.DomEvent.on(el, L.Draggable.START[i], stop);
+ }
+
+ return L.DomEvent
+ .on(el, 'click', L.DomEvent._fakeStop)
+ .on(el, 'dblclick', stop);
+ },
+
+ preventDefault: function (e) {
+
+ if (e.preventDefault) {
+ e.preventDefault();
+ } else {
+ e.returnValue = false;
+ }
+ return this;
+ },
+
+ stop: function (e) {
+ return L.DomEvent
+ .preventDefault(e)
+ .stopPropagation(e);
+ },
+
+ getMousePosition: function (e, container) {
+ if (!container) {
+ return new L.Point(e.clientX, e.clientY);
+ }
+
+ var rect = container.getBoundingClientRect();
+
+ return new L.Point(
+ e.clientX - rect.left - container.clientLeft,
+ e.clientY - rect.top - container.clientTop);
+ },
+
+ getWheelDelta: function (e) {
+
+ var delta = 0;
+
+ if (e.wheelDelta) {
+ delta = e.wheelDelta / 120;
+ }
+ if (e.detail) {
+ delta = -e.detail / 3;
+ }
+ return delta;
+ },
+
+ _skipEvents: {},
+
+ _fakeStop: function (e) {
+ // fakes stopPropagation by setting a special event flag, checked/reset with L.DomEvent._skipped(e)
+ L.DomEvent._skipEvents[e.type] = true;
+ },
+
+ _skipped: function (e) {
+ var skipped = this._skipEvents[e.type];
+ // reset when checking, as it's only used in map container and propagates outside of the map
+ this._skipEvents[e.type] = false;
+ return skipped;
+ },
+
+ // check if element really left/entered the event target (for mouseenter/mouseleave)
+ _checkMouse: function (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);
+ },
+
+ _getEvent: function () { // evil magic for IE
+ /*jshint noarg:false */
+ var e = window.event;
+ if (!e) {
+ var caller = arguments.callee.caller;
+ while (caller) {
+ e = caller['arguments'][0];
+ if (e && window.Event === e.constructor) {
+ break;
+ }
+ caller = caller.caller;
+ }
+ }
+ return e;
+ },
+
+ // this is a horrible workaround for a bug in Android where a single touch triggers two click events
+ _filterClick: function (e, handler) {
+ var timeStamp = (e.timeStamp || e.originalEvent.timeStamp),
+ elapsed = L.DomEvent._lastClick && (timeStamp - L.DomEvent._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)) {
+ L.DomEvent.stop(e);
+ return;
+ }
+ L.DomEvent._lastClick = timeStamp;
+
+ return handler(e);
+ }
+};
+
+L.DomEvent.on = L.DomEvent.addListener;
+L.DomEvent.off = L.DomEvent.removeListener;
+
+
+/*
+ * L.Draggable allows you to add dragging capabilities to any element. Supports mobile devices too.
+ */
+
+L.Draggable = L.Class.extend({
+ includes: L.Mixin.Events,
+
+ statics: {
+ START: L.Browser.touch ? ['touchstart', 'mousedown'] : ['mousedown'],
+ END: {
+ mousedown: 'mouseup',
+ touchstart: 'touchend',
+ pointerdown: 'touchend',
+ MSPointerDown: 'touchend'
+ },
+ MOVE: {
+ mousedown: 'mousemove',
+ touchstart: 'touchmove',
+ pointerdown: 'touchmove',
+ MSPointerDown: 'touchmove'
+ }
+ },
+
+ initialize: function (element, dragStartTarget) {
+ this._element = element;
+ this._dragStartTarget = dragStartTarget || element;
+ },
+
+ enable: function () {
+ if (this._enabled) { return; }
+
+ for (var i = L.Draggable.START.length - 1; i >= 0; i--) {
+ L.DomEvent.on(this._dragStartTarget, L.Draggable.START[i], this._onDown, this);
+ }
+
+ this._enabled = true;
+ },
+
+ disable: function () {
+ if (!this._enabled) { return; }
+
+ for (var i = L.Draggable.START.length - 1; i >= 0; i--) {
+ L.DomEvent.off(this._dragStartTarget, L.Draggable.START[i], this._onDown, this);
+ }
+
+ this._enabled = false;
+ this._moved = false;
+ },
+
+ _onDown: function (e) {
+ this._moved = false;
+
+ if (e.shiftKey || ((e.which !== 1) && (e.button !== 1) && !e.touches)) { return; }
+
+ L.DomEvent.stopPropagation(e);
+
+ if (L.Draggable._disabled) { return; }
+
+ L.DomUtil.disableImageDrag();
+ L.DomUtil.disableTextSelection();
+
+ if (this._moving) { return; }
+
+ var first = e.touches ? e.touches[0] : e;
+
+ this._startPoint = new L.Point(first.clientX, first.clientY);
+ this._startPos = this._newPos = L.DomUtil.getPosition(this._element);
+
+ L.DomEvent
+ .on(document, L.Draggable.MOVE[e.type], this._onMove, this)
+ .on(document, L.Draggable.END[e.type], this._onUp, this);
+ },
+
+ _onMove: function (e) {
+ if (e.touches && e.touches.length > 1) {
+ this._moved = true;
+ return;
+ }
+
+ var first = (e.touches && e.touches.length === 1 ? e.touches[0] : e),
+ newPoint = new L.Point(first.clientX, first.clientY),
+ offset = newPoint.subtract(this._startPoint);
+
+ if (!offset.x && !offset.y) { return; }
+ if (L.Browser.touch && Math.abs(offset.x) + Math.abs(offset.y) < 3) { return; }
+
+ L.DomEvent.preventDefault(e);
+
+ if (!this._moved) {
+ this.fire('dragstart');
+
+ this._moved = true;
+ this._startPos = L.DomUtil.getPosition(this._element).subtract(offset);
+
+ L.DomUtil.addClass(document.body, 'leaflet-dragging');
+ this._lastTarget = e.target || e.srcElement;
+ L.DomUtil.addClass(this._lastTarget, 'leaflet-drag-target');
+ }
+
+ this._newPos = this._startPos.add(offset);
+ this._moving = true;
+
+ L.Util.cancelAnimFrame(this._animRequest);
+ this._animRequest = L.Util.requestAnimFrame(this._updatePosition, this, true, this._dragStartTarget);
+ },
+
+ _updatePosition: function () {
+ this.fire('predrag');
+ L.DomUtil.setPosition(this._element, this._newPos);
+ this.fire('drag');
+ },
+
+ _onUp: function () {
+ L.DomUtil.removeClass(document.body, 'leaflet-dragging');
+
+ if (this._lastTarget) {
+ L.DomUtil.removeClass(this._lastTarget, 'leaflet-drag-target');
+ this._lastTarget = null;
+ }
+
+ for (var i in L.Draggable.MOVE) {
+ L.DomEvent
+ .off(document, L.Draggable.MOVE[i], this._onMove)
+ .off(document, L.Draggable.END[i], this._onUp);
+ }
+
+ L.DomUtil.enableImageDrag();
+ L.DomUtil.enableTextSelection();
+
+ if (this._moved && this._moving) {
+ // ensure drag is not fired after dragend
+ L.Util.cancelAnimFrame(this._animRequest);
+
+ this.fire('dragend', {
+ distance: this._newPos.distanceTo(this._startPos)
+ });
+ }
+
+ this._moving = false;
+ }
+});
+
+
+/*
+ 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.
+*/
+
+L.Handler = L.Class.extend({
+ initialize: function (map) {
+ this._map = map;
+ },
+
+ enable: function () {
+ if (this._enabled) { return; }
+
+ this._enabled = true;
+ this.addHooks();
+ },
+
+ disable: function () {
+ if (!this._enabled) { return; }
+
+ this._enabled = false;
+ this.removeHooks();
+ },
+
+ enabled: function () {
+ return !!this._enabled;
+ }
+});
+
+
+/*
+ * L.Handler.MapDrag is used to make the map draggable (with panning inertia), enabled by default.
+ */
+
+L.Map.mergeOptions({
+ dragging: true,
+
+ inertia: !L.Browser.android23,
+ inertiaDeceleration: 3400, // px/s^2
+ inertiaMaxSpeed: Infinity, // px/s
+ inertiaThreshold: L.Browser.touch ? 32 : 18, // ms
+ easeLinearity: 0.25,
+
+ // TODO refactor, move to CRS
+ worldCopyJump: false
+});
+
+L.Map.Drag = L.Handler.extend({
+ addHooks: function () {
+ if (!this._draggable) {
+ var map = this._map;
+
+ this._draggable = new L.Draggable(map._mapPane, map._container);
+
+ this._draggable.on({
+ 'dragstart': this._onDragStart,
+ 'drag': this._onDrag,
+ 'dragend': this._onDragEnd
+ }, this);
+
+ if (map.options.worldCopyJump) {
+ this._draggable.on('predrag', this._onPreDrag, this);
+ map.on('viewreset', this._onViewReset, this);
+
+ map.whenReady(this._onViewReset, this);
+ }
+ }
+ this._draggable.enable();
+ },
+
+ removeHooks: function () {
+ this._draggable.disable();
+ },
+
+ moved: function () {
+ return this._draggable && this._draggable._moved;
+ },
+
+ _onDragStart: function () {
+ var map = this._map;
+
+ if (map._panAnim) {
+ map._panAnim.stop();
+ }
+
+ map
+ .fire('movestart')
+ .fire('dragstart');
+
+ if (map.options.inertia) {
+ this._positions = [];
+ this._times = [];
+ }
+ },
+
+ _onDrag: function () {
+ if (this._map.options.inertia) {
+ var time = this._lastTime = +new Date(),
+ pos = this._lastPos = this._draggable._newPos;
+
+ this._positions.push(pos);
+ this._times.push(time);
+
+ if (time - this._times[0] > 200) {
+ this._positions.shift();
+ this._times.shift();
+ }
+ }
+
+ this._map
+ .fire('move')
+ .fire('drag');
+ },
+
+ _onViewReset: function () {
+ // TODO fix hardcoded Earth values
+ var pxCenter = this._map.getSize()._divideBy(2),
+ pxWorldCenter = this._map.latLngToLayerPoint([0, 0]);
+
+ this._initialWorldOffset = pxWorldCenter.subtract(pxCenter).x;
+ this._worldWidth = this._map.project([0, 180]).x;
+ },
+
+ _onPreDrag: function () {
+ // TODO refactor to be able to adjust map pane position after zoom
+ var worldWidth = this._worldWidth,
+ halfWidth = Math.round(worldWidth / 2),
+ dx = this._initialWorldOffset,
+ x = this._draggable._newPos.x,
+ newX1 = (x - halfWidth + dx) % worldWidth + halfWidth - dx,
+ newX2 = (x + halfWidth + dx) % worldWidth - halfWidth - dx,
+ newX = Math.abs(newX1 + dx) < Math.abs(newX2 + dx) ? newX1 : newX2;
+
+ this._draggable._newPos.x = newX;
+ },
+
+ _onDragEnd: function (e) {
+ var map = this._map,
+ options = map.options,
+ delay = +new Date() - this._lastTime,
+
+ noInertia = !options.inertia || delay > options.inertiaThreshold || !this._positions[0];
+
+ map.fire('dragend', e);
+
+ if (noInertia) {
+ map.fire('moveend');
+
+ } else {
+
+ var direction = this._lastPos.subtract(this._positions[0]),
+ duration = (this._lastTime + delay - this._times[0]) / 1000,
+ ease = options.easeLinearity,
+
+ speedVector = direction.multiplyBy(ease / duration),
+ speed = speedVector.distanceTo([0, 0]),
+
+ limitedSpeed = Math.min(options.inertiaMaxSpeed, speed),
+ limitedSpeedVector = speedVector.multiplyBy(limitedSpeed / speed),
+
+ decelerationDuration = limitedSpeed / (options.inertiaDeceleration * ease),
+ offset = limitedSpeedVector.multiplyBy(-decelerationDuration / 2).round();
+
+ if (!offset.x || !offset.y) {
+ map.fire('moveend');
+
+ } else {
+ offset = map._limitOffset(offset, map.options.maxBounds);
+
+ L.Util.requestAnimFrame(function () {
+ map.panBy(offset, {
+ duration: decelerationDuration,
+ easeLinearity: ease,
+ noMoveStart: true
+ });
+ });
+ }
+ }
+ }
+});
+
+L.Map.addInitHook('addHandler', 'dragging', L.Map.Drag);
+
+
+/*
+ * L.Handler.DoubleClickZoom is used to handle double-click zoom on the map, enabled by default.
+ */
+
+L.Map.mergeOptions({
+ doubleClickZoom: true
+});
+
+L.Map.DoubleClickZoom = L.Handler.extend({
+ addHooks: function () {
+ this._map.on('dblclick', this._onDoubleClick, this);
+ },
+
+ removeHooks: function () {
+ this._map.off('dblclick', this._onDoubleClick, this);
+ },
+
+ _onDoubleClick: function (e) {
+ var map = this._map,
+ zoom = map.getZoom() + (e.originalEvent.shiftKey ? -1 : 1);
+
+ if (map.options.doubleClickZoom === 'center') {
+ map.setZoom(zoom);
+ } else {
+ map.setZoomAround(e.containerPoint, zoom);
+ }
+ }
+});
+
+L.Map.addInitHook('addHandler', 'doubleClickZoom', L.Map.DoubleClickZoom);
+
+
+/*
+ * L.Handler.ScrollWheelZoom is used by L.Map to enable mouse scroll wheel zoom on the map.
+ */
+
+L.Map.mergeOptions({
+ scrollWheelZoom: true
+});
+
+L.Map.ScrollWheelZoom = L.Handler.extend({
+ addHooks: function () {
+ L.DomEvent.on(this._map._container, 'mousewheel', this._onWheelScroll, this);
+ L.DomEvent.on(this._map._container, 'MozMousePixelScroll', L.DomEvent.preventDefault);
+ this._delta = 0;
+ },
+
+ removeHooks: function () {
+ L.DomEvent.off(this._map._container, 'mousewheel', this._onWheelScroll);
+ L.DomEvent.off(this._map._container, 'MozMousePixelScroll', L.DomEvent.preventDefault);
+ },
+
+ _onWheelScroll: function (e) {
+ var delta = L.DomEvent.getWheelDelta(e);
+
+ this._delta += delta;
+ this._lastMousePos = this._map.mouseEventToContainerPoint(e);
+
+ if (!this._startTime) {
+ this._startTime = +new Date();
+ }
+
+ var left = Math.max(40 - (+new Date() - this._startTime), 0);
+
+ clearTimeout(this._timer);
+ this._timer = setTimeout(L.bind(this._performZoom, this), left);
+
+ L.DomEvent.preventDefault(e);
+ L.DomEvent.stopPropagation(e);
+ },
+
+ _performZoom: function () {
+ var map = this._map,
+ delta = this._delta,
+ zoom = map.getZoom();
+
+ delta = delta > 0 ? Math.ceil(delta) : Math.floor(delta);
+ delta = Math.max(Math.min(delta, 4), -4);
+ delta = map._limitZoom(zoom + delta) - zoom;
+
+ this._delta = 0;
+ this._startTime = null;
+
+ if (!delta) { return; }
+
+ if (map.options.scrollWheelZoom === 'center') {
+ map.setZoom(zoom + delta);
+ } else {
+ map.setZoomAround(this._lastMousePos, zoom + delta);
+ }
+ }
+});
+
+L.Map.addInitHook('addHandler', 'scrollWheelZoom', L.Map.ScrollWheelZoom);
+
+
+/*
+ * Extends the event handling code with double tap support for mobile browsers.
+ */
+
+L.extend(L.DomEvent, {
+
+ _touchstart: L.Browser.msPointer ? 'MSPointerDown' : L.Browser.pointer ? 'pointerdown' : 'touchstart',
+ _touchend: L.Browser.msPointer ? 'MSPointerUp' : L.Browser.pointer ? 'pointerup' : 'touchend',
+
+ // inspired by Zepto touch code by Thomas Fuchs
+ addDoubleTapListener: function (obj, handler, id) {
+ var last,
+ doubleTap = false,
+ delay = 250,
+ touch,
+ pre = '_leaflet_',
+ touchstart = this._touchstart,
+ touchend = this._touchend,
+ trackedTouches = [];
+
+ function onTouchStart(e) {
+ var count;
+
+ if (L.Browser.pointer) {
+ trackedTouches.push(e.pointerId);
+ count = trackedTouches.length;
+ } else {
+ count = e.touches.length;
+ }
+ if (count > 1) {
+ return;
+ }
+
+ var now = Date.now(),
+ delta = now - (last || now);
+
+ touch = e.touches ? e.touches[0] : e;
+ doubleTap = (delta > 0 && delta <= delay);
+ last = now;
+ }
+
+ function onTouchEnd(e) {
+ if (L.Browser.pointer) {
+ var idx = trackedTouches.indexOf(e.pointerId);
+ if (idx === -1) {
+ return;
+ }
+ trackedTouches.splice(idx, 1);
+ }
+
+ if (doubleTap) {
+ if (L.Browser.pointer) {
+ // work around .type being readonly with MSPointer* events
+ var newTouch = { },
+ prop;
+
+ // jshint forin:false
+ for (var i in touch) {
+ prop = touch[i];
+ if (typeof prop === 'function') {
+ newTouch[i] = prop.bind(touch);
+ } else {
+ newTouch[i] = prop;
+ }
+ }
+ touch = newTouch;
+ }
+ touch.type = 'dblclick';
+ handler(touch);
+ last = null;
+ }
+ }
+ obj[pre + touchstart + id] = onTouchStart;
+ obj[pre + touchend + id] = onTouchEnd;
+
+ // on pointer we need to listen on the document, otherwise a drag starting on the map and moving off screen
+ // will not come through to us, so we will lose track of how many touches are ongoing
+ var endElement = L.Browser.pointer ? document.documentElement : obj;
+
+ obj.addEventListener(touchstart, onTouchStart, false);
+ endElement.addEventListener(touchend, onTouchEnd, false);
+
+ if (L.Browser.pointer) {
+ endElement.addEventListener(L.DomEvent.POINTER_CANCEL, onTouchEnd, false);
+ }
+
+ return this;
+ },
+
+ removeDoubleTapListener: function (obj, id) {
+ var pre = '_leaflet_';
+
+ obj.removeEventListener(this._touchstart, obj[pre + this._touchstart + id], false);
+ (L.Browser.pointer ? document.documentElement : obj).removeEventListener(
+ this._touchend, obj[pre + this._touchend + id], false);
+
+ if (L.Browser.pointer) {
+ document.documentElement.removeEventListener(L.DomEvent.POINTER_CANCEL, obj[pre + this._touchend + id],
+ false);
+ }
+
+ return this;
+ }
+});
+
+
+/*
+ * Extends L.DomEvent to provide touch support for Internet Explorer and Windows-based devices.
+ */
+
+L.extend(L.DomEvent, {
+
+ //static
+ POINTER_DOWN: L.Browser.msPointer ? 'MSPointerDown' : 'pointerdown',
+ POINTER_MOVE: L.Browser.msPointer ? 'MSPointerMove' : 'pointermove',
+ POINTER_UP: L.Browser.msPointer ? 'MSPointerUp' : 'pointerup',
+ POINTER_CANCEL: L.Browser.msPointer ? 'MSPointerCancel' : 'pointercancel',
+
+ _pointers: [],
+ _pointerDocumentListener: false,
+
+ // Provides a touch events wrapper for (ms)pointer events.
+ // Based on changes by veproza https://github.com/CloudMade/Leaflet/pull/1019
+ //ref http://www.w3.org/TR/pointerevents/ https://www.w3.org/Bugs/Public/show_bug.cgi?id=22890
+
+ addPointerListener: function (obj, type, handler, id) {
+
+ switch (type) {
+ case 'touchstart':
+ return this.addPointerListenerStart(obj, type, handler, id);
+ case 'touchend':
+ return this.addPointerListenerEnd(obj, type, handler, id);
+ case 'touchmove':
+ return this.addPointerListenerMove(obj, type, handler, id);
+ default:
+ throw 'Unknown touch event type';
+ }
+ },
+
+ addPointerListenerStart: function (obj, type, handler, id) {
+ var pre = '_leaflet_',
+ pointers = this._pointers;
+
+ var cb = function (e) {
+
+ L.DomEvent.preventDefault(e);
+
+ var alreadyInArray = false;
+ for (var i = 0; i < pointers.length; i++) {
+ if (pointers[i].pointerId === e.pointerId) {
+ alreadyInArray = true;
+ break;
+ }
+ }
+ if (!alreadyInArray) {
+ pointers.push(e);
+ }
+
+ e.touches = pointers.slice();
+ e.changedTouches = [e];
+
+ handler(e);
+ };
+
+ obj[pre + 'touchstart' + id] = cb;
+ obj.addEventListener(this.POINTER_DOWN, cb, false);
+
+ // need to also listen for end events to keep the _pointers list accurate
+ // this needs to be on the body and never go away
+ if (!this._pointerDocumentListener) {
+ var internalCb = function (e) {
+ for (var i = 0; i < pointers.length; i++) {
+ if (pointers[i].pointerId === e.pointerId) {
+ pointers.splice(i, 1);
+ break;
+ }
+ }
+ };
+ //We listen on the documentElement as any drags that end by moving the touch off the screen get fired there
+ document.documentElement.addEventListener(this.POINTER_UP, internalCb, false);
+ document.documentElement.addEventListener(this.POINTER_CANCEL, internalCb, false);
+
+ this._pointerDocumentListener = true;
+ }
+
+ return this;
+ },
+
+ addPointerListenerMove: function (obj, type, handler, id) {
+ var pre = '_leaflet_',
+ touches = this._pointers;
+
+ function cb(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; }
+
+ for (var i = 0; i < touches.length; i++) {
+ if (touches[i].pointerId === e.pointerId) {
+ touches[i] = e;
+ break;
+ }
+ }
+
+ e.touches = touches.slice();
+ e.changedTouches = [e];
+
+ handler(e);
+ }
+
+ obj[pre + 'touchmove' + id] = cb;
+ obj.addEventListener(this.POINTER_MOVE, cb, false);
+
+ return this;
+ },
+
+ addPointerListenerEnd: function (obj, type, handler, id) {
+ var pre = '_leaflet_',
+ touches = this._pointers;
+
+ var cb = function (e) {
+ for (var i = 0; i < touches.length; i++) {
+ if (touches[i].pointerId === e.pointerId) {
+ touches.splice(i, 1);
+ break;
+ }
+ }
+
+ e.touches = touches.slice();
+ e.changedTouches = [e];
+
+ handler(e);
+ };
+
+ obj[pre + 'touchend' + id] = cb;
+ obj.addEventListener(this.POINTER_UP, cb, false);
+ obj.addEventListener(this.POINTER_CANCEL, cb, false);
+
+ return this;
+ },
+
+ removePointerListener: function (obj, type, id) {
+ var pre = '_leaflet_',
+ cb = obj[pre + type + id];
+
+ switch (type) {
+ case 'touchstart':
+ obj.removeEventListener(this.POINTER_DOWN, cb, false);
+ break;
+ case 'touchmove':
+ obj.removeEventListener(this.POINTER_MOVE, cb, false);
+ break;
+ case 'touchend':
+ obj.removeEventListener(this.POINTER_UP, cb, false);
+ obj.removeEventListener(this.POINTER_CANCEL, cb, false);
+ break;
+ }
+
+ return this;
+ }
+});
+
+
+/*
+ * L.Handler.TouchZoom is used by L.Map to add pinch zoom on supported mobile browsers.
+ */
+
+L.Map.mergeOptions({
+ touchZoom: L.Browser.touch && !L.Browser.android23,
+ bounceAtZoomLimits: true
+});
+
+L.Map.TouchZoom = L.Handler.extend({
+ addHooks: function () {
+ L.DomEvent.on(this._map._container, 'touchstart', this._onTouchStart, this);
+ },
+
+ removeHooks: function () {
+ L.DomEvent.off(this._map._container, 'touchstart', this._onTouchStart, this);
+ },
+
+ _onTouchStart: function (e) {
+ var map = this._map;
+
+ if (!e.touches || e.touches.length !== 2 || map._animatingZoom || this._zooming) { return; }
+
+ var p1 = map.mouseEventToLayerPoint(e.touches[0]),
+ p2 = map.mouseEventToLayerPoint(e.touches[1]),
+ viewCenter = map._getCenterLayerPoint();
+
+ this._startCenter = p1.add(p2)._divideBy(2);
+ this._startDist = p1.distanceTo(p2);
+
+ this._moved = false;
+ this._zooming = true;
+
+ this._centerOffset = viewCenter.subtract(this._startCenter);
+
+ if (map._panAnim) {
+ map._panAnim.stop();
+ }
+
+ L.DomEvent
+ .on(document, 'touchmove', this._onTouchMove, this)
+ .on(document, 'touchend', this._onTouchEnd, this);
+
+ L.DomEvent.preventDefault(e);
+ },
+
+ _onTouchMove: function (e) {
+ var map = this._map;
+
+ if (!e.touches || e.touches.length !== 2 || !this._zooming) { return; }
+
+ var p1 = map.mouseEventToLayerPoint(e.touches[0]),
+ p2 = map.mouseEventToLayerPoint(e.touches[1]);
+
+ this._scale = p1.distanceTo(p2) / this._startDist;
+ this._delta = p1._add(p2)._divideBy(2)._subtract(this._startCenter);
+
+ if (this._scale === 1) { return; }
+
+ if (!map.options.bounceAtZoomLimits) {
+ if ((map.getZoom() === map.getMinZoom() && this._scale < 1) ||
+ (map.getZoom() === map.getMaxZoom() && this._scale > 1)) { return; }
+ }
+
+ if (!this._moved) {
+ L.DomUtil.addClass(map._mapPane, 'leaflet-touching');
+
+ map
+ .fire('movestart')
+ .fire('zoomstart');
+
+ this._moved = true;
+ }
+
+ L.Util.cancelAnimFrame(this._animRequest);
+ this._animRequest = L.Util.requestAnimFrame(
+ this._updateOnMove, this, true, this._map._container);
+
+ L.DomEvent.preventDefault(e);
+ },
+
+ _updateOnMove: function () {
+ var map = this._map,
+ origin = this._getScaleOrigin(),
+ center = map.layerPointToLatLng(origin),
+ zoom = map.getScaleZoom(this._scale);
+
+ map._animateZoom(center, zoom, this._startCenter, this._scale, this._delta, false, true);
+ },
+
+ _onTouchEnd: function () {
+ if (!this._moved || !this._zooming) {
+ this._zooming = false;
+ return;
+ }
+
+ var map = this._map;
+
+ this._zooming = false;
+ L.DomUtil.removeClass(map._mapPane, 'leaflet-touching');
+ L.Util.cancelAnimFrame(this._animRequest);
+
+ L.DomEvent
+ .off(document, 'touchmove', this._onTouchMove)
+ .off(document, 'touchend', this._onTouchEnd);
+
+ var origin = this._getScaleOrigin(),
+ center = map.layerPointToLatLng(origin),
+
+ oldZoom = map.getZoom(),
+ floatZoomDelta = map.getScaleZoom(this._scale) - oldZoom,
+ roundZoomDelta = (floatZoomDelta > 0 ?
+ Math.ceil(floatZoomDelta) : Math.floor(floatZoomDelta)),
+
+ zoom = map._limitZoom(oldZoom + roundZoomDelta),
+ scale = map.getZoomScale(zoom) / this._scale;
+
+ map._animateZoom(center, zoom, origin, scale);
+ },
+
+ _getScaleOrigin: function () {
+ var centerOffset = this._centerOffset.subtract(this._delta).divideBy(this._scale);
+ return this._startCenter.add(centerOffset);
+ }
+});
+
+L.Map.addInitHook('addHandler', 'touchZoom', L.Map.TouchZoom);
+
+
+/*
+ * L.Map.Tap is used to enable mobile hacks like quick taps and long hold.
+ */
+
+L.Map.mergeOptions({
+ tap: true,
+ tapTolerance: 15
+});
+
+L.Map.Tap = L.Handler.extend({
+ addHooks: function () {
+ L.DomEvent.on(this._map._container, 'touchstart', this._onDown, this);
+ },
+
+ removeHooks: function () {
+ L.DomEvent.off(this._map._container, 'touchstart', this._onDown, this);
+ },
+
+ _onDown: function (e) {
+ if (!e.touches) { return; }
+
+ L.DomEvent.preventDefault(e);
+
+ this._fireClick = true;
+
+ // don't simulate click or track longpress if more than 1 touch
+ if (e.touches.length > 1) {
+ this._fireClick = false;
+ clearTimeout(this._holdTimeout);
+ return;
+ }
+
+ var first = e.touches[0],
+ el = first.target;
+
+ this._startPos = this._newPos = new L.Point(first.clientX, first.clientY);
+
+ // if touching a link, highlight it
+ if (el.tagName && el.tagName.toLowerCase() === 'a') {
+ L.DomUtil.addClass(el, 'leaflet-active');
+ }
+
+ // simulate long hold but setting a timeout
+ this._holdTimeout = setTimeout(L.bind(function () {
+ if (this._isTapValid()) {
+ this._fireClick = false;
+ this._onUp();
+ this._simulateEvent('contextmenu', first);
+ }
+ }, this), 1000);
+
+ L.DomEvent
+ .on(document, 'touchmove', this._onMove, this)
+ .on(document, 'touchend', this._onUp, this);
+ },
+
+ _onUp: function (e) {
+ clearTimeout(this._holdTimeout);
+
+ L.DomEvent
+ .off(document, 'touchmove', this._onMove, this)
+ .off(document, 'touchend', this._onUp, this);
+
+ if (this._fireClick && e && e.changedTouches) {
+
+ var first = e.changedTouches[0],
+ el = first.target;
+
+ if (el && el.tagName && el.tagName.toLowerCase() === 'a') {
+ L.DomUtil.removeClass(el, 'leaflet-active');
+ }
+
+ // simulate click if the touch didn't move too much
+ if (this._isTapValid()) {
+ this._simulateEvent('click', first);
+ }
+ }
+ },
+
+ _isTapValid: function () {
+ return this._newPos.distanceTo(this._startPos) <= this._map.options.tapTolerance;
+ },
+
+ _onMove: function (e) {
+ var first = e.touches[0];
+ this._newPos = new L.Point(first.clientX, first.clientY);
+ },
+
+ _simulateEvent: function (type, e) {
+ var simulatedEvent = document.createEvent('MouseEvents');
+
+ simulatedEvent._simulated = true;
+ e.target._simulatedClick = true;
+
+ simulatedEvent.initMouseEvent(
+ type, true, true, window, 1,
+ e.screenX, e.screenY,
+ e.clientX, e.clientY,
+ false, false, false, false, 0, null);
+
+ e.target.dispatchEvent(simulatedEvent);
+ }
+});
+
+if (L.Browser.touch && !L.Browser.pointer) {
+ L.Map.addInitHook('addHandler', 'tap', L.Map.Tap);
+}
+
+
+/*
+ * L.Handler.ShiftDragZoom is used to add shift-drag zoom interaction to the map
+ * (zoom to a selected bounding box), enabled by default.
+ */
+
+L.Map.mergeOptions({
+ boxZoom: true
+});
+
+L.Map.BoxZoom = L.Handler.extend({
+ initialize: function (map) {
+ this._map = map;
+ this._container = map._container;
+ this._pane = map._panes.overlayPane;
+ this._moved = false;
+ },
+
+ addHooks: function () {
+ L.DomEvent.on(this._container, 'mousedown', this._onMouseDown, this);
+ },
+
+ removeHooks: function () {
+ L.DomEvent.off(this._container, 'mousedown', this._onMouseDown);
+ this._moved = false;
+ },
+
+ moved: function () {
+ return this._moved;
+ },
+
+ _onMouseDown: function (e) {
+ this._moved = false;
+
+ if (!e.shiftKey || ((e.which !== 1) && (e.button !== 1))) { return false; }
+
+ L.DomUtil.disableTextSelection();
+ L.DomUtil.disableImageDrag();
+
+ this._startLayerPoint = this._map.mouseEventToLayerPoint(e);
+
+ L.DomEvent
+ .on(document, 'mousemove', this._onMouseMove, this)
+ .on(document, 'mouseup', this._onMouseUp, this)
+ .on(document, 'keydown', this._onKeyDown, this);
+ },
+
+ _onMouseMove: function (e) {
+ if (!this._moved) {
+ this._box = L.DomUtil.create('div', 'leaflet-zoom-box', this._pane);
+ L.DomUtil.setPosition(this._box, this._startLayerPoint);
+
+ //TODO refactor: move cursor to styles
+ this._container.style.cursor = 'crosshair';
+ this._map.fire('boxzoomstart');
+ }
+
+ var startPoint = this._startLayerPoint,
+ box = this._box,
+
+ layerPoint = this._map.mouseEventToLayerPoint(e),
+ offset = layerPoint.subtract(startPoint),
+
+ newPos = new L.Point(
+ Math.min(layerPoint.x, startPoint.x),
+ Math.min(layerPoint.y, startPoint.y));
+
+ L.DomUtil.setPosition(box, newPos);
+
+ this._moved = true;
+
+ // TODO refactor: remove hardcoded 4 pixels
+ box.style.width = (Math.max(0, Math.abs(offset.x) - 4)) + 'px';
+ box.style.height = (Math.max(0, Math.abs(offset.y) - 4)) + 'px';
+ },
+
+ _finish: function () {
+ if (this._moved) {
+ this._pane.removeChild(this._box);
+ this._container.style.cursor = '';
+ }
+
+ L.DomUtil.enableTextSelection();
+ L.DomUtil.enableImageDrag();
+
+ L.DomEvent
+ .off(document, 'mousemove', this._onMouseMove)
+ .off(document, 'mouseup', this._onMouseUp)
+ .off(document, 'keydown', this._onKeyDown);
+ },
+
+ _onMouseUp: function (e) {
+
+ this._finish();
+
+ var map = this._map,
+ layerPoint = map.mouseEventToLayerPoint(e);
+
+ if (this._startLayerPoint.equals(layerPoint)) { return; }
+
+ var bounds = new L.LatLngBounds(
+ map.layerPointToLatLng(this._startLayerPoint),
+ map.layerPointToLatLng(layerPoint));
+
+ map.fitBounds(bounds);
+
+ map.fire('boxzoomend', {
+ boxZoomBounds: bounds
+ });
+ },
+
+ _onKeyDown: function (e) {
+ if (e.keyCode === 27) {
+ this._finish();
+ }
+ }
+});
+
+L.Map.addInitHook('addHandler', 'boxZoom', L.Map.BoxZoom);
+
+
+/*
+ * L.Map.Keyboard is handling keyboard interaction with the map, enabled by default.
+ */
+
+L.Map.mergeOptions({
+ keyboard: true,
+ keyboardPanOffset: 80,
+ keyboardZoomOffset: 1
+});
+
+L.Map.Keyboard = L.Handler.extend({
+
+ keyCodes: {
+ left: [37],
+ right: [39],
+ down: [40],
+ up: [38],
+ zoomIn: [187, 107, 61, 171],
+ zoomOut: [189, 109, 173]
+ },
+
+ initialize: function (map) {
+ this._map = map;
+
+ this._setPanOffset(map.options.keyboardPanOffset);
+ this._setZoomOffset(map.options.keyboardZoomOffset);
+ },
+
+ addHooks: function () {
+ var container = this._map._container;
+
+ // make the container focusable by tabbing
+ if (container.tabIndex === -1) {
+ container.tabIndex = '0';
+ }
+
+ L.DomEvent
+ .on(container, 'focus', this._onFocus, this)
+ .on(container, 'blur', this._onBlur, this)
+ .on(container, 'mousedown', this._onMouseDown, this);
+
+ this._map
+ .on('focus', this._addHooks, this)
+ .on('blur', this._removeHooks, this);
+ },
+
+ removeHooks: function () {
+ this._removeHooks();
+
+ var container = this._map._container;
+
+ L.DomEvent
+ .off(container, 'focus', this._onFocus, this)
+ .off(container, 'blur', this._onBlur, this)
+ .off(container, 'mousedown', this._onMouseDown, this);
+
+ this._map
+ .off('focus', this._addHooks, this)
+ .off('blur', this._removeHooks, this);
+ },
+
+ _onMouseDown: function () {
+ if (this._focused) { return; }
+
+ var body = document.body,
+ docEl = document.documentElement,
+ top = body.scrollTop || docEl.scrollTop,
+ left = body.scrollLeft || docEl.scrollLeft;
+
+ this._map._container.focus();
+
+ window.scrollTo(left, top);
+ },
+
+ _onFocus: function () {
+ this._focused = true;
+ this._map.fire('focus');
+ },
+
+ _onBlur: function () {
+ this._focused = false;
+ this._map.fire('blur');
+ },
+
+ _setPanOffset: function (pan) {
+ var keys = this._panKeys = {},
+ codes = this.keyCodes,
+ i, len;
+
+ for (i = 0, len = codes.left.length; i < len; i++) {
+ keys[codes.left[i]] = [-1 * pan, 0];
+ }
+ for (i = 0, len = codes.right.length; i < len; i++) {
+ keys[codes.right[i]] = [pan, 0];
+ }
+ for (i = 0, len = codes.down.length; i < len; i++) {
+ keys[codes.down[i]] = [0, pan];
+ }
+ for (i = 0, len = codes.up.length; i < len; i++) {
+ keys[codes.up[i]] = [0, -1 * pan];
+ }
+ },
+
+ _setZoomOffset: function (zoom) {
+ var keys = this._zoomKeys = {},
+ codes = this.keyCodes,
+ i, len;
+
+ for (i = 0, len = codes.zoomIn.length; i < len; i++) {
+ keys[codes.zoomIn[i]] = zoom;
+ }
+ for (i = 0, len = codes.zoomOut.length; i < len; i++) {
+ keys[codes.zoomOut[i]] = -zoom;
+ }
+ },
+
+ _addHooks: function () {
+ L.DomEvent.on(document, 'keydown', this._onKeyDown, this);
+ },
+
+ _removeHooks: function () {
+ L.DomEvent.off(document, 'keydown', this._onKeyDown, this);
+ },
+
+ _onKeyDown: function (e) {
+ var key = e.keyCode,
+ map = this._map;
+
+ if (key in this._panKeys) {
+
+ if (map._panAnim && map._panAnim._inProgress) { return; }
+
+ map.panBy(this._panKeys[key]);
+
+ if (map.options.maxBounds) {
+ map.panInsideBounds(map.options.maxBounds);
+ }
+
+ } else if (key in this._zoomKeys) {
+ map.setZoom(map.getZoom() + this._zoomKeys[key]);
+
+ } else {
+ return;
+ }
+
+ L.DomEvent.stop(e);
+ }
+});
+
+L.Map.addInitHook('addHandler', 'keyboard', L.Map.Keyboard);
+
+
+/*
+ * L.Handler.MarkerDrag is used internally by L.Marker to make the markers draggable.
+ */
+
+L.Handler.MarkerDrag = L.Handler.extend({
+ initialize: function (marker) {
+ this._marker = marker;
+ },
+
+ addHooks: function () {
+ var icon = this._marker._icon;
+ if (!this._draggable) {
+ this._draggable = new L.Draggable(icon, icon);
+ }
+
+ this._draggable
+ .on('dragstart', this._onDragStart, this)
+ .on('drag', this._onDrag, this)
+ .on('dragend', this._onDragEnd, this);
+ this._draggable.enable();
+ L.DomUtil.addClass(this._marker._icon, 'leaflet-marker-draggable');
+ },
+
+ removeHooks: function () {
+ this._draggable
+ .off('dragstart', this._onDragStart, this)
+ .off('drag', this._onDrag, this)
+ .off('dragend', this._onDragEnd, this);
+
+ this._draggable.disable();
+ L.DomUtil.removeClass(this._marker._icon, 'leaflet-marker-draggable');
+ },
+
+ moved: function () {
+ return this._draggable && this._draggable._moved;
+ },
+
+ _onDragStart: function () {
+ this._marker
+ .closePopup()
+ .fire('movestart')
+ .fire('dragstart');
+ },
+
+ _onDrag: function () {
+ var marker = this._marker,
+ shadow = marker._shadow,
+ iconPos = L.DomUtil.getPosition(marker._icon),
+ latlng = marker._map.layerPointToLatLng(iconPos);
+
+ // update shadow position
+ if (shadow) {
+ L.DomUtil.setPosition(shadow, iconPos);
+ }
+
+ marker._latlng = latlng;
+
+ marker
+ .fire('move', {latlng: latlng})
+ .fire('drag');
+ },
+
+ _onDragEnd: function (e) {
+ this._marker
+ .fire('moveend')
+ .fire('dragend', e);
+ }
+});
+
+
+/*
+ * L.Control is a base class for implementing map controls. Handles positioning.
+ * All other controls extend from this class.
+ */
+
+L.Control = L.Class.extend({
+ options: {
+ position: 'topright'
+ },
+
+ initialize: function (options) {
+ L.setOptions(this, options);
+ },
+
+ getPosition: function () {
+ return this.options.position;
+ },
+
+ setPosition: function (position) {
+ var map = this._map;
+
+ if (map) {
+ map.removeControl(this);
+ }
+
+ this.options.position = position;
+
+ if (map) {
+ map.addControl(this);
+ }
+
+ return this;
+ },
+
+ getContainer: function () {
+ return this._container;
+ },
+
+ addTo: function (map) {
+ this._map = map;
+
+ var container = this._container = this.onAdd(map),
+ pos = this.getPosition(),
+ corner = map._controlCorners[pos];
+
+ L.DomUtil.addClass(container, 'leaflet-control');
+
+ if (pos.indexOf('bottom') !== -1) {
+ corner.insertBefore(container, corner.firstChild);
+ } else {
+ corner.appendChild(container);
+ }
+
+ return this;
+ },
+
+ removeFrom: function (map) {
+ var pos = this.getPosition(),
+ corner = map._controlCorners[pos];
+
+ corner.removeChild(this._container);
+ this._map = null;
+
+ if (this.onRemove) {
+ this.onRemove(map);
+ }
+
+ return this;
+ },
+
+ _refocusOnMap: function () {
+ if (this._map) {
+ this._map.getContainer().focus();
+ }
+ }
+});
+
+L.control = function (options) {
+ return new L.Control(options);
+};
+
+
+// adds control-related methods to L.Map
+
+L.Map.include({
+ addControl: function (control) {
+ control.addTo(this);
+ return this;
+ },
+
+ removeControl: function (control) {
+ control.removeFrom(this);
+ return this;
+ },
+
+ _initControlPos: function () {
+ var corners = this._controlCorners = {},
+ l = 'leaflet-',
+ container = this._controlContainer =
+ L.DomUtil.create('div', l + 'control-container', this._container);
+
+ function createCorner(vSide, hSide) {
+ var className = l + vSide + ' ' + l + hSide;
+
+ corners[vSide + hSide] = L.DomUtil.create('div', className, container);
+ }
+
+ createCorner('top', 'left');
+ createCorner('top', 'right');
+ createCorner('bottom', 'left');
+ createCorner('bottom', 'right');
+ },
+
+ _clearControlPos: function () {
+ this._container.removeChild(this._controlContainer);
+ }
+});
+
+
+/*
+ * L.Control.Zoom is used for the default zoom buttons on the map.
+ */
+
+L.Control.Zoom = L.Control.extend({
+ options: {
+ position: 'topleft',
+ zoomInText: '+',
+ zoomInTitle: 'Zoom in',
+ zoomOutText: '-',
+ zoomOutTitle: 'Zoom out'
+ },
+
+ onAdd: function (map) {
+ var zoomName = 'leaflet-control-zoom',
+ container = L.DomUtil.create('div', zoomName + ' leaflet-bar');
+
+ this._map = map;
+
+ this._zoomInButton = this._createButton(
+ this.options.zoomInText, this.options.zoomInTitle,
+ zoomName + '-in', container, this._zoomIn, this);
+ this._zoomOutButton = this._createButton(
+ this.options.zoomOutText, this.options.zoomOutTitle,
+ zoomName + '-out', container, this._zoomOut, this);
+
+ this._updateDisabled();
+ map.on('zoomend zoomlevelschange', this._updateDisabled, this);
+
+ return container;
+ },
+
+ onRemove: function (map) {
+ map.off('zoomend zoomlevelschange', this._updateDisabled, this);
+ },
+
+ _zoomIn: function (e) {
+ this._map.zoomIn(e.shiftKey ? 3 : 1);
+ },
+
+ _zoomOut: function (e) {
+ this._map.zoomOut(e.shiftKey ? 3 : 1);
+ },
+
+ _createButton: function (html, title, className, container, fn, context) {
+ var link = L.DomUtil.create('a', className, container);
+ link.innerHTML = html;
+ link.href = '#';
+ link.title = title;
+
+ var stop = L.DomEvent.stopPropagation;
+
+ L.DomEvent
+ .on(link, 'click', stop)
+ .on(link, 'mousedown', stop)
+ .on(link, 'dblclick', stop)
+ .on(link, 'click', L.DomEvent.preventDefault)
+ .on(link, 'click', fn, context)
+ .on(link, 'click', this._refocusOnMap, context);
+
+ return link;
+ },
+
+ _updateDisabled: function () {
+ var map = this._map,
+ className = 'leaflet-disabled';
+
+ L.DomUtil.removeClass(this._zoomInButton, className);
+ L.DomUtil.removeClass(this._zoomOutButton, className);
+
+ if (map._zoom === map.getMinZoom()) {
+ L.DomUtil.addClass(this._zoomOutButton, className);
+ }
+ if (map._zoom === map.getMaxZoom()) {
+ L.DomUtil.addClass(this._zoomInButton, className);
+ }
+ }
+});
+
+L.Map.mergeOptions({
+ zoomControl: true
+});
+
+L.Map.addInitHook(function () {
+ if (this.options.zoomControl) {
+ this.zoomControl = new L.Control.Zoom();
+ this.addControl(this.zoomControl);
+ }
+});
+
+L.control.zoom = function (options) {
+ return new L.Control.Zoom(options);
+};
+
+
+
+/*
+ * L.Control.Attribution is used for displaying attribution on the map (added by default).
+ */
+
+L.Control.Attribution = L.Control.extend({
+ options: {
+ position: 'bottomright',
+ prefix: 'Leaflet '
+ },
+
+ initialize: function (options) {
+ L.setOptions(this, options);
+
+ this._attributions = {};
+ },
+
+ onAdd: function (map) {
+ this._container = L.DomUtil.create('div', 'leaflet-control-attribution');
+ L.DomEvent.disableClickPropagation(this._container);
+
+ for (var i in map._layers) {
+ if (map._layers[i].getAttribution) {
+ this.addAttribution(map._layers[i].getAttribution());
+ }
+ }
+
+ map
+ .on('layeradd', this._onLayerAdd, this)
+ .on('layerremove', this._onLayerRemove, this);
+
+ this._update();
+
+ return this._container;
+ },
+
+ onRemove: function (map) {
+ map
+ .off('layeradd', this._onLayerAdd)
+ .off('layerremove', this._onLayerRemove);
+
+ },
+
+ setPrefix: function (prefix) {
+ this.options.prefix = prefix;
+ this._update();
+ return this;
+ },
+
+ addAttribution: function (text) {
+ if (!text) { return; }
+
+ if (!this._attributions[text]) {
+ this._attributions[text] = 0;
+ }
+ this._attributions[text]++;
+
+ this._update();
+
+ return this;
+ },
+
+ removeAttribution: function (text) {
+ if (!text) { return; }
+
+ 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(' | ');
+ },
+
+ _onLayerAdd: function (e) {
+ if (e.layer.getAttribution) {
+ this.addAttribution(e.layer.getAttribution());
+ }
+ },
+
+ _onLayerRemove: function (e) {
+ if (e.layer.getAttribution) {
+ this.removeAttribution(e.layer.getAttribution());
+ }
+ }
+});
+
+L.Map.mergeOptions({
+ attributionControl: true
+});
+
+L.Map.addInitHook(function () {
+ if (this.options.attributionControl) {
+ this.attributionControl = (new L.Control.Attribution()).addTo(this);
+ }
+});
+
+L.control.attribution = function (options) {
+ return new L.Control.Attribution(options);
+};
+
+
+/*
+ * L.Control.Scale is used for displaying metric/imperial scale on the map.
+ */
+
+L.Control.Scale = L.Control.extend({
+ options: {
+ position: 'bottomleft',
+ maxWidth: 100,
+ metric: true,
+ imperial: true,
+ updateWhenIdle: false
+ },
+
+ onAdd: function (map) {
+ this._map = map;
+
+ var className = 'leaflet-control-scale',
+ container = L.DomUtil.create('div', className),
+ options = this.options;
+
+ this._addScales(options, className, 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 = L.DomUtil.create('div', className + '-line', container);
+ }
+ if (options.imperial) {
+ this._iScale = L.DomUtil.create('div', className + '-line', container);
+ }
+ },
+
+ _update: function () {
+ var bounds = this._map.getBounds(),
+ centerLat = bounds.getCenter().lat,
+ halfWorldMeters = 6378137 * Math.PI * Math.cos(centerLat * Math.PI / 180),
+ dist = halfWorldMeters * (bounds.getNorthEast().lng - bounds.getSouthWest().lng) / 180,
+
+ size = this._map.getSize(),
+ options = this.options,
+ maxMeters = 0;
+
+ if (size.x > 0) {
+ maxMeters = dist * (options.maxWidth / size.x);
+ }
+
+ this._updateScales(options, maxMeters);
+ },
+
+ _updateScales: function (options, maxMeters) {
+ if (options.metric && maxMeters) {
+ this._updateMetric(maxMeters);
+ }
+
+ if (options.imperial && maxMeters) {
+ this._updateImperial(maxMeters);
+ }
+ },
+
+ _updateMetric: function (maxMeters) {
+ var meters = this._getRoundNum(maxMeters);
+
+ this._mScale.style.width = this._getScaleWidth(meters / maxMeters) + 'px';
+ this._mScale.innerHTML = meters < 1000 ? meters + ' m' : (meters / 1000) + ' km';
+ },
+
+ _updateImperial: function (maxMeters) {
+ var maxFeet = maxMeters * 3.2808399,
+ scale = this._iScale,
+ maxMiles, miles, feet;
+
+ if (maxFeet > 5280) {
+ maxMiles = maxFeet / 5280;
+ miles = this._getRoundNum(maxMiles);
+
+ scale.style.width = this._getScaleWidth(miles / maxMiles) + 'px';
+ scale.innerHTML = miles + ' mi';
+
+ } else {
+ feet = this._getRoundNum(maxFeet);
+
+ scale.style.width = this._getScaleWidth(feet / maxFeet) + 'px';
+ scale.innerHTML = feet + ' ft';
+ }
+ },
+
+ _getScaleWidth: function (ratio) {
+ return Math.round(this.options.maxWidth * ratio) - 10;
+ },
+
+ _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;
+ }
+});
+
+L.control.scale = function (options) {
+ return new L.Control.Scale(options);
+};
+
+
+/*
+ * L.Control.Layers is a control to allow users to switch between different layers on the map.
+ */
+
+L.Control.Layers = L.Control.extend({
+ options: {
+ collapsed: true,
+ position: 'topright',
+ autoZIndex: true
+ },
+
+ initialize: function (baseLayers, overlays, options) {
+ L.setOptions(this, options);
+
+ 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();
+
+ map
+ .on('layeradd', this._onLayerChange, this)
+ .on('layerremove', this._onLayerChange, this);
+
+ return this._container;
+ },
+
+ onRemove: function (map) {
+ map
+ .off('layeradd', this._onLayerChange, this)
+ .off('layerremove', this._onLayerChange, this);
+ },
+
+ addBaseLayer: function (layer, name) {
+ this._addLayer(layer, name);
+ this._update();
+ return this;
+ },
+
+ addOverlay: function (layer, name) {
+ this._addLayer(layer, name, true);
+ this._update();
+ return this;
+ },
+
+ removeLayer: function (layer) {
+ var id = L.stamp(layer);
+ delete this._layers[id];
+ this._update();
+ return this;
+ },
+
+ _initLayout: function () {
+ var className = 'leaflet-control-layers',
+ container = this._container = L.DomUtil.create('div', className);
+
+ //Makes this work on IE10 Touch devices by stopping it from firing a mouseout event when the touch is released
+ container.setAttribute('aria-haspopup', true);
+
+ if (!L.Browser.touch) {
+ L.DomEvent
+ .disableClickPropagation(container)
+ .disableScrollPropagation(container);
+ } else {
+ L.DomEvent.on(container, 'click', L.DomEvent.stopPropagation);
+ }
+
+ var form = this._form = L.DomUtil.create('form', className + '-list');
+
+ if (this.options.collapsed) {
+ if (!L.Browser.android) {
+ L.DomEvent
+ .on(container, 'mouseover', this._expand, this)
+ .on(container, 'mouseout', this._collapse, this);
+ }
+ var link = this._layersLink = L.DomUtil.create('a', className + '-toggle', container);
+ link.href = '#';
+ link.title = 'Layers';
+
+ if (L.Browser.touch) {
+ L.DomEvent
+ .on(link, 'click', L.DomEvent.stop)
+ .on(link, 'click', this._expand, this);
+ }
+ else {
+ L.DomEvent.on(link, 'focus', this._expand, this);
+ }
+ //Work around for Firefox android issue https://github.com/Leaflet/Leaflet/issues/2033
+ L.DomEvent.on(form, 'click', function () {
+ setTimeout(L.bind(this._onInputClick, this), 0);
+ }, this);
+
+ this._map.on('click', this._collapse, this);
+ // TODO keyboard accessibility
+ } else {
+ this._expand();
+ }
+
+ this._baseLayersList = L.DomUtil.create('div', className + '-base', form);
+ this._separator = L.DomUtil.create('div', className + '-separator', form);
+ this._overlaysList = L.DomUtil.create('div', className + '-overlays', form);
+
+ container.appendChild(form);
+ },
+
+ _addLayer: function (layer, name, overlay) {
+ var id = L.stamp(layer);
+
+ this._layers[id] = {
+ layer: layer,
+ name: name,
+ overlay: overlay
+ };
+
+ if (this.options.autoZIndex && layer.setZIndex) {
+ this._lastZIndex++;
+ layer.setZIndex(this._lastZIndex);
+ }
+ },
+
+ _update: function () {
+ if (!this._container) {
+ return;
+ }
+
+ this._baseLayersList.innerHTML = '';
+ this._overlaysList.innerHTML = '';
+
+ var baseLayersPresent = false,
+ overlaysPresent = false,
+ i, obj;
+
+ for (i in this._layers) {
+ obj = this._layers[i];
+ this._addItem(obj);
+ overlaysPresent = overlaysPresent || obj.overlay;
+ baseLayersPresent = baseLayersPresent || !obj.overlay;
+ }
+
+ this._separator.style.display = overlaysPresent && baseLayersPresent ? '' : 'none';
+ },
+
+ _onLayerChange: function (e) {
+ var obj = this._layers[L.stamp(e.layer)];
+
+ if (!obj) { return; }
+
+ if (!this._handlingClick) {
+ this._update();
+ }
+
+ var type = obj.overlay ?
+ (e.type === 'layeradd' ? 'overlayadd' : 'overlayremove') :
+ (e.type === 'layeradd' ? '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'),
+ input,
+ checked = this._map.hasLayer(obj.layer);
+
+ 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);
+ }
+
+ input.layerId = L.stamp(obj.layer);
+
+ L.DomEvent.on(input, 'click', this._onInputClick, this);
+
+ var name = document.createElement('span');
+ name.innerHTML = ' ' + obj.name;
+
+ label.appendChild(input);
+ label.appendChild(name);
+
+ var container = obj.overlay ? this._overlaysList : this._baseLayersList;
+ container.appendChild(label);
+
+ return label;
+ },
+
+ _onInputClick: function () {
+ var i, input, obj,
+ inputs = this._form.getElementsByTagName('input'),
+ inputsLen = inputs.length;
+
+ this._handlingClick = true;
+
+ for (i = 0; i < inputsLen; i++) {
+ input = inputs[i];
+ obj = this._layers[input.layerId];
+
+ if (input.checked && !this._map.hasLayer(obj.layer)) {
+ this._map.addLayer(obj.layer);
+
+ } else if (!input.checked && this._map.hasLayer(obj.layer)) {
+ this._map.removeLayer(obj.layer);
+ }
+ }
+
+ this._handlingClick = false;
+
+ this._refocusOnMap();
+ },
+
+ _expand: function () {
+ L.DomUtil.addClass(this._container, 'leaflet-control-layers-expanded');
+ },
+
+ _collapse: function () {
+ this._container.className = this._container.className.replace(' leaflet-control-layers-expanded', '');
+ }
+});
+
+L.control.layers = function (baseLayers, overlays, options) {
+ return new L.Control.Layers(baseLayers, overlays, options);
+};
+
+
+/*
+ * L.PosAnimation is used by Leaflet internally for pan animations.
+ */
+
+L.PosAnimation = L.Class.extend({
+ includes: L.Mixin.Events,
+
+ run: function (el, newPos, duration, easeLinearity) { // (HTMLElement, Point[, Number, Number])
+ this.stop();
+
+ this._el = el;
+ this._inProgress = true;
+ this._newPos = newPos;
+
+ this.fire('start');
+
+ el.style[L.DomUtil.TRANSITION] = 'all ' + (duration || 0.25) +
+ 's cubic-bezier(0,0,' + (easeLinearity || 0.5) + ',1)';
+
+ L.DomEvent.on(el, L.DomUtil.TRANSITION_END, this._onTransitionEnd, this);
+ L.DomUtil.setPosition(el, newPos);
+
+ // toggle reflow, Chrome flickers for some reason if you don't do this
+ L.Util.falseFn(el.offsetWidth);
+
+ // there's no native way to track value updates of transitioned properties, so we imitate this
+ this._stepTimer = setInterval(L.bind(this._onStep, this), 50);
+ },
+
+ stop: function () {
+ if (!this._inProgress) { return; }
+
+ // if we just removed the transition property, the element would jump to its final position,
+ // so we need to make it stay at the current position
+
+ L.DomUtil.setPosition(this._el, this._getPos());
+ this._onTransitionEnd();
+ L.Util.falseFn(this._el.offsetWidth); // force reflow in case we are about to start a new animation
+ },
+
+ _onStep: function () {
+ var stepPos = this._getPos();
+ if (!stepPos) {
+ this._onTransitionEnd();
+ return;
+ }
+ // jshint camelcase: false
+ // make L.DomUtil.getPosition return intermediate position value during animation
+ this._el._leaflet_pos = stepPos;
+
+ this.fire('step');
+ },
+
+ // you can't easily get intermediate values of properties animated with CSS3 Transitions,
+ // we need to parse computed style (in case of transform it returns matrix string)
+
+ _transformRe: /([-+]?(?:\d*\.)?\d+)\D*, ([-+]?(?:\d*\.)?\d+)\D*\)/,
+
+ _getPos: function () {
+ var left, top, matches,
+ el = this._el,
+ style = window.getComputedStyle(el);
+
+ if (L.Browser.any3d) {
+ matches = style[L.DomUtil.TRANSFORM].match(this._transformRe);
+ if (!matches) { return; }
+ left = parseFloat(matches[1]);
+ top = parseFloat(matches[2]);
+ } else {
+ left = parseFloat(style.left);
+ top = parseFloat(style.top);
+ }
+
+ return new L.Point(left, top, true);
+ },
+
+ _onTransitionEnd: function () {
+ L.DomEvent.off(this._el, L.DomUtil.TRANSITION_END, this._onTransitionEnd, this);
+
+ if (!this._inProgress) { return; }
+ this._inProgress = false;
+
+ this._el.style[L.DomUtil.TRANSITION] = '';
+
+ // jshint camelcase: false
+ // make sure L.DomUtil.getPosition returns the final position value after animation
+ this._el._leaflet_pos = this._newPos;
+
+ clearInterval(this._stepTimer);
+
+ this.fire('step').fire('end');
+ }
+
+});
+
+
+/*
+ * Extends L.Map to handle panning animations.
+ */
+
+L.Map.include({
+
+ setView: function (center, zoom, options) {
+
+ zoom = zoom === undefined ? this._zoom : this._limitZoom(zoom);
+ center = this._limitCenter(L.latLng(center), zoom, this.options.maxBounds);
+ options = options || {};
+
+ if (this._panAnim) {
+ this._panAnim.stop();
+ }
+
+ if (this._loaded && !options.reset && options !== true) {
+
+ if (options.animate !== undefined) {
+ options.zoom = L.extend({animate: options.animate}, options.zoom);
+ options.pan = L.extend({animate: options.animate}, options.pan);
+ }
+
+ // try animating pan or zoom
+ var animated = (this._zoom !== zoom) ?
+ this._tryAnimatedZoom && this._tryAnimatedZoom(center, zoom, options.zoom) :
+ this._tryAnimatedPan(center, options.pan);
+
+ if (animated) {
+ // 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;
+ },
+
+ panBy: function (offset, options) {
+ offset = L.point(offset).round();
+ options = options || {};
+
+ if (!offset.x && !offset.y) {
+ return this;
+ }
+
+ if (!this._panAnim) {
+ this._panAnim = new L.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) {
+ L.DomUtil.addClass(this._mapPane, 'leaflet-pan-anim');
+
+ var newPos = this._getMapPanePos().subtract(offset);
+ this._panAnim.run(this._mapPane, newPos, options.duration || 0.25, options.easeLinearity);
+ } else {
+ this._rawPanBy(offset);
+ this.fire('move').fire('moveend');
+ }
+
+ return this;
+ },
+
+ _onPanTransitionStep: function () {
+ this.fire('move');
+ },
+
+ _onPanTransitionEnd: function () {
+ L.DomUtil.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;
+ }
+});
+
+
+/*
+ * L.PosAnimation fallback implementation that powers Leaflet pan animations
+ * in browsers that don't support CSS3 Transitions.
+ */
+
+L.PosAnimation = L.DomUtil.TRANSITION ? L.PosAnimation : L.PosAnimation.extend({
+
+ run: function (el, newPos, duration, easeLinearity) { // (HTMLElement, Point[, Number, Number])
+ 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 = L.DomUtil.getPosition(el);
+ this._offset = newPos.subtract(this._startPos);
+ this._startTime = +new Date();
+
+ this.fire('start');
+
+ this._animate();
+ },
+
+ stop: function () {
+ if (!this._inProgress) { return; }
+
+ this._step();
+ this._complete();
+ },
+
+ _animate: function () {
+ // animation loop
+ this._animId = L.Util.requestAnimFrame(this._animate, this);
+ this._step();
+ },
+
+ _step: function () {
+ var elapsed = (+new Date()) - this._startTime,
+ duration = this._duration * 1000;
+
+ if (elapsed < duration) {
+ this._runFrame(this._easeOut(elapsed / duration));
+ } else {
+ this._runFrame(1);
+ this._complete();
+ }
+ },
+
+ _runFrame: function (progress) {
+ var pos = this._startPos.add(this._offset.multiplyBy(progress));
+ L.DomUtil.setPosition(this._el, pos);
+
+ this.fire('step');
+ },
+
+ _complete: function () {
+ L.Util.cancelAnimFrame(this._animId);
+
+ this._inProgress = false;
+ this.fire('end');
+ },
+
+ _easeOut: function (t) {
+ return 1 - Math.pow(1 - t, this._easeOutPower);
+ }
+});
+
+
+/*
+ * Extends L.Map to handle zoom animations.
+ */
+
+L.Map.mergeOptions({
+ zoomAnimation: true,
+ zoomAnimationThreshold: 4
+});
+
+if (L.DomUtil.TRANSITION) {
+
+ L.Map.addInitHook(function () {
+ // don't animate on browsers without hardware-accelerated transitions or old Android/Opera
+ this._zoomAnimated = this.options.zoomAnimation && L.DomUtil.TRANSITION &&
+ L.Browser.any3d && !L.Browser.android23 && !L.Browser.mobileOpera;
+
+ // 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) {
+ L.DomEvent.on(this._mapPane, L.DomUtil.TRANSITION_END, this._catchTransitionEnd, this);
+ }
+ });
+}
+
+L.Map.include(!L.DomUtil.TRANSITION ? {} : {
+
+ _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),
+ origin = this._getCenterLayerPoint()._add(offset);
+
+ // 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; }
+
+ this
+ .fire('movestart')
+ .fire('zoomstart');
+
+ this._animateZoom(center, zoom, origin, scale, null, true);
+
+ return true;
+ },
+
+ _animateZoom: function (center, zoom, origin, scale, delta, backwards, forTouchZoom) {
+
+ if (!forTouchZoom) {
+ this._animatingZoom = true;
+ }
+
+ // put transform transition on all layers with leaflet-zoom-animated class
+ L.DomUtil.addClass(this._mapPane, 'leaflet-zoom-anim');
+
+ // remember what center/zoom to set after animation
+ this._animateToCenter = center;
+ this._animateToZoom = zoom;
+
+ // disable any dragging during animation
+ if (L.Draggable) {
+ L.Draggable._disabled = true;
+ }
+
+ L.Util.requestAnimFrame(function () {
+ this.fire('zoomanim', {
+ center: center,
+ zoom: zoom,
+ origin: origin,
+ scale: scale,
+ delta: delta,
+ backwards: backwards
+ });
+ }, this);
+ },
+
+ _onZoomTransitionEnd: function () {
+
+ this._animatingZoom = false;
+
+ L.DomUtil.removeClass(this._mapPane, 'leaflet-zoom-anim');
+
+ this._resetView(this._animateToCenter, this._animateToZoom, true, true);
+
+ if (L.Draggable) {
+ L.Draggable._disabled = false;
+ }
+ }
+});
+
+
+/*
+ Zoom animation logic for L.TileLayer.
+*/
+
+L.TileLayer.include({
+ _animateZoom: function (e) {
+ if (!this._animating) {
+ this._animating = true;
+ this._prepareBgBuffer();
+ }
+
+ var bg = this._bgBuffer,
+ transform = L.DomUtil.TRANSFORM,
+ initialTransform = e.delta ? L.DomUtil.getTranslateString(e.delta) : bg.style[transform],
+ scaleStr = L.DomUtil.getScaleString(e.scale, e.origin);
+
+ bg.style[transform] = e.backwards ?
+ scaleStr + ' ' + initialTransform :
+ initialTransform + ' ' + scaleStr;
+ },
+
+ _endZoomAnim: function () {
+ var front = this._tileContainer,
+ bg = this._bgBuffer;
+
+ front.style.visibility = '';
+ front.parentNode.appendChild(front); // Bring to fore
+
+ // force reflow
+ L.Util.falseFn(bg.offsetWidth);
+
+ this._animating = false;
+ },
+
+ _clearBgBuffer: function () {
+ var map = this._map;
+
+ if (map && !map._animatingZoom && !map.touchZoom._zooming) {
+ this._bgBuffer.innerHTML = '';
+ this._bgBuffer.style[L.DomUtil.TRANSFORM] = '';
+ }
+ },
+
+ _prepareBgBuffer: function () {
+
+ var front = this._tileContainer,
+ bg = this._bgBuffer;
+
+ // if foreground layer doesn't have many tiles but bg layer does,
+ // keep the existing bg layer and just zoom it some more
+
+ var bgLoaded = this._getLoadedTilesPercentage(bg),
+ frontLoaded = this._getLoadedTilesPercentage(front);
+
+ if (bg && bgLoaded > 0.5 && frontLoaded < 0.5) {
+
+ front.style.visibility = 'hidden';
+ this._stopLoadingImages(front);
+ return;
+ }
+
+ // prepare the buffer to become the front tile pane
+ bg.style.visibility = 'hidden';
+ bg.style[L.DomUtil.TRANSFORM] = '';
+
+ // switch out the current layer to be the new bg layer (and vice-versa)
+ this._tileContainer = bg;
+ bg = this._bgBuffer = front;
+
+ this._stopLoadingImages(bg);
+
+ //prevent bg buffer from clearing right after zoom
+ clearTimeout(this._clearBgBufferTimer);
+ },
+
+ _getLoadedTilesPercentage: function (container) {
+ var tiles = container.getElementsByTagName('img'),
+ i, len, count = 0;
+
+ for (i = 0, len = tiles.length; i < len; i++) {
+ if (tiles[i].complete) {
+ count++;
+ }
+ }
+ return count / len;
+ },
+
+ // stops loading all tiles in the background layer
+ _stopLoadingImages: function (container) {
+ var tiles = Array.prototype.slice.call(container.getElementsByTagName('img')),
+ i, len, tile;
+
+ for (i = 0, len = tiles.length; i < len; i++) {
+ tile = tiles[i];
+
+ if (!tile.complete) {
+ tile.onload = L.Util.falseFn;
+ tile.onerror = L.Util.falseFn;
+ tile.src = L.Util.emptyImageUrl;
+
+ tile.parentNode.removeChild(tile);
+ }
+ }
+ }
+});
+
+
+/*
+ * Provides L.Map with convenient shortcuts for using browser geolocation features.
+ */
+
+L.Map.include({
+ _defaultLocateOptions: {
+ watch: false,
+ setView: false,
+ maxZoom: Infinity,
+ timeout: 10000,
+ maximumAge: 0,
+ enableHighAccuracy: false
+ },
+
+ locate: function (/*Object*/ options) {
+
+ options = this._locateOptions = L.extend(this._defaultLocateOptions, options);
+
+ if (!navigator.geolocation) {
+ this._handleGeolocationError({
+ code: 0,
+ message: 'Geolocation not supported.'
+ });
+ return this;
+ }
+
+ var onResponse = L.bind(this._handleGeolocationResponse, this),
+ onError = L.bind(this._handleGeolocationError, this);
+
+ if (options.watch) {
+ this._locationWatchId =
+ navigator.geolocation.watchPosition(onResponse, onError, options);
+ } else {
+ navigator.geolocation.getCurrentPosition(onResponse, onError, options);
+ }
+ return this;
+ },
+
+ stopLocate: function () {
+ if (navigator.geolocation) {
+ 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();
+ }
+
+ this.fire('locationerror', {
+ code: c,
+ message: 'Geolocation error: ' + message + '.'
+ });
+ },
+
+ _handleGeolocationResponse: function (pos) {
+ var lat = pos.coords.latitude,
+ lng = pos.coords.longitude,
+ latlng = new L.LatLng(lat, lng),
+
+ latAccuracy = 180 * pos.coords.accuracy / 40075017,
+ lngAccuracy = latAccuracy / Math.cos(L.LatLng.DEG_TO_RAD * lat),
+
+ bounds = L.latLngBounds(
+ [lat - latAccuracy, lng - lngAccuracy],
+ [lat + latAccuracy, lng + lngAccuracy]),
+
+ options = this._locateOptions;
+
+ if (options.setView) {
+ var zoom = Math.min(this.getBoundsZoom(bounds), options.maxZoom);
+ this.setView(latlng, 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];
+ }
+ }
+
+ this.fire('locationfound', data);
+ }
+});
+
+
+}(window, document));
\ No newline at end of file
diff --git a/servers/php/js/leaflet-0.7.3/leaflet.css b/servers/php/js/leaflet-0.7.3/leaflet.css
new file mode 100644
index 0000000..ac0cd17
--- /dev/null
+++ b/servers/php/js/leaflet-0.7.3/leaflet.css
@@ -0,0 +1,478 @@
+/* required styles */
+
+.leaflet-map-pane,
+.leaflet-tile,
+.leaflet-marker-icon,
+.leaflet-marker-shadow,
+.leaflet-tile-pane,
+.leaflet-tile-container,
+.leaflet-overlay-pane,
+.leaflet-shadow-pane,
+.leaflet-marker-pane,
+.leaflet-popup-pane,
+.leaflet-overlay-pane svg,
+.leaflet-zoom-box,
+.leaflet-image-layer,
+.leaflet-layer {
+ position: absolute;
+ left: 0;
+ top: 0;
+ }
+.leaflet-container {
+ overflow: hidden;
+ -ms-touch-action: none;
+ }
+.leaflet-tile,
+.leaflet-marker-icon,
+.leaflet-marker-shadow {
+ -webkit-user-select: none;
+ -moz-user-select: none;
+ user-select: none;
+ -webkit-user-drag: none;
+ }
+.leaflet-marker-icon,
+.leaflet-marker-shadow {
+ display: block;
+ }
+/* map is broken in FF if you have max-width: 100% on tiles */
+.leaflet-container img {
+ max-width: none !important;
+ }
+/* stupid Android 2 doesn't understand "max-width: none" properly */
+.leaflet-container img.leaflet-image-layer {
+ max-width: 15000px !important;
+ }
+.leaflet-tile {
+ filter: inherit;
+ visibility: hidden;
+ }
+.leaflet-tile-loaded {
+ visibility: inherit;
+ }
+.leaflet-zoom-box {
+ width: 0;
+ height: 0;
+ }
+/* workaround for https://bugzilla.mozilla.org/show_bug.cgi?id=888319 */
+.leaflet-overlay-pane svg {
+ -moz-user-select: none;
+ }
+
+.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-vml-shape {
+ width: 1px;
+ height: 1px;
+ }
+.lvml {
+ behavior: url(#default#VML);
+ display: inline-block;
+ position: absolute;
+ }
+
+
+/* control positioning */
+
+.leaflet-control {
+ position: relative;
+ z-index: 7;
+ pointer-events: auto;
+ }
+.leaflet-top,
+.leaflet-bottom {
+ position: absolute;
+ z-index: 1000;
+ pointer-events: none;
+ }
+.leaflet-top {
+ top: 0;
+ }
+.leaflet-right {
+ right: 0;
+ }
+.leaflet-bottom {
+ bottom: 0;
+ }
+.leaflet-left {
+ left: 0;
+ }
+.leaflet-control {
+ float: left;
+ clear: both;
+ }
+.leaflet-right .leaflet-control {
+ float: right;
+ }
+.leaflet-top .leaflet-control {
+ margin-top: 10px;
+ }
+.leaflet-bottom .leaflet-control {
+ margin-bottom: 10px;
+ }
+.leaflet-left .leaflet-control {
+ margin-left: 10px;
+ }
+.leaflet-right .leaflet-control {
+ margin-right: 10px;
+ }
+
+
+/* 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,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-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;
+ }
+
+
+/* cursors */
+
+.leaflet-clickable {
+ cursor: pointer;
+ }
+.leaflet-container {
+ cursor: -webkit-grab;
+ cursor: -moz-grab;
+ }
+.leaflet-popup-pane,
+.leaflet-control {
+ cursor: auto;
+ }
+.leaflet-dragging .leaflet-container,
+.leaflet-dragging .leaflet-clickable {
+ cursor: move;
+ cursor: -webkit-grabbing;
+ cursor: -moz-grabbing;
+ }
+
+
+/* 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-bar a:hover {
+ background-color: #f4f4f4;
+ }
+.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-bar a.leaflet-disabled {
+ cursor: default;
+ background-color: #f4f4f4;
+ color: #bbb;
+ }
+
+.leaflet-touch .leaflet-bar a {
+ width: 30px;
+ height: 30px;
+ line-height: 30px;
+ }
+
+
+/* zoom control */
+
+.leaflet-control-zoom-in,
+.leaflet-control-zoom-out {
+ 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 5px rgba(0,0,0,0.4);
+ background: #fff;
+ border-radius: 5px;
+ }
+.leaflet-control-layers-toggle {
+ background-image: url(images/layers.png);
+ width: 36px;
+ height: 36px;
+ }
+.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;
+ }
+.leaflet-control-layers .leaflet-control-layers-list,
+.leaflet-control-layers-expanded .leaflet-control-layers-toggle {
+ display: none;
+ }
+.leaflet-control-layers-expanded .leaflet-control-layers-list {
+ display: block;
+ position: relative;
+ }
+.leaflet-control-layers-expanded {
+ padding: 6px 10px 6px 6px;
+ color: #333;
+ background: #fff;
+ }
+.leaflet-control-layers-selector {
+ margin-top: 2px;
+ position: relative;
+ top: 1px;
+ }
+.leaflet-control-layers label {
+ display: block;
+ }
+.leaflet-control-layers-separator {
+ height: 0;
+ border-top: 1px solid #ddd;
+ margin: 5px -10px 5px -6px;
+ }
+
+
+/* attribution and scale controls */
+
+.leaflet-container .leaflet-control-attribution {
+ 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-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;
+ line-height: 1.1;
+ padding: 2px 5px 1px;
+ font-size: 11px;
+ white-space: nowrap;
+ overflow: hidden;
+ -moz-box-sizing: content-box;
+ box-sizing: content-box;
+
+ background: #fff;
+ background: rgba(255, 255, 255, 0.5);
+ }
+.leaflet-control-scale-line:not(:first-child) {
+ border-top: 2px solid #777;
+ border-bottom: none;
+ margin-top: -2px;
+ }
+.leaflet-control-scale-line:not(:first-child):not(:last-child) {
+ border-bottom: 2px solid #777;
+ }
+
+.leaflet-touch .leaflet-control-attribution,
+.leaflet-touch .leaflet-control-layers,
+.leaflet-touch .leaflet-bar {
+ box-shadow: none;
+ }
+.leaflet-touch .leaflet-control-layers,
+.leaflet-touch .leaflet-bar {
+ border: 2px solid rgba(0,0,0,0.2);
+ background-clip: padding-box;
+ }
+
+
+/* popup */
+
+.leaflet-popup {
+ position: absolute;
+ text-align: center;
+ }
+.leaflet-popup-content-wrapper {
+ padding: 1px;
+ text-align: left;
+ border-radius: 12px;
+ }
+.leaflet-popup-content {
+ 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;
+ overflow: hidden;
+ }
+.leaflet-popup-tip {
+ width: 17px;
+ height: 17px;
+ padding: 1px;
+
+ margin: -10px auto 0;
+
+ -webkit-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;
+
+ 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 4px 0 0;
+ text-align: center;
+ width: 18px;
+ height: 14px;
+ font: 16px/14px Tahoma, Verdana, sans-serif;
+ color: #c3c3c3;
+ text-decoration: none;
+ font-weight: bold;
+ background: transparent;
+ }
+.leaflet-container a.leaflet-popup-close-button:hover {
+ color: #999;
+ }
+.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;
+
+ -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-oldie .leaflet-popup-tip-container {
+ margin-top: -1px;
+ }
+
+.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;
+ }
diff --git a/servers/php/js/leaflet-0.7.3/leaflet.js b/servers/php/js/leaflet-0.7.3/leaflet.js
new file mode 100644
index 0000000..03434b7
--- /dev/null
+++ b/servers/php/js/leaflet-0.7.3/leaflet.js
@@ -0,0 +1,9 @@
+/*
+ Leaflet, a JavaScript library for mobile-friendly interactive maps. http://leafletjs.com
+ (c) 2010-2013, Vladimir Agafonkin
+ (c) 2010-2011, CloudMade
+*/
+!function(t,e,i){var n=t.L,o={};o.version="0.7.3","object"==typeof module&&"object"==typeof module.exports?module.exports=o:"function"==typeof define&&define.amd&&define(o),o.noConflict=function(){return t.L=n,this},t.L=o,o.Util={extend:function(t){var e,i,n,o,s=Array.prototype.slice.call(arguments,1);for(i=0,n=s.length;n>i;i++){o=s[i]||{};for(e in o)o.hasOwnProperty(e)&&(t[e]=o[e])}return t},bind:function(t,e){var i=arguments.length>2?Array.prototype.slice.call(arguments,2):null;return function(){return t.apply(e,i||arguments)}},stamp:function(){var t=0,e="_leaflet_id";return function(i){return i[e]=i[e]||++t,i[e]}}(),invokeEach:function(t,e,i){var n,o;if("object"==typeof t){o=Array.prototype.slice.call(arguments,3);for(n in t)e.apply(i,[n,t[n]].concat(o));return!0}return!1},limitExecByInterval:function(t,e,i){var n,o;return function s(){var a=arguments;return n?void(o=!0):(n=!0,setTimeout(function(){n=!1,o&&(s.apply(i,a),o=!1)},e),void t.apply(i,a))}},falseFn:function(){return!1},formatNum:function(t,e){var i=Math.pow(10,e||5);return Math.round(t*i)/i},trim:function(t){return t.trim?t.trim():t.replace(/^\s+|\s+$/g,"")},splitWords:function(t){return o.Util.trim(t).split(/\s+/)},setOptions:function(t,e){return t.options=o.extend({},t.options,e),t.options},getParamString:function(t,e,i){var n=[];for(var o in t)n.push(encodeURIComponent(i?o.toUpperCase():o)+"="+encodeURIComponent(t[o]));return(e&&-1!==e.indexOf("?")?"&":"?")+n.join("&")},template:function(t,e){return t.replace(/\{ *([\w_]+) *\}/g,function(t,n){var o=e[n];if(o===i)throw new Error("No value provided for variable "+t);return"function"==typeof o&&(o=o(e)),o})},isArray:Array.isArray||function(t){return"[object Array]"===Object.prototype.toString.call(t)},emptyImageUrl:"data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs="},function(){function e(e){var i,n,o=["webkit","moz","o","ms"];for(i=0;it;t++)n._initHooks[t].call(this)}},e},o.Class.include=function(t){o.extend(this.prototype,t)},o.Class.mergeOptions=function(t){o.extend(this.prototype.options,t)},o.Class.addInitHook=function(t){var e=Array.prototype.slice.call(arguments,1),i="function"==typeof t?t:function(){this[t].apply(this,e)};this.prototype._initHooks=this.prototype._initHooks||[],this.prototype._initHooks.push(i)};var s="_leaflet_events";o.Mixin={},o.Mixin.Events={addEventListener:function(t,e,i){if(o.Util.invokeEach(t,this.addEventListener,this,e,i))return this;var n,a,r,h,l,u,c,d=this[s]=this[s]||{},p=i&&i!==this&&o.stamp(i);for(t=o.Util.splitWords(t),n=0,a=t.length;a>n;n++)r={action:e,context:i||this},h=t[n],p?(l=h+"_idx",u=l+"_len",c=d[l]=d[l]||{},c[p]||(c[p]=[],d[u]=(d[u]||0)+1),c[p].push(r)):(d[h]=d[h]||[],d[h].push(r));return this},hasEventListeners:function(t){var e=this[s];return!!e&&(t in e&&e[t].length>0||t+"_idx"in e&&e[t+"_idx_len"]>0)},removeEventListener:function(t,e,i){if(!this[s])return this;if(!t)return this.clearAllEventListeners();if(o.Util.invokeEach(t,this.removeEventListener,this,e,i))return this;var n,a,r,h,l,u,c,d,p,_=this[s],m=i&&i!==this&&o.stamp(i);for(t=o.Util.splitWords(t),n=0,a=t.length;a>n;n++)if(r=t[n],u=r+"_idx",c=u+"_len",d=_[u],e){if(h=m&&d?d[m]:_[r]){for(l=h.length-1;l>=0;l--)h[l].action!==e||i&&h[l].context!==i||(p=h.splice(l,1),p[0].action=o.Util.falseFn);i&&d&&0===h.length&&(delete d[m],_[c]--)}}else delete _[r],delete _[u],delete _[c];return this},clearAllEventListeners:function(){return delete this[s],this},fireEvent:function(t,e){if(!this.hasEventListeners(t))return this;var i,n,a,r,h,l=o.Util.extend({},e,{type:t,target:this}),u=this[s];if(u[t])for(i=u[t].slice(),n=0,a=i.length;a>n;n++)i[n].action.call(i[n].context,l);r=u[t+"_idx"];for(h in r)if(i=r[h].slice())for(n=0,a=i.length;a>n;n++)i[n].action.call(i[n].context,l);return this},addOneTimeEventListener:function(t,e,i){if(o.Util.invokeEach(t,this.addOneTimeEventListener,this,e,i))return this;var n=o.bind(function(){this.removeEventListener(t,e,i).removeEventListener(t,n,i)},this);return this.addEventListener(t,e,i).addEventListener(t,n,i)}},o.Mixin.Events.on=o.Mixin.Events.addEventListener,o.Mixin.Events.off=o.Mixin.Events.removeEventListener,o.Mixin.Events.once=o.Mixin.Events.addOneTimeEventListener,o.Mixin.Events.fire=o.Mixin.Events.fireEvent,function(){var n="ActiveXObject"in t,s=n&&!e.addEventListener,a=navigator.userAgent.toLowerCase(),r=-1!==a.indexOf("webkit"),h=-1!==a.indexOf("chrome"),l=-1!==a.indexOf("phantom"),u=-1!==a.indexOf("android"),c=-1!==a.search("android [23]"),d=-1!==a.indexOf("gecko"),p=typeof orientation!=i+"",_=t.navigator&&t.navigator.msPointerEnabled&&t.navigator.msMaxTouchPoints&&!t.PointerEvent,m=t.PointerEvent&&t.navigator.pointerEnabled&&t.navigator.maxTouchPoints||_,f="devicePixelRatio"in t&&t.devicePixelRatio>1||"matchMedia"in t&&t.matchMedia("(min-resolution:144dpi)")&&t.matchMedia("(min-resolution:144dpi)").matches,g=e.documentElement,v=n&&"transition"in g.style,y="WebKitCSSMatrix"in t&&"m11"in new t.WebKitCSSMatrix&&!c,P="MozPerspective"in g.style,L="OTransition"in g.style,x=!t.L_DISABLE_3D&&(v||y||P||L)&&!l,w=!t.L_NO_TOUCH&&!l&&function(){var t="ontouchstart";if(m||t in g)return!0;var i=e.createElement("div"),n=!1;return i.setAttribute?(i.setAttribute(t,"return;"),"function"==typeof i[t]&&(n=!0),i.removeAttribute(t),i=null,n):!1}();o.Browser={ie:n,ielt9:s,webkit:r,gecko:d&&!r&&!t.opera&&!n,android:u,android23:c,chrome:h,ie3d:v,webkit3d:y,gecko3d:P,opera3d:L,any3d:x,mobile:p,mobileWebkit:p&&r,mobileWebkit3d:p&&y,mobileOpera:p&&t.opera,touch:w,msPointer:_,pointer:m,retina:f}}(),o.Point=function(t,e,i){this.x=i?Math.round(t):t,this.y=i?Math.round(e):e},o.Point.prototype={clone:function(){return new o.Point(this.x,this.y)},add:function(t){return this.clone()._add(o.point(t))},_add:function(t){return this.x+=t.x,this.y+=t.y,this},subtract:function(t){return this.clone()._subtract(o.point(t))},_subtract:function(t){return this.x-=t.x,this.y-=t.y,this},divideBy:function(t){return this.clone()._divideBy(t)},_divideBy:function(t){return this.x/=t,this.y/=t,this},multiplyBy:function(t){return this.clone()._multiplyBy(t)},_multiplyBy:function(t){return this.x*=t,this.y*=t,this},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},distanceTo:function(t){t=o.point(t);var e=t.x-this.x,i=t.y-this.y;return Math.sqrt(e*e+i*i)},equals:function(t){return t=o.point(t),t.x===this.x&&t.y===this.y},contains:function(t){return t=o.point(t),Math.abs(t.x)<=Math.abs(this.x)&&Math.abs(t.y)<=Math.abs(this.y)},toString:function(){return"Point("+o.Util.formatNum(this.x)+", "+o.Util.formatNum(this.y)+")"}},o.point=function(t,e,n){return t instanceof o.Point?t:o.Util.isArray(t)?new o.Point(t[0],t[1]):t===i||null===t?t:new o.Point(t,e,n)},o.Bounds=function(t,e){if(t)for(var i=e?[t,e]:t,n=0,o=i.length;o>n;n++)this.extend(i[n])},o.Bounds.prototype={extend:function(t){return t=o.point(t),this.min||this.max?(this.min.x=Math.min(t.x,this.min.x),this.max.x=Math.max(t.x,this.max.x),this.min.y=Math.min(t.y,this.min.y),this.max.y=Math.max(t.y,this.max.y)):(this.min=t.clone(),this.max=t.clone()),this},getCenter:function(t){return new o.Point((this.min.x+this.max.x)/2,(this.min.y+this.max.y)/2,t)},getBottomLeft:function(){return new o.Point(this.min.x,this.max.y)},getTopRight:function(){return new o.Point(this.max.x,this.min.y)},getSize:function(){return this.max.subtract(this.min)},contains:function(t){var e,i;return t="number"==typeof t[0]||t instanceof o.Point?o.point(t):o.bounds(t),t instanceof o.Bounds?(e=t.min,i=t.max):e=i=t,e.x>=this.min.x&&i.x<=this.max.x&&e.y>=this.min.y&&i.y<=this.max.y},intersects:function(t){t=o.bounds(t);var e=this.min,i=this.max,n=t.min,s=t.max,a=s.x>=e.x&&n.x<=i.x,r=s.y>=e.y&&n.y<=i.y;return a&&r},isValid:function(){return!(!this.min||!this.max)}},o.bounds=function(t,e){return!t||t instanceof o.Bounds?t:new o.Bounds(t,e)},o.Transformation=function(t,e,i,n){this._a=t,this._b=e,this._c=i,this._d=n},o.Transformation.prototype={transform:function(t,e){return this._transform(t.clone(),e)},_transform:function(t,e){return e=e||1,t.x=e*(this._a*t.x+this._b),t.y=e*(this._c*t.y+this._d),t},untransform:function(t,e){return e=e||1,new o.Point((t.x/e-this._b)/this._a,(t.y/e-this._d)/this._c)}},o.DomUtil={get:function(t){return"string"==typeof t?e.getElementById(t):t},getStyle:function(t,i){var n=t.style[i];if(!n&&t.currentStyle&&(n=t.currentStyle[i]),(!n||"auto"===n)&&e.defaultView){var o=e.defaultView.getComputedStyle(t,null);n=o?o[i]:null}return"auto"===n?null:n},getViewportOffset:function(t){var i,n=0,s=0,a=t,r=e.body,h=e.documentElement;do{if(n+=a.offsetTop||0,s+=a.offsetLeft||0,n+=parseInt(o.DomUtil.getStyle(a,"borderTopWidth"),10)||0,s+=parseInt(o.DomUtil.getStyle(a,"borderLeftWidth"),10)||0,i=o.DomUtil.getStyle(a,"position"),a.offsetParent===r&&"absolute"===i)break;if("fixed"===i){n+=r.scrollTop||h.scrollTop||0,s+=r.scrollLeft||h.scrollLeft||0;break}if("relative"===i&&!a.offsetLeft){var l=o.DomUtil.getStyle(a,"width"),u=o.DomUtil.getStyle(a,"max-width"),c=a.getBoundingClientRect();("none"!==l||"none"!==u)&&(s+=c.left+a.clientLeft),n+=c.top+(r.scrollTop||h.scrollTop||0);break}a=a.offsetParent}while(a);a=t;do{if(a===r)break;n-=a.scrollTop||0,s-=a.scrollLeft||0,a=a.parentNode}while(a);return new o.Point(s,n)},documentIsLtr:function(){return o.DomUtil._docIsLtrCached||(o.DomUtil._docIsLtrCached=!0,o.DomUtil._docIsLtr="ltr"===o.DomUtil.getStyle(e.body,"direction")),o.DomUtil._docIsLtr},create:function(t,i,n){var o=e.createElement(t);return o.className=i,n&&n.appendChild(o),o},hasClass:function(t,e){if(t.classList!==i)return t.classList.contains(e);var n=o.DomUtil._getClass(t);return n.length>0&&new RegExp("(^|\\s)"+e+"(\\s|$)").test(n)},addClass:function(t,e){if(t.classList!==i)for(var n=o.Util.splitWords(e),s=0,a=n.length;a>s;s++)t.classList.add(n[s]);else if(!o.DomUtil.hasClass(t,e)){var r=o.DomUtil._getClass(t);o.DomUtil._setClass(t,(r?r+" ":"")+e)}},removeClass:function(t,e){t.classList!==i?t.classList.remove(e):o.DomUtil._setClass(t,o.Util.trim((" "+o.DomUtil._getClass(t)+" ").replace(" "+e+" "," ")))},_setClass:function(t,e){t.className.baseVal===i?t.className=e:t.className.baseVal=e},_getClass:function(t){return t.className.baseVal===i?t.className:t.className.baseVal},setOpacity:function(t,e){if("opacity"in t.style)t.style.opacity=e;else if("filter"in t.style){var i=!1,n="DXImageTransform.Microsoft.Alpha";try{i=t.filters.item(n)}catch(o){if(1===e)return}e=Math.round(100*e),i?(i.Enabled=100!==e,i.Opacity=e):t.style.filter+=" progid:"+n+"(opacity="+e+")"}},testProp:function(t){for(var i=e.documentElement.style,n=0;ni||i===e?e:t),new o.LatLng(this.lat,i)}},o.latLng=function(t,e){return t instanceof o.LatLng?t:o.Util.isArray(t)?"number"==typeof t[0]||"string"==typeof t[0]?new o.LatLng(t[0],t[1],t[2]):null:t===i||null===t?t:"object"==typeof t&&"lat"in t?new o.LatLng(t.lat,"lng"in t?t.lng:t.lon):e===i?null:new o.LatLng(t,e)},o.LatLngBounds=function(t,e){if(t)for(var i=e?[t,e]:t,n=0,o=i.length;o>n;n++)this.extend(i[n])},o.LatLngBounds.prototype={extend:function(t){if(!t)return this;var e=o.latLng(t);return t=null!==e?e:o.latLngBounds(t),t instanceof o.LatLng?this._southWest||this._northEast?(this._southWest.lat=Math.min(t.lat,this._southWest.lat),this._southWest.lng=Math.min(t.lng,this._southWest.lng),this._northEast.lat=Math.max(t.lat,this._northEast.lat),this._northEast.lng=Math.max(t.lng,this._northEast.lng)):(this._southWest=new o.LatLng(t.lat,t.lng),this._northEast=new o.LatLng(t.lat,t.lng)):t instanceof o.LatLngBounds&&(this.extend(t._southWest),this.extend(t._northEast)),this},pad:function(t){var e=this._southWest,i=this._northEast,n=Math.abs(e.lat-i.lat)*t,s=Math.abs(e.lng-i.lng)*t;return new o.LatLngBounds(new o.LatLng(e.lat-n,e.lng-s),new o.LatLng(i.lat+n,i.lng+s))},getCenter:function(){return new o.LatLng((this._southWest.lat+this._northEast.lat)/2,(this._southWest.lng+this._northEast.lng)/2)},getSouthWest:function(){return this._southWest},getNorthEast:function(){return this._northEast},getNorthWest:function(){return new o.LatLng(this.getNorth(),this.getWest())},getSouthEast:function(){return new o.LatLng(this.getSouth(),this.getEast())},getWest:function(){return this._southWest.lng},getSouth:function(){return this._southWest.lat},getEast:function(){return this._northEast.lng},getNorth:function(){return this._northEast.lat},contains:function(t){t="number"==typeof t[0]||t instanceof o.LatLng?o.latLng(t):o.latLngBounds(t);var e,i,n=this._southWest,s=this._northEast;return t instanceof o.LatLngBounds?(e=t.getSouthWest(),i=t.getNorthEast()):e=i=t,e.lat>=n.lat&&i.lat<=s.lat&&e.lng>=n.lng&&i.lng<=s.lng},intersects:function(t){t=o.latLngBounds(t);var e=this._southWest,i=this._northEast,n=t.getSouthWest(),s=t.getNorthEast(),a=s.lat>=e.lat&&n.lat<=i.lat,r=s.lng>=e.lng&&n.lng<=i.lng;return a&&r},toBBoxString:function(){return[this.getWest(),this.getSouth(),this.getEast(),this.getNorth()].join(",")},equals:function(t){return t?(t=o.latLngBounds(t),this._southWest.equals(t.getSouthWest())&&this._northEast.equals(t.getNorthEast())):!1},isValid:function(){return!(!this._southWest||!this._northEast)}},o.latLngBounds=function(t,e){return!t||t instanceof o.LatLngBounds?t:new o.LatLngBounds(t,e)},o.Projection={},o.Projection.SphericalMercator={MAX_LATITUDE:85.0511287798,project:function(t){var e=o.LatLng.DEG_TO_RAD,i=this.MAX_LATITUDE,n=Math.max(Math.min(i,t.lat),-i),s=t.lng*e,a=n*e;return a=Math.log(Math.tan(Math.PI/4+a/2)),new o.Point(s,a)},unproject:function(t){var e=o.LatLng.RAD_TO_DEG,i=t.x*e,n=(2*Math.atan(Math.exp(t.y))-Math.PI/2)*e;return new o.LatLng(n,i)}},o.Projection.LonLat={project:function(t){return new o.Point(t.lng,t.lat)},unproject:function(t){return new o.LatLng(t.y,t.x)}},o.CRS={latLngToPoint:function(t,e){var i=this.projection.project(t),n=this.scale(e);return this.transformation._transform(i,n)},pointToLatLng:function(t,e){var i=this.scale(e),n=this.transformation.untransform(t,i);return this.projection.unproject(n)},project:function(t){return this.projection.project(t)},scale:function(t){return 256*Math.pow(2,t)},getSize:function(t){var e=this.scale(t);return o.point(e,e)}},o.CRS.Simple=o.extend({},o.CRS,{projection:o.Projection.LonLat,transformation:new o.Transformation(1,0,-1,0),scale:function(t){return Math.pow(2,t)}}),o.CRS.EPSG3857=o.extend({},o.CRS,{code:"EPSG:3857",projection:o.Projection.SphericalMercator,transformation:new o.Transformation(.5/Math.PI,.5,-.5/Math.PI,.5),project:function(t){var e=this.projection.project(t),i=6378137;return e.multiplyBy(i)}}),o.CRS.EPSG900913=o.extend({},o.CRS.EPSG3857,{code:"EPSG:900913"}),o.CRS.EPSG4326=o.extend({},o.CRS,{code:"EPSG:4326",projection:o.Projection.LonLat,transformation:new o.Transformation(1/360,.5,-1/360,.5)}),o.Map=o.Class.extend({includes:o.Mixin.Events,options:{crs:o.CRS.EPSG3857,fadeAnimation:o.DomUtil.TRANSITION&&!o.Browser.android23,trackResize:!0,markerZoomAnimation:o.DomUtil.TRANSITION&&o.Browser.any3d},initialize:function(t,e){e=o.setOptions(this,e),this._initContainer(t),this._initLayout(),this._onResize=o.bind(this._onResize,this),this._initEvents(),e.maxBounds&&this.setMaxBounds(e.maxBounds),e.center&&e.zoom!==i&&this.setView(o.latLng(e.center),e.zoom,{reset:!0}),this._handlers=[],this._layers={},this._zoomBoundLayers={},this._tileLayersNum=0,this.callInitHooks(),this._addLayers(e.layers)},setView:function(t,e){return e=e===i?this.getZoom():e,this._resetView(o.latLng(t),this._limitZoom(e)),this},setZoom:function(t,e){return this._loaded?this.setView(this.getCenter(),t,{zoom:e}):(this._zoom=this._limitZoom(t),this)},zoomIn:function(t,e){return this.setZoom(this._zoom+(t||1),e)},zoomOut:function(t,e){return this.setZoom(this._zoom-(t||1),e)},setZoomAround:function(t,e,i){var n=this.getZoomScale(e),s=this.getSize().divideBy(2),a=t instanceof o.Point?t:this.latLngToContainerPoint(t),r=a.subtract(s).multiplyBy(1-1/n),h=this.containerPointToLatLng(s.add(r));return this.setView(h,e,{zoom:i})},fitBounds:function(t,e){e=e||{},t=t.getBounds?t.getBounds():o.latLngBounds(t);var i=o.point(e.paddingTopLeft||e.padding||[0,0]),n=o.point(e.paddingBottomRight||e.padding||[0,0]),s=this.getBoundsZoom(t,!1,i.add(n)),a=n.subtract(i).divideBy(2),r=this.project(t.getSouthWest(),s),h=this.project(t.getNorthEast(),s),l=this.unproject(r.add(h).divideBy(2).add(a),s);return s=e&&e.maxZoom?Math.min(e.maxZoom,s):s,this.setView(l,s,e)},fitWorld:function(t){return this.fitBounds([[-90,-180],[90,180]],t)},panTo:function(t,e){return this.setView(t,this._zoom,{pan:e})},panBy:function(t){return this.fire("movestart"),this._rawPanBy(o.point(t)),this.fire("move"),this.fire("moveend")},setMaxBounds:function(t){return t=o.latLngBounds(t),this.options.maxBounds=t,t?(this._loaded&&this._panInsideMaxBounds(),this.on("moveend",this._panInsideMaxBounds,this)):this.off("moveend",this._panInsideMaxBounds,this)},panInsideBounds:function(t,e){var i=this.getCenter(),n=this._limitCenter(i,this._zoom,t);return i.equals(n)?this:this.panTo(n,e)},addLayer:function(t){var e=o.stamp(t);return this._layers[e]?this:(this._layers[e]=t,!t.options||isNaN(t.options.maxZoom)&&isNaN(t.options.minZoom)||(this._zoomBoundLayers[e]=t,this._updateZoomLevels()),this.options.zoomAnimation&&o.TileLayer&&t instanceof o.TileLayer&&(this._tileLayersNum++,this._tileLayersToLoad++,t.on("load",this._onTileLayerLoad,this)),this._loaded&&this._layerAdd(t),this)},removeLayer:function(t){var e=o.stamp(t);return this._layers[e]?(this._loaded&&t.onRemove(this),delete this._layers[e],this._loaded&&this.fire("layerremove",{layer:t}),this._zoomBoundLayers[e]&&(delete this._zoomBoundLayers[e],this._updateZoomLevels()),this.options.zoomAnimation&&o.TileLayer&&t instanceof o.TileLayer&&(this._tileLayersNum--,this._tileLayersToLoad--,t.off("load",this._onTileLayerLoad,this)),this):this},hasLayer:function(t){return t?o.stamp(t)in this._layers:!1},eachLayer:function(t,e){for(var i in this._layers)t.call(e,this._layers[i]);return this},invalidateSize:function(t){if(!this._loaded)return this;t=o.extend({animate:!1,pan:!0},t===!0?{animate:!0}:t);var e=this.getSize();this._sizeChanged=!0,this._initialCenter=null;var i=this.getSize(),n=e.divideBy(2).round(),s=i.divideBy(2).round(),a=n.subtract(s);return a.x||a.y?(t.animate&&t.pan?this.panBy(a):(t.pan&&this._rawPanBy(a),this.fire("move"),t.debounceMoveend?(clearTimeout(this._sizeTimer),this._sizeTimer=setTimeout(o.bind(this.fire,this,"moveend"),200)):this.fire("moveend")),this.fire("resize",{oldSize:e,newSize:i})):this},addHandler:function(t,e){if(!e)return this;var i=this[t]=new e(this);return this._handlers.push(i),this.options[t]&&i.enable(),this},remove:function(){this._loaded&&this.fire("unload"),this._initEvents("off");try{delete this._container._leaflet}catch(t){this._container._leaflet=i}return this._clearPanes(),this._clearControlPos&&this._clearControlPos(),this._clearHandlers(),this},getCenter:function(){return this._checkIfLoaded(),this._initialCenter&&!this._moved()?this._initialCenter:this.layerPointToLatLng(this._getCenterLayerPoint())},getZoom:function(){return this._zoom},getBounds:function(){var t=this.getPixelBounds(),e=this.unproject(t.getBottomLeft()),i=this.unproject(t.getTopRight());return new o.LatLngBounds(e,i)},getMinZoom:function(){return this.options.minZoom===i?this._layersMinZoom===i?0:this._layersMinZoom:this.options.minZoom},getMaxZoom:function(){return this.options.maxZoom===i?this._layersMaxZoom===i?1/0:this._layersMaxZoom:this.options.maxZoom},getBoundsZoom:function(t,e,i){t=o.latLngBounds(t);var n,s=this.getMinZoom()-(e?1:0),a=this.getMaxZoom(),r=this.getSize(),h=t.getNorthWest(),l=t.getSouthEast(),u=!0;i=o.point(i||[0,0]);do s++,n=this.project(l,s).subtract(this.project(h,s)).add(i),u=e?n.x=s);return u&&e?null:e?s:s-1},getSize:function(){return(!this._size||this._sizeChanged)&&(this._size=new o.Point(this._container.clientWidth,this._container.clientHeight),this._sizeChanged=!1),this._size.clone()},getPixelBounds:function(){var t=this._getTopLeftPoint();return new o.Bounds(t,t.add(this.getSize()))},getPixelOrigin:function(){return this._checkIfLoaded(),this._initialTopLeftPoint},getPanes:function(){return this._panes},getContainer:function(){return this._container},getZoomScale:function(t){var e=this.options.crs;return e.scale(t)/e.scale(this._zoom)},getScaleZoom:function(t){return this._zoom+Math.log(t)/Math.LN2},project:function(t,e){return e=e===i?this._zoom:e,this.options.crs.latLngToPoint(o.latLng(t),e)},unproject:function(t,e){return e=e===i?this._zoom:e,this.options.crs.pointToLatLng(o.point(t),e)},layerPointToLatLng:function(t){var e=o.point(t).add(this.getPixelOrigin());return this.unproject(e)},latLngToLayerPoint:function(t){var e=this.project(o.latLng(t))._round();return e._subtract(this.getPixelOrigin())},containerPointToLayerPoint:function(t){return o.point(t).subtract(this._getMapPanePos())},layerPointToContainerPoint:function(t){return o.point(t).add(this._getMapPanePos())},containerPointToLatLng:function(t){var e=this.containerPointToLayerPoint(o.point(t));return this.layerPointToLatLng(e)},latLngToContainerPoint:function(t){return this.layerPointToContainerPoint(this.latLngToLayerPoint(o.latLng(t)))},mouseEventToContainerPoint:function(t){return o.DomEvent.getMousePosition(t,this._container)},mouseEventToLayerPoint:function(t){return this.containerPointToLayerPoint(this.mouseEventToContainerPoint(t))},mouseEventToLatLng:function(t){return this.layerPointToLatLng(this.mouseEventToLayerPoint(t))},_initContainer:function(t){var e=this._container=o.DomUtil.get(t);if(!e)throw new Error("Map container not found.");if(e._leaflet)throw new Error("Map container is already initialized.");e._leaflet=!0},_initLayout:function(){var t=this._container;o.DomUtil.addClass(t,"leaflet-container"+(o.Browser.touch?" leaflet-touch":"")+(o.Browser.retina?" leaflet-retina":"")+(o.Browser.ielt9?" leaflet-oldie":"")+(this.options.fadeAnimation?" leaflet-fade-anim":""));var e=o.DomUtil.getStyle(t,"position");"absolute"!==e&&"relative"!==e&&"fixed"!==e&&(t.style.position="relative"),this._initPanes(),this._initControlPos&&this._initControlPos()},_initPanes:function(){var t=this._panes={};this._mapPane=t.mapPane=this._createPane("leaflet-map-pane",this._container),this._tilePane=t.tilePane=this._createPane("leaflet-tile-pane",this._mapPane),t.objectsPane=this._createPane("leaflet-objects-pane",this._mapPane),t.shadowPane=this._createPane("leaflet-shadow-pane"),t.overlayPane=this._createPane("leaflet-overlay-pane"),t.markerPane=this._createPane("leaflet-marker-pane"),t.popupPane=this._createPane("leaflet-popup-pane");var e=" leaflet-zoom-hide";this.options.markerZoomAnimation||(o.DomUtil.addClass(t.markerPane,e),o.DomUtil.addClass(t.shadowPane,e),o.DomUtil.addClass(t.popupPane,e))},_createPane:function(t,e){return o.DomUtil.create("div",t,e||this._panes.objectsPane)},_clearPanes:function(){this._container.removeChild(this._mapPane)},_addLayers:function(t){t=t?o.Util.isArray(t)?t:[t]:[];for(var e=0,i=t.length;i>e;e++)this.addLayer(t[e])},_resetView:function(t,e,i,n){var s=this._zoom!==e;n||(this.fire("movestart"),s&&this.fire("zoomstart")),this._zoom=e,this._initialCenter=t,this._initialTopLeftPoint=this._getNewTopLeftPoint(t),i?this._initialTopLeftPoint._add(this._getMapPanePos()):o.DomUtil.setPosition(this._mapPane,new o.Point(0,0)),this._tileLayersToLoad=this._tileLayersNum;var a=!this._loaded;this._loaded=!0,this.fire("viewreset",{hard:!i}),a&&(this.fire("load"),this.eachLayer(this._layerAdd,this)),this.fire("move"),(s||n)&&this.fire("zoomend"),this.fire("moveend",{hard:!i})},_rawPanBy:function(t){o.DomUtil.setPosition(this._mapPane,this._getMapPanePos().subtract(t))},_getZoomSpan:function(){return this.getMaxZoom()-this.getMinZoom()},_updateZoomLevels:function(){var t,e=1/0,n=-1/0,o=this._getZoomSpan();for(t in this._zoomBoundLayers){var s=this._zoomBoundLayers[t];isNaN(s.options.minZoom)||(e=Math.min(e,s.options.minZoom)),isNaN(s.options.maxZoom)||(n=Math.max(n,s.options.maxZoom))}t===i?this._layersMaxZoom=this._layersMinZoom=i:(this._layersMaxZoom=n,this._layersMinZoom=e),o!==this._getZoomSpan()&&this.fire("zoomlevelschange")},_panInsideMaxBounds:function(){this.panInsideBounds(this.options.maxBounds)},_checkIfLoaded:function(){if(!this._loaded)throw new Error("Set map center and zoom first.")},_initEvents:function(e){if(o.DomEvent){e=e||"on",o.DomEvent[e](this._container,"click",this._onMouseClick,this);var i,n,s=["dblclick","mousedown","mouseup","mouseenter","mouseleave","mousemove","contextmenu"];for(i=0,n=s.length;n>i;i++)o.DomEvent[e](this._container,s[i],this._fireMouseEvent,this);this.options.trackResize&&o.DomEvent[e](t,"resize",this._onResize,this)}},_onResize:function(){o.Util.cancelAnimFrame(this._resizeRequest),this._resizeRequest=o.Util.requestAnimFrame(function(){this.invalidateSize({debounceMoveend:!0})},this,!1,this._container)},_onMouseClick:function(t){!this._loaded||!t._simulated&&(this.dragging&&this.dragging.moved()||this.boxZoom&&this.boxZoom.moved())||o.DomEvent._skipped(t)||(this.fire("preclick"),this._fireMouseEvent(t))},_fireMouseEvent:function(t){if(this._loaded&&!o.DomEvent._skipped(t)){var e=t.type;if(e="mouseenter"===e?"mouseover":"mouseleave"===e?"mouseout":e,this.hasEventListeners(e)){"contextmenu"===e&&o.DomEvent.preventDefault(t);var i=this.mouseEventToContainerPoint(t),n=this.containerPointToLayerPoint(i),s=this.layerPointToLatLng(n);this.fire(e,{latlng:s,layerPoint:n,containerPoint:i,originalEvent:t})}}},_onTileLayerLoad:function(){this._tileLayersToLoad--,this._tileLayersNum&&!this._tileLayersToLoad&&this.fire("tilelayersload")},_clearHandlers:function(){for(var t=0,e=this._handlers.length;e>t;t++)this._handlers[t].disable()},whenReady:function(t,e){return this._loaded?t.call(e||this,this):this.on("load",t,e),this},_layerAdd:function(t){t.onAdd(this),this.fire("layeradd",{layer:t})},_getMapPanePos:function(){return o.DomUtil.getPosition(this._mapPane)},_moved:function(){var t=this._getMapPanePos();return t&&!t.equals([0,0])},_getTopLeftPoint:function(){return this.getPixelOrigin().subtract(this._getMapPanePos())},_getNewTopLeftPoint:function(t,e){var i=this.getSize()._divideBy(2);return this.project(t,e)._subtract(i)._round()},_latLngToNewLayerPoint:function(t,e,i){var n=this._getNewTopLeftPoint(i,e).add(this._getMapPanePos());return this.project(t,e)._subtract(n)},_getCenterLayerPoint:function(){return this.containerPointToLayerPoint(this.getSize()._divideBy(2))},_getCenterOffset:function(t){return this.latLngToLayerPoint(t).subtract(this._getCenterLayerPoint())},_limitCenter:function(t,e,i){if(!i)return t;var n=this.project(t,e),s=this.getSize().divideBy(2),a=new o.Bounds(n.subtract(s),n.add(s)),r=this._getBoundsOffset(a,i,e);return this.unproject(n.add(r),e)},_limitOffset:function(t,e){if(!e)return t;var i=this.getPixelBounds(),n=new o.Bounds(i.min.add(t),i.max.add(t));return t.add(this._getBoundsOffset(n,e))},_getBoundsOffset:function(t,e,i){var n=this.project(e.getNorthWest(),i).subtract(t.min),s=this.project(e.getSouthEast(),i).subtract(t.max),a=this._rebound(n.x,-s.x),r=this._rebound(n.y,-s.y);return new o.Point(a,r)},_rebound:function(t,e){return t+e>0?Math.round(t-e)/2:Math.max(0,Math.ceil(t))-Math.max(0,Math.floor(e))},_limitZoom:function(t){var e=this.getMinZoom(),i=this.getMaxZoom();return Math.max(e,Math.min(i,t))}}),o.map=function(t,e){return new o.Map(t,e)},o.Projection.Mercator={MAX_LATITUDE:85.0840591556,R_MINOR:6356752.314245179,R_MAJOR:6378137,project:function(t){var e=o.LatLng.DEG_TO_RAD,i=this.MAX_LATITUDE,n=Math.max(Math.min(i,t.lat),-i),s=this.R_MAJOR,a=this.R_MINOR,r=t.lng*e*s,h=n*e,l=a/s,u=Math.sqrt(1-l*l),c=u*Math.sin(h);c=Math.pow((1-c)/(1+c),.5*u);var d=Math.tan(.5*(.5*Math.PI-h))/c;return h=-s*Math.log(d),new o.Point(r,h)},unproject:function(t){for(var e,i=o.LatLng.RAD_TO_DEG,n=this.R_MAJOR,s=this.R_MINOR,a=t.x*i/n,r=s/n,h=Math.sqrt(1-r*r),l=Math.exp(-t.y/n),u=Math.PI/2-2*Math.atan(l),c=15,d=1e-7,p=c,_=.1;Math.abs(_)>d&&--p>0;)e=h*Math.sin(u),_=Math.PI/2-2*Math.atan(l*Math.pow((1-e)/(1+e),.5*h))-u,u+=_;
+return new o.LatLng(u*i,a)}},o.CRS.EPSG3395=o.extend({},o.CRS,{code:"EPSG:3395",projection:o.Projection.Mercator,transformation:function(){var t=o.Projection.Mercator,e=t.R_MAJOR,i=.5/(Math.PI*e);return new o.Transformation(i,.5,-i,.5)}()}),o.TileLayer=o.Class.extend({includes:o.Mixin.Events,options:{minZoom:0,maxZoom:18,tileSize:256,subdomains:"abc",errorTileUrl:"",attribution:"",zoomOffset:0,opacity:1,unloadInvisibleTiles:o.Browser.mobile,updateWhenIdle:o.Browser.mobile},initialize:function(t,e){e=o.setOptions(this,e),e.detectRetina&&o.Browser.retina&&e.maxZoom>0&&(e.tileSize=Math.floor(e.tileSize/2),e.zoomOffset++,e.minZoom>0&&e.minZoom--,this.options.maxZoom--),e.bounds&&(e.bounds=o.latLngBounds(e.bounds)),this._url=t;var i=this.options.subdomains;"string"==typeof i&&(this.options.subdomains=i.split(""))},onAdd:function(t){this._map=t,this._animated=t._zoomAnimated,this._initContainer(),t.on({viewreset:this._reset,moveend:this._update},this),this._animated&&t.on({zoomanim:this._animateZoom,zoomend:this._endZoomAnim},this),this.options.updateWhenIdle||(this._limitedUpdate=o.Util.limitExecByInterval(this._update,150,this),t.on("move",this._limitedUpdate,this)),this._reset(),this._update()},addTo:function(t){return t.addLayer(this),this},onRemove:function(t){this._container.parentNode.removeChild(this._container),t.off({viewreset:this._reset,moveend:this._update},this),this._animated&&t.off({zoomanim:this._animateZoom,zoomend:this._endZoomAnim},this),this.options.updateWhenIdle||t.off("move",this._limitedUpdate,this),this._container=null,this._map=null},bringToFront:function(){var t=this._map._panes.tilePane;return this._container&&(t.appendChild(this._container),this._setAutoZIndex(t,Math.max)),this},bringToBack:function(){var t=this._map._panes.tilePane;return this._container&&(t.insertBefore(this._container,t.firstChild),this._setAutoZIndex(t,Math.min)),this},getAttribution:function(){return this.options.attribution},getContainer:function(){return this._container},setOpacity:function(t){return this.options.opacity=t,this._map&&this._updateOpacity(),this},setZIndex:function(t){return this.options.zIndex=t,this._updateZIndex(),this},setUrl:function(t,e){return this._url=t,e||this.redraw(),this},redraw:function(){return this._map&&(this._reset({hard:!0}),this._update()),this},_updateZIndex:function(){this._container&&this.options.zIndex!==i&&(this._container.style.zIndex=this.options.zIndex)},_setAutoZIndex:function(t,e){var i,n,o,s=t.children,a=-e(1/0,-1/0);for(n=0,o=s.length;o>n;n++)s[n]!==this._container&&(i=parseInt(s[n].style.zIndex,10),isNaN(i)||(a=e(a,i)));this.options.zIndex=this._container.style.zIndex=(isFinite(a)?a:0)+e(1,-1)},_updateOpacity:function(){var t,e=this._tiles;if(o.Browser.ielt9)for(t in e)o.DomUtil.setOpacity(e[t],this.options.opacity);else o.DomUtil.setOpacity(this._container,this.options.opacity)},_initContainer:function(){var t=this._map._panes.tilePane;if(!this._container){if(this._container=o.DomUtil.create("div","leaflet-layer"),this._updateZIndex(),this._animated){var e="leaflet-tile-container";this._bgBuffer=o.DomUtil.create("div",e,this._container),this._tileContainer=o.DomUtil.create("div",e,this._container)}else this._tileContainer=this._container;t.appendChild(this._container),this.options.opacity<1&&this._updateOpacity()}},_reset:function(t){for(var e in this._tiles)this.fire("tileunload",{tile:this._tiles[e]});this._tiles={},this._tilesToLoad=0,this.options.reuseTiles&&(this._unusedTiles=[]),this._tileContainer.innerHTML="",this._animated&&t&&t.hard&&this._clearBgBuffer(),this._initContainer()},_getTileSize:function(){var t=this._map,e=t.getZoom()+this.options.zoomOffset,i=this.options.maxNativeZoom,n=this.options.tileSize;return i&&e>i&&(n=Math.round(t.getZoomScale(e)/t.getZoomScale(i)*n)),n},_update:function(){if(this._map){var t=this._map,e=t.getPixelBounds(),i=t.getZoom(),n=this._getTileSize();if(!(i>this.options.maxZoom||in;n++)this._addTile(a[n],l);this._tileContainer.appendChild(l)}},_tileShouldBeLoaded:function(t){if(t.x+":"+t.y in this._tiles)return!1;var e=this.options;if(!e.continuousWorld){var i=this._getWrapTileNum();if(e.noWrap&&(t.x<0||t.x>=i.x)||t.y<0||t.y>=i.y)return!1}if(e.bounds){var n=e.tileSize,o=t.multiplyBy(n),s=o.add([n,n]),a=this._map.unproject(o),r=this._map.unproject(s);if(e.continuousWorld||e.noWrap||(a=a.wrap(),r=r.wrap()),!e.bounds.intersects([a,r]))return!1}return!0},_removeOtherTiles:function(t){var e,i,n,o;for(o in this._tiles)e=o.split(":"),i=parseInt(e[0],10),n=parseInt(e[1],10),(it.max.x||nt.max.y)&&this._removeTile(o)},_removeTile:function(t){var e=this._tiles[t];this.fire("tileunload",{tile:e,url:e.src}),this.options.reuseTiles?(o.DomUtil.removeClass(e,"leaflet-tile-loaded"),this._unusedTiles.push(e)):e.parentNode===this._tileContainer&&this._tileContainer.removeChild(e),o.Browser.android||(e.onload=null,e.src=o.Util.emptyImageUrl),delete this._tiles[t]},_addTile:function(t,e){var i=this._getTilePos(t),n=this._getTile();o.DomUtil.setPosition(n,i,o.Browser.chrome),this._tiles[t.x+":"+t.y]=n,this._loadTile(n,t),n.parentNode!==this._tileContainer&&e.appendChild(n)},_getZoomForUrl:function(){var t=this.options,e=this._map.getZoom();return t.zoomReverse&&(e=t.maxZoom-e),e+=t.zoomOffset,t.maxNativeZoom?Math.min(e,t.maxNativeZoom):e},_getTilePos:function(t){var e=this._map.getPixelOrigin(),i=this._getTileSize();return t.multiplyBy(i).subtract(e)},getTileUrl:function(t){return o.Util.template(this._url,o.extend({s:this._getSubdomain(t),z:t.z,x:t.x,y:t.y},this.options))},_getWrapTileNum:function(){var t=this._map.options.crs,e=t.getSize(this._map.getZoom());return e.divideBy(this._getTileSize())._floor()},_adjustTilePoint:function(t){var e=this._getWrapTileNum();this.options.continuousWorld||this.options.noWrap||(t.x=(t.x%e.x+e.x)%e.x),this.options.tms&&(t.y=e.y-t.y-1),t.z=this._getZoomForUrl()},_getSubdomain:function(t){var e=Math.abs(t.x+t.y)%this.options.subdomains.length;return this.options.subdomains[e]},_getTile:function(){if(this.options.reuseTiles&&this._unusedTiles.length>0){var t=this._unusedTiles.pop();return this._resetTile(t),t}return this._createTile()},_resetTile:function(){},_createTile:function(){var t=o.DomUtil.create("img","leaflet-tile");return t.style.width=t.style.height=this._getTileSize()+"px",t.galleryimg="no",t.onselectstart=t.onmousemove=o.Util.falseFn,o.Browser.ielt9&&this.options.opacity!==i&&o.DomUtil.setOpacity(t,this.options.opacity),o.Browser.mobileWebkit3d&&(t.style.WebkitBackfaceVisibility="hidden"),t},_loadTile:function(t,e){t._layer=this,t.onload=this._tileOnLoad,t.onerror=this._tileOnError,this._adjustTilePoint(e),t.src=this.getTileUrl(e),this.fire("tileloadstart",{tile:t,url:t.src})},_tileLoaded:function(){this._tilesToLoad--,this._animated&&o.DomUtil.addClass(this._tileContainer,"leaflet-zoom-animated"),this._tilesToLoad||(this.fire("load"),this._animated&&(clearTimeout(this._clearBgBufferTimer),this._clearBgBufferTimer=setTimeout(o.bind(this._clearBgBuffer,this),500)))},_tileOnLoad:function(){var t=this._layer;this.src!==o.Util.emptyImageUrl&&(o.DomUtil.addClass(this,"leaflet-tile-loaded"),t.fire("tileload",{tile:this,url:this.src})),t._tileLoaded()},_tileOnError:function(){var t=this._layer;t.fire("tileerror",{tile:this,url:this.src});var e=t.options.errorTileUrl;e&&(this.src=e),t._tileLoaded()}}),o.tileLayer=function(t,e){return new o.TileLayer(t,e)},o.TileLayer.WMS=o.TileLayer.extend({defaultWmsParams:{service:"WMS",request:"GetMap",version:"1.1.1",layers:"",styles:"",format:"image/jpeg",transparent:!1},initialize:function(t,e){this._url=t;var i=o.extend({},this.defaultWmsParams),n=e.tileSize||this.options.tileSize;i.width=i.height=e.detectRetina&&o.Browser.retina?2*n:n;for(var s in e)this.options.hasOwnProperty(s)||"crs"===s||(i[s]=e[s]);this.wmsParams=i,o.setOptions(this,e)},onAdd:function(t){this._crs=this.options.crs||t.options.crs,this._wmsVersion=parseFloat(this.wmsParams.version);var e=this._wmsVersion>=1.3?"crs":"srs";this.wmsParams[e]=this._crs.code,o.TileLayer.prototype.onAdd.call(this,t)},getTileUrl:function(t){var e=this._map,i=this.options.tileSize,n=t.multiplyBy(i),s=n.add([i,i]),a=this._crs.project(e.unproject(n,t.z)),r=this._crs.project(e.unproject(s,t.z)),h=this._wmsVersion>=1.3&&this._crs===o.CRS.EPSG4326?[r.y,a.x,a.y,r.x].join(","):[a.x,r.y,r.x,a.y].join(","),l=o.Util.template(this._url,{s:this._getSubdomain(t)});return l+o.Util.getParamString(this.wmsParams,l,!0)+"&BBOX="+h},setParams:function(t,e){return o.extend(this.wmsParams,t),e||this.redraw(),this}}),o.tileLayer.wms=function(t,e){return new o.TileLayer.WMS(t,e)},o.TileLayer.Canvas=o.TileLayer.extend({options:{async:!1},initialize:function(t){o.setOptions(this,t)},redraw:function(){this._map&&(this._reset({hard:!0}),this._update());for(var t in this._tiles)this._redrawTile(this._tiles[t]);return this},_redrawTile:function(t){this.drawTile(t,t._tilePoint,this._map._zoom)},_createTile:function(){var t=o.DomUtil.create("canvas","leaflet-tile");return t.width=t.height=this.options.tileSize,t.onselectstart=t.onmousemove=o.Util.falseFn,t},_loadTile:function(t,e){t._layer=this,t._tilePoint=e,this._redrawTile(t),this.options.async||this.tileDrawn(t)},drawTile:function(){},tileDrawn:function(t){this._tileOnLoad.call(t)}}),o.tileLayer.canvas=function(t){return new o.TileLayer.Canvas(t)},o.ImageOverlay=o.Class.extend({includes:o.Mixin.Events,options:{opacity:1},initialize:function(t,e,i){this._url=t,this._bounds=o.latLngBounds(e),o.setOptions(this,i)},onAdd:function(t){this._map=t,this._image||this._initImage(),t._panes.overlayPane.appendChild(this._image),t.on("viewreset",this._reset,this),t.options.zoomAnimation&&o.Browser.any3d&&t.on("zoomanim",this._animateZoom,this),this._reset()},onRemove:function(t){t.getPanes().overlayPane.removeChild(this._image),t.off("viewreset",this._reset,this),t.options.zoomAnimation&&t.off("zoomanim",this._animateZoom,this)},addTo:function(t){return t.addLayer(this),this},setOpacity:function(t){return this.options.opacity=t,this._updateOpacity(),this},bringToFront:function(){return this._image&&this._map._panes.overlayPane.appendChild(this._image),this},bringToBack:function(){var t=this._map._panes.overlayPane;return this._image&&t.insertBefore(this._image,t.firstChild),this},setUrl:function(t){this._url=t,this._image.src=this._url},getAttribution:function(){return this.options.attribution},_initImage:function(){this._image=o.DomUtil.create("img","leaflet-image-layer"),this._map.options.zoomAnimation&&o.Browser.any3d?o.DomUtil.addClass(this._image,"leaflet-zoom-animated"):o.DomUtil.addClass(this._image,"leaflet-zoom-hide"),this._updateOpacity(),o.extend(this._image,{galleryimg:"no",onselectstart:o.Util.falseFn,onmousemove:o.Util.falseFn,onload:o.bind(this._onImageLoad,this),src:this._url})},_animateZoom:function(t){var e=this._map,i=this._image,n=e.getZoomScale(t.zoom),s=this._bounds.getNorthWest(),a=this._bounds.getSouthEast(),r=e._latLngToNewLayerPoint(s,t.zoom,t.center),h=e._latLngToNewLayerPoint(a,t.zoom,t.center)._subtract(r),l=r._add(h._multiplyBy(.5*(1-1/n)));i.style[o.DomUtil.TRANSFORM]=o.DomUtil.getTranslateString(l)+" scale("+n+") "},_reset:function(){var t=this._image,e=this._map.latLngToLayerPoint(this._bounds.getNorthWest()),i=this._map.latLngToLayerPoint(this._bounds.getSouthEast())._subtract(e);o.DomUtil.setPosition(t,e),t.style.width=i.x+"px",t.style.height=i.y+"px"},_onImageLoad:function(){this.fire("load")},_updateOpacity:function(){o.DomUtil.setOpacity(this._image,this.options.opacity)}}),o.imageOverlay=function(t,e,i){return new o.ImageOverlay(t,e,i)},o.Icon=o.Class.extend({options:{className:""},initialize:function(t){o.setOptions(this,t)},createIcon:function(t){return this._createIcon("icon",t)},createShadow:function(t){return this._createIcon("shadow",t)},_createIcon:function(t,e){var i=this._getIconUrl(t);if(!i){if("icon"===t)throw new Error("iconUrl not set in Icon options (see the docs).");return null}var n;return n=e&&"IMG"===e.tagName?this._createImg(i,e):this._createImg(i),this._setIconStyles(n,t),n},_setIconStyles:function(t,e){var i,n=this.options,s=o.point(n[e+"Size"]);i=o.point("shadow"===e?n.shadowAnchor||n.iconAnchor:n.iconAnchor),!i&&s&&(i=s.divideBy(2,!0)),t.className="leaflet-marker-"+e+" "+n.className,i&&(t.style.marginLeft=-i.x+"px",t.style.marginTop=-i.y+"px"),s&&(t.style.width=s.x+"px",t.style.height=s.y+"px")},_createImg:function(t,i){return i=i||e.createElement("img"),i.src=t,i},_getIconUrl:function(t){return o.Browser.retina&&this.options[t+"RetinaUrl"]?this.options[t+"RetinaUrl"]:this.options[t+"Url"]}}),o.icon=function(t){return new o.Icon(t)},o.Icon.Default=o.Icon.extend({options:{iconSize:[25,41],iconAnchor:[12,41],popupAnchor:[1,-34],shadowSize:[41,41]},_getIconUrl:function(t){var e=t+"Url";if(this.options[e])return this.options[e];o.Browser.retina&&"icon"===t&&(t+="-2x");var i=o.Icon.Default.imagePath;if(!i)throw new Error("Couldn't autodetect L.Icon.Default.imagePath, set it manually.");return i+"/marker-"+t+".png"}}),o.Icon.Default.imagePath=function(){var t,i,n,o,s,a=e.getElementsByTagName("script"),r=/[\/^]leaflet[\-\._]?([\w\-\._]*)\.js\??/;for(t=0,i=a.length;i>t;t++)if(n=a[t].src,o=n.match(r))return s=n.split(r)[0],(s?s+"/":"")+"images"}(),o.Marker=o.Class.extend({includes:o.Mixin.Events,options:{icon:new o.Icon.Default,title:"",alt:"",clickable:!0,draggable:!1,keyboard:!0,zIndexOffset:0,opacity:1,riseOnHover:!1,riseOffset:250},initialize:function(t,e){o.setOptions(this,e),this._latlng=o.latLng(t)},onAdd:function(t){this._map=t,t.on("viewreset",this.update,this),this._initIcon(),this.update(),this.fire("add"),t.options.zoomAnimation&&t.options.markerZoomAnimation&&t.on("zoomanim",this._animateZoom,this)},addTo:function(t){return t.addLayer(this),this},onRemove:function(t){this.dragging&&this.dragging.disable(),this._removeIcon(),this._removeShadow(),this.fire("remove"),t.off({viewreset:this.update,zoomanim:this._animateZoom},this),this._map=null},getLatLng:function(){return this._latlng},setLatLng:function(t){return this._latlng=o.latLng(t),this.update(),this.fire("move",{latlng:this._latlng})},setZIndexOffset:function(t){return this.options.zIndexOffset=t,this.update(),this},setIcon:function(t){return this.options.icon=t,this._map&&(this._initIcon(),this.update()),this._popup&&this.bindPopup(this._popup),this},update:function(){if(this._icon){var t=this._map.latLngToLayerPoint(this._latlng).round();this._setPos(t)}return this},_initIcon:function(){var t=this.options,e=this._map,i=e.options.zoomAnimation&&e.options.markerZoomAnimation,n=i?"leaflet-zoom-animated":"leaflet-zoom-hide",s=t.icon.createIcon(this._icon),a=!1;s!==this._icon&&(this._icon&&this._removeIcon(),a=!0,t.title&&(s.title=t.title),t.alt&&(s.alt=t.alt)),o.DomUtil.addClass(s,n),t.keyboard&&(s.tabIndex="0"),this._icon=s,this._initInteraction(),t.riseOnHover&&o.DomEvent.on(s,"mouseover",this._bringToFront,this).on(s,"mouseout",this._resetZIndex,this);var r=t.icon.createShadow(this._shadow),h=!1;r!==this._shadow&&(this._removeShadow(),h=!0),r&&o.DomUtil.addClass(r,n),this._shadow=r,t.opacity<1&&this._updateOpacity();var l=this._map._panes;a&&l.markerPane.appendChild(this._icon),r&&h&&l.shadowPane.appendChild(this._shadow)},_removeIcon:function(){this.options.riseOnHover&&o.DomEvent.off(this._icon,"mouseover",this._bringToFront).off(this._icon,"mouseout",this._resetZIndex),this._map._panes.markerPane.removeChild(this._icon),this._icon=null},_removeShadow:function(){this._shadow&&this._map._panes.shadowPane.removeChild(this._shadow),this._shadow=null},_setPos:function(t){o.DomUtil.setPosition(this._icon,t),this._shadow&&o.DomUtil.setPosition(this._shadow,t),this._zIndex=t.y+this.options.zIndexOffset,this._resetZIndex()},_updateZIndex:function(t){this._icon.style.zIndex=this._zIndex+t},_animateZoom:function(t){var e=this._map._latLngToNewLayerPoint(this._latlng,t.zoom,t.center).round();this._setPos(e)},_initInteraction:function(){if(this.options.clickable){var t=this._icon,e=["dblclick","mousedown","mouseover","mouseout","contextmenu"];o.DomUtil.addClass(t,"leaflet-clickable"),o.DomEvent.on(t,"click",this._onMouseClick,this),o.DomEvent.on(t,"keypress",this._onKeyPress,this);for(var i=0;is?(e.height=s+"px",o.DomUtil.addClass(t,a)):o.DomUtil.removeClass(t,a),this._containerWidth=this._container.offsetWidth},_updatePosition:function(){if(this._map){var t=this._map.latLngToLayerPoint(this._latlng),e=this._animated,i=o.point(this.options.offset);e&&o.DomUtil.setPosition(this._container,t),this._containerBottom=-i.y-(e?0:t.y),this._containerLeft=-Math.round(this._containerWidth/2)+i.x+(e?0:t.x),this._container.style.bottom=this._containerBottom+"px",this._container.style.left=this._containerLeft+"px"}},_zoomAnimation:function(t){var e=this._map._latLngToNewLayerPoint(this._latlng,t.zoom,t.center);o.DomUtil.setPosition(this._container,e)},_adjustPan:function(){if(this.options.autoPan){var t=this._map,e=this._container.offsetHeight,i=this._containerWidth,n=new o.Point(this._containerLeft,-e-this._containerBottom);this._animated&&n._add(o.DomUtil.getPosition(this._container));var s=t.layerPointToContainerPoint(n),a=o.point(this.options.autoPanPadding),r=o.point(this.options.autoPanPaddingTopLeft||a),h=o.point(this.options.autoPanPaddingBottomRight||a),l=t.getSize(),u=0,c=0;s.x+i+h.x>l.x&&(u=s.x+i-l.x+h.x),s.x-u-r.x<0&&(u=s.x-r.x),s.y+e+h.y>l.y&&(c=s.y+e-l.y+h.y),s.y-c-r.y<0&&(c=s.y-r.y),(u||c)&&t.fire("autopanstart").panBy([u,c])}},_onCloseButtonClick:function(t){this._close(),o.DomEvent.stop(t)}}),o.popup=function(t,e){return new o.Popup(t,e)},o.Map.include({openPopup:function(t,e,i){if(this.closePopup(),!(t instanceof o.Popup)){var n=t;t=new o.Popup(i).setLatLng(e).setContent(n)}return t._isOpen=!0,this._popup=t,this.addLayer(t)},closePopup:function(t){return t&&t!==this._popup||(t=this._popup,this._popup=null),t&&(this.removeLayer(t),t._isOpen=!1),this}}),o.Marker.include({openPopup:function(){return this._popup&&this._map&&!this._map.hasLayer(this._popup)&&(this._popup.setLatLng(this._latlng),this._map.openPopup(this._popup)),this},closePopup:function(){return this._popup&&this._popup._close(),this},togglePopup:function(){return this._popup&&(this._popup._isOpen?this.closePopup():this.openPopup()),this},bindPopup:function(t,e){var i=o.point(this.options.icon.options.popupAnchor||[0,0]);return i=i.add(o.Popup.prototype.options.offset),e&&e.offset&&(i=i.add(e.offset)),e=o.extend({offset:i},e),this._popupHandlersAdded||(this.on("click",this.togglePopup,this).on("remove",this.closePopup,this).on("move",this._movePopup,this),this._popupHandlersAdded=!0),t instanceof o.Popup?(o.setOptions(t,e),this._popup=t):this._popup=new o.Popup(e,this).setContent(t),this},setPopupContent:function(t){return this._popup&&this._popup.setContent(t),this},unbindPopup:function(){return this._popup&&(this._popup=null,this.off("click",this.togglePopup,this).off("remove",this.closePopup,this).off("move",this._movePopup,this),this._popupHandlersAdded=!1),this},getPopup:function(){return this._popup},_movePopup:function(t){this._popup.setLatLng(t.latlng)}}),o.LayerGroup=o.Class.extend({initialize:function(t){this._layers={};var e,i;if(t)for(e=0,i=t.length;i>e;e++)this.addLayer(t[e])},addLayer:function(t){var e=this.getLayerId(t);return this._layers[e]=t,this._map&&this._map.addLayer(t),this},removeLayer:function(t){var e=t in this._layers?t:this.getLayerId(t);return this._map&&this._layers[e]&&this._map.removeLayer(this._layers[e]),delete this._layers[e],this},hasLayer:function(t){return t?t in this._layers||this.getLayerId(t)in this._layers:!1},clearLayers:function(){return this.eachLayer(this.removeLayer,this),this},invoke:function(t){var e,i,n=Array.prototype.slice.call(arguments,1);for(e in this._layers)i=this._layers[e],i[t]&&i[t].apply(i,n);return this},onAdd:function(t){this._map=t,this.eachLayer(t.addLayer,t)},onRemove:function(t){this.eachLayer(t.removeLayer,t),this._map=null},addTo:function(t){return t.addLayer(this),this},eachLayer:function(t,e){for(var i in this._layers)t.call(e,this._layers[i]);return this},getLayer:function(t){return this._layers[t]},getLayers:function(){var t=[];for(var e in this._layers)t.push(this._layers[e]);return t},setZIndex:function(t){return this.invoke("setZIndex",t)},getLayerId:function(t){return o.stamp(t)}}),o.layerGroup=function(t){return new o.LayerGroup(t)},o.FeatureGroup=o.LayerGroup.extend({includes:o.Mixin.Events,statics:{EVENTS:"click dblclick mouseover mouseout mousemove contextmenu popupopen popupclose"},addLayer:function(t){return this.hasLayer(t)?this:("on"in t&&t.on(o.FeatureGroup.EVENTS,this._propagateEvent,this),o.LayerGroup.prototype.addLayer.call(this,t),this._popupContent&&t.bindPopup&&t.bindPopup(this._popupContent,this._popupOptions),this.fire("layeradd",{layer:t}))},removeLayer:function(t){return this.hasLayer(t)?(t in this._layers&&(t=this._layers[t]),t.off(o.FeatureGroup.EVENTS,this._propagateEvent,this),o.LayerGroup.prototype.removeLayer.call(this,t),this._popupContent&&this.invoke("unbindPopup"),this.fire("layerremove",{layer:t})):this},bindPopup:function(t,e){return this._popupContent=t,this._popupOptions=e,this.invoke("bindPopup",t,e)},openPopup:function(t){for(var e in this._layers){this._layers[e].openPopup(t);break}return this},setStyle:function(t){return this.invoke("setStyle",t)},bringToFront:function(){return this.invoke("bringToFront")},bringToBack:function(){return this.invoke("bringToBack")},getBounds:function(){var t=new o.LatLngBounds;return this.eachLayer(function(e){t.extend(e instanceof o.Marker?e.getLatLng():e.getBounds())}),t},_propagateEvent:function(t){t=o.extend({layer:t.target,target:this},t),this.fire(t.type,t)}}),o.featureGroup=function(t){return new o.FeatureGroup(t)},o.Path=o.Class.extend({includes:[o.Mixin.Events],statics:{CLIP_PADDING:function(){var e=o.Browser.mobile?1280:2e3,i=(e/Math.max(t.outerWidth,t.outerHeight)-1)/2;return Math.max(0,Math.min(.5,i))}()},options:{stroke:!0,color:"#0033ff",dashArray:null,lineCap:null,lineJoin:null,weight:5,opacity:.5,fill:!1,fillColor:null,fillOpacity:.2,clickable:!0},initialize:function(t){o.setOptions(this,t)},onAdd:function(t){this._map=t,this._container||(this._initElements(),this._initEvents()),this.projectLatlngs(),this._updatePath(),this._container&&this._map._pathRoot.appendChild(this._container),this.fire("add"),t.on({viewreset:this.projectLatlngs,moveend:this._updatePath},this)},addTo:function(t){return t.addLayer(this),this},onRemove:function(t){t._pathRoot.removeChild(this._container),this.fire("remove"),this._map=null,o.Browser.vml&&(this._container=null,this._stroke=null,this._fill=null),t.off({viewreset:this.projectLatlngs,moveend:this._updatePath},this)},projectLatlngs:function(){},setStyle:function(t){return o.setOptions(this,t),this._container&&this._updateStyle(),this},redraw:function(){return this._map&&(this.projectLatlngs(),this._updatePath()),this}}),o.Map.include({_updatePathViewport:function(){var t=o.Path.CLIP_PADDING,e=this.getSize(),i=o.DomUtil.getPosition(this._mapPane),n=i.multiplyBy(-1)._subtract(e.multiplyBy(t)._round()),s=n.add(e.multiplyBy(1+2*t)._round());this._pathViewport=new o.Bounds(n,s)}}),o.Path.SVG_NS="http://www.w3.org/2000/svg",o.Browser.svg=!(!e.createElementNS||!e.createElementNS(o.Path.SVG_NS,"svg").createSVGRect),o.Path=o.Path.extend({statics:{SVG:o.Browser.svg},bringToFront:function(){var t=this._map._pathRoot,e=this._container;return e&&t.lastChild!==e&&t.appendChild(e),this},bringToBack:function(){var t=this._map._pathRoot,e=this._container,i=t.firstChild;return e&&i!==e&&t.insertBefore(e,i),this},getPathString:function(){},_createElement:function(t){return e.createElementNS(o.Path.SVG_NS,t)},_initElements:function(){this._map._initPathRoot(),this._initPath(),this._initStyle()},_initPath:function(){this._container=this._createElement("g"),this._path=this._createElement("path"),this.options.className&&o.DomUtil.addClass(this._path,this.options.className),this._container.appendChild(this._path)},_initStyle:function(){this.options.stroke&&(this._path.setAttribute("stroke-linejoin","round"),this._path.setAttribute("stroke-linecap","round")),this.options.fill&&this._path.setAttribute("fill-rule","evenodd"),this.options.pointerEvents&&this._path.setAttribute("pointer-events",this.options.pointerEvents),this.options.clickable||this.options.pointerEvents||this._path.setAttribute("pointer-events","none"),this._updateStyle()},_updateStyle:function(){this.options.stroke?(this._path.setAttribute("stroke",this.options.color),this._path.setAttribute("stroke-opacity",this.options.opacity),this._path.setAttribute("stroke-width",this.options.weight),this.options.dashArray?this._path.setAttribute("stroke-dasharray",this.options.dashArray):this._path.removeAttribute("stroke-dasharray"),this.options.lineCap&&this._path.setAttribute("stroke-linecap",this.options.lineCap),this.options.lineJoin&&this._path.setAttribute("stroke-linejoin",this.options.lineJoin)):this._path.setAttribute("stroke","none"),this.options.fill?(this._path.setAttribute("fill",this.options.fillColor||this.options.color),this._path.setAttribute("fill-opacity",this.options.fillOpacity)):this._path.setAttribute("fill","none")},_updatePath:function(){var t=this.getPathString();t||(t="M0 0"),this._path.setAttribute("d",t)},_initEvents:function(){if(this.options.clickable){(o.Browser.svg||!o.Browser.vml)&&o.DomUtil.addClass(this._path,"leaflet-clickable"),o.DomEvent.on(this._container,"click",this._onMouseClick,this);for(var t=["dblclick","mousedown","mouseover","mouseout","mousemove","contextmenu"],e=0;e ';var i=t.firstChild;return i.style.behavior="url(#default#VML)",i&&"object"==typeof i.adj}catch(n){return!1}}(),o.Path=o.Browser.svg||!o.Browser.vml?o.Path:o.Path.extend({statics:{VML:!0,CLIP_PADDING:.02},_createElement:function(){try{return e.namespaces.add("lvml","urn:schemas-microsoft-com:vml"),function(t){return e.createElement("')}}catch(t){return function(t){return e.createElement("<"+t+' xmlns="urn:schemas-microsoft.com:vml" class="lvml">')}}}(),_initPath:function(){var t=this._container=this._createElement("shape");o.DomUtil.addClass(t,"leaflet-vml-shape"+(this.options.className?" "+this.options.className:"")),this.options.clickable&&o.DomUtil.addClass(t,"leaflet-clickable"),t.coordsize="1 1",this._path=this._createElement("path"),t.appendChild(this._path),this._map._pathRoot.appendChild(t)},_initStyle:function(){this._updateStyle()},_updateStyle:function(){var t=this._stroke,e=this._fill,i=this.options,n=this._container;n.stroked=i.stroke,n.filled=i.fill,i.stroke?(t||(t=this._stroke=this._createElement("stroke"),t.endcap="round",n.appendChild(t)),t.weight=i.weight+"px",t.color=i.color,t.opacity=i.opacity,t.dashStyle=i.dashArray?o.Util.isArray(i.dashArray)?i.dashArray.join(" "):i.dashArray.replace(/( *, *)/g," "):"",i.lineCap&&(t.endcap=i.lineCap.replace("butt","flat")),i.lineJoin&&(t.joinstyle=i.lineJoin)):t&&(n.removeChild(t),this._stroke=null),i.fill?(e||(e=this._fill=this._createElement("fill"),n.appendChild(e)),e.color=i.fillColor||i.color,e.opacity=i.fillOpacity):e&&(n.removeChild(e),this._fill=null)},_updatePath:function(){var t=this._container.style;t.display="none",this._path.v=this.getPathString()+" ",t.display=""}}),o.Map.include(o.Browser.svg||!o.Browser.vml?{}:{_initPathRoot:function(){if(!this._pathRoot){var t=this._pathRoot=e.createElement("div");t.className="leaflet-vml-container",this._panes.overlayPane.appendChild(t),this.on("moveend",this._updatePathViewport),this._updatePathViewport()}}}),o.Browser.canvas=function(){return!!e.createElement("canvas").getContext}(),o.Path=o.Path.SVG&&!t.L_PREFER_CANVAS||!o.Browser.canvas?o.Path:o.Path.extend({statics:{CANVAS:!0,SVG:!1},redraw:function(){return this._map&&(this.projectLatlngs(),this._requestUpdate()),this},setStyle:function(t){return o.setOptions(this,t),this._map&&(this._updateStyle(),this._requestUpdate()),this},onRemove:function(t){t.off("viewreset",this.projectLatlngs,this).off("moveend",this._updatePath,this),this.options.clickable&&(this._map.off("click",this._onClick,this),this._map.off("mousemove",this._onMouseMove,this)),this._requestUpdate(),this.fire("remove"),this._map=null},_requestUpdate:function(){this._map&&!o.Path._updateRequest&&(o.Path._updateRequest=o.Util.requestAnimFrame(this._fireMapMoveEnd,this._map))},_fireMapMoveEnd:function(){o.Path._updateRequest=null,this.fire("moveend")},_initElements:function(){this._map._initPathRoot(),this._ctx=this._map._canvasCtx},_updateStyle:function(){var t=this.options;t.stroke&&(this._ctx.lineWidth=t.weight,this._ctx.strokeStyle=t.color),t.fill&&(this._ctx.fillStyle=t.fillColor||t.color)},_drawPath:function(){var t,e,i,n,s,a;for(this._ctx.beginPath(),t=0,i=this._parts.length;i>t;t++){for(e=0,n=this._parts[t].length;n>e;e++)s=this._parts[t][e],a=(0===e?"move":"line")+"To",this._ctx[a](s.x,s.y);this instanceof o.Polygon&&this._ctx.closePath()}},_checkIfEmpty:function(){return!this._parts.length},_updatePath:function(){if(!this._checkIfEmpty()){var t=this._ctx,e=this.options;this._drawPath(),t.save(),this._updateStyle(),e.fill&&(t.globalAlpha=e.fillOpacity,t.fill()),e.stroke&&(t.globalAlpha=e.opacity,t.stroke()),t.restore()}},_initEvents:function(){this.options.clickable&&(this._map.on("mousemove",this._onMouseMove,this),this._map.on("click",this._onClick,this))},_onClick:function(t){this._containsPoint(t.layerPoint)&&this.fire("click",t)},_onMouseMove:function(t){this._map&&!this._map._animatingZoom&&(this._containsPoint(t.layerPoint)?(this._ctx.canvas.style.cursor="pointer",this._mouseInside=!0,this.fire("mouseover",t)):this._mouseInside&&(this._ctx.canvas.style.cursor="",this._mouseInside=!1,this.fire("mouseout",t)))}}),o.Map.include(o.Path.SVG&&!t.L_PREFER_CANVAS||!o.Browser.canvas?{}:{_initPathRoot:function(){var t,i=this._pathRoot;i||(i=this._pathRoot=e.createElement("canvas"),i.style.position="absolute",t=this._canvasCtx=i.getContext("2d"),t.lineCap="round",t.lineJoin="round",this._panes.overlayPane.appendChild(i),this.options.zoomAnimation&&(this._pathRoot.className="leaflet-zoom-animated",this.on("zoomanim",this._animatePathZoom),this.on("zoomend",this._endPathZoom)),this.on("moveend",this._updateCanvasViewport),this._updateCanvasViewport())},_updateCanvasViewport:function(){if(!this._pathZooming){this._updatePathViewport();var t=this._pathViewport,e=t.min,i=t.max.subtract(e),n=this._pathRoot;o.DomUtil.setPosition(n,e),n.width=i.x,n.height=i.y,n.getContext("2d").translate(-e.x,-e.y)}}}),o.LineUtil={simplify:function(t,e){if(!e||!t.length)return t.slice();var i=e*e;return t=this._reducePoints(t,i),t=this._simplifyDP(t,i)},pointToSegmentDistance:function(t,e,i){return Math.sqrt(this._sqClosestPointOnSegment(t,e,i,!0))},closestPointOnSegment:function(t,e,i){return this._sqClosestPointOnSegment(t,e,i)},_simplifyDP:function(t,e){var n=t.length,o=typeof Uint8Array!=i+""?Uint8Array:Array,s=new o(n);s[0]=s[n-1]=1,this._simplifyDPStep(t,s,e,0,n-1);var a,r=[];for(a=0;n>a;a++)s[a]&&r.push(t[a]);return r},_simplifyDPStep:function(t,e,i,n,o){var s,a,r,h=0;for(a=n+1;o-1>=a;a++)r=this._sqClosestPointOnSegment(t[a],t[n],t[o],!0),r>h&&(s=a,h=r);h>i&&(e[s]=1,this._simplifyDPStep(t,e,i,n,s),this._simplifyDPStep(t,e,i,s,o))},_reducePoints:function(t,e){for(var i=[t[0]],n=1,o=0,s=t.length;s>n;n++)this._sqDist(t[n],t[o])>e&&(i.push(t[n]),o=n);return s-1>o&&i.push(t[s-1]),i},clipSegment:function(t,e,i,n){var o,s,a,r=n?this._lastCode:this._getBitCode(t,i),h=this._getBitCode(e,i);for(this._lastCode=h;;){if(!(r|h))return[t,e];if(r&h)return!1;o=r||h,s=this._getEdgeIntersection(t,e,o,i),a=this._getBitCode(s,i),o===r?(t=s,r=a):(e=s,h=a)}},_getEdgeIntersection:function(t,e,i,n){var s=e.x-t.x,a=e.y-t.y,r=n.min,h=n.max;return 8&i?new o.Point(t.x+s*(h.y-t.y)/a,h.y):4&i?new o.Point(t.x+s*(r.y-t.y)/a,r.y):2&i?new o.Point(h.x,t.y+a*(h.x-t.x)/s):1&i?new o.Point(r.x,t.y+a*(r.x-t.x)/s):void 0},_getBitCode:function(t,e){var i=0;return t.xe.max.x&&(i|=2),t.ye.max.y&&(i|=8),i},_sqDist:function(t,e){var i=e.x-t.x,n=e.y-t.y;return i*i+n*n},_sqClosestPointOnSegment:function(t,e,i,n){var s,a=e.x,r=e.y,h=i.x-a,l=i.y-r,u=h*h+l*l;return u>0&&(s=((t.x-a)*h+(t.y-r)*l)/u,s>1?(a=i.x,r=i.y):s>0&&(a+=h*s,r+=l*s)),h=t.x-a,l=t.y-r,n?h*h+l*l:new o.Point(a,r)}},o.Polyline=o.Path.extend({initialize:function(t,e){o.Path.prototype.initialize.call(this,e),this._latlngs=this._convertLatLngs(t)},options:{smoothFactor:1,noClip:!1},projectLatlngs:function(){this._originalPoints=[];for(var t=0,e=this._latlngs.length;e>t;t++)this._originalPoints[t]=this._map.latLngToLayerPoint(this._latlngs[t])},getPathString:function(){for(var t=0,e=this._parts.length,i="";e>t;t++)i+=this._getPathPartStr(this._parts[t]);return i},getLatLngs:function(){return this._latlngs},setLatLngs:function(t){return this._latlngs=this._convertLatLngs(t),this.redraw()},addLatLng:function(t){return this._latlngs.push(o.latLng(t)),this.redraw()},spliceLatLngs:function(){var t=[].splice.apply(this._latlngs,arguments);return this._convertLatLngs(this._latlngs,!0),this.redraw(),t},closestLayerPoint:function(t){for(var e,i,n=1/0,s=this._parts,a=null,r=0,h=s.length;h>r;r++)for(var l=s[r],u=1,c=l.length;c>u;u++){e=l[u-1],i=l[u];var d=o.LineUtil._sqClosestPointOnSegment(t,e,i,!0);n>d&&(n=d,a=o.LineUtil._sqClosestPointOnSegment(t,e,i))}return a&&(a.distance=Math.sqrt(n)),a},getBounds:function(){return new o.LatLngBounds(this.getLatLngs())},_convertLatLngs:function(t,e){var i,n,s=e?t:[];for(i=0,n=t.length;n>i;i++){if(o.Util.isArray(t[i])&&"number"!=typeof t[i][0])return;s[i]=o.latLng(t[i])}return s},_initEvents:function(){o.Path.prototype._initEvents.call(this)},_getPathPartStr:function(t){for(var e,i=o.Path.VML,n=0,s=t.length,a="";s>n;n++)e=t[n],i&&e._round(),a+=(n?"L":"M")+e.x+" "+e.y;return a},_clipPoints:function(){var t,e,i,n=this._originalPoints,s=n.length;if(this.options.noClip)return void(this._parts=[n]);this._parts=[];var a=this._parts,r=this._map._pathViewport,h=o.LineUtil;for(t=0,e=0;s-1>t;t++)i=h.clipSegment(n[t],n[t+1],r,t),i&&(a[e]=a[e]||[],a[e].push(i[0]),(i[1]!==n[t+1]||t===s-2)&&(a[e].push(i[1]),e++))},_simplifyPoints:function(){for(var t=this._parts,e=o.LineUtil,i=0,n=t.length;n>i;i++)t[i]=e.simplify(t[i],this.options.smoothFactor)},_updatePath:function(){this._map&&(this._clipPoints(),this._simplifyPoints(),o.Path.prototype._updatePath.call(this))}}),o.polyline=function(t,e){return new o.Polyline(t,e)},o.PolyUtil={},o.PolyUtil.clipPolygon=function(t,e){var i,n,s,a,r,h,l,u,c,d=[1,4,2,8],p=o.LineUtil;for(n=0,l=t.length;l>n;n++)t[n]._code=p._getBitCode(t[n],e);for(a=0;4>a;a++){for(u=d[a],i=[],n=0,l=t.length,s=l-1;l>n;s=n++)r=t[n],h=t[s],r._code&u?h._code&u||(c=p._getEdgeIntersection(h,r,u,e),c._code=p._getBitCode(c,e),i.push(c)):(h._code&u&&(c=p._getEdgeIntersection(h,r,u,e),c._code=p._getBitCode(c,e),i.push(c)),i.push(r));t=i}return t},o.Polygon=o.Polyline.extend({options:{fill:!0},initialize:function(t,e){o.Polyline.prototype.initialize.call(this,t,e),this._initWithHoles(t)},_initWithHoles:function(t){var e,i,n;if(t&&o.Util.isArray(t[0])&&"number"!=typeof t[0][0])for(this._latlngs=this._convertLatLngs(t[0]),this._holes=t.slice(1),e=0,i=this._holes.length;i>e;e++)n=this._holes[e]=this._convertLatLngs(this._holes[e]),n[0].equals(n[n.length-1])&&n.pop();t=this._latlngs,t.length>=2&&t[0].equals(t[t.length-1])&&t.pop()},projectLatlngs:function(){if(o.Polyline.prototype.projectLatlngs.call(this),this._holePoints=[],this._holes){var t,e,i,n;for(t=0,i=this._holes.length;i>t;t++)for(this._holePoints[t]=[],e=0,n=this._holes[t].length;n>e;e++)this._holePoints[t][e]=this._map.latLngToLayerPoint(this._holes[t][e])}},setLatLngs:function(t){return t&&o.Util.isArray(t[0])&&"number"!=typeof t[0][0]?(this._initWithHoles(t),this.redraw()):o.Polyline.prototype.setLatLngs.call(this,t)},_clipPoints:function(){var t=this._originalPoints,e=[];if(this._parts=[t].concat(this._holePoints),!this.options.noClip){for(var i=0,n=this._parts.length;n>i;i++){var s=o.PolyUtil.clipPolygon(this._parts[i],this._map._pathViewport);s.length&&e.push(s)}this._parts=e}},_getPathPartStr:function(t){var e=o.Polyline.prototype._getPathPartStr.call(this,t);return e+(o.Browser.svg?"z":"x")}}),o.polygon=function(t,e){return new o.Polygon(t,e)},function(){function t(t){return o.FeatureGroup.extend({initialize:function(t,e){this._layers={},this._options=e,this.setLatLngs(t)},setLatLngs:function(e){var i=0,n=e.length;for(this.eachLayer(function(t){n>i?t.setLatLngs(e[i++]):this.removeLayer(t)},this);n>i;)this.addLayer(new t(e[i++],this._options));return this},getLatLngs:function(){var t=[];return this.eachLayer(function(e){t.push(e.getLatLngs())}),t}})}o.MultiPolyline=t(o.Polyline),o.MultiPolygon=t(o.Polygon),o.multiPolyline=function(t,e){return new o.MultiPolyline(t,e)},o.multiPolygon=function(t,e){return new o.MultiPolygon(t,e)}}(),o.Rectangle=o.Polygon.extend({initialize:function(t,e){o.Polygon.prototype.initialize.call(this,this._boundsToLatLngs(t),e)},setBounds:function(t){this.setLatLngs(this._boundsToLatLngs(t))},_boundsToLatLngs:function(t){return t=o.latLngBounds(t),[t.getSouthWest(),t.getNorthWest(),t.getNorthEast(),t.getSouthEast()]}}),o.rectangle=function(t,e){return new o.Rectangle(t,e)},o.Circle=o.Path.extend({initialize:function(t,e,i){o.Path.prototype.initialize.call(this,i),this._latlng=o.latLng(t),this._mRadius=e},options:{fill:!0},setLatLng:function(t){return this._latlng=o.latLng(t),this.redraw()},setRadius:function(t){return this._mRadius=t,this.redraw()},projectLatlngs:function(){var t=this._getLngRadius(),e=this._latlng,i=this._map.latLngToLayerPoint([e.lat,e.lng-t]);this._point=this._map.latLngToLayerPoint(e),this._radius=Math.max(this._point.x-i.x,1)},getBounds:function(){var t=this._getLngRadius(),e=this._mRadius/40075017*360,i=this._latlng;return new o.LatLngBounds([i.lat-e,i.lng-t],[i.lat+e,i.lng+t])},getLatLng:function(){return this._latlng},getPathString:function(){var t=this._point,e=this._radius;return this._checkIfEmpty()?"":o.Browser.svg?"M"+t.x+","+(t.y-e)+"A"+e+","+e+",0,1,1,"+(t.x-.1)+","+(t.y-e)+" z":(t._round(),e=Math.round(e),"AL "+t.x+","+t.y+" "+e+","+e+" 0,23592600")},getRadius:function(){return this._mRadius},_getLatRadius:function(){return this._mRadius/40075017*360},_getLngRadius:function(){return this._getLatRadius()/Math.cos(o.LatLng.DEG_TO_RAD*this._latlng.lat)},_checkIfEmpty:function(){if(!this._map)return!1;var t=this._map._pathViewport,e=this._radius,i=this._point;return i.x-e>t.max.x||i.y-e>t.max.y||i.x+ei;i++)for(l=this._parts[i],n=0,r=l.length,s=r-1;r>n;s=n++)if((e||0!==n)&&(h=o.LineUtil.pointToSegmentDistance(t,l[s],l[n]),u>=h))return!0;return!1}}:{}),o.Polygon.include(o.Path.CANVAS?{_containsPoint:function(t){var e,i,n,s,a,r,h,l,u=!1;if(o.Polyline.prototype._containsPoint.call(this,t,!0))return!0;for(s=0,h=this._parts.length;h>s;s++)for(e=this._parts[s],a=0,l=e.length,r=l-1;l>a;r=a++)i=e[a],n=e[r],i.y>t.y!=n.y>t.y&&t.x<(n.x-i.x)*(t.y-i.y)/(n.y-i.y)+i.x&&(u=!u);return u}}:{}),o.Circle.include(o.Path.CANVAS?{_drawPath:function(){var t=this._point;this._ctx.beginPath(),this._ctx.arc(t.x,t.y,this._radius,0,2*Math.PI,!1)},_containsPoint:function(t){var e=this._point,i=this.options.stroke?this.options.weight/2:0;return t.distanceTo(e)<=this._radius+i}}:{}),o.CircleMarker.include(o.Path.CANVAS?{_updateStyle:function(){o.Path.prototype._updateStyle.call(this)}}:{}),o.GeoJSON=o.FeatureGroup.extend({initialize:function(t,e){o.setOptions(this,e),this._layers={},t&&this.addData(t)},addData:function(t){var e,i,n,s=o.Util.isArray(t)?t:t.features;if(s){for(e=0,i=s.length;i>e;e++)n=s[e],(n.geometries||n.geometry||n.features||n.coordinates)&&this.addData(s[e]);return this}var a=this.options;if(!a.filter||a.filter(t)){var r=o.GeoJSON.geometryToLayer(t,a.pointToLayer,a.coordsToLatLng,a);return r.feature=o.GeoJSON.asFeature(t),r.defaultOptions=r.options,this.resetStyle(r),a.onEachFeature&&a.onEachFeature(t,r),this.addLayer(r)}},resetStyle:function(t){var e=this.options.style;e&&(o.Util.extend(t.options,t.defaultOptions),this._setLayerStyle(t,e))},setStyle:function(t){this.eachLayer(function(e){this._setLayerStyle(e,t)},this)},_setLayerStyle:function(t,e){"function"==typeof e&&(e=e(t.feature)),t.setStyle&&t.setStyle(e)}}),o.extend(o.GeoJSON,{geometryToLayer:function(t,e,i,n){var s,a,r,h,l="Feature"===t.type?t.geometry:t,u=l.coordinates,c=[];switch(i=i||this.coordsToLatLng,l.type){case"Point":return s=i(u),e?e(t,s):new o.Marker(s);case"MultiPoint":for(r=0,h=u.length;h>r;r++)s=i(u[r]),c.push(e?e(t,s):new o.Marker(s));return new o.FeatureGroup(c);case"LineString":return a=this.coordsToLatLngs(u,0,i),new o.Polyline(a,n);case"Polygon":if(2===u.length&&!u[1].length)throw new Error("Invalid GeoJSON object.");return a=this.coordsToLatLngs(u,1,i),new o.Polygon(a,n);case"MultiLineString":return a=this.coordsToLatLngs(u,1,i),new o.MultiPolyline(a,n);case"MultiPolygon":return a=this.coordsToLatLngs(u,2,i),new o.MultiPolygon(a,n);case"GeometryCollection":for(r=0,h=l.geometries.length;h>r;r++)c.push(this.geometryToLayer({geometry:l.geometries[r],type:"Feature",properties:t.properties},e,i,n));return new o.FeatureGroup(c);default:throw new Error("Invalid GeoJSON object.")}},coordsToLatLng:function(t){return new o.LatLng(t[1],t[0],t[2])},coordsToLatLngs:function(t,e,i){var n,o,s,a=[];for(o=0,s=t.length;s>o;o++)n=e?this.coordsToLatLngs(t[o],e-1,i):(i||this.coordsToLatLng)(t[o]),a.push(n);return a},latLngToCoords:function(t){var e=[t.lng,t.lat];return t.alt!==i&&e.push(t.alt),e},latLngsToCoords:function(t){for(var e=[],i=0,n=t.length;n>i;i++)e.push(o.GeoJSON.latLngToCoords(t[i]));return e},getFeature:function(t,e){return t.feature?o.extend({},t.feature,{geometry:e}):o.GeoJSON.asFeature(e)},asFeature:function(t){return"Feature"===t.type?t:{type:"Feature",properties:{},geometry:t}}});var a={toGeoJSON:function(){return o.GeoJSON.getFeature(this,{type:"Point",coordinates:o.GeoJSON.latLngToCoords(this.getLatLng())})}};o.Marker.include(a),o.Circle.include(a),o.CircleMarker.include(a),o.Polyline.include({toGeoJSON:function(){return o.GeoJSON.getFeature(this,{type:"LineString",coordinates:o.GeoJSON.latLngsToCoords(this.getLatLngs())})}}),o.Polygon.include({toGeoJSON:function(){var t,e,i,n=[o.GeoJSON.latLngsToCoords(this.getLatLngs())];if(n[0].push(n[0][0]),this._holes)for(t=0,e=this._holes.length;e>t;t++)i=o.GeoJSON.latLngsToCoords(this._holes[t]),i.push(i[0]),n.push(i);return o.GeoJSON.getFeature(this,{type:"Polygon",coordinates:n})}}),function(){function t(t){return function(){var e=[];return this.eachLayer(function(t){e.push(t.toGeoJSON().geometry.coordinates)}),o.GeoJSON.getFeature(this,{type:t,coordinates:e})}}o.MultiPolyline.include({toGeoJSON:t("MultiLineString")}),o.MultiPolygon.include({toGeoJSON:t("MultiPolygon")}),o.LayerGroup.include({toGeoJSON:function(){var e,i=this.feature&&this.feature.geometry,n=[];if(i&&"MultiPoint"===i.type)return t("MultiPoint").call(this);var s=i&&"GeometryCollection"===i.type;return this.eachLayer(function(t){t.toGeoJSON&&(e=t.toGeoJSON(),n.push(s?e.geometry:o.GeoJSON.asFeature(e)))}),s?o.GeoJSON.getFeature(this,{geometries:n,type:"GeometryCollection"}):{type:"FeatureCollection",features:n}}})}(),o.geoJson=function(t,e){return new o.GeoJSON(t,e)},o.DomEvent={addListener:function(t,e,i,n){var s,a,r,h=o.stamp(i),l="_leaflet_"+e+h;return t[l]?this:(s=function(e){return i.call(n||t,e||o.DomEvent._getEvent())},o.Browser.pointer&&0===e.indexOf("touch")?this.addPointerListener(t,e,s,h):(o.Browser.touch&&"dblclick"===e&&this.addDoubleTapListener&&this.addDoubleTapListener(t,s,h),"addEventListener"in t?"mousewheel"===e?(t.addEventListener("DOMMouseScroll",s,!1),t.addEventListener(e,s,!1)):"mouseenter"===e||"mouseleave"===e?(a=s,r="mouseenter"===e?"mouseover":"mouseout",s=function(e){return o.DomEvent._checkMouse(t,e)?a(e):void 0},t.addEventListener(r,s,!1)):"click"===e&&o.Browser.android?(a=s,s=function(t){return o.DomEvent._filterClick(t,a)},t.addEventListener(e,s,!1)):t.addEventListener(e,s,!1):"attachEvent"in t&&t.attachEvent("on"+e,s),t[l]=s,this))},removeListener:function(t,e,i){var n=o.stamp(i),s="_leaflet_"+e+n,a=t[s];return a?(o.Browser.pointer&&0===e.indexOf("touch")?this.removePointerListener(t,e,n):o.Browser.touch&&"dblclick"===e&&this.removeDoubleTapListener?this.removeDoubleTapListener(t,n):"removeEventListener"in t?"mousewheel"===e?(t.removeEventListener("DOMMouseScroll",a,!1),t.removeEventListener(e,a,!1)):"mouseenter"===e||"mouseleave"===e?t.removeEventListener("mouseenter"===e?"mouseover":"mouseout",a,!1):t.removeEventListener(e,a,!1):"detachEvent"in t&&t.detachEvent("on"+e,a),t[s]=null,this):this},stopPropagation:function(t){return t.stopPropagation?t.stopPropagation():t.cancelBubble=!0,o.DomEvent._skipped(t),this},disableScrollPropagation:function(t){var e=o.DomEvent.stopPropagation;return o.DomEvent.on(t,"mousewheel",e).on(t,"MozMousePixelScroll",e)},disableClickPropagation:function(t){for(var e=o.DomEvent.stopPropagation,i=o.Draggable.START.length-1;i>=0;i--)o.DomEvent.on(t,o.Draggable.START[i],e);return o.DomEvent.on(t,"click",o.DomEvent._fakeStop).on(t,"dblclick",e)},preventDefault:function(t){return t.preventDefault?t.preventDefault():t.returnValue=!1,this},stop:function(t){return o.DomEvent.preventDefault(t).stopPropagation(t)},getMousePosition:function(t,e){if(!e)return new o.Point(t.clientX,t.clientY);var i=e.getBoundingClientRect();return new o.Point(t.clientX-i.left-e.clientLeft,t.clientY-i.top-e.clientTop)},getWheelDelta:function(t){var e=0;return t.wheelDelta&&(e=t.wheelDelta/120),t.detail&&(e=-t.detail/3),e},_skipEvents:{},_fakeStop:function(t){o.DomEvent._skipEvents[t.type]=!0},_skipped:function(t){var e=this._skipEvents[t.type];return this._skipEvents[t.type]=!1,e},_checkMouse:function(t,e){var i=e.relatedTarget;if(!i)return!0;try{for(;i&&i!==t;)i=i.parentNode}catch(n){return!1}return i!==t},_getEvent:function(){var e=t.event;if(!e)for(var i=arguments.callee.caller;i&&(e=i.arguments[0],!e||t.Event!==e.constructor);)i=i.caller;return e},_filterClick:function(t,e){var i=t.timeStamp||t.originalEvent.timeStamp,n=o.DomEvent._lastClick&&i-o.DomEvent._lastClick;return n&&n>100&&500>n||t.target._simulatedClick&&!t._simulated?void o.DomEvent.stop(t):(o.DomEvent._lastClick=i,e(t))}},o.DomEvent.on=o.DomEvent.addListener,o.DomEvent.off=o.DomEvent.removeListener,o.Draggable=o.Class.extend({includes:o.Mixin.Events,statics:{START:o.Browser.touch?["touchstart","mousedown"]:["mousedown"],END:{mousedown:"mouseup",touchstart:"touchend",pointerdown:"touchend",MSPointerDown:"touchend"},MOVE:{mousedown:"mousemove",touchstart:"touchmove",pointerdown:"touchmove",MSPointerDown:"touchmove"}},initialize:function(t,e){this._element=t,this._dragStartTarget=e||t},enable:function(){if(!this._enabled){for(var t=o.Draggable.START.length-1;t>=0;t--)o.DomEvent.on(this._dragStartTarget,o.Draggable.START[t],this._onDown,this);this._enabled=!0}},disable:function(){if(this._enabled){for(var t=o.Draggable.START.length-1;t>=0;t--)o.DomEvent.off(this._dragStartTarget,o.Draggable.START[t],this._onDown,this);this._enabled=!1,this._moved=!1}},_onDown:function(t){if(this._moved=!1,!(t.shiftKey||1!==t.which&&1!==t.button&&!t.touches||(o.DomEvent.stopPropagation(t),o.Draggable._disabled||(o.DomUtil.disableImageDrag(),o.DomUtil.disableTextSelection(),this._moving)))){var i=t.touches?t.touches[0]:t;this._startPoint=new o.Point(i.clientX,i.clientY),this._startPos=this._newPos=o.DomUtil.getPosition(this._element),o.DomEvent.on(e,o.Draggable.MOVE[t.type],this._onMove,this).on(e,o.Draggable.END[t.type],this._onUp,this)}},_onMove:function(t){if(t.touches&&t.touches.length>1)return void(this._moved=!0);var i=t.touches&&1===t.touches.length?t.touches[0]:t,n=new o.Point(i.clientX,i.clientY),s=n.subtract(this._startPoint);(s.x||s.y)&&(o.Browser.touch&&Math.abs(s.x)+Math.abs(s.y)<3||(o.DomEvent.preventDefault(t),this._moved||(this.fire("dragstart"),this._moved=!0,this._startPos=o.DomUtil.getPosition(this._element).subtract(s),o.DomUtil.addClass(e.body,"leaflet-dragging"),this._lastTarget=t.target||t.srcElement,o.DomUtil.addClass(this._lastTarget,"leaflet-drag-target")),this._newPos=this._startPos.add(s),this._moving=!0,o.Util.cancelAnimFrame(this._animRequest),this._animRequest=o.Util.requestAnimFrame(this._updatePosition,this,!0,this._dragStartTarget)))},_updatePosition:function(){this.fire("predrag"),o.DomUtil.setPosition(this._element,this._newPos),this.fire("drag")},_onUp:function(){o.DomUtil.removeClass(e.body,"leaflet-dragging"),this._lastTarget&&(o.DomUtil.removeClass(this._lastTarget,"leaflet-drag-target"),this._lastTarget=null);for(var t in o.Draggable.MOVE)o.DomEvent.off(e,o.Draggable.MOVE[t],this._onMove).off(e,o.Draggable.END[t],this._onUp);o.DomUtil.enableImageDrag(),o.DomUtil.enableTextSelection(),this._moved&&this._moving&&(o.Util.cancelAnimFrame(this._animRequest),this.fire("dragend",{distance:this._newPos.distanceTo(this._startPos)})),this._moving=!1}}),o.Handler=o.Class.extend({initialize:function(t){this._map=t},enable:function(){this._enabled||(this._enabled=!0,this.addHooks())},disable:function(){this._enabled&&(this._enabled=!1,this.removeHooks())},enabled:function(){return!!this._enabled}}),o.Map.mergeOptions({dragging:!0,inertia:!o.Browser.android23,inertiaDeceleration:3400,inertiaMaxSpeed:1/0,inertiaThreshold:o.Browser.touch?32:18,easeLinearity:.25,worldCopyJump:!1}),o.Map.Drag=o.Handler.extend({addHooks:function(){if(!this._draggable){var t=this._map;this._draggable=new o.Draggable(t._mapPane,t._container),this._draggable.on({dragstart:this._onDragStart,drag:this._onDrag,dragend:this._onDragEnd},this),t.options.worldCopyJump&&(this._draggable.on("predrag",this._onPreDrag,this),t.on("viewreset",this._onViewReset,this),t.whenReady(this._onViewReset,this))}this._draggable.enable()},removeHooks:function(){this._draggable.disable()},moved:function(){return this._draggable&&this._draggable._moved},_onDragStart:function(){var t=this._map;t._panAnim&&t._panAnim.stop(),t.fire("movestart").fire("dragstart"),t.options.inertia&&(this._positions=[],this._times=[])},_onDrag:function(){if(this._map.options.inertia){var t=this._lastTime=+new Date,e=this._lastPos=this._draggable._newPos;this._positions.push(e),this._times.push(t),t-this._times[0]>200&&(this._positions.shift(),this._times.shift())}this._map.fire("move").fire("drag")},_onViewReset:function(){var t=this._map.getSize()._divideBy(2),e=this._map.latLngToLayerPoint([0,0]);this._initialWorldOffset=e.subtract(t).x,this._worldWidth=this._map.project([0,180]).x},_onPreDrag:function(){var t=this._worldWidth,e=Math.round(t/2),i=this._initialWorldOffset,n=this._draggable._newPos.x,o=(n-e+i)%t+e-i,s=(n+e+i)%t-e-i,a=Math.abs(o+i)i.inertiaThreshold||!this._positions[0];if(e.fire("dragend",t),s)e.fire("moveend");else{var a=this._lastPos.subtract(this._positions[0]),r=(this._lastTime+n-this._times[0])/1e3,h=i.easeLinearity,l=a.multiplyBy(h/r),u=l.distanceTo([0,0]),c=Math.min(i.inertiaMaxSpeed,u),d=l.multiplyBy(c/u),p=c/(i.inertiaDeceleration*h),_=d.multiplyBy(-p/2).round();_.x&&_.y?(_=e._limitOffset(_,e.options.maxBounds),o.Util.requestAnimFrame(function(){e.panBy(_,{duration:p,easeLinearity:h,noMoveStart:!0})})):e.fire("moveend")}}}),o.Map.addInitHook("addHandler","dragging",o.Map.Drag),o.Map.mergeOptions({doubleClickZoom:!0}),o.Map.DoubleClickZoom=o.Handler.extend({addHooks:function(){this._map.on("dblclick",this._onDoubleClick,this)},removeHooks:function(){this._map.off("dblclick",this._onDoubleClick,this)},_onDoubleClick:function(t){var e=this._map,i=e.getZoom()+(t.originalEvent.shiftKey?-1:1);"center"===e.options.doubleClickZoom?e.setZoom(i):e.setZoomAround(t.containerPoint,i)}}),o.Map.addInitHook("addHandler","doubleClickZoom",o.Map.DoubleClickZoom),o.Map.mergeOptions({scrollWheelZoom:!0}),o.Map.ScrollWheelZoom=o.Handler.extend({addHooks:function(){o.DomEvent.on(this._map._container,"mousewheel",this._onWheelScroll,this),o.DomEvent.on(this._map._container,"MozMousePixelScroll",o.DomEvent.preventDefault),this._delta=0},removeHooks:function(){o.DomEvent.off(this._map._container,"mousewheel",this._onWheelScroll),o.DomEvent.off(this._map._container,"MozMousePixelScroll",o.DomEvent.preventDefault)},_onWheelScroll:function(t){var e=o.DomEvent.getWheelDelta(t);this._delta+=e,this._lastMousePos=this._map.mouseEventToContainerPoint(t),this._startTime||(this._startTime=+new Date);var i=Math.max(40-(+new Date-this._startTime),0);clearTimeout(this._timer),this._timer=setTimeout(o.bind(this._performZoom,this),i),o.DomEvent.preventDefault(t),o.DomEvent.stopPropagation(t)},_performZoom:function(){var t=this._map,e=this._delta,i=t.getZoom();e=e>0?Math.ceil(e):Math.floor(e),e=Math.max(Math.min(e,4),-4),e=t._limitZoom(i+e)-i,this._delta=0,this._startTime=null,e&&("center"===t.options.scrollWheelZoom?t.setZoom(i+e):t.setZoomAround(this._lastMousePos,i+e))}}),o.Map.addInitHook("addHandler","scrollWheelZoom",o.Map.ScrollWheelZoom),o.extend(o.DomEvent,{_touchstart:o.Browser.msPointer?"MSPointerDown":o.Browser.pointer?"pointerdown":"touchstart",_touchend:o.Browser.msPointer?"MSPointerUp":o.Browser.pointer?"pointerup":"touchend",addDoubleTapListener:function(t,i,n){function s(t){var e;if(o.Browser.pointer?(_.push(t.pointerId),e=_.length):e=t.touches.length,!(e>1)){var i=Date.now(),n=i-(r||i);h=t.touches?t.touches[0]:t,l=n>0&&u>=n,r=i}}function a(t){if(o.Browser.pointer){var e=_.indexOf(t.pointerId);if(-1===e)return;_.splice(e,1)}if(l){if(o.Browser.pointer){var n,s={};for(var a in h)n=h[a],s[a]="function"==typeof n?n.bind(h):n;h=s}h.type="dblclick",i(h),r=null}}var r,h,l=!1,u=250,c="_leaflet_",d=this._touchstart,p=this._touchend,_=[];t[c+d+n]=s,t[c+p+n]=a;var m=o.Browser.pointer?e.documentElement:t;return t.addEventListener(d,s,!1),m.addEventListener(p,a,!1),o.Browser.pointer&&m.addEventListener(o.DomEvent.POINTER_CANCEL,a,!1),this},removeDoubleTapListener:function(t,i){var n="_leaflet_";return t.removeEventListener(this._touchstart,t[n+this._touchstart+i],!1),(o.Browser.pointer?e.documentElement:t).removeEventListener(this._touchend,t[n+this._touchend+i],!1),o.Browser.pointer&&e.documentElement.removeEventListener(o.DomEvent.POINTER_CANCEL,t[n+this._touchend+i],!1),this}}),o.extend(o.DomEvent,{POINTER_DOWN:o.Browser.msPointer?"MSPointerDown":"pointerdown",POINTER_MOVE:o.Browser.msPointer?"MSPointerMove":"pointermove",POINTER_UP:o.Browser.msPointer?"MSPointerUp":"pointerup",POINTER_CANCEL:o.Browser.msPointer?"MSPointerCancel":"pointercancel",_pointers:[],_pointerDocumentListener:!1,addPointerListener:function(t,e,i,n){switch(e){case"touchstart":return this.addPointerListenerStart(t,e,i,n);case"touchend":return this.addPointerListenerEnd(t,e,i,n);case"touchmove":return this.addPointerListenerMove(t,e,i,n);default:throw"Unknown touch event type"}},addPointerListenerStart:function(t,i,n,s){var a="_leaflet_",r=this._pointers,h=function(t){o.DomEvent.preventDefault(t);for(var e=!1,i=0;i1))&&(this._moved||(o.DomUtil.addClass(e._mapPane,"leaflet-touching"),e.fire("movestart").fire("zoomstart"),this._moved=!0),o.Util.cancelAnimFrame(this._animRequest),this._animRequest=o.Util.requestAnimFrame(this._updateOnMove,this,!0,this._map._container),o.DomEvent.preventDefault(t))}},_updateOnMove:function(){var t=this._map,e=this._getScaleOrigin(),i=t.layerPointToLatLng(e),n=t.getScaleZoom(this._scale);t._animateZoom(i,n,this._startCenter,this._scale,this._delta,!1,!0)},_onTouchEnd:function(){if(!this._moved||!this._zooming)return void(this._zooming=!1);var t=this._map;this._zooming=!1,o.DomUtil.removeClass(t._mapPane,"leaflet-touching"),o.Util.cancelAnimFrame(this._animRequest),o.DomEvent.off(e,"touchmove",this._onTouchMove).off(e,"touchend",this._onTouchEnd);var i=this._getScaleOrigin(),n=t.layerPointToLatLng(i),s=t.getZoom(),a=t.getScaleZoom(this._scale)-s,r=a>0?Math.ceil(a):Math.floor(a),h=t._limitZoom(s+r),l=t.getZoomScale(h)/this._scale;t._animateZoom(n,h,i,l)},_getScaleOrigin:function(){var t=this._centerOffset.subtract(this._delta).divideBy(this._scale);return this._startCenter.add(t)}}),o.Map.addInitHook("addHandler","touchZoom",o.Map.TouchZoom),o.Map.mergeOptions({tap:!0,tapTolerance:15}),o.Map.Tap=o.Handler.extend({addHooks:function(){o.DomEvent.on(this._map._container,"touchstart",this._onDown,this)},removeHooks:function(){o.DomEvent.off(this._map._container,"touchstart",this._onDown,this)},_onDown:function(t){if(t.touches){if(o.DomEvent.preventDefault(t),this._fireClick=!0,t.touches.length>1)return this._fireClick=!1,void clearTimeout(this._holdTimeout);var i=t.touches[0],n=i.target;this._startPos=this._newPos=new o.Point(i.clientX,i.clientY),n.tagName&&"a"===n.tagName.toLowerCase()&&o.DomUtil.addClass(n,"leaflet-active"),this._holdTimeout=setTimeout(o.bind(function(){this._isTapValid()&&(this._fireClick=!1,this._onUp(),this._simulateEvent("contextmenu",i))},this),1e3),o.DomEvent.on(e,"touchmove",this._onMove,this).on(e,"touchend",this._onUp,this)}},_onUp:function(t){if(clearTimeout(this._holdTimeout),o.DomEvent.off(e,"touchmove",this._onMove,this).off(e,"touchend",this._onUp,this),this._fireClick&&t&&t.changedTouches){var i=t.changedTouches[0],n=i.target;n&&n.tagName&&"a"===n.tagName.toLowerCase()&&o.DomUtil.removeClass(n,"leaflet-active"),this._isTapValid()&&this._simulateEvent("click",i)}},_isTapValid:function(){return this._newPos.distanceTo(this._startPos)<=this._map.options.tapTolerance},_onMove:function(t){var e=t.touches[0];this._newPos=new o.Point(e.clientX,e.clientY)},_simulateEvent:function(i,n){var o=e.createEvent("MouseEvents");o._simulated=!0,n.target._simulatedClick=!0,o.initMouseEvent(i,!0,!0,t,1,n.screenX,n.screenY,n.clientX,n.clientY,!1,!1,!1,!1,0,null),n.target.dispatchEvent(o)}}),o.Browser.touch&&!o.Browser.pointer&&o.Map.addInitHook("addHandler","tap",o.Map.Tap),o.Map.mergeOptions({boxZoom:!0}),o.Map.BoxZoom=o.Handler.extend({initialize:function(t){this._map=t,this._container=t._container,this._pane=t._panes.overlayPane,this._moved=!1},addHooks:function(){o.DomEvent.on(this._container,"mousedown",this._onMouseDown,this)},removeHooks:function(){o.DomEvent.off(this._container,"mousedown",this._onMouseDown),this._moved=!1},moved:function(){return this._moved},_onMouseDown:function(t){return this._moved=!1,!t.shiftKey||1!==t.which&&1!==t.button?!1:(o.DomUtil.disableTextSelection(),o.DomUtil.disableImageDrag(),this._startLayerPoint=this._map.mouseEventToLayerPoint(t),void o.DomEvent.on(e,"mousemove",this._onMouseMove,this).on(e,"mouseup",this._onMouseUp,this).on(e,"keydown",this._onKeyDown,this))},_onMouseMove:function(t){this._moved||(this._box=o.DomUtil.create("div","leaflet-zoom-box",this._pane),o.DomUtil.setPosition(this._box,this._startLayerPoint),this._container.style.cursor="crosshair",this._map.fire("boxzoomstart"));var e=this._startLayerPoint,i=this._box,n=this._map.mouseEventToLayerPoint(t),s=n.subtract(e),a=new o.Point(Math.min(n.x,e.x),Math.min(n.y,e.y));o.DomUtil.setPosition(i,a),this._moved=!0,i.style.width=Math.max(0,Math.abs(s.x)-4)+"px",i.style.height=Math.max(0,Math.abs(s.y)-4)+"px"},_finish:function(){this._moved&&(this._pane.removeChild(this._box),this._container.style.cursor=""),o.DomUtil.enableTextSelection(),o.DomUtil.enableImageDrag(),o.DomEvent.off(e,"mousemove",this._onMouseMove).off(e,"mouseup",this._onMouseUp).off(e,"keydown",this._onKeyDown)},_onMouseUp:function(t){this._finish();var e=this._map,i=e.mouseEventToLayerPoint(t);if(!this._startLayerPoint.equals(i)){var n=new o.LatLngBounds(e.layerPointToLatLng(this._startLayerPoint),e.layerPointToLatLng(i));e.fitBounds(n),e.fire("boxzoomend",{boxZoomBounds:n})}},_onKeyDown:function(t){27===t.keyCode&&this._finish()}}),o.Map.addInitHook("addHandler","boxZoom",o.Map.BoxZoom),o.Map.mergeOptions({keyboard:!0,keyboardPanOffset:80,keyboardZoomOffset:1}),o.Map.Keyboard=o.Handler.extend({keyCodes:{left:[37],right:[39],down:[40],up:[38],zoomIn:[187,107,61,171],zoomOut:[189,109,173]},initialize:function(t){this._map=t,this._setPanOffset(t.options.keyboardPanOffset),this._setZoomOffset(t.options.keyboardZoomOffset)},addHooks:function(){var t=this._map._container;-1===t.tabIndex&&(t.tabIndex="0"),o.DomEvent.on(t,"focus",this._onFocus,this).on(t,"blur",this._onBlur,this).on(t,"mousedown",this._onMouseDown,this),this._map.on("focus",this._addHooks,this).on("blur",this._removeHooks,this)},removeHooks:function(){this._removeHooks();var t=this._map._container;o.DomEvent.off(t,"focus",this._onFocus,this).off(t,"blur",this._onBlur,this).off(t,"mousedown",this._onMouseDown,this),this._map.off("focus",this._addHooks,this).off("blur",this._removeHooks,this)},_onMouseDown:function(){if(!this._focused){var i=e.body,n=e.documentElement,o=i.scrollTop||n.scrollTop,s=i.scrollLeft||n.scrollLeft;this._map._container.focus(),t.scrollTo(s,o)}},_onFocus:function(){this._focused=!0,this._map.fire("focus")},_onBlur:function(){this._focused=!1,this._map.fire("blur")},_setPanOffset:function(t){var e,i,n=this._panKeys={},o=this.keyCodes;for(e=0,i=o.left.length;i>e;e++)n[o.left[e]]=[-1*t,0];for(e=0,i=o.right.length;i>e;e++)n[o.right[e]]=[t,0];for(e=0,i=o.down.length;i>e;e++)n[o.down[e]]=[0,t];for(e=0,i=o.up.length;i>e;e++)n[o.up[e]]=[0,-1*t]},_setZoomOffset:function(t){var e,i,n=this._zoomKeys={},o=this.keyCodes;for(e=0,i=o.zoomIn.length;i>e;e++)n[o.zoomIn[e]]=t;for(e=0,i=o.zoomOut.length;i>e;e++)n[o.zoomOut[e]]=-t},_addHooks:function(){o.DomEvent.on(e,"keydown",this._onKeyDown,this)},_removeHooks:function(){o.DomEvent.off(e,"keydown",this._onKeyDown,this)},_onKeyDown:function(t){var e=t.keyCode,i=this._map;if(e in this._panKeys){if(i._panAnim&&i._panAnim._inProgress)return;i.panBy(this._panKeys[e]),i.options.maxBounds&&i.panInsideBounds(i.options.maxBounds)}else{if(!(e in this._zoomKeys))return;i.setZoom(i.getZoom()+this._zoomKeys[e])}o.DomEvent.stop(t)}}),o.Map.addInitHook("addHandler","keyboard",o.Map.Keyboard),o.Handler.MarkerDrag=o.Handler.extend({initialize:function(t){this._marker=t},addHooks:function(){var t=this._marker._icon;this._draggable||(this._draggable=new o.Draggable(t,t)),this._draggable.on("dragstart",this._onDragStart,this).on("drag",this._onDrag,this).on("dragend",this._onDragEnd,this),this._draggable.enable(),o.DomUtil.addClass(this._marker._icon,"leaflet-marker-draggable")},removeHooks:function(){this._draggable.off("dragstart",this._onDragStart,this).off("drag",this._onDrag,this).off("dragend",this._onDragEnd,this),this._draggable.disable(),o.DomUtil.removeClass(this._marker._icon,"leaflet-marker-draggable")},moved:function(){return this._draggable&&this._draggable._moved},_onDragStart:function(){this._marker.closePopup().fire("movestart").fire("dragstart")},_onDrag:function(){var t=this._marker,e=t._shadow,i=o.DomUtil.getPosition(t._icon),n=t._map.layerPointToLatLng(i);e&&o.DomUtil.setPosition(e,i),t._latlng=n,t.fire("move",{latlng:n}).fire("drag")},_onDragEnd:function(t){this._marker.fire("moveend").fire("dragend",t)}}),o.Control=o.Class.extend({options:{position:"topright"},initialize:function(t){o.setOptions(this,t)},getPosition:function(){return this.options.position},setPosition:function(t){var e=this._map;return e&&e.removeControl(this),this.options.position=t,e&&e.addControl(this),this},getContainer:function(){return this._container},addTo:function(t){this._map=t;var e=this._container=this.onAdd(t),i=this.getPosition(),n=t._controlCorners[i];return o.DomUtil.addClass(e,"leaflet-control"),-1!==i.indexOf("bottom")?n.insertBefore(e,n.firstChild):n.appendChild(e),this},removeFrom:function(t){var e=this.getPosition(),i=t._controlCorners[e];return i.removeChild(this._container),this._map=null,this.onRemove&&this.onRemove(t),this},_refocusOnMap:function(){this._map&&this._map.getContainer().focus()}}),o.control=function(t){return new o.Control(t)},o.Map.include({addControl:function(t){return t.addTo(this),this},removeControl:function(t){return t.removeFrom(this),this},_initControlPos:function(){function t(t,s){var a=i+t+" "+i+s;e[t+s]=o.DomUtil.create("div",a,n)}var e=this._controlCorners={},i="leaflet-",n=this._controlContainer=o.DomUtil.create("div",i+"control-container",this._container);t("top","left"),t("top","right"),t("bottom","left"),t("bottom","right")},_clearControlPos:function(){this._container.removeChild(this._controlContainer)}}),o.Control.Zoom=o.Control.extend({options:{position:"topleft",zoomInText:"+",zoomInTitle:"Zoom in",zoomOutText:"-",zoomOutTitle:"Zoom out"},onAdd:function(t){var e="leaflet-control-zoom",i=o.DomUtil.create("div",e+" leaflet-bar");return this._map=t,this._zoomInButton=this._createButton(this.options.zoomInText,this.options.zoomInTitle,e+"-in",i,this._zoomIn,this),this._zoomOutButton=this._createButton(this.options.zoomOutText,this.options.zoomOutTitle,e+"-out",i,this._zoomOut,this),this._updateDisabled(),t.on("zoomend zoomlevelschange",this._updateDisabled,this),i},onRemove:function(t){t.off("zoomend zoomlevelschange",this._updateDisabled,this)},_zoomIn:function(t){this._map.zoomIn(t.shiftKey?3:1)},_zoomOut:function(t){this._map.zoomOut(t.shiftKey?3:1)},_createButton:function(t,e,i,n,s,a){var r=o.DomUtil.create("a",i,n);r.innerHTML=t,r.href="#",r.title=e;var h=o.DomEvent.stopPropagation;return o.DomEvent.on(r,"click",h).on(r,"mousedown",h).on(r,"dblclick",h).on(r,"click",o.DomEvent.preventDefault).on(r,"click",s,a).on(r,"click",this._refocusOnMap,a),r},_updateDisabled:function(){var t=this._map,e="leaflet-disabled";o.DomUtil.removeClass(this._zoomInButton,e),o.DomUtil.removeClass(this._zoomOutButton,e),t._zoom===t.getMinZoom()&&o.DomUtil.addClass(this._zoomOutButton,e),t._zoom===t.getMaxZoom()&&o.DomUtil.addClass(this._zoomInButton,e)}}),o.Map.mergeOptions({zoomControl:!0}),o.Map.addInitHook(function(){this.options.zoomControl&&(this.zoomControl=new o.Control.Zoom,this.addControl(this.zoomControl))}),o.control.zoom=function(t){return new o.Control.Zoom(t)},o.Control.Attribution=o.Control.extend({options:{position:"bottomright",prefix:'Leaflet '},initialize:function(t){o.setOptions(this,t),this._attributions={}},onAdd:function(t){this._container=o.DomUtil.create("div","leaflet-control-attribution"),o.DomEvent.disableClickPropagation(this._container);for(var e in t._layers)t._layers[e].getAttribution&&this.addAttribution(t._layers[e].getAttribution());return t.on("layeradd",this._onLayerAdd,this).on("layerremove",this._onLayerRemove,this),this._update(),this._container},onRemove:function(t){t.off("layeradd",this._onLayerAdd).off("layerremove",this._onLayerRemove)},setPrefix:function(t){return this.options.prefix=t,this._update(),this},addAttribution:function(t){return t?(this._attributions[t]||(this._attributions[t]=0),this._attributions[t]++,this._update(),this):void 0},removeAttribution:function(t){return t?(this._attributions[t]&&(this._attributions[t]--,this._update()),this):void 0},_update:function(){if(this._map){var t=[];for(var e in this._attributions)this._attributions[e]&&t.push(e);var i=[];this.options.prefix&&i.push(this.options.prefix),t.length&&i.push(t.join(", ")),this._container.innerHTML=i.join(" | ")}},_onLayerAdd:function(t){t.layer.getAttribution&&this.addAttribution(t.layer.getAttribution())},_onLayerRemove:function(t){t.layer.getAttribution&&this.removeAttribution(t.layer.getAttribution())}}),o.Map.mergeOptions({attributionControl:!0}),o.Map.addInitHook(function(){this.options.attributionControl&&(this.attributionControl=(new o.Control.Attribution).addTo(this))}),o.control.attribution=function(t){return new o.Control.Attribution(t)},o.Control.Scale=o.Control.extend({options:{position:"bottomleft",maxWidth:100,metric:!0,imperial:!0,updateWhenIdle:!1},onAdd:function(t){this._map=t;var e="leaflet-control-scale",i=o.DomUtil.create("div",e),n=this.options;return this._addScales(n,e,i),t.on(n.updateWhenIdle?"moveend":"move",this._update,this),t.whenReady(this._update,this),i},onRemove:function(t){t.off(this.options.updateWhenIdle?"moveend":"move",this._update,this)},_addScales:function(t,e,i){t.metric&&(this._mScale=o.DomUtil.create("div",e+"-line",i)),t.imperial&&(this._iScale=o.DomUtil.create("div",e+"-line",i))},_update:function(){var t=this._map.getBounds(),e=t.getCenter().lat,i=6378137*Math.PI*Math.cos(e*Math.PI/180),n=i*(t.getNorthEast().lng-t.getSouthWest().lng)/180,o=this._map.getSize(),s=this.options,a=0;o.x>0&&(a=n*(s.maxWidth/o.x)),this._updateScales(s,a)},_updateScales:function(t,e){t.metric&&e&&this._updateMetric(e),t.imperial&&e&&this._updateImperial(e)},_updateMetric:function(t){var e=this._getRoundNum(t);this._mScale.style.width=this._getScaleWidth(e/t)+"px",this._mScale.innerHTML=1e3>e?e+" m":e/1e3+" km"},_updateImperial:function(t){var e,i,n,o=3.2808399*t,s=this._iScale;o>5280?(e=o/5280,i=this._getRoundNum(e),s.style.width=this._getScaleWidth(i/e)+"px",s.innerHTML=i+" mi"):(n=this._getRoundNum(o),s.style.width=this._getScaleWidth(n/o)+"px",s.innerHTML=n+" ft")},_getScaleWidth:function(t){return Math.round(this.options.maxWidth*t)-10},_getRoundNum:function(t){var e=Math.pow(10,(Math.floor(t)+"").length-1),i=t/e;return i=i>=10?10:i>=5?5:i>=3?3:i>=2?2:1,e*i}}),o.control.scale=function(t){return new o.Control.Scale(t)},o.Control.Layers=o.Control.extend({options:{collapsed:!0,position:"topright",autoZIndex:!0},initialize:function(t,e,i){o.setOptions(this,i),this._layers={},this._lastZIndex=0,this._handlingClick=!1;for(var n in t)this._addLayer(t[n],n);for(n in e)this._addLayer(e[n],n,!0)},onAdd:function(t){return this._initLayout(),this._update(),t.on("layeradd",this._onLayerChange,this).on("layerremove",this._onLayerChange,this),this._container},onRemove:function(t){t.off("layeradd",this._onLayerChange,this).off("layerremove",this._onLayerChange,this)},addBaseLayer:function(t,e){return this._addLayer(t,e),this._update(),this},addOverlay:function(t,e){return this._addLayer(t,e,!0),this._update(),this},removeLayer:function(t){var e=o.stamp(t);return delete this._layers[e],this._update(),this},_initLayout:function(){var t="leaflet-control-layers",e=this._container=o.DomUtil.create("div",t);e.setAttribute("aria-haspopup",!0),o.Browser.touch?o.DomEvent.on(e,"click",o.DomEvent.stopPropagation):o.DomEvent.disableClickPropagation(e).disableScrollPropagation(e);var i=this._form=o.DomUtil.create("form",t+"-list");if(this.options.collapsed){o.Browser.android||o.DomEvent.on(e,"mouseover",this._expand,this).on(e,"mouseout",this._collapse,this);var n=this._layersLink=o.DomUtil.create("a",t+"-toggle",e);n.href="#",n.title="Layers",o.Browser.touch?o.DomEvent.on(n,"click",o.DomEvent.stop).on(n,"click",this._expand,this):o.DomEvent.on(n,"focus",this._expand,this),o.DomEvent.on(i,"click",function(){setTimeout(o.bind(this._onInputClick,this),0)},this),this._map.on("click",this._collapse,this)}else this._expand();this._baseLayersList=o.DomUtil.create("div",t+"-base",i),this._separator=o.DomUtil.create("div",t+"-separator",i),this._overlaysList=o.DomUtil.create("div",t+"-overlays",i),e.appendChild(i)},_addLayer:function(t,e,i){var n=o.stamp(t);this._layers[n]={layer:t,name:e,overlay:i},this.options.autoZIndex&&t.setZIndex&&(this._lastZIndex++,t.setZIndex(this._lastZIndex))},_update:function(){if(this._container){this._baseLayersList.innerHTML="",this._overlaysList.innerHTML="";var t,e,i=!1,n=!1;for(t in this._layers)e=this._layers[t],this._addItem(e),n=n||e.overlay,i=i||!e.overlay;this._separator.style.display=n&&i?"":"none"}},_onLayerChange:function(t){var e=this._layers[o.stamp(t.layer)];if(e){this._handlingClick||this._update();var i=e.overlay?"layeradd"===t.type?"overlayadd":"overlayremove":"layeradd"===t.type?"baselayerchange":null;i&&this._map.fire(i,e)}},_createRadioElement:function(t,i){var n=' ";var o=e.createElement("div");return o.innerHTML=n,o.firstChild},_addItem:function(t){var i,n=e.createElement("label"),s=this._map.hasLayer(t.layer);t.overlay?(i=e.createElement("input"),i.type="checkbox",i.className="leaflet-control-layers-selector",i.defaultChecked=s):i=this._createRadioElement("leaflet-base-layers",s),i.layerId=o.stamp(t.layer),o.DomEvent.on(i,"click",this._onInputClick,this);var a=e.createElement("span");a.innerHTML=" "+t.name,n.appendChild(i),n.appendChild(a);var r=t.overlay?this._overlaysList:this._baseLayersList;return r.appendChild(n),n},_onInputClick:function(){var t,e,i,n=this._form.getElementsByTagName("input"),o=n.length;for(this._handlingClick=!0,t=0;o>t;t++)e=n[t],i=this._layers[e.layerId],e.checked&&!this._map.hasLayer(i.layer)?this._map.addLayer(i.layer):!e.checked&&this._map.hasLayer(i.layer)&&this._map.removeLayer(i.layer);this._handlingClick=!1,this._refocusOnMap()},_expand:function(){o.DomUtil.addClass(this._container,"leaflet-control-layers-expanded")},_collapse:function(){this._container.className=this._container.className.replace(" leaflet-control-layers-expanded","")}}),o.control.layers=function(t,e,i){return new o.Control.Layers(t,e,i)},o.PosAnimation=o.Class.extend({includes:o.Mixin.Events,run:function(t,e,i,n){this.stop(),this._el=t,this._inProgress=!0,this._newPos=e,this.fire("start"),t.style[o.DomUtil.TRANSITION]="all "+(i||.25)+"s cubic-bezier(0,0,"+(n||.5)+",1)",o.DomEvent.on(t,o.DomUtil.TRANSITION_END,this._onTransitionEnd,this),o.DomUtil.setPosition(t,e),o.Util.falseFn(t.offsetWidth),this._stepTimer=setInterval(o.bind(this._onStep,this),50)},stop:function(){this._inProgress&&(o.DomUtil.setPosition(this._el,this._getPos()),this._onTransitionEnd(),o.Util.falseFn(this._el.offsetWidth))},_onStep:function(){var t=this._getPos();return t?(this._el._leaflet_pos=t,void this.fire("step")):void this._onTransitionEnd()},_transformRe:/([-+]?(?:\d*\.)?\d+)\D*, ([-+]?(?:\d*\.)?\d+)\D*\)/,_getPos:function(){var e,i,n,s=this._el,a=t.getComputedStyle(s);if(o.Browser.any3d){if(n=a[o.DomUtil.TRANSFORM].match(this._transformRe),!n)return;e=parseFloat(n[1]),i=parseFloat(n[2])}else e=parseFloat(a.left),i=parseFloat(a.top);return new o.Point(e,i,!0)},_onTransitionEnd:function(){o.DomEvent.off(this._el,o.DomUtil.TRANSITION_END,this._onTransitionEnd,this),this._inProgress&&(this._inProgress=!1,this._el.style[o.DomUtil.TRANSITION]="",this._el._leaflet_pos=this._newPos,clearInterval(this._stepTimer),this.fire("step").fire("end"))}}),o.Map.include({setView:function(t,e,n){if(e=e===i?this._zoom:this._limitZoom(e),t=this._limitCenter(o.latLng(t),e,this.options.maxBounds),n=n||{},this._panAnim&&this._panAnim.stop(),this._loaded&&!n.reset&&n!==!0){n.animate!==i&&(n.zoom=o.extend({animate:n.animate},n.zoom),n.pan=o.extend({animate:n.animate},n.pan));var s=this._zoom!==e?this._tryAnimatedZoom&&this._tryAnimatedZoom(t,e,n.zoom):this._tryAnimatedPan(t,n.pan);if(s)return clearTimeout(this._sizeTimer),this}return this._resetView(t,e),this},panBy:function(t,e){if(t=o.point(t).round(),e=e||{},!t.x&&!t.y)return this;if(this._panAnim||(this._panAnim=new o.PosAnimation,this._panAnim.on({step:this._onPanTransitionStep,end:this._onPanTransitionEnd},this)),e.noMoveStart||this.fire("movestart"),e.animate!==!1){o.DomUtil.addClass(this._mapPane,"leaflet-pan-anim");var i=this._getMapPanePos().subtract(t);this._panAnim.run(this._mapPane,i,e.duration||.25,e.easeLinearity)}else this._rawPanBy(t),this.fire("move").fire("moveend");return this},_onPanTransitionStep:function(){this.fire("move")},_onPanTransitionEnd:function(){o.DomUtil.removeClass(this._mapPane,"leaflet-pan-anim"),this.fire("moveend")},_tryAnimatedPan:function(t,e){var i=this._getCenterOffset(t)._floor();return(e&&e.animate)===!0||this.getSize().contains(i)?(this.panBy(i,e),!0):!1}}),o.PosAnimation=o.DomUtil.TRANSITION?o.PosAnimation:o.PosAnimation.extend({run:function(t,e,i,n){this.stop(),this._el=t,this._inProgress=!0,this._duration=i||.25,this._easeOutPower=1/Math.max(n||.5,.2),this._startPos=o.DomUtil.getPosition(t),this._offset=e.subtract(this._startPos),this._startTime=+new Date,this.fire("start"),this._animate()},stop:function(){this._inProgress&&(this._step(),this._complete())},_animate:function(){this._animId=o.Util.requestAnimFrame(this._animate,this),this._step()},_step:function(){var t=+new Date-this._startTime,e=1e3*this._duration;e>t?this._runFrame(this._easeOut(t/e)):(this._runFrame(1),this._complete())},_runFrame:function(t){var e=this._startPos.add(this._offset.multiplyBy(t));o.DomUtil.setPosition(this._el,e),this.fire("step")},_complete:function(){o.Util.cancelAnimFrame(this._animId),this._inProgress=!1,this.fire("end")},_easeOut:function(t){return 1-Math.pow(1-t,this._easeOutPower)}}),o.Map.mergeOptions({zoomAnimation:!0,zoomAnimationThreshold:4}),o.DomUtil.TRANSITION&&o.Map.addInitHook(function(){this._zoomAnimated=this.options.zoomAnimation&&o.DomUtil.TRANSITION&&o.Browser.any3d&&!o.Browser.android23&&!o.Browser.mobileOpera,this._zoomAnimated&&o.DomEvent.on(this._mapPane,o.DomUtil.TRANSITION_END,this._catchTransitionEnd,this)}),o.Map.include(o.DomUtil.TRANSITION?{_catchTransitionEnd:function(t){this._animatingZoom&&t.propertyName.indexOf("transform")>=0&&this._onZoomTransitionEnd()},_nothingToAnimate:function(){return!this._container.getElementsByClassName("leaflet-zoom-animated").length},_tryAnimatedZoom:function(t,e,i){if(this._animatingZoom)return!0;if(i=i||{},!this._zoomAnimated||i.animate===!1||this._nothingToAnimate()||Math.abs(e-this._zoom)>this.options.zoomAnimationThreshold)return!1;var n=this.getZoomScale(e),o=this._getCenterOffset(t)._divideBy(1-1/n),s=this._getCenterLayerPoint()._add(o);return i.animate===!0||this.getSize().contains(o)?(this.fire("movestart").fire("zoomstart"),this._animateZoom(t,e,s,n,null,!0),!0):!1},_animateZoom:function(t,e,i,n,s,a,r){r||(this._animatingZoom=!0),o.DomUtil.addClass(this._mapPane,"leaflet-zoom-anim"),this._animateToCenter=t,this._animateToZoom=e,o.Draggable&&(o.Draggable._disabled=!0),o.Util.requestAnimFrame(function(){this.fire("zoomanim",{center:t,zoom:e,origin:i,scale:n,delta:s,backwards:a})},this)},_onZoomTransitionEnd:function(){this._animatingZoom=!1,o.DomUtil.removeClass(this._mapPane,"leaflet-zoom-anim"),this._resetView(this._animateToCenter,this._animateToZoom,!0,!0),o.Draggable&&(o.Draggable._disabled=!1)}}:{}),o.TileLayer.include({_animateZoom:function(t){this._animating||(this._animating=!0,this._prepareBgBuffer());var e=this._bgBuffer,i=o.DomUtil.TRANSFORM,n=t.delta?o.DomUtil.getTranslateString(t.delta):e.style[i],s=o.DomUtil.getScaleString(t.scale,t.origin);e.style[i]=t.backwards?s+" "+n:n+" "+s},_endZoomAnim:function(){var t=this._tileContainer,e=this._bgBuffer;t.style.visibility="",t.parentNode.appendChild(t),o.Util.falseFn(e.offsetWidth),this._animating=!1},_clearBgBuffer:function(){var t=this._map;!t||t._animatingZoom||t.touchZoom._zooming||(this._bgBuffer.innerHTML="",this._bgBuffer.style[o.DomUtil.TRANSFORM]="")},_prepareBgBuffer:function(){var t=this._tileContainer,e=this._bgBuffer,i=this._getLoadedTilesPercentage(e),n=this._getLoadedTilesPercentage(t);return e&&i>.5&&.5>n?(t.style.visibility="hidden",void this._stopLoadingImages(t)):(e.style.visibility="hidden",e.style[o.DomUtil.TRANSFORM]="",this._tileContainer=e,e=this._bgBuffer=t,this._stopLoadingImages(e),void clearTimeout(this._clearBgBufferTimer))},_getLoadedTilesPercentage:function(t){var e,i,n=t.getElementsByTagName("img"),o=0;for(e=0,i=n.length;i>e;e++)n[e].complete&&o++;return o/i},_stopLoadingImages:function(t){var e,i,n,s=Array.prototype.slice.call(t.getElementsByTagName("img"));for(e=0,i=s.length;i>e;e++)n=s[e],n.complete||(n.onload=o.Util.falseFn,n.onerror=o.Util.falseFn,n.src=o.Util.emptyImageUrl,n.parentNode.removeChild(n))}}),o.Map.include({_defaultLocateOptions:{watch:!1,setView:!1,maxZoom:1/0,timeout:1e4,maximumAge:0,enableHighAccuracy:!1},locate:function(t){if(t=this._locateOptions=o.extend(this._defaultLocateOptions,t),!navigator.geolocation)return this._handleGeolocationError({code:0,message:"Geolocation not supported."}),this;var e=o.bind(this._handleGeolocationResponse,this),i=o.bind(this._handleGeolocationError,this);return t.watch?this._locationWatchId=navigator.geolocation.watchPosition(e,i,t):navigator.geolocation.getCurrentPosition(e,i,t),this},stopLocate:function(){return navigator.geolocation&&navigator.geolocation.clearWatch(this._locationWatchId),this._locateOptions&&(this._locateOptions.setView=!1),this},_handleGeolocationError:function(t){var e=t.code,i=t.message||(1===e?"permission denied":2===e?"position unavailable":"timeout");this._locateOptions.setView&&!this._loaded&&this.fitWorld(),this.fire("locationerror",{code:e,message:"Geolocation error: "+i+"."})},_handleGeolocationResponse:function(t){var e=t.coords.latitude,i=t.coords.longitude,n=new o.LatLng(e,i),s=180*t.coords.accuracy/40075017,a=s/Math.cos(o.LatLng.DEG_TO_RAD*e),r=o.latLngBounds([e-s,i-a],[e+s,i+a]),h=this._locateOptions;if(h.setView){var l=Math.min(this.getBoundsZoom(r),h.maxZoom);this.setView(n,l)}var u={latlng:n,bounds:r,timestamp:t.timestamp};for(var c in t.coords)"number"==typeof t.coords[c]&&(u[c]=t.coords[c]);this.fire("locationfound",u)}})}(window,document);
\ No newline at end of file
diff --git a/servers/php/js/leaflet-plugins/LICENSE b/servers/php/js/leaflet-plugins/LICENSE
new file mode 100644
index 0000000..f371b7d
--- /dev/null
+++ b/servers/php/js/leaflet-plugins/LICENSE
@@ -0,0 +1,22 @@
+Copyright (c) 2011-2012, Pavel Shramov
+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.
\ No newline at end of file
diff --git a/servers/php/js/leaflet-plugins/bing.js b/servers/php/js/leaflet-plugins/bing.js
new file mode 100644
index 0000000..9c1d7aa
--- /dev/null
+++ b/servers/php/js/leaflet-plugins/bing.js
@@ -0,0 +1,131 @@
+/*
+Copyright (c) 2011-2012, Pavel Shramov
+All rights reserved.
+https://github.com/robertharm/leaflet-plugins
+License in same folder as this file
+*/
+
+L.BingLayer = L.TileLayer.extend({
+ options: {
+ subdomains: [0, 1, 2, 3],
+ type: 'Road',
+ attribution: 'Bing',
+ culture: ''
+ },
+
+ initialize: function(key, options) {
+ L.Util.setOptions(this, options);
+
+ this._key = key;
+ this._url = null;
+ this.meta = {};
+ this.loadMetadata();
+ },
+
+ tile2quad: function(x, y, z) {
+ var quad = '';
+ for (var i = z; i > 0; i--) {
+ var digit = 0;
+ var mask = 1 << (i - 1);
+ if ((x & mask) != 0) digit += 1;
+ if ((y & mask) != 0) digit += 2;
+ quad = quad + digit;
+ }
+ return quad;
+ },
+
+ getTileUrl: function(p, z) {
+ var z = this._getZoomForUrl();
+ var subdomains = this.options.subdomains,
+ s = this.options.subdomains[Math.abs((p.x + p.y) % subdomains.length)];
+ return this._url.replace('{subdomain}', s)
+ .replace('{quadkey}', this.tile2quad(p.x, p.y, z))
+ .replace('{culture}', this.options.culture);
+ },
+
+ loadMetadata: function() {
+ var _this = this;
+ var cbid = '_bing_metadata_' + L.Util.stamp(this);
+ window[cbid] = function (meta) {
+ _this.meta = meta;
+ window[cbid] = undefined;
+ var e = document.getElementById(cbid);
+ e.parentNode.removeChild(e);
+ if (meta.errorDetails) {
+ if (window.console) console.log("Leaflet Bing Plugin Error - Got metadata: " + meta.errorDetails);
+ return;
+ }
+ _this.initMetadata();
+ };
+ var url = document.location.protocol + "//dev.virtualearth.net/REST/v1/Imagery/Metadata/" + this.options.type + "?include=ImageryProviders&jsonp=" + cbid +
+ "&key=" + this._key + "&UriScheme=" + document.location.protocol.slice(0, -1);
+ var script = document.createElement("script");
+ script.type = "text/javascript";
+ script.src = url;
+ script.id = cbid;
+ document.getElementsByTagName("head")[0].appendChild(script);
+ },
+
+ initMetadata: function() {
+ var r = this.meta.resourceSets[0].resources[0];
+ this.options.subdomains = r.imageUrlSubdomains;
+ this._url = r.imageUrl;
+ this._providers = [];
+ if (r.imageryProviders) {
+ for (var i = 0; i < r.imageryProviders.length; i++) {
+ var p = r.imageryProviders[i];
+ for (var j = 0; j < p.coverageAreas.length; j++) {
+ var c = p.coverageAreas[j];
+ var coverage = {zoomMin: c.zoomMin, zoomMax: c.zoomMax, active: false};
+ var bounds = new L.LatLngBounds(
+ new L.LatLng(c.bbox[0]+0.01, c.bbox[1]+0.01),
+ new L.LatLng(c.bbox[2]-0.01, c.bbox[3]-0.01)
+ );
+ coverage.bounds = bounds;
+ coverage.attrib = p.attribution;
+ this._providers.push(coverage);
+ }
+ }
+ }
+ this._update();
+ },
+
+ _update: function() {
+ if (this._url == null || !this._map) return;
+ this._update_attribution();
+ L.TileLayer.prototype._update.apply(this, []);
+ },
+
+ _update_attribution: function() {
+ var bounds = this._map.getBounds();
+ var zoom = this._map.getZoom();
+ for (var i = 0; i < this._providers.length; i++) {
+ var p = this._providers[i];
+ if ((zoom <= p.zoomMax && zoom >= p.zoomMin) &&
+ bounds.intersects(p.bounds)) {
+ if (!p.active && this._map.attributionControl)
+ this._map.attributionControl.addAttribution(p.attrib);
+ p.active = true;
+ } else {
+ if (p.active && this._map.attributionControl)
+ this._map.attributionControl.removeAttribution(p.attrib);
+ p.active = false;
+ }
+ }
+ },
+
+ onRemove: function(map) {
+ for (var i = 0; i < this._providers.length; i++) {
+ var p = this._providers[i];
+ if (p.active && this._map.attributionControl) {
+ this._map.attributionControl.removeAttribution(p.attrib);
+ p.active = false;
+ }
+ }
+ L.TileLayer.prototype.onRemove.apply(this, [map]);
+ }
+});
+
+L.bingLayer = function (key, options) {
+ return new L.BingLayer(key, options);
+};
\ No newline at end of file
diff --git a/servers/php/js/leaflet-plugins/google.js b/servers/php/js/leaflet-plugins/google.js
new file mode 100644
index 0000000..60196b2
--- /dev/null
+++ b/servers/php/js/leaflet-plugins/google.js
@@ -0,0 +1,242 @@
+/*
+Copyright (c) 2011-2012, Pavel Shramov
+All rights reserved.
+https://github.com/robertharm/leaflet-plugins
+License in same folder as this file
+*/
+
+var adsense_status = 'disabled';
+
+L.Google = L.Class.extend( {
+ includes: L.Mixin.Events,
+
+ options: {
+ minZoom: 0,
+ maxZoom: 18,
+ tileSize: 256,
+ subdomains: 'abc',
+ errorTileUrl: '',
+ attribution: '',
+ opacity: 1,
+ continuousWorld: false,
+ noWrap: false,
+ mapOptions: {
+ backgroundColor: '#dddddd'
+ }
+ },
+
+ // Possible types: SATELLITE, ROADMAP, HYBRID, TERRAIN
+ initialize: function(type, options) {
+ L.Util.setOptions(this, options);
+
+ this._ready = google.maps.Map !== undefined;
+ if (!this._ready) L.Google.asyncWait.push(this);
+
+ this._type = type || 'ROADMAP';
+ },
+
+ onAdd: function(map, insertAtTheBottom) {
+ this._map = map;
+ this._insertAtTheBottom = insertAtTheBottom;
+
+ // create a container div for tiles
+ this._initContainer();
+ this._initMapObject();
+
+ if (adsense_status == 'enabled') {
+ this._initAdSense();
+ }
+
+ // set up events
+ map.on('viewreset', this._resetCallback, this);
+
+ this._limitedUpdate = L.Util.limitExecByInterval(this._update, 150, this);
+ map.on('move', this._update, this);
+
+ map.on('zoomanim', this._handleZoomAnim, this);
+
+ //20px instead of 1em to avoid a slight overlap with google's attribution
+ map._controlCorners.bottomright.style.marginBottom = '20px';
+
+ this._reset();
+ this._update();
+ },
+
+ onRemove: function(map) {
+ this._map._container.removeChild(this._container);
+ this._map.off('viewreset', this._resetCallback, this);
+ this._map.off('move', this._update, this);
+ this._map.off('zoomanim', this._handleZoomAnim, this);
+ map._controlCorners.bottomright.style.marginBottom = '0em';
+ },
+
+ getAttribution: function() {
+ return this.options.attribution;
+ },
+
+ setOpacity: function(opacity) {
+ this.options.opacity = opacity;
+ if (opacity < 1) {
+ L.DomUtil.setOpacity(this._container, opacity);
+ }
+ },
+
+ setElementSize: function(e, size) {
+ e.style.width = size.x + 'px';
+ e.style.height = size.y + 'px';
+ },
+
+ _initContainer: function() {
+ var tilePane = this._map._container,
+ first = tilePane.firstChild;
+
+ if (!this._container) {
+ this._container = L.DomUtil.create('div', 'leaflet-google-layer leaflet-top leaflet-left');
+ this._container.id = '_GMapContainer_' + L.Util.stamp(this);
+ this._container.style.zIndex = 'auto';
+ }
+
+ tilePane.insertBefore(this._container, first);
+
+ this.setOpacity(this.options.opacity);
+ this.setElementSize(this._container, this._map.getSize());
+ },
+
+ _initMapObject: function() {
+ if (!this._ready) return;
+ this._google_center = new google.maps.LatLng(0, 0);
+ var map = new google.maps.Map(this._container, {
+ center: this._google_center,
+ zoom: 0,
+ tilt: 0,
+ mapTypeId: google.maps.MapTypeId[this._type],
+ disableDefaultUI: true,
+ keyboardShortcuts: false,
+ draggable: false,
+ disableDoubleClickZoom: true,
+ scrollwheel: false,
+ streetViewControl: false,
+ styles: this.options.mapOptions.styles,
+ backgroundColor: this.options.mapOptions.backgroundColor
+ });
+
+ var _this = this;
+ this._reposition = google.maps.event.addListenerOnce(map, 'center_changed',
+ function() { _this.onReposition(); });
+ this._google = map;
+
+ google.maps.event.addListenerOnce(map, 'idle',
+ function() { _this._checkZoomLevels(); });
+
+ //Reporting that map-object was initialized.
+ this.fire('MapObjectInitialized', { mapObject: map });
+ },
+
+ _checkZoomLevels: function() {
+ //setting the zoom level on the Google map may result in a different zoom level than the one requested
+ //(it won't go beyond the level for which they have data).
+ // verify and make sure the zoom levels on both Leaflet and Google maps are consistent
+ if (this._google.getZoom() !== this._map.getZoom()) {
+ //zoom levels are out of sync. Set the leaflet zoom level to match the google one
+ this._map.setZoom( this._google.getZoom() );
+ }
+
+ // this._resetCallback(this._container);
+ },
+
+ _resetCallback: function(e) {
+ this._reset(e.hard);
+ },
+
+ _reset: function(clearOldContainer) {
+ this._initContainer();
+ },
+
+ _update: function(e) {
+ if (!this._google) return;
+ this._resize();
+
+ var center = e && e.latlng ? e.latlng : this._map.getCenter();
+ var _center = new google.maps.LatLng(center.lat, center.lng);
+
+ this._google.setCenter(_center);
+ this._google.setZoom(Math.round(this._map.getZoom()));
+
+ this._checkZoomLevels();
+ },
+
+ _resize: function() {
+ var size = this._map.getSize();
+ if (this._container.style.width === size.x &&
+ this._container.style.height === size.y)
+ return;
+ this.setElementSize(this._container, size);
+ this.onReposition();
+ },
+
+
+ _handleZoomAnim: function (e) {
+ var center = e.center;
+ var _center = new google.maps.LatLng(center.lat, center.lng);
+
+ this._google.setCenter(_center);
+ this._google.setZoom(Math.round(e.zoom));
+ },
+
+
+ onReposition: function() {
+ if (!this._google) return;
+ google.maps.event.trigger(this._google, 'resize');
+ },
+
+ _initAdSense: function() {
+ var adUnitDiv = document.createElement('div');
+ adUnitDiv.className = "leaflet-control";
+ adUnitDiv.style.margin = "0";
+ adUnitDiv.style.clear = "none";
+ var user_adsense = new String;
+ user_adsense.format = google.maps.adsense.AdFormat["HALF_BANNER"];
+ user_adsense.position = google.maps.ControlPosition["TOP_CENTER"];
+ user_adsense.cposition = "TOP_CENTER";
+ user_adsense.backgroundColor = "#c4d4f3";
+ user_adsense.borderColor = "#aaa";
+ user_adsense.titleColor = "#0000cc";
+ user_adsense.textColor = "#000000";
+ user_adsense.urlColor = "#009900";
+ user_adsense.channelNumber = "6961715451";
+ user_adsense.publisherID = "pub-7095775186404141";
+
+ var adUnitOptions = {
+ format: user_adsense.format,
+ position: user_adsense.position,
+ backgroundColor: user_adsense.backgroundColor,
+ borderColor: user_adsense.borderColor,
+ titleColor: user_adsense.titleColor,
+ textColor: user_adsense.textColor,
+ urlColor: user_adsense.urlColor,
+ map: this._google,
+ visible: true,
+ channelNumber: user_adsense.channelNumber,
+ publisherId: user_adsense.publisherID
+ }
+ //info: dont load ads on minimaps
+ var size = this._map.getSize();
+ if (size.x > 150) {
+ this._adUnit = new google.maps.adsense.AdUnit(adUnitDiv, adUnitOptions);
+ }
+ }
+});
+
+L.Google.asyncWait = [];
+L.Google.asyncInitialize = function() {
+ var i;
+ for (i = 0; i < L.Google.asyncWait.length; i++) {
+ var o = L.Google.asyncWait[i];
+ o._ready = true;
+ if (o._container) {
+ o._initMapObject();
+ o._update();
+ }
+ }
+ L.Google.asyncWait = [];
+};
\ No newline at end of file
diff --git a/servers/php/js/maps.js b/servers/php/js/maps.js
new file mode 100644
index 0000000..6fe74b2
--- /dev/null
+++ b/servers/php/js/maps.js
@@ -0,0 +1,465 @@
+jQuery(document).ready(function($) {
+ var routeSelect = document.getElementById('routeSelect');
+ var map = document.getElementById('map-canvas');
+ var autoRefresh = false;
+ var intervalID = 0;
+ var sessionIDArray;
+ var viewingAllRoutes = false;
+
+ getAllRoutesForMap();
+ loadRoutesIntoDropdownBox();
+
+ $("#routeSelect").change(function() {
+ if (hasMap()) {
+ viewingAllRoutes = false;
+
+ getRouteForMap();
+ }
+ });
+
+ $("#refresh").click(function() {
+ if (viewingAllRoutes) {
+ getAllRoutesForMap();
+ } else {
+ if (hasMap()) {
+ getRouteForMap();
+ }
+ }
+ });
+
+ $("#delete").click(function() {
+ deleteRoute();
+ });
+
+ $('#autorefresh').click(function() {
+ if (autoRefresh) {
+ turnOffAutoRefresh();
+ } else {
+ turnOnAutoRefresh();
+ }
+ });
+
+ $("#viewall").click(function() {
+ getAllRoutesForMap();
+ });
+
+ function getAllRoutesForMap() {
+ viewingAllRoutes = true;
+ routeSelect.selectedIndex = 0;
+ showPermanentMessage('Please select a route below');
+
+ $.ajax({
+ url: 'getallroutesformap.php',
+ type: 'GET',
+ dataType: 'json',
+ success: function(data) {
+ loadGPSLocations(data);
+ },
+ error: function (xhr, status, errorThrown) {
+ console.log("error status: " + xhr.status);
+ console.log("errorThrown: " + errorThrown);
+ }
+ });
+ }
+
+ function loadRoutesIntoDropdownBox() {
+ $.ajax({
+ url: 'getroutes.php',
+ type: 'GET',
+ dataType: 'json',
+ success: function(data) {
+ loadRoutes(data);
+ },
+ error: function (xhr, status, errorThrown) {
+ console.log("status: " + xhr.status);
+ console.log("errorThrown: " + errorThrown);
+ }
+ });
+ }
+
+ function loadRoutes(json) {
+ if (json.length == 0) {
+ showPermanentMessage('There are no routes available to view');
+ }
+ else {
+ // create the first option of the dropdown box
+ var option = document.createElement('option');
+ option.setAttribute('value', '0');
+ option.innerHTML = 'Select Route...';
+ routeSelect.appendChild(option);
+
+ // when a user taps on a marker, the position of the sessionID in this array is the position of the route
+ // in the dropdown box. it's used below to set the index of the dropdown box when the map is changed
+ sessionIDArray = [];
+
+ // iterate through the routes and load them into the dropdwon box.
+ $(json.routes).each(function(key, value){
+ var option = document.createElement('option');
+ option.setAttribute('value', '?sessionid=' + $(this).attr('sessionID'));
+
+ sessionIDArray.push($(this).attr('sessionID'));
+
+ option.innerHTML = $(this).attr('phoneNumber') + " " + $(this).attr('times');
+ routeSelect.appendChild(option);
+ });
+
+ // need to reset this for firefox
+ routeSelect.selectedIndex = 0;
+
+ showPermanentMessage('Please select a route below');
+ }
+ }
+
+ function getRouteForMap() {
+ if (hasMap()) {
+ // console.log($("#routeSelect").prop("selectedIndex"));
+
+ var url = 'getrouteformap.php' + $('#routeSelect').val();
+
+ $.ajax({
+ url: url,
+ type: 'GET',
+ dataType: 'json',
+ success: function(data) {
+ loadGPSLocations(data);
+ },
+ error: function (xhr, status, errorThrown) {
+ console.log("status: " + xhr.status);
+ console.log("errorThrown: " + errorThrown);
+ }
+ });
+
+ }
+ }
+
+ function loadGPSLocations(json) {
+ // console.log(JSON.stringify(json));
+
+ if (json.length == 0) {
+ showPermanentMessage('There is no tracking data to view');
+ map.innerHTML = '';
+ }
+ else {
+ if (map.id == 'map-canvas') {
+ // clear any old map objects
+ document.getElementById('map-canvas').outerHTML = "
";
+
+ // use leaflet (http://leafletjs.com/) to create our map and map layers
+ var gpsTrackerMap = new L.map('map-canvas');
+
+ var openStreetMapsURL = ('https:' == document.location.protocol ? 'https://' : 'http://') +
+ '{s}.tile.openstreetmap.org/{z}/{x}/{y}.png';
+ var openStreetMapsLayer = new L.TileLayer(openStreetMapsURL,
+ {attribution:'©2014 OpenStreetMap contributors'});
+
+ // need to get your own bing maps key, http://www.microsoft.com/maps/create-a-bing-maps-key.aspx
+ var bingMapsLayer = new L.BingLayer("AnH1IKGCBwAiBWfYAHMtIfIhMVybHFx2GxsReNP5W0z6P8kRa67_QwhM4PglI9yL");
+ var googleMapsLayer = new L.Google('ROADMAP');
+
+ // this fixes the zoom buttons from freezing
+ // https://github.com/shramov/leaflet-plugins/issues/62
+ L.polyline([[0, 0], ]).addTo(gpsTrackerMap);
+
+ // this sets which map layer will first be displayed
+ gpsTrackerMap.addLayer(googleMapsLayer);
+
+ // this is the switcher control to switch between map types
+ gpsTrackerMap.addControl(new L.Control.Layers({
+ 'Bing Maps':bingMapsLayer,
+ 'Google Maps':googleMapsLayer,
+ 'OpenStreetMaps':openStreetMapsLayer
+ }, {}));
+ }
+
+ var finalLocation = false;
+ var counter = 0;
+ var locationArray = [];
+
+ // iterate through the locations and create map markers for each location
+ $(json.locations).each(function(key, value){
+ var latitude = $(this).attr('latitude');
+ var longitude = $(this).attr('longitude');
+ var sessionID = $(this).attr('sessionID');
+ var tempLocation = new L.LatLng(latitude, longitude);
+
+ locationArray.push(tempLocation);
+ counter++;
+
+ // want to set the map center on the last location
+ if (counter == $(json.locations).length) {
+ //gpsTrackerMap.setView(tempLocation, zoom); if using fixed zoom
+ finalLocation = true;
+
+ if (!viewingAllRoutes) {
+ displayCityName(latitude, longitude);
+ }
+ }
+
+ var marker = createMarker(
+ latitude,
+ longitude,
+ sessionID,
+ $(this).attr('speed'),
+ $(this).attr('direction'),
+ $(this).attr('distance'),
+ $(this).attr('locationMethod'),
+ $(this).attr('gpsTime'),
+ $(this).attr('phoneNumber'),
+ $(this).attr('sessionID'),
+ $(this).attr('accuracy'),
+ $(this).attr('extraInfo'),
+ gpsTrackerMap, finalLocation);
+ });
+
+ // fit markers within window
+ var bounds = new L.LatLngBounds(locationArray);
+ gpsTrackerMap.fitBounds(bounds);
+
+ // restarting interval here in case we are coming from viewing all routes
+ if (autoRefresh) {
+ restartInterval();
+ }
+ }
+ }
+
+ function createMarker(latitude, longitude, sessionID, speed, direction, distance, locationMethod, gpsTime,
+ phoneNumber, sessionID, accuracy, extraInfo, map, finalLocation) {
+ var iconUrl;
+
+ if (finalLocation) {
+ iconUrl = 'images/coolred_small.png';
+ } else {
+ iconUrl = 'images/coolgreen2_small.png';
+ }
+
+ var markerIcon = new L.Icon({
+ iconUrl: iconUrl,
+ shadowUrl: 'images/coolshadow_small.png',
+ iconSize: [12, 20],
+ shadowSize: [22, 20],
+ iconAnchor: [6, 20],
+ shadowAnchor: [6, 20],
+ popupAnchor: [-3, -25]
+ });
+
+ var lastMarker = "";
+
+ // when a user clicks on last marker, let them know it's final one
+ if (finalLocation) {
+ lastMarker = " Final location ";
+ }
+
+ // convert from meters to feet
+ accuracy = parseInt(accuracy * 3.28);
+
+ var popupWindowText = "" +
+ " " +
+ " " + lastMarker +
+ " Speed: " + speed + " mph " +
+ "Distance: " + distance + " mi " +
+ "Time: " + gpsTime + " " +
+ "Name: " + phoneNumber + " " +
+ "Accuracy: " + accuracy + " ft
";
+
+
+ var gpstrackerMarker;
+ var title = phoneNumber + " - " + gpsTime
+
+ // make sure the final red marker always displays on top
+ if (finalLocation) {
+ gpstrackerMarker = new L.marker(new L.LatLng(latitude, longitude), {title: title, icon: markerIcon, zIndexOffset: 999}).bindPopup(popupWindowText).addTo(map);
+ } else {
+ gpstrackerMarker = new L.marker(new L.LatLng(latitude, longitude), {title: title, icon: markerIcon}).bindPopup(popupWindowText).addTo(map);
+ }
+
+ // if we are viewing all routes, we want to go to a route when a user taps on a marker instead of displaying popupWindow
+ if (viewingAllRoutes) {
+ gpstrackerMarker.unbindPopup();
+
+ gpstrackerMarker.on("click", function() {
+ var url = 'getrouteformap.php?sessionid=' + sessionID;
+
+ viewingAllRoutes = false;
+
+ var indexOfRouteInRouteSelectDropdwon = sessionIDArray.indexOf(sessionID) + 1;
+ routeSelect.selectedIndex = indexOfRouteInRouteSelectDropdwon;
+
+ if (autoRefresh) {
+ restartInterval();
+ }
+
+ $.ajax({
+ url: url,
+ type: 'GET',
+ dataType: 'json',
+ success: function(data) {
+ loadGPSLocations(data);
+ },
+ error: function (xhr, status, errorThrown) {
+ console.log("status: " + xhr.status);
+ console.log("errorThrown: " + errorThrown);
+ }
+ });
+ }); // on click
+ }
+ }
+
+ function getCompassImage(azimuth) {
+ if ((azimuth >= 337 && azimuth <= 360) || (azimuth >= 0 && azimuth < 23))
+ return "compassN";
+ if (azimuth >= 23 && azimuth < 68)
+ return "compassNE";
+ if (azimuth >= 68 && azimuth < 113)
+ return "compassE";
+ if (azimuth >= 113 && azimuth < 158)
+ return "compassSE";
+ if (azimuth >= 158 && azimuth < 203)
+ return "compassS";
+ if (azimuth >= 203 && azimuth < 248)
+ return "compassSW";
+ if (azimuth >= 248 && azimuth < 293)
+ return "compassW";
+ if (azimuth >= 293 && azimuth < 337)
+ return "compassNW";
+
+ return "";
+ }
+
+ // check to see if we have a map loaded, don't want to autorefresh or delete without it
+ function hasMap() {
+ if (routeSelect.selectedIndex == 0) { // means no map
+ return false;
+ }
+ else {
+ return true;
+ }
+ }
+
+ function displayCityName(latitude, longitude) {
+ var lat = parseFloat(latitude);
+ var lng = parseFloat(longitude);
+ var latlng = new google.maps.LatLng(lat, lng);
+ reverseGeocoder = new google.maps.Geocoder();
+ reverseGeocoder.geocode({'latLng': latlng}, function(results, status) {
+ if (status == google.maps.GeocoderStatus.OK) {
+ // results[0] is full address
+ if (results[1]) {
+ reverseGeocoderResult = results[1].formatted_address;
+ showPermanentMessage(reverseGeocoderResult);
+ }
+ } else {
+ console.log('Geocoder failed due to: ' + status);
+ }
+ });
+ }
+
+ function turnOffAutoRefresh() {
+ console.log('turnOffAutoRefresh');
+ showMessage('Auto Refresh Off');
+ $('#autorefresh').val('Auto Refresh - Off');
+
+ autoRefresh = false;
+ clearInterval(intervalID);
+ }
+
+ function turnOnAutoRefresh() {
+ console.log('turnOnAutoRefresh');
+
+ showMessage('Auto Refresh On (1 min)');
+ $('#autorefresh').val('Auto Refresh - On');
+ autoRefresh = true;
+
+ restartInterval();
+ }
+
+ function restartInterval() {
+ // if someone is viewing all routes and then switches to a single route
+ // while autorefresh is on then the setInterval is going to be running with getAllRoutesForMap
+ // and not getRouteForMap
+
+ clearInterval(intervalID);
+
+ if (viewingAllRoutes) {
+ intervalID = setInterval(getAllRoutesForMap, 60 * 1000); // one minute
+ } else {
+ intervalID = setInterval(getRouteForMap, 60 * 1000);
+ }
+ }
+
+ function deleteRoute() {
+ if (hasMap()) {
+
+ // comment out these two lines to get delete working
+ // var answer = confirm("Disabled here on test website, this works fine.");
+ // return false;
+
+ var answer = confirm("This will permanently delete this route\n from the database. Do you want to delete?");
+ if (answer){
+ var url = 'deleteroute.php' + $('#routeSelect').val();
+
+ $.ajax({
+ url: url,
+ type: 'GET',
+ success: function() {
+ deleteRouteResponse();
+ getAllRoutesForMap();
+ }
+ });
+ }
+ else {
+ return false;
+ }
+ }
+ else {
+ alert("Please select a route before trying to delete.");
+ }
+ }
+
+ function deleteRouteResponse() {
+ routeSelect.length = 0;
+
+ document.getElementById('map-canvas').outerHTML = "
";
+
+ $.ajax({
+ url: 'getroutes.php',
+ type: 'GET',
+ success: function(data) {
+ loadRoutes(data);
+ }
+ });
+ }
+
+ // message visible for 7 seconds
+ function showMessage(message) {
+ // if we show a message like start auto refresh, we want to put back our current address afterwards
+ var tempMessage = $('#messages').html();
+
+ $('#messages').html(message);
+ setTimeout(function() {
+ $('#messages').html(tempMessage);
+ }, 7 * 1000); // 7 seconds
+ }
+
+ function showPermanentMessage(message) {
+ $('#messages').html(message);
+ }
+
+ // for debugging, console.log(objectToString(map));
+ function objectToString (obj) {
+ var str = '';
+ for (var p in obj) {
+ if (obj.hasOwnProperty(p)) {
+ str += p + ': ' + obj[p] + '\n';
+ }
+ }
+ return str;
+ }
+
+ function setTheme() {
+ //var bodyBackgroundColor = $('body').css('backgroundColor');
+ //$('.container').css('background-color', bodyBackgroundColor);
+ //$('body').css('background-color', '#ccc');
+ // $('head').append(' ');
+ }
+});
+
diff --git a/servers/php/mysql/gpstracker-03-14-14.sql b/servers/php/mysql/gpstracker-03-14-14.sql
deleted file mode 100644
index d7fb926..0000000
--- a/servers/php/mysql/gpstracker-03-14-14.sql
+++ /dev/null
@@ -1,238 +0,0 @@
--- MySQL dump 10.13 Distrib 5.5.35, for debian-linux-gnu (i686)
---
--- Host: localhost Database: gpstracker
--- ------------------------------------------------------
--- Server version 5.5.35-0ubuntu0.12.04.2
-
-/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
-/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
-/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
-/*!40101 SET NAMES utf8 */;
-/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */;
-/*!40103 SET TIME_ZONE='+00:00' */;
-/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;
-/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
-/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
-/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
-
---
--- Table structure for table `gpslocations`
---
-
-DROP TABLE IF EXISTS `gpslocations`;
-/*!40101 SET @saved_cs_client = @@character_set_client */;
-/*!40101 SET character_set_client = utf8 */;
-CREATE TABLE `gpslocations` (
- `GPSLocationID` int(10) unsigned NOT NULL AUTO_INCREMENT,
- `LastUpdate` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
- `Latitude` decimal(10,6) NOT NULL DEFAULT '0.000000',
- `Longitude` decimal(10,6) NOT NULL DEFAULT '0.000000',
- `phoneNumber` varchar(50) NOT NULL DEFAULT '',
- `sessionID` varchar(50) NOT NULL DEFAULT '',
- `speed` int(10) unsigned NOT NULL DEFAULT '0',
- `direction` int(10) unsigned NOT NULL DEFAULT '0',
- `distance` decimal(10,1) NOT NULL DEFAULT '0.0',
- `gpsTime` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
- `LocationMethod` varchar(50) NOT NULL DEFAULT '',
- `accuracy` int(10) unsigned NOT NULL DEFAULT '0',
- `extraInfo` varchar(255) NOT NULL DEFAULT '',
- `eventType` varchar(50) NOT NULL DEFAULT '',
- PRIMARY KEY (`GPSLocationID`),
- KEY `sessionIDIndex` (`sessionID`),
- KEY `phoneNumberIndex` (`phoneNumber`)
-) ENGINE=InnoDB AUTO_INCREMENT=1186 DEFAULT CHARSET=utf8;
-/*!40101 SET character_set_client = @saved_cs_client */;
-
---
--- Dumping data for table `gpslocations`
---
-
-LOCK TABLES `gpslocations` WRITE;
-/*!40000 ALTER TABLE `gpslocations` DISABLE KEYS */;
-INSERT INTO `gpslocations` VALUES (45,'2014-03-03 13:22:10',47.475931,-122.021119,'iosUser','8BA21D90-3F90-407F-BAAE-800B04B1F5EC',0,0,0.0,'2014-03-03 13:22:08','n/a',65,'altitude: 120m','ios'),(114,'2014-03-06 20:21:22',37.785834,-122.406417,'iosUser','9E260B81-57D5-4C36-AED0-34C5F7676A45',0,0,0.0,'2014-03-07 17:21:14','n/a',5,'altitude: 0m','ios'),(121,'2014-03-07 09:16:19',-41.322089,174.771919,'iosUser','09287DC1-D8B2-43F4-885A-9EB8DB0A7CE9',0,0,0.0,'2014-03-08 06:16:19','n/a',65,'altitude: 31m','ios'),(122,'2014-03-07 09:17:59',-41.322139,174.771739,'iosUser','25DE2DB6-FFCE-462F-A22D-25994B0D901A',0,0,0.0,'2014-03-08 06:18:01','n/a',50,'altitude: 29m','ios'),(135,'2014-03-07 21:54:22',37.818813,22.661872,'androidUser','94e63b1f-aa6f-439a-83d8-b57fabb28355',0,0,0.0,'2014-03-08 07:54:19','fused',2276,'0.0','android'),(136,'2014-03-07 21:54:34',37.818813,22.661872,'androidUser','712db5d9-d495-44d4-b2e7-3bd177ebf169',0,0,0.0,'2014-03-08 07:54:35','fused',2276,'0.0','android'),(179,'2014-03-08 11:24:15',37.818813,22.661872,'User-4427','5e1d94a0-c3fb-47b8-9846-4eedb7f12375',0,0,0.0,'2014-03-08 21:24:15','fused',2276,'0.0','android'),(192,'2014-03-08 12:24:12',51.922687,4.500411,'X-androidUser','2b9a3194-491b-448f-bcef-89cc8299d68b',0,0,0.0,'2014-03-08 21:24:08','fused',25,'0.0','android'),(200,'2014-03-08 12:47:38',51.922655,4.500412,'ELAWONEN','722ab5cc-9cd1-439b-abfa-5f82a7e119a1',0,0,0.0,'2014-03-08 21:47:33','fused',26,'0.0','android'),(212,'2014-03-09 15:44:55',12.968171,77.533864,'androidUser','98464f77-a247-4048-a9b2-3d1e4191cb9c',0,0,0.0,'2014-03-10 04:14:50','fused',25,'0.0','android'),(213,'2014-03-09 15:45:05',12.968171,77.533864,'androidUser','98464f77-a247-4048-a9b2-3d1e4191cb9c',0,0,0.0,'2014-03-10 04:15:02','fused',25,'0.0','android'),(214,'2014-03-09 15:45:17',12.968171,77.533864,'androidUser','98464f77-a247-4048-a9b2-3d1e4191cb9c',0,0,0.0,'2014-03-10 04:15:14','fused',25,'0.0','android'),(215,'2014-03-09 15:45:23',12.968171,77.533864,'androidUser','98464f77-a247-4048-a9b2-3d1e4191cb9c',0,0,0.0,'2014-03-10 04:15:20','fused',25,'0.0','android'),(216,'2014-03-09 15:45:26',12.968104,77.533922,'androidUser','5fe474b4-8c3f-44c9-bf34-ebd517c340ef',0,0,0.0,'2014-03-10 04:15:23','fused',14,'0.0','android'),(217,'2014-03-09 15:45:32',12.968100,77.533911,'androidUser','5fe474b4-8c3f-44c9-bf34-ebd517c340ef',0,0,0.0,'2014-03-10 04:15:29','fused',12,'0.0','android'),(218,'2014-03-09 15:45:44',12.968134,77.533898,'androidUser','5fe474b4-8c3f-44c9-bf34-ebd517c340ef',0,0,0.0,'2014-03-10 04:15:41','fused',10,'0.0','android'),(1131,'2014-03-10 20:36:48',12.967535,77.532635,'andy','677332d9-e137-4767-b3fa-bf5c121509f7',0,0,0.0,'2014-03-11 09:06:43','fused',834,'0.0','android'),(1144,'2014-03-11 18:38:29',12.890422,77.578681,'androidUser','68c85b1c-18e2-49de-9286-0afc4396af2c',0,0,0.0,'2014-03-12 07:08:27','fused',20,'0.0','android'),(1145,'2014-03-11 18:42:04',12.890421,77.578679,'androidUser','31d5e433-fec7-40e6-85ea-9445551079dd',0,0,0.0,'2014-03-12 07:12:02','fused',29,'0.0','android'),(1156,'2014-03-12 16:52:43',12.890422,77.578682,'androidUser','46693e80-7411-4f06-aec4-1edc76585c08',0,0,0.0,'2014-03-13 05:22:41','fused',27,'0.0','android'),(1159,'2014-03-12 17:58:07',12.890422,77.578679,'GalaxyUser989','01606372-4610-41c2-b5c0-d4c7ce638738',0,0,0.0,'2014-03-13 06:28:05','fused',28,'0.0','android'),(1167,'2014-03-13 07:14:42',17.398090,78.571595,'androidUser','09c8fe0a-be70-4dad-9e6a-feb4148aade3',0,0,0.0,'2014-03-13 19:44:38','fused',1073,'0.0','android'),(1168,'2014-03-13 07:27:57',17.398090,78.571595,'androidUser','016c6409-0d7f-46f3-b1aa-4bc16092c43b',0,0,0.0,'2014-03-13 19:57:54','fused',1073,'0.0','android'),(1173,'2014-03-13 08:01:54',17.398090,78.571595,'androidUser','3bbeecb7-c869-48b0-a161-8475c5bc18cf',0,0,0.0,'2014-03-13 20:31:50','fused',1073,'0.0','android'),(1174,'2014-03-13 08:02:54',17.398090,78.571595,'androidUser','3bbeecb7-c869-48b0-a161-8475c5bc18cf',0,0,0.0,'2014-03-13 20:32:50','fused',1073,'0.0','android'),(1175,'2014-03-13 08:04:30',17.398090,78.571595,'androidUser','8ac5faa3-ba48-4e93-a6d0-e8a8b63514d6',0,0,0.0,'2014-03-13 20:34:26','fused',1073,'0.0','android');
-/*!40000 ALTER TABLE `gpslocations` ENABLE KEYS */;
-UNLOCK TABLES;
-
---
--- Dumping routines for database 'gpstracker'
---
-/*!50003 DROP PROCEDURE IF EXISTS `prcDeleteRoute` */;
-/*!50003 SET @saved_cs_client = @@character_set_client */ ;
-/*!50003 SET @saved_cs_results = @@character_set_results */ ;
-/*!50003 SET @saved_col_connection = @@collation_connection */ ;
-/*!50003 SET character_set_client = utf8 */ ;
-/*!50003 SET character_set_results = utf8 */ ;
-/*!50003 SET collation_connection = utf8_general_ci */ ;
-/*!50003 SET @saved_sql_mode = @@sql_mode */ ;
-/*!50003 SET sql_mode = '' */ ;
-DELIMITER ;;
-CREATE DEFINER=`root`@`localhost` PROCEDURE `prcDeleteRoute`(
-_sessionID VARCHAR(50),
-_phoneNumber VARCHAR(50))
-BEGIN
- DELETE FROM gpslocations
- WHERE sessionID = _sessionID
- AND phoneNumber = _phoneNumber
- ORDER BY lastupdate;
-END ;;
-DELIMITER ;
-/*!50003 SET sql_mode = @saved_sql_mode */ ;
-/*!50003 SET character_set_client = @saved_cs_client */ ;
-/*!50003 SET character_set_results = @saved_cs_results */ ;
-/*!50003 SET collation_connection = @saved_col_connection */ ;
-/*!50003 DROP PROCEDURE IF EXISTS `prcGetAllGpsLocations` */;
-/*!50003 SET @saved_cs_client = @@character_set_client */ ;
-/*!50003 SET @saved_cs_results = @@character_set_results */ ;
-/*!50003 SET @saved_col_connection = @@collation_connection */ ;
-/*!50003 SET character_set_client = utf8 */ ;
-/*!50003 SET character_set_results = utf8 */ ;
-/*!50003 SET collation_connection = utf8_general_ci */ ;
-/*!50003 SET @saved_sql_mode = @@sql_mode */ ;
-/*!50003 SET sql_mode = '' */ ;
-DELIMITER ;;
-CREATE DEFINER=`gpstracker_user`@`localhost` PROCEDURE `prcGetAllGpsLocations`()
-BEGIN
-
-SELECT * FROM gpslocations;
-
-END ;;
-DELIMITER ;
-/*!50003 SET sql_mode = @saved_sql_mode */ ;
-/*!50003 SET character_set_client = @saved_cs_client */ ;
-/*!50003 SET character_set_results = @saved_cs_results */ ;
-/*!50003 SET collation_connection = @saved_col_connection */ ;
-/*!50003 DROP PROCEDURE IF EXISTS `prcGetRouteForMap` */;
-/*!50003 SET @saved_cs_client = @@character_set_client */ ;
-/*!50003 SET @saved_cs_results = @@character_set_results */ ;
-/*!50003 SET @saved_col_connection = @@collation_connection */ ;
-/*!50003 SET character_set_client = utf8 */ ;
-/*!50003 SET character_set_results = utf8 */ ;
-/*!50003 SET collation_connection = utf8_general_ci */ ;
-/*!50003 SET @saved_sql_mode = @@sql_mode */ ;
-/*!50003 SET sql_mode = '' */ ;
-DELIMITER ;;
-CREATE DEFINER=`root`@`localhost` PROCEDURE `prcGetRouteForMap`(
-_sessionID VARCHAR(50),
-_phoneNumber VARCHAR(50))
-BEGIN
- SELECT
- CONCAT('{ "latitude":"', CAST(latitude AS CHAR),'", "longitude":"', CAST(longitude AS CHAR), '", "speed":"', CAST(speed AS CHAR), '", "direction":"', CAST(direction AS CHAR), '", "distance":"', CAST(distance AS CHAR), '", "locationMethod":"', locationMethod, '", "gpsTime":"', DATE_FORMAT(gpsTime, '%b %e %Y %h:%i%p'), '", "phoneNumber":"', phoneNumber, '", "sessionID":"', CAST(sessionID AS CHAR), '", "accuracy":"', CAST(accuracy AS CHAR), '", "extraInfo":"', extraInfo, '" }') json
- FROM gpslocations
- WHERE sessionID = _sessionID
- AND phoneNumber = _phoneNumber
- ORDER BY lastupdate;
-END ;;
-DELIMITER ;
-/*!50003 SET sql_mode = @saved_sql_mode */ ;
-/*!50003 SET character_set_client = @saved_cs_client */ ;
-/*!50003 SET character_set_results = @saved_cs_results */ ;
-/*!50003 SET collation_connection = @saved_col_connection */ ;
-/*!50003 DROP PROCEDURE IF EXISTS `prcGetRoutes` */;
-/*!50003 SET @saved_cs_client = @@character_set_client */ ;
-/*!50003 SET @saved_cs_results = @@character_set_results */ ;
-/*!50003 SET @saved_col_connection = @@collation_connection */ ;
-/*!50003 SET character_set_client = utf8 */ ;
-/*!50003 SET character_set_results = utf8 */ ;
-/*!50003 SET collation_connection = utf8_general_ci */ ;
-/*!50003 SET @saved_sql_mode = @@sql_mode */ ;
-/*!50003 SET sql_mode = '' */ ;
-DELIMITER ;;
-CREATE DEFINER=`root`@`localhost` PROCEDURE `prcGetRoutes`()
-BEGIN
- CREATE TEMPORARY TABLE tempRoutes (
- sessionID VARCHAR(50),
- phoneNumber VARCHAR(50),
- startTime DATETIME,
- endTime DATETIME)
- ENGINE = MEMORY;
-
- INSERT INTO tempRoutes (sessionID, phoneNumber)
- SELECT DISTINCT sessionID, phoneNumber
- FROM gpslocations;
-
- UPDATE tempRoutes tr
- SET startTime = (SELECT MIN(gpsTime) FROM gpslocations gl
- WHERE gl.sessionID = tr.sessionID
- AND gl.phoneNumber = tr.phoneNumber);
-
- UPDATE tempRoutes tr
- SET endTime = (SELECT MAX(gpsTime) FROM gpslocations gl
- WHERE gl.sessionID = tr.sessionID
- AND gl.phoneNumber = tr.phoneNumber);
-
- SELECT
-
- CONCAT('{ "sessionID": "', CAST(sessionID AS CHAR), '", "phoneNumber": "', phoneNumber, '", "times": "(', DATE_FORMAT(startTime, '%b %e %Y %h:%i%p'), ' - ', DATE_FORMAT(endTime, '%b %e %Y %h:%i%p'), ')" }') json
- FROM tempRoutes
- ORDER BY startTime DESC;
-
- DROP TABLE tempRoutes;
-END ;;
-DELIMITER ;
-/*!50003 SET sql_mode = @saved_sql_mode */ ;
-/*!50003 SET character_set_client = @saved_cs_client */ ;
-/*!50003 SET character_set_results = @saved_cs_results */ ;
-/*!50003 SET collation_connection = @saved_col_connection */ ;
-/*!50003 DROP PROCEDURE IF EXISTS `prcGetUUID` */;
-/*!50003 SET @saved_cs_client = @@character_set_client */ ;
-/*!50003 SET @saved_cs_results = @@character_set_results */ ;
-/*!50003 SET @saved_col_connection = @@collation_connection */ ;
-/*!50003 SET character_set_client = utf8 */ ;
-/*!50003 SET character_set_results = utf8 */ ;
-/*!50003 SET collation_connection = utf8_general_ci */ ;
-/*!50003 SET @saved_sql_mode = @@sql_mode */ ;
-/*!50003 SET sql_mode = '' */ ;
-DELIMITER ;;
-CREATE DEFINER=`gpstracker_user`@`localhost` PROCEDURE `prcGetUUID`()
-BEGIN
- SELECT UUID();
-END ;;
-DELIMITER ;
-/*!50003 SET sql_mode = @saved_sql_mode */ ;
-/*!50003 SET character_set_client = @saved_cs_client */ ;
-/*!50003 SET character_set_results = @saved_cs_results */ ;
-/*!50003 SET collation_connection = @saved_col_connection */ ;
-/*!50003 DROP PROCEDURE IF EXISTS `prcSaveGPSLocation` */;
-/*!50003 SET @saved_cs_client = @@character_set_client */ ;
-/*!50003 SET @saved_cs_results = @@character_set_results */ ;
-/*!50003 SET @saved_col_connection = @@collation_connection */ ;
-/*!50003 SET character_set_client = utf8 */ ;
-/*!50003 SET character_set_results = utf8 */ ;
-/*!50003 SET collation_connection = utf8_general_ci */ ;
-/*!50003 SET @saved_sql_mode = @@sql_mode */ ;
-/*!50003 SET sql_mode = '' */ ;
-DELIMITER ;;
-CREATE DEFINER=`gpstracker_user`@`localhost` PROCEDURE `prcSaveGPSLocation`(
-_lat VARCHAR(45),
-_lng VARCHAR(45),
-_mph VARCHAR(45),
-_direction VARCHAR(45),
-_distance VARCHAR(45),
-_date VARCHAR(100),
-_locationMethod VARCHAR(100),
-_phoneNumber VARCHAR(20),
-_sessionID VARCHAR(50),
-_accuracy VARCHAR(20),
-_extraInfo VARCHAR(255),
-_eventType VARCHAR(50)
-)
-BEGIN
- INSERT INTO gpslocations (latitude, longitude, speed, direction, distance, gpsTime, locationMethod, phoneNumber, sessionID, accuracy, extraInfo, eventType)
- VALUES (_lat, _lng, _mph, _direction, _distance, _date, _locationMethod, _phoneNumber, _sessionID, _accuracy, _extraInfo, _eventType);
-END ;;
-DELIMITER ;
-/*!50003 SET sql_mode = @saved_sql_mode */ ;
-/*!50003 SET character_set_client = @saved_cs_client */ ;
-/*!50003 SET character_set_results = @saved_cs_results */ ;
-/*!50003 SET collation_connection = @saved_col_connection */ ;
-/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;
-
-/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
-/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;
-/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */;
-/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
-/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
-/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
-/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;
-
--- Dump completed on 2014-03-13 11:42:37
diff --git a/servers/php/mysql/gpstracker-09-12-14.sql b/servers/php/mysql/gpstracker-09-12-14.sql
new file mode 100644
index 0000000..b9ddec2
--- /dev/null
+++ b/servers/php/mysql/gpstracker-09-12-14.sql
@@ -0,0 +1,220 @@
+-- MySQL dump 10.13 Distrib 5.5.38, for debian-linux-gnu (i686)
+--
+-- Host: localhost Database: gpstracker
+-- ------------------------------------------------------
+-- Server version 5.5.38-0ubuntu0.12.04.1
+
+/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
+/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
+/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
+/*!40101 SET NAMES utf8 */;
+/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */;
+/*!40103 SET TIME_ZONE='+00:00' */;
+/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;
+/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
+/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
+/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
+
+--
+-- Table structure for table `gpslocations`
+--
+
+DROP TABLE IF EXISTS `gpslocations`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `gpslocations` (
+ `GPSLocationID` int(10) unsigned NOT NULL AUTO_INCREMENT,
+ `lastUpdate` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ `latitude` decimal(10,6) NOT NULL DEFAULT '0.000000',
+ `longitude` decimal(10,6) NOT NULL DEFAULT '0.000000',
+ `phoneNumber` varchar(50) NOT NULL DEFAULT '',
+ `userName` varchar(50) NOT NULL DEFAULT '',
+ `sessionID` varchar(50) NOT NULL DEFAULT '',
+ `speed` int(10) unsigned NOT NULL DEFAULT '0',
+ `direction` int(10) unsigned NOT NULL DEFAULT '0',
+ `distance` decimal(10,1) NOT NULL DEFAULT '0.0',
+ `gpsTime` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
+ `locationMethod` varchar(50) NOT NULL DEFAULT '',
+ `accuracy` int(10) unsigned NOT NULL DEFAULT '0',
+ `extraInfo` varchar(255) NOT NULL DEFAULT '',
+ `eventType` varchar(50) NOT NULL DEFAULT '',
+ PRIMARY KEY (`GPSLocationID`),
+ KEY `sessionIDIndex` (`sessionID`),
+ KEY `phoneNumberIndex` (`phoneNumber`),
+ KEY `userNameIndex` (`userName`)
+) ENGINE=InnoDB AUTO_INCREMENT=10 DEFAULT CHARSET=utf8;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Dumping data for table `gpslocations`
+--
+
+LOCK TABLES `gpslocations` WRITE;
+/*!40000 ALTER TABLE `gpslocations` DISABLE KEYS */;
+INSERT INTO `gpslocations` VALUES (1,'2007-01-03 19:37:00',47.627327,-122.325691,'gpsTracker3','gpsTracker3','8BA21D90-3F90-407F-BAAE-800B04B1F5EB',0,0,0.0,'2007-01-03 19:37:00','na',137,'na','gpsTracker'),(2,'2007-01-03 19:38:00',47.607258,-122.330077,'gpsTracker3','gpsTracker3','8BA21D90-3F90-407F-BAAE-800B04B1F5EB',0,0,0.0,'2007-01-03 19:38:00','na',137,'na','gpsTracker'),(3,'2007-01-03 19:39:00',47.601703,-122.324670,'gpsTracker3','gpsTracker3','8BA21D90-3F90-407F-BAAE-800B04B1F5EB',0,0,0.0,'2007-01-03 19:39:00','na',137,'na','gpsTracker'),(4,'0000-00-00 00:00:00',47.593757,-122.195074,'gpsTracker2','gpsTracker2','8BA21D90-3F90-407F-BAAE-800B04B1F5EC',0,0,0.0,'2007-01-03 19:40:00','na',137,'na','gpsTracker'),(5,'2007-01-03 19:41:00',47.601397,-122.190353,'gpsTracker2','gpsTracker2','8BA21D90-3F90-407F-BAAE-800B04B1F5EC',0,0,0.0,'2007-01-03 19:41:00','na',137,'na','gpsTracker'),(6,'2007-01-03 19:42:00',47.610020,-122.190697,'gpsTracker2','gpsTracker2','8BA21D90-3F90-407F-BAAE-800B04B1F5EC',0,0,0.0,'2007-01-03 19:42:00','na',137,'na','gpsTracker'),(7,'2007-01-03 19:43:00',47.636631,-122.214558,'gpsTracker1','gpsTracker1','8BA21D90-3F90-407F-BAAE-800B04B1F5ED',0,0,0.0,'2007-01-03 19:43:00','na',137,'na','gpsTracker'),(8,'2007-01-03 19:44:00',47.637961,-122.201769,'gpsTracker1','gpsTracker1','8BA21D90-3F90-407F-BAAE-800B04B1F5ED',0,0,0.0,'2007-01-03 19:44:00','na',137,'na','gpsTracker'),(9,'2007-01-03 19:45:00',47.642935,-122.209579,'gpsTracker1','gpsTracker1','8BA21D90-3F90-407F-BAAE-800B04B1F5ED',0,0,0.0,'2007-01-03 19:45:00','na',137,'na','gpsTracker');
+/*!40000 ALTER TABLE `gpslocations` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Dumping routines for database 'gpstracker'
+--
+/*!50003 DROP PROCEDURE IF EXISTS `prcDeleteRoute` */;
+/*!50003 SET @saved_cs_client = @@character_set_client */ ;
+/*!50003 SET @saved_cs_results = @@character_set_results */ ;
+/*!50003 SET @saved_col_connection = @@collation_connection */ ;
+/*!50003 SET character_set_client = utf8 */ ;
+/*!50003 SET character_set_results = utf8 */ ;
+/*!50003 SET collation_connection = utf8_general_ci */ ;
+/*!50003 SET @saved_sql_mode = @@sql_mode */ ;
+/*!50003 SET sql_mode = '' */ ;
+DELIMITER ;;
+CREATE DEFINER=`root`@`localhost` PROCEDURE `prcDeleteRoute`(
+_sessionID VARCHAR(50))
+BEGIN
+ DELETE FROM gpslocations
+ WHERE sessionID = _sessionID;
+END ;;
+DELIMITER ;
+/*!50003 SET sql_mode = @saved_sql_mode */ ;
+/*!50003 SET character_set_client = @saved_cs_client */ ;
+/*!50003 SET character_set_results = @saved_cs_results */ ;
+/*!50003 SET collation_connection = @saved_col_connection */ ;
+/*!50003 DROP PROCEDURE IF EXISTS `prcGetAllRoutesForMap` */;
+/*!50003 SET @saved_cs_client = @@character_set_client */ ;
+/*!50003 SET @saved_cs_results = @@character_set_results */ ;
+/*!50003 SET @saved_col_connection = @@collation_connection */ ;
+/*!50003 SET character_set_client = utf8 */ ;
+/*!50003 SET character_set_results = utf8 */ ;
+/*!50003 SET collation_connection = utf8_general_ci */ ;
+/*!50003 SET @saved_sql_mode = @@sql_mode */ ;
+/*!50003 SET sql_mode = '' */ ;
+DELIMITER ;;
+CREATE DEFINER=`root`@`localhost` PROCEDURE `prcGetAllRoutesForMap`()
+BEGIN
+ SELECT DISTINCT(sessionId), MAX(gpsTime) gpsTime,
+ CONCAT('{ "latitude":"', CAST(latitude AS CHAR),'", "longitude":"', CAST(longitude AS CHAR), '", "speed":"', CAST(speed AS CHAR), '", "direction":"', CAST(direction AS CHAR), '", "distance":"', CAST(distance AS CHAR), '", "locationMethod":"', locationMethod, '", "gpsTime":"', DATE_FORMAT(gpsTime, '%b %e %Y %h:%i%p'), '", "phoneNumber":"', phoneNumber, '", "sessionID":"', CAST(sessionID AS CHAR), '", "accuracy":"', CAST(accuracy AS CHAR), '", "extraInfo":"', extraInfo, '" }') json
+ FROM gpslocations
+ WHERE sessionID != '0' && CHAR_LENGTH(sessionID) != 0 && gpstime != '0000-00-00 00:00:00'
+ GROUP BY sessionID;
+END ;;
+DELIMITER ;
+/*!50003 SET sql_mode = @saved_sql_mode */ ;
+/*!50003 SET character_set_client = @saved_cs_client */ ;
+/*!50003 SET character_set_results = @saved_cs_results */ ;
+/*!50003 SET collation_connection = @saved_col_connection */ ;
+/*!50003 DROP PROCEDURE IF EXISTS `prcGetRouteForMap` */;
+/*!50003 SET @saved_cs_client = @@character_set_client */ ;
+/*!50003 SET @saved_cs_results = @@character_set_results */ ;
+/*!50003 SET @saved_col_connection = @@collation_connection */ ;
+/*!50003 SET character_set_client = utf8 */ ;
+/*!50003 SET character_set_results = utf8 */ ;
+/*!50003 SET collation_connection = utf8_general_ci */ ;
+/*!50003 SET @saved_sql_mode = @@sql_mode */ ;
+/*!50003 SET sql_mode = '' */ ;
+DELIMITER ;;
+CREATE DEFINER=`root`@`localhost` PROCEDURE `prcGetRouteForMap`(
+_sessionID VARCHAR(50))
+BEGIN
+ SELECT
+ CONCAT('{ "latitude":"', CAST(latitude AS CHAR),'", "longitude":"', CAST(longitude AS CHAR), '", "speed":"', CAST(speed AS CHAR), '", "direction":"', CAST(direction AS CHAR), '", "distance":"', CAST(distance AS CHAR), '", "locationMethod":"', locationMethod, '", "gpsTime":"', DATE_FORMAT(gpsTime, '%b %e %Y %h:%i%p'), '", "phoneNumber":"', phoneNumber, '", "sessionID":"', CAST(sessionID AS CHAR), '", "accuracy":"', CAST(accuracy AS CHAR), '", "extraInfo":"', extraInfo, '" }') json
+ FROM gpslocations
+ WHERE sessionID = _sessionID
+ ORDER BY lastupdate;
+END ;;
+DELIMITER ;
+/*!50003 SET sql_mode = @saved_sql_mode */ ;
+/*!50003 SET character_set_client = @saved_cs_client */ ;
+/*!50003 SET character_set_results = @saved_cs_results */ ;
+/*!50003 SET collation_connection = @saved_col_connection */ ;
+/*!50003 DROP PROCEDURE IF EXISTS `prcGetRoutes` */;
+/*!50003 SET @saved_cs_client = @@character_set_client */ ;
+/*!50003 SET @saved_cs_results = @@character_set_results */ ;
+/*!50003 SET @saved_col_connection = @@collation_connection */ ;
+/*!50003 SET character_set_client = utf8 */ ;
+/*!50003 SET character_set_results = utf8 */ ;
+/*!50003 SET collation_connection = utf8_general_ci */ ;
+/*!50003 SET @saved_sql_mode = @@sql_mode */ ;
+/*!50003 SET sql_mode = '' */ ;
+DELIMITER ;;
+CREATE DEFINER=`gpstracker_user`@`localhost` PROCEDURE `prcGetRoutes`()
+BEGIN
+ CREATE TEMPORARY TABLE tempRoutes (
+ sessionID VARCHAR(50),
+ phoneNumber VARCHAR(50),
+ startTime DATETIME,
+ endTime DATETIME)
+ ENGINE = MEMORY;
+
+ INSERT INTO tempRoutes (sessionID, phoneNumber)
+ SELECT DISTINCT sessionID, phoneNumber
+ FROM gpslocations;
+
+ UPDATE tempRoutes tr
+ SET startTime = (SELECT MIN(gpsTime) FROM gpslocations gl
+ WHERE gl.sessionID = tr.sessionID
+ AND gl.phoneNumber = tr.phoneNumber);
+
+ UPDATE tempRoutes tr
+ SET endTime = (SELECT MAX(gpsTime) FROM gpslocations gl
+ WHERE gl.sessionID = tr.sessionID
+ AND gl.phoneNumber = tr.phoneNumber);
+
+ SELECT
+
+ CONCAT('{ "sessionID": "', CAST(sessionID AS CHAR), '", "phoneNumber": "', phoneNumber, '", "times": "(', DATE_FORMAT(startTime, '%b %e %Y %h:%i%p'), ' - ', DATE_FORMAT(endTime, '%b %e %Y %h:%i%p'), ')" }') json
+ FROM tempRoutes
+ ORDER BY startTime DESC;
+
+ DROP TABLE tempRoutes;
+END ;;
+DELIMITER ;
+/*!50003 SET sql_mode = @saved_sql_mode */ ;
+/*!50003 SET character_set_client = @saved_cs_client */ ;
+/*!50003 SET character_set_results = @saved_cs_results */ ;
+/*!50003 SET collation_connection = @saved_col_connection */ ;
+/*!50003 DROP PROCEDURE IF EXISTS `prcSaveGPSLocation` */;
+/*!50003 SET @saved_cs_client = @@character_set_client */ ;
+/*!50003 SET @saved_cs_results = @@character_set_results */ ;
+/*!50003 SET @saved_col_connection = @@collation_connection */ ;
+/*!50003 SET character_set_client = utf8 */ ;
+/*!50003 SET character_set_results = utf8 */ ;
+/*!50003 SET collation_connection = utf8_general_ci */ ;
+/*!50003 SET @saved_sql_mode = @@sql_mode */ ;
+/*!50003 SET sql_mode = '' */ ;
+DELIMITER ;;
+CREATE DEFINER=`gpstracker_user`@`localhost` PROCEDURE `prcSaveGPSLocation`(
+_lat VARCHAR(45),
+_lng VARCHAR(45),
+_mph VARCHAR(45),
+_direction VARCHAR(45),
+_distance VARCHAR(45),
+_date VARCHAR(100),
+_locationMethod VARCHAR(100),
+_phoneNumber VARCHAR(20),
+_sessionID VARCHAR(50),
+_accuracy VARCHAR(20),
+_extraInfo VARCHAR(255),
+_eventType VARCHAR(50)
+)
+BEGIN
+ INSERT INTO gpslocations (latitude, longitude, speed, direction, distance, gpsTime, locationMethod, phoneNumber, sessionID, accuracy, extraInfo, eventType)
+ VALUES (_lat, _lng, _mph, _direction, _distance, _date, _locationMethod, _phoneNumber, _sessionID, _accuracy, _extraInfo, _eventType);
+
+ SELECT NOW();
+END ;;
+DELIMITER ;
+/*!50003 SET sql_mode = @saved_sql_mode */ ;
+/*!50003 SET character_set_client = @saved_cs_client */ ;
+/*!50003 SET character_set_results = @saved_cs_results */ ;
+/*!50003 SET collation_connection = @saved_col_connection */ ;
+/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;
+
+/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
+/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;
+/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */;
+/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
+/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
+/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
+/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;
+
+-- Dump completed on 2014-09-12 18:15:37
diff --git a/servers/php/styles/styles.css b/servers/php/styles/styles.css
deleted file mode 100644
index 9c6a681..0000000
--- a/servers/php/styles/styles.css
+++ /dev/null
@@ -1,62 +0,0 @@
-
-* {
- margin: 0;
- padding: 0;
-}
-body {
- font-family: arial, helvetica, sans-serif;
- font-size: 17px;
-}
-#test {
- position:absolute;
- left: 750px;
- top: 50px;
- width:400px;
-}
-#messages {
- position:absolute;
- left: 10px;
- top: 10px;
- width:950px;
-}
-#map {
- position:absolute;
- left: 10px;
- top: 40px;
- width:700px;
- height:450px;
- border:1px;
- border-style:solid;
- background-color:#FFF;
-}
-#selectRoute {
- position:absolute;
- left: 10px;
- top: 510px;
- width: 600px;
-}
-#selectRefresh {
- position:absolute;
- left: 10px;
- top: 550px;
- width: 250px;
-}
-#selectZoomLevel {
- position:absolute;
- left: 360px;
- top: 550px;
- width: 250px;
-}
-#delete {
- position:absolute;
- left: 637px;
- top: 510px;
- width: 75px;
-}
-#refresh {
- position:absolute;
- left: 637px;
- top: 550px;
- width: 75px;
-}
-
diff --git a/servers/php/updatelocation.php b/servers/php/updatelocation.php
index 6ba502e..94ae203 100644
--- a/servers/php/updatelocation.php
+++ b/servers/php/updatelocation.php
@@ -1,48 +1,51 @@
$latitude,
+ ':longitude' => $longitude,
+ ':speed' => $speed,
+ ':direction' => $direction,
+ ':distance' => $distance,
+ ':date' => $date,
+ ':locationmethod' => $locationmethod,
+ ':phonenumber' => $phonenumber,
+ ':sessionid' => $sessionid,
+ ':accuracy' => $accuracy,
+ ':extrainfo' => $extrainfo,
+ ':eventtype' => $eventtype
+ );
- // save the gps location to the database
- // i'm not to worried about sql injection here since i'm calling a stored procedure here
- $query = 'CALL prcSaveGPSLocation(\''
- . $latitude . '\',\''
- . $longitude . '\',\''
- . $speed . '\',\''
- . $direction . '\',\''
- . $distance . '\',\''
- . $date . '\',\''
- . $locationMethod . '\',\''
- . $phoneNumber . '\',\''
- . $sessionID . '\',\''
- . $accuracy . '\',\''
- . $extraInfo . '\',\''
- . $eventType . '\')';
+ $stmt = $pdo->prepare('CALL prcSaveGPSLocation(
+ :latitude,
+ :longitude,
+ :speed,
+ :direction,
+ :distance,
+ :date,
+ :locationmethod,
+ :phonenumber,
+ :sessionid,
+ :accuracy,
+ :extrainfo,
+ :eventtype);'
+ );
- if (!$mysqli->multi_query($query)) {
- die('$mysqli->multi_query: ' . $mysqli->error);
- }
-
- $mysqli->close();
-
- echo '0';
-
+ $stmt->execute($params);
+ echo '0';
?>