vue2-leaflet-imageoverlay-rotated
Version:
ImageOverlay plugin extension for vue2-leaflet package
1,664 lines (1,383 loc) • 485 kB
JavaScript
(function webpackUniversalModuleDefinition(root, factory) {
if(typeof exports === 'object' && typeof module === 'object')
module.exports = factory();
else if(typeof define === 'function' && define.amd)
define([], factory);
else if(typeof exports === 'object')
exports["Vue2LeafletImageOverlayRotated"] = factory();
else
root["Vue2LeafletImageOverlayRotated"] = factory();
})(typeof self !== 'undefined' ? self : this, function() {
return /******/ (function(modules) { // webpackBootstrap
/******/ // The module cache
/******/ var installedModules = {};
/******/
/******/ // The require function
/******/ function __webpack_require__(moduleId) {
/******/
/******/ // Check if module is in cache
/******/ if(installedModules[moduleId]) {
/******/ return installedModules[moduleId].exports;
/******/ }
/******/ // Create a new module (and put it into the cache)
/******/ var module = installedModules[moduleId] = {
/******/ i: moduleId,
/******/ l: false,
/******/ exports: {}
/******/ };
/******/
/******/ // Execute the module function
/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
/******/
/******/ // Flag the module as loaded
/******/ module.l = true;
/******/
/******/ // Return the exports of the module
/******/ return module.exports;
/******/ }
/******/
/******/
/******/ // expose the modules object (__webpack_modules__)
/******/ __webpack_require__.m = modules;
/******/
/******/ // expose the module cache
/******/ __webpack_require__.c = installedModules;
/******/
/******/ // define getter function for harmony exports
/******/ __webpack_require__.d = function(exports, name, getter) {
/******/ if(!__webpack_require__.o(exports, name)) {
/******/ Object.defineProperty(exports, name, { enumerable: true, get: getter });
/******/ }
/******/ };
/******/
/******/ // define __esModule on exports
/******/ __webpack_require__.r = function(exports) {
/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
/******/ }
/******/ Object.defineProperty(exports, '__esModule', { value: true });
/******/ };
/******/
/******/ // create a fake namespace object
/******/ // mode & 1: value is a module id, require it
/******/ // mode & 2: merge all properties of value into the ns
/******/ // mode & 4: return value when already ns object
/******/ // mode & 8|1: behave like require
/******/ __webpack_require__.t = function(value, mode) {
/******/ if(mode & 1) value = __webpack_require__(value);
/******/ if(mode & 8) return value;
/******/ if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;
/******/ var ns = Object.create(null);
/******/ __webpack_require__.r(ns);
/******/ Object.defineProperty(ns, 'default', { enumerable: true, value: value });
/******/ if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));
/******/ return ns;
/******/ };
/******/
/******/ // getDefaultExport function for compatibility with non-harmony modules
/******/ __webpack_require__.n = function(module) {
/******/ var getter = module && module.__esModule ?
/******/ function getDefault() { return module['default']; } :
/******/ function getModuleExports() { return module; };
/******/ __webpack_require__.d(getter, 'a', getter);
/******/ return getter;
/******/ };
/******/
/******/ // Object.prototype.hasOwnProperty.call
/******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
/******/
/******/ // __webpack_public_path__
/******/ __webpack_require__.p = "";
/******/
/******/
/******/ // Load entry module and return exports
/******/ return __webpack_require__(__webpack_require__.s = "+xUi");
/******/ })
/************************************************************************/
/******/ ({
/***/ "+rLv":
/***/ (function(module, exports, __webpack_require__) {
var document = __webpack_require__("dyZX").document;
module.exports = document && document.documentElement;
/***/ }),
/***/ "+xUi":
/***/ (function(module, __webpack_exports__, __webpack_require__) {
"use strict";
__webpack_require__.r(__webpack_exports__);
// EXTERNAL MODULE: ./node_modules/@vue/cli-service/lib/commands/build/setPublicPath.js
var setPublicPath = __webpack_require__("HrLf");
// EXTERNAL MODULE: ./node_modules/core-js/modules/es6.number.constructor.js
var es6_number_constructor = __webpack_require__("xfY5");
// EXTERNAL MODULE: ./node_modules/leaflet/dist/leaflet-src.js
var leaflet_src = __webpack_require__("4R65");
var leaflet_src_default = /*#__PURE__*/__webpack_require__.n(leaflet_src);
// EXTERNAL MODULE: ./node_modules/vue2-leaflet/dist/vue2-leaflet.min.js
var vue2_leaflet_min = __webpack_require__("RY4O");
// EXTERNAL MODULE: ./node_modules/core-js/modules/es6.function.bind.js
var es6_function_bind = __webpack_require__("2Spj");
// CONCATENATED MODULE: ./src/leafletImageOverlayRotated.js
/*
* 🍂 class ImageOverlay.Rotated
* 🍂 inherits ImageOverlay
*
* Like `ImageOverlay`, but rotates and skews the image. This is done by using
* *three* control points instead of *two*.
*
* @example
*
* ```
* var latlngs = {};
* latlngs.topleft = L.latLng(40.52256691873593, -3.7743186950683594),
* latlngs.topright = L.latLng(40.5210255066156, -3.7734764814376835),
* latlngs.bottomleft = L.latLng(40.52180437272552, -3.7768453359603886);
*
* var overlay = L.imageOverlay.rotated("./palacio.jpg", latlngs, {
* opacity: 0.4,
* interactive: true,
* attribution: "© <a href='http://www.ign.es'>Instituto Geográfico Nacional de España</a>"
* });
* ```
*/
leaflet_src_default.a.ImageOverlay.Rotated = leaflet_src_default.a.ImageOverlay.extend({
initialize: function initialize(image, latlngs, options) {
console.log({
image: image,
latlngs: latlngs,
options: options
});
if (typeof image === 'string') {
this._url = image;
} else {
// Assume that the first parameter is an instance of HTMLImage or HTMLCanvas
this._rawImage = image;
}
this._topLeft = leaflet_src_default.a.latLng(latlngs.topleft);
this._topRight = leaflet_src_default.a.latLng(latlngs.topright);
this._bottomLeft = leaflet_src_default.a.latLng(latlngs.bottomleft);
leaflet_src_default.a.Util.setOptions(this, options);
},
onAdd: function onAdd(map) {
if (!this._image) {
this._initImage();
if (this.options.opacity < 1) {
this._updateOpacity();
}
}
if (this.options.interactive) {
leaflet_src_default.a.DomUtil.addClass(this._rawImage, 'leaflet-interactive');
this.addInteractiveTarget(this._rawImage);
}
map.on('zoomend resetview', this._reset, this);
this.getPane().appendChild(this._image);
this._reset();
},
onRemove: function onRemove(map) {
map.off('zoomend resetview', this._reset, this);
leaflet_src_default.a.ImageOverlay.prototype.onRemove.call(this, map);
},
_initImage: function _initImage() {
var img = this._rawImage;
if (this._url) {
img = leaflet_src_default.a.DomUtil.create('img');
img.style.display = 'none'; // Hide while the first transform (zero or one frames) is being done
if (this.options.crossOrigin) {
img.crossOrigin = '';
}
img.src = this._url;
this._rawImage = img;
}
leaflet_src_default.a.DomUtil.addClass(img, 'leaflet-image-layer'); // this._image is reused by some of the methods of the parent class and
// must keep the name, even if it is counter-intuitive.
this._image = leaflet_src_default.a.DomUtil.create('div', "leaflet-image-layer ".concat(this._zoomAnimated ? 'leaflet-zoom-animated' : ''));
var div = this._image;
this._updateZIndex(); // apply z-index style setting to the div (if defined)
div.appendChild(img);
div.onselectstart = leaflet_src_default.a.Util.falseFn;
div.onmousemove = leaflet_src_default.a.Util.falseFn;
img.onload = function () {
this._reset();
img.style.display = 'block';
this.fire('load');
}.bind(this);
img.alt = this.options.alt;
},
_reset: function _reset() {
var div = this._image; // Project control points to container-pixel coordinates
var pxTopLeft = this._map.latLngToLayerPoint(this._topLeft);
var pxTopRight = this._map.latLngToLayerPoint(this._topRight);
var pxBottomLeft = this._map.latLngToLayerPoint(this._bottomLeft); // Infer coordinate of bottom right
var pxBottomRight = pxTopRight.subtract(pxTopLeft).add(pxBottomLeft); // pxBounds is mostly for positioning the <div> container
var pxBounds = leaflet_src_default.a.bounds([pxTopLeft, pxTopRight, pxBottomLeft, pxBottomRight]);
var size = pxBounds.getSize();
var pxTopLeftInDiv = pxTopLeft.subtract(pxBounds.min); // Calculate the skew angles, both in X and Y
var vectorX = pxTopRight.subtract(pxTopLeft);
var vectorY = pxBottomLeft.subtract(pxTopLeft);
var skewX = Math.atan2(vectorX.y, vectorX.x);
var skewY = Math.atan2(vectorY.x, vectorY.y); // LatLngBounds used for animations
this._bounds = leaflet_src_default.a.latLngBounds(this._map.layerPointToLatLng(pxBounds.min), this._map.layerPointToLatLng(pxBounds.max));
leaflet_src_default.a.DomUtil.setPosition(div, pxBounds.min);
div.style.width = "".concat(size.x, "px");
div.style.height = "".concat(size.y, "px");
var imgW = this._rawImage.width;
var imgH = this._rawImage.height;
if (!imgW || !imgH) {
return; // Probably because the image hasn't loaded yet.
}
var scaleX = pxTopLeft.distanceTo(pxTopRight) / (imgW * Math.cos(skewX));
var scaleY = pxTopLeft.distanceTo(pxBottomLeft) / (imgH * Math.cos(skewY));
this._rawImage.style.transformOrigin = '0 0';
this._rawImage.style.transform = "translate(".concat(pxTopLeftInDiv.x, "px, ").concat(pxTopLeftInDiv.y, "px)") + "skew(".concat(skewY, "rad, ").concat(skewX, "rad) ") + "scale(".concat(scaleX, ", ").concat(scaleY, ") ");
},
reposition: function reposition(latlngs) {
this._topLeft = leaflet_src_default.a.latLng(latlngs.topleft);
this._topRight = leaflet_src_default.a.latLng(latlngs.topright);
this._bottomLeft = leaflet_src_default.a.latLng(latlngs.bottomleft);
this._reset();
},
setUrl: function setUrl(url) {
this._url = url;
if (this._rawImage) {
this._rawImage.src = url;
}
return this;
}
});
/* 🍂factory imageOverlay.rotated(imageUrl: String|HTMLImageElement|HTMLCanvasElement,
* latlngs: Object, options?: ImageOverlay options)
* Instantiates a rotated/skewed image overlay, given the image URL and
* the `LatLng`s of three of its corners.
*
* Alternatively to specifying the URL of the image, an existing instance of `HTMLImageElement`
* or `HTMLCanvasElement` can be used.
*/
leaflet_src_default.a.imageOverlay.rotated = function (imgSrc, latlngs, options) {
return new leaflet_src_default.a.ImageOverlay.Rotated(imgSrc, latlngs, options);
};
// CONCATENATED MODULE: ./node_modules/cache-loader/dist/cjs.js??ref--12-0!./node_modules/thread-loader/dist/cjs.js!./node_modules/babel-loader/lib!./node_modules/vue-loader/lib??vue-loader-options!./src/Vue2LeafletImageOverlayRotated.vue?vue&type=script&lang=js
var props = {
url: {
type: String
},
latlngs: {},
opacity: {
type: Number,
default: 1.0
},
alt: {
type: String,
default: ''
},
interactive: {
type: Boolean,
default: false
},
crossOrigin: {
type: Boolean,
default: false
},
errorOverlayUrl: {
type: String,
default: ''
},
visible: {
type: Boolean,
custom: true,
default: true
}
};
/* harmony default export */ var Vue2LeafletImageOverlayRotatedvue_type_script_lang_js = ({
name: 'LImageOverlayRotated',
props: props,
data: function data() {
return {
ready: false
};
},
mounted: function mounted() {
var options = {
opacity: this.opacity,
alt: this.alt,
interactive: this.interactive,
crossOrigin: this.crossOrigin,
errorOverlayUrl: this.errorOverlayUrl
};
this.mapObject = leaflet_src_default.a.imageOverlay.rotated(this.url, this.latlngs, options);
leaflet_src_default.a.DomEvent.on(this.mapObject, this.$listeners);
Object(vue2_leaflet_min["propsBinder"])(this, this.mapObject, props);
this.parentContainer = Object(vue2_leaflet_min["findRealParent"])(this.$parent);
this.parentContainer.addLayer(this, !this.visible);
},
beforeDestroy: function beforeDestroy() {
this.parentContainer.removeLayer(this);
},
methods: {
setVisible: function setVisible(newVal, oldVal) {
if (newVal === oldVal) return;
if (this.mapObject) {
if (newVal) {
this.parentContainer.addLayer(this);
} else {
this.parentContainer.removeLayer(this);
}
}
},
getBounds: function getBounds() {
return this.mapObject.getBounds();
}
},
render: function render() {
return null;
}
});
// CONCATENATED MODULE: ./src/Vue2LeafletImageOverlayRotated.vue?vue&type=script&lang=js
/* harmony default export */ var src_Vue2LeafletImageOverlayRotatedvue_type_script_lang_js = (Vue2LeafletImageOverlayRotatedvue_type_script_lang_js);
// CONCATENATED MODULE: ./node_modules/vue-loader/lib/runtime/componentNormalizer.js
/* globals __VUE_SSR_CONTEXT__ */
// IMPORTANT: Do NOT use ES2015 features in this file (except for modules).
// This module is a runtime utility for cleaner component module output and will
// be included in the final webpack user bundle.
function normalizeComponent (
scriptExports,
render,
staticRenderFns,
functionalTemplate,
injectStyles,
scopeId,
moduleIdentifier, /* server only */
shadowMode /* vue-cli only */
) {
// Vue.extend constructor export interop
var options = typeof scriptExports === 'function'
? scriptExports.options
: scriptExports
// render functions
if (render) {
options.render = render
options.staticRenderFns = staticRenderFns
options._compiled = true
}
// functional template
if (functionalTemplate) {
options.functional = true
}
// scopedId
if (scopeId) {
options._scopeId = 'data-v-' + scopeId
}
var hook
if (moduleIdentifier) { // server build
hook = function (context) {
// 2.3 injection
context =
context || // cached call
(this.$vnode && this.$vnode.ssrContext) || // stateful
(this.parent && this.parent.$vnode && this.parent.$vnode.ssrContext) // functional
// 2.2 with runInNewContext: true
if (!context && typeof __VUE_SSR_CONTEXT__ !== 'undefined') {
context = __VUE_SSR_CONTEXT__
}
// inject component styles
if (injectStyles) {
injectStyles.call(this, context)
}
// register component module identifier for async chunk inferrence
if (context && context._registeredComponents) {
context._registeredComponents.add(moduleIdentifier)
}
}
// used by ssr in case component is cached and beforeCreate
// never gets called
options._ssrRegister = hook
} else if (injectStyles) {
hook = shadowMode
? function () { injectStyles.call(this, this.$root.$options.shadowRoot) }
: injectStyles
}
if (hook) {
if (options.functional) {
// for template-only hot-reload because in that case the render fn doesn't
// go through the normalizer
options._injectStyles = hook
// register for functioal component in vue file
var originalRender = options.render
options.render = function renderWithStyleInjection (h, context) {
hook.call(context)
return originalRender(h, context)
}
} else {
// inject component registration as beforeCreate hook
var existing = options.beforeCreate
options.beforeCreate = existing
? [].concat(existing, hook)
: [hook]
}
}
return {
exports: scriptExports,
options: options
}
}
// CONCATENATED MODULE: ./src/Vue2LeafletImageOverlayRotated.vue
var render, staticRenderFns
/* normalize component */
var component = normalizeComponent(
src_Vue2LeafletImageOverlayRotatedvue_type_script_lang_js,
render,
staticRenderFns,
false,
null,
null,
null
)
/* harmony default export */ var Vue2LeafletImageOverlayRotated = (component.exports);
// CONCATENATED MODULE: ./node_modules/@vue/cli-service/lib/commands/build/entry-lib.js
/* harmony default export */ var entry_lib = __webpack_exports__["default"] = (Vue2LeafletImageOverlayRotated);
/***/ }),
/***/ "/e88":
/***/ (function(module, exports) {
module.exports = '\x09\x0A\x0B\x0C\x0D\x20\xA0\u1680\u180E\u2000\u2001\u2002\u2003' +
'\u2004\u2005\u2006\u2007\u2008\u2009\u200A\u202F\u205F\u3000\u2028\u2029\uFEFF';
/***/ }),
/***/ "0/R4":
/***/ (function(module, exports) {
module.exports = function (it) {
return typeof it === 'object' ? it !== null : typeof it === 'function';
};
/***/ }),
/***/ "2OiF":
/***/ (function(module, exports) {
module.exports = function (it) {
if (typeof it != 'function') throw TypeError(it + ' is not a function!');
return it;
};
/***/ }),
/***/ "2Spj":
/***/ (function(module, exports, __webpack_require__) {
// 19.2.3.2 / 15.3.4.5 Function.prototype.bind(thisArg, args...)
var $export = __webpack_require__("XKFU");
$export($export.P, 'Function', { bind: __webpack_require__("8MEG") });
/***/ }),
/***/ "4R4u":
/***/ (function(module, exports) {
// IE 8- don't enum bug keys
module.exports = (
'constructor,hasOwnProperty,isPrototypeOf,propertyIsEnumerable,toLocaleString,toString,valueOf'
).split(',');
/***/ }),
/***/ "4R65":
/***/ (function(module, exports, __webpack_require__) {
/* @preserve
* Leaflet 1.3.1, a JS library for interactive maps. http://leafletjs.com
* (c) 2010-2017 Vladimir Agafonkin, (c) 2010-2011 CloudMade
*/
(function (global, factory) {
true ? factory(exports) :
undefined;
}(this, (function (exports) { 'use strict';
var version = "1.3.1";
/*
* @namespace Util
*
* Various utility functions, used by Leaflet internally.
*/
var freeze = Object.freeze;
Object.freeze = function (obj) { return obj; };
// @function extend(dest: Object, src?: Object): Object
// Merges the properties of the `src` object (or multiple objects) into `dest` object and returns the latter. Has an `L.extend` shortcut.
function extend(dest) {
var i, j, len, src;
for (j = 1, len = arguments.length; j < len; j++) {
src = arguments[j];
for (i in src) {
dest[i] = src[i];
}
}
return dest;
}
// @function create(proto: Object, properties?: Object): Object
// Compatibility polyfill for [Object.create](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/create)
var create = Object.create || (function () {
function F() {}
return function (proto) {
F.prototype = proto;
return new F();
};
})();
// @function bind(fn: Function, …): Function
// Returns a new function bound to the arguments passed, like [Function.prototype.bind](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Function/bind).
// Has a `L.bind()` shortcut.
function bind(fn, obj) {
var slice = Array.prototype.slice;
if (fn.bind) {
return fn.bind.apply(fn, slice.call(arguments, 1));
}
var args = slice.call(arguments, 2);
return function () {
return fn.apply(obj, args.length ? args.concat(slice.call(arguments)) : arguments);
};
}
// @property lastId: Number
// Last unique ID used by [`stamp()`](#util-stamp)
var lastId = 0;
// @function stamp(obj: Object): Number
// Returns the unique ID of an object, assigning it one if it doesn't have it.
function stamp(obj) {
/*eslint-disable */
obj._leaflet_id = obj._leaflet_id || ++lastId;
return obj._leaflet_id;
/* eslint-enable */
}
// @function throttle(fn: Function, time: Number, context: Object): Function
// Returns a function which executes function `fn` with the given scope `context`
// (so that the `this` keyword refers to `context` inside `fn`'s code). The function
// `fn` will be called no more than one time per given amount of `time`. The arguments
// received by the bound function will be any arguments passed when binding the
// function, followed by any arguments passed when invoking the bound function.
// Has an `L.throttle` shortcut.
function throttle(fn, time, context) {
var lock, args, wrapperFn, later;
later = function () {
// reset lock and call if queued
lock = false;
if (args) {
wrapperFn.apply(context, args);
args = false;
}
};
wrapperFn = function () {
if (lock) {
// called too soon, queue to call later
args = arguments;
} else {
// call and lock until later
fn.apply(context, arguments);
setTimeout(later, time);
lock = true;
}
};
return wrapperFn;
}
// @function wrapNum(num: Number, range: Number[], includeMax?: Boolean): Number
// Returns the number `num` modulo `range` in such a way so it lies within
// `range[0]` and `range[1]`. The returned value will be always smaller than
// `range[1]` unless `includeMax` is set to `true`.
function wrapNum(x, range, includeMax) {
var max = range[1],
min = range[0],
d = max - min;
return x === max && includeMax ? x : ((x - min) % d + d) % d + min;
}
// @function falseFn(): Function
// Returns a function which always returns `false`.
function falseFn() { return false; }
// @function formatNum(num: Number, digits?: Number): Number
// Returns the number `num` rounded to `digits` decimals, or to 6 decimals by default.
function formatNum(num, digits) {
var pow = Math.pow(10, (digits === undefined ? 6 : digits));
return Math.round(num * pow) / pow;
}
// @function trim(str: String): String
// Compatibility polyfill for [String.prototype.trim](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String/Trim)
function trim(str) {
return str.trim ? str.trim() : str.replace(/^\s+|\s+$/g, '');
}
// @function splitWords(str: String): String[]
// Trims and splits the string on whitespace and returns the array of parts.
function splitWords(str) {
return trim(str).split(/\s+/);
}
// @function setOptions(obj: Object, options: Object): Object
// Merges the given properties to the `options` of the `obj` object, returning the resulting options. See `Class options`. Has an `L.setOptions` shortcut.
function setOptions(obj, options) {
if (!obj.hasOwnProperty('options')) {
obj.options = obj.options ? create(obj.options) : {};
}
for (var i in options) {
obj.options[i] = options[i];
}
return obj.options;
}
// @function getParamString(obj: Object, existingUrl?: String, uppercase?: Boolean): String
// Converts an object into a parameter URL string, e.g. `{a: "foo", b: "bar"}`
// translates to `'?a=foo&b=bar'`. If `existingUrl` is set, the parameters will
// be appended at the end. If `uppercase` is `true`, the parameter names will
// be uppercased (e.g. `'?A=foo&B=bar'`)
function getParamString(obj, existingUrl, uppercase) {
var params = [];
for (var i in obj) {
params.push(encodeURIComponent(uppercase ? i.toUpperCase() : i) + '=' + encodeURIComponent(obj[i]));
}
return ((!existingUrl || existingUrl.indexOf('?') === -1) ? '?' : '&') + params.join('&');
}
var templateRe = /\{ *([\w_-]+) *\}/g;
// @function template(str: String, data: Object): String
// Simple templating facility, accepts a template string of the form `'Hello {a}, {b}'`
// and a data object like `{a: 'foo', b: 'bar'}`, returns evaluated string
// `('Hello foo, bar')`. You can also specify functions instead of strings for
// data values — they will be evaluated passing `data` as an argument.
function template(str, data) {
return str.replace(templateRe, function (str, key) {
var value = data[key];
if (value === undefined) {
throw new Error('No value provided for variable ' + str);
} else if (typeof value === 'function') {
value = value(data);
}
return value;
});
}
// @function isArray(obj): Boolean
// Compatibility polyfill for [Array.isArray](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/isArray)
var isArray = Array.isArray || function (obj) {
return (Object.prototype.toString.call(obj) === '[object Array]');
};
// @function indexOf(array: Array, el: Object): Number
// Compatibility polyfill for [Array.prototype.indexOf](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/indexOf)
function indexOf(array, el) {
for (var i = 0; i < array.length; i++) {
if (array[i] === el) { return i; }
}
return -1;
}
// @property emptyImageUrl: String
// Data URI string containing a base64-encoded empty GIF image.
// Used as a hack to free memory from unused images on WebKit-powered
// mobile devices (by setting image `src` to this string).
var emptyImageUrl = 'data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs=';
// inspired by http://paulirish.com/2011/requestanimationframe-for-smart-animating/
function getPrefixed(name) {
return window['webkit' + name] || window['moz' + name] || window['ms' + name];
}
var lastTime = 0;
// fallback for IE 7-8
function timeoutDefer(fn) {
var time = +new Date(),
timeToCall = Math.max(0, 16 - (time - lastTime));
lastTime = time + timeToCall;
return window.setTimeout(fn, timeToCall);
}
var requestFn = window.requestAnimationFrame || getPrefixed('RequestAnimationFrame') || timeoutDefer;
var cancelFn = window.cancelAnimationFrame || getPrefixed('CancelAnimationFrame') ||
getPrefixed('CancelRequestAnimationFrame') || function (id) { window.clearTimeout(id); };
// @function requestAnimFrame(fn: Function, context?: Object, immediate?: Boolean): Number
// Schedules `fn` to be executed when the browser repaints. `fn` is bound to
// `context` if given. When `immediate` is set, `fn` is called immediately if
// the browser doesn't have native support for
// [`window.requestAnimationFrame`](https://developer.mozilla.org/docs/Web/API/window/requestAnimationFrame),
// otherwise it's delayed. Returns a request ID that can be used to cancel the request.
function requestAnimFrame(fn, context, immediate) {
if (immediate && requestFn === timeoutDefer) {
fn.call(context);
} else {
return requestFn.call(window, bind(fn, context));
}
}
// @function cancelAnimFrame(id: Number): undefined
// Cancels a previous `requestAnimFrame`. See also [window.cancelAnimationFrame](https://developer.mozilla.org/docs/Web/API/window/cancelAnimationFrame).
function cancelAnimFrame(id) {
if (id) {
cancelFn.call(window, id);
}
}
var Util = (Object.freeze || Object)({
freeze: freeze,
extend: extend,
create: create,
bind: bind,
lastId: lastId,
stamp: stamp,
throttle: throttle,
wrapNum: wrapNum,
falseFn: falseFn,
formatNum: formatNum,
trim: trim,
splitWords: splitWords,
setOptions: setOptions,
getParamString: getParamString,
template: template,
isArray: isArray,
indexOf: indexOf,
emptyImageUrl: emptyImageUrl,
requestFn: requestFn,
cancelFn: cancelFn,
requestAnimFrame: requestAnimFrame,
cancelAnimFrame: cancelAnimFrame
});
// @class Class
// @aka L.Class
// @section
// @uninheritable
// Thanks to John Resig and Dean Edwards for inspiration!
function Class() {}
Class.extend = function (props) {
// @function extend(props: Object): Function
// [Extends the current class](#class-inheritance) given the properties to be included.
// Returns a Javascript function that is a class constructor (to be called with `new`).
var NewClass = function () {
// call the constructor
if (this.initialize) {
this.initialize.apply(this, arguments);
}
// call all constructor hooks
this.callInitHooks();
};
var parentProto = NewClass.__super__ = this.prototype;
var proto = create(parentProto);
proto.constructor = NewClass;
NewClass.prototype = proto;
// inherit parent's statics
for (var i in this) {
if (this.hasOwnProperty(i) && i !== 'prototype' && i !== '__super__') {
NewClass[i] = this[i];
}
}
// mix static properties into the class
if (props.statics) {
extend(NewClass, props.statics);
delete props.statics;
}
// mix includes into the prototype
if (props.includes) {
checkDeprecatedMixinEvents(props.includes);
extend.apply(null, [proto].concat(props.includes));
delete props.includes;
}
// merge options
if (proto.options) {
props.options = extend(create(proto.options), props.options);
}
// mix given properties into the prototype
extend(proto, props);
proto._initHooks = [];
// add method for calling all hooks
proto.callInitHooks = function () {
if (this._initHooksCalled) { return; }
if (parentProto.callInitHooks) {
parentProto.callInitHooks.call(this);
}
this._initHooksCalled = true;
for (var i = 0, len = proto._initHooks.length; i < len; i++) {
proto._initHooks[i].call(this);
}
};
return NewClass;
};
// @function include(properties: Object): this
// [Includes a mixin](#class-includes) into the current class.
Class.include = function (props) {
extend(this.prototype, props);
return this;
};
// @function mergeOptions(options: Object): this
// [Merges `options`](#class-options) into the defaults of the class.
Class.mergeOptions = function (options) {
extend(this.prototype.options, options);
return this;
};
// @function addInitHook(fn: Function): this
// Adds a [constructor hook](#class-constructor-hooks) to the class.
Class.addInitHook = function (fn) { // (Function) || (String, args...)
var args = Array.prototype.slice.call(arguments, 1);
var init = typeof fn === 'function' ? fn : function () {
this[fn].apply(this, args);
};
this.prototype._initHooks = this.prototype._initHooks || [];
this.prototype._initHooks.push(init);
return this;
};
function checkDeprecatedMixinEvents(includes) {
if (typeof L === 'undefined' || !L || !L.Mixin) { return; }
includes = isArray(includes) ? includes : [includes];
for (var i = 0; i < includes.length; i++) {
if (includes[i] === L.Mixin.Events) {
console.warn('Deprecated include of L.Mixin.Events: ' +
'this property will be removed in future releases, ' +
'please inherit from L.Evented instead.', new Error().stack);
}
}
}
/*
* @class Evented
* @aka L.Evented
* @inherits Class
*
* A set of methods shared between event-powered classes (like `Map` and `Marker`). Generally, events allow you to execute some function when something happens with an object (e.g. the user clicks on the map, causing the map to fire `'click'` event).
*
* @example
*
* ```js
* map.on('click', function(e) {
* alert(e.latlng);
* } );
* ```
*
* Leaflet deals with event listeners by reference, so if you want to add a listener and then remove it, define it as a function:
*
* ```js
* function onClick(e) { ... }
*
* map.on('click', onClick);
* map.off('click', onClick);
* ```
*/
var Events = {
/* @method on(type: String, fn: Function, context?: Object): this
* Adds a listener function (`fn`) to a particular event type of the object. You can optionally specify the context of the listener (object the this keyword will point to). You can also pass several space-separated types (e.g. `'click dblclick'`).
*
* @alternative
* @method on(eventMap: Object): this
* Adds a set of type/listener pairs, e.g. `{click: onClick, mousemove: onMouseMove}`
*/
on: function (types, fn, context) {
// types can be a map of types/handlers
if (typeof types === 'object') {
for (var type in types) {
// we don't process space-separated events here for performance;
// it's a hot path since Layer uses the on(obj) syntax
this._on(type, types[type], fn);
}
} else {
// types can be a string of space-separated words
types = splitWords(types);
for (var i = 0, len = types.length; i < len; i++) {
this._on(types[i], fn, context);
}
}
return this;
},
/* @method off(type: String, fn?: Function, context?: Object): this
* Removes a previously added listener function. If no function is specified, it will remove all the listeners of that particular event from the object. Note that if you passed a custom context to `on`, you must pass the same context to `off` in order to remove the listener.
*
* @alternative
* @method off(eventMap: Object): this
* Removes a set of type/listener pairs.
*
* @alternative
* @method off: this
* Removes all listeners to all events on the object.
*/
off: function (types, fn, context) {
if (!types) {
// clear all listeners if called without arguments
delete this._events;
} else if (typeof types === 'object') {
for (var type in types) {
this._off(type, types[type], fn);
}
} else {
types = splitWords(types);
for (var i = 0, len = types.length; i < len; i++) {
this._off(types[i], fn, context);
}
}
return this;
},
// attach listener (without syntactic sugar now)
_on: function (type, fn, context) {
this._events = this._events || {};
/* get/init listeners for type */
var typeListeners = this._events[type];
if (!typeListeners) {
typeListeners = [];
this._events[type] = typeListeners;
}
if (context === this) {
// Less memory footprint.
context = undefined;
}
var newListener = {fn: fn, ctx: context},
listeners = typeListeners;
// check if fn already there
for (var i = 0, len = listeners.length; i < len; i++) {
if (listeners[i].fn === fn && listeners[i].ctx === context) {
return;
}
}
listeners.push(newListener);
},
_off: function (type, fn, context) {
var listeners,
i,
len;
if (!this._events) { return; }
listeners = this._events[type];
if (!listeners) {
return;
}
if (!fn) {
// Set all removed listeners to noop so they are not called if remove happens in fire
for (i = 0, len = listeners.length; i < len; i++) {
listeners[i].fn = falseFn;
}
// clear all listeners for a type if function isn't specified
delete this._events[type];
return;
}
if (context === this) {
context = undefined;
}
if (listeners) {
// find fn and remove it
for (i = 0, len = listeners.length; i < len; i++) {
var l = listeners[i];
if (l.ctx !== context) { continue; }
if (l.fn === fn) {
// set the removed listener to noop so that's not called if remove happens in fire
l.fn = falseFn;
if (this._firingCount) {
/* copy array in case events are being fired */
this._events[type] = listeners = listeners.slice();
}
listeners.splice(i, 1);
return;
}
}
}
},
// @method fire(type: String, data?: Object, propagate?: Boolean): this
// Fires an event of the specified type. You can optionally provide an data
// object — the first argument of the listener function will contain its
// properties. The event can optionally be propagated to event parents.
fire: function (type, data, propagate) {
if (!this.listens(type, propagate)) { return this; }
var event = extend({}, data, {
type: type,
target: this,
sourceTarget: data && data.sourceTarget || this
});
if (this._events) {
var listeners = this._events[type];
if (listeners) {
this._firingCount = (this._firingCount + 1) || 1;
for (var i = 0, len = listeners.length; i < len; i++) {
var l = listeners[i];
l.fn.call(l.ctx || this, event);
}
this._firingCount--;
}
}
if (propagate) {
// propagate the event to parents (set with addEventParent)
this._propagateEvent(event);
}
return this;
},
// @method listens(type: String): Boolean
// Returns `true` if a particular event type has any listeners attached to it.
listens: function (type, propagate) {
var listeners = this._events && this._events[type];
if (listeners && listeners.length) { return true; }
if (propagate) {
// also check parents for listeners if event propagates
for (var id in this._eventParents) {
if (this._eventParents[id].listens(type, propagate)) { return true; }
}
}
return false;
},
// @method once(…): this
// Behaves as [`on(…)`](#evented-on), except the listener will only get fired once and then removed.
once: function (types, fn, context) {
if (typeof types === 'object') {
for (var type in types) {
this.once(type, types[type], fn);
}
return this;
}
var handler = bind(function () {
this
.off(types, fn, context)
.off(types, handler, context);
}, this);
// add a listener that's executed once and removed after that
return this
.on(types, fn, context)
.on(types, handler, context);
},
// @method addEventParent(obj: Evented): this
// Adds an event parent - an `Evented` that will receive propagated events
addEventParent: function (obj) {
this._eventParents = this._eventParents || {};
this._eventParents[stamp(obj)] = obj;
return this;
},
// @method removeEventParent(obj: Evented): this
// Removes an event parent, so it will stop receiving propagated events
removeEventParent: function (obj) {
if (this._eventParents) {
delete this._eventParents[stamp(obj)];
}
return this;
},
_propagateEvent: function (e) {
for (var id in this._eventParents) {
this._eventParents[id].fire(e.type, extend({
layer: e.target,
propagatedFrom: e.target
}, e), true);
}
}
};
// aliases; we should ditch those eventually
// @method addEventListener(…): this
// Alias to [`on(…)`](#evented-on)
Events.addEventListener = Events.on;
// @method removeEventListener(…): this
// Alias to [`off(…)`](#evented-off)
// @method clearAllEventListeners(…): this
// Alias to [`off()`](#evented-off)
Events.removeEventListener = Events.clearAllEventListeners = Events.off;
// @method addOneTimeEventListener(…): this
// Alias to [`once(…)`](#evented-once)
Events.addOneTimeEventListener = Events.once;
// @method fireEvent(…): this
// Alias to [`fire(…)`](#evented-fire)
Events.fireEvent = Events.fire;
// @method hasEventListeners(…): Boolean
// Alias to [`listens(…)`](#evented-listens)
Events.hasEventListeners = Events.listens;
var Evented = Class.extend(Events);
/*
* @class Point
* @aka L.Point
*
* Represents a point with `x` and `y` coordinates in pixels.
*
* @example
*
* ```js
* var point = L.point(200, 300);
* ```
*
* All Leaflet methods and options that accept `Point` objects also accept them in a simple Array form (unless noted otherwise), so these lines are equivalent:
*
* ```js
* map.panBy([200, 300]);
* map.panBy(L.point(200, 300));
* ```
*
* Note that `Point` does not inherit from Leafet's `Class` object,
* which means new classes can't inherit from it, and new methods
* can't be added to it with the `include` function.
*/
function Point(x, y, round) {
// @property x: Number; The `x` coordinate of the point
this.x = (round ? Math.round(x) : x);
// @property y: Number; The `y` coordinate of the point
this.y = (round ? Math.round(y) : y);
}
var trunc = Math.trunc || function (v) {
return v > 0 ? Math.floor(v) : Math.ceil(v);
};
Point.prototype = {
// @method clone(): Point
// Returns a copy of the current point.
clone: function () {
return new Point(this.x, this.y);
},
// @method add(otherPoint: Point): Point
// Returns the result of addition of the current and the given points.
add: function (point) {
// non-destructive, returns a new point
return this.clone()._add(toPoint(point));
},
_add: function (point) {
// destructive, used directly for performance in situations where it's safe to modify existing point
this.x += point.x;
this.y += point.y;
return this;
},
// @method subtract(otherPoint: Point): Point
// Returns the result of subtraction of the given point from the current.
subtract: function (point) {
return this.clone()._subtract(toPoint(point));
},
_subtract: function (point) {
this.x -= point.x;
this.y -= point.y;
return this;
},
// @method divideBy(num: Number): Point
// Returns the result of division of the current point by the given number.
divideBy: function (num) {
return this.clone()._divideBy(num);
},
_divideBy: function (num) {
this.x /= num;
this.y /= num;
return this;
},
// @method multiplyBy(num: Number): Point
// Returns the result of multiplication of the current point by the given number.
multiplyBy: function (num) {
return this.clone()._multiplyBy(num);
},
_multiplyBy: function (num) {
this.x *= num;
this.y *= num;
return this;
},
// @method scaleBy(scale: Point): Point
// Multiply each coordinate of the current point by each coordinate of
// `scale`. In linear algebra terms, multiply the point by the
// [scaling matrix](https://en.wikipedia.org/wiki/Scaling_%28geometry%29#Matrix_representation)
// defined by `scale`.
scaleBy: function (point) {
return new Point(this.x * point.x, this.y * point.y);
},
// @method unscaleBy(scale: Point): Point
// Inverse of `scaleBy`. Divide each coordinate of the current point by
// each coordinate of `scale`.
unscaleBy: function (point) {
return new Point(this.x / point.x, this.y / point.y);
},
// @method round(): Point
// Returns a copy of the current point with rounded coordinates.
round: function () {
return this.clone()._round();
},
_round: function () {
this.x = Math.round(this.x);
this.y = Math.round(this.y);
return this;
},
// @method floor(): Point
// Returns a copy of the current point with floored coordinates (rounded down).
floor: function () {
return this.clone()._floor();
},
_floor: function () {
this.x = Math.floor(this.x);
this.y = Math.floor(this.y);
return this;
},
// @method ceil(): Point
// Returns a copy of the current point with ceiled coordinates (rounded up).
ceil: function () {
return this.clone()._ceil();
},
_ceil: function () {
this.x = Math.ceil(this.x);
this.y = Math.ceil(this.y);
return this;
},
// @method trunc(): Point
// Returns a copy of the current point with truncated coordinates (rounded towards zero).
trunc: function () {
return this.clone()._trunc();
},
_trunc: function () {
this.x = trunc(this.x);
this.y = trunc(this.y);
return this;
},
// @method distanceTo(otherPoint: Point): Number
// Returns the cartesian distance between the current and the given points.
distanceTo: function (point) {
point = toPoint(point);
var x = point.x - this.x,
y = point.y - this.y;
return Math.sqrt(x * x + y * y);
},
// @method equals(otherPoint: Point): Boolean
// Returns `true` if the given point has the same coordinates.
equals: function (point) {
point = toPoint(point);
return point.x === this.x &&
point.y === this.y;
},
// @method contains(otherPoint: Point): Boolean
// Returns `true` if both coordinates of the given point are less than the corresponding current point coordinates (in absolute values).
contains: function (point) {
point = toPoint(point);
return Math.abs(point.x) <= Math.abs(this.x) &&
Math.abs(point.y) <= Math.abs(this.y);
},
// @method toString(): String
// Returns a string representation of the point for debugging purposes.
toString: function () {
return 'Point(' +
formatNum(this.x) + ', ' +
formatNum(this.y) + ')';
}
};
// @factory L.point(x: Number, y: Number, round?: Boolean)
// Creates a Point object with the given `x` and `y` coordinates. If optional `round` is set to true, rounds the `x` and `y` values.
// @alternative
// @factory L.point(coords: Number[])
// Expects an array of the form `[x, y]` instead.
// @alternative
// @factory L.point(coords: Object)
// Expects a plain object of the form `{x: Number, y: Number}` instead.
function toPoint(x, y, round) {
if (x instanceof Point) {
return x;
}
if (isArray(x)) {
return new Point(x[0], x[1]);
}
if (x === undefined || x === null) {
return x;
}
if (typeof x === 'object' && 'x' in x && 'y' in x) {
return new Point(x.x, x.y);
}
return new Point(x, y, round);
}
/*
* @class Bounds
* @aka L.Bounds
*
* Represents a rectangular area in pixel coordinates.
*
* @example
*
* ```js
* var p1 = L.point(10, 10),
* p2 = L.point(40, 60),
* bounds = L.bounds(p1, p2);
* ```
*
* All Leaflet methods that accept `Bounds` objects also accept them in a simple Array form (unless noted otherwise), so the bounds example above can be passed like this:
*
* ```js
* otherBounds.intersects([[10, 10], [40, 60]]);
* ```
*
* Note that `Bounds` does not inherit from Leafet's `Class` object,
* which means new classes can't inherit from it, and new methods
* can't be added to it with the `include` function.
*/
function Bounds(a, b) {
if (!a) { return; }
var points = b ? [a, b] : a;
for (var i = 0, len = points.length; i < len; i++) {
this.extend(points[i]);
}
}
Bounds.prototype = {
// @method extend(point: Point): this
// Extends the bounds to contain the given point.
extend: function (point) { // (Point)
point = toPoint(point);
// @property min: Point
// The top left corner of the rectangle.
// @property max: Point
// The bottom right corner of the rectangle.
if (!this.min && !this.max) {
this.min = point.clone();
this.max = point.clone();
} else {
this.min.x = Math.min(point.x, this.min.x);
this.max.x = Math.max(point.x, this.max.x);
this.min.y = Math.min(point.y, this.min.y);
this.max.y = Math.max(point.y, this.max.y);
}
return this;
},
// @method getCenter(round?: Boolean): Point
// Returns the center point of the bounds.
getCenter: function (round) {
return new Point(
(this.min.x + this.max.x) / 2,
(this.min.y + this.max.y) / 2, round);
},
// @method getBottomLeft(): Point
// Returns the bottom-left point of the bounds.
getBottomLeft: function () {
return new Point(this.min.x, this.max.y);
},
// @method getTopRight(): Point
// Returns the top-right point of the bounds.
getTopRight: function () { // -> Point
return new Point(this.max.x, this.min.y);
},
// @method getTopLeft(): Point
// Returns the top-left point of the bounds (i.e. [`this.min`](#bounds-min)).
getTopLeft: function () {
return this.min; // left, top
},
// @method getBottomRight(): Point
// Returns the bottom-right point of the bounds (i.e. [`this.max`](#bounds-max)).
getBottomRight: function () {
return this.max; // right, bottom
},
// @method getSize(): Point
// Returns the size of the given bounds
getSize: function () {
return this.max.subtract(this.min);
},
// @method contains(otherBounds: Bounds): Boolean
// Returns `true` if the rectangle contains the given one.
// @alternative
// @method contains(point: Point): Boolean
// Returns `true` if the rectangle contains the given point.
contains: function (obj) {
var min, max;
if (typeof obj[0] === 'number' || obj instanceof Point) {
obj = toPoint(obj);
} else {
obj = toBounds(obj);
}
if (obj instanceof Bounds) {
min = obj.min;
max = obj.max;
} else {
min = max = obj;
}
return (min.x >= this.min.x) &&
(max.x <= this.max.x) &&
(min.y >= this.min.y) &&
(max.y <= this.max.y);
},
// @method intersects(otherBounds: Bounds): Boolean
// Returns `true` if the rectangle intersects the given bounds. Two bounds
// intersect if they have at least one point in common.
intersects: function (bounds) { // (Bounds) -> Boolean
bounds = toBounds(bounds);
var min = this.min,
max = this.max,
min2 = bounds.min,
max2 = bounds.max,
xIntersects = (max2.x >= min.x) && (min2.x <= max.x),
yIntersects = (max2.y >= min.y) && (min2.y <= max.y);
return xIntersects && yIntersects;
},
// @method overlaps(otherBounds: Bounds): Boolean
// Returns `true` if the rectangle overlaps the given bounds. Two bounds
// overlap if their intersection is an area.
overlaps: function (bounds) { // (Bounds) -> Boolean
bounds = toBounds(bounds);
var min = this.min,
max = this.max,
min2 = bounds.min,
max2 = bounds.max,
xOverlaps = (max2.x > min.x) && (min2.x < max.x),
yOverlaps = (max2.y > min.y) && (min2.y < max.y);
return xOverlaps && yOverlaps;
},
isValid: function () {
return !!(this.min && this.max);
}
};
// @factory L.bounds(corner1: Point, corner2: Point)
// Creates a Bounds object from two corners coordinate pairs.
// @alternative
// @factory L.bounds(points: Point[])
// Creates a Bounds object from the given array of points.
function toBounds(a, b) {
if (!a || a instanceof Bounds) {
return a;
}
return new Bounds(a, b);
}
/*
* @class LatLngBounds
* @aka L.LatLngBounds
*
* Represents a rectangular geographical area on a map.
*
* @example
*
* ```js
* var corner1 = L.latLng(40