UNPKG

@senx/discovery-plugin-marauder

Version:

Discovery plugin - Marauder's map

1,482 lines (1,254 loc) 477 kB
'use strict'; const utils = require('./utils-c9264fdc.js'); var domToImage = {exports: {}}; (function (module) { (function (global) { var util = newUtil(); var inliner = newInliner(); var fontFaces = newFontFaces(); var images = newImages(); // Default impl options var defaultOptions = { // Default is to fail on error, no placeholder imagePlaceholder: undefined, // Default cache bust is false, it will use the cache cacheBust: false }; var domtoimage = { toSvg: toSvg, toPng: toPng, toJpeg: toJpeg, toBlob: toBlob, toPixelData: toPixelData, impl: { fontFaces: fontFaces, images: images, util: util, inliner: inliner, options: {} } }; module.exports = domtoimage; /** * @param {Node} node - The DOM Node object to render * @param {Object} options - Rendering options * @param {Function} options.filter - Should return true if passed node should be included in the output * (excluding node means excluding it's children as well). Not called on the root node. * @param {String} options.bgcolor - color for the background, any valid CSS color value. * @param {Number} options.width - width to be applied to node before rendering. * @param {Number} options.height - height to be applied to node before rendering. * @param {Object} options.style - an object whose properties to be copied to node's style before rendering. * @param {Number} options.quality - a Number between 0 and 1 indicating image quality (applicable to JPEG only), defaults to 1.0. * @param {String} options.imagePlaceholder - dataURL to use as a placeholder for failed images, default behaviour is to fail fast on images we can't fetch * @param {Boolean} options.cacheBust - set to true to cache bust by appending the time to the request url * @return {Promise} - A promise that is fulfilled with a SVG image data URL * */ function toSvg(node, options) { options = options || {}; copyOptions(options); return Promise.resolve(node) .then(function (node) { return cloneNode(node, options.filter, true); }) .then(embedFonts) .then(inlineImages) .then(applyOptions) .then(function (clone) { return makeSvgDataUri(clone, options.width || util.width(node), options.height || util.height(node) ); }); function applyOptions(clone) { if (options.bgcolor) clone.style.backgroundColor = options.bgcolor; if (options.width) clone.style.width = options.width + 'px'; if (options.height) clone.style.height = options.height + 'px'; if (options.style) Object.keys(options.style).forEach(function (property) { clone.style[property] = options.style[property]; }); return clone; } } /** * @param {Node} node - The DOM Node object to render * @param {Object} options - Rendering options, @see {@link toSvg} * @return {Promise} - A promise that is fulfilled with a Uint8Array containing RGBA pixel data. * */ function toPixelData(node, options) { return draw(node, options || {}) .then(function (canvas) { return canvas.getContext('2d').getImageData( 0, 0, util.width(node), util.height(node) ).data; }); } /** * @param {Node} node - The DOM Node object to render * @param {Object} options - Rendering options, @see {@link toSvg} * @return {Promise} - A promise that is fulfilled with a PNG image data URL * */ function toPng(node, options) { return draw(node, options || {}) .then(function (canvas) { return canvas.toDataURL(); }); } /** * @param {Node} node - The DOM Node object to render * @param {Object} options - Rendering options, @see {@link toSvg} * @return {Promise} - A promise that is fulfilled with a JPEG image data URL * */ function toJpeg(node, options) { options = options || {}; return draw(node, options) .then(function (canvas) { return canvas.toDataURL('image/jpeg', options.quality || 1.0); }); } /** * @param {Node} node - The DOM Node object to render * @param {Object} options - Rendering options, @see {@link toSvg} * @return {Promise} - A promise that is fulfilled with a PNG image blob * */ function toBlob(node, options) { return draw(node, options || {}) .then(util.canvasToBlob); } function copyOptions(options) { // Copy options to impl options for use in impl if(typeof(options.imagePlaceholder) === 'undefined') { domtoimage.impl.options.imagePlaceholder = defaultOptions.imagePlaceholder; } else { domtoimage.impl.options.imagePlaceholder = options.imagePlaceholder; } if(typeof(options.cacheBust) === 'undefined') { domtoimage.impl.options.cacheBust = defaultOptions.cacheBust; } else { domtoimage.impl.options.cacheBust = options.cacheBust; } } function draw(domNode, options) { return toSvg(domNode, options) .then(util.makeImage) .then(util.delay(100)) .then(function (image) { var canvas = newCanvas(domNode); canvas.getContext('2d').drawImage(image, 0, 0); return canvas; }); function newCanvas(domNode) { var canvas = document.createElement('canvas'); canvas.width = options.width || util.width(domNode); canvas.height = options.height || util.height(domNode); if (options.bgcolor) { var ctx = canvas.getContext('2d'); ctx.fillStyle = options.bgcolor; ctx.fillRect(0, 0, canvas.width, canvas.height); } return canvas; } } function cloneNode(node, filter, root) { if (!root && filter && !filter(node)) return Promise.resolve(); return Promise.resolve(node) .then(makeNodeCopy) .then(function (clone) { return cloneChildren(node, clone, filter); }) .then(function (clone) { return processClone(node, clone); }); function makeNodeCopy(node) { if (node instanceof HTMLCanvasElement) return util.makeImage(node.toDataURL()); return node.cloneNode(false); } function cloneChildren(original, clone, filter) { var children = original.childNodes; if (children.length === 0) return Promise.resolve(clone); return cloneChildrenInOrder(clone, util.asArray(children), filter) .then(function () { return clone; }); function cloneChildrenInOrder(parent, children, filter) { var done = Promise.resolve(); children.forEach(function (child) { done = done .then(function () { return cloneNode(child, filter); }) .then(function (childClone) { if (childClone) parent.appendChild(childClone); }); }); return done; } } function processClone(original, clone) { if (!(clone instanceof Element)) return clone; return Promise.resolve() .then(cloneStyle) .then(clonePseudoElements) .then(copyUserInput) .then(fixSvg) .then(function () { return clone; }); function cloneStyle() { copyStyle(window.getComputedStyle(original), clone.style); function copyStyle(source, target) { if (source.cssText) target.cssText = source.cssText; else copyProperties(source, target); function copyProperties(source, target) { util.asArray(source).forEach(function (name) { target.setProperty( name, source.getPropertyValue(name), source.getPropertyPriority(name) ); }); } } } function clonePseudoElements() { [':before', ':after'].forEach(function (element) { clonePseudoElement(element); }); function clonePseudoElement(element) { var style = window.getComputedStyle(original, element); var content = style.getPropertyValue('content'); if (content === '' || content === 'none') return; var className = util.uid(); clone.className = clone.className + ' ' + className; var styleElement = document.createElement('style'); styleElement.appendChild(formatPseudoElementStyle(className, element, style)); clone.appendChild(styleElement); function formatPseudoElementStyle(className, element, style) { var selector = '.' + className + ':' + element; var cssText = style.cssText ? formatCssText(style) : formatCssProperties(style); return document.createTextNode(selector + '{' + cssText + '}'); function formatCssText(style) { var content = style.getPropertyValue('content'); return style.cssText + ' content: ' + content + ';'; } function formatCssProperties(style) { return util.asArray(style) .map(formatProperty) .join('; ') + ';'; function formatProperty(name) { return name + ': ' + style.getPropertyValue(name) + (style.getPropertyPriority(name) ? ' !important' : ''); } } } } } function copyUserInput() { if (original instanceof HTMLTextAreaElement) clone.innerHTML = original.value; if (original instanceof HTMLInputElement) clone.setAttribute("value", original.value); } function fixSvg() { if (!(clone instanceof SVGElement)) return; clone.setAttribute('xmlns', 'http://www.w3.org/2000/svg'); if (!(clone instanceof SVGRectElement)) return; ['width', 'height'].forEach(function (attribute) { var value = clone.getAttribute(attribute); if (!value) return; clone.style.setProperty(attribute, value); }); } } } function embedFonts(node) { return fontFaces.resolveAll() .then(function (cssText) { var styleNode = document.createElement('style'); node.appendChild(styleNode); styleNode.appendChild(document.createTextNode(cssText)); return node; }); } function inlineImages(node) { return images.inlineAll(node) .then(function () { return node; }); } function makeSvgDataUri(node, width, height) { return Promise.resolve(node) .then(function (node) { node.setAttribute('xmlns', 'http://www.w3.org/1999/xhtml'); return new XMLSerializer().serializeToString(node); }) .then(util.escapeXhtml) .then(function (xhtml) { return '<foreignObject x="0" y="0" width="100%" height="100%">' + xhtml + '</foreignObject>'; }) .then(function (foreignObject) { return '<svg xmlns="http://www.w3.org/2000/svg" width="' + width + '" height="' + height + '">' + foreignObject + '</svg>'; }) .then(function (svg) { return 'data:image/svg+xml;charset=utf-8,' + svg; }); } function newUtil() { return { escape: escape, parseExtension: parseExtension, mimeType: mimeType, dataAsUrl: dataAsUrl, isDataUrl: isDataUrl, canvasToBlob: canvasToBlob, resolveUrl: resolveUrl, getAndEncode: getAndEncode, uid: uid(), delay: delay, asArray: asArray, escapeXhtml: escapeXhtml, makeImage: makeImage, width: width, height: height }; function mimes() { /* * Only WOFF and EOT mime types for fonts are 'real' * see http://www.iana.org/assignments/media-types/media-types.xhtml */ var WOFF = 'application/font-woff'; var JPEG = 'image/jpeg'; return { 'woff': WOFF, 'woff2': WOFF, 'ttf': 'application/font-truetype', 'eot': 'application/vnd.ms-fontobject', 'png': 'image/png', 'jpg': JPEG, 'jpeg': JPEG, 'gif': 'image/gif', 'tiff': 'image/tiff', 'svg': 'image/svg+xml' }; } function parseExtension(url) { var match = /\.([^\.\/]*?)$/g.exec(url); if (match) return match[1]; else return ''; } function mimeType(url) { var extension = parseExtension(url).toLowerCase(); return mimes()[extension] || ''; } function isDataUrl(url) { return url.search(/^(data:)/) !== -1; } function toBlob(canvas) { return new Promise(function (resolve) { var binaryString = window.atob(canvas.toDataURL().split(',')[1]); var length = binaryString.length; var binaryArray = new Uint8Array(length); for (var i = 0; i < length; i++) binaryArray[i] = binaryString.charCodeAt(i); resolve(new Blob([binaryArray], { type: 'image/png' })); }); } function canvasToBlob(canvas) { if (canvas.toBlob) return new Promise(function (resolve) { canvas.toBlob(resolve); }); return toBlob(canvas); } function resolveUrl(url, baseUrl) { var doc = document.implementation.createHTMLDocument(); var base = doc.createElement('base'); doc.head.appendChild(base); var a = doc.createElement('a'); doc.body.appendChild(a); base.href = baseUrl; a.href = url; return a.href; } function uid() { var index = 0; return function () { return 'u' + fourRandomChars() + index++; function fourRandomChars() { /* see http://stackoverflow.com/a/6248722/2519373 */ return ('0000' + (Math.random() * Math.pow(36, 4) << 0).toString(36)).slice(-4); } }; } function makeImage(uri) { return new Promise(function (resolve, reject) { var image = new Image(); image.onload = function () { resolve(image); }; image.onerror = reject; image.src = uri; }); } function getAndEncode(url) { var TIMEOUT = 30000; if(domtoimage.impl.options.cacheBust) { // Cache bypass so we dont have CORS issues with cached images // Source: https://developer.mozilla.org/en/docs/Web/API/XMLHttpRequest/Using_XMLHttpRequest#Bypassing_the_cache url += ((/\?/).test(url) ? "&" : "?") + (new Date()).getTime(); } return new Promise(function (resolve) { var request = new XMLHttpRequest(); request.onreadystatechange = done; request.ontimeout = timeout; request.responseType = 'blob'; request.timeout = TIMEOUT; request.open('GET', url, true); request.send(); var placeholder; if(domtoimage.impl.options.imagePlaceholder) { var split = domtoimage.impl.options.imagePlaceholder.split(/,/); if(split && split[1]) { placeholder = split[1]; } } function done() { if (request.readyState !== 4) return; if (request.status !== 200) { if(placeholder) { resolve(placeholder); } else { fail('cannot fetch resource: ' + url + ', status: ' + request.status); } return; } var encoder = new FileReader(); encoder.onloadend = function () { var content = encoder.result.split(/,/)[1]; resolve(content); }; encoder.readAsDataURL(request.response); } function timeout() { if(placeholder) { resolve(placeholder); } else { fail('timeout of ' + TIMEOUT + 'ms occured while fetching resource: ' + url); } } function fail(message) { console.error(message); resolve(''); } }); } function dataAsUrl(content, type) { return 'data:' + type + ';base64,' + content; } function escape(string) { return string.replace(/([.*+?^${}()|\[\]\/\\])/g, '\\$1'); } function delay(ms) { return function (arg) { return new Promise(function (resolve) { setTimeout(function () { resolve(arg); }, ms); }); }; } function asArray(arrayLike) { var array = []; var length = arrayLike.length; for (var i = 0; i < length; i++) array.push(arrayLike[i]); return array; } function escapeXhtml(string) { return string.replace(/#/g, '%23').replace(/\n/g, '%0A'); } function width(node) { var leftBorder = px(node, 'border-left-width'); var rightBorder = px(node, 'border-right-width'); return node.scrollWidth + leftBorder + rightBorder; } function height(node) { var topBorder = px(node, 'border-top-width'); var bottomBorder = px(node, 'border-bottom-width'); return node.scrollHeight + topBorder + bottomBorder; } function px(node, styleProperty) { var value = window.getComputedStyle(node).getPropertyValue(styleProperty); return parseFloat(value.replace('px', '')); } } function newInliner() { var URL_REGEX = /url\(['"]?([^'"]+?)['"]?\)/g; return { inlineAll: inlineAll, shouldProcess: shouldProcess, impl: { readUrls: readUrls, inline: inline } }; function shouldProcess(string) { return string.search(URL_REGEX) !== -1; } function readUrls(string) { var result = []; var match; while ((match = URL_REGEX.exec(string)) !== null) { result.push(match[1]); } return result.filter(function (url) { return !util.isDataUrl(url); }); } function inline(string, url, baseUrl, get) { return Promise.resolve(url) .then(function (url) { return baseUrl ? util.resolveUrl(url, baseUrl) : url; }) .then(get || util.getAndEncode) .then(function (data) { return util.dataAsUrl(data, util.mimeType(url)); }) .then(function (dataUrl) { return string.replace(urlAsRegex(url), '$1' + dataUrl + '$3'); }); function urlAsRegex(url) { return new RegExp('(url\\([\'"]?)(' + util.escape(url) + ')([\'"]?\\))', 'g'); } } function inlineAll(string, baseUrl, get) { if (nothingToInline()) return Promise.resolve(string); return Promise.resolve(string) .then(readUrls) .then(function (urls) { var done = Promise.resolve(string); urls.forEach(function (url) { done = done.then(function (string) { return inline(string, url, baseUrl, get); }); }); return done; }); function nothingToInline() { return !shouldProcess(string); } } } function newFontFaces() { return { resolveAll: resolveAll, impl: { readAll: readAll } }; function resolveAll() { return readAll() .then(function (webFonts) { return Promise.all( webFonts.map(function (webFont) { return webFont.resolve(); }) ); }) .then(function (cssStrings) { return cssStrings.join('\n'); }); } function readAll() { return Promise.resolve(util.asArray(document.styleSheets)) .then(getCssRules) .then(selectWebFontRules) .then(function (rules) { return rules.map(newWebFont); }); function selectWebFontRules(cssRules) { return cssRules .filter(function (rule) { return rule.type === CSSRule.FONT_FACE_RULE; }) .filter(function (rule) { return inliner.shouldProcess(rule.style.getPropertyValue('src')); }); } function getCssRules(styleSheets) { var cssRules = []; styleSheets.forEach(function (sheet) { try { util.asArray(sheet.cssRules || []).forEach(cssRules.push.bind(cssRules)); } catch (e) { console.log('Error while reading CSS rules from ' + sheet.href, e.toString()); } }); return cssRules; } function newWebFont(webFontRule) { return { resolve: function resolve() { var baseUrl = (webFontRule.parentStyleSheet || {}).href; return inliner.inlineAll(webFontRule.cssText, baseUrl); }, src: function () { return webFontRule.style.getPropertyValue('src'); } }; } } } function newImages() { return { inlineAll: inlineAll, impl: { newImage: newImage } }; function newImage(element) { return { inline: inline }; function inline(get) { if (util.isDataUrl(element.src)) return Promise.resolve(); return Promise.resolve(element.src) .then(get || util.getAndEncode) .then(function (data) { return util.dataAsUrl(data, util.mimeType(element.src)); }) .then(function (dataUrl) { return new Promise(function (resolve, reject) { element.onload = resolve; element.onerror = reject; element.src = dataUrl; }); }); } } function inlineAll(node) { if (!(node instanceof Element)) return Promise.resolve(node); return inlineBackground(node) .then(function () { if (node instanceof HTMLImageElement) return newImage(node).inline(); else return Promise.all( util.asArray(node.childNodes).map(function (child) { return inlineAll(child); }) ); }); function inlineBackground(node) { var background = node.style.getPropertyValue('background'); if (!background) return Promise.resolve(node); return inliner.inlineAll(background) .then(function (inlined) { node.style.setProperty( 'background', inlined, node.style.getPropertyPriority('background') ); }) .then(function () { return node; }); } } } })(); }(domToImage)); const domtoimage = domToImage.exports; var leafletSrc = {exports: {}}; /* @preserve * Leaflet 1.9.4, a JS library for interactive maps. https://leafletjs.com * (c) 2010-2023 Vladimir Agafonkin, (c) 2010-2011 CloudMade */ (function (module, exports) { (function (global, factory) { factory(exports) ; })(utils.commonjsGlobal, (function (exports) { var version = "1.9.4"; /* * @namespace Util * * Various utility functions, used by Leaflet internally. */ // @function extend(dest: Object, src?: Object): Object // Merges the properties of the `src` object (or multiple objects) into `dest` object and returns the latter. Has an `L.extend` shortcut. function extend(dest) { var i, j, len, src; for (j = 1, len = arguments.length; j < len; j++) { src = arguments[j]; for (i in src) { dest[i] = src[i]; } } return dest; } // @function create(proto: Object, properties?: Object): Object // Compatibility polyfill for [Object.create](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/create) var create$2 = 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) { if (!('_leaflet_id' in obj)) { obj['_leaflet_id'] = ++lastId; } return obj._leaflet_id; } // @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, precision?: Number|false): Number // Returns the number `num` rounded with specified `precision`. // The default `precision` value is 6 decimal places. // `false` can be passed to skip any processing (can be useful to avoid round-off errors). function formatNum(num, precision) { if (precision === false) { return num; } var pow = Math.pow(10, precision === undefined ? 6 : precision); 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 (!Object.prototype.hasOwnProperty.call(obj, 'options')) { obj.options = obj.options ? create$2(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 https://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 = { __proto__: null, extend: extend, create: create$2, bind: bind, get lastId () { return 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 () { setOptions(this); // 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$2(parentProto); proto.constructor = NewClass; NewClass.prototype = proto; // inherit parent's statics for (var i in this) { if (Object.prototype.hasOwnProperty.call(this, i) && i !== 'prototype' && i !== '__super__') { NewClass[i] = this[i]; } } // mix static properties into the class if (props.statics) { extend(NewClass, props.statics); } // mix includes into the prototype if (props.includes) { checkDeprecatedMixinEvents(props.includes); extend.apply(null, [proto].concat(props.includes)); } // mix given properties into the prototype extend(proto, props); delete proto.statics; delete proto.includes; // merge options if (proto.options) { proto.options = parentProto.options ? create$2(parentProto.options) : {}; extend(proto.options, props.options); } 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) { var parentOptions = this.prototype.options; extend(this.prototype, props); if (props.options) { this.prototype.options = parentOptions; this.mergeOptions(props.options); } 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) { /* global L: true */ 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. This includes implicitly attached events. */ off: function (types, fn, context) { if (!arguments.length) { // 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); var removeAll = arguments.length === 1; for (var i = 0, len = types.length; i < len; i++) { if (removeAll) { this._off(types[i]); } else { this._off(types[i], fn, context); } } } return this; }, // attach listener (without syntactic sugar now) _on: function (type, fn, context, _once) { if (typeof fn !== 'function') { console.warn('wrong listener type: ' + typeof fn); return; } // check if fn already there if (this._listens(type, fn, context) !== false) { return; } if (context === this) { // Less memory footprint. context = undefined; } var newListener = {fn: fn, ctx: context}; if (_once) { newListener.once = true; } this._events = this._events || {}; this._events[type] = this._events[type] || []; this._events[type].push(newListener); }, _off: function (type, fn, context) { var listeners, i, len; if (!this._events) { return; } listeners = this._events[type]; if (!listeners) { return; } if (arguments.length === 1) { // remove all if (this._firingCount) { // 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 (typeof fn !== 'function') { console.warn('wrong listener type: ' + typeof fn); return; } // find fn and remove it var index = this._listens(type, fn, context); if (index !== false) { var listener = listeners[index]; if (this._firingCount) { // set the removed listener to noop so that's not called if remove happens in fire listener.fn = falseFn; /* copy array in case events are being fired */ this._events[type] = listeners = listeners.slice(); } listeners.splice(index, 1); } }, // @method fire(type: String, data?: Object, propagate?: Boolean): this // Fires an event of the specified type. You can optionally provide a 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]; // off overwrites l.fn, so we need to copy fn to a var var fn = l.fn; if (l.once) { this.off(type, fn, l.ctx); } 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, propagate?: Boolean): Boolean // @method listens(type: String, fn: Function, context?: Object, propagate?: Boolean): Boolean // Returns `true` if a particular event type has any listeners attached to it. // The verification can optionally be propagated, it will return `true` if parents have the listener attached to it. listens: function (type, fn, context, propagate) { if (typeof type !== 'string') { console.warn('"string" type argument expected'); } // we don't overwrite the input `fn` value, because we need to use it for propagation var _fn = fn; if (typeof fn !== 'function') { propagate = !!fn; _fn = undefined; context = undefined; } var listeners = this._events && this._events[type]; if (listeners && listeners.length) { if (this._listens(type, _fn, context) !== false) { return true; } } if (propagate) { // also check parents for listeners if event propagates for (var id in this._eventParents) { if (this._eventParents[id].listens(type, fn, context, propagate)) { return true; } } } return false; }, // returns the index (number) or false _listens: function (type, fn, context) { if (!this._events) { return false; } var listeners = this._events[type] || []; if (!fn) { return !!listeners.length; } if (context === this) { // Less memory footprint. context = undefined; } for (var i = 0, len = listeners.length; i < len; i++) { if (listeners[i].fn === fn && listeners[i].ctx === context) { return i; } } 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) { // 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, true); } } 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, true); } } return this; }, // @method addEventParent(obj: Evented): this // Adds an event parent - an `Evented` that will receive propagated events addEventParent: function (obj) { this._eventParents = this._eventParents |