Commit 899e9758d62c235671549131f0e803e5385ccc07

Authored by Renato De Donato
1 parent fa7d515e

geojson <3

controllets/data-table-controllet/data-table-controllet.html
... ... @@ -335,13 +335,14 @@
335 335  
336 336 this._resize();
337 337  
338   - this.$.tooltip_wornings.show();
  338 + this.async(function() {
  339 + this.$.tooltip_wornings.show();
  340 + }, 100);
339 341 },
340 342  
341 343 reset : function(){
342 344 this.setData([]);
343 345 $(this.$.tbody).animate({ scrollTop: 0}, 0);
344   -// this.filter = "";
345 346 },
346 347  
347 348 _onPrevClick : function(){
... ... @@ -478,7 +479,7 @@
478 479 var w = $(this.$.data_table_container).width();
479 480 $(this.$.tbody).width(w).height(h);
480 481 $(this.$.tbody).perfectScrollbar('update');
481   - }, 1);
  482 + }, 0);
482 483 }
483 484  
484 485 });
... ...
controllets/select-visualization-controllet/select-datalet-inputs.html
... ... @@ -276,7 +276,11 @@
276 276  
277 277 setInputs : function(inputs) {
278 278 var temp = this._copy(inputs);
279   - if (inputs.constructor != Array) {//table --> 1 input
  279 + if (inputs.constructor != Array && inputs.name == "GEOJSON") {
  280 + temp = [];
  281 + temp.push(inputs);
  282 + }
  283 + else if (inputs.constructor != Array) {//table --> 1 input
280 284 var name = inputs.name;
281 285 var description = inputs.description;
282 286 var selection = inputs.selection;
... ...
datalets/base-ajax-json-alasql-datalet/base-ajax-json-alasql-datalet.html
... ... @@ -34,7 +34,7 @@
34 34 <script type="text/javascript" src="../../alasql-utility/alasql.min.js"></script>
35 35 <script type="text/javascript" src="../../alasql-utility/alasql-utility.js"></script>
36 36  
37   -<script type="text/javascript" src="../../bower_components/JSDataChecker/jsdatachecker.min.js"></script>
  37 +<script type="text/javascript" src="../../bower_components/jsdatachecker/jsdatachecker.min.js"></script>
38 38  
39 39 <!--
40 40 The `base-ajax-json-jsonpath-datalet` extend the base component and define the mechanism to access and select data.
... ...
datalets/leafletjs-geojson-datalet/demo/index.html 0 → 100755
  1 +<!DOCTYPE html>
  2 +<html lang="en">
  3 +<head>
  4 + <meta charset="UTF-8">
  5 + <title></title>
  6 +
  7 + <script>
  8 + </script>
  9 +
  10 +</head>
  11 +<body>
  12 +
  13 +<script src="https://code.jquery.com/jquery-2.1.4.min.js" type="text/javascript"></script>
  14 +<link rel="import" href="../leafletjs-datalet.html" />
  15 +
  16 +<leafletjs-datalet data-url="http://ckan.routetopa.eu/api/action/datastore_search?resource_id=73e02092-85a1-434e-85fe-0c9a43aa9a52&amp;limit=10000"
  17 + fields='["result,records,Lat","result,records,Lng", "result,records,Link"]'></leafletjs-datalet>
  18 +
  19 +
  20 +</body>
  21 +</html>
0 22 \ No newline at end of file
... ...
datalets/leafletjs-geojson-datalet/docs.html 0 → 100755
  1 +<!DOCTYPE html>
  2 +<html lang="en">
  3 +<head>
  4 + <link rel="import" href="../../bower_components/iron-component-page/iron-component-page.html">
  5 + <meta charset="UTF-8">
  6 +</head>
  7 +<body>
  8 +
  9 +<iron-component-page src="leafletjs-datalet.html"></iron-component-page>
  10 +
  11 +</body>
  12 +</html>
0 13 \ No newline at end of file
... ...
datalets/leafletjs-geojson-datalet/leafletjs-geojson-datalet.html 0 → 100755
  1 +<!--
  2 +@license
  3 + The MIT License (MIT)
  4 +
  5 + Copyright (c) 2015 Dipartimento di Informatica - Università di Salerno - Italy
  6 +
  7 + Permission is hereby granted, free of charge, to any person obtaining a copy
  8 + of this software and associated documentation files (the "Software"), to deal
  9 + in the Software without restriction, including without limitation the rights
  10 + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  11 + copies of the Software, and to permit persons to whom the Software is
  12 + furnished to do so, subject to the following conditions:
  13 +
  14 + The above copyright notice and this permission notice shall be included in
  15 + all copies or substantial portions of the Software.
  16 +
  17 + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  18 + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  19 + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  20 + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  21 + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  22 + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  23 + THE SOFTWARE.
  24 +-->
  25 +
  26 +<!--
  27 +* Developed by :
  28 +* ROUTE-TO-PA Project - grant No 645860. - www.routetopa.eu
  29 +*
  30 +-->
  31 +
  32 +<link rel="import" href="../base-ajax-json-alasql-datalet/base-ajax-json-alasql-datalet.html">
  33 +
  34 +<!--
  35 +
  36 +`leafletjs-datalet` is a map datalet based on open source project leafletjs <http://leafletjs.com/>
  37 +
  38 +Example:
  39 +
  40 + <leafletjs-datalet
  41 + data-url="http://ckan.routetopa.eu/api/action/datastore_search?resource_id=#"
  42 + fields='["field1","field2"]'>
  43 + </leafletjs-datalet>
  44 +
  45 +@element leafletjs-datalet
  46 +@status v0.1
  47 +@demo demo/index.html
  48 +@group datalets
  49 +-->
  50 +
  51 +<dom-module name="leafletjs-geojson-datalet">
  52 + <template>
  53 + <link rel="stylesheet" href="leafletsjs/leaflet.css" />
  54 + <style>
  55 + #leafletjs {height: 600px;}
  56 + </style>
  57 +
  58 + <div id="leafletjs"></div>
  59 + <base-ajax-json-alasql-datalet data-url="{{dataUrl}}" fields="{{fields}}" data="{{data}}" title="{{title}}" description="{{description}}" export_menu="{{export_menu}}"></base-ajax-json-alasql-datalet>
  60 +
  61 + </template>
  62 +
  63 + <script src="leafletsjs/leaflet.js"></script>
  64 +
  65 + <script>
  66 +
  67 + var leafletjsBehavior = {
  68 +
  69 + /**
  70 + * Read markers coordinates from the data object and add the marker to the map.
  71 + * Call a method for the map redraw and set the viewport in order to fit all the markers in the viewport..
  72 + *
  73 + * @method transformData
  74 + */
  75 + presentData: function(){
  76 +
  77 + var t = this;
  78 + try{
  79 + this._component.map = L.map(this._component.$.leafletjs).setView([0, 0], 13, {reset:true});
  80 + }catch(e){}
  81 +
  82 +
  83 + L.tileLayer('http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
  84 + maxZoom: 18,
  85 + attribution: 'Map data © <a href="http://openstreetmap.org">OpenStreetMap</a> contributors'
  86 + }).addTo(this._component.map);
  87 +
  88 +
  89 + L.Icon.Default.imagePath = 'http://services.routetopa.eu/DEEalerProvider/COMPONENTS/datalets/leafletjs-datalet/leafletsjs/images';
  90 +
  91 + var coordinates = [];
  92 + var coordinates_index = 0;
  93 + var isArray = t.data[0].data[0].constructor === Array;
  94 +
  95 + for(var i=0; i<t.data[0].data.length; i++)
  96 + {
  97 + if(isArray)
  98 + {
  99 + if(!isNaN(t.data[0].data[i][0]) && !isNaN(t.data[0].data[i][1]))
  100 + coordinates.push([parseFloat(t.data[0].data[i][0]), parseFloat(t.data[0].data[i][1])]);
  101 + else
  102 + continue;
  103 + }
  104 + else
  105 + {
  106 + if(!isNaN(t.data[0].data[i]) && !isNaN(t.data[1].data[i]))
  107 + coordinates.push([parseFloat(t.data[0].data[i]), parseFloat(t.data[1].data[i])]);
  108 + else
  109 + continue;
  110 + }
  111 +
  112 + var marker = L.marker([coordinates[coordinates_index][0], coordinates[coordinates_index][1]]).addTo(t._component.map);
  113 + coordinates_index++;
  114 +
  115 + if(t.data.length > 2)
  116 + {
  117 + var popupText = "";
  118 + for(var j=2; j<t.data.length; j++)
  119 + {
  120 + if(typeof t.data[j] != 'undefined' && typeof t.data[j].data[i] != 'undefined')
  121 + {
  122 + if (t.data[j].data[i].toString().match(new RegExp("^(http[s]?:\\/\\/(www\\.)?|ftp:\\/\\/(www\\.)?|www\\.){1}([0-9A-Za-z-\\.@:%_\+~#=]+)+((\\.[a-zA-Z]{2,3})+)(/(.)*)?(\\?(.)*)?")))
  123 + popupText += '<image height="100" width="100" src="' + t.data[j].data[i] + '" /><br/>';
  124 + else
  125 + popupText += '<span>' + t.data[j].name + ' : ' + t.data[j].data[i] + '</span><br/>'
  126 + }
  127 + }
  128 +
  129 + var popup = L.popup().setContent(popupText);
  130 + marker.bindPopup(popup);
  131 + }
  132 + }
  133 +
  134 + t._component.map._onResize();
  135 + t._component.map.invalidateSize(false);
  136 + t._component.map.fitBounds(coordinates);
  137 +
  138 + }
  139 + };
  140 +
  141 +
  142 + Polymer({
  143 +
  144 + is : 'leafletjs-geojson-datalet',
  145 +
  146 + properties : {
  147 + /**
  148 + * Store a reference to the leafletjs map object created in 'ready' callback
  149 + * @attribute map
  150 + * @type Object
  151 + */
  152 + map :
  153 + {
  154 + type: Object,
  155 + value:null
  156 + },
  157 + /**
  158 + * An Array with all the markers extracted from the dataset
  159 + * @attribute markers
  160 + * @type Array
  161 + */
  162 + markers :
  163 + {
  164 + type : Array,
  165 + value : []
  166 + },
  167 + /**
  168 + * It's the component behavior
  169 + *
  170 + * @attribute behavior
  171 + * @type Object
  172 + * @default {}
  173 + */
  174 + behavior : {
  175 + type : Object,
  176 + value : {}
  177 + },
  178 + /**
  179 + * Control the export menu
  180 + * xxxx BITMASK. FROM RIGHT : HTML, PNG, RTF, MY SPACE (eg. 1111 show all, 0000 hide all)
  181 + *
  182 + * @attribute export_menu
  183 + * @type Number
  184 + * @default 15
  185 + */
  186 + export_menu : {
  187 + type : Number,
  188 + value : 9 // xxxx BITMASK. FROM RIGHT : HTML, PNG, RTF, MY SPACE (eg. 1111 show all, 0000 hide all)
  189 + }
  190 + },
  191 +
  192 + /**
  193 + * 'ready' callback create the map object, set the tileLayer to openstreetmap and the default icon image path.
  194 + * Moreover extend the leafletjsComponentBehavior with BaseDataletBehavior, WorkcycleBehavior and leafletjsBehavior
  195 + * and run the Datalet workcycle.
  196 + *
  197 + * @method ready
  198 + */
  199 + ready: function(){
  200 + this.behavior = $.extend(true, {}, BaseDataletBehavior, WorkcycleBehavior, AjaxJsonAlasqlBehavior, leafletjsBehavior);
  201 + this.async(function(){this.behavior.init(this)},100);
  202 + }
  203 + });
  204 + </script>
  205 +</dom-module>
  206 +
... ...
datalets/leafletjs-geojson-datalet/leafletjs-geojson-datalet.png 0 → 100644

5.32 KB

datalets/leafletjs-geojson-datalet/leafletsjs/images/layers-2x.png 0 → 100755

2.83 KB

datalets/leafletjs-geojson-datalet/leafletsjs/images/layers.png 0 → 100755

1.47 KB

datalets/leafletjs-geojson-datalet/leafletsjs/images/marker-icon-2x.png 0 → 100755

3.94 KB

datalets/leafletjs-geojson-datalet/leafletsjs/images/marker-icon.png 0 → 100755

1.71 KB

datalets/leafletjs-geojson-datalet/leafletsjs/images/marker-shadow.png 0 → 100755

797 Bytes

datalets/leafletjs-geojson-datalet/leafletsjs/leaflet-src.js 0 → 100755
Changes suppressed. Click to show
  1 +/*
  2 + Leaflet, a JavaScript library for mobile-friendly interactive maps. http://leafletjs.com
  3 + (c) 2010-2013, Vladimir Agafonkin
  4 + (c) 2010-2011, CloudMade
  5 +*/
  6 +(function (window, document, undefined) {
  7 +var oldL = window.L,
  8 + L = {};
  9 +
  10 +L.version = '0.7.3';
  11 +
  12 +// define Leaflet for Node module pattern loaders, including Browserify
  13 +if (typeof module === 'object' && typeof module.exports === 'object') {
  14 + module.exports = L;
  15 +
  16 +// define Leaflet as an AMD module
  17 +} else if (typeof define === 'function' && define.amd) {
  18 + define(L);
  19 +}
  20 +
  21 +// define Leaflet as a global L variable, saving the original L to restore later if needed
  22 +
  23 +L.noConflict = function () {
  24 + window.L = oldL;
  25 + return this;
  26 +};
  27 +
  28 +window.L = L;
  29 +
  30 +
  31 +/*
  32 + * L.Util contains various utility functions used throughout Leaflet code.
  33 + */
  34 +
  35 +L.Util = {
  36 + extend: function (dest) { // (Object[, Object, ...]) ->
  37 + var sources = Array.prototype.slice.call(arguments, 1),
  38 + i, j, len, src;
  39 +
  40 + for (j = 0, len = sources.length; j < len; j++) {
  41 + src = sources[j] || {};
  42 + for (i in src) {
  43 + if (src.hasOwnProperty(i)) {
  44 + dest[i] = src[i];
  45 + }
  46 + }
  47 + }
  48 + return dest;
  49 + },
  50 +
  51 + bind: function (fn, obj) { // (Function, Object) -> Function
  52 + var args = arguments.length > 2 ? Array.prototype.slice.call(arguments, 2) : null;
  53 + return function () {
  54 + return fn.apply(obj, args || arguments);
  55 + };
  56 + },
  57 +
  58 + stamp: (function () {
  59 + var lastId = 0,
  60 + key = '_leaflet_id';
  61 + return function (obj) {
  62 + obj[key] = obj[key] || ++lastId;
  63 + return obj[key];
  64 + };
  65 + }()),
  66 +
  67 + invokeEach: function (obj, method, context) {
  68 + var i, args;
  69 +
  70 + if (typeof obj === 'object') {
  71 + args = Array.prototype.slice.call(arguments, 3);
  72 +
  73 + for (i in obj) {
  74 + method.apply(context, [i, obj[i]].concat(args));
  75 + }
  76 + return true;
  77 + }
  78 +
  79 + return false;
  80 + },
  81 +
  82 + limitExecByInterval: function (fn, time, context) {
  83 + var lock, execOnUnlock;
  84 +
  85 + return function wrapperFn() {
  86 + var args = arguments;
  87 +
  88 + if (lock) {
  89 + execOnUnlock = true;
  90 + return;
  91 + }
  92 +
  93 + lock = true;
  94 +
  95 + setTimeout(function () {
  96 + lock = false;
  97 +
  98 + if (execOnUnlock) {
  99 + wrapperFn.apply(context, args);
  100 + execOnUnlock = false;
  101 + }
  102 + }, time);
  103 +
  104 + fn.apply(context, args);
  105 + };
  106 + },
  107 +
  108 + falseFn: function () {
  109 + return false;
  110 + },
  111 +
  112 + formatNum: function (num, digits) {
  113 + var pow = Math.pow(10, digits || 5);
  114 + return Math.round(num * pow) / pow;
  115 + },
  116 +
  117 + trim: function (str) {
  118 + return str.trim ? str.trim() : str.replace(/^\s+|\s+$/g, '');
  119 + },
  120 +
  121 + splitWords: function (str) {
  122 + return L.Util.trim(str).split(/\s+/);
  123 + },
  124 +
  125 + setOptions: function (obj, options) {
  126 + obj.options = L.extend({}, obj.options, options);
  127 + return obj.options;
  128 + },
  129 +
  130 + getParamString: function (obj, existingUrl, uppercase) {
  131 + var params = [];
  132 + for (var i in obj) {
  133 + params.push(encodeURIComponent(uppercase ? i.toUpperCase() : i) + '=' + encodeURIComponent(obj[i]));
  134 + }
  135 + return ((!existingUrl || existingUrl.indexOf('?') === -1) ? '?' : '&') + params.join('&');
  136 + },
  137 + template: function (str, data) {
  138 + return str.replace(/\{ *([\w_]+) *\}/g, function (str, key) {
  139 + var value = data[key];
  140 + if (value === undefined) {
  141 + throw new Error('No value provided for variable ' + str);
  142 + } else if (typeof value === 'function') {
  143 + value = value(data);
  144 + }
  145 + return value;
  146 + });
  147 + },
  148 +
  149 + isArray: Array.isArray || function (obj) {
  150 + return (Object.prototype.toString.call(obj) === '[object Array]');
  151 + },
  152 +
  153 + emptyImageUrl: 'data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs='
  154 +};
  155 +
  156 +(function () {
  157 +
  158 + // inspired by http://paulirish.com/2011/requestanimationframe-for-smart-animating/
  159 +
  160 + function getPrefixed(name) {
  161 + var i, fn,
  162 + prefixes = ['webkit', 'moz', 'o', 'ms'];
  163 +
  164 + for (i = 0; i < prefixes.length && !fn; i++) {
  165 + fn = window[prefixes[i] + name];
  166 + }
  167 +
  168 + return fn;
  169 + }
  170 +
  171 + var lastTime = 0;
  172 +
  173 + function timeoutDefer(fn) {
  174 + var time = +new Date(),
  175 + timeToCall = Math.max(0, 16 - (time - lastTime));
  176 +
  177 + lastTime = time + timeToCall;
  178 + return window.setTimeout(fn, timeToCall);
  179 + }
  180 +
  181 + var requestFn = window.requestAnimationFrame ||
  182 + getPrefixed('RequestAnimationFrame') || timeoutDefer;
  183 +
  184 + var cancelFn = window.cancelAnimationFrame ||
  185 + getPrefixed('CancelAnimationFrame') ||
  186 + getPrefixed('CancelRequestAnimationFrame') ||
  187 + function (id) { window.clearTimeout(id); };
  188 +
  189 +
  190 + L.Util.requestAnimFrame = function (fn, context, immediate, element) {
  191 + fn = L.bind(fn, context);
  192 +
  193 + if (immediate && requestFn === timeoutDefer) {
  194 + fn();
  195 + } else {
  196 + return requestFn.call(window, fn, element);
  197 + }
  198 + };
  199 +
  200 + L.Util.cancelAnimFrame = function (id) {
  201 + if (id) {
  202 + cancelFn.call(window, id);
  203 + }
  204 + };
  205 +
  206 +}());
  207 +
  208 +// shortcuts for most used utility functions
  209 +L.extend = L.Util.extend;
  210 +L.bind = L.Util.bind;
  211 +L.stamp = L.Util.stamp;
  212 +L.setOptions = L.Util.setOptions;
  213 +
  214 +
  215 +/*
  216 + * L.Class powers the OOP facilities of the library.
  217 + * Thanks to John Resig and Dean Edwards for inspiration!
  218 + */
  219 +
  220 +L.Class = function () {};
  221 +
  222 +L.Class.extend = function (props) {
  223 +
  224 + // extended class with the new prototype
  225 + var NewClass = function () {
  226 +
  227 + // call the constructor
  228 + if (this.initialize) {
  229 + this.initialize.apply(this, arguments);
  230 + }
  231 +
  232 + // call all constructor hooks
  233 + if (this._initHooks) {
  234 + this.callInitHooks();
  235 + }
  236 + };
  237 +
  238 + // instantiate class without calling constructor
  239 + var F = function () {};
  240 + F.prototype = this.prototype;
  241 +
  242 + var proto = new F();
  243 + proto.constructor = NewClass;
  244 +
  245 + NewClass.prototype = proto;
  246 +
  247 + //inherit parent's statics
  248 + for (var i in this) {
  249 + if (this.hasOwnProperty(i) && i !== 'prototype') {
  250 + NewClass[i] = this[i];
  251 + }
  252 + }
  253 +
  254 + // mix static properties into the class
  255 + if (props.statics) {
  256 + L.extend(NewClass, props.statics);
  257 + delete props.statics;
  258 + }
  259 +
  260 + // mix includes into the prototype
  261 + if (props.includes) {
  262 + L.Util.extend.apply(null, [proto].concat(props.includes));
  263 + delete props.includes;
  264 + }
  265 +
  266 + // merge options
  267 + if (props.options && proto.options) {
  268 + props.options = L.extend({}, proto.options, props.options);
  269 + }
  270 +
  271 + // mix given properties into the prototype
  272 + L.extend(proto, props);
  273 +
  274 + proto._initHooks = [];
  275 +
  276 + var parent = this;
  277 + // jshint camelcase: false
  278 + NewClass.__super__ = parent.prototype;
  279 +
  280 + // add method for calling all hooks
  281 + proto.callInitHooks = function () {
  282 +
  283 + if (this._initHooksCalled) { return; }
  284 +
  285 + if (parent.prototype.callInitHooks) {
  286 + parent.prototype.callInitHooks.call(this);
  287 + }
  288 +
  289 + this._initHooksCalled = true;
  290 +
  291 + for (var i = 0, len = proto._initHooks.length; i < len; i++) {
  292 + proto._initHooks[i].call(this);
  293 + }
  294 + };
  295 +
  296 + return NewClass;
  297 +};
  298 +
  299 +
  300 +// method for adding properties to prototype
  301 +L.Class.include = function (props) {
  302 + L.extend(this.prototype, props);
  303 +};
  304 +
  305 +// merge new default options to the Class
  306 +L.Class.mergeOptions = function (options) {
  307 + L.extend(this.prototype.options, options);
  308 +};
  309 +
  310 +// add a constructor hook
  311 +L.Class.addInitHook = function (fn) { // (Function) || (String, args...)
  312 + var args = Array.prototype.slice.call(arguments, 1);
  313 +
  314 + var init = typeof fn === 'function' ? fn : function () {
  315 + this[fn].apply(this, args);
  316 + };
  317 +
  318 + this.prototype._initHooks = this.prototype._initHooks || [];
  319 + this.prototype._initHooks.push(init);
  320 +};
  321 +
  322 +
  323 +/*
  324 + * L.Mixin.Events is used to add custom events functionality to Leaflet classes.
  325 + */
  326 +
  327 +var eventsKey = '_leaflet_events';
  328 +
  329 +L.Mixin = {};
  330 +
  331 +L.Mixin.Events = {
  332 +
  333 + addEventListener: function (types, fn, context) { // (String, Function[, Object]) or (Object[, Object])
  334 +
  335 + // types can be a map of types/handlers
  336 + if (L.Util.invokeEach(types, this.addEventListener, this, fn, context)) { return this; }
  337 +
  338 + var events = this[eventsKey] = this[eventsKey] || {},
  339 + contextId = context && context !== this && L.stamp(context),
  340 + i, len, event, type, indexKey, indexLenKey, typeIndex;
  341 +
  342 + // types can be a string of space-separated words
  343 + types = L.Util.splitWords(types);
  344 +
  345 + for (i = 0, len = types.length; i < len; i++) {
  346 + event = {
  347 + action: fn,
  348 + context: context || this
  349 + };
  350 + type = types[i];
  351 +
  352 + if (contextId) {
  353 + // store listeners of a particular context in a separate hash (if it has an id)
  354 + // gives a major performance boost when removing thousands of map layers
  355 +
  356 + indexKey = type + '_idx';
  357 + indexLenKey = indexKey + '_len';
  358 +
  359 + typeIndex = events[indexKey] = events[indexKey] || {};
  360 +
  361 + if (!typeIndex[contextId]) {
  362 + typeIndex[contextId] = [];
  363 +
  364 + // keep track of the number of keys in the index to quickly check if it's empty
  365 + events[indexLenKey] = (events[indexLenKey] || 0) + 1;
  366 + }
  367 +
  368 + typeIndex[contextId].push(event);
  369 +
  370 +
  371 + } else {
  372 + events[type] = events[type] || [];
  373 + events[type].push(event);
  374 + }
  375 + }
  376 +
  377 + return this;
  378 + },
  379 +
  380 + hasEventListeners: function (type) { // (String) -> Boolean
  381 + var events = this[eventsKey];
  382 + return !!events && ((type in events && events[type].length > 0) ||
  383 + (type + '_idx' in events && events[type + '_idx_len'] > 0));
  384 + },
  385 +
  386 + removeEventListener: function (types, fn, context) { // ([String, Function, Object]) or (Object[, Object])
  387 +
  388 + if (!this[eventsKey]) {
  389 + return this;
  390 + }
  391 +
  392 + if (!types) {
  393 + return this.clearAllEventListeners();
  394 + }
  395 +
  396 + if (L.Util.invokeEach(types, this.removeEventListener, this, fn, context)) { return this; }
  397 +
  398 + var events = this[eventsKey],
  399 + contextId = context && context !== this && L.stamp(context),
  400 + i, len, type, listeners, j, indexKey, indexLenKey, typeIndex, removed;
  401 +
  402 + types = L.Util.splitWords(types);
  403 +
  404 + for (i = 0, len = types.length; i < len; i++) {
  405 + type = types[i];
  406 + indexKey = type + '_idx';
  407 + indexLenKey = indexKey + '_len';
  408 +
  409 + typeIndex = events[indexKey];
  410 +
  411 + if (!fn) {
  412 + // clear all listeners for a type if function isn't specified
  413 + delete events[type];
  414 + delete events[indexKey];
  415 + delete events[indexLenKey];
  416 +
  417 + } else {
  418 + listeners = contextId && typeIndex ? typeIndex[contextId] : events[type];
  419 +
  420 + if (listeners) {
  421 + for (j = listeners.length - 1; j >= 0; j--) {
  422 + if ((listeners[j].action === fn) && (!context || (listeners[j].context === context))) {
  423 + removed = listeners.splice(j, 1);
  424 + // set the old action to a no-op, because it is possible
  425 + // that the listener is being iterated over as part of a dispatch
  426 + removed[0].action = L.Util.falseFn;
  427 + }
  428 + }
  429 +
  430 + if (context && typeIndex && (listeners.length === 0)) {
  431 + delete typeIndex[contextId];
  432 + events[indexLenKey]--;
  433 + }
  434 + }
  435 + }
  436 + }
  437 +
  438 + return this;
  439 + },
  440 +
  441 + clearAllEventListeners: function () {
  442 + delete this[eventsKey];
  443 + return this;
  444 + },
  445 +
  446 + fireEvent: function (type, data) { // (String[, Object])
  447 + if (!this.hasEventListeners(type)) {
  448 + return this;
  449 + }
  450 +
  451 + var event = L.Util.extend({}, data, { type: type, target: this });
  452 +
  453 + var events = this[eventsKey],
  454 + listeners, i, len, typeIndex, contextId;
  455 +
  456 + if (events[type]) {
  457 + // make sure adding/removing listeners inside other listeners won't cause infinite loop
  458 + listeners = events[type].slice();
  459 +
  460 + for (i = 0, len = listeners.length; i < len; i++) {
  461 + listeners[i].action.call(listeners[i].context, event);
  462 + }
  463 + }
  464 +
  465 + // fire event for the context-indexed listeners as well
  466 + typeIndex = events[type + '_idx'];
  467 +
  468 + for (contextId in typeIndex) {
  469 + listeners = typeIndex[contextId].slice();
  470 +
  471 + if (listeners) {
  472 + for (i = 0, len = listeners.length; i < len; i++) {
  473 + listeners[i].action.call(listeners[i].context, event);
  474 + }
  475 + }
  476 + }
  477 +
  478 + return this;
  479 + },
  480 +
  481 + addOneTimeEventListener: function (types, fn, context) {
  482 +
  483 + if (L.Util.invokeEach(types, this.addOneTimeEventListener, this, fn, context)) { return this; }
  484 +
  485 + var handler = L.bind(function () {
  486 + this
  487 + .removeEventListener(types, fn, context)
  488 + .removeEventListener(types, handler, context);
  489 + }, this);
  490 +
  491 + return this
  492 + .addEventListener(types, fn, context)
  493 + .addEventListener(types, handler, context);
  494 + }
  495 +};
  496 +
  497 +L.Mixin.Events.on = L.Mixin.Events.addEventListener;
  498 +L.Mixin.Events.off = L.Mixin.Events.removeEventListener;
  499 +L.Mixin.Events.once = L.Mixin.Events.addOneTimeEventListener;
  500 +L.Mixin.Events.fire = L.Mixin.Events.fireEvent;
  501 +
  502 +
  503 +/*
  504 + * L.Browser handles different browser and feature detections for internal Leaflet use.
  505 + */
  506 +
  507 +(function () {
  508 +
  509 + var ie = 'ActiveXObject' in window,
  510 + ielt9 = ie && !document.addEventListener,
  511 +
  512 + // terrible browser detection to work around Safari / iOS / Android browser bugs
  513 + ua = navigator.userAgent.toLowerCase(),
  514 + webkit = ua.indexOf('webkit') !== -1,
  515 + chrome = ua.indexOf('chrome') !== -1,
  516 + phantomjs = ua.indexOf('phantom') !== -1,
  517 + android = ua.indexOf('android') !== -1,
  518 + android23 = ua.search('android [23]') !== -1,
  519 + gecko = ua.indexOf('gecko') !== -1,
  520 +
  521 + mobile = typeof orientation !== undefined + '',
  522 + msPointer = window.navigator && window.navigator.msPointerEnabled &&
  523 + window.navigator.msMaxTouchPoints && !window.PointerEvent,
  524 + pointer = (window.PointerEvent && window.navigator.pointerEnabled && window.navigator.maxTouchPoints) ||
  525 + msPointer,
  526 + retina = ('devicePixelRatio' in window && window.devicePixelRatio > 1) ||
  527 + ('matchMedia' in window && window.matchMedia('(min-resolution:144dpi)') &&
  528 + window.matchMedia('(min-resolution:144dpi)').matches),
  529 +
  530 + doc = document.documentElement,
  531 + ie3d = ie && ('transition' in doc.style),
  532 + webkit3d = ('WebKitCSSMatrix' in window) && ('m11' in new window.WebKitCSSMatrix()) && !android23,
  533 + gecko3d = 'MozPerspective' in doc.style,
  534 + opera3d = 'OTransition' in doc.style,
  535 + any3d = !window.L_DISABLE_3D && (ie3d || webkit3d || gecko3d || opera3d) && !phantomjs;
  536 +
  537 +
  538 + // PhantomJS has 'ontouchstart' in document.documentElement, but doesn't actually support touch.
  539 + // https://github.com/Leaflet/Leaflet/pull/1434#issuecomment-13843151
  540 +
  541 + var touch = !window.L_NO_TOUCH && !phantomjs && (function () {
  542 +
  543 + var startName = 'ontouchstart';
  544 +
  545 + // IE10+ (We simulate these into touch* events in L.DomEvent and L.DomEvent.Pointer) or WebKit, etc.
  546 + if (pointer || (startName in doc)) {
  547 + return true;
  548 + }
  549 +
  550 + // Firefox/Gecko
  551 + var div = document.createElement('div'),
  552 + supported = false;
  553 +
  554 + if (!div.setAttribute) {
  555 + return false;
  556 + }
  557 + div.setAttribute(startName, 'return;');
  558 +
  559 + if (typeof div[startName] === 'function') {
  560 + supported = true;
  561 + }
  562 +
  563 + div.removeAttribute(startName);
  564 + div = null;
  565 +
  566 + return supported;
  567 + }());
  568 +
  569 +
  570 + L.Browser = {
  571 + ie: ie,
  572 + ielt9: ielt9,
  573 + webkit: webkit,
  574 + gecko: gecko && !webkit && !window.opera && !ie,
  575 +
  576 + android: android,
  577 + android23: android23,
  578 +
  579 + chrome: chrome,
  580 +
  581 + ie3d: ie3d,
  582 + webkit3d: webkit3d,
  583 + gecko3d: gecko3d,
  584 + opera3d: opera3d,
  585 + any3d: any3d,
  586 +
  587 + mobile: mobile,
  588 + mobileWebkit: mobile && webkit,
  589 + mobileWebkit3d: mobile && webkit3d,
  590 + mobileOpera: mobile && window.opera,
  591 +
  592 + touch: touch,
  593 + msPointer: msPointer,
  594 + pointer: pointer,
  595 +
  596 + retina: retina
  597 + };
  598 +
  599 +}());
  600 +
  601 +
  602 +/*
  603 + * L.Point represents a point with x and y coordinates.
  604 + */
  605 +
  606 +L.Point = function (/*Number*/ x, /*Number*/ y, /*Boolean*/ round) {
  607 + this.x = (round ? Math.round(x) : x);
  608 + this.y = (round ? Math.round(y) : y);
  609 +};
  610 +
  611 +L.Point.prototype = {
  612 +
  613 + clone: function () {
  614 + return new L.Point(this.x, this.y);
  615 + },
  616 +
  617 + // non-destructive, returns a new point
  618 + add: function (point) {
  619 + return this.clone()._add(L.point(point));
  620 + },
  621 +
  622 + // destructive, used directly for performance in situations where it's safe to modify existing point
  623 + _add: function (point) {
  624 + this.x += point.x;
  625 + this.y += point.y;
  626 + return this;
  627 + },
  628 +
  629 + subtract: function (point) {
  630 + return this.clone()._subtract(L.point(point));
  631 + },
  632 +
  633 + _subtract: function (point) {
  634 + this.x -= point.x;
  635 + this.y -= point.y;
  636 + return this;
  637 + },
  638 +
  639 + divideBy: function (num) {
  640 + return this.clone()._divideBy(num);
  641 + },
  642 +
  643 + _divideBy: function (num) {
  644 + this.x /= num;
  645 + this.y /= num;
  646 + return this;
  647 + },
  648 +
  649 + multiplyBy: function (num) {
  650 + return this.clone()._multiplyBy(num);
  651 + },
  652 +
  653 + _multiplyBy: function (num) {
  654 + this.x *= num;
  655 + this.y *= num;
  656 + return this;
  657 + },
  658 +
  659 + round: function () {
  660 + return this.clone()._round();
  661 + },
  662 +
  663 + _round: function () {
  664 + this.x = Math.round(this.x);
  665 + this.y = Math.round(this.y);
  666 + return this;
  667 + },
  668 +
  669 + floor: function () {
  670 + return this.clone()._floor();
  671 + },
  672 +
  673 + _floor: function () {
  674 + this.x = Math.floor(this.x);
  675 + this.y = Math.floor(this.y);
  676 + return this;
  677 + },
  678 +
  679 + distanceTo: function (point) {
  680 + point = L.point(point);
  681 +
  682 + var x = point.x - this.x,
  683 + y = point.y - this.y;
  684 +
  685 + return Math.sqrt(x * x + y * y);
  686 + },
  687 +
  688 + equals: function (point) {
  689 + point = L.point(point);
  690 +
  691 + return point.x === this.x &&
  692 + point.y === this.y;
  693 + },
  694 +
  695 + contains: function (point) {
  696 + point = L.point(point);
  697 +
  698 + return Math.abs(point.x) <= Math.abs(this.x) &&
  699 + Math.abs(point.y) <= Math.abs(this.y);
  700 + },
  701 +
  702 + toString: function () {
  703 + return 'Point(' +
  704 + L.Util.formatNum(this.x) + ', ' +
  705 + L.Util.formatNum(this.y) + ')';
  706 + }
  707 +};
  708 +
  709 +L.point = function (x, y, round) {
  710 + if (x instanceof L.Point) {
  711 + return x;
  712 + }
  713 + if (L.Util.isArray(x)) {
  714 + return new L.Point(x[0], x[1]);
  715 + }
  716 + if (x === undefined || x === null) {
  717 + return x;
  718 + }
  719 + return new L.Point(x, y, round);
  720 +};
  721 +
  722 +
  723 +/*
  724 + * L.Bounds represents a rectangular area on the screen in pixel coordinates.
  725 + */
  726 +
  727 +L.Bounds = function (a, b) { //(Point, Point) or Point[]
  728 + if (!a) { return; }
  729 +
  730 + var points = b ? [a, b] : a;
  731 +
  732 + for (var i = 0, len = points.length; i < len; i++) {
  733 + this.extend(points[i]);
  734 + }
  735 +};
  736 +
  737 +L.Bounds.prototype = {
  738 + // extend the bounds to contain the given point
  739 + extend: function (point) { // (Point)
  740 + point = L.point(point);
  741 +
  742 + if (!this.min && !this.max) {
  743 + this.min = point.clone();
  744 + this.max = point.clone();
  745 + } else {
  746 + this.min.x = Math.min(point.x, this.min.x);
  747 + this.max.x = Math.max(point.x, this.max.x);
  748 + this.min.y = Math.min(point.y, this.min.y);
  749 + this.max.y = Math.max(point.y, this.max.y);
  750 + }
  751 + return this;
  752 + },
  753 +
  754 + getCenter: function (round) { // (Boolean) -> Point
  755 + return new L.Point(
  756 + (this.min.x + this.max.x) / 2,
  757 + (this.min.y + this.max.y) / 2, round);
  758 + },
  759 +
  760 + getBottomLeft: function () { // -> Point
  761 + return new L.Point(this.min.x, this.max.y);
  762 + },
  763 +
  764 + getTopRight: function () { // -> Point
  765 + return new L.Point(this.max.x, this.min.y);
  766 + },
  767 +
  768 + getSize: function () {
  769 + return this.max.subtract(this.min);
  770 + },
  771 +
  772 + contains: function (obj) { // (Bounds) or (Point) -> Boolean
  773 + var min, max;
  774 +
  775 + if (typeof obj[0] === 'number' || obj instanceof L.Point) {
  776 + obj = L.point(obj);
  777 + } else {
  778 + obj = L.bounds(obj);
  779 + }
  780 +
  781 + if (obj instanceof L.Bounds) {
  782 + min = obj.min;
  783 + max = obj.max;
  784 + } else {
  785 + min = max = obj;
  786 + }
  787 +
  788 + return (min.x >= this.min.x) &&
  789 + (max.x <= this.max.x) &&
  790 + (min.y >= this.min.y) &&
  791 + (max.y <= this.max.y);
  792 + },
  793 +
  794 + intersects: function (bounds) { // (Bounds) -> Boolean
  795 + bounds = L.bounds(bounds);
  796 +
  797 + var min = this.min,
  798 + max = this.max,
  799 + min2 = bounds.min,
  800 + max2 = bounds.max,
  801 + xIntersects = (max2.x >= min.x) && (min2.x <= max.x),
  802 + yIntersects = (max2.y >= min.y) && (min2.y <= max.y);
  803 +
  804 + return xIntersects && yIntersects;
  805 + },
  806 +
  807 + isValid: function () {
  808 + return !!(this.min && this.max);
  809 + }
  810 +};
  811 +
  812 +L.bounds = function (a, b) { // (Bounds) or (Point, Point) or (Point[])
  813 + if (!a || a instanceof L.Bounds) {
  814 + return a;
  815 + }
  816 + return new L.Bounds(a, b);
  817 +};
  818 +
  819 +
  820 +/*
  821 + * L.Transformation is an utility class to perform simple point transformations through a 2d-matrix.
  822 + */
  823 +
  824 +L.Transformation = function (a, b, c, d) {
  825 + this._a = a;
  826 + this._b = b;
  827 + this._c = c;
  828 + this._d = d;
  829 +};
  830 +
  831 +L.Transformation.prototype = {
  832 + transform: function (point, scale) { // (Point, Number) -> Point
  833 + return this._transform(point.clone(), scale);
  834 + },
  835 +
  836 + // destructive transform (faster)
  837 + _transform: function (point, scale) {
  838 + scale = scale || 1;
  839 + point.x = scale * (this._a * point.x + this._b);
  840 + point.y = scale * (this._c * point.y + this._d);
  841 + return point;
  842 + },
  843 +
  844 + untransform: function (point, scale) {
  845 + scale = scale || 1;
  846 + return new L.Point(
  847 + (point.x / scale - this._b) / this._a,
  848 + (point.y / scale - this._d) / this._c);
  849 + }
  850 +};
  851 +
  852 +
  853 +/*
  854 + * L.DomUtil contains various utility functions for working with DOM.
  855 + */
  856 +
  857 +L.DomUtil = {
  858 + get: function (id) {
  859 + return (typeof id === 'string' ? document.getElementById(id) : id);
  860 + },
  861 +
  862 + getStyle: function (el, style) {
  863 +
  864 + var value = el.style[style];
  865 +
  866 + if (!value && el.currentStyle) {
  867 + value = el.currentStyle[style];
  868 + }
  869 +
  870 + if ((!value || value === 'auto') && document.defaultView) {
  871 + var css = document.defaultView.getComputedStyle(el, null);
  872 + value = css ? css[style] : null;
  873 + }
  874 +
  875 + return value === 'auto' ? null : value;
  876 + },
  877 +
  878 + getViewportOffset: function (element) {
  879 +
  880 + var top = 0,
  881 + left = 0,
  882 + el = element,
  883 + docBody = document.body,
  884 + docEl = document.documentElement,
  885 + pos;
  886 +
  887 + do {
  888 + top += el.offsetTop || 0;
  889 + left += el.offsetLeft || 0;
  890 +
  891 + //add borders
  892 + top += parseInt(L.DomUtil.getStyle(el, 'borderTopWidth'), 10) || 0;
  893 + left += parseInt(L.DomUtil.getStyle(el, 'borderLeftWidth'), 10) || 0;
  894 +
  895 + pos = L.DomUtil.getStyle(el, 'position');
  896 +
  897 + if (el.offsetParent === docBody && pos === 'absolute') { break; }
  898 +
  899 + if (pos === 'fixed') {
  900 + top += docBody.scrollTop || docEl.scrollTop || 0;
  901 + left += docBody.scrollLeft || docEl.scrollLeft || 0;
  902 + break;
  903 + }
  904 +
  905 + if (pos === 'relative' && !el.offsetLeft) {
  906 + var width = L.DomUtil.getStyle(el, 'width'),
  907 + maxWidth = L.DomUtil.getStyle(el, 'max-width'),
  908 + r = el.getBoundingClientRect();
  909 +
  910 + if (width !== 'none' || maxWidth !== 'none') {
  911 + left += r.left + el.clientLeft;
  912 + }
  913 +
  914 + //calculate full y offset since we're breaking out of the loop
  915 + top += r.top + (docBody.scrollTop || docEl.scrollTop || 0);
  916 +
  917 + break;
  918 + }
  919 +
  920 + el = el.offsetParent;
  921 +
  922 + } while (el);
  923 +
  924 + el = element;
  925 +
  926 + do {
  927 + if (el === docBody) { break; }
  928 +
  929 + top -= el.scrollTop || 0;
  930 + left -= el.scrollLeft || 0;
  931 +
  932 + el = el.parentNode;
  933 + } while (el);
  934 +
  935 + return new L.Point(left, top);
  936 + },
  937 +
  938 + documentIsLtr: function () {
  939 + if (!L.DomUtil._docIsLtrCached) {
  940 + L.DomUtil._docIsLtrCached = true;
  941 + L.DomUtil._docIsLtr = L.DomUtil.getStyle(document.body, 'direction') === 'ltr';
  942 + }
  943 + return L.DomUtil._docIsLtr;
  944 + },
  945 +
  946 + create: function (tagName, className, container) {
  947 +
  948 + var el = document.createElement(tagName);
  949 + el.className = className;
  950 +
  951 + if (container) {
  952 + container.appendChild(el);
  953 + }
  954 +
  955 + return el;
  956 + },
  957 +
  958 + hasClass: function (el, name) {
  959 + if (el.classList !== undefined) {
  960 + return el.classList.contains(name);
  961 + }
  962 + var className = L.DomUtil._getClass(el);
  963 + return className.length > 0 && new RegExp('(^|\\s)' + name + '(\\s|$)').test(className);
  964 + },
  965 +
  966 + addClass: function (el, name) {
  967 + if (el.classList !== undefined) {
  968 + var classes = L.Util.splitWords(name);
  969 + for (var i = 0, len = classes.length; i < len; i++) {
  970 + el.classList.add(classes[i]);
  971 + }
  972 + } else if (!L.DomUtil.hasClass(el, name)) {
  973 + var className = L.DomUtil._getClass(el);
  974 + L.DomUtil._setClass(el, (className ? className + ' ' : '') + name);
  975 + }
  976 + },
  977 +
  978 + removeClass: function (el, name) {
  979 + if (el.classList !== undefined) {
  980 + el.classList.remove(name);
  981 + } else {
  982 + L.DomUtil._setClass(el, L.Util.trim((' ' + L.DomUtil._getClass(el) + ' ').replace(' ' + name + ' ', ' ')));
  983 + }
  984 + },
  985 +
  986 + _setClass: function (el, name) {
  987 + if (el.className.baseVal === undefined) {
  988 + el.className = name;
  989 + } else {
  990 + // in case of SVG element
  991 + el.className.baseVal = name;
  992 + }
  993 + },
  994 +
  995 + _getClass: function (el) {
  996 + return el.className.baseVal === undefined ? el.className : el.className.baseVal;
  997 + },
  998 +
  999 + setOpacity: function (el, value) {
  1000 +
  1001 + if ('opacity' in el.style) {
  1002 + el.style.opacity = value;
  1003 +
  1004 + } else if ('filter' in el.style) {
  1005 +
  1006 + var filter = false,
  1007 + filterName = 'DXImageTransform.Microsoft.Alpha';
  1008 +
  1009 + // filters collection throws an error if we try to retrieve a filter that doesn't exist
  1010 + try {
  1011 + filter = el.filters.item(filterName);
  1012 + } catch (e) {
  1013 + // don't set opacity to 1 if we haven't already set an opacity,
  1014 + // it isn't needed and breaks transparent pngs.
  1015 + if (value === 1) { return; }
  1016 + }
  1017 +
  1018 + value = Math.round(value * 100);
  1019 +
  1020 + if (filter) {
  1021 + filter.Enabled = (value !== 100);
  1022 + filter.Opacity = value;
  1023 + } else {
  1024 + el.style.filter += ' progid:' + filterName + '(opacity=' + value + ')';
  1025 + }
  1026 + }
  1027 + },
  1028 +
  1029 + testProp: function (props) {
  1030 +
  1031 + var style = document.documentElement.style;
  1032 +
  1033 + for (var i = 0; i < props.length; i++) {
  1034 + if (props[i] in style) {
  1035 + return props[i];
  1036 + }
  1037 + }
  1038 + return false;
  1039 + },
  1040 +
  1041 + getTranslateString: function (point) {
  1042 + // on WebKit browsers (Chrome/Safari/iOS Safari/Android) using translate3d instead of translate
  1043 + // makes animation smoother as it ensures HW accel is used. Firefox 13 doesn't care
  1044 + // (same speed either way), Opera 12 doesn't support translate3d
  1045 +
  1046 + var is3d = L.Browser.webkit3d,
  1047 + open = 'translate' + (is3d ? '3d' : '') + '(',
  1048 + close = (is3d ? ',0' : '') + ')';
  1049 +
  1050 + return open + point.x + 'px,' + point.y + 'px' + close;
  1051 + },
  1052 +
  1053 + getScaleString: function (scale, origin) {
  1054 +
  1055 + var preTranslateStr = L.DomUtil.getTranslateString(origin.add(origin.multiplyBy(-1 * scale))),
  1056 + scaleStr = ' scale(' + scale + ') ';
  1057 +
  1058 + return preTranslateStr + scaleStr;
  1059 + },
  1060 +
  1061 + setPosition: function (el, point, disable3D) { // (HTMLElement, Point[, Boolean])
  1062 +
  1063 + // jshint camelcase: false
  1064 + el._leaflet_pos = point;
  1065 +
  1066 + if (!disable3D && L.Browser.any3d) {
  1067 + el.style[L.DomUtil.TRANSFORM] = L.DomUtil.getTranslateString(point);
  1068 + } else {
  1069 + el.style.left = point.x + 'px';
  1070 + el.style.top = point.y + 'px';
  1071 + }
  1072 + },
  1073 +
  1074 + getPosition: function (el) {
  1075 + // this method is only used for elements previously positioned using setPosition,
  1076 + // so it's safe to cache the position for performance
  1077 +
  1078 + // jshint camelcase: false
  1079 + return el._leaflet_pos;
  1080 + }
  1081 +};
  1082 +
  1083 +
  1084 +// prefix style property names
  1085 +
  1086 +L.DomUtil.TRANSFORM = L.DomUtil.testProp(
  1087 + ['transform', 'WebkitTransform', 'OTransform', 'MozTransform', 'msTransform']);
  1088 +
  1089 +// webkitTransition comes first because some browser versions that drop vendor prefix don't do
  1090 +// the same for the transitionend event, in particular the Android 4.1 stock browser
  1091 +
  1092 +L.DomUtil.TRANSITION = L.DomUtil.testProp(
  1093 + ['webkitTransition', 'transition', 'OTransition', 'MozTransition', 'msTransition']);
  1094 +
  1095 +L.DomUtil.TRANSITION_END =
  1096 + L.DomUtil.TRANSITION === 'webkitTransition' || L.DomUtil.TRANSITION === 'OTransition' ?
  1097 + L.DomUtil.TRANSITION + 'End' : 'transitionend';
  1098 +
  1099 +(function () {
  1100 + if ('onselectstart' in document) {
  1101 + L.extend(L.DomUtil, {
  1102 + disableTextSelection: function () {
  1103 + L.DomEvent.on(window, 'selectstart', L.DomEvent.preventDefault);
  1104 + },
  1105 +
  1106 + enableTextSelection: function () {
  1107 + L.DomEvent.off(window, 'selectstart', L.DomEvent.preventDefault);
  1108 + }
  1109 + });
  1110 + } else {
  1111 + var userSelectProperty = L.DomUtil.testProp(
  1112 + ['userSelect', 'WebkitUserSelect', 'OUserSelect', 'MozUserSelect', 'msUserSelect']);
  1113 +
  1114 + L.extend(L.DomUtil, {
  1115 + disableTextSelection: function () {
  1116 + if (userSelectProperty) {
  1117 + var style = document.documentElement.style;
  1118 + this._userSelect = style[userSelectProperty];
  1119 + style[userSelectProperty] = 'none';
  1120 + }
  1121 + },
  1122 +
  1123 + enableTextSelection: function () {
  1124 + if (userSelectProperty) {
  1125 + document.documentElement.style[userSelectProperty] = this._userSelect;
  1126 + delete this._userSelect;
  1127 + }
  1128 + }
  1129 + });
  1130 + }
  1131 +
  1132 + L.extend(L.DomUtil, {
  1133 + disableImageDrag: function () {
  1134 + L.DomEvent.on(window, 'dragstart', L.DomEvent.preventDefault);
  1135 + },
  1136 +
  1137 + enableImageDrag: function () {
  1138 + L.DomEvent.off(window, 'dragstart', L.DomEvent.preventDefault);
  1139 + }
  1140 + });
  1141 +})();
  1142 +
  1143 +
  1144 +/*
  1145 + * L.LatLng represents a geographical point with latitude and longitude coordinates.
  1146 + */
  1147 +
  1148 +L.LatLng = function (lat, lng, alt) { // (Number, Number, Number)
  1149 + lat = parseFloat(lat);
  1150 + lng = parseFloat(lng);
  1151 +
  1152 + if (isNaN(lat) || isNaN(lng)) {
  1153 + throw new Error('Invalid LatLng object: (' + lat + ', ' + lng + ')');
  1154 + }
  1155 +
  1156 + this.lat = lat;
  1157 + this.lng = lng;
  1158 +
  1159 + if (alt !== undefined) {
  1160 + this.alt = parseFloat(alt);
  1161 + }
  1162 +};
  1163 +
  1164 +L.extend(L.LatLng, {
  1165 + DEG_TO_RAD: Math.PI / 180,
  1166 + RAD_TO_DEG: 180 / Math.PI,
  1167 + MAX_MARGIN: 1.0E-9 // max margin of error for the "equals" check
  1168 +});
  1169 +
  1170 +L.LatLng.prototype = {
  1171 + equals: function (obj) { // (LatLng) -> Boolean
  1172 + if (!obj) { return false; }
  1173 +
  1174 + obj = L.latLng(obj);
  1175 +
  1176 + var margin = Math.max(
  1177 + Math.abs(this.lat - obj.lat),
  1178 + Math.abs(this.lng - obj.lng));
  1179 +
  1180 + return margin <= L.LatLng.MAX_MARGIN;
  1181 + },
  1182 +
  1183 + toString: function (precision) { // (Number) -> String
  1184 + return 'LatLng(' +
  1185 + L.Util.formatNum(this.lat, precision) + ', ' +
  1186 + L.Util.formatNum(this.lng, precision) + ')';
  1187 + },
  1188 +
  1189 + // Haversine distance formula, see http://en.wikipedia.org/wiki/Haversine_formula
  1190 + // TODO move to projection code, LatLng shouldn't know about Earth
  1191 + distanceTo: function (other) { // (LatLng) -> Number
  1192 + other = L.latLng(other);
  1193 +
  1194 + var R = 6378137, // earth radius in meters
  1195 + d2r = L.LatLng.DEG_TO_RAD,
  1196 + dLat = (other.lat - this.lat) * d2r,
  1197 + dLon = (other.lng - this.lng) * d2r,
  1198 + lat1 = this.lat * d2r,
  1199 + lat2 = other.lat * d2r,
  1200 + sin1 = Math.sin(dLat / 2),
  1201 + sin2 = Math.sin(dLon / 2);
  1202 +
  1203 + var a = sin1 * sin1 + sin2 * sin2 * Math.cos(lat1) * Math.cos(lat2);
  1204 +
  1205 + return R * 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
  1206 + },
  1207 +
  1208 + wrap: function (a, b) { // (Number, Number) -> LatLng
  1209 + var lng = this.lng;
  1210 +
  1211 + a = a || -180;
  1212 + b = b || 180;
  1213 +
  1214 + lng = (lng + b) % (b - a) + (lng < a || lng === b ? b : a);
  1215 +
  1216 + return new L.LatLng(this.lat, lng);
  1217 + }
  1218 +};
  1219 +
  1220 +L.latLng = function (a, b) { // (LatLng) or ([Number, Number]) or (Number, Number)
  1221 + if (a instanceof L.LatLng) {
  1222 + return a;
  1223 + }
  1224 + if (L.Util.isArray(a)) {
  1225 + if (typeof a[0] === 'number' || typeof a[0] === 'string') {
  1226 + return new L.LatLng(a[0], a[1], a[2]);
  1227 + } else {
  1228 + return null;
  1229 + }
  1230 + }
  1231 + if (a === undefined || a === null) {
  1232 + return a;
  1233 + }
  1234 + if (typeof a === 'object' && 'lat' in a) {
  1235 + return new L.LatLng(a.lat, 'lng' in a ? a.lng : a.lon);
  1236 + }
  1237 + if (b === undefined) {
  1238 + return null;
  1239 + }
  1240 + return new L.LatLng(a, b);
  1241 +};
  1242 +
  1243 +
  1244 +
  1245 +/*
  1246 + * L.LatLngBounds represents a rectangular area on the map in geographical coordinates.
  1247 + */
  1248 +
  1249 +L.LatLngBounds = function (southWest, northEast) { // (LatLng, LatLng) or (LatLng[])
  1250 + if (!southWest) { return; }
  1251 +
  1252 + var latlngs = northEast ? [southWest, northEast] : southWest;
  1253 +
  1254 + for (var i = 0, len = latlngs.length; i < len; i++) {
  1255 + this.extend(latlngs[i]);
  1256 + }
  1257 +};
  1258 +
  1259 +L.LatLngBounds.prototype = {
  1260 + // extend the bounds to contain the given point or bounds
  1261 + extend: function (obj) { // (LatLng) or (LatLngBounds)
  1262 + if (!obj) { return this; }
  1263 +
  1264 + var latLng = L.latLng(obj);
  1265 + if (latLng !== null) {
  1266 + obj = latLng;
  1267 + } else {
  1268 + obj = L.latLngBounds(obj);
  1269 + }
  1270 +
  1271 + if (obj instanceof L.LatLng) {
  1272 + if (!this._southWest && !this._northEast) {
  1273 + this._southWest = new L.LatLng(obj.lat, obj.lng);
  1274 + this._northEast = new L.LatLng(obj.lat, obj.lng);
  1275 + } else {
  1276 + this._southWest.lat = Math.min(obj.lat, this._southWest.lat);
  1277 + this._southWest.lng = Math.min(obj.lng, this._southWest.lng);
  1278 +
  1279 + this._northEast.lat = Math.max(obj.lat, this._northEast.lat);
  1280 + this._northEast.lng = Math.max(obj.lng, this._northEast.lng);
  1281 + }
  1282 + } else if (obj instanceof L.LatLngBounds) {
  1283 + this.extend(obj._southWest);
  1284 + this.extend(obj._northEast);
  1285 + }
  1286 + return this;
  1287 + },
  1288 +
  1289 + // extend the bounds by a percentage
  1290 + pad: function (bufferRatio) { // (Number) -> LatLngBounds
  1291 + var sw = this._southWest,
  1292 + ne = this._northEast,
  1293 + heightBuffer = Math.abs(sw.lat - ne.lat) * bufferRatio,
  1294 + widthBuffer = Math.abs(sw.lng - ne.lng) * bufferRatio;
  1295 +
  1296 + return new L.LatLngBounds(
  1297 + new L.LatLng(sw.lat - heightBuffer, sw.lng - widthBuffer),
  1298 + new L.LatLng(ne.lat + heightBuffer, ne.lng + widthBuffer));
  1299 + },
  1300 +
  1301 + getCenter: function () { // -> LatLng
  1302 + return new L.LatLng(
  1303 + (this._southWest.lat + this._northEast.lat) / 2,
  1304 + (this._southWest.lng + this._northEast.lng) / 2);
  1305 + },
  1306 +
  1307 + getSouthWest: function () {
  1308 + return this._southWest;
  1309 + },
  1310 +
  1311 + getNorthEast: function () {
  1312 + return this._northEast;
  1313 + },
  1314 +
  1315 + getNorthWest: function () {
  1316 + return new L.LatLng(this.getNorth(), this.getWest());
  1317 + },
  1318 +
  1319 + getSouthEast: function () {
  1320 + return new L.LatLng(this.getSouth(), this.getEast());
  1321 + },
  1322 +
  1323 + getWest: function () {
  1324 + return this._southWest.lng;
  1325 + },
  1326 +
  1327 + getSouth: function () {
  1328 + return this._southWest.lat;
  1329 + },
  1330 +
  1331 + getEast: function () {
  1332 + return this._northEast.lng;
  1333 + },
  1334 +
  1335 + getNorth: function () {
  1336 + return this._northEast.lat;
  1337 + },
  1338 +
  1339 + contains: function (obj) { // (LatLngBounds) or (LatLng) -> Boolean
  1340 + if (typeof obj[0] === 'number' || obj instanceof L.LatLng) {
  1341 + obj = L.latLng(obj);
  1342 + } else {
  1343 + obj = L.latLngBounds(obj);
  1344 + }
  1345 +
  1346 + var sw = this._southWest,
  1347 + ne = this._northEast,
  1348 + sw2, ne2;
  1349 +
  1350 + if (obj instanceof L.LatLngBounds) {
  1351 + sw2 = obj.getSouthWest();
  1352 + ne2 = obj.getNorthEast();
  1353 + } else {
  1354 + sw2 = ne2 = obj;
  1355 + }
  1356 +
  1357 + return (sw2.lat >= sw.lat) && (ne2.lat <= ne.lat) &&
  1358 + (sw2.lng >= sw.lng) && (ne2.lng <= ne.lng);
  1359 + },
  1360 +
  1361 + intersects: function (bounds) { // (LatLngBounds)
  1362 + bounds = L.latLngBounds(bounds);
  1363 +
  1364 + var sw = this._southWest,
  1365 + ne = this._northEast,
  1366 + sw2 = bounds.getSouthWest(),
  1367 + ne2 = bounds.getNorthEast(),
  1368 +
  1369 + latIntersects = (ne2.lat >= sw.lat) && (sw2.lat <= ne.lat),
  1370 + lngIntersects = (ne2.lng >= sw.lng) && (sw2.lng <= ne.lng);
  1371 +
  1372 + return latIntersects && lngIntersects;
  1373 + },
  1374 +
  1375 + toBBoxString: function () {
  1376 + return [this.getWest(), this.getSouth(), this.getEast(), this.getNorth()].join(',');
  1377 + },
  1378 +
  1379 + equals: function (bounds) { // (LatLngBounds)
  1380 + if (!bounds) { return false; }
  1381 +
  1382 + bounds = L.latLngBounds(bounds);
  1383 +
  1384 + return this._southWest.equals(bounds.getSouthWest()) &&
  1385 + this._northEast.equals(bounds.getNorthEast());
  1386 + },
  1387 +
  1388 + isValid: function () {
  1389 + return !!(this._southWest && this._northEast);
  1390 + }
  1391 +};
  1392 +
  1393 +//TODO International date line?
  1394 +
  1395 +L.latLngBounds = function (a, b) { // (LatLngBounds) or (LatLng, LatLng)
  1396 + if (!a || a instanceof L.LatLngBounds) {
  1397 + return a;
  1398 + }
  1399 + return new L.LatLngBounds(a, b);
  1400 +};
  1401 +
  1402 +
  1403 +/*
  1404 + * L.Projection contains various geographical projections used by CRS classes.
  1405 + */
  1406 +
  1407 +L.Projection = {};
  1408 +
  1409 +
  1410 +/*
  1411 + * Spherical Mercator is the most popular map projection, used by EPSG:3857 CRS used by default.
  1412