Proof of concept for the other one
at main 10779 lines 435 kB view raw
1var __create = Object.create; 2var __defProp = Object.defineProperty; 3var __getOwnPropDesc = Object.getOwnPropertyDescriptor; 4var __getOwnPropNames = Object.getOwnPropertyNames; 5var __getProtoOf = Object.getPrototypeOf; 6var __hasOwnProp = Object.prototype.hasOwnProperty; 7var __commonJS = (cb, mod) => function __require() { 8 return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports; 9}; 10var __export = (target, all) => { 11 for (var name in all) 12 __defProp(target, name, { get: all[name], enumerable: true }); 13}; 14var __copyProps = (to, from, except, desc) => { 15 if (from && typeof from === "object" || typeof from === "function") { 16 for (let key of __getOwnPropNames(from)) 17 if (!__hasOwnProp.call(to, key) && key !== except) 18 __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); 19 } 20 return to; 21}; 22var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( 23 // If the importer is in node compatibility mode or this is not an ESM 24 // file that has been converted to a CommonJS file using a Babel- 25 // compatible transform (i.e. "__esModule" has not been set), then set 26 // "default" to the CommonJS "module.exports" for node compatibility. 27 isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, 28 mod 29)); 30var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); 31 32// node_modules/leaflet/dist/leaflet-src.js 33var require_leaflet_src = __commonJS({ 34 "node_modules/leaflet/dist/leaflet-src.js"(exports, module2) { 35 (function(global, factory) { 36 typeof exports === "object" && typeof module2 !== "undefined" ? factory(exports) : typeof define === "function" && define.amd ? define(["exports"], factory) : (global = typeof globalThis !== "undefined" ? globalThis : global || self, factory(global.leaflet = {})); 37 })(exports, function(exports2) { 38 "use strict"; 39 var version = "1.9.4"; 40 function extend(dest) { 41 var i, j, len, src; 42 for (j = 1, len = arguments.length; j < len; j++) { 43 src = arguments[j]; 44 for (i in src) { 45 dest[i] = src[i]; 46 } 47 } 48 return dest; 49 } 50 var create$2 = Object.create || /* @__PURE__ */ function() { 51 function F() { 52 } 53 return function(proto) { 54 F.prototype = proto; 55 return new F(); 56 }; 57 }(); 58 function bind(fn, obj) { 59 var slice = Array.prototype.slice; 60 if (fn.bind) { 61 return fn.bind.apply(fn, slice.call(arguments, 1)); 62 } 63 var args = slice.call(arguments, 2); 64 return function() { 65 return fn.apply(obj, args.length ? args.concat(slice.call(arguments)) : arguments); 66 }; 67 } 68 var lastId = 0; 69 function stamp(obj) { 70 if (!("_leaflet_id" in obj)) { 71 obj["_leaflet_id"] = ++lastId; 72 } 73 return obj._leaflet_id; 74 } 75 function throttle(fn, time, context) { 76 var lock, args, wrapperFn, later; 77 later = function() { 78 lock = false; 79 if (args) { 80 wrapperFn.apply(context, args); 81 args = false; 82 } 83 }; 84 wrapperFn = function() { 85 if (lock) { 86 args = arguments; 87 } else { 88 fn.apply(context, arguments); 89 setTimeout(later, time); 90 lock = true; 91 } 92 }; 93 return wrapperFn; 94 } 95 function wrapNum(x, range, includeMax) { 96 var max = range[1], min = range[0], d = max - min; 97 return x === max && includeMax ? x : ((x - min) % d + d) % d + min; 98 } 99 function falseFn() { 100 return false; 101 } 102 function formatNum(num, precision) { 103 if (precision === false) { 104 return num; 105 } 106 var pow = Math.pow(10, precision === void 0 ? 6 : precision); 107 return Math.round(num * pow) / pow; 108 } 109 function trim(str) { 110 return str.trim ? str.trim() : str.replace(/^\s+|\s+$/g, ""); 111 } 112 function splitWords(str) { 113 return trim(str).split(/\s+/); 114 } 115 function setOptions(obj, options) { 116 if (!Object.prototype.hasOwnProperty.call(obj, "options")) { 117 obj.options = obj.options ? create$2(obj.options) : {}; 118 } 119 for (var i in options) { 120 obj.options[i] = options[i]; 121 } 122 return obj.options; 123 } 124 function getParamString(obj, existingUrl, uppercase) { 125 var params = []; 126 for (var i in obj) { 127 params.push(encodeURIComponent(uppercase ? i.toUpperCase() : i) + "=" + encodeURIComponent(obj[i])); 128 } 129 return (!existingUrl || existingUrl.indexOf("?") === -1 ? "?" : "&") + params.join("&"); 130 } 131 var templateRe = /\{ *([\w_ -]+) *\}/g; 132 function template(str, data) { 133 return str.replace(templateRe, function(str2, key) { 134 var value = data[key]; 135 if (value === void 0) { 136 throw new Error("No value provided for variable " + str2); 137 } else if (typeof value === "function") { 138 value = value(data); 139 } 140 return value; 141 }); 142 } 143 var isArray = Array.isArray || function(obj) { 144 return Object.prototype.toString.call(obj) === "[object Array]"; 145 }; 146 function indexOf(array, el) { 147 for (var i = 0; i < array.length; i++) { 148 if (array[i] === el) { 149 return i; 150 } 151 } 152 return -1; 153 } 154 var emptyImageUrl = "data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs="; 155 function getPrefixed(name) { 156 return window["webkit" + name] || window["moz" + name] || window["ms" + name]; 157 } 158 var lastTime = 0; 159 function timeoutDefer(fn) { 160 var time = +/* @__PURE__ */ new Date(), timeToCall = Math.max(0, 16 - (time - lastTime)); 161 lastTime = time + timeToCall; 162 return window.setTimeout(fn, timeToCall); 163 } 164 var requestFn = window.requestAnimationFrame || getPrefixed("RequestAnimationFrame") || timeoutDefer; 165 var cancelFn = window.cancelAnimationFrame || getPrefixed("CancelAnimationFrame") || getPrefixed("CancelRequestAnimationFrame") || function(id) { 166 window.clearTimeout(id); 167 }; 168 function requestAnimFrame(fn, context, immediate) { 169 if (immediate && requestFn === timeoutDefer) { 170 fn.call(context); 171 } else { 172 return requestFn.call(window, bind(fn, context)); 173 } 174 } 175 function cancelAnimFrame(id) { 176 if (id) { 177 cancelFn.call(window, id); 178 } 179 } 180 var Util = { 181 __proto__: null, 182 extend, 183 create: create$2, 184 bind, 185 get lastId() { 186 return lastId; 187 }, 188 stamp, 189 throttle, 190 wrapNum, 191 falseFn, 192 formatNum, 193 trim, 194 splitWords, 195 setOptions, 196 getParamString, 197 template, 198 isArray, 199 indexOf, 200 emptyImageUrl, 201 requestFn, 202 cancelFn, 203 requestAnimFrame, 204 cancelAnimFrame 205 }; 206 function Class() { 207 } 208 Class.extend = function(props) { 209 var NewClass = function() { 210 setOptions(this); 211 if (this.initialize) { 212 this.initialize.apply(this, arguments); 213 } 214 this.callInitHooks(); 215 }; 216 var parentProto = NewClass.__super__ = this.prototype; 217 var proto = create$2(parentProto); 218 proto.constructor = NewClass; 219 NewClass.prototype = proto; 220 for (var i in this) { 221 if (Object.prototype.hasOwnProperty.call(this, i) && i !== "prototype" && i !== "__super__") { 222 NewClass[i] = this[i]; 223 } 224 } 225 if (props.statics) { 226 extend(NewClass, props.statics); 227 } 228 if (props.includes) { 229 checkDeprecatedMixinEvents(props.includes); 230 extend.apply(null, [proto].concat(props.includes)); 231 } 232 extend(proto, props); 233 delete proto.statics; 234 delete proto.includes; 235 if (proto.options) { 236 proto.options = parentProto.options ? create$2(parentProto.options) : {}; 237 extend(proto.options, props.options); 238 } 239 proto._initHooks = []; 240 proto.callInitHooks = function() { 241 if (this._initHooksCalled) { 242 return; 243 } 244 if (parentProto.callInitHooks) { 245 parentProto.callInitHooks.call(this); 246 } 247 this._initHooksCalled = true; 248 for (var i2 = 0, len = proto._initHooks.length; i2 < len; i2++) { 249 proto._initHooks[i2].call(this); 250 } 251 }; 252 return NewClass; 253 }; 254 Class.include = function(props) { 255 var parentOptions = this.prototype.options; 256 extend(this.prototype, props); 257 if (props.options) { 258 this.prototype.options = parentOptions; 259 this.mergeOptions(props.options); 260 } 261 return this; 262 }; 263 Class.mergeOptions = function(options) { 264 extend(this.prototype.options, options); 265 return this; 266 }; 267 Class.addInitHook = function(fn) { 268 var args = Array.prototype.slice.call(arguments, 1); 269 var init = typeof fn === "function" ? fn : function() { 270 this[fn].apply(this, args); 271 }; 272 this.prototype._initHooks = this.prototype._initHooks || []; 273 this.prototype._initHooks.push(init); 274 return this; 275 }; 276 function checkDeprecatedMixinEvents(includes) { 277 if (typeof L === "undefined" || !L || !L.Mixin) { 278 return; 279 } 280 includes = isArray(includes) ? includes : [includes]; 281 for (var i = 0; i < includes.length; i++) { 282 if (includes[i] === L.Mixin.Events) { 283 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); 284 } 285 } 286 } 287 var Events = { 288 /* @method on(type: String, fn: Function, context?: Object): this 289 * 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'`). 290 * 291 * @alternative 292 * @method on(eventMap: Object): this 293 * Adds a set of type/listener pairs, e.g. `{click: onClick, mousemove: onMouseMove}` 294 */ 295 on: function(types, fn, context) { 296 if (typeof types === "object") { 297 for (var type in types) { 298 this._on(type, types[type], fn); 299 } 300 } else { 301 types = splitWords(types); 302 for (var i = 0, len = types.length; i < len; i++) { 303 this._on(types[i], fn, context); 304 } 305 } 306 return this; 307 }, 308 /* @method off(type: String, fn?: Function, context?: Object): this 309 * 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. 310 * 311 * @alternative 312 * @method off(eventMap: Object): this 313 * Removes a set of type/listener pairs. 314 * 315 * @alternative 316 * @method off: this 317 * Removes all listeners to all events on the object. This includes implicitly attached events. 318 */ 319 off: function(types, fn, context) { 320 if (!arguments.length) { 321 delete this._events; 322 } else if (typeof types === "object") { 323 for (var type in types) { 324 this._off(type, types[type], fn); 325 } 326 } else { 327 types = splitWords(types); 328 var removeAll = arguments.length === 1; 329 for (var i = 0, len = types.length; i < len; i++) { 330 if (removeAll) { 331 this._off(types[i]); 332 } else { 333 this._off(types[i], fn, context); 334 } 335 } 336 } 337 return this; 338 }, 339 // attach listener (without syntactic sugar now) 340 _on: function(type, fn, context, _once) { 341 if (typeof fn !== "function") { 342 console.warn("wrong listener type: " + typeof fn); 343 return; 344 } 345 if (this._listens(type, fn, context) !== false) { 346 return; 347 } 348 if (context === this) { 349 context = void 0; 350 } 351 var newListener = { fn, ctx: context }; 352 if (_once) { 353 newListener.once = true; 354 } 355 this._events = this._events || {}; 356 this._events[type] = this._events[type] || []; 357 this._events[type].push(newListener); 358 }, 359 _off: function(type, fn, context) { 360 var listeners, i, len; 361 if (!this._events) { 362 return; 363 } 364 listeners = this._events[type]; 365 if (!listeners) { 366 return; 367 } 368 if (arguments.length === 1) { 369 if (this._firingCount) { 370 for (i = 0, len = listeners.length; i < len; i++) { 371 listeners[i].fn = falseFn; 372 } 373 } 374 delete this._events[type]; 375 return; 376 } 377 if (typeof fn !== "function") { 378 console.warn("wrong listener type: " + typeof fn); 379 return; 380 } 381 var index2 = this._listens(type, fn, context); 382 if (index2 !== false) { 383 var listener = listeners[index2]; 384 if (this._firingCount) { 385 listener.fn = falseFn; 386 this._events[type] = listeners = listeners.slice(); 387 } 388 listeners.splice(index2, 1); 389 } 390 }, 391 // @method fire(type: String, data?: Object, propagate?: Boolean): this 392 // Fires an event of the specified type. You can optionally provide a data 393 // object — the first argument of the listener function will contain its 394 // properties. The event can optionally be propagated to event parents. 395 fire: function(type, data, propagate) { 396 if (!this.listens(type, propagate)) { 397 return this; 398 } 399 var event = extend({}, data, { 400 type, 401 target: this, 402 sourceTarget: data && data.sourceTarget || this 403 }); 404 if (this._events) { 405 var listeners = this._events[type]; 406 if (listeners) { 407 this._firingCount = this._firingCount + 1 || 1; 408 for (var i = 0, len = listeners.length; i < len; i++) { 409 var l = listeners[i]; 410 var fn = l.fn; 411 if (l.once) { 412 this.off(type, fn, l.ctx); 413 } 414 fn.call(l.ctx || this, event); 415 } 416 this._firingCount--; 417 } 418 } 419 if (propagate) { 420 this._propagateEvent(event); 421 } 422 return this; 423 }, 424 // @method listens(type: String, propagate?: Boolean): Boolean 425 // @method listens(type: String, fn: Function, context?: Object, propagate?: Boolean): Boolean 426 // Returns `true` if a particular event type has any listeners attached to it. 427 // The verification can optionally be propagated, it will return `true` if parents have the listener attached to it. 428 listens: function(type, fn, context, propagate) { 429 if (typeof type !== "string") { 430 console.warn('"string" type argument expected'); 431 } 432 var _fn = fn; 433 if (typeof fn !== "function") { 434 propagate = !!fn; 435 _fn = void 0; 436 context = void 0; 437 } 438 var listeners = this._events && this._events[type]; 439 if (listeners && listeners.length) { 440 if (this._listens(type, _fn, context) !== false) { 441 return true; 442 } 443 } 444 if (propagate) { 445 for (var id in this._eventParents) { 446 if (this._eventParents[id].listens(type, fn, context, propagate)) { 447 return true; 448 } 449 } 450 } 451 return false; 452 }, 453 // returns the index (number) or false 454 _listens: function(type, fn, context) { 455 if (!this._events) { 456 return false; 457 } 458 var listeners = this._events[type] || []; 459 if (!fn) { 460 return !!listeners.length; 461 } 462 if (context === this) { 463 context = void 0; 464 } 465 for (var i = 0, len = listeners.length; i < len; i++) { 466 if (listeners[i].fn === fn && listeners[i].ctx === context) { 467 return i; 468 } 469 } 470 return false; 471 }, 472 // @method once(…): this 473 // Behaves as [`on(…)`](#evented-on), except the listener will only get fired once and then removed. 474 once: function(types, fn, context) { 475 if (typeof types === "object") { 476 for (var type in types) { 477 this._on(type, types[type], fn, true); 478 } 479 } else { 480 types = splitWords(types); 481 for (var i = 0, len = types.length; i < len; i++) { 482 this._on(types[i], fn, context, true); 483 } 484 } 485 return this; 486 }, 487 // @method addEventParent(obj: Evented): this 488 // Adds an event parent - an `Evented` that will receive propagated events 489 addEventParent: function(obj) { 490 this._eventParents = this._eventParents || {}; 491 this._eventParents[stamp(obj)] = obj; 492 return this; 493 }, 494 // @method removeEventParent(obj: Evented): this 495 // Removes an event parent, so it will stop receiving propagated events 496 removeEventParent: function(obj) { 497 if (this._eventParents) { 498 delete this._eventParents[stamp(obj)]; 499 } 500 return this; 501 }, 502 _propagateEvent: function(e) { 503 for (var id in this._eventParents) { 504 this._eventParents[id].fire(e.type, extend({ 505 layer: e.target, 506 propagatedFrom: e.target 507 }, e), true); 508 } 509 } 510 }; 511 Events.addEventListener = Events.on; 512 Events.removeEventListener = Events.clearAllEventListeners = Events.off; 513 Events.addOneTimeEventListener = Events.once; 514 Events.fireEvent = Events.fire; 515 Events.hasEventListeners = Events.listens; 516 var Evented = Class.extend(Events); 517 function Point(x, y, round) { 518 this.x = round ? Math.round(x) : x; 519 this.y = round ? Math.round(y) : y; 520 } 521 var trunc = Math.trunc || function(v) { 522 return v > 0 ? Math.floor(v) : Math.ceil(v); 523 }; 524 Point.prototype = { 525 // @method clone(): Point 526 // Returns a copy of the current point. 527 clone: function() { 528 return new Point(this.x, this.y); 529 }, 530 // @method add(otherPoint: Point): Point 531 // Returns the result of addition of the current and the given points. 532 add: function(point) { 533 return this.clone()._add(toPoint(point)); 534 }, 535 _add: function(point) { 536 this.x += point.x; 537 this.y += point.y; 538 return this; 539 }, 540 // @method subtract(otherPoint: Point): Point 541 // Returns the result of subtraction of the given point from the current. 542 subtract: function(point) { 543 return this.clone()._subtract(toPoint(point)); 544 }, 545 _subtract: function(point) { 546 this.x -= point.x; 547 this.y -= point.y; 548 return this; 549 }, 550 // @method divideBy(num: Number): Point 551 // Returns the result of division of the current point by the given number. 552 divideBy: function(num) { 553 return this.clone()._divideBy(num); 554 }, 555 _divideBy: function(num) { 556 this.x /= num; 557 this.y /= num; 558 return this; 559 }, 560 // @method multiplyBy(num: Number): Point 561 // Returns the result of multiplication of the current point by the given number. 562 multiplyBy: function(num) { 563 return this.clone()._multiplyBy(num); 564 }, 565 _multiplyBy: function(num) { 566 this.x *= num; 567 this.y *= num; 568 return this; 569 }, 570 // @method scaleBy(scale: Point): Point 571 // Multiply each coordinate of the current point by each coordinate of 572 // `scale`. In linear algebra terms, multiply the point by the 573 // [scaling matrix](https://en.wikipedia.org/wiki/Scaling_%28geometry%29#Matrix_representation) 574 // defined by `scale`. 575 scaleBy: function(point) { 576 return new Point(this.x * point.x, this.y * point.y); 577 }, 578 // @method unscaleBy(scale: Point): Point 579 // Inverse of `scaleBy`. Divide each coordinate of the current point by 580 // each coordinate of `scale`. 581 unscaleBy: function(point) { 582 return new Point(this.x / point.x, this.y / point.y); 583 }, 584 // @method round(): Point 585 // Returns a copy of the current point with rounded coordinates. 586 round: function() { 587 return this.clone()._round(); 588 }, 589 _round: function() { 590 this.x = Math.round(this.x); 591 this.y = Math.round(this.y); 592 return this; 593 }, 594 // @method floor(): Point 595 // Returns a copy of the current point with floored coordinates (rounded down). 596 floor: function() { 597 return this.clone()._floor(); 598 }, 599 _floor: function() { 600 this.x = Math.floor(this.x); 601 this.y = Math.floor(this.y); 602 return this; 603 }, 604 // @method ceil(): Point 605 // Returns a copy of the current point with ceiled coordinates (rounded up). 606 ceil: function() { 607 return this.clone()._ceil(); 608 }, 609 _ceil: function() { 610 this.x = Math.ceil(this.x); 611 this.y = Math.ceil(this.y); 612 return this; 613 }, 614 // @method trunc(): Point 615 // Returns a copy of the current point with truncated coordinates (rounded towards zero). 616 trunc: function() { 617 return this.clone()._trunc(); 618 }, 619 _trunc: function() { 620 this.x = trunc(this.x); 621 this.y = trunc(this.y); 622 return this; 623 }, 624 // @method distanceTo(otherPoint: Point): Number 625 // Returns the cartesian distance between the current and the given points. 626 distanceTo: function(point) { 627 point = toPoint(point); 628 var x = point.x - this.x, y = point.y - this.y; 629 return Math.sqrt(x * x + y * y); 630 }, 631 // @method equals(otherPoint: Point): Boolean 632 // Returns `true` if the given point has the same coordinates. 633 equals: function(point) { 634 point = toPoint(point); 635 return point.x === this.x && point.y === this.y; 636 }, 637 // @method contains(otherPoint: Point): Boolean 638 // Returns `true` if both coordinates of the given point are less than the corresponding current point coordinates (in absolute values). 639 contains: function(point) { 640 point = toPoint(point); 641 return Math.abs(point.x) <= Math.abs(this.x) && Math.abs(point.y) <= Math.abs(this.y); 642 }, 643 // @method toString(): String 644 // Returns a string representation of the point for debugging purposes. 645 toString: function() { 646 return "Point(" + formatNum(this.x) + ", " + formatNum(this.y) + ")"; 647 } 648 }; 649 function toPoint(x, y, round) { 650 if (x instanceof Point) { 651 return x; 652 } 653 if (isArray(x)) { 654 return new Point(x[0], x[1]); 655 } 656 if (x === void 0 || x === null) { 657 return x; 658 } 659 if (typeof x === "object" && "x" in x && "y" in x) { 660 return new Point(x.x, x.y); 661 } 662 return new Point(x, y, round); 663 } 664 function Bounds(a, b) { 665 if (!a) { 666 return; 667 } 668 var points = b ? [a, b] : a; 669 for (var i = 0, len = points.length; i < len; i++) { 670 this.extend(points[i]); 671 } 672 } 673 Bounds.prototype = { 674 // @method extend(point: Point): this 675 // Extends the bounds to contain the given point. 676 // @alternative 677 // @method extend(otherBounds: Bounds): this 678 // Extend the bounds to contain the given bounds 679 extend: function(obj) { 680 var min2, max2; 681 if (!obj) { 682 return this; 683 } 684 if (obj instanceof Point || typeof obj[0] === "number" || "x" in obj) { 685 min2 = max2 = toPoint(obj); 686 } else { 687 obj = toBounds(obj); 688 min2 = obj.min; 689 max2 = obj.max; 690 if (!min2 || !max2) { 691 return this; 692 } 693 } 694 if (!this.min && !this.max) { 695 this.min = min2.clone(); 696 this.max = max2.clone(); 697 } else { 698 this.min.x = Math.min(min2.x, this.min.x); 699 this.max.x = Math.max(max2.x, this.max.x); 700 this.min.y = Math.min(min2.y, this.min.y); 701 this.max.y = Math.max(max2.y, this.max.y); 702 } 703 return this; 704 }, 705 // @method getCenter(round?: Boolean): Point 706 // Returns the center point of the bounds. 707 getCenter: function(round) { 708 return toPoint( 709 (this.min.x + this.max.x) / 2, 710 (this.min.y + this.max.y) / 2, 711 round 712 ); 713 }, 714 // @method getBottomLeft(): Point 715 // Returns the bottom-left point of the bounds. 716 getBottomLeft: function() { 717 return toPoint(this.min.x, this.max.y); 718 }, 719 // @method getTopRight(): Point 720 // Returns the top-right point of the bounds. 721 getTopRight: function() { 722 return toPoint(this.max.x, this.min.y); 723 }, 724 // @method getTopLeft(): Point 725 // Returns the top-left point of the bounds (i.e. [`this.min`](#bounds-min)). 726 getTopLeft: function() { 727 return this.min; 728 }, 729 // @method getBottomRight(): Point 730 // Returns the bottom-right point of the bounds (i.e. [`this.max`](#bounds-max)). 731 getBottomRight: function() { 732 return this.max; 733 }, 734 // @method getSize(): Point 735 // Returns the size of the given bounds 736 getSize: function() { 737 return this.max.subtract(this.min); 738 }, 739 // @method contains(otherBounds: Bounds): Boolean 740 // Returns `true` if the rectangle contains the given one. 741 // @alternative 742 // @method contains(point: Point): Boolean 743 // Returns `true` if the rectangle contains the given point. 744 contains: function(obj) { 745 var min, max; 746 if (typeof obj[0] === "number" || obj instanceof Point) { 747 obj = toPoint(obj); 748 } else { 749 obj = toBounds(obj); 750 } 751 if (obj instanceof Bounds) { 752 min = obj.min; 753 max = obj.max; 754 } else { 755 min = max = obj; 756 } 757 return min.x >= this.min.x && max.x <= this.max.x && min.y >= this.min.y && max.y <= this.max.y; 758 }, 759 // @method intersects(otherBounds: Bounds): Boolean 760 // Returns `true` if the rectangle intersects the given bounds. Two bounds 761 // intersect if they have at least one point in common. 762 intersects: function(bounds) { 763 bounds = toBounds(bounds); 764 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; 765 return xIntersects && yIntersects; 766 }, 767 // @method overlaps(otherBounds: Bounds): Boolean 768 // Returns `true` if the rectangle overlaps the given bounds. Two bounds 769 // overlap if their intersection is an area. 770 overlaps: function(bounds) { 771 bounds = toBounds(bounds); 772 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; 773 return xOverlaps && yOverlaps; 774 }, 775 // @method isValid(): Boolean 776 // Returns `true` if the bounds are properly initialized. 777 isValid: function() { 778 return !!(this.min && this.max); 779 }, 780 // @method pad(bufferRatio: Number): Bounds 781 // Returns bounds created by extending or retracting the current bounds by a given ratio in each direction. 782 // For example, a ratio of 0.5 extends the bounds by 50% in each direction. 783 // Negative values will retract the bounds. 784 pad: function(bufferRatio) { 785 var min = this.min, max = this.max, heightBuffer = Math.abs(min.x - max.x) * bufferRatio, widthBuffer = Math.abs(min.y - max.y) * bufferRatio; 786 return toBounds( 787 toPoint(min.x - heightBuffer, min.y - widthBuffer), 788 toPoint(max.x + heightBuffer, max.y + widthBuffer) 789 ); 790 }, 791 // @method equals(otherBounds: Bounds): Boolean 792 // Returns `true` if the rectangle is equivalent to the given bounds. 793 equals: function(bounds) { 794 if (!bounds) { 795 return false; 796 } 797 bounds = toBounds(bounds); 798 return this.min.equals(bounds.getTopLeft()) && this.max.equals(bounds.getBottomRight()); 799 } 800 }; 801 function toBounds(a, b) { 802 if (!a || a instanceof Bounds) { 803 return a; 804 } 805 return new Bounds(a, b); 806 } 807 function LatLngBounds(corner1, corner2) { 808 if (!corner1) { 809 return; 810 } 811 var latlngs = corner2 ? [corner1, corner2] : corner1; 812 for (var i = 0, len = latlngs.length; i < len; i++) { 813 this.extend(latlngs[i]); 814 } 815 } 816 LatLngBounds.prototype = { 817 // @method extend(latlng: LatLng): this 818 // Extend the bounds to contain the given point 819 // @alternative 820 // @method extend(otherBounds: LatLngBounds): this 821 // Extend the bounds to contain the given bounds 822 extend: function(obj) { 823 var sw = this._southWest, ne = this._northEast, sw2, ne2; 824 if (obj instanceof LatLng) { 825 sw2 = obj; 826 ne2 = obj; 827 } else if (obj instanceof LatLngBounds) { 828 sw2 = obj._southWest; 829 ne2 = obj._northEast; 830 if (!sw2 || !ne2) { 831 return this; 832 } 833 } else { 834 return obj ? this.extend(toLatLng(obj) || toLatLngBounds(obj)) : this; 835 } 836 if (!sw && !ne) { 837 this._southWest = new LatLng(sw2.lat, sw2.lng); 838 this._northEast = new LatLng(ne2.lat, ne2.lng); 839 } else { 840 sw.lat = Math.min(sw2.lat, sw.lat); 841 sw.lng = Math.min(sw2.lng, sw.lng); 842 ne.lat = Math.max(ne2.lat, ne.lat); 843 ne.lng = Math.max(ne2.lng, ne.lng); 844 } 845 return this; 846 }, 847 // @method pad(bufferRatio: Number): LatLngBounds 848 // Returns bounds created by extending or retracting the current bounds by a given ratio in each direction. 849 // For example, a ratio of 0.5 extends the bounds by 50% in each direction. 850 // Negative values will retract the bounds. 851 pad: function(bufferRatio) { 852 var sw = this._southWest, ne = this._northEast, heightBuffer = Math.abs(sw.lat - ne.lat) * bufferRatio, widthBuffer = Math.abs(sw.lng - ne.lng) * bufferRatio; 853 return new LatLngBounds( 854 new LatLng(sw.lat - heightBuffer, sw.lng - widthBuffer), 855 new LatLng(ne.lat + heightBuffer, ne.lng + widthBuffer) 856 ); 857 }, 858 // @method getCenter(): LatLng 859 // Returns the center point of the bounds. 860 getCenter: function() { 861 return new LatLng( 862 (this._southWest.lat + this._northEast.lat) / 2, 863 (this._southWest.lng + this._northEast.lng) / 2 864 ); 865 }, 866 // @method getSouthWest(): LatLng 867 // Returns the south-west point of the bounds. 868 getSouthWest: function() { 869 return this._southWest; 870 }, 871 // @method getNorthEast(): LatLng 872 // Returns the north-east point of the bounds. 873 getNorthEast: function() { 874 return this._northEast; 875 }, 876 // @method getNorthWest(): LatLng 877 // Returns the north-west point of the bounds. 878 getNorthWest: function() { 879 return new LatLng(this.getNorth(), this.getWest()); 880 }, 881 // @method getSouthEast(): LatLng 882 // Returns the south-east point of the bounds. 883 getSouthEast: function() { 884 return new LatLng(this.getSouth(), this.getEast()); 885 }, 886 // @method getWest(): Number 887 // Returns the west longitude of the bounds 888 getWest: function() { 889 return this._southWest.lng; 890 }, 891 // @method getSouth(): Number 892 // Returns the south latitude of the bounds 893 getSouth: function() { 894 return this._southWest.lat; 895 }, 896 // @method getEast(): Number 897 // Returns the east longitude of the bounds 898 getEast: function() { 899 return this._northEast.lng; 900 }, 901 // @method getNorth(): Number 902 // Returns the north latitude of the bounds 903 getNorth: function() { 904 return this._northEast.lat; 905 }, 906 // @method contains(otherBounds: LatLngBounds): Boolean 907 // Returns `true` if the rectangle contains the given one. 908 // @alternative 909 // @method contains (latlng: LatLng): Boolean 910 // Returns `true` if the rectangle contains the given point. 911 contains: function(obj) { 912 if (typeof obj[0] === "number" || obj instanceof LatLng || "lat" in obj) { 913 obj = toLatLng(obj); 914 } else { 915 obj = toLatLngBounds(obj); 916 } 917 var sw = this._southWest, ne = this._northEast, sw2, ne2; 918 if (obj instanceof LatLngBounds) { 919 sw2 = obj.getSouthWest(); 920 ne2 = obj.getNorthEast(); 921 } else { 922 sw2 = ne2 = obj; 923 } 924 return sw2.lat >= sw.lat && ne2.lat <= ne.lat && sw2.lng >= sw.lng && ne2.lng <= ne.lng; 925 }, 926 // @method intersects(otherBounds: LatLngBounds): Boolean 927 // Returns `true` if the rectangle intersects the given bounds. Two bounds intersect if they have at least one point in common. 928 intersects: function(bounds) { 929 bounds = toLatLngBounds(bounds); 930 var sw = this._southWest, ne = this._northEast, sw2 = bounds.getSouthWest(), ne2 = bounds.getNorthEast(), latIntersects = ne2.lat >= sw.lat && sw2.lat <= ne.lat, lngIntersects = ne2.lng >= sw.lng && sw2.lng <= ne.lng; 931 return latIntersects && lngIntersects; 932 }, 933 // @method overlaps(otherBounds: LatLngBounds): Boolean 934 // Returns `true` if the rectangle overlaps the given bounds. Two bounds overlap if their intersection is an area. 935 overlaps: function(bounds) { 936 bounds = toLatLngBounds(bounds); 937 var sw = this._southWest, ne = this._northEast, sw2 = bounds.getSouthWest(), ne2 = bounds.getNorthEast(), latOverlaps = ne2.lat > sw.lat && sw2.lat < ne.lat, lngOverlaps = ne2.lng > sw.lng && sw2.lng < ne.lng; 938 return latOverlaps && lngOverlaps; 939 }, 940 // @method toBBoxString(): String 941 // Returns a string with bounding box coordinates in a 'southwest_lng,southwest_lat,northeast_lng,northeast_lat' format. Useful for sending requests to web services that return geo data. 942 toBBoxString: function() { 943 return [this.getWest(), this.getSouth(), this.getEast(), this.getNorth()].join(","); 944 }, 945 // @method equals(otherBounds: LatLngBounds, maxMargin?: Number): Boolean 946 // Returns `true` if the rectangle is equivalent (within a small margin of error) to the given bounds. The margin of error can be overridden by setting `maxMargin` to a small number. 947 equals: function(bounds, maxMargin) { 948 if (!bounds) { 949 return false; 950 } 951 bounds = toLatLngBounds(bounds); 952 return this._southWest.equals(bounds.getSouthWest(), maxMargin) && this._northEast.equals(bounds.getNorthEast(), maxMargin); 953 }, 954 // @method isValid(): Boolean 955 // Returns `true` if the bounds are properly initialized. 956 isValid: function() { 957 return !!(this._southWest && this._northEast); 958 } 959 }; 960 function toLatLngBounds(a, b) { 961 if (a instanceof LatLngBounds) { 962 return a; 963 } 964 return new LatLngBounds(a, b); 965 } 966 function LatLng(lat, lng, alt) { 967 if (isNaN(lat) || isNaN(lng)) { 968 throw new Error("Invalid LatLng object: (" + lat + ", " + lng + ")"); 969 } 970 this.lat = +lat; 971 this.lng = +lng; 972 if (alt !== void 0) { 973 this.alt = +alt; 974 } 975 } 976 LatLng.prototype = { 977 // @method equals(otherLatLng: LatLng, maxMargin?: Number): Boolean 978 // Returns `true` if the given `LatLng` point is at the same position (within a small margin of error). The margin of error can be overridden by setting `maxMargin` to a small number. 979 equals: function(obj, maxMargin) { 980 if (!obj) { 981 return false; 982 } 983 obj = toLatLng(obj); 984 var margin = Math.max( 985 Math.abs(this.lat - obj.lat), 986 Math.abs(this.lng - obj.lng) 987 ); 988 return margin <= (maxMargin === void 0 ? 1e-9 : maxMargin); 989 }, 990 // @method toString(): String 991 // Returns a string representation of the point (for debugging purposes). 992 toString: function(precision) { 993 return "LatLng(" + formatNum(this.lat, precision) + ", " + formatNum(this.lng, precision) + ")"; 994 }, 995 // @method distanceTo(otherLatLng: LatLng): Number 996 // Returns the distance (in meters) to the given `LatLng` calculated using the [Spherical Law of Cosines](https://en.wikipedia.org/wiki/Spherical_law_of_cosines). 997 distanceTo: function(other) { 998 return Earth.distance(this, toLatLng(other)); 999 }, 1000 // @method wrap(): LatLng 1001 // Returns a new `LatLng` object with the longitude wrapped so it's always between -180 and +180 degrees. 1002 wrap: function() { 1003 return Earth.wrapLatLng(this); 1004 }, 1005 // @method toBounds(sizeInMeters: Number): LatLngBounds 1006 // Returns a new `LatLngBounds` object in which each boundary is `sizeInMeters/2` meters apart from the `LatLng`. 1007 toBounds: function(sizeInMeters) { 1008 var latAccuracy = 180 * sizeInMeters / 40075017, lngAccuracy = latAccuracy / Math.cos(Math.PI / 180 * this.lat); 1009 return toLatLngBounds( 1010 [this.lat - latAccuracy, this.lng - lngAccuracy], 1011 [this.lat + latAccuracy, this.lng + lngAccuracy] 1012 ); 1013 }, 1014 clone: function() { 1015 return new LatLng(this.lat, this.lng, this.alt); 1016 } 1017 }; 1018 function toLatLng(a, b, c) { 1019 if (a instanceof LatLng) { 1020 return a; 1021 } 1022 if (isArray(a) && typeof a[0] !== "object") { 1023 if (a.length === 3) { 1024 return new LatLng(a[0], a[1], a[2]); 1025 } 1026 if (a.length === 2) { 1027 return new LatLng(a[0], a[1]); 1028 } 1029 return null; 1030 } 1031 if (a === void 0 || a === null) { 1032 return a; 1033 } 1034 if (typeof a === "object" && "lat" in a) { 1035 return new LatLng(a.lat, "lng" in a ? a.lng : a.lon, a.alt); 1036 } 1037 if (b === void 0) { 1038 return null; 1039 } 1040 return new LatLng(a, b, c); 1041 } 1042 var CRS = { 1043 // @method latLngToPoint(latlng: LatLng, zoom: Number): Point 1044 // Projects geographical coordinates into pixel coordinates for a given zoom. 1045 latLngToPoint: function(latlng, zoom2) { 1046 var projectedPoint = this.projection.project(latlng), scale2 = this.scale(zoom2); 1047 return this.transformation._transform(projectedPoint, scale2); 1048 }, 1049 // @method pointToLatLng(point: Point, zoom: Number): LatLng 1050 // The inverse of `latLngToPoint`. Projects pixel coordinates on a given 1051 // zoom into geographical coordinates. 1052 pointToLatLng: function(point, zoom2) { 1053 var scale2 = this.scale(zoom2), untransformedPoint = this.transformation.untransform(point, scale2); 1054 return this.projection.unproject(untransformedPoint); 1055 }, 1056 // @method project(latlng: LatLng): Point 1057 // Projects geographical coordinates into coordinates in units accepted for 1058 // this CRS (e.g. meters for EPSG:3857, for passing it to WMS services). 1059 project: function(latlng) { 1060 return this.projection.project(latlng); 1061 }, 1062 // @method unproject(point: Point): LatLng 1063 // Given a projected coordinate returns the corresponding LatLng. 1064 // The inverse of `project`. 1065 unproject: function(point) { 1066 return this.projection.unproject(point); 1067 }, 1068 // @method scale(zoom: Number): Number 1069 // Returns the scale used when transforming projected coordinates into 1070 // pixel coordinates for a particular zoom. For example, it returns 1071 // `256 * 2^zoom` for Mercator-based CRS. 1072 scale: function(zoom2) { 1073 return 256 * Math.pow(2, zoom2); 1074 }, 1075 // @method zoom(scale: Number): Number 1076 // Inverse of `scale()`, returns the zoom level corresponding to a scale 1077 // factor of `scale`. 1078 zoom: function(scale2) { 1079 return Math.log(scale2 / 256) / Math.LN2; 1080 }, 1081 // @method getProjectedBounds(zoom: Number): Bounds 1082 // Returns the projection's bounds scaled and transformed for the provided `zoom`. 1083 getProjectedBounds: function(zoom2) { 1084 if (this.infinite) { 1085 return null; 1086 } 1087 var b = this.projection.bounds, s = this.scale(zoom2), min = this.transformation.transform(b.min, s), max = this.transformation.transform(b.max, s); 1088 return new Bounds(min, max); 1089 }, 1090 // @method distance(latlng1: LatLng, latlng2: LatLng): Number 1091 // Returns the distance between two geographical coordinates. 1092 // @property code: String 1093 // Standard code name of the CRS passed into WMS services (e.g. `'EPSG:3857'`) 1094 // 1095 // @property wrapLng: Number[] 1096 // An array of two numbers defining whether the longitude (horizontal) coordinate 1097 // axis wraps around a given range and how. Defaults to `[-180, 180]` in most 1098 // geographical CRSs. If `undefined`, the longitude axis does not wrap around. 1099 // 1100 // @property wrapLat: Number[] 1101 // Like `wrapLng`, but for the latitude (vertical) axis. 1102 // wrapLng: [min, max], 1103 // wrapLat: [min, max], 1104 // @property infinite: Boolean 1105 // If true, the coordinate space will be unbounded (infinite in both axes) 1106 infinite: false, 1107 // @method wrapLatLng(latlng: LatLng): LatLng 1108 // Returns a `LatLng` where lat and lng has been wrapped according to the 1109 // CRS's `wrapLat` and `wrapLng` properties, if they are outside the CRS's bounds. 1110 wrapLatLng: function(latlng) { 1111 var lng = this.wrapLng ? wrapNum(latlng.lng, this.wrapLng, true) : latlng.lng, lat = this.wrapLat ? wrapNum(latlng.lat, this.wrapLat, true) : latlng.lat, alt = latlng.alt; 1112 return new LatLng(lat, lng, alt); 1113 }, 1114 // @method wrapLatLngBounds(bounds: LatLngBounds): LatLngBounds 1115 // Returns a `LatLngBounds` with the same size as the given one, ensuring 1116 // that its center is within the CRS's bounds. 1117 // Only accepts actual `L.LatLngBounds` instances, not arrays. 1118 wrapLatLngBounds: function(bounds) { 1119 var center = bounds.getCenter(), newCenter = this.wrapLatLng(center), latShift = center.lat - newCenter.lat, lngShift = center.lng - newCenter.lng; 1120 if (latShift === 0 && lngShift === 0) { 1121 return bounds; 1122 } 1123 var sw = bounds.getSouthWest(), ne = bounds.getNorthEast(), newSw = new LatLng(sw.lat - latShift, sw.lng - lngShift), newNe = new LatLng(ne.lat - latShift, ne.lng - lngShift); 1124 return new LatLngBounds(newSw, newNe); 1125 } 1126 }; 1127 var Earth = extend({}, CRS, { 1128 wrapLng: [-180, 180], 1129 // Mean Earth Radius, as recommended for use by 1130 // the International Union of Geodesy and Geophysics, 1131 // see https://rosettacode.org/wiki/Haversine_formula 1132 R: 6371e3, 1133 // distance between two geographical points using spherical law of cosines approximation 1134 distance: function(latlng1, latlng2) { 1135 var rad = Math.PI / 180, lat1 = latlng1.lat * rad, lat2 = latlng2.lat * rad, sinDLat = Math.sin((latlng2.lat - latlng1.lat) * rad / 2), sinDLon = Math.sin((latlng2.lng - latlng1.lng) * rad / 2), a = sinDLat * sinDLat + Math.cos(lat1) * Math.cos(lat2) * sinDLon * sinDLon, c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a)); 1136 return this.R * c; 1137 } 1138 }); 1139 var earthRadius = 6378137; 1140 var SphericalMercator = { 1141 R: earthRadius, 1142 MAX_LATITUDE: 85.0511287798, 1143 project: function(latlng) { 1144 var d = Math.PI / 180, max = this.MAX_LATITUDE, lat = Math.max(Math.min(max, latlng.lat), -max), sin = Math.sin(lat * d); 1145 return new Point( 1146 this.R * latlng.lng * d, 1147 this.R * Math.log((1 + sin) / (1 - sin)) / 2 1148 ); 1149 }, 1150 unproject: function(point) { 1151 var d = 180 / Math.PI; 1152 return new LatLng( 1153 (2 * Math.atan(Math.exp(point.y / this.R)) - Math.PI / 2) * d, 1154 point.x * d / this.R 1155 ); 1156 }, 1157 bounds: function() { 1158 var d = earthRadius * Math.PI; 1159 return new Bounds([-d, -d], [d, d]); 1160 }() 1161 }; 1162 function Transformation(a, b, c, d) { 1163 if (isArray(a)) { 1164 this._a = a[0]; 1165 this._b = a[1]; 1166 this._c = a[2]; 1167 this._d = a[3]; 1168 return; 1169 } 1170 this._a = a; 1171 this._b = b; 1172 this._c = c; 1173 this._d = d; 1174 } 1175 Transformation.prototype = { 1176 // @method transform(point: Point, scale?: Number): Point 1177 // Returns a transformed point, optionally multiplied by the given scale. 1178 // Only accepts actual `L.Point` instances, not arrays. 1179 transform: function(point, scale2) { 1180 return this._transform(point.clone(), scale2); 1181 }, 1182 // destructive transform (faster) 1183 _transform: function(point, scale2) { 1184 scale2 = scale2 || 1; 1185 point.x = scale2 * (this._a * point.x + this._b); 1186 point.y = scale2 * (this._c * point.y + this._d); 1187 return point; 1188 }, 1189 // @method untransform(point: Point, scale?: Number): Point 1190 // Returns the reverse transformation of the given point, optionally divided 1191 // by the given scale. Only accepts actual `L.Point` instances, not arrays. 1192 untransform: function(point, scale2) { 1193 scale2 = scale2 || 1; 1194 return new Point( 1195 (point.x / scale2 - this._b) / this._a, 1196 (point.y / scale2 - this._d) / this._c 1197 ); 1198 } 1199 }; 1200 function toTransformation(a, b, c, d) { 1201 return new Transformation(a, b, c, d); 1202 } 1203 var EPSG3857 = extend({}, Earth, { 1204 code: "EPSG:3857", 1205 projection: SphericalMercator, 1206 transformation: function() { 1207 var scale2 = 0.5 / (Math.PI * SphericalMercator.R); 1208 return toTransformation(scale2, 0.5, -scale2, 0.5); 1209 }() 1210 }); 1211 var EPSG900913 = extend({}, EPSG3857, { 1212 code: "EPSG:900913" 1213 }); 1214 function svgCreate(name) { 1215 return document.createElementNS("http://www.w3.org/2000/svg", name); 1216 } 1217 function pointsToPath(rings, closed) { 1218 var str = "", i, j, len, len2, points, p; 1219 for (i = 0, len = rings.length; i < len; i++) { 1220 points = rings[i]; 1221 for (j = 0, len2 = points.length; j < len2; j++) { 1222 p = points[j]; 1223 str += (j ? "L" : "M") + p.x + " " + p.y; 1224 } 1225 str += closed ? Browser.svg ? "z" : "x" : ""; 1226 } 1227 return str || "M0 0"; 1228 } 1229 var style = document.documentElement.style; 1230 var ie = "ActiveXObject" in window; 1231 var ielt9 = ie && !document.addEventListener; 1232 var edge = "msLaunchUri" in navigator && !("documentMode" in document); 1233 var webkit = userAgentContains("webkit"); 1234 var android = userAgentContains("android"); 1235 var android23 = userAgentContains("android 2") || userAgentContains("android 3"); 1236 var webkitVer = parseInt(/WebKit\/([0-9]+)|$/.exec(navigator.userAgent)[1], 10); 1237 var androidStock = android && userAgentContains("Google") && webkitVer < 537 && !("AudioNode" in window); 1238 var opera = !!window.opera; 1239 var chrome = !edge && userAgentContains("chrome"); 1240 var gecko = userAgentContains("gecko") && !webkit && !opera && !ie; 1241 var safari = !chrome && userAgentContains("safari"); 1242 var phantom = userAgentContains("phantom"); 1243 var opera12 = "OTransition" in style; 1244 var win = navigator.platform.indexOf("Win") === 0; 1245 var ie3d = ie && "transition" in style; 1246 var webkit3d = "WebKitCSSMatrix" in window && "m11" in new window.WebKitCSSMatrix() && !android23; 1247 var gecko3d = "MozPerspective" in style; 1248 var any3d = !window.L_DISABLE_3D && (ie3d || webkit3d || gecko3d) && !opera12 && !phantom; 1249 var mobile = typeof orientation !== "undefined" || userAgentContains("mobile"); 1250 var mobileWebkit = mobile && webkit; 1251 var mobileWebkit3d = mobile && webkit3d; 1252 var msPointer = !window.PointerEvent && window.MSPointerEvent; 1253 var pointer = !!(window.PointerEvent || msPointer); 1254 var touchNative = "ontouchstart" in window || !!window.TouchEvent; 1255 var touch = !window.L_NO_TOUCH && (touchNative || pointer); 1256 var mobileOpera = mobile && opera; 1257 var mobileGecko = mobile && gecko; 1258 var retina = (window.devicePixelRatio || window.screen.deviceXDPI / window.screen.logicalXDPI) > 1; 1259 var passiveEvents = function() { 1260 var supportsPassiveOption = false; 1261 try { 1262 var opts = Object.defineProperty({}, "passive", { 1263 get: function() { 1264 supportsPassiveOption = true; 1265 } 1266 }); 1267 window.addEventListener("testPassiveEventSupport", falseFn, opts); 1268 window.removeEventListener("testPassiveEventSupport", falseFn, opts); 1269 } catch (e) { 1270 } 1271 return supportsPassiveOption; 1272 }(); 1273 var canvas$1 = function() { 1274 return !!document.createElement("canvas").getContext; 1275 }(); 1276 var svg$1 = !!(document.createElementNS && svgCreate("svg").createSVGRect); 1277 var inlineSvg = !!svg$1 && function() { 1278 var div = document.createElement("div"); 1279 div.innerHTML = "<svg/>"; 1280 return (div.firstChild && div.firstChild.namespaceURI) === "http://www.w3.org/2000/svg"; 1281 }(); 1282 var vml = !svg$1 && function() { 1283 try { 1284 var div = document.createElement("div"); 1285 div.innerHTML = '<v:shape adj="1"/>'; 1286 var shape = div.firstChild; 1287 shape.style.behavior = "url(#default#VML)"; 1288 return shape && typeof shape.adj === "object"; 1289 } catch (e) { 1290 return false; 1291 } 1292 }(); 1293 var mac = navigator.platform.indexOf("Mac") === 0; 1294 var linux = navigator.platform.indexOf("Linux") === 0; 1295 function userAgentContains(str) { 1296 return navigator.userAgent.toLowerCase().indexOf(str) >= 0; 1297 } 1298 var Browser = { 1299 ie, 1300 ielt9, 1301 edge, 1302 webkit, 1303 android, 1304 android23, 1305 androidStock, 1306 opera, 1307 chrome, 1308 gecko, 1309 safari, 1310 phantom, 1311 opera12, 1312 win, 1313 ie3d, 1314 webkit3d, 1315 gecko3d, 1316 any3d, 1317 mobile, 1318 mobileWebkit, 1319 mobileWebkit3d, 1320 msPointer, 1321 pointer, 1322 touch, 1323 touchNative, 1324 mobileOpera, 1325 mobileGecko, 1326 retina, 1327 passiveEvents, 1328 canvas: canvas$1, 1329 svg: svg$1, 1330 vml, 1331 inlineSvg, 1332 mac, 1333 linux 1334 }; 1335 var POINTER_DOWN = Browser.msPointer ? "MSPointerDown" : "pointerdown"; 1336 var POINTER_MOVE = Browser.msPointer ? "MSPointerMove" : "pointermove"; 1337 var POINTER_UP = Browser.msPointer ? "MSPointerUp" : "pointerup"; 1338 var POINTER_CANCEL = Browser.msPointer ? "MSPointerCancel" : "pointercancel"; 1339 var pEvent = { 1340 touchstart: POINTER_DOWN, 1341 touchmove: POINTER_MOVE, 1342 touchend: POINTER_UP, 1343 touchcancel: POINTER_CANCEL 1344 }; 1345 var handle = { 1346 touchstart: _onPointerStart, 1347 touchmove: _handlePointer, 1348 touchend: _handlePointer, 1349 touchcancel: _handlePointer 1350 }; 1351 var _pointers = {}; 1352 var _pointerDocListener = false; 1353 function addPointerListener(obj, type, handler) { 1354 if (type === "touchstart") { 1355 _addPointerDocListener(); 1356 } 1357 if (!handle[type]) { 1358 console.warn("wrong event specified:", type); 1359 return falseFn; 1360 } 1361 handler = handle[type].bind(this, handler); 1362 obj.addEventListener(pEvent[type], handler, false); 1363 return handler; 1364 } 1365 function removePointerListener(obj, type, handler) { 1366 if (!pEvent[type]) { 1367 console.warn("wrong event specified:", type); 1368 return; 1369 } 1370 obj.removeEventListener(pEvent[type], handler, false); 1371 } 1372 function _globalPointerDown(e) { 1373 _pointers[e.pointerId] = e; 1374 } 1375 function _globalPointerMove(e) { 1376 if (_pointers[e.pointerId]) { 1377 _pointers[e.pointerId] = e; 1378 } 1379 } 1380 function _globalPointerUp(e) { 1381 delete _pointers[e.pointerId]; 1382 } 1383 function _addPointerDocListener() { 1384 if (!_pointerDocListener) { 1385 document.addEventListener(POINTER_DOWN, _globalPointerDown, true); 1386 document.addEventListener(POINTER_MOVE, _globalPointerMove, true); 1387 document.addEventListener(POINTER_UP, _globalPointerUp, true); 1388 document.addEventListener(POINTER_CANCEL, _globalPointerUp, true); 1389 _pointerDocListener = true; 1390 } 1391 } 1392 function _handlePointer(handler, e) { 1393 if (e.pointerType === (e.MSPOINTER_TYPE_MOUSE || "mouse")) { 1394 return; 1395 } 1396 e.touches = []; 1397 for (var i in _pointers) { 1398 e.touches.push(_pointers[i]); 1399 } 1400 e.changedTouches = [e]; 1401 handler(e); 1402 } 1403 function _onPointerStart(handler, e) { 1404 if (e.MSPOINTER_TYPE_TOUCH && e.pointerType === e.MSPOINTER_TYPE_TOUCH) { 1405 preventDefault(e); 1406 } 1407 _handlePointer(handler, e); 1408 } 1409 function makeDblclick(event) { 1410 var newEvent = {}, prop, i; 1411 for (i in event) { 1412 prop = event[i]; 1413 newEvent[i] = prop && prop.bind ? prop.bind(event) : prop; 1414 } 1415 event = newEvent; 1416 newEvent.type = "dblclick"; 1417 newEvent.detail = 2; 1418 newEvent.isTrusted = false; 1419 newEvent._simulated = true; 1420 return newEvent; 1421 } 1422 var delay = 200; 1423 function addDoubleTapListener(obj, handler) { 1424 obj.addEventListener("dblclick", handler); 1425 var last = 0, detail; 1426 function simDblclick(e) { 1427 if (e.detail !== 1) { 1428 detail = e.detail; 1429 return; 1430 } 1431 if (e.pointerType === "mouse" || e.sourceCapabilities && !e.sourceCapabilities.firesTouchEvents) { 1432 return; 1433 } 1434 var path = getPropagationPath(e); 1435 if (path.some(function(el) { 1436 return el instanceof HTMLLabelElement && el.attributes.for; 1437 }) && !path.some(function(el) { 1438 return el instanceof HTMLInputElement || el instanceof HTMLSelectElement; 1439 })) { 1440 return; 1441 } 1442 var now = Date.now(); 1443 if (now - last <= delay) { 1444 detail++; 1445 if (detail === 2) { 1446 handler(makeDblclick(e)); 1447 } 1448 } else { 1449 detail = 1; 1450 } 1451 last = now; 1452 } 1453 obj.addEventListener("click", simDblclick); 1454 return { 1455 dblclick: handler, 1456 simDblclick 1457 }; 1458 } 1459 function removeDoubleTapListener(obj, handlers) { 1460 obj.removeEventListener("dblclick", handlers.dblclick); 1461 obj.removeEventListener("click", handlers.simDblclick); 1462 } 1463 var TRANSFORM = testProp( 1464 ["transform", "webkitTransform", "OTransform", "MozTransform", "msTransform"] 1465 ); 1466 var TRANSITION = testProp( 1467 ["webkitTransition", "transition", "OTransition", "MozTransition", "msTransition"] 1468 ); 1469 var TRANSITION_END = TRANSITION === "webkitTransition" || TRANSITION === "OTransition" ? TRANSITION + "End" : "transitionend"; 1470 function get(id) { 1471 return typeof id === "string" ? document.getElementById(id) : id; 1472 } 1473 function getStyle(el, style2) { 1474 var value = el.style[style2] || el.currentStyle && el.currentStyle[style2]; 1475 if ((!value || value === "auto") && document.defaultView) { 1476 var css = document.defaultView.getComputedStyle(el, null); 1477 value = css ? css[style2] : null; 1478 } 1479 return value === "auto" ? null : value; 1480 } 1481 function create$1(tagName, className, container) { 1482 var el = document.createElement(tagName); 1483 el.className = className || ""; 1484 if (container) { 1485 container.appendChild(el); 1486 } 1487 return el; 1488 } 1489 function remove(el) { 1490 var parent = el.parentNode; 1491 if (parent) { 1492 parent.removeChild(el); 1493 } 1494 } 1495 function empty(el) { 1496 while (el.firstChild) { 1497 el.removeChild(el.firstChild); 1498 } 1499 } 1500 function toFront(el) { 1501 var parent = el.parentNode; 1502 if (parent && parent.lastChild !== el) { 1503 parent.appendChild(el); 1504 } 1505 } 1506 function toBack(el) { 1507 var parent = el.parentNode; 1508 if (parent && parent.firstChild !== el) { 1509 parent.insertBefore(el, parent.firstChild); 1510 } 1511 } 1512 function hasClass(el, name) { 1513 if (el.classList !== void 0) { 1514 return el.classList.contains(name); 1515 } 1516 var className = getClass(el); 1517 return className.length > 0 && new RegExp("(^|\\s)" + name + "(\\s|$)").test(className); 1518 } 1519 function addClass(el, name) { 1520 if (el.classList !== void 0) { 1521 var classes = splitWords(name); 1522 for (var i = 0, len = classes.length; i < len; i++) { 1523 el.classList.add(classes[i]); 1524 } 1525 } else if (!hasClass(el, name)) { 1526 var className = getClass(el); 1527 setClass(el, (className ? className + " " : "") + name); 1528 } 1529 } 1530 function removeClass(el, name) { 1531 if (el.classList !== void 0) { 1532 el.classList.remove(name); 1533 } else { 1534 setClass(el, trim((" " + getClass(el) + " ").replace(" " + name + " ", " "))); 1535 } 1536 } 1537 function setClass(el, name) { 1538 if (el.className.baseVal === void 0) { 1539 el.className = name; 1540 } else { 1541 el.className.baseVal = name; 1542 } 1543 } 1544 function getClass(el) { 1545 if (el.correspondingElement) { 1546 el = el.correspondingElement; 1547 } 1548 return el.className.baseVal === void 0 ? el.className : el.className.baseVal; 1549 } 1550 function setOpacity(el, value) { 1551 if ("opacity" in el.style) { 1552 el.style.opacity = value; 1553 } else if ("filter" in el.style) { 1554 _setOpacityIE(el, value); 1555 } 1556 } 1557 function _setOpacityIE(el, value) { 1558 var filter = false, filterName = "DXImageTransform.Microsoft.Alpha"; 1559 try { 1560 filter = el.filters.item(filterName); 1561 } catch (e) { 1562 if (value === 1) { 1563 return; 1564 } 1565 } 1566 value = Math.round(value * 100); 1567 if (filter) { 1568 filter.Enabled = value !== 100; 1569 filter.Opacity = value; 1570 } else { 1571 el.style.filter += " progid:" + filterName + "(opacity=" + value + ")"; 1572 } 1573 } 1574 function testProp(props) { 1575 var style2 = document.documentElement.style; 1576 for (var i = 0; i < props.length; i++) { 1577 if (props[i] in style2) { 1578 return props[i]; 1579 } 1580 } 1581 return false; 1582 } 1583 function setTransform(el, offset, scale2) { 1584 var pos = offset || new Point(0, 0); 1585 el.style[TRANSFORM] = (Browser.ie3d ? "translate(" + pos.x + "px," + pos.y + "px)" : "translate3d(" + pos.x + "px," + pos.y + "px,0)") + (scale2 ? " scale(" + scale2 + ")" : ""); 1586 } 1587 function setPosition(el, point) { 1588 el._leaflet_pos = point; 1589 if (Browser.any3d) { 1590 setTransform(el, point); 1591 } else { 1592 el.style.left = point.x + "px"; 1593 el.style.top = point.y + "px"; 1594 } 1595 } 1596 function getPosition(el) { 1597 return el._leaflet_pos || new Point(0, 0); 1598 } 1599 var disableTextSelection; 1600 var enableTextSelection; 1601 var _userSelect; 1602 if ("onselectstart" in document) { 1603 disableTextSelection = function() { 1604 on(window, "selectstart", preventDefault); 1605 }; 1606 enableTextSelection = function() { 1607 off(window, "selectstart", preventDefault); 1608 }; 1609 } else { 1610 var userSelectProperty = testProp( 1611 ["userSelect", "WebkitUserSelect", "OUserSelect", "MozUserSelect", "msUserSelect"] 1612 ); 1613 disableTextSelection = function() { 1614 if (userSelectProperty) { 1615 var style2 = document.documentElement.style; 1616 _userSelect = style2[userSelectProperty]; 1617 style2[userSelectProperty] = "none"; 1618 } 1619 }; 1620 enableTextSelection = function() { 1621 if (userSelectProperty) { 1622 document.documentElement.style[userSelectProperty] = _userSelect; 1623 _userSelect = void 0; 1624 } 1625 }; 1626 } 1627 function disableImageDrag() { 1628 on(window, "dragstart", preventDefault); 1629 } 1630 function enableImageDrag() { 1631 off(window, "dragstart", preventDefault); 1632 } 1633 var _outlineElement, _outlineStyle; 1634 function preventOutline(element) { 1635 while (element.tabIndex === -1) { 1636 element = element.parentNode; 1637 } 1638 if (!element.style) { 1639 return; 1640 } 1641 restoreOutline(); 1642 _outlineElement = element; 1643 _outlineStyle = element.style.outlineStyle; 1644 element.style.outlineStyle = "none"; 1645 on(window, "keydown", restoreOutline); 1646 } 1647 function restoreOutline() { 1648 if (!_outlineElement) { 1649 return; 1650 } 1651 _outlineElement.style.outlineStyle = _outlineStyle; 1652 _outlineElement = void 0; 1653 _outlineStyle = void 0; 1654 off(window, "keydown", restoreOutline); 1655 } 1656 function getSizedParentNode(element) { 1657 do { 1658 element = element.parentNode; 1659 } while ((!element.offsetWidth || !element.offsetHeight) && element !== document.body); 1660 return element; 1661 } 1662 function getScale(element) { 1663 var rect = element.getBoundingClientRect(); 1664 return { 1665 x: rect.width / element.offsetWidth || 1, 1666 y: rect.height / element.offsetHeight || 1, 1667 boundingClientRect: rect 1668 }; 1669 } 1670 var DomUtil = { 1671 __proto__: null, 1672 TRANSFORM, 1673 TRANSITION, 1674 TRANSITION_END, 1675 get, 1676 getStyle, 1677 create: create$1, 1678 remove, 1679 empty, 1680 toFront, 1681 toBack, 1682 hasClass, 1683 addClass, 1684 removeClass, 1685 setClass, 1686 getClass, 1687 setOpacity, 1688 testProp, 1689 setTransform, 1690 setPosition, 1691 getPosition, 1692 get disableTextSelection() { 1693 return disableTextSelection; 1694 }, 1695 get enableTextSelection() { 1696 return enableTextSelection; 1697 }, 1698 disableImageDrag, 1699 enableImageDrag, 1700 preventOutline, 1701 restoreOutline, 1702 getSizedParentNode, 1703 getScale 1704 }; 1705 function on(obj, types, fn, context) { 1706 if (types && typeof types === "object") { 1707 for (var type in types) { 1708 addOne(obj, type, types[type], fn); 1709 } 1710 } else { 1711 types = splitWords(types); 1712 for (var i = 0, len = types.length; i < len; i++) { 1713 addOne(obj, types[i], fn, context); 1714 } 1715 } 1716 return this; 1717 } 1718 var eventsKey = "_leaflet_events"; 1719 function off(obj, types, fn, context) { 1720 if (arguments.length === 1) { 1721 batchRemove(obj); 1722 delete obj[eventsKey]; 1723 } else if (types && typeof types === "object") { 1724 for (var type in types) { 1725 removeOne(obj, type, types[type], fn); 1726 } 1727 } else { 1728 types = splitWords(types); 1729 if (arguments.length === 2) { 1730 batchRemove(obj, function(type2) { 1731 return indexOf(types, type2) !== -1; 1732 }); 1733 } else { 1734 for (var i = 0, len = types.length; i < len; i++) { 1735 removeOne(obj, types[i], fn, context); 1736 } 1737 } 1738 } 1739 return this; 1740 } 1741 function batchRemove(obj, filterFn) { 1742 for (var id in obj[eventsKey]) { 1743 var type = id.split(/\d/)[0]; 1744 if (!filterFn || filterFn(type)) { 1745 removeOne(obj, type, null, null, id); 1746 } 1747 } 1748 } 1749 var mouseSubst = { 1750 mouseenter: "mouseover", 1751 mouseleave: "mouseout", 1752 wheel: !("onwheel" in window) && "mousewheel" 1753 }; 1754 function addOne(obj, type, fn, context) { 1755 var id = type + stamp(fn) + (context ? "_" + stamp(context) : ""); 1756 if (obj[eventsKey] && obj[eventsKey][id]) { 1757 return this; 1758 } 1759 var handler = function(e) { 1760 return fn.call(context || obj, e || window.event); 1761 }; 1762 var originalHandler = handler; 1763 if (!Browser.touchNative && Browser.pointer && type.indexOf("touch") === 0) { 1764 handler = addPointerListener(obj, type, handler); 1765 } else if (Browser.touch && type === "dblclick") { 1766 handler = addDoubleTapListener(obj, handler); 1767 } else if ("addEventListener" in obj) { 1768 if (type === "touchstart" || type === "touchmove" || type === "wheel" || type === "mousewheel") { 1769 obj.addEventListener(mouseSubst[type] || type, handler, Browser.passiveEvents ? { passive: false } : false); 1770 } else if (type === "mouseenter" || type === "mouseleave") { 1771 handler = function(e) { 1772 e = e || window.event; 1773 if (isExternalTarget(obj, e)) { 1774 originalHandler(e); 1775 } 1776 }; 1777 obj.addEventListener(mouseSubst[type], handler, false); 1778 } else { 1779 obj.addEventListener(type, originalHandler, false); 1780 } 1781 } else { 1782 obj.attachEvent("on" + type, handler); 1783 } 1784 obj[eventsKey] = obj[eventsKey] || {}; 1785 obj[eventsKey][id] = handler; 1786 } 1787 function removeOne(obj, type, fn, context, id) { 1788 id = id || type + stamp(fn) + (context ? "_" + stamp(context) : ""); 1789 var handler = obj[eventsKey] && obj[eventsKey][id]; 1790 if (!handler) { 1791 return this; 1792 } 1793 if (!Browser.touchNative && Browser.pointer && type.indexOf("touch") === 0) { 1794 removePointerListener(obj, type, handler); 1795 } else if (Browser.touch && type === "dblclick") { 1796 removeDoubleTapListener(obj, handler); 1797 } else if ("removeEventListener" in obj) { 1798 obj.removeEventListener(mouseSubst[type] || type, handler, false); 1799 } else { 1800 obj.detachEvent("on" + type, handler); 1801 } 1802 obj[eventsKey][id] = null; 1803 } 1804 function stopPropagation(e) { 1805 if (e.stopPropagation) { 1806 e.stopPropagation(); 1807 } else if (e.originalEvent) { 1808 e.originalEvent._stopped = true; 1809 } else { 1810 e.cancelBubble = true; 1811 } 1812 return this; 1813 } 1814 function disableScrollPropagation(el) { 1815 addOne(el, "wheel", stopPropagation); 1816 return this; 1817 } 1818 function disableClickPropagation(el) { 1819 on(el, "mousedown touchstart dblclick contextmenu", stopPropagation); 1820 el["_leaflet_disable_click"] = true; 1821 return this; 1822 } 1823 function preventDefault(e) { 1824 if (e.preventDefault) { 1825 e.preventDefault(); 1826 } else { 1827 e.returnValue = false; 1828 } 1829 return this; 1830 } 1831 function stop(e) { 1832 preventDefault(e); 1833 stopPropagation(e); 1834 return this; 1835 } 1836 function getPropagationPath(ev) { 1837 if (ev.composedPath) { 1838 return ev.composedPath(); 1839 } 1840 var path = []; 1841 var el = ev.target; 1842 while (el) { 1843 path.push(el); 1844 el = el.parentNode; 1845 } 1846 return path; 1847 } 1848 function getMousePosition(e, container) { 1849 if (!container) { 1850 return new Point(e.clientX, e.clientY); 1851 } 1852 var scale2 = getScale(container), offset = scale2.boundingClientRect; 1853 return new Point( 1854 // offset.left/top values are in page scale (like clientX/Y), 1855 // whereas clientLeft/Top (border width) values are the original values (before CSS scale applies). 1856 (e.clientX - offset.left) / scale2.x - container.clientLeft, 1857 (e.clientY - offset.top) / scale2.y - container.clientTop 1858 ); 1859 } 1860 var wheelPxFactor = Browser.linux && Browser.chrome ? window.devicePixelRatio : Browser.mac ? window.devicePixelRatio * 3 : window.devicePixelRatio > 0 ? 2 * window.devicePixelRatio : 1; 1861 function getWheelDelta(e) { 1862 return Browser.edge ? e.wheelDeltaY / 2 : ( 1863 // Don't trust window-geometry-based delta 1864 e.deltaY && e.deltaMode === 0 ? -e.deltaY / wheelPxFactor : ( 1865 // Pixels 1866 e.deltaY && e.deltaMode === 1 ? -e.deltaY * 20 : ( 1867 // Lines 1868 e.deltaY && e.deltaMode === 2 ? -e.deltaY * 60 : ( 1869 // Pages 1870 e.deltaX || e.deltaZ ? 0 : ( 1871 // Skip horizontal/depth wheel events 1872 e.wheelDelta ? (e.wheelDeltaY || e.wheelDelta) / 2 : ( 1873 // Legacy IE pixels 1874 e.detail && Math.abs(e.detail) < 32765 ? -e.detail * 20 : ( 1875 // Legacy Moz lines 1876 e.detail ? e.detail / -32765 * 60 : ( 1877 // Legacy Moz pages 1878 0 1879 ) 1880 ) 1881 ) 1882 ) 1883 ) 1884 ) 1885 ) 1886 ); 1887 } 1888 function isExternalTarget(el, e) { 1889 var related = e.relatedTarget; 1890 if (!related) { 1891 return true; 1892 } 1893 try { 1894 while (related && related !== el) { 1895 related = related.parentNode; 1896 } 1897 } catch (err) { 1898 return false; 1899 } 1900 return related !== el; 1901 } 1902 var DomEvent = { 1903 __proto__: null, 1904 on, 1905 off, 1906 stopPropagation, 1907 disableScrollPropagation, 1908 disableClickPropagation, 1909 preventDefault, 1910 stop, 1911 getPropagationPath, 1912 getMousePosition, 1913 getWheelDelta, 1914 isExternalTarget, 1915 addListener: on, 1916 removeListener: off 1917 }; 1918 var PosAnimation = Evented.extend({ 1919 // @method run(el: HTMLElement, newPos: Point, duration?: Number, easeLinearity?: Number) 1920 // Run an animation of a given element to a new position, optionally setting 1921 // duration in seconds (`0.25` by default) and easing linearity factor (3rd 1922 // argument of the [cubic bezier curve](https://cubic-bezier.com/#0,0,.5,1), 1923 // `0.5` by default). 1924 run: function(el, newPos, duration, easeLinearity) { 1925 this.stop(); 1926 this._el = el; 1927 this._inProgress = true; 1928 this._duration = duration || 0.25; 1929 this._easeOutPower = 1 / Math.max(easeLinearity || 0.5, 0.2); 1930 this._startPos = getPosition(el); 1931 this._offset = newPos.subtract(this._startPos); 1932 this._startTime = +/* @__PURE__ */ new Date(); 1933 this.fire("start"); 1934 this._animate(); 1935 }, 1936 // @method stop() 1937 // Stops the animation (if currently running). 1938 stop: function() { 1939 if (!this._inProgress) { 1940 return; 1941 } 1942 this._step(true); 1943 this._complete(); 1944 }, 1945 _animate: function() { 1946 this._animId = requestAnimFrame(this._animate, this); 1947 this._step(); 1948 }, 1949 _step: function(round) { 1950 var elapsed = +/* @__PURE__ */ new Date() - this._startTime, duration = this._duration * 1e3; 1951 if (elapsed < duration) { 1952 this._runFrame(this._easeOut(elapsed / duration), round); 1953 } else { 1954 this._runFrame(1); 1955 this._complete(); 1956 } 1957 }, 1958 _runFrame: function(progress, round) { 1959 var pos = this._startPos.add(this._offset.multiplyBy(progress)); 1960 if (round) { 1961 pos._round(); 1962 } 1963 setPosition(this._el, pos); 1964 this.fire("step"); 1965 }, 1966 _complete: function() { 1967 cancelAnimFrame(this._animId); 1968 this._inProgress = false; 1969 this.fire("end"); 1970 }, 1971 _easeOut: function(t) { 1972 return 1 - Math.pow(1 - t, this._easeOutPower); 1973 } 1974 }); 1975 var Map2 = Evented.extend({ 1976 options: { 1977 // @section Map State Options 1978 // @option crs: CRS = L.CRS.EPSG3857 1979 // The [Coordinate Reference System](#crs) to use. Don't change this if you're not 1980 // sure what it means. 1981 crs: EPSG3857, 1982 // @option center: LatLng = undefined 1983 // Initial geographic center of the map 1984 center: void 0, 1985 // @option zoom: Number = undefined 1986 // Initial map zoom level 1987 zoom: void 0, 1988 // @option minZoom: Number = * 1989 // Minimum zoom level of the map. 1990 // If not specified and at least one `GridLayer` or `TileLayer` is in the map, 1991 // the lowest of their `minZoom` options will be used instead. 1992 minZoom: void 0, 1993 // @option maxZoom: Number = * 1994 // Maximum zoom level of the map. 1995 // If not specified and at least one `GridLayer` or `TileLayer` is in the map, 1996 // the highest of their `maxZoom` options will be used instead. 1997 maxZoom: void 0, 1998 // @option layers: Layer[] = [] 1999 // Array of layers that will be added to the map initially 2000 layers: [], 2001 // @option maxBounds: LatLngBounds = null 2002 // When this option is set, the map restricts the view to the given 2003 // geographical bounds, bouncing the user back if the user tries to pan 2004 // outside the view. To set the restriction dynamically, use 2005 // [`setMaxBounds`](#map-setmaxbounds) method. 2006 maxBounds: void 0, 2007 // @option renderer: Renderer = * 2008 // The default method for drawing vector layers on the map. `L.SVG` 2009 // or `L.Canvas` by default depending on browser support. 2010 renderer: void 0, 2011 // @section Animation Options 2012 // @option zoomAnimation: Boolean = true 2013 // Whether the map zoom animation is enabled. By default it's enabled 2014 // in all browsers that support CSS3 Transitions except Android. 2015 zoomAnimation: true, 2016 // @option zoomAnimationThreshold: Number = 4 2017 // Won't animate zoom if the zoom difference exceeds this value. 2018 zoomAnimationThreshold: 4, 2019 // @option fadeAnimation: Boolean = true 2020 // Whether the tile fade animation is enabled. By default it's enabled 2021 // in all browsers that support CSS3 Transitions except Android. 2022 fadeAnimation: true, 2023 // @option markerZoomAnimation: Boolean = true 2024 // Whether markers animate their zoom with the zoom animation, if disabled 2025 // they will disappear for the length of the animation. By default it's 2026 // enabled in all browsers that support CSS3 Transitions except Android. 2027 markerZoomAnimation: true, 2028 // @option transform3DLimit: Number = 2^23 2029 // Defines the maximum size of a CSS translation transform. The default 2030 // value should not be changed unless a web browser positions layers in 2031 // the wrong place after doing a large `panBy`. 2032 transform3DLimit: 8388608, 2033 // Precision limit of a 32-bit float 2034 // @section Interaction Options 2035 // @option zoomSnap: Number = 1 2036 // Forces the map's zoom level to always be a multiple of this, particularly 2037 // right after a [`fitBounds()`](#map-fitbounds) or a pinch-zoom. 2038 // By default, the zoom level snaps to the nearest integer; lower values 2039 // (e.g. `0.5` or `0.1`) allow for greater granularity. A value of `0` 2040 // means the zoom level will not be snapped after `fitBounds` or a pinch-zoom. 2041 zoomSnap: 1, 2042 // @option zoomDelta: Number = 1 2043 // Controls how much the map's zoom level will change after a 2044 // [`zoomIn()`](#map-zoomin), [`zoomOut()`](#map-zoomout), pressing `+` 2045 // or `-` on the keyboard, or using the [zoom controls](#control-zoom). 2046 // Values smaller than `1` (e.g. `0.5`) allow for greater granularity. 2047 zoomDelta: 1, 2048 // @option trackResize: Boolean = true 2049 // Whether the map automatically handles browser window resize to update itself. 2050 trackResize: true 2051 }, 2052 initialize: function(id, options) { 2053 options = setOptions(this, options); 2054 this._handlers = []; 2055 this._layers = {}; 2056 this._zoomBoundLayers = {}; 2057 this._sizeChanged = true; 2058 this._initContainer(id); 2059 this._initLayout(); 2060 this._onResize = bind(this._onResize, this); 2061 this._initEvents(); 2062 if (options.maxBounds) { 2063 this.setMaxBounds(options.maxBounds); 2064 } 2065 if (options.zoom !== void 0) { 2066 this._zoom = this._limitZoom(options.zoom); 2067 } 2068 if (options.center && options.zoom !== void 0) { 2069 this.setView(toLatLng(options.center), options.zoom, { reset: true }); 2070 } 2071 this.callInitHooks(); 2072 this._zoomAnimated = TRANSITION && Browser.any3d && !Browser.mobileOpera && this.options.zoomAnimation; 2073 if (this._zoomAnimated) { 2074 this._createAnimProxy(); 2075 on(this._proxy, TRANSITION_END, this._catchTransitionEnd, this); 2076 } 2077 this._addLayers(this.options.layers); 2078 }, 2079 // @section Methods for modifying map state 2080 // @method setView(center: LatLng, zoom: Number, options?: Zoom/pan options): this 2081 // Sets the view of the map (geographical center and zoom) with the given 2082 // animation options. 2083 setView: function(center, zoom2, options) { 2084 zoom2 = zoom2 === void 0 ? this._zoom : this._limitZoom(zoom2); 2085 center = this._limitCenter(toLatLng(center), zoom2, this.options.maxBounds); 2086 options = options || {}; 2087 this._stop(); 2088 if (this._loaded && !options.reset && options !== true) { 2089 if (options.animate !== void 0) { 2090 options.zoom = extend({ animate: options.animate }, options.zoom); 2091 options.pan = extend({ animate: options.animate, duration: options.duration }, options.pan); 2092 } 2093 var moved = this._zoom !== zoom2 ? this._tryAnimatedZoom && this._tryAnimatedZoom(center, zoom2, options.zoom) : this._tryAnimatedPan(center, options.pan); 2094 if (moved) { 2095 clearTimeout(this._sizeTimer); 2096 return this; 2097 } 2098 } 2099 this._resetView(center, zoom2, options.pan && options.pan.noMoveStart); 2100 return this; 2101 }, 2102 // @method setZoom(zoom: Number, options?: Zoom/pan options): this 2103 // Sets the zoom of the map. 2104 setZoom: function(zoom2, options) { 2105 if (!this._loaded) { 2106 this._zoom = zoom2; 2107 return this; 2108 } 2109 return this.setView(this.getCenter(), zoom2, { zoom: options }); 2110 }, 2111 // @method zoomIn(delta?: Number, options?: Zoom options): this 2112 // Increases the zoom of the map by `delta` ([`zoomDelta`](#map-zoomdelta) by default). 2113 zoomIn: function(delta, options) { 2114 delta = delta || (Browser.any3d ? this.options.zoomDelta : 1); 2115 return this.setZoom(this._zoom + delta, options); 2116 }, 2117 // @method zoomOut(delta?: Number, options?: Zoom options): this 2118 // Decreases the zoom of the map by `delta` ([`zoomDelta`](#map-zoomdelta) by default). 2119 zoomOut: function(delta, options) { 2120 delta = delta || (Browser.any3d ? this.options.zoomDelta : 1); 2121 return this.setZoom(this._zoom - delta, options); 2122 }, 2123 // @method setZoomAround(latlng: LatLng, zoom: Number, options: Zoom options): this 2124 // Zooms the map while keeping a specified geographical point on the map 2125 // stationary (e.g. used internally for scroll zoom and double-click zoom). 2126 // @alternative 2127 // @method setZoomAround(offset: Point, zoom: Number, options: Zoom options): this 2128 // Zooms the map while keeping a specified pixel on the map (relative to the top-left corner) stationary. 2129 setZoomAround: function(latlng, zoom2, options) { 2130 var scale2 = this.getZoomScale(zoom2), viewHalf = this.getSize().divideBy(2), containerPoint = latlng instanceof Point ? latlng : this.latLngToContainerPoint(latlng), centerOffset = containerPoint.subtract(viewHalf).multiplyBy(1 - 1 / scale2), newCenter = this.containerPointToLatLng(viewHalf.add(centerOffset)); 2131 return this.setView(newCenter, zoom2, { zoom: options }); 2132 }, 2133 _getBoundsCenterZoom: function(bounds, options) { 2134 options = options || {}; 2135 bounds = bounds.getBounds ? bounds.getBounds() : toLatLngBounds(bounds); 2136 var paddingTL = toPoint(options.paddingTopLeft || options.padding || [0, 0]), paddingBR = toPoint(options.paddingBottomRight || options.padding || [0, 0]), zoom2 = this.getBoundsZoom(bounds, false, paddingTL.add(paddingBR)); 2137 zoom2 = typeof options.maxZoom === "number" ? Math.min(options.maxZoom, zoom2) : zoom2; 2138 if (zoom2 === Infinity) { 2139 return { 2140 center: bounds.getCenter(), 2141 zoom: zoom2 2142 }; 2143 } 2144 var paddingOffset = paddingBR.subtract(paddingTL).divideBy(2), swPoint = this.project(bounds.getSouthWest(), zoom2), nePoint = this.project(bounds.getNorthEast(), zoom2), center = this.unproject(swPoint.add(nePoint).divideBy(2).add(paddingOffset), zoom2); 2145 return { 2146 center, 2147 zoom: zoom2 2148 }; 2149 }, 2150 // @method fitBounds(bounds: LatLngBounds, options?: fitBounds options): this 2151 // Sets a map view that contains the given geographical bounds with the 2152 // maximum zoom level possible. 2153 fitBounds: function(bounds, options) { 2154 bounds = toLatLngBounds(bounds); 2155 if (!bounds.isValid()) { 2156 throw new Error("Bounds are not valid."); 2157 } 2158 var target = this._getBoundsCenterZoom(bounds, options); 2159 return this.setView(target.center, target.zoom, options); 2160 }, 2161 // @method fitWorld(options?: fitBounds options): this 2162 // Sets a map view that mostly contains the whole world with the maximum 2163 // zoom level possible. 2164 fitWorld: function(options) { 2165 return this.fitBounds([[-90, -180], [90, 180]], options); 2166 }, 2167 // @method panTo(latlng: LatLng, options?: Pan options): this 2168 // Pans the map to a given center. 2169 panTo: function(center, options) { 2170 return this.setView(center, this._zoom, { pan: options }); 2171 }, 2172 // @method panBy(offset: Point, options?: Pan options): this 2173 // Pans the map by a given number of pixels (animated). 2174 panBy: function(offset, options) { 2175 offset = toPoint(offset).round(); 2176 options = options || {}; 2177 if (!offset.x && !offset.y) { 2178 return this.fire("moveend"); 2179 } 2180 if (options.animate !== true && !this.getSize().contains(offset)) { 2181 this._resetView(this.unproject(this.project(this.getCenter()).add(offset)), this.getZoom()); 2182 return this; 2183 } 2184 if (!this._panAnim) { 2185 this._panAnim = new PosAnimation(); 2186 this._panAnim.on({ 2187 "step": this._onPanTransitionStep, 2188 "end": this._onPanTransitionEnd 2189 }, this); 2190 } 2191 if (!options.noMoveStart) { 2192 this.fire("movestart"); 2193 } 2194 if (options.animate !== false) { 2195 addClass(this._mapPane, "leaflet-pan-anim"); 2196 var newPos = this._getMapPanePos().subtract(offset).round(); 2197 this._panAnim.run(this._mapPane, newPos, options.duration || 0.25, options.easeLinearity); 2198 } else { 2199 this._rawPanBy(offset); 2200 this.fire("move").fire("moveend"); 2201 } 2202 return this; 2203 }, 2204 // @method flyTo(latlng: LatLng, zoom?: Number, options?: Zoom/pan options): this 2205 // Sets the view of the map (geographical center and zoom) performing a smooth 2206 // pan-zoom animation. 2207 flyTo: function(targetCenter, targetZoom, options) { 2208 options = options || {}; 2209 if (options.animate === false || !Browser.any3d) { 2210 return this.setView(targetCenter, targetZoom, options); 2211 } 2212 this._stop(); 2213 var from = this.project(this.getCenter()), to = this.project(targetCenter), size = this.getSize(), startZoom = this._zoom; 2214 targetCenter = toLatLng(targetCenter); 2215 targetZoom = targetZoom === void 0 ? startZoom : targetZoom; 2216 var w0 = Math.max(size.x, size.y), w1 = w0 * this.getZoomScale(startZoom, targetZoom), u1 = to.distanceTo(from) || 1, rho = 1.42, rho2 = rho * rho; 2217 function r(i) { 2218 var s1 = i ? -1 : 1, s2 = i ? w1 : w0, t1 = w1 * w1 - w0 * w0 + s1 * rho2 * rho2 * u1 * u1, b1 = 2 * s2 * rho2 * u1, b = t1 / b1, sq = Math.sqrt(b * b + 1) - b; 2219 var log = sq < 1e-9 ? -18 : Math.log(sq); 2220 return log; 2221 } 2222 function sinh(n) { 2223 return (Math.exp(n) - Math.exp(-n)) / 2; 2224 } 2225 function cosh(n) { 2226 return (Math.exp(n) + Math.exp(-n)) / 2; 2227 } 2228 function tanh(n) { 2229 return sinh(n) / cosh(n); 2230 } 2231 var r0 = r(0); 2232 function w(s) { 2233 return w0 * (cosh(r0) / cosh(r0 + rho * s)); 2234 } 2235 function u(s) { 2236 return w0 * (cosh(r0) * tanh(r0 + rho * s) - sinh(r0)) / rho2; 2237 } 2238 function easeOut(t) { 2239 return 1 - Math.pow(1 - t, 1.5); 2240 } 2241 var start = Date.now(), S = (r(1) - r0) / rho, duration = options.duration ? 1e3 * options.duration : 1e3 * S * 0.8; 2242 function frame() { 2243 var t = (Date.now() - start) / duration, s = easeOut(t) * S; 2244 if (t <= 1) { 2245 this._flyToFrame = requestAnimFrame(frame, this); 2246 this._move( 2247 this.unproject(from.add(to.subtract(from).multiplyBy(u(s) / u1)), startZoom), 2248 this.getScaleZoom(w0 / w(s), startZoom), 2249 { flyTo: true } 2250 ); 2251 } else { 2252 this._move(targetCenter, targetZoom)._moveEnd(true); 2253 } 2254 } 2255 this._moveStart(true, options.noMoveStart); 2256 frame.call(this); 2257 return this; 2258 }, 2259 // @method flyToBounds(bounds: LatLngBounds, options?: fitBounds options): this 2260 // Sets the view of the map with a smooth animation like [`flyTo`](#map-flyto), 2261 // but takes a bounds parameter like [`fitBounds`](#map-fitbounds). 2262 flyToBounds: function(bounds, options) { 2263 var target = this._getBoundsCenterZoom(bounds, options); 2264 return this.flyTo(target.center, target.zoom, options); 2265 }, 2266 // @method setMaxBounds(bounds: LatLngBounds): this 2267 // Restricts the map view to the given bounds (see the [maxBounds](#map-maxbounds) option). 2268 setMaxBounds: function(bounds) { 2269 bounds = toLatLngBounds(bounds); 2270 if (this.listens("moveend", this._panInsideMaxBounds)) { 2271 this.off("moveend", this._panInsideMaxBounds); 2272 } 2273 if (!bounds.isValid()) { 2274 this.options.maxBounds = null; 2275 return this; 2276 } 2277 this.options.maxBounds = bounds; 2278 if (this._loaded) { 2279 this._panInsideMaxBounds(); 2280 } 2281 return this.on("moveend", this._panInsideMaxBounds); 2282 }, 2283 // @method setMinZoom(zoom: Number): this 2284 // Sets the lower limit for the available zoom levels (see the [minZoom](#map-minzoom) option). 2285 setMinZoom: function(zoom2) { 2286 var oldZoom = this.options.minZoom; 2287 this.options.minZoom = zoom2; 2288 if (this._loaded && oldZoom !== zoom2) { 2289 this.fire("zoomlevelschange"); 2290 if (this.getZoom() < this.options.minZoom) { 2291 return this.setZoom(zoom2); 2292 } 2293 } 2294 return this; 2295 }, 2296 // @method setMaxZoom(zoom: Number): this 2297 // Sets the upper limit for the available zoom levels (see the [maxZoom](#map-maxzoom) option). 2298 setMaxZoom: function(zoom2) { 2299 var oldZoom = this.options.maxZoom; 2300 this.options.maxZoom = zoom2; 2301 if (this._loaded && oldZoom !== zoom2) { 2302 this.fire("zoomlevelschange"); 2303 if (this.getZoom() > this.options.maxZoom) { 2304 return this.setZoom(zoom2); 2305 } 2306 } 2307 return this; 2308 }, 2309 // @method panInsideBounds(bounds: LatLngBounds, options?: Pan options): this 2310 // Pans the map to the closest view that would lie inside the given bounds (if it's not already), controlling the animation using the options specific, if any. 2311 panInsideBounds: function(bounds, options) { 2312 this._enforcingBounds = true; 2313 var center = this.getCenter(), newCenter = this._limitCenter(center, this._zoom, toLatLngBounds(bounds)); 2314 if (!center.equals(newCenter)) { 2315 this.panTo(newCenter, options); 2316 } 2317 this._enforcingBounds = false; 2318 return this; 2319 }, 2320 // @method panInside(latlng: LatLng, options?: padding options): this 2321 // Pans the map the minimum amount to make the `latlng` visible. Use 2322 // padding options to fit the display to more restricted bounds. 2323 // If `latlng` is already within the (optionally padded) display bounds, 2324 // the map will not be panned. 2325 panInside: function(latlng, options) { 2326 options = options || {}; 2327 var paddingTL = toPoint(options.paddingTopLeft || options.padding || [0, 0]), paddingBR = toPoint(options.paddingBottomRight || options.padding || [0, 0]), pixelCenter = this.project(this.getCenter()), pixelPoint = this.project(latlng), pixelBounds = this.getPixelBounds(), paddedBounds = toBounds([pixelBounds.min.add(paddingTL), pixelBounds.max.subtract(paddingBR)]), paddedSize = paddedBounds.getSize(); 2328 if (!paddedBounds.contains(pixelPoint)) { 2329 this._enforcingBounds = true; 2330 var centerOffset = pixelPoint.subtract(paddedBounds.getCenter()); 2331 var offset = paddedBounds.extend(pixelPoint).getSize().subtract(paddedSize); 2332 pixelCenter.x += centerOffset.x < 0 ? -offset.x : offset.x; 2333 pixelCenter.y += centerOffset.y < 0 ? -offset.y : offset.y; 2334 this.panTo(this.unproject(pixelCenter), options); 2335 this._enforcingBounds = false; 2336 } 2337 return this; 2338 }, 2339 // @method invalidateSize(options: Zoom/pan options): this 2340 // Checks if the map container size changed and updates the map if so — 2341 // call it after you've changed the map size dynamically, also animating 2342 // pan by default. If `options.pan` is `false`, panning will not occur. 2343 // If `options.debounceMoveend` is `true`, it will delay `moveend` event so 2344 // that it doesn't happen often even if the method is called many 2345 // times in a row. 2346 // @alternative 2347 // @method invalidateSize(animate: Boolean): this 2348 // Checks if the map container size changed and updates the map if so — 2349 // call it after you've changed the map size dynamically, also animating 2350 // pan by default. 2351 invalidateSize: function(options) { 2352 if (!this._loaded) { 2353 return this; 2354 } 2355 options = extend({ 2356 animate: false, 2357 pan: true 2358 }, options === true ? { animate: true } : options); 2359 var oldSize = this.getSize(); 2360 this._sizeChanged = true; 2361 this._lastCenter = null; 2362 var newSize = this.getSize(), oldCenter = oldSize.divideBy(2).round(), newCenter = newSize.divideBy(2).round(), offset = oldCenter.subtract(newCenter); 2363 if (!offset.x && !offset.y) { 2364 return this; 2365 } 2366 if (options.animate && options.pan) { 2367 this.panBy(offset); 2368 } else { 2369 if (options.pan) { 2370 this._rawPanBy(offset); 2371 } 2372 this.fire("move"); 2373 if (options.debounceMoveend) { 2374 clearTimeout(this._sizeTimer); 2375 this._sizeTimer = setTimeout(bind(this.fire, this, "moveend"), 200); 2376 } else { 2377 this.fire("moveend"); 2378 } 2379 } 2380 return this.fire("resize", { 2381 oldSize, 2382 newSize 2383 }); 2384 }, 2385 // @section Methods for modifying map state 2386 // @method stop(): this 2387 // Stops the currently running `panTo` or `flyTo` animation, if any. 2388 stop: function() { 2389 this.setZoom(this._limitZoom(this._zoom)); 2390 if (!this.options.zoomSnap) { 2391 this.fire("viewreset"); 2392 } 2393 return this._stop(); 2394 }, 2395 // @section Geolocation methods 2396 // @method locate(options?: Locate options): this 2397 // Tries to locate the user using the Geolocation API, firing a [`locationfound`](#map-locationfound) 2398 // event with location data on success or a [`locationerror`](#map-locationerror) event on failure, 2399 // and optionally sets the map view to the user's location with respect to 2400 // detection accuracy (or to the world view if geolocation failed). 2401 // Note that, if your page doesn't use HTTPS, this method will fail in 2402 // modern browsers ([Chrome 50 and newer](https://sites.google.com/a/chromium.org/dev/Home/chromium-security/deprecating-powerful-features-on-insecure-origins)) 2403 // See `Locate options` for more details. 2404 locate: function(options) { 2405 options = this._locateOptions = extend({ 2406 timeout: 1e4, 2407 watch: false 2408 // setView: false 2409 // maxZoom: <Number> 2410 // maximumAge: 0 2411 // enableHighAccuracy: false 2412 }, options); 2413 if (!("geolocation" in navigator)) { 2414 this._handleGeolocationError({ 2415 code: 0, 2416 message: "Geolocation not supported." 2417 }); 2418 return this; 2419 } 2420 var onResponse = bind(this._handleGeolocationResponse, this), onError = bind(this._handleGeolocationError, this); 2421 if (options.watch) { 2422 this._locationWatchId = navigator.geolocation.watchPosition(onResponse, onError, options); 2423 } else { 2424 navigator.geolocation.getCurrentPosition(onResponse, onError, options); 2425 } 2426 return this; 2427 }, 2428 // @method stopLocate(): this 2429 // Stops watching location previously initiated by `map.locate({watch: true})` 2430 // and aborts resetting the map view if map.locate was called with 2431 // `{setView: true}`. 2432 stopLocate: function() { 2433 if (navigator.geolocation && navigator.geolocation.clearWatch) { 2434 navigator.geolocation.clearWatch(this._locationWatchId); 2435 } 2436 if (this._locateOptions) { 2437 this._locateOptions.setView = false; 2438 } 2439 return this; 2440 }, 2441 _handleGeolocationError: function(error) { 2442 if (!this._container._leaflet_id) { 2443 return; 2444 } 2445 var c = error.code, message = error.message || (c === 1 ? "permission denied" : c === 2 ? "position unavailable" : "timeout"); 2446 if (this._locateOptions.setView && !this._loaded) { 2447 this.fitWorld(); 2448 } 2449 this.fire("locationerror", { 2450 code: c, 2451 message: "Geolocation error: " + message + "." 2452 }); 2453 }, 2454 _handleGeolocationResponse: function(pos) { 2455 if (!this._container._leaflet_id) { 2456 return; 2457 } 2458 var lat = pos.coords.latitude, lng = pos.coords.longitude, latlng = new LatLng(lat, lng), bounds = latlng.toBounds(pos.coords.accuracy * 2), options = this._locateOptions; 2459 if (options.setView) { 2460 var zoom2 = this.getBoundsZoom(bounds); 2461 this.setView(latlng, options.maxZoom ? Math.min(zoom2, options.maxZoom) : zoom2); 2462 } 2463 var data = { 2464 latlng, 2465 bounds, 2466 timestamp: pos.timestamp 2467 }; 2468 for (var i in pos.coords) { 2469 if (typeof pos.coords[i] === "number") { 2470 data[i] = pos.coords[i]; 2471 } 2472 } 2473 this.fire("locationfound", data); 2474 }, 2475 // TODO Appropriate docs section? 2476 // @section Other Methods 2477 // @method addHandler(name: String, HandlerClass: Function): this 2478 // Adds a new `Handler` to the map, given its name and constructor function. 2479 addHandler: function(name, HandlerClass) { 2480 if (!HandlerClass) { 2481 return this; 2482 } 2483 var handler = this[name] = new HandlerClass(this); 2484 this._handlers.push(handler); 2485 if (this.options[name]) { 2486 handler.enable(); 2487 } 2488 return this; 2489 }, 2490 // @method remove(): this 2491 // Destroys the map and clears all related event listeners. 2492 remove: function() { 2493 this._initEvents(true); 2494 if (this.options.maxBounds) { 2495 this.off("moveend", this._panInsideMaxBounds); 2496 } 2497 if (this._containerId !== this._container._leaflet_id) { 2498 throw new Error("Map container is being reused by another instance"); 2499 } 2500 try { 2501 delete this._container._leaflet_id; 2502 delete this._containerId; 2503 } catch (e) { 2504 this._container._leaflet_id = void 0; 2505 this._containerId = void 0; 2506 } 2507 if (this._locationWatchId !== void 0) { 2508 this.stopLocate(); 2509 } 2510 this._stop(); 2511 remove(this._mapPane); 2512 if (this._clearControlPos) { 2513 this._clearControlPos(); 2514 } 2515 if (this._resizeRequest) { 2516 cancelAnimFrame(this._resizeRequest); 2517 this._resizeRequest = null; 2518 } 2519 this._clearHandlers(); 2520 if (this._loaded) { 2521 this.fire("unload"); 2522 } 2523 var i; 2524 for (i in this._layers) { 2525 this._layers[i].remove(); 2526 } 2527 for (i in this._panes) { 2528 remove(this._panes[i]); 2529 } 2530 this._layers = []; 2531 this._panes = []; 2532 delete this._mapPane; 2533 delete this._renderer; 2534 return this; 2535 }, 2536 // @section Other Methods 2537 // @method createPane(name: String, container?: HTMLElement): HTMLElement 2538 // Creates a new [map pane](#map-pane) with the given name if it doesn't exist already, 2539 // then returns it. The pane is created as a child of `container`, or 2540 // as a child of the main map pane if not set. 2541 createPane: function(name, container) { 2542 var className = "leaflet-pane" + (name ? " leaflet-" + name.replace("Pane", "") + "-pane" : ""), pane = create$1("div", className, container || this._mapPane); 2543 if (name) { 2544 this._panes[name] = pane; 2545 } 2546 return pane; 2547 }, 2548 // @section Methods for Getting Map State 2549 // @method getCenter(): LatLng 2550 // Returns the geographical center of the map view 2551 getCenter: function() { 2552 this._checkIfLoaded(); 2553 if (this._lastCenter && !this._moved()) { 2554 return this._lastCenter.clone(); 2555 } 2556 return this.layerPointToLatLng(this._getCenterLayerPoint()); 2557 }, 2558 // @method getZoom(): Number 2559 // Returns the current zoom level of the map view 2560 getZoom: function() { 2561 return this._zoom; 2562 }, 2563 // @method getBounds(): LatLngBounds 2564 // Returns the geographical bounds visible in the current map view 2565 getBounds: function() { 2566 var bounds = this.getPixelBounds(), sw = this.unproject(bounds.getBottomLeft()), ne = this.unproject(bounds.getTopRight()); 2567 return new LatLngBounds(sw, ne); 2568 }, 2569 // @method getMinZoom(): Number 2570 // Returns the minimum zoom level of the map (if set in the `minZoom` option of the map or of any layers), or `0` by default. 2571 getMinZoom: function() { 2572 return this.options.minZoom === void 0 ? this._layersMinZoom || 0 : this.options.minZoom; 2573 }, 2574 // @method getMaxZoom(): Number 2575 // Returns the maximum zoom level of the map (if set in the `maxZoom` option of the map or of any layers). 2576 getMaxZoom: function() { 2577 return this.options.maxZoom === void 0 ? this._layersMaxZoom === void 0 ? Infinity : this._layersMaxZoom : this.options.maxZoom; 2578 }, 2579 // @method getBoundsZoom(bounds: LatLngBounds, inside?: Boolean, padding?: Point): Number 2580 // Returns the maximum zoom level on which the given bounds fit to the map 2581 // view in its entirety. If `inside` (optional) is set to `true`, the method 2582 // instead returns the minimum zoom level on which the map view fits into 2583 // the given bounds in its entirety. 2584 getBoundsZoom: function(bounds, inside, padding) { 2585 bounds = toLatLngBounds(bounds); 2586 padding = toPoint(padding || [0, 0]); 2587 var zoom2 = this.getZoom() || 0, min = this.getMinZoom(), max = this.getMaxZoom(), nw = bounds.getNorthWest(), se = bounds.getSouthEast(), size = this.getSize().subtract(padding), boundsSize = toBounds(this.project(se, zoom2), this.project(nw, zoom2)).getSize(), snap = Browser.any3d ? this.options.zoomSnap : 1, scalex = size.x / boundsSize.x, scaley = size.y / boundsSize.y, scale2 = inside ? Math.max(scalex, scaley) : Math.min(scalex, scaley); 2588 zoom2 = this.getScaleZoom(scale2, zoom2); 2589 if (snap) { 2590 zoom2 = Math.round(zoom2 / (snap / 100)) * (snap / 100); 2591 zoom2 = inside ? Math.ceil(zoom2 / snap) * snap : Math.floor(zoom2 / snap) * snap; 2592 } 2593 return Math.max(min, Math.min(max, zoom2)); 2594 }, 2595 // @method getSize(): Point 2596 // Returns the current size of the map container (in pixels). 2597 getSize: function() { 2598 if (!this._size || this._sizeChanged) { 2599 this._size = new Point( 2600 this._container.clientWidth || 0, 2601 this._container.clientHeight || 0 2602 ); 2603 this._sizeChanged = false; 2604 } 2605 return this._size.clone(); 2606 }, 2607 // @method getPixelBounds(): Bounds 2608 // Returns the bounds of the current map view in projected pixel 2609 // coordinates (sometimes useful in layer and overlay implementations). 2610 getPixelBounds: function(center, zoom2) { 2611 var topLeftPoint = this._getTopLeftPoint(center, zoom2); 2612 return new Bounds(topLeftPoint, topLeftPoint.add(this.getSize())); 2613 }, 2614 // TODO: Check semantics - isn't the pixel origin the 0,0 coord relative to 2615 // the map pane? "left point of the map layer" can be confusing, specially 2616 // since there can be negative offsets. 2617 // @method getPixelOrigin(): Point 2618 // Returns the projected pixel coordinates of the top left point of 2619 // the map layer (useful in custom layer and overlay implementations). 2620 getPixelOrigin: function() { 2621 this._checkIfLoaded(); 2622 return this._pixelOrigin; 2623 }, 2624 // @method getPixelWorldBounds(zoom?: Number): Bounds 2625 // Returns the world's bounds in pixel coordinates for zoom level `zoom`. 2626 // If `zoom` is omitted, the map's current zoom level is used. 2627 getPixelWorldBounds: function(zoom2) { 2628 return this.options.crs.getProjectedBounds(zoom2 === void 0 ? this.getZoom() : zoom2); 2629 }, 2630 // @section Other Methods 2631 // @method getPane(pane: String|HTMLElement): HTMLElement 2632 // Returns a [map pane](#map-pane), given its name or its HTML element (its identity). 2633 getPane: function(pane) { 2634 return typeof pane === "string" ? this._panes[pane] : pane; 2635 }, 2636 // @method getPanes(): Object 2637 // Returns a plain object containing the names of all [panes](#map-pane) as keys and 2638 // the panes as values. 2639 getPanes: function() { 2640 return this._panes; 2641 }, 2642 // @method getContainer: HTMLElement 2643 // Returns the HTML element that contains the map. 2644 getContainer: function() { 2645 return this._container; 2646 }, 2647 // @section Conversion Methods 2648 // @method getZoomScale(toZoom: Number, fromZoom: Number): Number 2649 // Returns the scale factor to be applied to a map transition from zoom level 2650 // `fromZoom` to `toZoom`. Used internally to help with zoom animations. 2651 getZoomScale: function(toZoom, fromZoom) { 2652 var crs = this.options.crs; 2653 fromZoom = fromZoom === void 0 ? this._zoom : fromZoom; 2654 return crs.scale(toZoom) / crs.scale(fromZoom); 2655 }, 2656 // @method getScaleZoom(scale: Number, fromZoom: Number): Number 2657 // Returns the zoom level that the map would end up at, if it is at `fromZoom` 2658 // level and everything is scaled by a factor of `scale`. Inverse of 2659 // [`getZoomScale`](#map-getZoomScale). 2660 getScaleZoom: function(scale2, fromZoom) { 2661 var crs = this.options.crs; 2662 fromZoom = fromZoom === void 0 ? this._zoom : fromZoom; 2663 var zoom2 = crs.zoom(scale2 * crs.scale(fromZoom)); 2664 return isNaN(zoom2) ? Infinity : zoom2; 2665 }, 2666 // @method project(latlng: LatLng, zoom: Number): Point 2667 // Projects a geographical coordinate `LatLng` according to the projection 2668 // of the map's CRS, then scales it according to `zoom` and the CRS's 2669 // `Transformation`. The result is pixel coordinate relative to 2670 // the CRS origin. 2671 project: function(latlng, zoom2) { 2672 zoom2 = zoom2 === void 0 ? this._zoom : zoom2; 2673 return this.options.crs.latLngToPoint(toLatLng(latlng), zoom2); 2674 }, 2675 // @method unproject(point: Point, zoom: Number): LatLng 2676 // Inverse of [`project`](#map-project). 2677 unproject: function(point, zoom2) { 2678 zoom2 = zoom2 === void 0 ? this._zoom : zoom2; 2679 return this.options.crs.pointToLatLng(toPoint(point), zoom2); 2680 }, 2681 // @method layerPointToLatLng(point: Point): LatLng 2682 // Given a pixel coordinate relative to the [origin pixel](#map-getpixelorigin), 2683 // returns the corresponding geographical coordinate (for the current zoom level). 2684 layerPointToLatLng: function(point) { 2685 var projectedPoint = toPoint(point).add(this.getPixelOrigin()); 2686 return this.unproject(projectedPoint); 2687 }, 2688 // @method latLngToLayerPoint(latlng: LatLng): Point 2689 // Given a geographical coordinate, returns the corresponding pixel coordinate 2690 // relative to the [origin pixel](#map-getpixelorigin). 2691 latLngToLayerPoint: function(latlng) { 2692 var projectedPoint = this.project(toLatLng(latlng))._round(); 2693 return projectedPoint._subtract(this.getPixelOrigin()); 2694 }, 2695 // @method wrapLatLng(latlng: LatLng): LatLng 2696 // Returns a `LatLng` where `lat` and `lng` has been wrapped according to the 2697 // map's CRS's `wrapLat` and `wrapLng` properties, if they are outside the 2698 // CRS's bounds. 2699 // By default this means longitude is wrapped around the dateline so its 2700 // value is between -180 and +180 degrees. 2701 wrapLatLng: function(latlng) { 2702 return this.options.crs.wrapLatLng(toLatLng(latlng)); 2703 }, 2704 // @method wrapLatLngBounds(bounds: LatLngBounds): LatLngBounds 2705 // Returns a `LatLngBounds` with the same size as the given one, ensuring that 2706 // its center is within the CRS's bounds. 2707 // By default this means the center longitude is wrapped around the dateline so its 2708 // value is between -180 and +180 degrees, and the majority of the bounds 2709 // overlaps the CRS's bounds. 2710 wrapLatLngBounds: function(latlng) { 2711 return this.options.crs.wrapLatLngBounds(toLatLngBounds(latlng)); 2712 }, 2713 // @method distance(latlng1: LatLng, latlng2: LatLng): Number 2714 // Returns the distance between two geographical coordinates according to 2715 // the map's CRS. By default this measures distance in meters. 2716 distance: function(latlng1, latlng2) { 2717 return this.options.crs.distance(toLatLng(latlng1), toLatLng(latlng2)); 2718 }, 2719 // @method containerPointToLayerPoint(point: Point): Point 2720 // Given a pixel coordinate relative to the map container, returns the corresponding 2721 // pixel coordinate relative to the [origin pixel](#map-getpixelorigin). 2722 containerPointToLayerPoint: function(point) { 2723 return toPoint(point).subtract(this._getMapPanePos()); 2724 }, 2725 // @method layerPointToContainerPoint(point: Point): Point 2726 // Given a pixel coordinate relative to the [origin pixel](#map-getpixelorigin), 2727 // returns the corresponding pixel coordinate relative to the map container. 2728 layerPointToContainerPoint: function(point) { 2729 return toPoint(point).add(this._getMapPanePos()); 2730 }, 2731 // @method containerPointToLatLng(point: Point): LatLng 2732 // Given a pixel coordinate relative to the map container, returns 2733 // the corresponding geographical coordinate (for the current zoom level). 2734 containerPointToLatLng: function(point) { 2735 var layerPoint = this.containerPointToLayerPoint(toPoint(point)); 2736 return this.layerPointToLatLng(layerPoint); 2737 }, 2738 // @method latLngToContainerPoint(latlng: LatLng): Point 2739 // Given a geographical coordinate, returns the corresponding pixel coordinate 2740 // relative to the map container. 2741 latLngToContainerPoint: function(latlng) { 2742 return this.layerPointToContainerPoint(this.latLngToLayerPoint(toLatLng(latlng))); 2743 }, 2744 // @method mouseEventToContainerPoint(ev: MouseEvent): Point 2745 // Given a MouseEvent object, returns the pixel coordinate relative to the 2746 // map container where the event took place. 2747 mouseEventToContainerPoint: function(e) { 2748 return getMousePosition(e, this._container); 2749 }, 2750 // @method mouseEventToLayerPoint(ev: MouseEvent): Point 2751 // Given a MouseEvent object, returns the pixel coordinate relative to 2752 // the [origin pixel](#map-getpixelorigin) where the event took place. 2753 mouseEventToLayerPoint: function(e) { 2754 return this.containerPointToLayerPoint(this.mouseEventToContainerPoint(e)); 2755 }, 2756 // @method mouseEventToLatLng(ev: MouseEvent): LatLng 2757 // Given a MouseEvent object, returns geographical coordinate where the 2758 // event took place. 2759 mouseEventToLatLng: function(e) { 2760 return this.layerPointToLatLng(this.mouseEventToLayerPoint(e)); 2761 }, 2762 // map initialization methods 2763 _initContainer: function(id) { 2764 var container = this._container = get(id); 2765 if (!container) { 2766 throw new Error("Map container not found."); 2767 } else if (container._leaflet_id) { 2768 throw new Error("Map container is already initialized."); 2769 } 2770 on(container, "scroll", this._onScroll, this); 2771 this._containerId = stamp(container); 2772 }, 2773 _initLayout: function() { 2774 var container = this._container; 2775 this._fadeAnimated = this.options.fadeAnimation && Browser.any3d; 2776 addClass(container, "leaflet-container" + (Browser.touch ? " leaflet-touch" : "") + (Browser.retina ? " leaflet-retina" : "") + (Browser.ielt9 ? " leaflet-oldie" : "") + (Browser.safari ? " leaflet-safari" : "") + (this._fadeAnimated ? " leaflet-fade-anim" : "")); 2777 var position = getStyle(container, "position"); 2778 if (position !== "absolute" && position !== "relative" && position !== "fixed" && position !== "sticky") { 2779 container.style.position = "relative"; 2780 } 2781 this._initPanes(); 2782 if (this._initControlPos) { 2783 this._initControlPos(); 2784 } 2785 }, 2786 _initPanes: function() { 2787 var panes = this._panes = {}; 2788 this._paneRenderers = {}; 2789 this._mapPane = this.createPane("mapPane", this._container); 2790 setPosition(this._mapPane, new Point(0, 0)); 2791 this.createPane("tilePane"); 2792 this.createPane("overlayPane"); 2793 this.createPane("shadowPane"); 2794 this.createPane("markerPane"); 2795 this.createPane("tooltipPane"); 2796 this.createPane("popupPane"); 2797 if (!this.options.markerZoomAnimation) { 2798 addClass(panes.markerPane, "leaflet-zoom-hide"); 2799 addClass(panes.shadowPane, "leaflet-zoom-hide"); 2800 } 2801 }, 2802 // private methods that modify map state 2803 // @section Map state change events 2804 _resetView: function(center, zoom2, noMoveStart) { 2805 setPosition(this._mapPane, new Point(0, 0)); 2806 var loading = !this._loaded; 2807 this._loaded = true; 2808 zoom2 = this._limitZoom(zoom2); 2809 this.fire("viewprereset"); 2810 var zoomChanged = this._zoom !== zoom2; 2811 this._moveStart(zoomChanged, noMoveStart)._move(center, zoom2)._moveEnd(zoomChanged); 2812 this.fire("viewreset"); 2813 if (loading) { 2814 this.fire("load"); 2815 } 2816 }, 2817 _moveStart: function(zoomChanged, noMoveStart) { 2818 if (zoomChanged) { 2819 this.fire("zoomstart"); 2820 } 2821 if (!noMoveStart) { 2822 this.fire("movestart"); 2823 } 2824 return this; 2825 }, 2826 _move: function(center, zoom2, data, supressEvent) { 2827 if (zoom2 === void 0) { 2828 zoom2 = this._zoom; 2829 } 2830 var zoomChanged = this._zoom !== zoom2; 2831 this._zoom = zoom2; 2832 this._lastCenter = center; 2833 this._pixelOrigin = this._getNewPixelOrigin(center); 2834 if (!supressEvent) { 2835 if (zoomChanged || data && data.pinch) { 2836 this.fire("zoom", data); 2837 } 2838 this.fire("move", data); 2839 } else if (data && data.pinch) { 2840 this.fire("zoom", data); 2841 } 2842 return this; 2843 }, 2844 _moveEnd: function(zoomChanged) { 2845 if (zoomChanged) { 2846 this.fire("zoomend"); 2847 } 2848 return this.fire("moveend"); 2849 }, 2850 _stop: function() { 2851 cancelAnimFrame(this._flyToFrame); 2852 if (this._panAnim) { 2853 this._panAnim.stop(); 2854 } 2855 return this; 2856 }, 2857 _rawPanBy: function(offset) { 2858 setPosition(this._mapPane, this._getMapPanePos().subtract(offset)); 2859 }, 2860 _getZoomSpan: function() { 2861 return this.getMaxZoom() - this.getMinZoom(); 2862 }, 2863 _panInsideMaxBounds: function() { 2864 if (!this._enforcingBounds) { 2865 this.panInsideBounds(this.options.maxBounds); 2866 } 2867 }, 2868 _checkIfLoaded: function() { 2869 if (!this._loaded) { 2870 throw new Error("Set map center and zoom first."); 2871 } 2872 }, 2873 // DOM event handling 2874 // @section Interaction events 2875 _initEvents: function(remove2) { 2876 this._targets = {}; 2877 this._targets[stamp(this._container)] = this; 2878 var onOff = remove2 ? off : on; 2879 onOff(this._container, "click dblclick mousedown mouseup mouseover mouseout mousemove contextmenu keypress keydown keyup", this._handleDOMEvent, this); 2880 if (this.options.trackResize) { 2881 onOff(window, "resize", this._onResize, this); 2882 } 2883 if (Browser.any3d && this.options.transform3DLimit) { 2884 (remove2 ? this.off : this.on).call(this, "moveend", this._onMoveEnd); 2885 } 2886 }, 2887 _onResize: function() { 2888 cancelAnimFrame(this._resizeRequest); 2889 this._resizeRequest = requestAnimFrame( 2890 function() { 2891 this.invalidateSize({ debounceMoveend: true }); 2892 }, 2893 this 2894 ); 2895 }, 2896 _onScroll: function() { 2897 this._container.scrollTop = 0; 2898 this._container.scrollLeft = 0; 2899 }, 2900 _onMoveEnd: function() { 2901 var pos = this._getMapPanePos(); 2902 if (Math.max(Math.abs(pos.x), Math.abs(pos.y)) >= this.options.transform3DLimit) { 2903 this._resetView(this.getCenter(), this.getZoom()); 2904 } 2905 }, 2906 _findEventTargets: function(e, type) { 2907 var targets = [], target, isHover = type === "mouseout" || type === "mouseover", src = e.target || e.srcElement, dragging = false; 2908 while (src) { 2909 target = this._targets[stamp(src)]; 2910 if (target && (type === "click" || type === "preclick") && this._draggableMoved(target)) { 2911 dragging = true; 2912 break; 2913 } 2914 if (target && target.listens(type, true)) { 2915 if (isHover && !isExternalTarget(src, e)) { 2916 break; 2917 } 2918 targets.push(target); 2919 if (isHover) { 2920 break; 2921 } 2922 } 2923 if (src === this._container) { 2924 break; 2925 } 2926 src = src.parentNode; 2927 } 2928 if (!targets.length && !dragging && !isHover && this.listens(type, true)) { 2929 targets = [this]; 2930 } 2931 return targets; 2932 }, 2933 _isClickDisabled: function(el) { 2934 while (el && el !== this._container) { 2935 if (el["_leaflet_disable_click"]) { 2936 return true; 2937 } 2938 el = el.parentNode; 2939 } 2940 }, 2941 _handleDOMEvent: function(e) { 2942 var el = e.target || e.srcElement; 2943 if (!this._loaded || el["_leaflet_disable_events"] || e.type === "click" && this._isClickDisabled(el)) { 2944 return; 2945 } 2946 var type = e.type; 2947 if (type === "mousedown") { 2948 preventOutline(el); 2949 } 2950 this._fireDOMEvent(e, type); 2951 }, 2952 _mouseEvents: ["click", "dblclick", "mouseover", "mouseout", "contextmenu"], 2953 _fireDOMEvent: function(e, type, canvasTargets) { 2954 if (e.type === "click") { 2955 var synth = extend({}, e); 2956 synth.type = "preclick"; 2957 this._fireDOMEvent(synth, synth.type, canvasTargets); 2958 } 2959 var targets = this._findEventTargets(e, type); 2960 if (canvasTargets) { 2961 var filtered = []; 2962 for (var i = 0; i < canvasTargets.length; i++) { 2963 if (canvasTargets[i].listens(type, true)) { 2964 filtered.push(canvasTargets[i]); 2965 } 2966 } 2967 targets = filtered.concat(targets); 2968 } 2969 if (!targets.length) { 2970 return; 2971 } 2972 if (type === "contextmenu") { 2973 preventDefault(e); 2974 } 2975 var target = targets[0]; 2976 var data = { 2977 originalEvent: e 2978 }; 2979 if (e.type !== "keypress" && e.type !== "keydown" && e.type !== "keyup") { 2980 var isMarker = target.getLatLng && (!target._radius || target._radius <= 10); 2981 data.containerPoint = isMarker ? this.latLngToContainerPoint(target.getLatLng()) : this.mouseEventToContainerPoint(e); 2982 data.layerPoint = this.containerPointToLayerPoint(data.containerPoint); 2983 data.latlng = isMarker ? target.getLatLng() : this.layerPointToLatLng(data.layerPoint); 2984 } 2985 for (i = 0; i < targets.length; i++) { 2986 targets[i].fire(type, data, true); 2987 if (data.originalEvent._stopped || targets[i].options.bubblingMouseEvents === false && indexOf(this._mouseEvents, type) !== -1) { 2988 return; 2989 } 2990 } 2991 }, 2992 _draggableMoved: function(obj) { 2993 obj = obj.dragging && obj.dragging.enabled() ? obj : this; 2994 return obj.dragging && obj.dragging.moved() || this.boxZoom && this.boxZoom.moved(); 2995 }, 2996 _clearHandlers: function() { 2997 for (var i = 0, len = this._handlers.length; i < len; i++) { 2998 this._handlers[i].disable(); 2999 } 3000 }, 3001 // @section Other Methods 3002 // @method whenReady(fn: Function, context?: Object): this 3003 // Runs the given function `fn` when the map gets initialized with 3004 // a view (center and zoom) and at least one layer, or immediately 3005 // if it's already initialized, optionally passing a function context. 3006 whenReady: function(callback, context) { 3007 if (this._loaded) { 3008 callback.call(context || this, { target: this }); 3009 } else { 3010 this.on("load", callback, context); 3011 } 3012 return this; 3013 }, 3014 // private methods for getting map state 3015 _getMapPanePos: function() { 3016 return getPosition(this._mapPane) || new Point(0, 0); 3017 }, 3018 _moved: function() { 3019 var pos = this._getMapPanePos(); 3020 return pos && !pos.equals([0, 0]); 3021 }, 3022 _getTopLeftPoint: function(center, zoom2) { 3023 var pixelOrigin = center && zoom2 !== void 0 ? this._getNewPixelOrigin(center, zoom2) : this.getPixelOrigin(); 3024 return pixelOrigin.subtract(this._getMapPanePos()); 3025 }, 3026 _getNewPixelOrigin: function(center, zoom2) { 3027 var viewHalf = this.getSize()._divideBy(2); 3028 return this.project(center, zoom2)._subtract(viewHalf)._add(this._getMapPanePos())._round(); 3029 }, 3030 _latLngToNewLayerPoint: function(latlng, zoom2, center) { 3031 var topLeft = this._getNewPixelOrigin(center, zoom2); 3032 return this.project(latlng, zoom2)._subtract(topLeft); 3033 }, 3034 _latLngBoundsToNewLayerBounds: function(latLngBounds, zoom2, center) { 3035 var topLeft = this._getNewPixelOrigin(center, zoom2); 3036 return toBounds([ 3037 this.project(latLngBounds.getSouthWest(), zoom2)._subtract(topLeft), 3038 this.project(latLngBounds.getNorthWest(), zoom2)._subtract(topLeft), 3039 this.project(latLngBounds.getSouthEast(), zoom2)._subtract(topLeft), 3040 this.project(latLngBounds.getNorthEast(), zoom2)._subtract(topLeft) 3041 ]); 3042 }, 3043 // layer point of the current center 3044 _getCenterLayerPoint: function() { 3045 return this.containerPointToLayerPoint(this.getSize()._divideBy(2)); 3046 }, 3047 // offset of the specified place to the current center in pixels 3048 _getCenterOffset: function(latlng) { 3049 return this.latLngToLayerPoint(latlng).subtract(this._getCenterLayerPoint()); 3050 }, 3051 // adjust center for view to get inside bounds 3052 _limitCenter: function(center, zoom2, bounds) { 3053 if (!bounds) { 3054 return center; 3055 } 3056 var centerPoint = this.project(center, zoom2), viewHalf = this.getSize().divideBy(2), viewBounds = new Bounds(centerPoint.subtract(viewHalf), centerPoint.add(viewHalf)), offset = this._getBoundsOffset(viewBounds, bounds, zoom2); 3057 if (Math.abs(offset.x) <= 1 && Math.abs(offset.y) <= 1) { 3058 return center; 3059 } 3060 return this.unproject(centerPoint.add(offset), zoom2); 3061 }, 3062 // adjust offset for view to get inside bounds 3063 _limitOffset: function(offset, bounds) { 3064 if (!bounds) { 3065 return offset; 3066 } 3067 var viewBounds = this.getPixelBounds(), newBounds = new Bounds(viewBounds.min.add(offset), viewBounds.max.add(offset)); 3068 return offset.add(this._getBoundsOffset(newBounds, bounds)); 3069 }, 3070 // returns offset needed for pxBounds to get inside maxBounds at a specified zoom 3071 _getBoundsOffset: function(pxBounds, maxBounds, zoom2) { 3072 var projectedMaxBounds = toBounds( 3073 this.project(maxBounds.getNorthEast(), zoom2), 3074 this.project(maxBounds.getSouthWest(), zoom2) 3075 ), minOffset = projectedMaxBounds.min.subtract(pxBounds.min), maxOffset = projectedMaxBounds.max.subtract(pxBounds.max), dx = this._rebound(minOffset.x, -maxOffset.x), dy = this._rebound(minOffset.y, -maxOffset.y); 3076 return new Point(dx, dy); 3077 }, 3078 _rebound: function(left, right) { 3079 return left + right > 0 ? Math.round(left - right) / 2 : Math.max(0, Math.ceil(left)) - Math.max(0, Math.floor(right)); 3080 }, 3081 _limitZoom: function(zoom2) { 3082 var min = this.getMinZoom(), max = this.getMaxZoom(), snap = Browser.any3d ? this.options.zoomSnap : 1; 3083 if (snap) { 3084 zoom2 = Math.round(zoom2 / snap) * snap; 3085 } 3086 return Math.max(min, Math.min(max, zoom2)); 3087 }, 3088 _onPanTransitionStep: function() { 3089 this.fire("move"); 3090 }, 3091 _onPanTransitionEnd: function() { 3092 removeClass(this._mapPane, "leaflet-pan-anim"); 3093 this.fire("moveend"); 3094 }, 3095 _tryAnimatedPan: function(center, options) { 3096 var offset = this._getCenterOffset(center)._trunc(); 3097 if ((options && options.animate) !== true && !this.getSize().contains(offset)) { 3098 return false; 3099 } 3100 this.panBy(offset, options); 3101 return true; 3102 }, 3103 _createAnimProxy: function() { 3104 var proxy = this._proxy = create$1("div", "leaflet-proxy leaflet-zoom-animated"); 3105 this._panes.mapPane.appendChild(proxy); 3106 this.on("zoomanim", function(e) { 3107 var prop = TRANSFORM, transform = this._proxy.style[prop]; 3108 setTransform(this._proxy, this.project(e.center, e.zoom), this.getZoomScale(e.zoom, 1)); 3109 if (transform === this._proxy.style[prop] && this._animatingZoom) { 3110 this._onZoomTransitionEnd(); 3111 } 3112 }, this); 3113 this.on("load moveend", this._animMoveEnd, this); 3114 this._on("unload", this._destroyAnimProxy, this); 3115 }, 3116 _destroyAnimProxy: function() { 3117 remove(this._proxy); 3118 this.off("load moveend", this._animMoveEnd, this); 3119 delete this._proxy; 3120 }, 3121 _animMoveEnd: function() { 3122 var c = this.getCenter(), z = this.getZoom(); 3123 setTransform(this._proxy, this.project(c, z), this.getZoomScale(z, 1)); 3124 }, 3125 _catchTransitionEnd: function(e) { 3126 if (this._animatingZoom && e.propertyName.indexOf("transform") >= 0) { 3127 this._onZoomTransitionEnd(); 3128 } 3129 }, 3130 _nothingToAnimate: function() { 3131 return !this._container.getElementsByClassName("leaflet-zoom-animated").length; 3132 }, 3133 _tryAnimatedZoom: function(center, zoom2, options) { 3134 if (this._animatingZoom) { 3135 return true; 3136 } 3137 options = options || {}; 3138 if (!this._zoomAnimated || options.animate === false || this._nothingToAnimate() || Math.abs(zoom2 - this._zoom) > this.options.zoomAnimationThreshold) { 3139 return false; 3140 } 3141 var scale2 = this.getZoomScale(zoom2), offset = this._getCenterOffset(center)._divideBy(1 - 1 / scale2); 3142 if (options.animate !== true && !this.getSize().contains(offset)) { 3143 return false; 3144 } 3145 requestAnimFrame(function() { 3146 this._moveStart(true, options.noMoveStart || false)._animateZoom(center, zoom2, true); 3147 }, this); 3148 return true; 3149 }, 3150 _animateZoom: function(center, zoom2, startAnim, noUpdate) { 3151 if (!this._mapPane) { 3152 return; 3153 } 3154 if (startAnim) { 3155 this._animatingZoom = true; 3156 this._animateToCenter = center; 3157 this._animateToZoom = zoom2; 3158 addClass(this._mapPane, "leaflet-zoom-anim"); 3159 } 3160 this.fire("zoomanim", { 3161 center, 3162 zoom: zoom2, 3163 noUpdate 3164 }); 3165 if (!this._tempFireZoomEvent) { 3166 this._tempFireZoomEvent = this._zoom !== this._animateToZoom; 3167 } 3168 this._move(this._animateToCenter, this._animateToZoom, void 0, true); 3169 setTimeout(bind(this._onZoomTransitionEnd, this), 250); 3170 }, 3171 _onZoomTransitionEnd: function() { 3172 if (!this._animatingZoom) { 3173 return; 3174 } 3175 if (this._mapPane) { 3176 removeClass(this._mapPane, "leaflet-zoom-anim"); 3177 } 3178 this._animatingZoom = false; 3179 this._move(this._animateToCenter, this._animateToZoom, void 0, true); 3180 if (this._tempFireZoomEvent) { 3181 this.fire("zoom"); 3182 } 3183 delete this._tempFireZoomEvent; 3184 this.fire("move"); 3185 this._moveEnd(true); 3186 } 3187 }); 3188 function createMap2(id, options) { 3189 return new Map2(id, options); 3190 } 3191 var Control = Class.extend({ 3192 // @section 3193 // @aka Control Options 3194 options: { 3195 // @option position: String = 'topright' 3196 // The position of the control (one of the map corners). Possible values are `'topleft'`, 3197 // `'topright'`, `'bottomleft'` or `'bottomright'` 3198 position: "topright" 3199 }, 3200 initialize: function(options) { 3201 setOptions(this, options); 3202 }, 3203 /* @section 3204 * Classes extending L.Control will inherit the following methods: 3205 * 3206 * @method getPosition: string 3207 * Returns the position of the control. 3208 */ 3209 getPosition: function() { 3210 return this.options.position; 3211 }, 3212 // @method setPosition(position: string): this 3213 // Sets the position of the control. 3214 setPosition: function(position) { 3215 var map2 = this._map; 3216 if (map2) { 3217 map2.removeControl(this); 3218 } 3219 this.options.position = position; 3220 if (map2) { 3221 map2.addControl(this); 3222 } 3223 return this; 3224 }, 3225 // @method getContainer: HTMLElement 3226 // Returns the HTMLElement that contains the control. 3227 getContainer: function() { 3228 return this._container; 3229 }, 3230 // @method addTo(map: Map): this 3231 // Adds the control to the given map. 3232 addTo: function(map2) { 3233 this.remove(); 3234 this._map = map2; 3235 var container = this._container = this.onAdd(map2), pos = this.getPosition(), corner = map2._controlCorners[pos]; 3236 addClass(container, "leaflet-control"); 3237 if (pos.indexOf("bottom") !== -1) { 3238 corner.insertBefore(container, corner.firstChild); 3239 } else { 3240 corner.appendChild(container); 3241 } 3242 this._map.on("unload", this.remove, this); 3243 return this; 3244 }, 3245 // @method remove: this 3246 // Removes the control from the map it is currently active on. 3247 remove: function() { 3248 if (!this._map) { 3249 return this; 3250 } 3251 remove(this._container); 3252 if (this.onRemove) { 3253 this.onRemove(this._map); 3254 } 3255 this._map.off("unload", this.remove, this); 3256 this._map = null; 3257 return this; 3258 }, 3259 _refocusOnMap: function(e) { 3260 if (this._map && e && e.screenX > 0 && e.screenY > 0) { 3261 this._map.getContainer().focus(); 3262 } 3263 } 3264 }); 3265 var control = function(options) { 3266 return new Control(options); 3267 }; 3268 Map2.include({ 3269 // @method addControl(control: Control): this 3270 // Adds the given control to the map 3271 addControl: function(control2) { 3272 control2.addTo(this); 3273 return this; 3274 }, 3275 // @method removeControl(control: Control): this 3276 // Removes the given control from the map 3277 removeControl: function(control2) { 3278 control2.remove(); 3279 return this; 3280 }, 3281 _initControlPos: function() { 3282 var corners = this._controlCorners = {}, l = "leaflet-", container = this._controlContainer = create$1("div", l + "control-container", this._container); 3283 function createCorner(vSide, hSide) { 3284 var className = l + vSide + " " + l + hSide; 3285 corners[vSide + hSide] = create$1("div", className, container); 3286 } 3287 createCorner("top", "left"); 3288 createCorner("top", "right"); 3289 createCorner("bottom", "left"); 3290 createCorner("bottom", "right"); 3291 }, 3292 _clearControlPos: function() { 3293 for (var i in this._controlCorners) { 3294 remove(this._controlCorners[i]); 3295 } 3296 remove(this._controlContainer); 3297 delete this._controlCorners; 3298 delete this._controlContainer; 3299 } 3300 }); 3301 var Layers = Control.extend({ 3302 // @section 3303 // @aka Control.Layers options 3304 options: { 3305 // @option collapsed: Boolean = true 3306 // If `true`, the control will be collapsed into an icon and expanded on mouse hover, touch, or keyboard activation. 3307 collapsed: true, 3308 position: "topright", 3309 // @option autoZIndex: Boolean = true 3310 // If `true`, the control will assign zIndexes in increasing order to all of its layers so that the order is preserved when switching them on/off. 3311 autoZIndex: true, 3312 // @option hideSingleBase: Boolean = false 3313 // If `true`, the base layers in the control will be hidden when there is only one. 3314 hideSingleBase: false, 3315 // @option sortLayers: Boolean = false 3316 // Whether to sort the layers. When `false`, layers will keep the order 3317 // in which they were added to the control. 3318 sortLayers: false, 3319 // @option sortFunction: Function = * 3320 // A [compare function](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/sort) 3321 // that will be used for sorting the layers, when `sortLayers` is `true`. 3322 // The function receives both the `L.Layer` instances and their names, as in 3323 // `sortFunction(layerA, layerB, nameA, nameB)`. 3324 // By default, it sorts layers alphabetically by their name. 3325 sortFunction: function(layerA, layerB, nameA, nameB) { 3326 return nameA < nameB ? -1 : nameB < nameA ? 1 : 0; 3327 } 3328 }, 3329 initialize: function(baseLayers, overlays, options) { 3330 setOptions(this, options); 3331 this._layerControlInputs = []; 3332 this._layers = []; 3333 this._lastZIndex = 0; 3334 this._handlingClick = false; 3335 this._preventClick = false; 3336 for (var i in baseLayers) { 3337 this._addLayer(baseLayers[i], i); 3338 } 3339 for (i in overlays) { 3340 this._addLayer(overlays[i], i, true); 3341 } 3342 }, 3343 onAdd: function(map2) { 3344 this._initLayout(); 3345 this._update(); 3346 this._map = map2; 3347 map2.on("zoomend", this._checkDisabledLayers, this); 3348 for (var i = 0; i < this._layers.length; i++) { 3349 this._layers[i].layer.on("add remove", this._onLayerChange, this); 3350 } 3351 return this._container; 3352 }, 3353 addTo: function(map2) { 3354 Control.prototype.addTo.call(this, map2); 3355 return this._expandIfNotCollapsed(); 3356 }, 3357 onRemove: function() { 3358 this._map.off("zoomend", this._checkDisabledLayers, this); 3359 for (var i = 0; i < this._layers.length; i++) { 3360 this._layers[i].layer.off("add remove", this._onLayerChange, this); 3361 } 3362 }, 3363 // @method addBaseLayer(layer: Layer, name: String): this 3364 // Adds a base layer (radio button entry) with the given name to the control. 3365 addBaseLayer: function(layer, name) { 3366 this._addLayer(layer, name); 3367 return this._map ? this._update() : this; 3368 }, 3369 // @method addOverlay(layer: Layer, name: String): this 3370 // Adds an overlay (checkbox entry) with the given name to the control. 3371 addOverlay: function(layer, name) { 3372 this._addLayer(layer, name, true); 3373 return this._map ? this._update() : this; 3374 }, 3375 // @method removeLayer(layer: Layer): this 3376 // Remove the given layer from the control. 3377 removeLayer: function(layer) { 3378 layer.off("add remove", this._onLayerChange, this); 3379 var obj = this._getLayer(stamp(layer)); 3380 if (obj) { 3381 this._layers.splice(this._layers.indexOf(obj), 1); 3382 } 3383 return this._map ? this._update() : this; 3384 }, 3385 // @method expand(): this 3386 // Expand the control container if collapsed. 3387 expand: function() { 3388 addClass(this._container, "leaflet-control-layers-expanded"); 3389 this._section.style.height = null; 3390 var acceptableHeight = this._map.getSize().y - (this._container.offsetTop + 50); 3391 if (acceptableHeight < this._section.clientHeight) { 3392 addClass(this._section, "leaflet-control-layers-scrollbar"); 3393 this._section.style.height = acceptableHeight + "px"; 3394 } else { 3395 removeClass(this._section, "leaflet-control-layers-scrollbar"); 3396 } 3397 this._checkDisabledLayers(); 3398 return this; 3399 }, 3400 // @method collapse(): this 3401 // Collapse the control container if expanded. 3402 collapse: function() { 3403 removeClass(this._container, "leaflet-control-layers-expanded"); 3404 return this; 3405 }, 3406 _initLayout: function() { 3407 var className = "leaflet-control-layers", container = this._container = create$1("div", className), collapsed = this.options.collapsed; 3408 container.setAttribute("aria-haspopup", true); 3409 disableClickPropagation(container); 3410 disableScrollPropagation(container); 3411 var section = this._section = create$1("section", className + "-list"); 3412 if (collapsed) { 3413 this._map.on("click", this.collapse, this); 3414 on(container, { 3415 mouseenter: this._expandSafely, 3416 mouseleave: this.collapse 3417 }, this); 3418 } 3419 var link = this._layersLink = create$1("a", className + "-toggle", container); 3420 link.href = "#"; 3421 link.title = "Layers"; 3422 link.setAttribute("role", "button"); 3423 on(link, { 3424 keydown: function(e) { 3425 if (e.keyCode === 13) { 3426 this._expandSafely(); 3427 } 3428 }, 3429 // Certain screen readers intercept the key event and instead send a click event 3430 click: function(e) { 3431 preventDefault(e); 3432 this._expandSafely(); 3433 } 3434 }, this); 3435 if (!collapsed) { 3436 this.expand(); 3437 } 3438 this._baseLayersList = create$1("div", className + "-base", section); 3439 this._separator = create$1("div", className + "-separator", section); 3440 this._overlaysList = create$1("div", className + "-overlays", section); 3441 container.appendChild(section); 3442 }, 3443 _getLayer: function(id) { 3444 for (var i = 0; i < this._layers.length; i++) { 3445 if (this._layers[i] && stamp(this._layers[i].layer) === id) { 3446 return this._layers[i]; 3447 } 3448 } 3449 }, 3450 _addLayer: function(layer, name, overlay) { 3451 if (this._map) { 3452 layer.on("add remove", this._onLayerChange, this); 3453 } 3454 this._layers.push({ 3455 layer, 3456 name, 3457 overlay 3458 }); 3459 if (this.options.sortLayers) { 3460 this._layers.sort(bind(function(a, b) { 3461 return this.options.sortFunction(a.layer, b.layer, a.name, b.name); 3462 }, this)); 3463 } 3464 if (this.options.autoZIndex && layer.setZIndex) { 3465 this._lastZIndex++; 3466 layer.setZIndex(this._lastZIndex); 3467 } 3468 this._expandIfNotCollapsed(); 3469 }, 3470 _update: function() { 3471 if (!this._container) { 3472 return this; 3473 } 3474 empty(this._baseLayersList); 3475 empty(this._overlaysList); 3476 this._layerControlInputs = []; 3477 var baseLayersPresent, overlaysPresent, i, obj, baseLayersCount = 0; 3478 for (i = 0; i < this._layers.length; i++) { 3479 obj = this._layers[i]; 3480 this._addItem(obj); 3481 overlaysPresent = overlaysPresent || obj.overlay; 3482 baseLayersPresent = baseLayersPresent || !obj.overlay; 3483 baseLayersCount += !obj.overlay ? 1 : 0; 3484 } 3485 if (this.options.hideSingleBase) { 3486 baseLayersPresent = baseLayersPresent && baseLayersCount > 1; 3487 this._baseLayersList.style.display = baseLayersPresent ? "" : "none"; 3488 } 3489 this._separator.style.display = overlaysPresent && baseLayersPresent ? "" : "none"; 3490 return this; 3491 }, 3492 _onLayerChange: function(e) { 3493 if (!this._handlingClick) { 3494 this._update(); 3495 } 3496 var obj = this._getLayer(stamp(e.target)); 3497 var type = obj.overlay ? e.type === "add" ? "overlayadd" : "overlayremove" : e.type === "add" ? "baselayerchange" : null; 3498 if (type) { 3499 this._map.fire(type, obj); 3500 } 3501 }, 3502 // IE7 bugs out if you create a radio dynamically, so you have to do it this hacky way (see https://stackoverflow.com/a/119079) 3503 _createRadioElement: function(name, checked) { 3504 var radioHtml = '<input type="radio" class="leaflet-control-layers-selector" name="' + name + '"' + (checked ? ' checked="checked"' : "") + "/>"; 3505 var radioFragment = document.createElement("div"); 3506 radioFragment.innerHTML = radioHtml; 3507 return radioFragment.firstChild; 3508 }, 3509 _addItem: function(obj) { 3510 var label = document.createElement("label"), checked = this._map.hasLayer(obj.layer), input; 3511 if (obj.overlay) { 3512 input = document.createElement("input"); 3513 input.type = "checkbox"; 3514 input.className = "leaflet-control-layers-selector"; 3515 input.defaultChecked = checked; 3516 } else { 3517 input = this._createRadioElement("leaflet-base-layers_" + stamp(this), checked); 3518 } 3519 this._layerControlInputs.push(input); 3520 input.layerId = stamp(obj.layer); 3521 on(input, "click", this._onInputClick, this); 3522 var name = document.createElement("span"); 3523 name.innerHTML = " " + obj.name; 3524 var holder = document.createElement("span"); 3525 label.appendChild(holder); 3526 holder.appendChild(input); 3527 holder.appendChild(name); 3528 var container = obj.overlay ? this._overlaysList : this._baseLayersList; 3529 container.appendChild(label); 3530 this._checkDisabledLayers(); 3531 return label; 3532 }, 3533 _onInputClick: function() { 3534 if (this._preventClick) { 3535 return; 3536 } 3537 var inputs = this._layerControlInputs, input, layer; 3538 var addedLayers = [], removedLayers = []; 3539 this._handlingClick = true; 3540 for (var i = inputs.length - 1; i >= 0; i--) { 3541 input = inputs[i]; 3542 layer = this._getLayer(input.layerId).layer; 3543 if (input.checked) { 3544 addedLayers.push(layer); 3545 } else if (!input.checked) { 3546 removedLayers.push(layer); 3547 } 3548 } 3549 for (i = 0; i < removedLayers.length; i++) { 3550 if (this._map.hasLayer(removedLayers[i])) { 3551 this._map.removeLayer(removedLayers[i]); 3552 } 3553 } 3554 for (i = 0; i < addedLayers.length; i++) { 3555 if (!this._map.hasLayer(addedLayers[i])) { 3556 this._map.addLayer(addedLayers[i]); 3557 } 3558 } 3559 this._handlingClick = false; 3560 this._refocusOnMap(); 3561 }, 3562 _checkDisabledLayers: function() { 3563 var inputs = this._layerControlInputs, input, layer, zoom2 = this._map.getZoom(); 3564 for (var i = inputs.length - 1; i >= 0; i--) { 3565 input = inputs[i]; 3566 layer = this._getLayer(input.layerId).layer; 3567 input.disabled = layer.options.minZoom !== void 0 && zoom2 < layer.options.minZoom || layer.options.maxZoom !== void 0 && zoom2 > layer.options.maxZoom; 3568 } 3569 }, 3570 _expandIfNotCollapsed: function() { 3571 if (this._map && !this.options.collapsed) { 3572 this.expand(); 3573 } 3574 return this; 3575 }, 3576 _expandSafely: function() { 3577 var section = this._section; 3578 this._preventClick = true; 3579 on(section, "click", preventDefault); 3580 this.expand(); 3581 var that = this; 3582 setTimeout(function() { 3583 off(section, "click", preventDefault); 3584 that._preventClick = false; 3585 }); 3586 } 3587 }); 3588 var layers = function(baseLayers, overlays, options) { 3589 return new Layers(baseLayers, overlays, options); 3590 }; 3591 var Zoom = Control.extend({ 3592 // @section 3593 // @aka Control.Zoom options 3594 options: { 3595 position: "topleft", 3596 // @option zoomInText: String = '<span aria-hidden="true">+</span>' 3597 // The text set on the 'zoom in' button. 3598 zoomInText: '<span aria-hidden="true">+</span>', 3599 // @option zoomInTitle: String = 'Zoom in' 3600 // The title set on the 'zoom in' button. 3601 zoomInTitle: "Zoom in", 3602 // @option zoomOutText: String = '<span aria-hidden="true">&#x2212;</span>' 3603 // The text set on the 'zoom out' button. 3604 zoomOutText: '<span aria-hidden="true">&#x2212;</span>', 3605 // @option zoomOutTitle: String = 'Zoom out' 3606 // The title set on the 'zoom out' button. 3607 zoomOutTitle: "Zoom out" 3608 }, 3609 onAdd: function(map2) { 3610 var zoomName = "leaflet-control-zoom", container = create$1("div", zoomName + " leaflet-bar"), options = this.options; 3611 this._zoomInButton = this._createButton( 3612 options.zoomInText, 3613 options.zoomInTitle, 3614 zoomName + "-in", 3615 container, 3616 this._zoomIn 3617 ); 3618 this._zoomOutButton = this._createButton( 3619 options.zoomOutText, 3620 options.zoomOutTitle, 3621 zoomName + "-out", 3622 container, 3623 this._zoomOut 3624 ); 3625 this._updateDisabled(); 3626 map2.on("zoomend zoomlevelschange", this._updateDisabled, this); 3627 return container; 3628 }, 3629 onRemove: function(map2) { 3630 map2.off("zoomend zoomlevelschange", this._updateDisabled, this); 3631 }, 3632 disable: function() { 3633 this._disabled = true; 3634 this._updateDisabled(); 3635 return this; 3636 }, 3637 enable: function() { 3638 this._disabled = false; 3639 this._updateDisabled(); 3640 return this; 3641 }, 3642 _zoomIn: function(e) { 3643 if (!this._disabled && this._map._zoom < this._map.getMaxZoom()) { 3644 this._map.zoomIn(this._map.options.zoomDelta * (e.shiftKey ? 3 : 1)); 3645 } 3646 }, 3647 _zoomOut: function(e) { 3648 if (!this._disabled && this._map._zoom > this._map.getMinZoom()) { 3649 this._map.zoomOut(this._map.options.zoomDelta * (e.shiftKey ? 3 : 1)); 3650 } 3651 }, 3652 _createButton: function(html, title, className, container, fn) { 3653 var link = create$1("a", className, container); 3654 link.innerHTML = html; 3655 link.href = "#"; 3656 link.title = title; 3657 link.setAttribute("role", "button"); 3658 link.setAttribute("aria-label", title); 3659 disableClickPropagation(link); 3660 on(link, "click", stop); 3661 on(link, "click", fn, this); 3662 on(link, "click", this._refocusOnMap, this); 3663 return link; 3664 }, 3665 _updateDisabled: function() { 3666 var map2 = this._map, className = "leaflet-disabled"; 3667 removeClass(this._zoomInButton, className); 3668 removeClass(this._zoomOutButton, className); 3669 this._zoomInButton.setAttribute("aria-disabled", "false"); 3670 this._zoomOutButton.setAttribute("aria-disabled", "false"); 3671 if (this._disabled || map2._zoom === map2.getMinZoom()) { 3672 addClass(this._zoomOutButton, className); 3673 this._zoomOutButton.setAttribute("aria-disabled", "true"); 3674 } 3675 if (this._disabled || map2._zoom === map2.getMaxZoom()) { 3676 addClass(this._zoomInButton, className); 3677 this._zoomInButton.setAttribute("aria-disabled", "true"); 3678 } 3679 } 3680 }); 3681 Map2.mergeOptions({ 3682 zoomControl: true 3683 }); 3684 Map2.addInitHook(function() { 3685 if (this.options.zoomControl) { 3686 this.zoomControl = new Zoom(); 3687 this.addControl(this.zoomControl); 3688 } 3689 }); 3690 var zoom = function(options) { 3691 return new Zoom(options); 3692 }; 3693 var Scale = Control.extend({ 3694 // @section 3695 // @aka Control.Scale options 3696 options: { 3697 position: "bottomleft", 3698 // @option maxWidth: Number = 100 3699 // Maximum width of the control in pixels. The width is set dynamically to show round values (e.g. 100, 200, 500). 3700 maxWidth: 100, 3701 // @option metric: Boolean = True 3702 // Whether to show the metric scale line (m/km). 3703 metric: true, 3704 // @option imperial: Boolean = True 3705 // Whether to show the imperial scale line (mi/ft). 3706 imperial: true 3707 // @option updateWhenIdle: Boolean = false 3708 // If `true`, the control is updated on [`moveend`](#map-moveend), otherwise it's always up-to-date (updated on [`move`](#map-move)). 3709 }, 3710 onAdd: function(map2) { 3711 var className = "leaflet-control-scale", container = create$1("div", className), options = this.options; 3712 this._addScales(options, className + "-line", container); 3713 map2.on(options.updateWhenIdle ? "moveend" : "move", this._update, this); 3714 map2.whenReady(this._update, this); 3715 return container; 3716 }, 3717 onRemove: function(map2) { 3718 map2.off(this.options.updateWhenIdle ? "moveend" : "move", this._update, this); 3719 }, 3720 _addScales: function(options, className, container) { 3721 if (options.metric) { 3722 this._mScale = create$1("div", className, container); 3723 } 3724 if (options.imperial) { 3725 this._iScale = create$1("div", className, container); 3726 } 3727 }, 3728 _update: function() { 3729 var map2 = this._map, y = map2.getSize().y / 2; 3730 var maxMeters = map2.distance( 3731 map2.containerPointToLatLng([0, y]), 3732 map2.containerPointToLatLng([this.options.maxWidth, y]) 3733 ); 3734 this._updateScales(maxMeters); 3735 }, 3736 _updateScales: function(maxMeters) { 3737 if (this.options.metric && maxMeters) { 3738 this._updateMetric(maxMeters); 3739 } 3740 if (this.options.imperial && maxMeters) { 3741 this._updateImperial(maxMeters); 3742 } 3743 }, 3744 _updateMetric: function(maxMeters) { 3745 var meters = this._getRoundNum(maxMeters), label = meters < 1e3 ? meters + " m" : meters / 1e3 + " km"; 3746 this._updateScale(this._mScale, label, meters / maxMeters); 3747 }, 3748 _updateImperial: function(maxMeters) { 3749 var maxFeet = maxMeters * 3.2808399, maxMiles, miles, feet; 3750 if (maxFeet > 5280) { 3751 maxMiles = maxFeet / 5280; 3752 miles = this._getRoundNum(maxMiles); 3753 this._updateScale(this._iScale, miles + " mi", miles / maxMiles); 3754 } else { 3755 feet = this._getRoundNum(maxFeet); 3756 this._updateScale(this._iScale, feet + " ft", feet / maxFeet); 3757 } 3758 }, 3759 _updateScale: function(scale2, text, ratio) { 3760 scale2.style.width = Math.round(this.options.maxWidth * ratio) + "px"; 3761 scale2.innerHTML = text; 3762 }, 3763 _getRoundNum: function(num) { 3764 var pow10 = Math.pow(10, (Math.floor(num) + "").length - 1), d = num / pow10; 3765 d = d >= 10 ? 10 : d >= 5 ? 5 : d >= 3 ? 3 : d >= 2 ? 2 : 1; 3766 return pow10 * d; 3767 } 3768 }); 3769 var scale = function(options) { 3770 return new Scale(options); 3771 }; 3772 var ukrainianFlag = '<svg aria-hidden="true" xmlns="http://www.w3.org/2000/svg" width="12" height="8" viewBox="0 0 12 8" class="leaflet-attribution-flag"><path fill="#4C7BE1" d="M0 0h12v4H0z"/><path fill="#FFD500" d="M0 4h12v3H0z"/><path fill="#E0BC00" d="M0 7h12v1H0z"/></svg>'; 3773 var Attribution = Control.extend({ 3774 // @section 3775 // @aka Control.Attribution options 3776 options: { 3777 position: "bottomright", 3778 // @option prefix: String|false = 'Leaflet' 3779 // The HTML text shown before the attributions. Pass `false` to disable. 3780 prefix: '<a href="https://leafletjs.com" title="A JavaScript library for interactive maps">' + (Browser.inlineSvg ? ukrainianFlag + " " : "") + "Leaflet</a>" 3781 }, 3782 initialize: function(options) { 3783 setOptions(this, options); 3784 this._attributions = {}; 3785 }, 3786 onAdd: function(map2) { 3787 map2.attributionControl = this; 3788 this._container = create$1("div", "leaflet-control-attribution"); 3789 disableClickPropagation(this._container); 3790 for (var i in map2._layers) { 3791 if (map2._layers[i].getAttribution) { 3792 this.addAttribution(map2._layers[i].getAttribution()); 3793 } 3794 } 3795 this._update(); 3796 map2.on("layeradd", this._addAttribution, this); 3797 return this._container; 3798 }, 3799 onRemove: function(map2) { 3800 map2.off("layeradd", this._addAttribution, this); 3801 }, 3802 _addAttribution: function(ev) { 3803 if (ev.layer.getAttribution) { 3804 this.addAttribution(ev.layer.getAttribution()); 3805 ev.layer.once("remove", function() { 3806 this.removeAttribution(ev.layer.getAttribution()); 3807 }, this); 3808 } 3809 }, 3810 // @method setPrefix(prefix: String|false): this 3811 // The HTML text shown before the attributions. Pass `false` to disable. 3812 setPrefix: function(prefix) { 3813 this.options.prefix = prefix; 3814 this._update(); 3815 return this; 3816 }, 3817 // @method addAttribution(text: String): this 3818 // Adds an attribution text (e.g. `'&copy; OpenStreetMap contributors'`). 3819 addAttribution: function(text) { 3820 if (!text) { 3821 return this; 3822 } 3823 if (!this._attributions[text]) { 3824 this._attributions[text] = 0; 3825 } 3826 this._attributions[text]++; 3827 this._update(); 3828 return this; 3829 }, 3830 // @method removeAttribution(text: String): this 3831 // Removes an attribution text. 3832 removeAttribution: function(text) { 3833 if (!text) { 3834 return this; 3835 } 3836 if (this._attributions[text]) { 3837 this._attributions[text]--; 3838 this._update(); 3839 } 3840 return this; 3841 }, 3842 _update: function() { 3843 if (!this._map) { 3844 return; 3845 } 3846 var attribs = []; 3847 for (var i in this._attributions) { 3848 if (this._attributions[i]) { 3849 attribs.push(i); 3850 } 3851 } 3852 var prefixAndAttribs = []; 3853 if (this.options.prefix) { 3854 prefixAndAttribs.push(this.options.prefix); 3855 } 3856 if (attribs.length) { 3857 prefixAndAttribs.push(attribs.join(", ")); 3858 } 3859 this._container.innerHTML = prefixAndAttribs.join(' <span aria-hidden="true">|</span> '); 3860 } 3861 }); 3862 Map2.mergeOptions({ 3863 attributionControl: true 3864 }); 3865 Map2.addInitHook(function() { 3866 if (this.options.attributionControl) { 3867 new Attribution().addTo(this); 3868 } 3869 }); 3870 var attribution = function(options) { 3871 return new Attribution(options); 3872 }; 3873 Control.Layers = Layers; 3874 Control.Zoom = Zoom; 3875 Control.Scale = Scale; 3876 Control.Attribution = Attribution; 3877 control.layers = layers; 3878 control.zoom = zoom; 3879 control.scale = scale; 3880 control.attribution = attribution; 3881 var Handler = Class.extend({ 3882 initialize: function(map2) { 3883 this._map = map2; 3884 }, 3885 // @method enable(): this 3886 // Enables the handler 3887 enable: function() { 3888 if (this._enabled) { 3889 return this; 3890 } 3891 this._enabled = true; 3892 this.addHooks(); 3893 return this; 3894 }, 3895 // @method disable(): this 3896 // Disables the handler 3897 disable: function() { 3898 if (!this._enabled) { 3899 return this; 3900 } 3901 this._enabled = false; 3902 this.removeHooks(); 3903 return this; 3904 }, 3905 // @method enabled(): Boolean 3906 // Returns `true` if the handler is enabled 3907 enabled: function() { 3908 return !!this._enabled; 3909 } 3910 // @section Extension methods 3911 // Classes inheriting from `Handler` must implement the two following methods: 3912 // @method addHooks() 3913 // Called when the handler is enabled, should add event hooks. 3914 // @method removeHooks() 3915 // Called when the handler is disabled, should remove the event hooks added previously. 3916 }); 3917 Handler.addTo = function(map2, name) { 3918 map2.addHandler(name, this); 3919 return this; 3920 }; 3921 var Mixin = { Events }; 3922 var START = Browser.touch ? "touchstart mousedown" : "mousedown"; 3923 var Draggable = Evented.extend({ 3924 options: { 3925 // @section 3926 // @aka Draggable options 3927 // @option clickTolerance: Number = 3 3928 // The max number of pixels a user can shift the mouse pointer during a click 3929 // for it to be considered a valid click (as opposed to a mouse drag). 3930 clickTolerance: 3 3931 }, 3932 // @constructor L.Draggable(el: HTMLElement, dragHandle?: HTMLElement, preventOutline?: Boolean, options?: Draggable options) 3933 // Creates a `Draggable` object for moving `el` when you start dragging the `dragHandle` element (equals `el` itself by default). 3934 initialize: function(element, dragStartTarget, preventOutline2, options) { 3935 setOptions(this, options); 3936 this._element = element; 3937 this._dragStartTarget = dragStartTarget || element; 3938 this._preventOutline = preventOutline2; 3939 }, 3940 // @method enable() 3941 // Enables the dragging ability 3942 enable: function() { 3943 if (this._enabled) { 3944 return; 3945 } 3946 on(this._dragStartTarget, START, this._onDown, this); 3947 this._enabled = true; 3948 }, 3949 // @method disable() 3950 // Disables the dragging ability 3951 disable: function() { 3952 if (!this._enabled) { 3953 return; 3954 } 3955 if (Draggable._dragging === this) { 3956 this.finishDrag(true); 3957 } 3958 off(this._dragStartTarget, START, this._onDown, this); 3959 this._enabled = false; 3960 this._moved = false; 3961 }, 3962 _onDown: function(e) { 3963 if (!this._enabled) { 3964 return; 3965 } 3966 this._moved = false; 3967 if (hasClass(this._element, "leaflet-zoom-anim")) { 3968 return; 3969 } 3970 if (e.touches && e.touches.length !== 1) { 3971 if (Draggable._dragging === this) { 3972 this.finishDrag(); 3973 } 3974 return; 3975 } 3976 if (Draggable._dragging || e.shiftKey || e.which !== 1 && e.button !== 1 && !e.touches) { 3977 return; 3978 } 3979 Draggable._dragging = this; 3980 if (this._preventOutline) { 3981 preventOutline(this._element); 3982 } 3983 disableImageDrag(); 3984 disableTextSelection(); 3985 if (this._moving) { 3986 return; 3987 } 3988 this.fire("down"); 3989 var first = e.touches ? e.touches[0] : e, sizedParent = getSizedParentNode(this._element); 3990 this._startPoint = new Point(first.clientX, first.clientY); 3991 this._startPos = getPosition(this._element); 3992 this._parentScale = getScale(sizedParent); 3993 var mouseevent = e.type === "mousedown"; 3994 on(document, mouseevent ? "mousemove" : "touchmove", this._onMove, this); 3995 on(document, mouseevent ? "mouseup" : "touchend touchcancel", this._onUp, this); 3996 }, 3997 _onMove: function(e) { 3998 if (!this._enabled) { 3999 return; 4000 } 4001 if (e.touches && e.touches.length > 1) { 4002 this._moved = true; 4003 return; 4004 } 4005 var first = e.touches && e.touches.length === 1 ? e.touches[0] : e, offset = new Point(first.clientX, first.clientY)._subtract(this._startPoint); 4006 if (!offset.x && !offset.y) { 4007 return; 4008 } 4009 if (Math.abs(offset.x) + Math.abs(offset.y) < this.options.clickTolerance) { 4010 return; 4011 } 4012 offset.x /= this._parentScale.x; 4013 offset.y /= this._parentScale.y; 4014 preventDefault(e); 4015 if (!this._moved) { 4016 this.fire("dragstart"); 4017 this._moved = true; 4018 addClass(document.body, "leaflet-dragging"); 4019 this._lastTarget = e.target || e.srcElement; 4020 if (window.SVGElementInstance && this._lastTarget instanceof window.SVGElementInstance) { 4021 this._lastTarget = this._lastTarget.correspondingUseElement; 4022 } 4023 addClass(this._lastTarget, "leaflet-drag-target"); 4024 } 4025 this._newPos = this._startPos.add(offset); 4026 this._moving = true; 4027 this._lastEvent = e; 4028 this._updatePosition(); 4029 }, 4030 _updatePosition: function() { 4031 var e = { originalEvent: this._lastEvent }; 4032 this.fire("predrag", e); 4033 setPosition(this._element, this._newPos); 4034 this.fire("drag", e); 4035 }, 4036 _onUp: function() { 4037 if (!this._enabled) { 4038 return; 4039 } 4040 this.finishDrag(); 4041 }, 4042 finishDrag: function(noInertia) { 4043 removeClass(document.body, "leaflet-dragging"); 4044 if (this._lastTarget) { 4045 removeClass(this._lastTarget, "leaflet-drag-target"); 4046 this._lastTarget = null; 4047 } 4048 off(document, "mousemove touchmove", this._onMove, this); 4049 off(document, "mouseup touchend touchcancel", this._onUp, this); 4050 enableImageDrag(); 4051 enableTextSelection(); 4052 var fireDragend = this._moved && this._moving; 4053 this._moving = false; 4054 Draggable._dragging = false; 4055 if (fireDragend) { 4056 this.fire("dragend", { 4057 noInertia, 4058 distance: this._newPos.distanceTo(this._startPos) 4059 }); 4060 } 4061 } 4062 }); 4063 function clipPolygon(points, bounds, round) { 4064 var clippedPoints, edges = [1, 4, 2, 8], i, j, k, a, b, len, edge2, p; 4065 for (i = 0, len = points.length; i < len; i++) { 4066 points[i]._code = _getBitCode(points[i], bounds); 4067 } 4068 for (k = 0; k < 4; k++) { 4069 edge2 = edges[k]; 4070 clippedPoints = []; 4071 for (i = 0, len = points.length, j = len - 1; i < len; j = i++) { 4072 a = points[i]; 4073 b = points[j]; 4074 if (!(a._code & edge2)) { 4075 if (b._code & edge2) { 4076 p = _getEdgeIntersection(b, a, edge2, bounds, round); 4077 p._code = _getBitCode(p, bounds); 4078 clippedPoints.push(p); 4079 } 4080 clippedPoints.push(a); 4081 } else if (!(b._code & edge2)) { 4082 p = _getEdgeIntersection(b, a, edge2, bounds, round); 4083 p._code = _getBitCode(p, bounds); 4084 clippedPoints.push(p); 4085 } 4086 } 4087 points = clippedPoints; 4088 } 4089 return points; 4090 } 4091 function polygonCenter(latlngs, crs) { 4092 var i, j, p1, p2, f, area, x, y, center; 4093 if (!latlngs || latlngs.length === 0) { 4094 throw new Error("latlngs not passed"); 4095 } 4096 if (!isFlat(latlngs)) { 4097 console.warn("latlngs are not flat! Only the first ring will be used"); 4098 latlngs = latlngs[0]; 4099 } 4100 var centroidLatLng = toLatLng([0, 0]); 4101 var bounds = toLatLngBounds(latlngs); 4102 var areaBounds = bounds.getNorthWest().distanceTo(bounds.getSouthWest()) * bounds.getNorthEast().distanceTo(bounds.getNorthWest()); 4103 if (areaBounds < 1700) { 4104 centroidLatLng = centroid(latlngs); 4105 } 4106 var len = latlngs.length; 4107 var points = []; 4108 for (i = 0; i < len; i++) { 4109 var latlng = toLatLng(latlngs[i]); 4110 points.push(crs.project(toLatLng([latlng.lat - centroidLatLng.lat, latlng.lng - centroidLatLng.lng]))); 4111 } 4112 area = x = y = 0; 4113 for (i = 0, j = len - 1; i < len; j = i++) { 4114 p1 = points[i]; 4115 p2 = points[j]; 4116 f = p1.y * p2.x - p2.y * p1.x; 4117 x += (p1.x + p2.x) * f; 4118 y += (p1.y + p2.y) * f; 4119 area += f * 3; 4120 } 4121 if (area === 0) { 4122 center = points[0]; 4123 } else { 4124 center = [x / area, y / area]; 4125 } 4126 var latlngCenter = crs.unproject(toPoint(center)); 4127 return toLatLng([latlngCenter.lat + centroidLatLng.lat, latlngCenter.lng + centroidLatLng.lng]); 4128 } 4129 function centroid(coords) { 4130 var latSum = 0; 4131 var lngSum = 0; 4132 var len = 0; 4133 for (var i = 0; i < coords.length; i++) { 4134 var latlng = toLatLng(coords[i]); 4135 latSum += latlng.lat; 4136 lngSum += latlng.lng; 4137 len++; 4138 } 4139 return toLatLng([latSum / len, lngSum / len]); 4140 } 4141 var PolyUtil = { 4142 __proto__: null, 4143 clipPolygon, 4144 polygonCenter, 4145 centroid 4146 }; 4147 function simplify(points, tolerance) { 4148 if (!tolerance || !points.length) { 4149 return points.slice(); 4150 } 4151 var sqTolerance = tolerance * tolerance; 4152 points = _reducePoints(points, sqTolerance); 4153 points = _simplifyDP(points, sqTolerance); 4154 return points; 4155 } 4156 function pointToSegmentDistance(p, p1, p2) { 4157 return Math.sqrt(_sqClosestPointOnSegment(p, p1, p2, true)); 4158 } 4159 function closestPointOnSegment(p, p1, p2) { 4160 return _sqClosestPointOnSegment(p, p1, p2); 4161 } 4162 function _simplifyDP(points, sqTolerance) { 4163 var len = points.length, ArrayConstructor = typeof Uint8Array !== "undefined" ? Uint8Array : Array, markers = new ArrayConstructor(len); 4164 markers[0] = markers[len - 1] = 1; 4165 _simplifyDPStep(points, markers, sqTolerance, 0, len - 1); 4166 var i, newPoints = []; 4167 for (i = 0; i < len; i++) { 4168 if (markers[i]) { 4169 newPoints.push(points[i]); 4170 } 4171 } 4172 return newPoints; 4173 } 4174 function _simplifyDPStep(points, markers, sqTolerance, first, last) { 4175 var maxSqDist = 0, index2, i, sqDist; 4176 for (i = first + 1; i <= last - 1; i++) { 4177 sqDist = _sqClosestPointOnSegment(points[i], points[first], points[last], true); 4178 if (sqDist > maxSqDist) { 4179 index2 = i; 4180 maxSqDist = sqDist; 4181 } 4182 } 4183 if (maxSqDist > sqTolerance) { 4184 markers[index2] = 1; 4185 _simplifyDPStep(points, markers, sqTolerance, first, index2); 4186 _simplifyDPStep(points, markers, sqTolerance, index2, last); 4187 } 4188 } 4189 function _reducePoints(points, sqTolerance) { 4190 var reducedPoints = [points[0]]; 4191 for (var i = 1, prev = 0, len = points.length; i < len; i++) { 4192 if (_sqDist(points[i], points[prev]) > sqTolerance) { 4193 reducedPoints.push(points[i]); 4194 prev = i; 4195 } 4196 } 4197 if (prev < len - 1) { 4198 reducedPoints.push(points[len - 1]); 4199 } 4200 return reducedPoints; 4201 } 4202 var _lastCode; 4203 function clipSegment(a, b, bounds, useLastCode, round) { 4204 var codeA = useLastCode ? _lastCode : _getBitCode(a, bounds), codeB = _getBitCode(b, bounds), codeOut, p, newCode; 4205 _lastCode = codeB; 4206 while (true) { 4207 if (!(codeA | codeB)) { 4208 return [a, b]; 4209 } 4210 if (codeA & codeB) { 4211 return false; 4212 } 4213 codeOut = codeA || codeB; 4214 p = _getEdgeIntersection(a, b, codeOut, bounds, round); 4215 newCode = _getBitCode(p, bounds); 4216 if (codeOut === codeA) { 4217 a = p; 4218 codeA = newCode; 4219 } else { 4220 b = p; 4221 codeB = newCode; 4222 } 4223 } 4224 } 4225 function _getEdgeIntersection(a, b, code, bounds, round) { 4226 var dx = b.x - a.x, dy = b.y - a.y, min = bounds.min, max = bounds.max, x, y; 4227 if (code & 8) { 4228 x = a.x + dx * (max.y - a.y) / dy; 4229 y = max.y; 4230 } else if (code & 4) { 4231 x = a.x + dx * (min.y - a.y) / dy; 4232 y = min.y; 4233 } else if (code & 2) { 4234 x = max.x; 4235 y = a.y + dy * (max.x - a.x) / dx; 4236 } else if (code & 1) { 4237 x = min.x; 4238 y = a.y + dy * (min.x - a.x) / dx; 4239 } 4240 return new Point(x, y, round); 4241 } 4242 function _getBitCode(p, bounds) { 4243 var code = 0; 4244 if (p.x < bounds.min.x) { 4245 code |= 1; 4246 } else if (p.x > bounds.max.x) { 4247 code |= 2; 4248 } 4249 if (p.y < bounds.min.y) { 4250 code |= 4; 4251 } else if (p.y > bounds.max.y) { 4252 code |= 8; 4253 } 4254 return code; 4255 } 4256 function _sqDist(p1, p2) { 4257 var dx = p2.x - p1.x, dy = p2.y - p1.y; 4258 return dx * dx + dy * dy; 4259 } 4260 function _sqClosestPointOnSegment(p, p1, p2, sqDist) { 4261 var x = p1.x, y = p1.y, dx = p2.x - x, dy = p2.y - y, dot = dx * dx + dy * dy, t; 4262 if (dot > 0) { 4263 t = ((p.x - x) * dx + (p.y - y) * dy) / dot; 4264 if (t > 1) { 4265 x = p2.x; 4266 y = p2.y; 4267 } else if (t > 0) { 4268 x += dx * t; 4269 y += dy * t; 4270 } 4271 } 4272 dx = p.x - x; 4273 dy = p.y - y; 4274 return sqDist ? dx * dx + dy * dy : new Point(x, y); 4275 } 4276 function isFlat(latlngs) { 4277 return !isArray(latlngs[0]) || typeof latlngs[0][0] !== "object" && typeof latlngs[0][0] !== "undefined"; 4278 } 4279 function _flat(latlngs) { 4280 console.warn("Deprecated use of _flat, please use L.LineUtil.isFlat instead."); 4281 return isFlat(latlngs); 4282 } 4283 function polylineCenter(latlngs, crs) { 4284 var i, halfDist, segDist, dist, p1, p2, ratio, center; 4285 if (!latlngs || latlngs.length === 0) { 4286 throw new Error("latlngs not passed"); 4287 } 4288 if (!isFlat(latlngs)) { 4289 console.warn("latlngs are not flat! Only the first ring will be used"); 4290 latlngs = latlngs[0]; 4291 } 4292 var centroidLatLng = toLatLng([0, 0]); 4293 var bounds = toLatLngBounds(latlngs); 4294 var areaBounds = bounds.getNorthWest().distanceTo(bounds.getSouthWest()) * bounds.getNorthEast().distanceTo(bounds.getNorthWest()); 4295 if (areaBounds < 1700) { 4296 centroidLatLng = centroid(latlngs); 4297 } 4298 var len = latlngs.length; 4299 var points = []; 4300 for (i = 0; i < len; i++) { 4301 var latlng = toLatLng(latlngs[i]); 4302 points.push(crs.project(toLatLng([latlng.lat - centroidLatLng.lat, latlng.lng - centroidLatLng.lng]))); 4303 } 4304 for (i = 0, halfDist = 0; i < len - 1; i++) { 4305 halfDist += points[i].distanceTo(points[i + 1]) / 2; 4306 } 4307 if (halfDist === 0) { 4308 center = points[0]; 4309 } else { 4310 for (i = 0, dist = 0; i < len - 1; i++) { 4311 p1 = points[i]; 4312 p2 = points[i + 1]; 4313 segDist = p1.distanceTo(p2); 4314 dist += segDist; 4315 if (dist > halfDist) { 4316 ratio = (dist - halfDist) / segDist; 4317 center = [ 4318 p2.x - ratio * (p2.x - p1.x), 4319 p2.y - ratio * (p2.y - p1.y) 4320 ]; 4321 break; 4322 } 4323 } 4324 } 4325 var latlngCenter = crs.unproject(toPoint(center)); 4326 return toLatLng([latlngCenter.lat + centroidLatLng.lat, latlngCenter.lng + centroidLatLng.lng]); 4327 } 4328 var LineUtil = { 4329 __proto__: null, 4330 simplify, 4331 pointToSegmentDistance, 4332 closestPointOnSegment, 4333 clipSegment, 4334 _getEdgeIntersection, 4335 _getBitCode, 4336 _sqClosestPointOnSegment, 4337 isFlat, 4338 _flat, 4339 polylineCenter 4340 }; 4341 var LonLat = { 4342 project: function(latlng) { 4343 return new Point(latlng.lng, latlng.lat); 4344 }, 4345 unproject: function(point) { 4346 return new LatLng(point.y, point.x); 4347 }, 4348 bounds: new Bounds([-180, -90], [180, 90]) 4349 }; 4350 var Mercator = { 4351 R: 6378137, 4352 R_MINOR: 6356752314245179e-9, 4353 bounds: new Bounds([-2003750834279e-5, -1549657073972e-5], [2003750834279e-5, 1876465623138e-5]), 4354 project: function(latlng) { 4355 var d = Math.PI / 180, r = this.R, y = latlng.lat * d, tmp = this.R_MINOR / r, e = Math.sqrt(1 - tmp * tmp), con = e * Math.sin(y); 4356 var ts = Math.tan(Math.PI / 4 - y / 2) / Math.pow((1 - con) / (1 + con), e / 2); 4357 y = -r * Math.log(Math.max(ts, 1e-10)); 4358 return new Point(latlng.lng * d * r, y); 4359 }, 4360 unproject: function(point) { 4361 var d = 180 / Math.PI, r = this.R, tmp = this.R_MINOR / r, e = Math.sqrt(1 - tmp * tmp), ts = Math.exp(-point.y / r), phi = Math.PI / 2 - 2 * Math.atan(ts); 4362 for (var i = 0, dphi = 0.1, con; i < 15 && Math.abs(dphi) > 1e-7; i++) { 4363 con = e * Math.sin(phi); 4364 con = Math.pow((1 - con) / (1 + con), e / 2); 4365 dphi = Math.PI / 2 - 2 * Math.atan(ts * con) - phi; 4366 phi += dphi; 4367 } 4368 return new LatLng(phi * d, point.x * d / r); 4369 } 4370 }; 4371 var index = { 4372 __proto__: null, 4373 LonLat, 4374 Mercator, 4375 SphericalMercator 4376 }; 4377 var EPSG3395 = extend({}, Earth, { 4378 code: "EPSG:3395", 4379 projection: Mercator, 4380 transformation: function() { 4381 var scale2 = 0.5 / (Math.PI * Mercator.R); 4382 return toTransformation(scale2, 0.5, -scale2, 0.5); 4383 }() 4384 }); 4385 var EPSG4326 = extend({}, Earth, { 4386 code: "EPSG:4326", 4387 projection: LonLat, 4388 transformation: toTransformation(1 / 180, 1, -1 / 180, 0.5) 4389 }); 4390 var Simple = extend({}, CRS, { 4391 projection: LonLat, 4392 transformation: toTransformation(1, 0, -1, 0), 4393 scale: function(zoom2) { 4394 return Math.pow(2, zoom2); 4395 }, 4396 zoom: function(scale2) { 4397 return Math.log(scale2) / Math.LN2; 4398 }, 4399 distance: function(latlng1, latlng2) { 4400 var dx = latlng2.lng - latlng1.lng, dy = latlng2.lat - latlng1.lat; 4401 return Math.sqrt(dx * dx + dy * dy); 4402 }, 4403 infinite: true 4404 }); 4405 CRS.Earth = Earth; 4406 CRS.EPSG3395 = EPSG3395; 4407 CRS.EPSG3857 = EPSG3857; 4408 CRS.EPSG900913 = EPSG900913; 4409 CRS.EPSG4326 = EPSG4326; 4410 CRS.Simple = Simple; 4411 var Layer = Evented.extend({ 4412 // Classes extending `L.Layer` will inherit the following options: 4413 options: { 4414 // @option pane: String = 'overlayPane' 4415 // By default the layer will be added to the map's [overlay pane](#map-overlaypane). Overriding this option will cause the layer to be placed on another pane by default. 4416 pane: "overlayPane", 4417 // @option attribution: String = null 4418 // String to be shown in the attribution control, e.g. "© OpenStreetMap contributors". It describes the layer data and is often a legal obligation towards copyright holders and tile providers. 4419 attribution: null, 4420 bubblingMouseEvents: true 4421 }, 4422 /* @section 4423 * Classes extending `L.Layer` will inherit the following methods: 4424 * 4425 * @method addTo(map: Map|LayerGroup): this 4426 * Adds the layer to the given map or layer group. 4427 */ 4428 addTo: function(map2) { 4429 map2.addLayer(this); 4430 return this; 4431 }, 4432 // @method remove: this 4433 // Removes the layer from the map it is currently active on. 4434 remove: function() { 4435 return this.removeFrom(this._map || this._mapToAdd); 4436 }, 4437 // @method removeFrom(map: Map): this 4438 // Removes the layer from the given map 4439 // 4440 // @alternative 4441 // @method removeFrom(group: LayerGroup): this 4442 // Removes the layer from the given `LayerGroup` 4443 removeFrom: function(obj) { 4444 if (obj) { 4445 obj.removeLayer(this); 4446 } 4447 return this; 4448 }, 4449 // @method getPane(name? : String): HTMLElement 4450 // Returns the `HTMLElement` representing the named pane on the map. If `name` is omitted, returns the pane for this layer. 4451 getPane: function(name) { 4452 return this._map.getPane(name ? this.options[name] || name : this.options.pane); 4453 }, 4454 addInteractiveTarget: function(targetEl) { 4455 this._map._targets[stamp(targetEl)] = this; 4456 return this; 4457 }, 4458 removeInteractiveTarget: function(targetEl) { 4459 delete this._map._targets[stamp(targetEl)]; 4460 return this; 4461 }, 4462 // @method getAttribution: String 4463 // Used by the `attribution control`, returns the [attribution option](#gridlayer-attribution). 4464 getAttribution: function() { 4465 return this.options.attribution; 4466 }, 4467 _layerAdd: function(e) { 4468 var map2 = e.target; 4469 if (!map2.hasLayer(this)) { 4470 return; 4471 } 4472 this._map = map2; 4473 this._zoomAnimated = map2._zoomAnimated; 4474 if (this.getEvents) { 4475 var events = this.getEvents(); 4476 map2.on(events, this); 4477 this.once("remove", function() { 4478 map2.off(events, this); 4479 }, this); 4480 } 4481 this.onAdd(map2); 4482 this.fire("add"); 4483 map2.fire("layeradd", { layer: this }); 4484 } 4485 }); 4486 Map2.include({ 4487 // @method addLayer(layer: Layer): this 4488 // Adds the given layer to the map 4489 addLayer: function(layer) { 4490 if (!layer._layerAdd) { 4491 throw new Error("The provided object is not a Layer."); 4492 } 4493 var id = stamp(layer); 4494 if (this._layers[id]) { 4495 return this; 4496 } 4497 this._layers[id] = layer; 4498 layer._mapToAdd = this; 4499 if (layer.beforeAdd) { 4500 layer.beforeAdd(this); 4501 } 4502 this.whenReady(layer._layerAdd, layer); 4503 return this; 4504 }, 4505 // @method removeLayer(layer: Layer): this 4506 // Removes the given layer from the map. 4507 removeLayer: function(layer) { 4508 var id = stamp(layer); 4509 if (!this._layers[id]) { 4510 return this; 4511 } 4512 if (this._loaded) { 4513 layer.onRemove(this); 4514 } 4515 delete this._layers[id]; 4516 if (this._loaded) { 4517 this.fire("layerremove", { layer }); 4518 layer.fire("remove"); 4519 } 4520 layer._map = layer._mapToAdd = null; 4521 return this; 4522 }, 4523 // @method hasLayer(layer: Layer): Boolean 4524 // Returns `true` if the given layer is currently added to the map 4525 hasLayer: function(layer) { 4526 return stamp(layer) in this._layers; 4527 }, 4528 /* @method eachLayer(fn: Function, context?: Object): this 4529 * Iterates over the layers of the map, optionally specifying context of the iterator function. 4530 * ``` 4531 * map.eachLayer(function(layer){ 4532 * layer.bindPopup('Hello'); 4533 * }); 4534 * ``` 4535 */ 4536 eachLayer: function(method, context) { 4537 for (var i in this._layers) { 4538 method.call(context, this._layers[i]); 4539 } 4540 return this; 4541 }, 4542 _addLayers: function(layers2) { 4543 layers2 = layers2 ? isArray(layers2) ? layers2 : [layers2] : []; 4544 for (var i = 0, len = layers2.length; i < len; i++) { 4545 this.addLayer(layers2[i]); 4546 } 4547 }, 4548 _addZoomLimit: function(layer) { 4549 if (!isNaN(layer.options.maxZoom) || !isNaN(layer.options.minZoom)) { 4550 this._zoomBoundLayers[stamp(layer)] = layer; 4551 this._updateZoomLevels(); 4552 } 4553 }, 4554 _removeZoomLimit: function(layer) { 4555 var id = stamp(layer); 4556 if (this._zoomBoundLayers[id]) { 4557 delete this._zoomBoundLayers[id]; 4558 this._updateZoomLevels(); 4559 } 4560 }, 4561 _updateZoomLevels: function() { 4562 var minZoom = Infinity, maxZoom = -Infinity, oldZoomSpan = this._getZoomSpan(); 4563 for (var i in this._zoomBoundLayers) { 4564 var options = this._zoomBoundLayers[i].options; 4565 minZoom = options.minZoom === void 0 ? minZoom : Math.min(minZoom, options.minZoom); 4566 maxZoom = options.maxZoom === void 0 ? maxZoom : Math.max(maxZoom, options.maxZoom); 4567 } 4568 this._layersMaxZoom = maxZoom === -Infinity ? void 0 : maxZoom; 4569 this._layersMinZoom = minZoom === Infinity ? void 0 : minZoom; 4570 if (oldZoomSpan !== this._getZoomSpan()) { 4571 this.fire("zoomlevelschange"); 4572 } 4573 if (this.options.maxZoom === void 0 && this._layersMaxZoom && this.getZoom() > this._layersMaxZoom) { 4574 this.setZoom(this._layersMaxZoom); 4575 } 4576 if (this.options.minZoom === void 0 && this._layersMinZoom && this.getZoom() < this._layersMinZoom) { 4577 this.setZoom(this._layersMinZoom); 4578 } 4579 } 4580 }); 4581 var LayerGroup = Layer.extend({ 4582 initialize: function(layers2, options) { 4583 setOptions(this, options); 4584 this._layers = {}; 4585 var i, len; 4586 if (layers2) { 4587 for (i = 0, len = layers2.length; i < len; i++) { 4588 this.addLayer(layers2[i]); 4589 } 4590 } 4591 }, 4592 // @method addLayer(layer: Layer): this 4593 // Adds the given layer to the group. 4594 addLayer: function(layer) { 4595 var id = this.getLayerId(layer); 4596 this._layers[id] = layer; 4597 if (this._map) { 4598 this._map.addLayer(layer); 4599 } 4600 return this; 4601 }, 4602 // @method removeLayer(layer: Layer): this 4603 // Removes the given layer from the group. 4604 // @alternative 4605 // @method removeLayer(id: Number): this 4606 // Removes the layer with the given internal ID from the group. 4607 removeLayer: function(layer) { 4608 var id = layer in this._layers ? layer : this.getLayerId(layer); 4609 if (this._map && this._layers[id]) { 4610 this._map.removeLayer(this._layers[id]); 4611 } 4612 delete this._layers[id]; 4613 return this; 4614 }, 4615 // @method hasLayer(layer: Layer): Boolean 4616 // Returns `true` if the given layer is currently added to the group. 4617 // @alternative 4618 // @method hasLayer(id: Number): Boolean 4619 // Returns `true` if the given internal ID is currently added to the group. 4620 hasLayer: function(layer) { 4621 var layerId = typeof layer === "number" ? layer : this.getLayerId(layer); 4622 return layerId in this._layers; 4623 }, 4624 // @method clearLayers(): this 4625 // Removes all the layers from the group. 4626 clearLayers: function() { 4627 return this.eachLayer(this.removeLayer, this); 4628 }, 4629 // @method invoke(methodName: String, …): this 4630 // Calls `methodName` on every layer contained in this group, passing any 4631 // additional parameters. Has no effect if the layers contained do not 4632 // implement `methodName`. 4633 invoke: function(methodName) { 4634 var args = Array.prototype.slice.call(arguments, 1), i, layer; 4635 for (i in this._layers) { 4636 layer = this._layers[i]; 4637 if (layer[methodName]) { 4638 layer[methodName].apply(layer, args); 4639 } 4640 } 4641 return this; 4642 }, 4643 onAdd: function(map2) { 4644 this.eachLayer(map2.addLayer, map2); 4645 }, 4646 onRemove: function(map2) { 4647 this.eachLayer(map2.removeLayer, map2); 4648 }, 4649 // @method eachLayer(fn: Function, context?: Object): this 4650 // Iterates over the layers of the group, optionally specifying context of the iterator function. 4651 // ```js 4652 // group.eachLayer(function (layer) { 4653 // layer.bindPopup('Hello'); 4654 // }); 4655 // ``` 4656 eachLayer: function(method, context) { 4657 for (var i in this._layers) { 4658 method.call(context, this._layers[i]); 4659 } 4660 return this; 4661 }, 4662 // @method getLayer(id: Number): Layer 4663 // Returns the layer with the given internal ID. 4664 getLayer: function(id) { 4665 return this._layers[id]; 4666 }, 4667 // @method getLayers(): Layer[] 4668 // Returns an array of all the layers added to the group. 4669 getLayers: function() { 4670 var layers2 = []; 4671 this.eachLayer(layers2.push, layers2); 4672 return layers2; 4673 }, 4674 // @method setZIndex(zIndex: Number): this 4675 // Calls `setZIndex` on every layer contained in this group, passing the z-index. 4676 setZIndex: function(zIndex) { 4677 return this.invoke("setZIndex", zIndex); 4678 }, 4679 // @method getLayerId(layer: Layer): Number 4680 // Returns the internal ID for a layer 4681 getLayerId: function(layer) { 4682 return stamp(layer); 4683 } 4684 }); 4685 var layerGroup2 = function(layers2, options) { 4686 return new LayerGroup(layers2, options); 4687 }; 4688 var FeatureGroup = LayerGroup.extend({ 4689 addLayer: function(layer) { 4690 if (this.hasLayer(layer)) { 4691 return this; 4692 } 4693 layer.addEventParent(this); 4694 LayerGroup.prototype.addLayer.call(this, layer); 4695 return this.fire("layeradd", { layer }); 4696 }, 4697 removeLayer: function(layer) { 4698 if (!this.hasLayer(layer)) { 4699 return this; 4700 } 4701 if (layer in this._layers) { 4702 layer = this._layers[layer]; 4703 } 4704 layer.removeEventParent(this); 4705 LayerGroup.prototype.removeLayer.call(this, layer); 4706 return this.fire("layerremove", { layer }); 4707 }, 4708 // @method setStyle(style: Path options): this 4709 // Sets the given path options to each layer of the group that has a `setStyle` method. 4710 setStyle: function(style2) { 4711 return this.invoke("setStyle", style2); 4712 }, 4713 // @method bringToFront(): this 4714 // Brings the layer group to the top of all other layers 4715 bringToFront: function() { 4716 return this.invoke("bringToFront"); 4717 }, 4718 // @method bringToBack(): this 4719 // Brings the layer group to the back of all other layers 4720 bringToBack: function() { 4721 return this.invoke("bringToBack"); 4722 }, 4723 // @method getBounds(): LatLngBounds 4724 // Returns the LatLngBounds of the Feature Group (created from bounds and coordinates of its children). 4725 getBounds: function() { 4726 var bounds = new LatLngBounds(); 4727 for (var id in this._layers) { 4728 var layer = this._layers[id]; 4729 bounds.extend(layer.getBounds ? layer.getBounds() : layer.getLatLng()); 4730 } 4731 return bounds; 4732 } 4733 }); 4734 var featureGroup2 = function(layers2, options) { 4735 return new FeatureGroup(layers2, options); 4736 }; 4737 var Icon = Class.extend({ 4738 /* @section 4739 * @aka Icon options 4740 * 4741 * @option iconUrl: String = null 4742 * **(required)** The URL to the icon image (absolute or relative to your script path). 4743 * 4744 * @option iconRetinaUrl: String = null 4745 * The URL to a retina sized version of the icon image (absolute or relative to your 4746 * script path). Used for Retina screen devices. 4747 * 4748 * @option iconSize: Point = null 4749 * Size of the icon image in pixels. 4750 * 4751 * @option iconAnchor: Point = null 4752 * The coordinates of the "tip" of the icon (relative to its top left corner). The icon 4753 * will be aligned so that this point is at the marker's geographical location. Centered 4754 * by default if size is specified, also can be set in CSS with negative margins. 4755 * 4756 * @option popupAnchor: Point = [0, 0] 4757 * The coordinates of the point from which popups will "open", relative to the icon anchor. 4758 * 4759 * @option tooltipAnchor: Point = [0, 0] 4760 * The coordinates of the point from which tooltips will "open", relative to the icon anchor. 4761 * 4762 * @option shadowUrl: String = null 4763 * The URL to the icon shadow image. If not specified, no shadow image will be created. 4764 * 4765 * @option shadowRetinaUrl: String = null 4766 * 4767 * @option shadowSize: Point = null 4768 * Size of the shadow image in pixels. 4769 * 4770 * @option shadowAnchor: Point = null 4771 * The coordinates of the "tip" of the shadow (relative to its top left corner) (the same 4772 * as iconAnchor if not specified). 4773 * 4774 * @option className: String = '' 4775 * A custom class name to assign to both icon and shadow images. Empty by default. 4776 */ 4777 options: { 4778 popupAnchor: [0, 0], 4779 tooltipAnchor: [0, 0], 4780 // @option crossOrigin: Boolean|String = false 4781 // Whether the crossOrigin attribute will be added to the tiles. 4782 // If a String is provided, all tiles will have their crossOrigin attribute set to the String provided. This is needed if you want to access tile pixel data. 4783 // Refer to [CORS Settings](https://developer.mozilla.org/en-US/docs/Web/HTML/CORS_settings_attributes) for valid String values. 4784 crossOrigin: false 4785 }, 4786 initialize: function(options) { 4787 setOptions(this, options); 4788 }, 4789 // @method createIcon(oldIcon?: HTMLElement): HTMLElement 4790 // Called internally when the icon has to be shown, returns a `<img>` HTML element 4791 // styled according to the options. 4792 createIcon: function(oldIcon) { 4793 return this._createIcon("icon", oldIcon); 4794 }, 4795 // @method createShadow(oldIcon?: HTMLElement): HTMLElement 4796 // As `createIcon`, but for the shadow beneath it. 4797 createShadow: function(oldIcon) { 4798 return this._createIcon("shadow", oldIcon); 4799 }, 4800 _createIcon: function(name, oldIcon) { 4801 var src = this._getIconUrl(name); 4802 if (!src) { 4803 if (name === "icon") { 4804 throw new Error("iconUrl not set in Icon options (see the docs)."); 4805 } 4806 return null; 4807 } 4808 var img = this._createImg(src, oldIcon && oldIcon.tagName === "IMG" ? oldIcon : null); 4809 this._setIconStyles(img, name); 4810 if (this.options.crossOrigin || this.options.crossOrigin === "") { 4811 img.crossOrigin = this.options.crossOrigin === true ? "" : this.options.crossOrigin; 4812 } 4813 return img; 4814 }, 4815 _setIconStyles: function(img, name) { 4816 var options = this.options; 4817 var sizeOption = options[name + "Size"]; 4818 if (typeof sizeOption === "number") { 4819 sizeOption = [sizeOption, sizeOption]; 4820 } 4821 var size = toPoint(sizeOption), anchor = toPoint(name === "shadow" && options.shadowAnchor || options.iconAnchor || size && size.divideBy(2, true)); 4822 img.className = "leaflet-marker-" + name + " " + (options.className || ""); 4823 if (anchor) { 4824 img.style.marginLeft = -anchor.x + "px"; 4825 img.style.marginTop = -anchor.y + "px"; 4826 } 4827 if (size) { 4828 img.style.width = size.x + "px"; 4829 img.style.height = size.y + "px"; 4830 } 4831 }, 4832 _createImg: function(src, el) { 4833 el = el || document.createElement("img"); 4834 el.src = src; 4835 return el; 4836 }, 4837 _getIconUrl: function(name) { 4838 return Browser.retina && this.options[name + "RetinaUrl"] || this.options[name + "Url"]; 4839 } 4840 }); 4841 function icon(options) { 4842 return new Icon(options); 4843 } 4844 var IconDefault = Icon.extend({ 4845 options: { 4846 iconUrl: "marker-icon.png", 4847 iconRetinaUrl: "marker-icon-2x.png", 4848 shadowUrl: "marker-shadow.png", 4849 iconSize: [25, 41], 4850 iconAnchor: [12, 41], 4851 popupAnchor: [1, -34], 4852 tooltipAnchor: [16, -28], 4853 shadowSize: [41, 41] 4854 }, 4855 _getIconUrl: function(name) { 4856 if (typeof IconDefault.imagePath !== "string") { 4857 IconDefault.imagePath = this._detectIconPath(); 4858 } 4859 return (this.options.imagePath || IconDefault.imagePath) + Icon.prototype._getIconUrl.call(this, name); 4860 }, 4861 _stripUrl: function(path) { 4862 var strip = function(str, re, idx) { 4863 var match = re.exec(str); 4864 return match && match[idx]; 4865 }; 4866 path = strip(path, /^url\((['"])?(.+)\1\)$/, 2); 4867 return path && strip(path, /^(.*)marker-icon\.png$/, 1); 4868 }, 4869 _detectIconPath: function() { 4870 var el = create$1("div", "leaflet-default-icon-path", document.body); 4871 var path = getStyle(el, "background-image") || getStyle(el, "backgroundImage"); 4872 document.body.removeChild(el); 4873 path = this._stripUrl(path); 4874 if (path) { 4875 return path; 4876 } 4877 var link = document.querySelector('link[href$="leaflet.css"]'); 4878 if (!link) { 4879 return ""; 4880 } 4881 return link.href.substring(0, link.href.length - "leaflet.css".length - 1); 4882 } 4883 }); 4884 var MarkerDrag = Handler.extend({ 4885 initialize: function(marker3) { 4886 this._marker = marker3; 4887 }, 4888 addHooks: function() { 4889 var icon2 = this._marker._icon; 4890 if (!this._draggable) { 4891 this._draggable = new Draggable(icon2, icon2, true); 4892 } 4893 this._draggable.on({ 4894 dragstart: this._onDragStart, 4895 predrag: this._onPreDrag, 4896 drag: this._onDrag, 4897 dragend: this._onDragEnd 4898 }, this).enable(); 4899 addClass(icon2, "leaflet-marker-draggable"); 4900 }, 4901 removeHooks: function() { 4902 this._draggable.off({ 4903 dragstart: this._onDragStart, 4904 predrag: this._onPreDrag, 4905 drag: this._onDrag, 4906 dragend: this._onDragEnd 4907 }, this).disable(); 4908 if (this._marker._icon) { 4909 removeClass(this._marker._icon, "leaflet-marker-draggable"); 4910 } 4911 }, 4912 moved: function() { 4913 return this._draggable && this._draggable._moved; 4914 }, 4915 _adjustPan: function(e) { 4916 var marker3 = this._marker, map2 = marker3._map, speed = this._marker.options.autoPanSpeed, padding = this._marker.options.autoPanPadding, iconPos = getPosition(marker3._icon), bounds = map2.getPixelBounds(), origin = map2.getPixelOrigin(); 4917 var panBounds = toBounds( 4918 bounds.min._subtract(origin).add(padding), 4919 bounds.max._subtract(origin).subtract(padding) 4920 ); 4921 if (!panBounds.contains(iconPos)) { 4922 var movement = toPoint( 4923 (Math.max(panBounds.max.x, iconPos.x) - panBounds.max.x) / (bounds.max.x - panBounds.max.x) - (Math.min(panBounds.min.x, iconPos.x) - panBounds.min.x) / (bounds.min.x - panBounds.min.x), 4924 (Math.max(panBounds.max.y, iconPos.y) - panBounds.max.y) / (bounds.max.y - panBounds.max.y) - (Math.min(panBounds.min.y, iconPos.y) - panBounds.min.y) / (bounds.min.y - panBounds.min.y) 4925 ).multiplyBy(speed); 4926 map2.panBy(movement, { animate: false }); 4927 this._draggable._newPos._add(movement); 4928 this._draggable._startPos._add(movement); 4929 setPosition(marker3._icon, this._draggable._newPos); 4930 this._onDrag(e); 4931 this._panRequest = requestAnimFrame(this._adjustPan.bind(this, e)); 4932 } 4933 }, 4934 _onDragStart: function() { 4935 this._oldLatLng = this._marker.getLatLng(); 4936 this._marker.closePopup && this._marker.closePopup(); 4937 this._marker.fire("movestart").fire("dragstart"); 4938 }, 4939 _onPreDrag: function(e) { 4940 if (this._marker.options.autoPan) { 4941 cancelAnimFrame(this._panRequest); 4942 this._panRequest = requestAnimFrame(this._adjustPan.bind(this, e)); 4943 } 4944 }, 4945 _onDrag: function(e) { 4946 var marker3 = this._marker, shadow = marker3._shadow, iconPos = getPosition(marker3._icon), latlng = marker3._map.layerPointToLatLng(iconPos); 4947 if (shadow) { 4948 setPosition(shadow, iconPos); 4949 } 4950 marker3._latlng = latlng; 4951 e.latlng = latlng; 4952 e.oldLatLng = this._oldLatLng; 4953 marker3.fire("move", e).fire("drag", e); 4954 }, 4955 _onDragEnd: function(e) { 4956 cancelAnimFrame(this._panRequest); 4957 delete this._oldLatLng; 4958 this._marker.fire("moveend").fire("dragend", e); 4959 } 4960 }); 4961 var Marker = Layer.extend({ 4962 // @section 4963 // @aka Marker options 4964 options: { 4965 // @option icon: Icon = * 4966 // Icon instance to use for rendering the marker. 4967 // See [Icon documentation](#L.Icon) for details on how to customize the marker icon. 4968 // If not specified, a common instance of `L.Icon.Default` is used. 4969 icon: new IconDefault(), 4970 // Option inherited from "Interactive layer" abstract class 4971 interactive: true, 4972 // @option keyboard: Boolean = true 4973 // Whether the marker can be tabbed to with a keyboard and clicked by pressing enter. 4974 keyboard: true, 4975 // @option title: String = '' 4976 // Text for the browser tooltip that appear on marker hover (no tooltip by default). 4977 // [Useful for accessibility](https://leafletjs.com/examples/accessibility/#markers-must-be-labelled). 4978 title: "", 4979 // @option alt: String = 'Marker' 4980 // Text for the `alt` attribute of the icon image. 4981 // [Useful for accessibility](https://leafletjs.com/examples/accessibility/#markers-must-be-labelled). 4982 alt: "Marker", 4983 // @option zIndexOffset: Number = 0 4984 // By default, marker images zIndex is set automatically based on its latitude. Use this option if you want to put the marker on top of all others (or below), specifying a high value like `1000` (or high negative value, respectively). 4985 zIndexOffset: 0, 4986 // @option opacity: Number = 1.0 4987 // The opacity of the marker. 4988 opacity: 1, 4989 // @option riseOnHover: Boolean = false 4990 // If `true`, the marker will get on top of others when you hover the mouse over it. 4991 riseOnHover: false, 4992 // @option riseOffset: Number = 250 4993 // The z-index offset used for the `riseOnHover` feature. 4994 riseOffset: 250, 4995 // @option pane: String = 'markerPane' 4996 // `Map pane` where the markers icon will be added. 4997 pane: "markerPane", 4998 // @option shadowPane: String = 'shadowPane' 4999 // `Map pane` where the markers shadow will be added. 5000 shadowPane: "shadowPane", 5001 // @option bubblingMouseEvents: Boolean = false 5002 // When `true`, a mouse event on this marker will trigger the same event on the map 5003 // (unless [`L.DomEvent.stopPropagation`](#domevent-stoppropagation) is used). 5004 bubblingMouseEvents: false, 5005 // @option autoPanOnFocus: Boolean = true 5006 // When `true`, the map will pan whenever the marker is focused (via 5007 // e.g. pressing `tab` on the keyboard) to ensure the marker is 5008 // visible within the map's bounds 5009 autoPanOnFocus: true, 5010 // @section Draggable marker options 5011 // @option draggable: Boolean = false 5012 // Whether the marker is draggable with mouse/touch or not. 5013 draggable: false, 5014 // @option autoPan: Boolean = false 5015 // Whether to pan the map when dragging this marker near its edge or not. 5016 autoPan: false, 5017 // @option autoPanPadding: Point = Point(50, 50) 5018 // Distance (in pixels to the left/right and to the top/bottom) of the 5019 // map edge to start panning the map. 5020 autoPanPadding: [50, 50], 5021 // @option autoPanSpeed: Number = 10 5022 // Number of pixels the map should pan by. 5023 autoPanSpeed: 10 5024 }, 5025 /* @section 5026 * 5027 * In addition to [shared layer methods](#Layer) like `addTo()` and `remove()` and [popup methods](#Popup) like bindPopup() you can also use the following methods: 5028 */ 5029 initialize: function(latlng, options) { 5030 setOptions(this, options); 5031 this._latlng = toLatLng(latlng); 5032 }, 5033 onAdd: function(map2) { 5034 this._zoomAnimated = this._zoomAnimated && map2.options.markerZoomAnimation; 5035 if (this._zoomAnimated) { 5036 map2.on("zoomanim", this._animateZoom, this); 5037 } 5038 this._initIcon(); 5039 this.update(); 5040 }, 5041 onRemove: function(map2) { 5042 if (this.dragging && this.dragging.enabled()) { 5043 this.options.draggable = true; 5044 this.dragging.removeHooks(); 5045 } 5046 delete this.dragging; 5047 if (this._zoomAnimated) { 5048 map2.off("zoomanim", this._animateZoom, this); 5049 } 5050 this._removeIcon(); 5051 this._removeShadow(); 5052 }, 5053 getEvents: function() { 5054 return { 5055 zoom: this.update, 5056 viewreset: this.update 5057 }; 5058 }, 5059 // @method getLatLng: LatLng 5060 // Returns the current geographical position of the marker. 5061 getLatLng: function() { 5062 return this._latlng; 5063 }, 5064 // @method setLatLng(latlng: LatLng): this 5065 // Changes the marker position to the given point. 5066 setLatLng: function(latlng) { 5067 var oldLatLng = this._latlng; 5068 this._latlng = toLatLng(latlng); 5069 this.update(); 5070 return this.fire("move", { oldLatLng, latlng: this._latlng }); 5071 }, 5072 // @method setZIndexOffset(offset: Number): this 5073 // Changes the [zIndex offset](#marker-zindexoffset) of the marker. 5074 setZIndexOffset: function(offset) { 5075 this.options.zIndexOffset = offset; 5076 return this.update(); 5077 }, 5078 // @method getIcon: Icon 5079 // Returns the current icon used by the marker 5080 getIcon: function() { 5081 return this.options.icon; 5082 }, 5083 // @method setIcon(icon: Icon): this 5084 // Changes the marker icon. 5085 setIcon: function(icon2) { 5086 this.options.icon = icon2; 5087 if (this._map) { 5088 this._initIcon(); 5089 this.update(); 5090 } 5091 if (this._popup) { 5092 this.bindPopup(this._popup, this._popup.options); 5093 } 5094 return this; 5095 }, 5096 getElement: function() { 5097 return this._icon; 5098 }, 5099 update: function() { 5100 if (this._icon && this._map) { 5101 var pos = this._map.latLngToLayerPoint(this._latlng).round(); 5102 this._setPos(pos); 5103 } 5104 return this; 5105 }, 5106 _initIcon: function() { 5107 var options = this.options, classToAdd = "leaflet-zoom-" + (this._zoomAnimated ? "animated" : "hide"); 5108 var icon2 = options.icon.createIcon(this._icon), addIcon = false; 5109 if (icon2 !== this._icon) { 5110 if (this._icon) { 5111 this._removeIcon(); 5112 } 5113 addIcon = true; 5114 if (options.title) { 5115 icon2.title = options.title; 5116 } 5117 if (icon2.tagName === "IMG") { 5118 icon2.alt = options.alt || ""; 5119 } 5120 } 5121 addClass(icon2, classToAdd); 5122 if (options.keyboard) { 5123 icon2.tabIndex = "0"; 5124 icon2.setAttribute("role", "button"); 5125 } 5126 this._icon = icon2; 5127 if (options.riseOnHover) { 5128 this.on({ 5129 mouseover: this._bringToFront, 5130 mouseout: this._resetZIndex 5131 }); 5132 } 5133 if (this.options.autoPanOnFocus) { 5134 on(icon2, "focus", this._panOnFocus, this); 5135 } 5136 var newShadow = options.icon.createShadow(this._shadow), addShadow = false; 5137 if (newShadow !== this._shadow) { 5138 this._removeShadow(); 5139 addShadow = true; 5140 } 5141 if (newShadow) { 5142 addClass(newShadow, classToAdd); 5143 newShadow.alt = ""; 5144 } 5145 this._shadow = newShadow; 5146 if (options.opacity < 1) { 5147 this._updateOpacity(); 5148 } 5149 if (addIcon) { 5150 this.getPane().appendChild(this._icon); 5151 } 5152 this._initInteraction(); 5153 if (newShadow && addShadow) { 5154 this.getPane(options.shadowPane).appendChild(this._shadow); 5155 } 5156 }, 5157 _removeIcon: function() { 5158 if (this.options.riseOnHover) { 5159 this.off({ 5160 mouseover: this._bringToFront, 5161 mouseout: this._resetZIndex 5162 }); 5163 } 5164 if (this.options.autoPanOnFocus) { 5165 off(this._icon, "focus", this._panOnFocus, this); 5166 } 5167 remove(this._icon); 5168 this.removeInteractiveTarget(this._icon); 5169 this._icon = null; 5170 }, 5171 _removeShadow: function() { 5172 if (this._shadow) { 5173 remove(this._shadow); 5174 } 5175 this._shadow = null; 5176 }, 5177 _setPos: function(pos) { 5178 if (this._icon) { 5179 setPosition(this._icon, pos); 5180 } 5181 if (this._shadow) { 5182 setPosition(this._shadow, pos); 5183 } 5184 this._zIndex = pos.y + this.options.zIndexOffset; 5185 this._resetZIndex(); 5186 }, 5187 _updateZIndex: function(offset) { 5188 if (this._icon) { 5189 this._icon.style.zIndex = this._zIndex + offset; 5190 } 5191 }, 5192 _animateZoom: function(opt) { 5193 var pos = this._map._latLngToNewLayerPoint(this._latlng, opt.zoom, opt.center).round(); 5194 this._setPos(pos); 5195 }, 5196 _initInteraction: function() { 5197 if (!this.options.interactive) { 5198 return; 5199 } 5200 addClass(this._icon, "leaflet-interactive"); 5201 this.addInteractiveTarget(this._icon); 5202 if (MarkerDrag) { 5203 var draggable = this.options.draggable; 5204 if (this.dragging) { 5205 draggable = this.dragging.enabled(); 5206 this.dragging.disable(); 5207 } 5208 this.dragging = new MarkerDrag(this); 5209 if (draggable) { 5210 this.dragging.enable(); 5211 } 5212 } 5213 }, 5214 // @method setOpacity(opacity: Number): this 5215 // Changes the opacity of the marker. 5216 setOpacity: function(opacity) { 5217 this.options.opacity = opacity; 5218 if (this._map) { 5219 this._updateOpacity(); 5220 } 5221 return this; 5222 }, 5223 _updateOpacity: function() { 5224 var opacity = this.options.opacity; 5225 if (this._icon) { 5226 setOpacity(this._icon, opacity); 5227 } 5228 if (this._shadow) { 5229 setOpacity(this._shadow, opacity); 5230 } 5231 }, 5232 _bringToFront: function() { 5233 this._updateZIndex(this.options.riseOffset); 5234 }, 5235 _resetZIndex: function() { 5236 this._updateZIndex(0); 5237 }, 5238 _panOnFocus: function() { 5239 var map2 = this._map; 5240 if (!map2) { 5241 return; 5242 } 5243 var iconOpts = this.options.icon.options; 5244 var size = iconOpts.iconSize ? toPoint(iconOpts.iconSize) : toPoint(0, 0); 5245 var anchor = iconOpts.iconAnchor ? toPoint(iconOpts.iconAnchor) : toPoint(0, 0); 5246 map2.panInside(this._latlng, { 5247 paddingTopLeft: anchor, 5248 paddingBottomRight: size.subtract(anchor) 5249 }); 5250 }, 5251 _getPopupAnchor: function() { 5252 return this.options.icon.options.popupAnchor; 5253 }, 5254 _getTooltipAnchor: function() { 5255 return this.options.icon.options.tooltipAnchor; 5256 } 5257 }); 5258 function marker2(latlng, options) { 5259 return new Marker(latlng, options); 5260 } 5261 var Path = Layer.extend({ 5262 // @section 5263 // @aka Path options 5264 options: { 5265 // @option stroke: Boolean = true 5266 // Whether to draw stroke along the path. Set it to `false` to disable borders on polygons or circles. 5267 stroke: true, 5268 // @option color: String = '#3388ff' 5269 // Stroke color 5270 color: "#3388ff", 5271 // @option weight: Number = 3 5272 // Stroke width in pixels 5273 weight: 3, 5274 // @option opacity: Number = 1.0 5275 // Stroke opacity 5276 opacity: 1, 5277 // @option lineCap: String= 'round' 5278 // A string that defines [shape to be used at the end](https://developer.mozilla.org/docs/Web/SVG/Attribute/stroke-linecap) of the stroke. 5279 lineCap: "round", 5280 // @option lineJoin: String = 'round' 5281 // A string that defines [shape to be used at the corners](https://developer.mozilla.org/docs/Web/SVG/Attribute/stroke-linejoin) of the stroke. 5282 lineJoin: "round", 5283 // @option dashArray: String = null 5284 // A string that defines the stroke [dash pattern](https://developer.mozilla.org/docs/Web/SVG/Attribute/stroke-dasharray). Doesn't work on `Canvas`-powered layers in [some old browsers](https://developer.mozilla.org/docs/Web/API/CanvasRenderingContext2D/setLineDash#Browser_compatibility). 5285 dashArray: null, 5286 // @option dashOffset: String = null 5287 // A string that defines the [distance into the dash pattern to start the dash](https://developer.mozilla.org/docs/Web/SVG/Attribute/stroke-dashoffset). Doesn't work on `Canvas`-powered layers in [some old browsers](https://developer.mozilla.org/docs/Web/API/CanvasRenderingContext2D/setLineDash#Browser_compatibility). 5288 dashOffset: null, 5289 // @option fill: Boolean = depends 5290 // Whether to fill the path with color. Set it to `false` to disable filling on polygons or circles. 5291 fill: false, 5292 // @option fillColor: String = * 5293 // Fill color. Defaults to the value of the [`color`](#path-color) option 5294 fillColor: null, 5295 // @option fillOpacity: Number = 0.2 5296 // Fill opacity. 5297 fillOpacity: 0.2, 5298 // @option fillRule: String = 'evenodd' 5299 // A string that defines [how the inside of a shape](https://developer.mozilla.org/docs/Web/SVG/Attribute/fill-rule) is determined. 5300 fillRule: "evenodd", 5301 // className: '', 5302 // Option inherited from "Interactive layer" abstract class 5303 interactive: true, 5304 // @option bubblingMouseEvents: Boolean = true 5305 // When `true`, a mouse event on this path will trigger the same event on the map 5306 // (unless [`L.DomEvent.stopPropagation`](#domevent-stoppropagation) is used). 5307 bubblingMouseEvents: true 5308 }, 5309 beforeAdd: function(map2) { 5310 this._renderer = map2.getRenderer(this); 5311 }, 5312 onAdd: function() { 5313 this._renderer._initPath(this); 5314 this._reset(); 5315 this._renderer._addPath(this); 5316 }, 5317 onRemove: function() { 5318 this._renderer._removePath(this); 5319 }, 5320 // @method redraw(): this 5321 // Redraws the layer. Sometimes useful after you changed the coordinates that the path uses. 5322 redraw: function() { 5323 if (this._map) { 5324 this._renderer._updatePath(this); 5325 } 5326 return this; 5327 }, 5328 // @method setStyle(style: Path options): this 5329 // Changes the appearance of a Path based on the options in the `Path options` object. 5330 setStyle: function(style2) { 5331 setOptions(this, style2); 5332 if (this._renderer) { 5333 this._renderer._updateStyle(this); 5334 if (this.options.stroke && style2 && Object.prototype.hasOwnProperty.call(style2, "weight")) { 5335 this._updateBounds(); 5336 } 5337 } 5338 return this; 5339 }, 5340 // @method bringToFront(): this 5341 // Brings the layer to the top of all path layers. 5342 bringToFront: function() { 5343 if (this._renderer) { 5344 this._renderer._bringToFront(this); 5345 } 5346 return this; 5347 }, 5348 // @method bringToBack(): this 5349 // Brings the layer to the bottom of all path layers. 5350 bringToBack: function() { 5351 if (this._renderer) { 5352 this._renderer._bringToBack(this); 5353 } 5354 return this; 5355 }, 5356 getElement: function() { 5357 return this._path; 5358 }, 5359 _reset: function() { 5360 this._project(); 5361 this._update(); 5362 }, 5363 _clickTolerance: function() { 5364 return (this.options.stroke ? this.options.weight / 2 : 0) + (this._renderer.options.tolerance || 0); 5365 } 5366 }); 5367 var CircleMarker = Path.extend({ 5368 // @section 5369 // @aka CircleMarker options 5370 options: { 5371 fill: true, 5372 // @option radius: Number = 10 5373 // Radius of the circle marker, in pixels 5374 radius: 10 5375 }, 5376 initialize: function(latlng, options) { 5377 setOptions(this, options); 5378 this._latlng = toLatLng(latlng); 5379 this._radius = this.options.radius; 5380 }, 5381 // @method setLatLng(latLng: LatLng): this 5382 // Sets the position of a circle marker to a new location. 5383 setLatLng: function(latlng) { 5384 var oldLatLng = this._latlng; 5385 this._latlng = toLatLng(latlng); 5386 this.redraw(); 5387 return this.fire("move", { oldLatLng, latlng: this._latlng }); 5388 }, 5389 // @method getLatLng(): LatLng 5390 // Returns the current geographical position of the circle marker 5391 getLatLng: function() { 5392 return this._latlng; 5393 }, 5394 // @method setRadius(radius: Number): this 5395 // Sets the radius of a circle marker. Units are in pixels. 5396 setRadius: function(radius) { 5397 this.options.radius = this._radius = radius; 5398 return this.redraw(); 5399 }, 5400 // @method getRadius(): Number 5401 // Returns the current radius of the circle 5402 getRadius: function() { 5403 return this._radius; 5404 }, 5405 setStyle: function(options) { 5406 var radius = options && options.radius || this._radius; 5407 Path.prototype.setStyle.call(this, options); 5408 this.setRadius(radius); 5409 return this; 5410 }, 5411 _project: function() { 5412 this._point = this._map.latLngToLayerPoint(this._latlng); 5413 this._updateBounds(); 5414 }, 5415 _updateBounds: function() { 5416 var r = this._radius, r2 = this._radiusY || r, w = this._clickTolerance(), p = [r + w, r2 + w]; 5417 this._pxBounds = new Bounds(this._point.subtract(p), this._point.add(p)); 5418 }, 5419 _update: function() { 5420 if (this._map) { 5421 this._updatePath(); 5422 } 5423 }, 5424 _updatePath: function() { 5425 this._renderer._updateCircle(this); 5426 }, 5427 _empty: function() { 5428 return this._radius && !this._renderer._bounds.intersects(this._pxBounds); 5429 }, 5430 // Needed by the `Canvas` renderer for interactivity 5431 _containsPoint: function(p) { 5432 return p.distanceTo(this._point) <= this._radius + this._clickTolerance(); 5433 } 5434 }); 5435 function circleMarker2(latlng, options) { 5436 return new CircleMarker(latlng, options); 5437 } 5438 var Circle = CircleMarker.extend({ 5439 initialize: function(latlng, options, legacyOptions) { 5440 if (typeof options === "number") { 5441 options = extend({}, legacyOptions, { radius: options }); 5442 } 5443 setOptions(this, options); 5444 this._latlng = toLatLng(latlng); 5445 if (isNaN(this.options.radius)) { 5446 throw new Error("Circle radius cannot be NaN"); 5447 } 5448 this._mRadius = this.options.radius; 5449 }, 5450 // @method setRadius(radius: Number): this 5451 // Sets the radius of a circle. Units are in meters. 5452 setRadius: function(radius) { 5453 this._mRadius = radius; 5454 return this.redraw(); 5455 }, 5456 // @method getRadius(): Number 5457 // Returns the current radius of a circle. Units are in meters. 5458 getRadius: function() { 5459 return this._mRadius; 5460 }, 5461 // @method getBounds(): LatLngBounds 5462 // Returns the `LatLngBounds` of the path. 5463 getBounds: function() { 5464 var half = [this._radius, this._radiusY || this._radius]; 5465 return new LatLngBounds( 5466 this._map.layerPointToLatLng(this._point.subtract(half)), 5467 this._map.layerPointToLatLng(this._point.add(half)) 5468 ); 5469 }, 5470 setStyle: Path.prototype.setStyle, 5471 _project: function() { 5472 var lng = this._latlng.lng, lat = this._latlng.lat, map2 = this._map, crs = map2.options.crs; 5473 if (crs.distance === Earth.distance) { 5474 var d = Math.PI / 180, latR = this._mRadius / Earth.R / d, top = map2.project([lat + latR, lng]), bottom = map2.project([lat - latR, lng]), p = top.add(bottom).divideBy(2), lat2 = map2.unproject(p).lat, lngR = Math.acos((Math.cos(latR * d) - Math.sin(lat * d) * Math.sin(lat2 * d)) / (Math.cos(lat * d) * Math.cos(lat2 * d))) / d; 5475 if (isNaN(lngR) || lngR === 0) { 5476 lngR = latR / Math.cos(Math.PI / 180 * lat); 5477 } 5478 this._point = p.subtract(map2.getPixelOrigin()); 5479 this._radius = isNaN(lngR) ? 0 : p.x - map2.project([lat2, lng - lngR]).x; 5480 this._radiusY = p.y - top.y; 5481 } else { 5482 var latlng2 = crs.unproject(crs.project(this._latlng).subtract([this._mRadius, 0])); 5483 this._point = map2.latLngToLayerPoint(this._latlng); 5484 this._radius = this._point.x - map2.latLngToLayerPoint(latlng2).x; 5485 } 5486 this._updateBounds(); 5487 } 5488 }); 5489 function circle(latlng, options, legacyOptions) { 5490 return new Circle(latlng, options, legacyOptions); 5491 } 5492 var Polyline = Path.extend({ 5493 // @section 5494 // @aka Polyline options 5495 options: { 5496 // @option smoothFactor: Number = 1.0 5497 // How much to simplify the polyline on each zoom level. More means 5498 // better performance and smoother look, and less means more accurate representation. 5499 smoothFactor: 1, 5500 // @option noClip: Boolean = false 5501 // Disable polyline clipping. 5502 noClip: false 5503 }, 5504 initialize: function(latlngs, options) { 5505 setOptions(this, options); 5506 this._setLatLngs(latlngs); 5507 }, 5508 // @method getLatLngs(): LatLng[] 5509 // Returns an array of the points in the path, or nested arrays of points in case of multi-polyline. 5510 getLatLngs: function() { 5511 return this._latlngs; 5512 }, 5513 // @method setLatLngs(latlngs: LatLng[]): this 5514 // Replaces all the points in the polyline with the given array of geographical points. 5515 setLatLngs: function(latlngs) { 5516 this._setLatLngs(latlngs); 5517 return this.redraw(); 5518 }, 5519 // @method isEmpty(): Boolean 5520 // Returns `true` if the Polyline has no LatLngs. 5521 isEmpty: function() { 5522 return !this._latlngs.length; 5523 }, 5524 // @method closestLayerPoint(p: Point): Point 5525 // Returns the point closest to `p` on the Polyline. 5526 closestLayerPoint: function(p) { 5527 var minDistance = Infinity, minPoint = null, closest = _sqClosestPointOnSegment, p1, p2; 5528 for (var j = 0, jLen = this._parts.length; j < jLen; j++) { 5529 var points = this._parts[j]; 5530 for (var i = 1, len = points.length; i < len; i++) { 5531 p1 = points[i - 1]; 5532 p2 = points[i]; 5533 var sqDist = closest(p, p1, p2, true); 5534 if (sqDist < minDistance) { 5535 minDistance = sqDist; 5536 minPoint = closest(p, p1, p2); 5537 } 5538 } 5539 } 5540 if (minPoint) { 5541 minPoint.distance = Math.sqrt(minDistance); 5542 } 5543 return minPoint; 5544 }, 5545 // @method getCenter(): LatLng 5546 // Returns the center ([centroid](https://en.wikipedia.org/wiki/Centroid)) of the polyline. 5547 getCenter: function() { 5548 if (!this._map) { 5549 throw new Error("Must add layer to map before using getCenter()"); 5550 } 5551 return polylineCenter(this._defaultShape(), this._map.options.crs); 5552 }, 5553 // @method getBounds(): LatLngBounds 5554 // Returns the `LatLngBounds` of the path. 5555 getBounds: function() { 5556 return this._bounds; 5557 }, 5558 // @method addLatLng(latlng: LatLng, latlngs?: LatLng[]): this 5559 // Adds a given point to the polyline. By default, adds to the first ring of 5560 // the polyline in case of a multi-polyline, but can be overridden by passing 5561 // a specific ring as a LatLng array (that you can earlier access with [`getLatLngs`](#polyline-getlatlngs)). 5562 addLatLng: function(latlng, latlngs) { 5563 latlngs = latlngs || this._defaultShape(); 5564 latlng = toLatLng(latlng); 5565 latlngs.push(latlng); 5566 this._bounds.extend(latlng); 5567 return this.redraw(); 5568 }, 5569 _setLatLngs: function(latlngs) { 5570 this._bounds = new LatLngBounds(); 5571 this._latlngs = this._convertLatLngs(latlngs); 5572 }, 5573 _defaultShape: function() { 5574 return isFlat(this._latlngs) ? this._latlngs : this._latlngs[0]; 5575 }, 5576 // recursively convert latlngs input into actual LatLng instances; calculate bounds along the way 5577 _convertLatLngs: function(latlngs) { 5578 var result = [], flat = isFlat(latlngs); 5579 for (var i = 0, len = latlngs.length; i < len; i++) { 5580 if (flat) { 5581 result[i] = toLatLng(latlngs[i]); 5582 this._bounds.extend(result[i]); 5583 } else { 5584 result[i] = this._convertLatLngs(latlngs[i]); 5585 } 5586 } 5587 return result; 5588 }, 5589 _project: function() { 5590 var pxBounds = new Bounds(); 5591 this._rings = []; 5592 this._projectLatlngs(this._latlngs, this._rings, pxBounds); 5593 if (this._bounds.isValid() && pxBounds.isValid()) { 5594 this._rawPxBounds = pxBounds; 5595 this._updateBounds(); 5596 } 5597 }, 5598 _updateBounds: function() { 5599 var w = this._clickTolerance(), p = new Point(w, w); 5600 if (!this._rawPxBounds) { 5601 return; 5602 } 5603 this._pxBounds = new Bounds([ 5604 this._rawPxBounds.min.subtract(p), 5605 this._rawPxBounds.max.add(p) 5606 ]); 5607 }, 5608 // recursively turns latlngs into a set of rings with projected coordinates 5609 _projectLatlngs: function(latlngs, result, projectedBounds) { 5610 var flat = latlngs[0] instanceof LatLng, len = latlngs.length, i, ring; 5611 if (flat) { 5612 ring = []; 5613 for (i = 0; i < len; i++) { 5614 ring[i] = this._map.latLngToLayerPoint(latlngs[i]); 5615 projectedBounds.extend(ring[i]); 5616 } 5617 result.push(ring); 5618 } else { 5619 for (i = 0; i < len; i++) { 5620 this._projectLatlngs(latlngs[i], result, projectedBounds); 5621 } 5622 } 5623 }, 5624 // clip polyline by renderer bounds so that we have less to render for performance 5625 _clipPoints: function() { 5626 var bounds = this._renderer._bounds; 5627 this._parts = []; 5628 if (!this._pxBounds || !this._pxBounds.intersects(bounds)) { 5629 return; 5630 } 5631 if (this.options.noClip) { 5632 this._parts = this._rings; 5633 return; 5634 } 5635 var parts = this._parts, i, j, k, len, len2, segment, points; 5636 for (i = 0, k = 0, len = this._rings.length; i < len; i++) { 5637 points = this._rings[i]; 5638 for (j = 0, len2 = points.length; j < len2 - 1; j++) { 5639 segment = clipSegment(points[j], points[j + 1], bounds, j, true); 5640 if (!segment) { 5641 continue; 5642 } 5643 parts[k] = parts[k] || []; 5644 parts[k].push(segment[0]); 5645 if (segment[1] !== points[j + 1] || j === len2 - 2) { 5646 parts[k].push(segment[1]); 5647 k++; 5648 } 5649 } 5650 } 5651 }, 5652 // simplify each clipped part of the polyline for performance 5653 _simplifyPoints: function() { 5654 var parts = this._parts, tolerance = this.options.smoothFactor; 5655 for (var i = 0, len = parts.length; i < len; i++) { 5656 parts[i] = simplify(parts[i], tolerance); 5657 } 5658 }, 5659 _update: function() { 5660 if (!this._map) { 5661 return; 5662 } 5663 this._clipPoints(); 5664 this._simplifyPoints(); 5665 this._updatePath(); 5666 }, 5667 _updatePath: function() { 5668 this._renderer._updatePoly(this); 5669 }, 5670 // Needed by the `Canvas` renderer for interactivity 5671 _containsPoint: function(p, closed) { 5672 var i, j, k, len, len2, part, w = this._clickTolerance(); 5673 if (!this._pxBounds || !this._pxBounds.contains(p)) { 5674 return false; 5675 } 5676 for (i = 0, len = this._parts.length; i < len; i++) { 5677 part = this._parts[i]; 5678 for (j = 0, len2 = part.length, k = len2 - 1; j < len2; k = j++) { 5679 if (!closed && j === 0) { 5680 continue; 5681 } 5682 if (pointToSegmentDistance(p, part[k], part[j]) <= w) { 5683 return true; 5684 } 5685 } 5686 } 5687 return false; 5688 } 5689 }); 5690 function polyline(latlngs, options) { 5691 return new Polyline(latlngs, options); 5692 } 5693 Polyline._flat = _flat; 5694 var Polygon = Polyline.extend({ 5695 options: { 5696 fill: true 5697 }, 5698 isEmpty: function() { 5699 return !this._latlngs.length || !this._latlngs[0].length; 5700 }, 5701 // @method getCenter(): LatLng 5702 // Returns the center ([centroid](http://en.wikipedia.org/wiki/Centroid)) of the Polygon. 5703 getCenter: function() { 5704 if (!this._map) { 5705 throw new Error("Must add layer to map before using getCenter()"); 5706 } 5707 return polygonCenter(this._defaultShape(), this._map.options.crs); 5708 }, 5709 _convertLatLngs: function(latlngs) { 5710 var result = Polyline.prototype._convertLatLngs.call(this, latlngs), len = result.length; 5711 if (len >= 2 && result[0] instanceof LatLng && result[0].equals(result[len - 1])) { 5712 result.pop(); 5713 } 5714 return result; 5715 }, 5716 _setLatLngs: function(latlngs) { 5717 Polyline.prototype._setLatLngs.call(this, latlngs); 5718 if (isFlat(this._latlngs)) { 5719 this._latlngs = [this._latlngs]; 5720 } 5721 }, 5722 _defaultShape: function() { 5723 return isFlat(this._latlngs[0]) ? this._latlngs[0] : this._latlngs[0][0]; 5724 }, 5725 _clipPoints: function() { 5726 var bounds = this._renderer._bounds, w = this.options.weight, p = new Point(w, w); 5727 bounds = new Bounds(bounds.min.subtract(p), bounds.max.add(p)); 5728 this._parts = []; 5729 if (!this._pxBounds || !this._pxBounds.intersects(bounds)) { 5730 return; 5731 } 5732 if (this.options.noClip) { 5733 this._parts = this._rings; 5734 return; 5735 } 5736 for (var i = 0, len = this._rings.length, clipped; i < len; i++) { 5737 clipped = clipPolygon(this._rings[i], bounds, true); 5738 if (clipped.length) { 5739 this._parts.push(clipped); 5740 } 5741 } 5742 }, 5743 _updatePath: function() { 5744 this._renderer._updatePoly(this, true); 5745 }, 5746 // Needed by the `Canvas` renderer for interactivity 5747 _containsPoint: function(p) { 5748 var inside = false, part, p1, p2, i, j, k, len, len2; 5749 if (!this._pxBounds || !this._pxBounds.contains(p)) { 5750 return false; 5751 } 5752 for (i = 0, len = this._parts.length; i < len; i++) { 5753 part = this._parts[i]; 5754 for (j = 0, len2 = part.length, k = len2 - 1; j < len2; k = j++) { 5755 p1 = part[j]; 5756 p2 = part[k]; 5757 if (p1.y > p.y !== p2.y > p.y && p.x < (p2.x - p1.x) * (p.y - p1.y) / (p2.y - p1.y) + p1.x) { 5758 inside = !inside; 5759 } 5760 } 5761 } 5762 return inside || Polyline.prototype._containsPoint.call(this, p, true); 5763 } 5764 }); 5765 function polygon(latlngs, options) { 5766 return new Polygon(latlngs, options); 5767 } 5768 var GeoJSON = FeatureGroup.extend({ 5769 /* @section 5770 * @aka GeoJSON options 5771 * 5772 * @option pointToLayer: Function = * 5773 * A `Function` defining how GeoJSON points spawn Leaflet layers. It is internally 5774 * called when data is added, passing the GeoJSON point feature and its `LatLng`. 5775 * The default is to spawn a default `Marker`: 5776 * ```js 5777 * function(geoJsonPoint, latlng) { 5778 * return L.marker(latlng); 5779 * } 5780 * ``` 5781 * 5782 * @option style: Function = * 5783 * A `Function` defining the `Path options` for styling GeoJSON lines and polygons, 5784 * called internally when data is added. 5785 * The default value is to not override any defaults: 5786 * ```js 5787 * function (geoJsonFeature) { 5788 * return {} 5789 * } 5790 * ``` 5791 * 5792 * @option onEachFeature: Function = * 5793 * A `Function` that will be called once for each created `Feature`, after it has 5794 * been created and styled. Useful for attaching events and popups to features. 5795 * The default is to do nothing with the newly created layers: 5796 * ```js 5797 * function (feature, layer) {} 5798 * ``` 5799 * 5800 * @option filter: Function = * 5801 * A `Function` that will be used to decide whether to include a feature or not. 5802 * The default is to include all features: 5803 * ```js 5804 * function (geoJsonFeature) { 5805 * return true; 5806 * } 5807 * ``` 5808 * Note: dynamically changing the `filter` option will have effect only on newly 5809 * added data. It will _not_ re-evaluate already included features. 5810 * 5811 * @option coordsToLatLng: Function = * 5812 * A `Function` that will be used for converting GeoJSON coordinates to `LatLng`s. 5813 * The default is the `coordsToLatLng` static method. 5814 * 5815 * @option markersInheritOptions: Boolean = false 5816 * Whether default Markers for "Point" type Features inherit from group options. 5817 */ 5818 initialize: function(geojson, options) { 5819 setOptions(this, options); 5820 this._layers = {}; 5821 if (geojson) { 5822 this.addData(geojson); 5823 } 5824 }, 5825 // @method addData( <GeoJSON> data ): this 5826 // Adds a GeoJSON object to the layer. 5827 addData: function(geojson) { 5828 var features = isArray(geojson) ? geojson : geojson.features, i, len, feature; 5829 if (features) { 5830 for (i = 0, len = features.length; i < len; i++) { 5831 feature = features[i]; 5832 if (feature.geometries || feature.geometry || feature.features || feature.coordinates) { 5833 this.addData(feature); 5834 } 5835 } 5836 return this; 5837 } 5838 var options = this.options; 5839 if (options.filter && !options.filter(geojson)) { 5840 return this; 5841 } 5842 var layer = geometryToLayer(geojson, options); 5843 if (!layer) { 5844 return this; 5845 } 5846 layer.feature = asFeature(geojson); 5847 layer.defaultOptions = layer.options; 5848 this.resetStyle(layer); 5849 if (options.onEachFeature) { 5850 options.onEachFeature(geojson, layer); 5851 } 5852 return this.addLayer(layer); 5853 }, 5854 // @method resetStyle( <Path> layer? ): this 5855 // Resets the given vector layer's style to the original GeoJSON style, useful for resetting style after hover events. 5856 // If `layer` is omitted, the style of all features in the current layer is reset. 5857 resetStyle: function(layer) { 5858 if (layer === void 0) { 5859 return this.eachLayer(this.resetStyle, this); 5860 } 5861 layer.options = extend({}, layer.defaultOptions); 5862 this._setLayerStyle(layer, this.options.style); 5863 return this; 5864 }, 5865 // @method setStyle( <Function> style ): this 5866 // Changes styles of GeoJSON vector layers with the given style function. 5867 setStyle: function(style2) { 5868 return this.eachLayer(function(layer) { 5869 this._setLayerStyle(layer, style2); 5870 }, this); 5871 }, 5872 _setLayerStyle: function(layer, style2) { 5873 if (layer.setStyle) { 5874 if (typeof style2 === "function") { 5875 style2 = style2(layer.feature); 5876 } 5877 layer.setStyle(style2); 5878 } 5879 } 5880 }); 5881 function geometryToLayer(geojson, options) { 5882 var geometry = geojson.type === "Feature" ? geojson.geometry : geojson, coords = geometry ? geometry.coordinates : null, layers2 = [], pointToLayer = options && options.pointToLayer, _coordsToLatLng = options && options.coordsToLatLng || coordsToLatLng, latlng, latlngs, i, len; 5883 if (!coords && !geometry) { 5884 return null; 5885 } 5886 switch (geometry.type) { 5887 case "Point": 5888 latlng = _coordsToLatLng(coords); 5889 return _pointToLayer(pointToLayer, geojson, latlng, options); 5890 case "MultiPoint": 5891 for (i = 0, len = coords.length; i < len; i++) { 5892 latlng = _coordsToLatLng(coords[i]); 5893 layers2.push(_pointToLayer(pointToLayer, geojson, latlng, options)); 5894 } 5895 return new FeatureGroup(layers2); 5896 case "LineString": 5897 case "MultiLineString": 5898 latlngs = coordsToLatLngs(coords, geometry.type === "LineString" ? 0 : 1, _coordsToLatLng); 5899 return new Polyline(latlngs, options); 5900 case "Polygon": 5901 case "MultiPolygon": 5902 latlngs = coordsToLatLngs(coords, geometry.type === "Polygon" ? 1 : 2, _coordsToLatLng); 5903 return new Polygon(latlngs, options); 5904 case "GeometryCollection": 5905 for (i = 0, len = geometry.geometries.length; i < len; i++) { 5906 var geoLayer = geometryToLayer({ 5907 geometry: geometry.geometries[i], 5908 type: "Feature", 5909 properties: geojson.properties 5910 }, options); 5911 if (geoLayer) { 5912 layers2.push(geoLayer); 5913 } 5914 } 5915 return new FeatureGroup(layers2); 5916 case "FeatureCollection": 5917 for (i = 0, len = geometry.features.length; i < len; i++) { 5918 var featureLayer = geometryToLayer(geometry.features[i], options); 5919 if (featureLayer) { 5920 layers2.push(featureLayer); 5921 } 5922 } 5923 return new FeatureGroup(layers2); 5924 default: 5925 throw new Error("Invalid GeoJSON object."); 5926 } 5927 } 5928 function _pointToLayer(pointToLayerFn, geojson, latlng, options) { 5929 return pointToLayerFn ? pointToLayerFn(geojson, latlng) : new Marker(latlng, options && options.markersInheritOptions && options); 5930 } 5931 function coordsToLatLng(coords) { 5932 return new LatLng(coords[1], coords[0], coords[2]); 5933 } 5934 function coordsToLatLngs(coords, levelsDeep, _coordsToLatLng) { 5935 var latlngs = []; 5936 for (var i = 0, len = coords.length, latlng; i < len; i++) { 5937 latlng = levelsDeep ? coordsToLatLngs(coords[i], levelsDeep - 1, _coordsToLatLng) : (_coordsToLatLng || coordsToLatLng)(coords[i]); 5938 latlngs.push(latlng); 5939 } 5940 return latlngs; 5941 } 5942 function latLngToCoords(latlng, precision) { 5943 latlng = toLatLng(latlng); 5944 return latlng.alt !== void 0 ? [formatNum(latlng.lng, precision), formatNum(latlng.lat, precision), formatNum(latlng.alt, precision)] : [formatNum(latlng.lng, precision), formatNum(latlng.lat, precision)]; 5945 } 5946 function latLngsToCoords(latlngs, levelsDeep, closed, precision) { 5947 var coords = []; 5948 for (var i = 0, len = latlngs.length; i < len; i++) { 5949 coords.push(levelsDeep ? latLngsToCoords(latlngs[i], isFlat(latlngs[i]) ? 0 : levelsDeep - 1, closed, precision) : latLngToCoords(latlngs[i], precision)); 5950 } 5951 if (!levelsDeep && closed && coords.length > 0) { 5952 coords.push(coords[0].slice()); 5953 } 5954 return coords; 5955 } 5956 function getFeature(layer, newGeometry) { 5957 return layer.feature ? extend({}, layer.feature, { geometry: newGeometry }) : asFeature(newGeometry); 5958 } 5959 function asFeature(geojson) { 5960 if (geojson.type === "Feature" || geojson.type === "FeatureCollection") { 5961 return geojson; 5962 } 5963 return { 5964 type: "Feature", 5965 properties: {}, 5966 geometry: geojson 5967 }; 5968 } 5969 var PointToGeoJSON = { 5970 toGeoJSON: function(precision) { 5971 return getFeature(this, { 5972 type: "Point", 5973 coordinates: latLngToCoords(this.getLatLng(), precision) 5974 }); 5975 } 5976 }; 5977 Marker.include(PointToGeoJSON); 5978 Circle.include(PointToGeoJSON); 5979 CircleMarker.include(PointToGeoJSON); 5980 Polyline.include({ 5981 toGeoJSON: function(precision) { 5982 var multi = !isFlat(this._latlngs); 5983 var coords = latLngsToCoords(this._latlngs, multi ? 1 : 0, false, precision); 5984 return getFeature(this, { 5985 type: (multi ? "Multi" : "") + "LineString", 5986 coordinates: coords 5987 }); 5988 } 5989 }); 5990 Polygon.include({ 5991 toGeoJSON: function(precision) { 5992 var holes = !isFlat(this._latlngs), multi = holes && !isFlat(this._latlngs[0]); 5993 var coords = latLngsToCoords(this._latlngs, multi ? 2 : holes ? 1 : 0, true, precision); 5994 if (!holes) { 5995 coords = [coords]; 5996 } 5997 return getFeature(this, { 5998 type: (multi ? "Multi" : "") + "Polygon", 5999 coordinates: coords 6000 }); 6001 } 6002 }); 6003 LayerGroup.include({ 6004 toMultiPoint: function(precision) { 6005 var coords = []; 6006 this.eachLayer(function(layer) { 6007 coords.push(layer.toGeoJSON(precision).geometry.coordinates); 6008 }); 6009 return getFeature(this, { 6010 type: "MultiPoint", 6011 coordinates: coords 6012 }); 6013 }, 6014 // @method toGeoJSON(precision?: Number|false): Object 6015 // Coordinates values are rounded with [`formatNum`](#util-formatnum) function with given `precision`. 6016 // Returns a [`GeoJSON`](https://en.wikipedia.org/wiki/GeoJSON) representation of the layer group (as a GeoJSON `FeatureCollection`, `GeometryCollection`, or `MultiPoint`). 6017 toGeoJSON: function(precision) { 6018 var type = this.feature && this.feature.geometry && this.feature.geometry.type; 6019 if (type === "MultiPoint") { 6020 return this.toMultiPoint(precision); 6021 } 6022 var isGeometryCollection = type === "GeometryCollection", jsons = []; 6023 this.eachLayer(function(layer) { 6024 if (layer.toGeoJSON) { 6025 var json = layer.toGeoJSON(precision); 6026 if (isGeometryCollection) { 6027 jsons.push(json.geometry); 6028 } else { 6029 var feature = asFeature(json); 6030 if (feature.type === "FeatureCollection") { 6031 jsons.push.apply(jsons, feature.features); 6032 } else { 6033 jsons.push(feature); 6034 } 6035 } 6036 } 6037 }); 6038 if (isGeometryCollection) { 6039 return getFeature(this, { 6040 geometries: jsons, 6041 type: "GeometryCollection" 6042 }); 6043 } 6044 return { 6045 type: "FeatureCollection", 6046 features: jsons 6047 }; 6048 } 6049 }); 6050 function geoJSON(geojson, options) { 6051 return new GeoJSON(geojson, options); 6052 } 6053 var geoJson = geoJSON; 6054 var ImageOverlay = Layer.extend({ 6055 // @section 6056 // @aka ImageOverlay options 6057 options: { 6058 // @option opacity: Number = 1.0 6059 // The opacity of the image overlay. 6060 opacity: 1, 6061 // @option alt: String = '' 6062 // Text for the `alt` attribute of the image (useful for accessibility). 6063 alt: "", 6064 // @option interactive: Boolean = false 6065 // If `true`, the image overlay will emit [mouse events](#interactive-layer) when clicked or hovered. 6066 interactive: false, 6067 // @option crossOrigin: Boolean|String = false 6068 // Whether the crossOrigin attribute will be added to the image. 6069 // If a String is provided, the image will have its crossOrigin attribute set to the String provided. This is needed if you want to access image pixel data. 6070 // Refer to [CORS Settings](https://developer.mozilla.org/en-US/docs/Web/HTML/CORS_settings_attributes) for valid String values. 6071 crossOrigin: false, 6072 // @option errorOverlayUrl: String = '' 6073 // URL to the overlay image to show in place of the overlay that failed to load. 6074 errorOverlayUrl: "", 6075 // @option zIndex: Number = 1 6076 // The explicit [zIndex](https://developer.mozilla.org/docs/Web/CSS/CSS_Positioning/Understanding_z_index) of the overlay layer. 6077 zIndex: 1, 6078 // @option className: String = '' 6079 // A custom class name to assign to the image. Empty by default. 6080 className: "" 6081 }, 6082 initialize: function(url, bounds, options) { 6083 this._url = url; 6084 this._bounds = toLatLngBounds(bounds); 6085 setOptions(this, options); 6086 }, 6087 onAdd: function() { 6088 if (!this._image) { 6089 this._initImage(); 6090 if (this.options.opacity < 1) { 6091 this._updateOpacity(); 6092 } 6093 } 6094 if (this.options.interactive) { 6095 addClass(this._image, "leaflet-interactive"); 6096 this.addInteractiveTarget(this._image); 6097 } 6098 this.getPane().appendChild(this._image); 6099 this._reset(); 6100 }, 6101 onRemove: function() { 6102 remove(this._image); 6103 if (this.options.interactive) { 6104 this.removeInteractiveTarget(this._image); 6105 } 6106 }, 6107 // @method setOpacity(opacity: Number): this 6108 // Sets the opacity of the overlay. 6109 setOpacity: function(opacity) { 6110 this.options.opacity = opacity; 6111 if (this._image) { 6112 this._updateOpacity(); 6113 } 6114 return this; 6115 }, 6116 setStyle: function(styleOpts) { 6117 if (styleOpts.opacity) { 6118 this.setOpacity(styleOpts.opacity); 6119 } 6120 return this; 6121 }, 6122 // @method bringToFront(): this 6123 // Brings the layer to the top of all overlays. 6124 bringToFront: function() { 6125 if (this._map) { 6126 toFront(this._image); 6127 } 6128 return this; 6129 }, 6130 // @method bringToBack(): this 6131 // Brings the layer to the bottom of all overlays. 6132 bringToBack: function() { 6133 if (this._map) { 6134 toBack(this._image); 6135 } 6136 return this; 6137 }, 6138 // @method setUrl(url: String): this 6139 // Changes the URL of the image. 6140 setUrl: function(url) { 6141 this._url = url; 6142 if (this._image) { 6143 this._image.src = url; 6144 } 6145 return this; 6146 }, 6147 // @method setBounds(bounds: LatLngBounds): this 6148 // Update the bounds that this ImageOverlay covers 6149 setBounds: function(bounds) { 6150 this._bounds = toLatLngBounds(bounds); 6151 if (this._map) { 6152 this._reset(); 6153 } 6154 return this; 6155 }, 6156 getEvents: function() { 6157 var events = { 6158 zoom: this._reset, 6159 viewreset: this._reset 6160 }; 6161 if (this._zoomAnimated) { 6162 events.zoomanim = this._animateZoom; 6163 } 6164 return events; 6165 }, 6166 // @method setZIndex(value: Number): this 6167 // Changes the [zIndex](#imageoverlay-zindex) of the image overlay. 6168 setZIndex: function(value) { 6169 this.options.zIndex = value; 6170 this._updateZIndex(); 6171 return this; 6172 }, 6173 // @method getBounds(): LatLngBounds 6174 // Get the bounds that this ImageOverlay covers 6175 getBounds: function() { 6176 return this._bounds; 6177 }, 6178 // @method getElement(): HTMLElement 6179 // Returns the instance of [`HTMLImageElement`](https://developer.mozilla.org/docs/Web/API/HTMLImageElement) 6180 // used by this overlay. 6181 getElement: function() { 6182 return this._image; 6183 }, 6184 _initImage: function() { 6185 var wasElementSupplied = this._url.tagName === "IMG"; 6186 var img = this._image = wasElementSupplied ? this._url : create$1("img"); 6187 addClass(img, "leaflet-image-layer"); 6188 if (this._zoomAnimated) { 6189 addClass(img, "leaflet-zoom-animated"); 6190 } 6191 if (this.options.className) { 6192 addClass(img, this.options.className); 6193 } 6194 img.onselectstart = falseFn; 6195 img.onmousemove = falseFn; 6196 img.onload = bind(this.fire, this, "load"); 6197 img.onerror = bind(this._overlayOnError, this, "error"); 6198 if (this.options.crossOrigin || this.options.crossOrigin === "") { 6199 img.crossOrigin = this.options.crossOrigin === true ? "" : this.options.crossOrigin; 6200 } 6201 if (this.options.zIndex) { 6202 this._updateZIndex(); 6203 } 6204 if (wasElementSupplied) { 6205 this._url = img.src; 6206 return; 6207 } 6208 img.src = this._url; 6209 img.alt = this.options.alt; 6210 }, 6211 _animateZoom: function(e) { 6212 var scale2 = this._map.getZoomScale(e.zoom), offset = this._map._latLngBoundsToNewLayerBounds(this._bounds, e.zoom, e.center).min; 6213 setTransform(this._image, offset, scale2); 6214 }, 6215 _reset: function() { 6216 var image = this._image, bounds = new Bounds( 6217 this._map.latLngToLayerPoint(this._bounds.getNorthWest()), 6218 this._map.latLngToLayerPoint(this._bounds.getSouthEast()) 6219 ), size = bounds.getSize(); 6220 setPosition(image, bounds.min); 6221 image.style.width = size.x + "px"; 6222 image.style.height = size.y + "px"; 6223 }, 6224 _updateOpacity: function() { 6225 setOpacity(this._image, this.options.opacity); 6226 }, 6227 _updateZIndex: function() { 6228 if (this._image && this.options.zIndex !== void 0 && this.options.zIndex !== null) { 6229 this._image.style.zIndex = this.options.zIndex; 6230 } 6231 }, 6232 _overlayOnError: function() { 6233 this.fire("error"); 6234 var errorUrl = this.options.errorOverlayUrl; 6235 if (errorUrl && this._url !== errorUrl) { 6236 this._url = errorUrl; 6237 this._image.src = errorUrl; 6238 } 6239 }, 6240 // @method getCenter(): LatLng 6241 // Returns the center of the ImageOverlay. 6242 getCenter: function() { 6243 return this._bounds.getCenter(); 6244 } 6245 }); 6246 var imageOverlay = function(url, bounds, options) { 6247 return new ImageOverlay(url, bounds, options); 6248 }; 6249 var VideoOverlay = ImageOverlay.extend({ 6250 // @section 6251 // @aka VideoOverlay options 6252 options: { 6253 // @option autoplay: Boolean = true 6254 // Whether the video starts playing automatically when loaded. 6255 // On some browsers autoplay will only work with `muted: true` 6256 autoplay: true, 6257 // @option loop: Boolean = true 6258 // Whether the video will loop back to the beginning when played. 6259 loop: true, 6260 // @option keepAspectRatio: Boolean = true 6261 // Whether the video will save aspect ratio after the projection. 6262 // Relevant for supported browsers. See [browser compatibility](https://developer.mozilla.org/en-US/docs/Web/CSS/object-fit) 6263 keepAspectRatio: true, 6264 // @option muted: Boolean = false 6265 // Whether the video starts on mute when loaded. 6266 muted: false, 6267 // @option playsInline: Boolean = true 6268 // Mobile browsers will play the video right where it is instead of open it up in fullscreen mode. 6269 playsInline: true 6270 }, 6271 _initImage: function() { 6272 var wasElementSupplied = this._url.tagName === "VIDEO"; 6273 var vid = this._image = wasElementSupplied ? this._url : create$1("video"); 6274 addClass(vid, "leaflet-image-layer"); 6275 if (this._zoomAnimated) { 6276 addClass(vid, "leaflet-zoom-animated"); 6277 } 6278 if (this.options.className) { 6279 addClass(vid, this.options.className); 6280 } 6281 vid.onselectstart = falseFn; 6282 vid.onmousemove = falseFn; 6283 vid.onloadeddata = bind(this.fire, this, "load"); 6284 if (wasElementSupplied) { 6285 var sourceElements = vid.getElementsByTagName("source"); 6286 var sources = []; 6287 for (var j = 0; j < sourceElements.length; j++) { 6288 sources.push(sourceElements[j].src); 6289 } 6290 this._url = sourceElements.length > 0 ? sources : [vid.src]; 6291 return; 6292 } 6293 if (!isArray(this._url)) { 6294 this._url = [this._url]; 6295 } 6296 if (!this.options.keepAspectRatio && Object.prototype.hasOwnProperty.call(vid.style, "objectFit")) { 6297 vid.style["objectFit"] = "fill"; 6298 } 6299 vid.autoplay = !!this.options.autoplay; 6300 vid.loop = !!this.options.loop; 6301 vid.muted = !!this.options.muted; 6302 vid.playsInline = !!this.options.playsInline; 6303 for (var i = 0; i < this._url.length; i++) { 6304 var source = create$1("source"); 6305 source.src = this._url[i]; 6306 vid.appendChild(source); 6307 } 6308 } 6309 // @method getElement(): HTMLVideoElement 6310 // Returns the instance of [`HTMLVideoElement`](https://developer.mozilla.org/docs/Web/API/HTMLVideoElement) 6311 // used by this overlay. 6312 }); 6313 function videoOverlay(video, bounds, options) { 6314 return new VideoOverlay(video, bounds, options); 6315 } 6316 var SVGOverlay = ImageOverlay.extend({ 6317 _initImage: function() { 6318 var el = this._image = this._url; 6319 addClass(el, "leaflet-image-layer"); 6320 if (this._zoomAnimated) { 6321 addClass(el, "leaflet-zoom-animated"); 6322 } 6323 if (this.options.className) { 6324 addClass(el, this.options.className); 6325 } 6326 el.onselectstart = falseFn; 6327 el.onmousemove = falseFn; 6328 } 6329 // @method getElement(): SVGElement 6330 // Returns the instance of [`SVGElement`](https://developer.mozilla.org/docs/Web/API/SVGElement) 6331 // used by this overlay. 6332 }); 6333 function svgOverlay(el, bounds, options) { 6334 return new SVGOverlay(el, bounds, options); 6335 } 6336 var DivOverlay = Layer.extend({ 6337 // @section 6338 // @aka DivOverlay options 6339 options: { 6340 // @option interactive: Boolean = false 6341 // If true, the popup/tooltip will listen to the mouse events. 6342 interactive: false, 6343 // @option offset: Point = Point(0, 0) 6344 // The offset of the overlay position. 6345 offset: [0, 0], 6346 // @option className: String = '' 6347 // A custom CSS class name to assign to the overlay. 6348 className: "", 6349 // @option pane: String = undefined 6350 // `Map pane` where the overlay will be added. 6351 pane: void 0, 6352 // @option content: String|HTMLElement|Function = '' 6353 // Sets the HTML content of the overlay while initializing. If a function is passed the source layer will be 6354 // passed to the function. The function should return a `String` or `HTMLElement` to be used in the overlay. 6355 content: "" 6356 }, 6357 initialize: function(options, source) { 6358 if (options && (options instanceof LatLng || isArray(options))) { 6359 this._latlng = toLatLng(options); 6360 setOptions(this, source); 6361 } else { 6362 setOptions(this, options); 6363 this._source = source; 6364 } 6365 if (this.options.content) { 6366 this._content = this.options.content; 6367 } 6368 }, 6369 // @method openOn(map: Map): this 6370 // Adds the overlay to the map. 6371 // Alternative to `map.openPopup(popup)`/`.openTooltip(tooltip)`. 6372 openOn: function(map2) { 6373 map2 = arguments.length ? map2 : this._source._map; 6374 if (!map2.hasLayer(this)) { 6375 map2.addLayer(this); 6376 } 6377 return this; 6378 }, 6379 // @method close(): this 6380 // Closes the overlay. 6381 // Alternative to `map.closePopup(popup)`/`.closeTooltip(tooltip)` 6382 // and `layer.closePopup()`/`.closeTooltip()`. 6383 close: function() { 6384 if (this._map) { 6385 this._map.removeLayer(this); 6386 } 6387 return this; 6388 }, 6389 // @method toggle(layer?: Layer): this 6390 // Opens or closes the overlay bound to layer depending on its current state. 6391 // Argument may be omitted only for overlay bound to layer. 6392 // Alternative to `layer.togglePopup()`/`.toggleTooltip()`. 6393 toggle: function(layer) { 6394 if (this._map) { 6395 this.close(); 6396 } else { 6397 if (arguments.length) { 6398 this._source = layer; 6399 } else { 6400 layer = this._source; 6401 } 6402 this._prepareOpen(); 6403 this.openOn(layer._map); 6404 } 6405 return this; 6406 }, 6407 onAdd: function(map2) { 6408 this._zoomAnimated = map2._zoomAnimated; 6409 if (!this._container) { 6410 this._initLayout(); 6411 } 6412 if (map2._fadeAnimated) { 6413 setOpacity(this._container, 0); 6414 } 6415 clearTimeout(this._removeTimeout); 6416 this.getPane().appendChild(this._container); 6417 this.update(); 6418 if (map2._fadeAnimated) { 6419 setOpacity(this._container, 1); 6420 } 6421 this.bringToFront(); 6422 if (this.options.interactive) { 6423 addClass(this._container, "leaflet-interactive"); 6424 this.addInteractiveTarget(this._container); 6425 } 6426 }, 6427 onRemove: function(map2) { 6428 if (map2._fadeAnimated) { 6429 setOpacity(this._container, 0); 6430 this._removeTimeout = setTimeout(bind(remove, void 0, this._container), 200); 6431 } else { 6432 remove(this._container); 6433 } 6434 if (this.options.interactive) { 6435 removeClass(this._container, "leaflet-interactive"); 6436 this.removeInteractiveTarget(this._container); 6437 } 6438 }, 6439 // @namespace DivOverlay 6440 // @method getLatLng: LatLng 6441 // Returns the geographical point of the overlay. 6442 getLatLng: function() { 6443 return this._latlng; 6444 }, 6445 // @method setLatLng(latlng: LatLng): this 6446 // Sets the geographical point where the overlay will open. 6447 setLatLng: function(latlng) { 6448 this._latlng = toLatLng(latlng); 6449 if (this._map) { 6450 this._updatePosition(); 6451 this._adjustPan(); 6452 } 6453 return this; 6454 }, 6455 // @method getContent: String|HTMLElement 6456 // Returns the content of the overlay. 6457 getContent: function() { 6458 return this._content; 6459 }, 6460 // @method setContent(htmlContent: String|HTMLElement|Function): this 6461 // Sets the HTML content of the overlay. If a function is passed the source layer will be passed to the function. 6462 // The function should return a `String` or `HTMLElement` to be used in the overlay. 6463 setContent: function(content) { 6464 this._content = content; 6465 this.update(); 6466 return this; 6467 }, 6468 // @method getElement: String|HTMLElement 6469 // Returns the HTML container of the overlay. 6470 getElement: function() { 6471 return this._container; 6472 }, 6473 // @method update: null 6474 // Updates the overlay content, layout and position. Useful for updating the overlay after something inside changed, e.g. image loaded. 6475 update: function() { 6476 if (!this._map) { 6477 return; 6478 } 6479 this._container.style.visibility = "hidden"; 6480 this._updateContent(); 6481 this._updateLayout(); 6482 this._updatePosition(); 6483 this._container.style.visibility = ""; 6484 this._adjustPan(); 6485 }, 6486 getEvents: function() { 6487 var events = { 6488 zoom: this._updatePosition, 6489 viewreset: this._updatePosition 6490 }; 6491 if (this._zoomAnimated) { 6492 events.zoomanim = this._animateZoom; 6493 } 6494 return events; 6495 }, 6496 // @method isOpen: Boolean 6497 // Returns `true` when the overlay is visible on the map. 6498 isOpen: function() { 6499 return !!this._map && this._map.hasLayer(this); 6500 }, 6501 // @method bringToFront: this 6502 // Brings this overlay in front of other overlays (in the same map pane). 6503 bringToFront: function() { 6504 if (this._map) { 6505 toFront(this._container); 6506 } 6507 return this; 6508 }, 6509 // @method bringToBack: this 6510 // Brings this overlay to the back of other overlays (in the same map pane). 6511 bringToBack: function() { 6512 if (this._map) { 6513 toBack(this._container); 6514 } 6515 return this; 6516 }, 6517 // prepare bound overlay to open: update latlng pos / content source (for FeatureGroup) 6518 _prepareOpen: function(latlng) { 6519 var source = this._source; 6520 if (!source._map) { 6521 return false; 6522 } 6523 if (source instanceof FeatureGroup) { 6524 source = null; 6525 var layers2 = this._source._layers; 6526 for (var id in layers2) { 6527 if (layers2[id]._map) { 6528 source = layers2[id]; 6529 break; 6530 } 6531 } 6532 if (!source) { 6533 return false; 6534 } 6535 this._source = source; 6536 } 6537 if (!latlng) { 6538 if (source.getCenter) { 6539 latlng = source.getCenter(); 6540 } else if (source.getLatLng) { 6541 latlng = source.getLatLng(); 6542 } else if (source.getBounds) { 6543 latlng = source.getBounds().getCenter(); 6544 } else { 6545 throw new Error("Unable to get source layer LatLng."); 6546 } 6547 } 6548 this.setLatLng(latlng); 6549 if (this._map) { 6550 this.update(); 6551 } 6552 return true; 6553 }, 6554 _updateContent: function() { 6555 if (!this._content) { 6556 return; 6557 } 6558 var node = this._contentNode; 6559 var content = typeof this._content === "function" ? this._content(this._source || this) : this._content; 6560 if (typeof content === "string") { 6561 node.innerHTML = content; 6562 } else { 6563 while (node.hasChildNodes()) { 6564 node.removeChild(node.firstChild); 6565 } 6566 node.appendChild(content); 6567 } 6568 this.fire("contentupdate"); 6569 }, 6570 _updatePosition: function() { 6571 if (!this._map) { 6572 return; 6573 } 6574 var pos = this._map.latLngToLayerPoint(this._latlng), offset = toPoint(this.options.offset), anchor = this._getAnchor(); 6575 if (this._zoomAnimated) { 6576 setPosition(this._container, pos.add(anchor)); 6577 } else { 6578 offset = offset.add(pos).add(anchor); 6579 } 6580 var bottom = this._containerBottom = -offset.y, left = this._containerLeft = -Math.round(this._containerWidth / 2) + offset.x; 6581 this._container.style.bottom = bottom + "px"; 6582 this._container.style.left = left + "px"; 6583 }, 6584 _getAnchor: function() { 6585 return [0, 0]; 6586 } 6587 }); 6588 Map2.include({ 6589 _initOverlay: function(OverlayClass, content, latlng, options) { 6590 var overlay = content; 6591 if (!(overlay instanceof OverlayClass)) { 6592 overlay = new OverlayClass(options).setContent(content); 6593 } 6594 if (latlng) { 6595 overlay.setLatLng(latlng); 6596 } 6597 return overlay; 6598 } 6599 }); 6600 Layer.include({ 6601 _initOverlay: function(OverlayClass, old, content, options) { 6602 var overlay = content; 6603 if (overlay instanceof OverlayClass) { 6604 setOptions(overlay, options); 6605 overlay._source = this; 6606 } else { 6607 overlay = old && !options ? old : new OverlayClass(options, this); 6608 overlay.setContent(content); 6609 } 6610 return overlay; 6611 } 6612 }); 6613 var Popup = DivOverlay.extend({ 6614 // @section 6615 // @aka Popup options 6616 options: { 6617 // @option pane: String = 'popupPane' 6618 // `Map pane` where the popup will be added. 6619 pane: "popupPane", 6620 // @option offset: Point = Point(0, 7) 6621 // The offset of the popup position. 6622 offset: [0, 7], 6623 // @option maxWidth: Number = 300 6624 // Max width of the popup, in pixels. 6625 maxWidth: 300, 6626 // @option minWidth: Number = 50 6627 // Min width of the popup, in pixels. 6628 minWidth: 50, 6629 // @option maxHeight: Number = null 6630 // If set, creates a scrollable container of the given height 6631 // inside a popup if its content exceeds it. 6632 // The scrollable container can be styled using the 6633 // `leaflet-popup-scrolled` CSS class selector. 6634 maxHeight: null, 6635 // @option autoPan: Boolean = true 6636 // Set it to `false` if you don't want the map to do panning animation 6637 // to fit the opened popup. 6638 autoPan: true, 6639 // @option autoPanPaddingTopLeft: Point = null 6640 // The margin between the popup and the top left corner of the map 6641 // view after autopanning was performed. 6642 autoPanPaddingTopLeft: null, 6643 // @option autoPanPaddingBottomRight: Point = null 6644 // The margin between the popup and the bottom right corner of the map 6645 // view after autopanning was performed. 6646 autoPanPaddingBottomRight: null, 6647 // @option autoPanPadding: Point = Point(5, 5) 6648 // Equivalent of setting both top left and bottom right autopan padding to the same value. 6649 autoPanPadding: [5, 5], 6650 // @option keepInView: Boolean = false 6651 // Set it to `true` if you want to prevent users from panning the popup 6652 // off of the screen while it is open. 6653 keepInView: false, 6654 // @option closeButton: Boolean = true 6655 // Controls the presence of a close button in the popup. 6656 closeButton: true, 6657 // @option autoClose: Boolean = true 6658 // Set it to `false` if you want to override the default behavior of 6659 // the popup closing when another popup is opened. 6660 autoClose: true, 6661 // @option closeOnEscapeKey: Boolean = true 6662 // Set it to `false` if you want to override the default behavior of 6663 // the ESC key for closing of the popup. 6664 closeOnEscapeKey: true, 6665 // @option closeOnClick: Boolean = * 6666 // Set it if you want to override the default behavior of the popup closing when user clicks 6667 // on the map. Defaults to the map's [`closePopupOnClick`](#map-closepopuponclick) option. 6668 // @option className: String = '' 6669 // A custom CSS class name to assign to the popup. 6670 className: "" 6671 }, 6672 // @namespace Popup 6673 // @method openOn(map: Map): this 6674 // Alternative to `map.openPopup(popup)`. 6675 // Adds the popup to the map and closes the previous one. 6676 openOn: function(map2) { 6677 map2 = arguments.length ? map2 : this._source._map; 6678 if (!map2.hasLayer(this) && map2._popup && map2._popup.options.autoClose) { 6679 map2.removeLayer(map2._popup); 6680 } 6681 map2._popup = this; 6682 return DivOverlay.prototype.openOn.call(this, map2); 6683 }, 6684 onAdd: function(map2) { 6685 DivOverlay.prototype.onAdd.call(this, map2); 6686 map2.fire("popupopen", { popup: this }); 6687 if (this._source) { 6688 this._source.fire("popupopen", { popup: this }, true); 6689 if (!(this._source instanceof Path)) { 6690 this._source.on("preclick", stopPropagation); 6691 } 6692 } 6693 }, 6694 onRemove: function(map2) { 6695 DivOverlay.prototype.onRemove.call(this, map2); 6696 map2.fire("popupclose", { popup: this }); 6697 if (this._source) { 6698 this._source.fire("popupclose", { popup: this }, true); 6699 if (!(this._source instanceof Path)) { 6700 this._source.off("preclick", stopPropagation); 6701 } 6702 } 6703 }, 6704 getEvents: function() { 6705 var events = DivOverlay.prototype.getEvents.call(this); 6706 if (this.options.closeOnClick !== void 0 ? this.options.closeOnClick : this._map.options.closePopupOnClick) { 6707 events.preclick = this.close; 6708 } 6709 if (this.options.keepInView) { 6710 events.moveend = this._adjustPan; 6711 } 6712 return events; 6713 }, 6714 _initLayout: function() { 6715 var prefix = "leaflet-popup", container = this._container = create$1( 6716 "div", 6717 prefix + " " + (this.options.className || "") + " leaflet-zoom-animated" 6718 ); 6719 var wrapper = this._wrapper = create$1("div", prefix + "-content-wrapper", container); 6720 this._contentNode = create$1("div", prefix + "-content", wrapper); 6721 disableClickPropagation(container); 6722 disableScrollPropagation(this._contentNode); 6723 on(container, "contextmenu", stopPropagation); 6724 this._tipContainer = create$1("div", prefix + "-tip-container", container); 6725 this._tip = create$1("div", prefix + "-tip", this._tipContainer); 6726 if (this.options.closeButton) { 6727 var closeButton = this._closeButton = create$1("a", prefix + "-close-button", container); 6728 closeButton.setAttribute("role", "button"); 6729 closeButton.setAttribute("aria-label", "Close popup"); 6730 closeButton.href = "#close"; 6731 closeButton.innerHTML = '<span aria-hidden="true">&#215;</span>'; 6732 on(closeButton, "click", function(ev) { 6733 preventDefault(ev); 6734 this.close(); 6735 }, this); 6736 } 6737 }, 6738 _updateLayout: function() { 6739 var container = this._contentNode, style2 = container.style; 6740 style2.width = ""; 6741 style2.whiteSpace = "nowrap"; 6742 var width = container.offsetWidth; 6743 width = Math.min(width, this.options.maxWidth); 6744 width = Math.max(width, this.options.minWidth); 6745 style2.width = width + 1 + "px"; 6746 style2.whiteSpace = ""; 6747 style2.height = ""; 6748 var height = container.offsetHeight, maxHeight = this.options.maxHeight, scrolledClass = "leaflet-popup-scrolled"; 6749 if (maxHeight && height > maxHeight) { 6750 style2.height = maxHeight + "px"; 6751 addClass(container, scrolledClass); 6752 } else { 6753 removeClass(container, scrolledClass); 6754 } 6755 this._containerWidth = this._container.offsetWidth; 6756 }, 6757 _animateZoom: function(e) { 6758 var pos = this._map._latLngToNewLayerPoint(this._latlng, e.zoom, e.center), anchor = this._getAnchor(); 6759 setPosition(this._container, pos.add(anchor)); 6760 }, 6761 _adjustPan: function() { 6762 if (!this.options.autoPan) { 6763 return; 6764 } 6765 if (this._map._panAnim) { 6766 this._map._panAnim.stop(); 6767 } 6768 if (this._autopanning) { 6769 this._autopanning = false; 6770 return; 6771 } 6772 var map2 = this._map, marginBottom = parseInt(getStyle(this._container, "marginBottom"), 10) || 0, containerHeight = this._container.offsetHeight + marginBottom, containerWidth = this._containerWidth, layerPos = new Point(this._containerLeft, -containerHeight - this._containerBottom); 6773 layerPos._add(getPosition(this._container)); 6774 var containerPos = map2.layerPointToContainerPoint(layerPos), padding = toPoint(this.options.autoPanPadding), paddingTL = toPoint(this.options.autoPanPaddingTopLeft || padding), paddingBR = toPoint(this.options.autoPanPaddingBottomRight || padding), size = map2.getSize(), dx = 0, dy = 0; 6775 if (containerPos.x + containerWidth + paddingBR.x > size.x) { 6776 dx = containerPos.x + containerWidth - size.x + paddingBR.x; 6777 } 6778 if (containerPos.x - dx - paddingTL.x < 0) { 6779 dx = containerPos.x - paddingTL.x; 6780 } 6781 if (containerPos.y + containerHeight + paddingBR.y > size.y) { 6782 dy = containerPos.y + containerHeight - size.y + paddingBR.y; 6783 } 6784 if (containerPos.y - dy - paddingTL.y < 0) { 6785 dy = containerPos.y - paddingTL.y; 6786 } 6787 if (dx || dy) { 6788 if (this.options.keepInView) { 6789 this._autopanning = true; 6790 } 6791 map2.fire("autopanstart").panBy([dx, dy]); 6792 } 6793 }, 6794 _getAnchor: function() { 6795 return toPoint(this._source && this._source._getPopupAnchor ? this._source._getPopupAnchor() : [0, 0]); 6796 } 6797 }); 6798 var popup = function(options, source) { 6799 return new Popup(options, source); 6800 }; 6801 Map2.mergeOptions({ 6802 closePopupOnClick: true 6803 }); 6804 Map2.include({ 6805 // @method openPopup(popup: Popup): this 6806 // Opens the specified popup while closing the previously opened (to make sure only one is opened at one time for usability). 6807 // @alternative 6808 // @method openPopup(content: String|HTMLElement, latlng: LatLng, options?: Popup options): this 6809 // Creates a popup with the specified content and options and opens it in the given point on a map. 6810 openPopup: function(popup2, latlng, options) { 6811 this._initOverlay(Popup, popup2, latlng, options).openOn(this); 6812 return this; 6813 }, 6814 // @method closePopup(popup?: Popup): this 6815 // Closes the popup previously opened with [openPopup](#map-openpopup) (or the given one). 6816 closePopup: function(popup2) { 6817 popup2 = arguments.length ? popup2 : this._popup; 6818 if (popup2) { 6819 popup2.close(); 6820 } 6821 return this; 6822 } 6823 }); 6824 Layer.include({ 6825 // @method bindPopup(content: String|HTMLElement|Function|Popup, options?: Popup options): this 6826 // Binds a popup to the layer with the passed `content` and sets up the 6827 // necessary event listeners. If a `Function` is passed it will receive 6828 // the layer as the first argument and should return a `String` or `HTMLElement`. 6829 bindPopup: function(content, options) { 6830 this._popup = this._initOverlay(Popup, this._popup, content, options); 6831 if (!this._popupHandlersAdded) { 6832 this.on({ 6833 click: this._openPopup, 6834 keypress: this._onKeyPress, 6835 remove: this.closePopup, 6836 move: this._movePopup 6837 }); 6838 this._popupHandlersAdded = true; 6839 } 6840 return this; 6841 }, 6842 // @method unbindPopup(): this 6843 // Removes the popup previously bound with `bindPopup`. 6844 unbindPopup: function() { 6845 if (this._popup) { 6846 this.off({ 6847 click: this._openPopup, 6848 keypress: this._onKeyPress, 6849 remove: this.closePopup, 6850 move: this._movePopup 6851 }); 6852 this._popupHandlersAdded = false; 6853 this._popup = null; 6854 } 6855 return this; 6856 }, 6857 // @method openPopup(latlng?: LatLng): this 6858 // Opens the bound popup at the specified `latlng` or at the default popup anchor if no `latlng` is passed. 6859 openPopup: function(latlng) { 6860 if (this._popup) { 6861 if (!(this instanceof FeatureGroup)) { 6862 this._popup._source = this; 6863 } 6864 if (this._popup._prepareOpen(latlng || this._latlng)) { 6865 this._popup.openOn(this._map); 6866 } 6867 } 6868 return this; 6869 }, 6870 // @method closePopup(): this 6871 // Closes the popup bound to this layer if it is open. 6872 closePopup: function() { 6873 if (this._popup) { 6874 this._popup.close(); 6875 } 6876 return this; 6877 }, 6878 // @method togglePopup(): this 6879 // Opens or closes the popup bound to this layer depending on its current state. 6880 togglePopup: function() { 6881 if (this._popup) { 6882 this._popup.toggle(this); 6883 } 6884 return this; 6885 }, 6886 // @method isPopupOpen(): boolean 6887 // Returns `true` if the popup bound to this layer is currently open. 6888 isPopupOpen: function() { 6889 return this._popup ? this._popup.isOpen() : false; 6890 }, 6891 // @method setPopupContent(content: String|HTMLElement|Popup): this 6892 // Sets the content of the popup bound to this layer. 6893 setPopupContent: function(content) { 6894 if (this._popup) { 6895 this._popup.setContent(content); 6896 } 6897 return this; 6898 }, 6899 // @method getPopup(): Popup 6900 // Returns the popup bound to this layer. 6901 getPopup: function() { 6902 return this._popup; 6903 }, 6904 _openPopup: function(e) { 6905 if (!this._popup || !this._map) { 6906 return; 6907 } 6908 stop(e); 6909 var target = e.layer || e.target; 6910 if (this._popup._source === target && !(target instanceof Path)) { 6911 if (this._map.hasLayer(this._popup)) { 6912 this.closePopup(); 6913 } else { 6914 this.openPopup(e.latlng); 6915 } 6916 return; 6917 } 6918 this._popup._source = target; 6919 this.openPopup(e.latlng); 6920 }, 6921 _movePopup: function(e) { 6922 this._popup.setLatLng(e.latlng); 6923 }, 6924 _onKeyPress: function(e) { 6925 if (e.originalEvent.keyCode === 13) { 6926 this._openPopup(e); 6927 } 6928 } 6929 }); 6930 var Tooltip = DivOverlay.extend({ 6931 // @section 6932 // @aka Tooltip options 6933 options: { 6934 // @option pane: String = 'tooltipPane' 6935 // `Map pane` where the tooltip will be added. 6936 pane: "tooltipPane", 6937 // @option offset: Point = Point(0, 0) 6938 // Optional offset of the tooltip position. 6939 offset: [0, 0], 6940 // @option direction: String = 'auto' 6941 // Direction where to open the tooltip. Possible values are: `right`, `left`, 6942 // `top`, `bottom`, `center`, `auto`. 6943 // `auto` will dynamically switch between `right` and `left` according to the tooltip 6944 // position on the map. 6945 direction: "auto", 6946 // @option permanent: Boolean = false 6947 // Whether to open the tooltip permanently or only on mouseover. 6948 permanent: false, 6949 // @option sticky: Boolean = false 6950 // If true, the tooltip will follow the mouse instead of being fixed at the feature center. 6951 sticky: false, 6952 // @option opacity: Number = 0.9 6953 // Tooltip container opacity. 6954 opacity: 0.9 6955 }, 6956 onAdd: function(map2) { 6957 DivOverlay.prototype.onAdd.call(this, map2); 6958 this.setOpacity(this.options.opacity); 6959 map2.fire("tooltipopen", { tooltip: this }); 6960 if (this._source) { 6961 this.addEventParent(this._source); 6962 this._source.fire("tooltipopen", { tooltip: this }, true); 6963 } 6964 }, 6965 onRemove: function(map2) { 6966 DivOverlay.prototype.onRemove.call(this, map2); 6967 map2.fire("tooltipclose", { tooltip: this }); 6968 if (this._source) { 6969 this.removeEventParent(this._source); 6970 this._source.fire("tooltipclose", { tooltip: this }, true); 6971 } 6972 }, 6973 getEvents: function() { 6974 var events = DivOverlay.prototype.getEvents.call(this); 6975 if (!this.options.permanent) { 6976 events.preclick = this.close; 6977 } 6978 return events; 6979 }, 6980 _initLayout: function() { 6981 var prefix = "leaflet-tooltip", className = prefix + " " + (this.options.className || "") + " leaflet-zoom-" + (this._zoomAnimated ? "animated" : "hide"); 6982 this._contentNode = this._container = create$1("div", className); 6983 this._container.setAttribute("role", "tooltip"); 6984 this._container.setAttribute("id", "leaflet-tooltip-" + stamp(this)); 6985 }, 6986 _updateLayout: function() { 6987 }, 6988 _adjustPan: function() { 6989 }, 6990 _setPosition: function(pos) { 6991 var subX, subY, map2 = this._map, container = this._container, centerPoint = map2.latLngToContainerPoint(map2.getCenter()), tooltipPoint = map2.layerPointToContainerPoint(pos), direction = this.options.direction, tooltipWidth = container.offsetWidth, tooltipHeight = container.offsetHeight, offset = toPoint(this.options.offset), anchor = this._getAnchor(); 6992 if (direction === "top") { 6993 subX = tooltipWidth / 2; 6994 subY = tooltipHeight; 6995 } else if (direction === "bottom") { 6996 subX = tooltipWidth / 2; 6997 subY = 0; 6998 } else if (direction === "center") { 6999 subX = tooltipWidth / 2; 7000 subY = tooltipHeight / 2; 7001 } else if (direction === "right") { 7002 subX = 0; 7003 subY = tooltipHeight / 2; 7004 } else if (direction === "left") { 7005 subX = tooltipWidth; 7006 subY = tooltipHeight / 2; 7007 } else if (tooltipPoint.x < centerPoint.x) { 7008 direction = "right"; 7009 subX = 0; 7010 subY = tooltipHeight / 2; 7011 } else { 7012 direction = "left"; 7013 subX = tooltipWidth + (offset.x + anchor.x) * 2; 7014 subY = tooltipHeight / 2; 7015 } 7016 pos = pos.subtract(toPoint(subX, subY, true)).add(offset).add(anchor); 7017 removeClass(container, "leaflet-tooltip-right"); 7018 removeClass(container, "leaflet-tooltip-left"); 7019 removeClass(container, "leaflet-tooltip-top"); 7020 removeClass(container, "leaflet-tooltip-bottom"); 7021 addClass(container, "leaflet-tooltip-" + direction); 7022 setPosition(container, pos); 7023 }, 7024 _updatePosition: function() { 7025 var pos = this._map.latLngToLayerPoint(this._latlng); 7026 this._setPosition(pos); 7027 }, 7028 setOpacity: function(opacity) { 7029 this.options.opacity = opacity; 7030 if (this._container) { 7031 setOpacity(this._container, opacity); 7032 } 7033 }, 7034 _animateZoom: function(e) { 7035 var pos = this._map._latLngToNewLayerPoint(this._latlng, e.zoom, e.center); 7036 this._setPosition(pos); 7037 }, 7038 _getAnchor: function() { 7039 return toPoint(this._source && this._source._getTooltipAnchor && !this.options.sticky ? this._source._getTooltipAnchor() : [0, 0]); 7040 } 7041 }); 7042 var tooltip = function(options, source) { 7043 return new Tooltip(options, source); 7044 }; 7045 Map2.include({ 7046 // @method openTooltip(tooltip: Tooltip): this 7047 // Opens the specified tooltip. 7048 // @alternative 7049 // @method openTooltip(content: String|HTMLElement, latlng: LatLng, options?: Tooltip options): this 7050 // Creates a tooltip with the specified content and options and open it. 7051 openTooltip: function(tooltip2, latlng, options) { 7052 this._initOverlay(Tooltip, tooltip2, latlng, options).openOn(this); 7053 return this; 7054 }, 7055 // @method closeTooltip(tooltip: Tooltip): this 7056 // Closes the tooltip given as parameter. 7057 closeTooltip: function(tooltip2) { 7058 tooltip2.close(); 7059 return this; 7060 } 7061 }); 7062 Layer.include({ 7063 // @method bindTooltip(content: String|HTMLElement|Function|Tooltip, options?: Tooltip options): this 7064 // Binds a tooltip to the layer with the passed `content` and sets up the 7065 // necessary event listeners. If a `Function` is passed it will receive 7066 // the layer as the first argument and should return a `String` or `HTMLElement`. 7067 bindTooltip: function(content, options) { 7068 if (this._tooltip && this.isTooltipOpen()) { 7069 this.unbindTooltip(); 7070 } 7071 this._tooltip = this._initOverlay(Tooltip, this._tooltip, content, options); 7072 this._initTooltipInteractions(); 7073 if (this._tooltip.options.permanent && this._map && this._map.hasLayer(this)) { 7074 this.openTooltip(); 7075 } 7076 return this; 7077 }, 7078 // @method unbindTooltip(): this 7079 // Removes the tooltip previously bound with `bindTooltip`. 7080 unbindTooltip: function() { 7081 if (this._tooltip) { 7082 this._initTooltipInteractions(true); 7083 this.closeTooltip(); 7084 this._tooltip = null; 7085 } 7086 return this; 7087 }, 7088 _initTooltipInteractions: function(remove2) { 7089 if (!remove2 && this._tooltipHandlersAdded) { 7090 return; 7091 } 7092 var onOff = remove2 ? "off" : "on", events = { 7093 remove: this.closeTooltip, 7094 move: this._moveTooltip 7095 }; 7096 if (!this._tooltip.options.permanent) { 7097 events.mouseover = this._openTooltip; 7098 events.mouseout = this.closeTooltip; 7099 events.click = this._openTooltip; 7100 if (this._map) { 7101 this._addFocusListeners(); 7102 } else { 7103 events.add = this._addFocusListeners; 7104 } 7105 } else { 7106 events.add = this._openTooltip; 7107 } 7108 if (this._tooltip.options.sticky) { 7109 events.mousemove = this._moveTooltip; 7110 } 7111 this[onOff](events); 7112 this._tooltipHandlersAdded = !remove2; 7113 }, 7114 // @method openTooltip(latlng?: LatLng): this 7115 // Opens the bound tooltip at the specified `latlng` or at the default tooltip anchor if no `latlng` is passed. 7116 openTooltip: function(latlng) { 7117 if (this._tooltip) { 7118 if (!(this instanceof FeatureGroup)) { 7119 this._tooltip._source = this; 7120 } 7121 if (this._tooltip._prepareOpen(latlng)) { 7122 this._tooltip.openOn(this._map); 7123 if (this.getElement) { 7124 this._setAriaDescribedByOnLayer(this); 7125 } else if (this.eachLayer) { 7126 this.eachLayer(this._setAriaDescribedByOnLayer, this); 7127 } 7128 } 7129 } 7130 return this; 7131 }, 7132 // @method closeTooltip(): this 7133 // Closes the tooltip bound to this layer if it is open. 7134 closeTooltip: function() { 7135 if (this._tooltip) { 7136 return this._tooltip.close(); 7137 } 7138 }, 7139 // @method toggleTooltip(): this 7140 // Opens or closes the tooltip bound to this layer depending on its current state. 7141 toggleTooltip: function() { 7142 if (this._tooltip) { 7143 this._tooltip.toggle(this); 7144 } 7145 return this; 7146 }, 7147 // @method isTooltipOpen(): boolean 7148 // Returns `true` if the tooltip bound to this layer is currently open. 7149 isTooltipOpen: function() { 7150 return this._tooltip.isOpen(); 7151 }, 7152 // @method setTooltipContent(content: String|HTMLElement|Tooltip): this 7153 // Sets the content of the tooltip bound to this layer. 7154 setTooltipContent: function(content) { 7155 if (this._tooltip) { 7156 this._tooltip.setContent(content); 7157 } 7158 return this; 7159 }, 7160 // @method getTooltip(): Tooltip 7161 // Returns the tooltip bound to this layer. 7162 getTooltip: function() { 7163 return this._tooltip; 7164 }, 7165 _addFocusListeners: function() { 7166 if (this.getElement) { 7167 this._addFocusListenersOnLayer(this); 7168 } else if (this.eachLayer) { 7169 this.eachLayer(this._addFocusListenersOnLayer, this); 7170 } 7171 }, 7172 _addFocusListenersOnLayer: function(layer) { 7173 var el = typeof layer.getElement === "function" && layer.getElement(); 7174 if (el) { 7175 on(el, "focus", function() { 7176 this._tooltip._source = layer; 7177 this.openTooltip(); 7178 }, this); 7179 on(el, "blur", this.closeTooltip, this); 7180 } 7181 }, 7182 _setAriaDescribedByOnLayer: function(layer) { 7183 var el = typeof layer.getElement === "function" && layer.getElement(); 7184 if (el) { 7185 el.setAttribute("aria-describedby", this._tooltip._container.id); 7186 } 7187 }, 7188 _openTooltip: function(e) { 7189 if (!this._tooltip || !this._map) { 7190 return; 7191 } 7192 if (this._map.dragging && this._map.dragging.moving() && !this._openOnceFlag) { 7193 this._openOnceFlag = true; 7194 var that = this; 7195 this._map.once("moveend", function() { 7196 that._openOnceFlag = false; 7197 that._openTooltip(e); 7198 }); 7199 return; 7200 } 7201 this._tooltip._source = e.layer || e.target; 7202 this.openTooltip(this._tooltip.options.sticky ? e.latlng : void 0); 7203 }, 7204 _moveTooltip: function(e) { 7205 var latlng = e.latlng, containerPoint, layerPoint; 7206 if (this._tooltip.options.sticky && e.originalEvent) { 7207 containerPoint = this._map.mouseEventToContainerPoint(e.originalEvent); 7208 layerPoint = this._map.containerPointToLayerPoint(containerPoint); 7209 latlng = this._map.layerPointToLatLng(layerPoint); 7210 } 7211 this._tooltip.setLatLng(latlng); 7212 } 7213 }); 7214 var DivIcon = Icon.extend({ 7215 options: { 7216 // @section 7217 // @aka DivIcon options 7218 iconSize: [12, 12], 7219 // also can be set through CSS 7220 // iconAnchor: (Point), 7221 // popupAnchor: (Point), 7222 // @option html: String|HTMLElement = '' 7223 // Custom HTML code to put inside the div element, empty by default. Alternatively, 7224 // an instance of `HTMLElement`. 7225 html: false, 7226 // @option bgPos: Point = [0, 0] 7227 // Optional relative position of the background, in pixels 7228 bgPos: null, 7229 className: "leaflet-div-icon" 7230 }, 7231 createIcon: function(oldIcon) { 7232 var div = oldIcon && oldIcon.tagName === "DIV" ? oldIcon : document.createElement("div"), options = this.options; 7233 if (options.html instanceof Element) { 7234 empty(div); 7235 div.appendChild(options.html); 7236 } else { 7237 div.innerHTML = options.html !== false ? options.html : ""; 7238 } 7239 if (options.bgPos) { 7240 var bgPos = toPoint(options.bgPos); 7241 div.style.backgroundPosition = -bgPos.x + "px " + -bgPos.y + "px"; 7242 } 7243 this._setIconStyles(div, "icon"); 7244 return div; 7245 }, 7246 createShadow: function() { 7247 return null; 7248 } 7249 }); 7250 function divIcon2(options) { 7251 return new DivIcon(options); 7252 } 7253 Icon.Default = IconDefault; 7254 var GridLayer = Layer.extend({ 7255 // @section 7256 // @aka GridLayer options 7257 options: { 7258 // @option tileSize: Number|Point = 256 7259 // Width and height of tiles in the grid. Use a number if width and height are equal, or `L.point(width, height)` otherwise. 7260 tileSize: 256, 7261 // @option opacity: Number = 1.0 7262 // Opacity of the tiles. Can be used in the `createTile()` function. 7263 opacity: 1, 7264 // @option updateWhenIdle: Boolean = (depends) 7265 // Load new tiles only when panning ends. 7266 // `true` by default on mobile browsers, in order to avoid too many requests and keep smooth navigation. 7267 // `false` otherwise in order to display new tiles _during_ panning, since it is easy to pan outside the 7268 // [`keepBuffer`](#gridlayer-keepbuffer) option in desktop browsers. 7269 updateWhenIdle: Browser.mobile, 7270 // @option updateWhenZooming: Boolean = true 7271 // By default, a smooth zoom animation (during a [touch zoom](#map-touchzoom) or a [`flyTo()`](#map-flyto)) will update grid layers every integer zoom level. Setting this option to `false` will update the grid layer only when the smooth animation ends. 7272 updateWhenZooming: true, 7273 // @option updateInterval: Number = 200 7274 // Tiles will not update more than once every `updateInterval` milliseconds when panning. 7275 updateInterval: 200, 7276 // @option zIndex: Number = 1 7277 // The explicit zIndex of the tile layer. 7278 zIndex: 1, 7279 // @option bounds: LatLngBounds = undefined 7280 // If set, tiles will only be loaded inside the set `LatLngBounds`. 7281 bounds: null, 7282 // @option minZoom: Number = 0 7283 // The minimum zoom level down to which this layer will be displayed (inclusive). 7284 minZoom: 0, 7285 // @option maxZoom: Number = undefined 7286 // The maximum zoom level up to which this layer will be displayed (inclusive). 7287 maxZoom: void 0, 7288 // @option maxNativeZoom: Number = undefined 7289 // Maximum zoom number the tile source has available. If it is specified, 7290 // the tiles on all zoom levels higher than `maxNativeZoom` will be loaded 7291 // from `maxNativeZoom` level and auto-scaled. 7292 maxNativeZoom: void 0, 7293 // @option minNativeZoom: Number = undefined 7294 // Minimum zoom number the tile source has available. If it is specified, 7295 // the tiles on all zoom levels lower than `minNativeZoom` will be loaded 7296 // from `minNativeZoom` level and auto-scaled. 7297 minNativeZoom: void 0, 7298 // @option noWrap: Boolean = false 7299 // Whether the layer is wrapped around the antimeridian. If `true`, the 7300 // GridLayer will only be displayed once at low zoom levels. Has no 7301 // effect when the [map CRS](#map-crs) doesn't wrap around. Can be used 7302 // in combination with [`bounds`](#gridlayer-bounds) to prevent requesting 7303 // tiles outside the CRS limits. 7304 noWrap: false, 7305 // @option pane: String = 'tilePane' 7306 // `Map pane` where the grid layer will be added. 7307 pane: "tilePane", 7308 // @option className: String = '' 7309 // A custom class name to assign to the tile layer. Empty by default. 7310 className: "", 7311 // @option keepBuffer: Number = 2 7312 // When panning the map, keep this many rows and columns of tiles before unloading them. 7313 keepBuffer: 2 7314 }, 7315 initialize: function(options) { 7316 setOptions(this, options); 7317 }, 7318 onAdd: function() { 7319 this._initContainer(); 7320 this._levels = {}; 7321 this._tiles = {}; 7322 this._resetView(); 7323 }, 7324 beforeAdd: function(map2) { 7325 map2._addZoomLimit(this); 7326 }, 7327 onRemove: function(map2) { 7328 this._removeAllTiles(); 7329 remove(this._container); 7330 map2._removeZoomLimit(this); 7331 this._container = null; 7332 this._tileZoom = void 0; 7333 }, 7334 // @method bringToFront: this 7335 // Brings the tile layer to the top of all tile layers. 7336 bringToFront: function() { 7337 if (this._map) { 7338 toFront(this._container); 7339 this._setAutoZIndex(Math.max); 7340 } 7341 return this; 7342 }, 7343 // @method bringToBack: this 7344 // Brings the tile layer to the bottom of all tile layers. 7345 bringToBack: function() { 7346 if (this._map) { 7347 toBack(this._container); 7348 this._setAutoZIndex(Math.min); 7349 } 7350 return this; 7351 }, 7352 // @method getContainer: HTMLElement 7353 // Returns the HTML element that contains the tiles for this layer. 7354 getContainer: function() { 7355 return this._container; 7356 }, 7357 // @method setOpacity(opacity: Number): this 7358 // Changes the [opacity](#gridlayer-opacity) of the grid layer. 7359 setOpacity: function(opacity) { 7360 this.options.opacity = opacity; 7361 this._updateOpacity(); 7362 return this; 7363 }, 7364 // @method setZIndex(zIndex: Number): this 7365 // Changes the [zIndex](#gridlayer-zindex) of the grid layer. 7366 setZIndex: function(zIndex) { 7367 this.options.zIndex = zIndex; 7368 this._updateZIndex(); 7369 return this; 7370 }, 7371 // @method isLoading: Boolean 7372 // Returns `true` if any tile in the grid layer has not finished loading. 7373 isLoading: function() { 7374 return this._loading; 7375 }, 7376 // @method redraw: this 7377 // Causes the layer to clear all the tiles and request them again. 7378 redraw: function() { 7379 if (this._map) { 7380 this._removeAllTiles(); 7381 var tileZoom = this._clampZoom(this._map.getZoom()); 7382 if (tileZoom !== this._tileZoom) { 7383 this._tileZoom = tileZoom; 7384 this._updateLevels(); 7385 } 7386 this._update(); 7387 } 7388 return this; 7389 }, 7390 getEvents: function() { 7391 var events = { 7392 viewprereset: this._invalidateAll, 7393 viewreset: this._resetView, 7394 zoom: this._resetView, 7395 moveend: this._onMoveEnd 7396 }; 7397 if (!this.options.updateWhenIdle) { 7398 if (!this._onMove) { 7399 this._onMove = throttle(this._onMoveEnd, this.options.updateInterval, this); 7400 } 7401 events.move = this._onMove; 7402 } 7403 if (this._zoomAnimated) { 7404 events.zoomanim = this._animateZoom; 7405 } 7406 return events; 7407 }, 7408 // @section Extension methods 7409 // Layers extending `GridLayer` shall reimplement the following method. 7410 // @method createTile(coords: Object, done?: Function): HTMLElement 7411 // Called only internally, must be overridden by classes extending `GridLayer`. 7412 // Returns the `HTMLElement` corresponding to the given `coords`. If the `done` callback 7413 // is specified, it must be called when the tile has finished loading and drawing. 7414 createTile: function() { 7415 return document.createElement("div"); 7416 }, 7417 // @section 7418 // @method getTileSize: Point 7419 // Normalizes the [tileSize option](#gridlayer-tilesize) into a point. Used by the `createTile()` method. 7420 getTileSize: function() { 7421 var s = this.options.tileSize; 7422 return s instanceof Point ? s : new Point(s, s); 7423 }, 7424 _updateZIndex: function() { 7425 if (this._container && this.options.zIndex !== void 0 && this.options.zIndex !== null) { 7426 this._container.style.zIndex = this.options.zIndex; 7427 } 7428 }, 7429 _setAutoZIndex: function(compare) { 7430 var layers2 = this.getPane().children, edgeZIndex = -compare(-Infinity, Infinity); 7431 for (var i = 0, len = layers2.length, zIndex; i < len; i++) { 7432 zIndex = layers2[i].style.zIndex; 7433 if (layers2[i] !== this._container && zIndex) { 7434 edgeZIndex = compare(edgeZIndex, +zIndex); 7435 } 7436 } 7437 if (isFinite(edgeZIndex)) { 7438 this.options.zIndex = edgeZIndex + compare(-1, 1); 7439 this._updateZIndex(); 7440 } 7441 }, 7442 _updateOpacity: function() { 7443 if (!this._map) { 7444 return; 7445 } 7446 if (Browser.ielt9) { 7447 return; 7448 } 7449 setOpacity(this._container, this.options.opacity); 7450 var now = +/* @__PURE__ */ new Date(), nextFrame = false, willPrune = false; 7451 for (var key in this._tiles) { 7452 var tile = this._tiles[key]; 7453 if (!tile.current || !tile.loaded) { 7454 continue; 7455 } 7456 var fade = Math.min(1, (now - tile.loaded) / 200); 7457 setOpacity(tile.el, fade); 7458 if (fade < 1) { 7459 nextFrame = true; 7460 } else { 7461 if (tile.active) { 7462 willPrune = true; 7463 } else { 7464 this._onOpaqueTile(tile); 7465 } 7466 tile.active = true; 7467 } 7468 } 7469 if (willPrune && !this._noPrune) { 7470 this._pruneTiles(); 7471 } 7472 if (nextFrame) { 7473 cancelAnimFrame(this._fadeFrame); 7474 this._fadeFrame = requestAnimFrame(this._updateOpacity, this); 7475 } 7476 }, 7477 _onOpaqueTile: falseFn, 7478 _initContainer: function() { 7479 if (this._container) { 7480 return; 7481 } 7482 this._container = create$1("div", "leaflet-layer " + (this.options.className || "")); 7483 this._updateZIndex(); 7484 if (this.options.opacity < 1) { 7485 this._updateOpacity(); 7486 } 7487 this.getPane().appendChild(this._container); 7488 }, 7489 _updateLevels: function() { 7490 var zoom2 = this._tileZoom, maxZoom = this.options.maxZoom; 7491 if (zoom2 === void 0) { 7492 return void 0; 7493 } 7494 for (var z in this._levels) { 7495 z = Number(z); 7496 if (this._levels[z].el.children.length || z === zoom2) { 7497 this._levels[z].el.style.zIndex = maxZoom - Math.abs(zoom2 - z); 7498 this._onUpdateLevel(z); 7499 } else { 7500 remove(this._levels[z].el); 7501 this._removeTilesAtZoom(z); 7502 this._onRemoveLevel(z); 7503 delete this._levels[z]; 7504 } 7505 } 7506 var level = this._levels[zoom2], map2 = this._map; 7507 if (!level) { 7508 level = this._levels[zoom2] = {}; 7509 level.el = create$1("div", "leaflet-tile-container leaflet-zoom-animated", this._container); 7510 level.el.style.zIndex = maxZoom; 7511 level.origin = map2.project(map2.unproject(map2.getPixelOrigin()), zoom2).round(); 7512 level.zoom = zoom2; 7513 this._setZoomTransform(level, map2.getCenter(), map2.getZoom()); 7514 falseFn(level.el.offsetWidth); 7515 this._onCreateLevel(level); 7516 } 7517 this._level = level; 7518 return level; 7519 }, 7520 _onUpdateLevel: falseFn, 7521 _onRemoveLevel: falseFn, 7522 _onCreateLevel: falseFn, 7523 _pruneTiles: function() { 7524 if (!this._map) { 7525 return; 7526 } 7527 var key, tile; 7528 var zoom2 = this._map.getZoom(); 7529 if (zoom2 > this.options.maxZoom || zoom2 < this.options.minZoom) { 7530 this._removeAllTiles(); 7531 return; 7532 } 7533 for (key in this._tiles) { 7534 tile = this._tiles[key]; 7535 tile.retain = tile.current; 7536 } 7537 for (key in this._tiles) { 7538 tile = this._tiles[key]; 7539 if (tile.current && !tile.active) { 7540 var coords = tile.coords; 7541 if (!this._retainParent(coords.x, coords.y, coords.z, coords.z - 5)) { 7542 this._retainChildren(coords.x, coords.y, coords.z, coords.z + 2); 7543 } 7544 } 7545 } 7546 for (key in this._tiles) { 7547 if (!this._tiles[key].retain) { 7548 this._removeTile(key); 7549 } 7550 } 7551 }, 7552 _removeTilesAtZoom: function(zoom2) { 7553 for (var key in this._tiles) { 7554 if (this._tiles[key].coords.z !== zoom2) { 7555 continue; 7556 } 7557 this._removeTile(key); 7558 } 7559 }, 7560 _removeAllTiles: function() { 7561 for (var key in this._tiles) { 7562 this._removeTile(key); 7563 } 7564 }, 7565 _invalidateAll: function() { 7566 for (var z in this._levels) { 7567 remove(this._levels[z].el); 7568 this._onRemoveLevel(Number(z)); 7569 delete this._levels[z]; 7570 } 7571 this._removeAllTiles(); 7572 this._tileZoom = void 0; 7573 }, 7574 _retainParent: function(x, y, z, minZoom) { 7575 var x2 = Math.floor(x / 2), y2 = Math.floor(y / 2), z2 = z - 1, coords2 = new Point(+x2, +y2); 7576 coords2.z = +z2; 7577 var key = this._tileCoordsToKey(coords2), tile = this._tiles[key]; 7578 if (tile && tile.active) { 7579 tile.retain = true; 7580 return true; 7581 } else if (tile && tile.loaded) { 7582 tile.retain = true; 7583 } 7584 if (z2 > minZoom) { 7585 return this._retainParent(x2, y2, z2, minZoom); 7586 } 7587 return false; 7588 }, 7589 _retainChildren: function(x, y, z, maxZoom) { 7590 for (var i = 2 * x; i < 2 * x + 2; i++) { 7591 for (var j = 2 * y; j < 2 * y + 2; j++) { 7592 var coords = new Point(i, j); 7593 coords.z = z + 1; 7594 var key = this._tileCoordsToKey(coords), tile = this._tiles[key]; 7595 if (tile && tile.active) { 7596 tile.retain = true; 7597 continue; 7598 } else if (tile && tile.loaded) { 7599 tile.retain = true; 7600 } 7601 if (z + 1 < maxZoom) { 7602 this._retainChildren(i, j, z + 1, maxZoom); 7603 } 7604 } 7605 } 7606 }, 7607 _resetView: function(e) { 7608 var animating = e && (e.pinch || e.flyTo); 7609 this._setView(this._map.getCenter(), this._map.getZoom(), animating, animating); 7610 }, 7611 _animateZoom: function(e) { 7612 this._setView(e.center, e.zoom, true, e.noUpdate); 7613 }, 7614 _clampZoom: function(zoom2) { 7615 var options = this.options; 7616 if (void 0 !== options.minNativeZoom && zoom2 < options.minNativeZoom) { 7617 return options.minNativeZoom; 7618 } 7619 if (void 0 !== options.maxNativeZoom && options.maxNativeZoom < zoom2) { 7620 return options.maxNativeZoom; 7621 } 7622 return zoom2; 7623 }, 7624 _setView: function(center, zoom2, noPrune, noUpdate) { 7625 var tileZoom = Math.round(zoom2); 7626 if (this.options.maxZoom !== void 0 && tileZoom > this.options.maxZoom || this.options.minZoom !== void 0 && tileZoom < this.options.minZoom) { 7627 tileZoom = void 0; 7628 } else { 7629 tileZoom = this._clampZoom(tileZoom); 7630 } 7631 var tileZoomChanged = this.options.updateWhenZooming && tileZoom !== this._tileZoom; 7632 if (!noUpdate || tileZoomChanged) { 7633 this._tileZoom = tileZoom; 7634 if (this._abortLoading) { 7635 this._abortLoading(); 7636 } 7637 this._updateLevels(); 7638 this._resetGrid(); 7639 if (tileZoom !== void 0) { 7640 this._update(center); 7641 } 7642 if (!noPrune) { 7643 this._pruneTiles(); 7644 } 7645 this._noPrune = !!noPrune; 7646 } 7647 this._setZoomTransforms(center, zoom2); 7648 }, 7649 _setZoomTransforms: function(center, zoom2) { 7650 for (var i in this._levels) { 7651 this._setZoomTransform(this._levels[i], center, zoom2); 7652 } 7653 }, 7654 _setZoomTransform: function(level, center, zoom2) { 7655 var scale2 = this._map.getZoomScale(zoom2, level.zoom), translate = level.origin.multiplyBy(scale2).subtract(this._map._getNewPixelOrigin(center, zoom2)).round(); 7656 if (Browser.any3d) { 7657 setTransform(level.el, translate, scale2); 7658 } else { 7659 setPosition(level.el, translate); 7660 } 7661 }, 7662 _resetGrid: function() { 7663 var map2 = this._map, crs = map2.options.crs, tileSize = this._tileSize = this.getTileSize(), tileZoom = this._tileZoom; 7664 var bounds = this._map.getPixelWorldBounds(this._tileZoom); 7665 if (bounds) { 7666 this._globalTileRange = this._pxBoundsToTileRange(bounds); 7667 } 7668 this._wrapX = crs.wrapLng && !this.options.noWrap && [ 7669 Math.floor(map2.project([0, crs.wrapLng[0]], tileZoom).x / tileSize.x), 7670 Math.ceil(map2.project([0, crs.wrapLng[1]], tileZoom).x / tileSize.y) 7671 ]; 7672 this._wrapY = crs.wrapLat && !this.options.noWrap && [ 7673 Math.floor(map2.project([crs.wrapLat[0], 0], tileZoom).y / tileSize.x), 7674 Math.ceil(map2.project([crs.wrapLat[1], 0], tileZoom).y / tileSize.y) 7675 ]; 7676 }, 7677 _onMoveEnd: function() { 7678 if (!this._map || this._map._animatingZoom) { 7679 return; 7680 } 7681 this._update(); 7682 }, 7683 _getTiledPixelBounds: function(center) { 7684 var map2 = this._map, mapZoom = map2._animatingZoom ? Math.max(map2._animateToZoom, map2.getZoom()) : map2.getZoom(), scale2 = map2.getZoomScale(mapZoom, this._tileZoom), pixelCenter = map2.project(center, this._tileZoom).floor(), halfSize = map2.getSize().divideBy(scale2 * 2); 7685 return new Bounds(pixelCenter.subtract(halfSize), pixelCenter.add(halfSize)); 7686 }, 7687 // Private method to load tiles in the grid's active zoom level according to map bounds 7688 _update: function(center) { 7689 var map2 = this._map; 7690 if (!map2) { 7691 return; 7692 } 7693 var zoom2 = this._clampZoom(map2.getZoom()); 7694 if (center === void 0) { 7695 center = map2.getCenter(); 7696 } 7697 if (this._tileZoom === void 0) { 7698 return; 7699 } 7700 var pixelBounds = this._getTiledPixelBounds(center), tileRange = this._pxBoundsToTileRange(pixelBounds), tileCenter = tileRange.getCenter(), queue = [], margin = this.options.keepBuffer, noPruneRange = new Bounds( 7701 tileRange.getBottomLeft().subtract([margin, -margin]), 7702 tileRange.getTopRight().add([margin, -margin]) 7703 ); 7704 if (!(isFinite(tileRange.min.x) && isFinite(tileRange.min.y) && isFinite(tileRange.max.x) && isFinite(tileRange.max.y))) { 7705 throw new Error("Attempted to load an infinite number of tiles"); 7706 } 7707 for (var key in this._tiles) { 7708 var c = this._tiles[key].coords; 7709 if (c.z !== this._tileZoom || !noPruneRange.contains(new Point(c.x, c.y))) { 7710 this._tiles[key].current = false; 7711 } 7712 } 7713 if (Math.abs(zoom2 - this._tileZoom) > 1) { 7714 this._setView(center, zoom2); 7715 return; 7716 } 7717 for (var j = tileRange.min.y; j <= tileRange.max.y; j++) { 7718 for (var i = tileRange.min.x; i <= tileRange.max.x; i++) { 7719 var coords = new Point(i, j); 7720 coords.z = this._tileZoom; 7721 if (!this._isValidTile(coords)) { 7722 continue; 7723 } 7724 var tile = this._tiles[this._tileCoordsToKey(coords)]; 7725 if (tile) { 7726 tile.current = true; 7727 } else { 7728 queue.push(coords); 7729 } 7730 } 7731 } 7732 queue.sort(function(a, b) { 7733 return a.distanceTo(tileCenter) - b.distanceTo(tileCenter); 7734 }); 7735 if (queue.length !== 0) { 7736 if (!this._loading) { 7737 this._loading = true; 7738 this.fire("loading"); 7739 } 7740 var fragment = document.createDocumentFragment(); 7741 for (i = 0; i < queue.length; i++) { 7742 this._addTile(queue[i], fragment); 7743 } 7744 this._level.el.appendChild(fragment); 7745 } 7746 }, 7747 _isValidTile: function(coords) { 7748 var crs = this._map.options.crs; 7749 if (!crs.infinite) { 7750 var bounds = this._globalTileRange; 7751 if (!crs.wrapLng && (coords.x < bounds.min.x || coords.x > bounds.max.x) || !crs.wrapLat && (coords.y < bounds.min.y || coords.y > bounds.max.y)) { 7752 return false; 7753 } 7754 } 7755 if (!this.options.bounds) { 7756 return true; 7757 } 7758 var tileBounds = this._tileCoordsToBounds(coords); 7759 return toLatLngBounds(this.options.bounds).overlaps(tileBounds); 7760 }, 7761 _keyToBounds: function(key) { 7762 return this._tileCoordsToBounds(this._keyToTileCoords(key)); 7763 }, 7764 _tileCoordsToNwSe: function(coords) { 7765 var map2 = this._map, tileSize = this.getTileSize(), nwPoint = coords.scaleBy(tileSize), sePoint = nwPoint.add(tileSize), nw = map2.unproject(nwPoint, coords.z), se = map2.unproject(sePoint, coords.z); 7766 return [nw, se]; 7767 }, 7768 // converts tile coordinates to its geographical bounds 7769 _tileCoordsToBounds: function(coords) { 7770 var bp = this._tileCoordsToNwSe(coords), bounds = new LatLngBounds(bp[0], bp[1]); 7771 if (!this.options.noWrap) { 7772 bounds = this._map.wrapLatLngBounds(bounds); 7773 } 7774 return bounds; 7775 }, 7776 // converts tile coordinates to key for the tile cache 7777 _tileCoordsToKey: function(coords) { 7778 return coords.x + ":" + coords.y + ":" + coords.z; 7779 }, 7780 // converts tile cache key to coordinates 7781 _keyToTileCoords: function(key) { 7782 var k = key.split(":"), coords = new Point(+k[0], +k[1]); 7783 coords.z = +k[2]; 7784 return coords; 7785 }, 7786 _removeTile: function(key) { 7787 var tile = this._tiles[key]; 7788 if (!tile) { 7789 return; 7790 } 7791 remove(tile.el); 7792 delete this._tiles[key]; 7793 this.fire("tileunload", { 7794 tile: tile.el, 7795 coords: this._keyToTileCoords(key) 7796 }); 7797 }, 7798 _initTile: function(tile) { 7799 addClass(tile, "leaflet-tile"); 7800 var tileSize = this.getTileSize(); 7801 tile.style.width = tileSize.x + "px"; 7802 tile.style.height = tileSize.y + "px"; 7803 tile.onselectstart = falseFn; 7804 tile.onmousemove = falseFn; 7805 if (Browser.ielt9 && this.options.opacity < 1) { 7806 setOpacity(tile, this.options.opacity); 7807 } 7808 }, 7809 _addTile: function(coords, container) { 7810 var tilePos = this._getTilePos(coords), key = this._tileCoordsToKey(coords); 7811 var tile = this.createTile(this._wrapCoords(coords), bind(this._tileReady, this, coords)); 7812 this._initTile(tile); 7813 if (this.createTile.length < 2) { 7814 requestAnimFrame(bind(this._tileReady, this, coords, null, tile)); 7815 } 7816 setPosition(tile, tilePos); 7817 this._tiles[key] = { 7818 el: tile, 7819 coords, 7820 current: true 7821 }; 7822 container.appendChild(tile); 7823 this.fire("tileloadstart", { 7824 tile, 7825 coords 7826 }); 7827 }, 7828 _tileReady: function(coords, err, tile) { 7829 if (err) { 7830 this.fire("tileerror", { 7831 error: err, 7832 tile, 7833 coords 7834 }); 7835 } 7836 var key = this._tileCoordsToKey(coords); 7837 tile = this._tiles[key]; 7838 if (!tile) { 7839 return; 7840 } 7841 tile.loaded = +/* @__PURE__ */ new Date(); 7842 if (this._map._fadeAnimated) { 7843 setOpacity(tile.el, 0); 7844 cancelAnimFrame(this._fadeFrame); 7845 this._fadeFrame = requestAnimFrame(this._updateOpacity, this); 7846 } else { 7847 tile.active = true; 7848 this._pruneTiles(); 7849 } 7850 if (!err) { 7851 addClass(tile.el, "leaflet-tile-loaded"); 7852 this.fire("tileload", { 7853 tile: tile.el, 7854 coords 7855 }); 7856 } 7857 if (this._noTilesToLoad()) { 7858 this._loading = false; 7859 this.fire("load"); 7860 if (Browser.ielt9 || !this._map._fadeAnimated) { 7861 requestAnimFrame(this._pruneTiles, this); 7862 } else { 7863 setTimeout(bind(this._pruneTiles, this), 250); 7864 } 7865 } 7866 }, 7867 _getTilePos: function(coords) { 7868 return coords.scaleBy(this.getTileSize()).subtract(this._level.origin); 7869 }, 7870 _wrapCoords: function(coords) { 7871 var newCoords = new Point( 7872 this._wrapX ? wrapNum(coords.x, this._wrapX) : coords.x, 7873 this._wrapY ? wrapNum(coords.y, this._wrapY) : coords.y 7874 ); 7875 newCoords.z = coords.z; 7876 return newCoords; 7877 }, 7878 _pxBoundsToTileRange: function(bounds) { 7879 var tileSize = this.getTileSize(); 7880 return new Bounds( 7881 bounds.min.unscaleBy(tileSize).floor(), 7882 bounds.max.unscaleBy(tileSize).ceil().subtract([1, 1]) 7883 ); 7884 }, 7885 _noTilesToLoad: function() { 7886 for (var key in this._tiles) { 7887 if (!this._tiles[key].loaded) { 7888 return false; 7889 } 7890 } 7891 return true; 7892 } 7893 }); 7894 function gridLayer(options) { 7895 return new GridLayer(options); 7896 } 7897 var TileLayer = GridLayer.extend({ 7898 // @section 7899 // @aka TileLayer options 7900 options: { 7901 // @option minZoom: Number = 0 7902 // The minimum zoom level down to which this layer will be displayed (inclusive). 7903 minZoom: 0, 7904 // @option maxZoom: Number = 18 7905 // The maximum zoom level up to which this layer will be displayed (inclusive). 7906 maxZoom: 18, 7907 // @option subdomains: String|String[] = 'abc' 7908 // Subdomains of the tile service. Can be passed in the form of one string (where each letter is a subdomain name) or an array of strings. 7909 subdomains: "abc", 7910 // @option errorTileUrl: String = '' 7911 // URL to the tile image to show in place of the tile that failed to load. 7912 errorTileUrl: "", 7913 // @option zoomOffset: Number = 0 7914 // The zoom number used in tile URLs will be offset with this value. 7915 zoomOffset: 0, 7916 // @option tms: Boolean = false 7917 // If `true`, inverses Y axis numbering for tiles (turn this on for [TMS](https://en.wikipedia.org/wiki/Tile_Map_Service) services). 7918 tms: false, 7919 // @option zoomReverse: Boolean = false 7920 // If set to true, the zoom number used in tile URLs will be reversed (`maxZoom - zoom` instead of `zoom`) 7921 zoomReverse: false, 7922 // @option detectRetina: Boolean = false 7923 // If `true` and user is on a retina display, it will request four tiles of half the specified size and a bigger zoom level in place of one to utilize the high resolution. 7924 detectRetina: false, 7925 // @option crossOrigin: Boolean|String = false 7926 // Whether the crossOrigin attribute will be added to the tiles. 7927 // If a String is provided, all tiles will have their crossOrigin attribute set to the String provided. This is needed if you want to access tile pixel data. 7928 // Refer to [CORS Settings](https://developer.mozilla.org/en-US/docs/Web/HTML/CORS_settings_attributes) for valid String values. 7929 crossOrigin: false, 7930 // @option referrerPolicy: Boolean|String = false 7931 // Whether the referrerPolicy attribute will be added to the tiles. 7932 // If a String is provided, all tiles will have their referrerPolicy attribute set to the String provided. 7933 // This may be needed if your map's rendering context has a strict default but your tile provider expects a valid referrer 7934 // (e.g. to validate an API token). 7935 // Refer to [HTMLImageElement.referrerPolicy](https://developer.mozilla.org/en-US/docs/Web/API/HTMLImageElement/referrerPolicy) for valid String values. 7936 referrerPolicy: false 7937 }, 7938 initialize: function(url, options) { 7939 this._url = url; 7940 options = setOptions(this, options); 7941 if (options.detectRetina && Browser.retina && options.maxZoom > 0) { 7942 options.tileSize = Math.floor(options.tileSize / 2); 7943 if (!options.zoomReverse) { 7944 options.zoomOffset++; 7945 options.maxZoom = Math.max(options.minZoom, options.maxZoom - 1); 7946 } else { 7947 options.zoomOffset--; 7948 options.minZoom = Math.min(options.maxZoom, options.minZoom + 1); 7949 } 7950 options.minZoom = Math.max(0, options.minZoom); 7951 } else if (!options.zoomReverse) { 7952 options.maxZoom = Math.max(options.minZoom, options.maxZoom); 7953 } else { 7954 options.minZoom = Math.min(options.maxZoom, options.minZoom); 7955 } 7956 if (typeof options.subdomains === "string") { 7957 options.subdomains = options.subdomains.split(""); 7958 } 7959 this.on("tileunload", this._onTileRemove); 7960 }, 7961 // @method setUrl(url: String, noRedraw?: Boolean): this 7962 // Updates the layer's URL template and redraws it (unless `noRedraw` is set to `true`). 7963 // If the URL does not change, the layer will not be redrawn unless 7964 // the noRedraw parameter is set to false. 7965 setUrl: function(url, noRedraw) { 7966 if (this._url === url && noRedraw === void 0) { 7967 noRedraw = true; 7968 } 7969 this._url = url; 7970 if (!noRedraw) { 7971 this.redraw(); 7972 } 7973 return this; 7974 }, 7975 // @method createTile(coords: Object, done?: Function): HTMLElement 7976 // Called only internally, overrides GridLayer's [`createTile()`](#gridlayer-createtile) 7977 // to return an `<img>` HTML element with the appropriate image URL given `coords`. The `done` 7978 // callback is called when the tile has been loaded. 7979 createTile: function(coords, done) { 7980 var tile = document.createElement("img"); 7981 on(tile, "load", bind(this._tileOnLoad, this, done, tile)); 7982 on(tile, "error", bind(this._tileOnError, this, done, tile)); 7983 if (this.options.crossOrigin || this.options.crossOrigin === "") { 7984 tile.crossOrigin = this.options.crossOrigin === true ? "" : this.options.crossOrigin; 7985 } 7986 if (typeof this.options.referrerPolicy === "string") { 7987 tile.referrerPolicy = this.options.referrerPolicy; 7988 } 7989 tile.alt = ""; 7990 tile.src = this.getTileUrl(coords); 7991 return tile; 7992 }, 7993 // @section Extension methods 7994 // @uninheritable 7995 // Layers extending `TileLayer` might reimplement the following method. 7996 // @method getTileUrl(coords: Object): String 7997 // Called only internally, returns the URL for a tile given its coordinates. 7998 // Classes extending `TileLayer` can override this function to provide custom tile URL naming schemes. 7999 getTileUrl: function(coords) { 8000 var data = { 8001 r: Browser.retina ? "@2x" : "", 8002 s: this._getSubdomain(coords), 8003 x: coords.x, 8004 y: coords.y, 8005 z: this._getZoomForUrl() 8006 }; 8007 if (this._map && !this._map.options.crs.infinite) { 8008 var invertedY = this._globalTileRange.max.y - coords.y; 8009 if (this.options.tms) { 8010 data["y"] = invertedY; 8011 } 8012 data["-y"] = invertedY; 8013 } 8014 return template(this._url, extend(data, this.options)); 8015 }, 8016 _tileOnLoad: function(done, tile) { 8017 if (Browser.ielt9) { 8018 setTimeout(bind(done, this, null, tile), 0); 8019 } else { 8020 done(null, tile); 8021 } 8022 }, 8023 _tileOnError: function(done, tile, e) { 8024 var errorUrl = this.options.errorTileUrl; 8025 if (errorUrl && tile.getAttribute("src") !== errorUrl) { 8026 tile.src = errorUrl; 8027 } 8028 done(e, tile); 8029 }, 8030 _onTileRemove: function(e) { 8031 e.tile.onload = null; 8032 }, 8033 _getZoomForUrl: function() { 8034 var zoom2 = this._tileZoom, maxZoom = this.options.maxZoom, zoomReverse = this.options.zoomReverse, zoomOffset = this.options.zoomOffset; 8035 if (zoomReverse) { 8036 zoom2 = maxZoom - zoom2; 8037 } 8038 return zoom2 + zoomOffset; 8039 }, 8040 _getSubdomain: function(tilePoint) { 8041 var index2 = Math.abs(tilePoint.x + tilePoint.y) % this.options.subdomains.length; 8042 return this.options.subdomains[index2]; 8043 }, 8044 // stops loading all tiles in the background layer 8045 _abortLoading: function() { 8046 var i, tile; 8047 for (i in this._tiles) { 8048 if (this._tiles[i].coords.z !== this._tileZoom) { 8049 tile = this._tiles[i].el; 8050 tile.onload = falseFn; 8051 tile.onerror = falseFn; 8052 if (!tile.complete) { 8053 tile.src = emptyImageUrl; 8054 var coords = this._tiles[i].coords; 8055 remove(tile); 8056 delete this._tiles[i]; 8057 this.fire("tileabort", { 8058 tile, 8059 coords 8060 }); 8061 } 8062 } 8063 } 8064 }, 8065 _removeTile: function(key) { 8066 var tile = this._tiles[key]; 8067 if (!tile) { 8068 return; 8069 } 8070 tile.el.setAttribute("src", emptyImageUrl); 8071 return GridLayer.prototype._removeTile.call(this, key); 8072 }, 8073 _tileReady: function(coords, err, tile) { 8074 if (!this._map || tile && tile.getAttribute("src") === emptyImageUrl) { 8075 return; 8076 } 8077 return GridLayer.prototype._tileReady.call(this, coords, err, tile); 8078 } 8079 }); 8080 function tileLayer2(url, options) { 8081 return new TileLayer(url, options); 8082 } 8083 var TileLayerWMS = TileLayer.extend({ 8084 // @section 8085 // @aka TileLayer.WMS options 8086 // If any custom options not documented here are used, they will be sent to the 8087 // WMS server as extra parameters in each request URL. This can be useful for 8088 // [non-standard vendor WMS parameters](https://docs.geoserver.org/stable/en/user/services/wms/vendor.html). 8089 defaultWmsParams: { 8090 service: "WMS", 8091 request: "GetMap", 8092 // @option layers: String = '' 8093 // **(required)** Comma-separated list of WMS layers to show. 8094 layers: "", 8095 // @option styles: String = '' 8096 // Comma-separated list of WMS styles. 8097 styles: "", 8098 // @option format: String = 'image/jpeg' 8099 // WMS image format (use `'image/png'` for layers with transparency). 8100 format: "image/jpeg", 8101 // @option transparent: Boolean = false 8102 // If `true`, the WMS service will return images with transparency. 8103 transparent: false, 8104 // @option version: String = '1.1.1' 8105 // Version of the WMS service to use 8106 version: "1.1.1" 8107 }, 8108 options: { 8109 // @option crs: CRS = null 8110 // Coordinate Reference System to use for the WMS requests, defaults to 8111 // map CRS. Don't change this if you're not sure what it means. 8112 crs: null, 8113 // @option uppercase: Boolean = false 8114 // If `true`, WMS request parameter keys will be uppercase. 8115 uppercase: false 8116 }, 8117 initialize: function(url, options) { 8118 this._url = url; 8119 var wmsParams = extend({}, this.defaultWmsParams); 8120 for (var i in options) { 8121 if (!(i in this.options)) { 8122 wmsParams[i] = options[i]; 8123 } 8124 } 8125 options = setOptions(this, options); 8126 var realRetina = options.detectRetina && Browser.retina ? 2 : 1; 8127 var tileSize = this.getTileSize(); 8128 wmsParams.width = tileSize.x * realRetina; 8129 wmsParams.height = tileSize.y * realRetina; 8130 this.wmsParams = wmsParams; 8131 }, 8132 onAdd: function(map2) { 8133 this._crs = this.options.crs || map2.options.crs; 8134 this._wmsVersion = parseFloat(this.wmsParams.version); 8135 var projectionKey = this._wmsVersion >= 1.3 ? "crs" : "srs"; 8136 this.wmsParams[projectionKey] = this._crs.code; 8137 TileLayer.prototype.onAdd.call(this, map2); 8138 }, 8139 getTileUrl: function(coords) { 8140 var tileBounds = this._tileCoordsToNwSe(coords), crs = this._crs, bounds = toBounds(crs.project(tileBounds[0]), crs.project(tileBounds[1])), min = bounds.min, max = bounds.max, bbox = (this._wmsVersion >= 1.3 && this._crs === EPSG4326 ? [min.y, min.x, max.y, max.x] : [min.x, min.y, max.x, max.y]).join(","), url = TileLayer.prototype.getTileUrl.call(this, coords); 8141 return url + getParamString(this.wmsParams, url, this.options.uppercase) + (this.options.uppercase ? "&BBOX=" : "&bbox=") + bbox; 8142 }, 8143 // @method setParams(params: Object, noRedraw?: Boolean): this 8144 // Merges an object with the new parameters and re-requests tiles on the current screen (unless `noRedraw` was set to true). 8145 setParams: function(params, noRedraw) { 8146 extend(this.wmsParams, params); 8147 if (!noRedraw) { 8148 this.redraw(); 8149 } 8150 return this; 8151 } 8152 }); 8153 function tileLayerWMS(url, options) { 8154 return new TileLayerWMS(url, options); 8155 } 8156 TileLayer.WMS = TileLayerWMS; 8157 tileLayer2.wms = tileLayerWMS; 8158 var Renderer = Layer.extend({ 8159 // @section 8160 // @aka Renderer options 8161 options: { 8162 // @option padding: Number = 0.1 8163 // How much to extend the clip area around the map view (relative to its size) 8164 // e.g. 0.1 would be 10% of map view in each direction 8165 padding: 0.1 8166 }, 8167 initialize: function(options) { 8168 setOptions(this, options); 8169 stamp(this); 8170 this._layers = this._layers || {}; 8171 }, 8172 onAdd: function() { 8173 if (!this._container) { 8174 this._initContainer(); 8175 addClass(this._container, "leaflet-zoom-animated"); 8176 } 8177 this.getPane().appendChild(this._container); 8178 this._update(); 8179 this.on("update", this._updatePaths, this); 8180 }, 8181 onRemove: function() { 8182 this.off("update", this._updatePaths, this); 8183 this._destroyContainer(); 8184 }, 8185 getEvents: function() { 8186 var events = { 8187 viewreset: this._reset, 8188 zoom: this._onZoom, 8189 moveend: this._update, 8190 zoomend: this._onZoomEnd 8191 }; 8192 if (this._zoomAnimated) { 8193 events.zoomanim = this._onAnimZoom; 8194 } 8195 return events; 8196 }, 8197 _onAnimZoom: function(ev) { 8198 this._updateTransform(ev.center, ev.zoom); 8199 }, 8200 _onZoom: function() { 8201 this._updateTransform(this._map.getCenter(), this._map.getZoom()); 8202 }, 8203 _updateTransform: function(center, zoom2) { 8204 var scale2 = this._map.getZoomScale(zoom2, this._zoom), viewHalf = this._map.getSize().multiplyBy(0.5 + this.options.padding), currentCenterPoint = this._map.project(this._center, zoom2), topLeftOffset = viewHalf.multiplyBy(-scale2).add(currentCenterPoint).subtract(this._map._getNewPixelOrigin(center, zoom2)); 8205 if (Browser.any3d) { 8206 setTransform(this._container, topLeftOffset, scale2); 8207 } else { 8208 setPosition(this._container, topLeftOffset); 8209 } 8210 }, 8211 _reset: function() { 8212 this._update(); 8213 this._updateTransform(this._center, this._zoom); 8214 for (var id in this._layers) { 8215 this._layers[id]._reset(); 8216 } 8217 }, 8218 _onZoomEnd: function() { 8219 for (var id in this._layers) { 8220 this._layers[id]._project(); 8221 } 8222 }, 8223 _updatePaths: function() { 8224 for (var id in this._layers) { 8225 this._layers[id]._update(); 8226 } 8227 }, 8228 _update: function() { 8229 var p = this.options.padding, size = this._map.getSize(), min = this._map.containerPointToLayerPoint(size.multiplyBy(-p)).round(); 8230 this._bounds = new Bounds(min, min.add(size.multiplyBy(1 + p * 2)).round()); 8231 this._center = this._map.getCenter(); 8232 this._zoom = this._map.getZoom(); 8233 } 8234 }); 8235 var Canvas = Renderer.extend({ 8236 // @section 8237 // @aka Canvas options 8238 options: { 8239 // @option tolerance: Number = 0 8240 // How much to extend the click tolerance around a path/object on the map. 8241 tolerance: 0 8242 }, 8243 getEvents: function() { 8244 var events = Renderer.prototype.getEvents.call(this); 8245 events.viewprereset = this._onViewPreReset; 8246 return events; 8247 }, 8248 _onViewPreReset: function() { 8249 this._postponeUpdatePaths = true; 8250 }, 8251 onAdd: function() { 8252 Renderer.prototype.onAdd.call(this); 8253 this._draw(); 8254 }, 8255 _initContainer: function() { 8256 var container = this._container = document.createElement("canvas"); 8257 on(container, "mousemove", this._onMouseMove, this); 8258 on(container, "click dblclick mousedown mouseup contextmenu", this._onClick, this); 8259 on(container, "mouseout", this._handleMouseOut, this); 8260 container["_leaflet_disable_events"] = true; 8261 this._ctx = container.getContext("2d"); 8262 }, 8263 _destroyContainer: function() { 8264 cancelAnimFrame(this._redrawRequest); 8265 delete this._ctx; 8266 remove(this._container); 8267 off(this._container); 8268 delete this._container; 8269 }, 8270 _updatePaths: function() { 8271 if (this._postponeUpdatePaths) { 8272 return; 8273 } 8274 var layer; 8275 this._redrawBounds = null; 8276 for (var id in this._layers) { 8277 layer = this._layers[id]; 8278 layer._update(); 8279 } 8280 this._redraw(); 8281 }, 8282 _update: function() { 8283 if (this._map._animatingZoom && this._bounds) { 8284 return; 8285 } 8286 Renderer.prototype._update.call(this); 8287 var b = this._bounds, container = this._container, size = b.getSize(), m = Browser.retina ? 2 : 1; 8288 setPosition(container, b.min); 8289 container.width = m * size.x; 8290 container.height = m * size.y; 8291 container.style.width = size.x + "px"; 8292 container.style.height = size.y + "px"; 8293 if (Browser.retina) { 8294 this._ctx.scale(2, 2); 8295 } 8296 this._ctx.translate(-b.min.x, -b.min.y); 8297 this.fire("update"); 8298 }, 8299 _reset: function() { 8300 Renderer.prototype._reset.call(this); 8301 if (this._postponeUpdatePaths) { 8302 this._postponeUpdatePaths = false; 8303 this._updatePaths(); 8304 } 8305 }, 8306 _initPath: function(layer) { 8307 this._updateDashArray(layer); 8308 this._layers[stamp(layer)] = layer; 8309 var order = layer._order = { 8310 layer, 8311 prev: this._drawLast, 8312 next: null 8313 }; 8314 if (this._drawLast) { 8315 this._drawLast.next = order; 8316 } 8317 this._drawLast = order; 8318 this._drawFirst = this._drawFirst || this._drawLast; 8319 }, 8320 _addPath: function(layer) { 8321 this._requestRedraw(layer); 8322 }, 8323 _removePath: function(layer) { 8324 var order = layer._order; 8325 var next = order.next; 8326 var prev = order.prev; 8327 if (next) { 8328 next.prev = prev; 8329 } else { 8330 this._drawLast = prev; 8331 } 8332 if (prev) { 8333 prev.next = next; 8334 } else { 8335 this._drawFirst = next; 8336 } 8337 delete layer._order; 8338 delete this._layers[stamp(layer)]; 8339 this._requestRedraw(layer); 8340 }, 8341 _updatePath: function(layer) { 8342 this._extendRedrawBounds(layer); 8343 layer._project(); 8344 layer._update(); 8345 this._requestRedraw(layer); 8346 }, 8347 _updateStyle: function(layer) { 8348 this._updateDashArray(layer); 8349 this._requestRedraw(layer); 8350 }, 8351 _updateDashArray: function(layer) { 8352 if (typeof layer.options.dashArray === "string") { 8353 var parts = layer.options.dashArray.split(/[, ]+/), dashArray = [], dashValue, i; 8354 for (i = 0; i < parts.length; i++) { 8355 dashValue = Number(parts[i]); 8356 if (isNaN(dashValue)) { 8357 return; 8358 } 8359 dashArray.push(dashValue); 8360 } 8361 layer.options._dashArray = dashArray; 8362 } else { 8363 layer.options._dashArray = layer.options.dashArray; 8364 } 8365 }, 8366 _requestRedraw: function(layer) { 8367 if (!this._map) { 8368 return; 8369 } 8370 this._extendRedrawBounds(layer); 8371 this._redrawRequest = this._redrawRequest || requestAnimFrame(this._redraw, this); 8372 }, 8373 _extendRedrawBounds: function(layer) { 8374 if (layer._pxBounds) { 8375 var padding = (layer.options.weight || 0) + 1; 8376 this._redrawBounds = this._redrawBounds || new Bounds(); 8377 this._redrawBounds.extend(layer._pxBounds.min.subtract([padding, padding])); 8378 this._redrawBounds.extend(layer._pxBounds.max.add([padding, padding])); 8379 } 8380 }, 8381 _redraw: function() { 8382 this._redrawRequest = null; 8383 if (this._redrawBounds) { 8384 this._redrawBounds.min._floor(); 8385 this._redrawBounds.max._ceil(); 8386 } 8387 this._clear(); 8388 this._draw(); 8389 this._redrawBounds = null; 8390 }, 8391 _clear: function() { 8392 var bounds = this._redrawBounds; 8393 if (bounds) { 8394 var size = bounds.getSize(); 8395 this._ctx.clearRect(bounds.min.x, bounds.min.y, size.x, size.y); 8396 } else { 8397 this._ctx.save(); 8398 this._ctx.setTransform(1, 0, 0, 1, 0, 0); 8399 this._ctx.clearRect(0, 0, this._container.width, this._container.height); 8400 this._ctx.restore(); 8401 } 8402 }, 8403 _draw: function() { 8404 var layer, bounds = this._redrawBounds; 8405 this._ctx.save(); 8406 if (bounds) { 8407 var size = bounds.getSize(); 8408 this._ctx.beginPath(); 8409 this._ctx.rect(bounds.min.x, bounds.min.y, size.x, size.y); 8410 this._ctx.clip(); 8411 } 8412 this._drawing = true; 8413 for (var order = this._drawFirst; order; order = order.next) { 8414 layer = order.layer; 8415 if (!bounds || layer._pxBounds && layer._pxBounds.intersects(bounds)) { 8416 layer._updatePath(); 8417 } 8418 } 8419 this._drawing = false; 8420 this._ctx.restore(); 8421 }, 8422 _updatePoly: function(layer, closed) { 8423 if (!this._drawing) { 8424 return; 8425 } 8426 var i, j, len2, p, parts = layer._parts, len = parts.length, ctx = this._ctx; 8427 if (!len) { 8428 return; 8429 } 8430 ctx.beginPath(); 8431 for (i = 0; i < len; i++) { 8432 for (j = 0, len2 = parts[i].length; j < len2; j++) { 8433 p = parts[i][j]; 8434 ctx[j ? "lineTo" : "moveTo"](p.x, p.y); 8435 } 8436 if (closed) { 8437 ctx.closePath(); 8438 } 8439 } 8440 this._fillStroke(ctx, layer); 8441 }, 8442 _updateCircle: function(layer) { 8443 if (!this._drawing || layer._empty()) { 8444 return; 8445 } 8446 var p = layer._point, ctx = this._ctx, r = Math.max(Math.round(layer._radius), 1), s = (Math.max(Math.round(layer._radiusY), 1) || r) / r; 8447 if (s !== 1) { 8448 ctx.save(); 8449 ctx.scale(1, s); 8450 } 8451 ctx.beginPath(); 8452 ctx.arc(p.x, p.y / s, r, 0, Math.PI * 2, false); 8453 if (s !== 1) { 8454 ctx.restore(); 8455 } 8456 this._fillStroke(ctx, layer); 8457 }, 8458 _fillStroke: function(ctx, layer) { 8459 var options = layer.options; 8460 if (options.fill) { 8461 ctx.globalAlpha = options.fillOpacity; 8462 ctx.fillStyle = options.fillColor || options.color; 8463 ctx.fill(options.fillRule || "evenodd"); 8464 } 8465 if (options.stroke && options.weight !== 0) { 8466 if (ctx.setLineDash) { 8467 ctx.setLineDash(layer.options && layer.options._dashArray || []); 8468 } 8469 ctx.globalAlpha = options.opacity; 8470 ctx.lineWidth = options.weight; 8471 ctx.strokeStyle = options.color; 8472 ctx.lineCap = options.lineCap; 8473 ctx.lineJoin = options.lineJoin; 8474 ctx.stroke(); 8475 } 8476 }, 8477 // Canvas obviously doesn't have mouse events for individual drawn objects, 8478 // so we emulate that by calculating what's under the mouse on mousemove/click manually 8479 _onClick: function(e) { 8480 var point = this._map.mouseEventToLayerPoint(e), layer, clickedLayer; 8481 for (var order = this._drawFirst; order; order = order.next) { 8482 layer = order.layer; 8483 if (layer.options.interactive && layer._containsPoint(point)) { 8484 if (!(e.type === "click" || e.type === "preclick") || !this._map._draggableMoved(layer)) { 8485 clickedLayer = layer; 8486 } 8487 } 8488 } 8489 this._fireEvent(clickedLayer ? [clickedLayer] : false, e); 8490 }, 8491 _onMouseMove: function(e) { 8492 if (!this._map || this._map.dragging.moving() || this._map._animatingZoom) { 8493 return; 8494 } 8495 var point = this._map.mouseEventToLayerPoint(e); 8496 this._handleMouseHover(e, point); 8497 }, 8498 _handleMouseOut: function(e) { 8499 var layer = this._hoveredLayer; 8500 if (layer) { 8501 removeClass(this._container, "leaflet-interactive"); 8502 this._fireEvent([layer], e, "mouseout"); 8503 this._hoveredLayer = null; 8504 this._mouseHoverThrottled = false; 8505 } 8506 }, 8507 _handleMouseHover: function(e, point) { 8508 if (this._mouseHoverThrottled) { 8509 return; 8510 } 8511 var layer, candidateHoveredLayer; 8512 for (var order = this._drawFirst; order; order = order.next) { 8513 layer = order.layer; 8514 if (layer.options.interactive && layer._containsPoint(point)) { 8515 candidateHoveredLayer = layer; 8516 } 8517 } 8518 if (candidateHoveredLayer !== this._hoveredLayer) { 8519 this._handleMouseOut(e); 8520 if (candidateHoveredLayer) { 8521 addClass(this._container, "leaflet-interactive"); 8522 this._fireEvent([candidateHoveredLayer], e, "mouseover"); 8523 this._hoveredLayer = candidateHoveredLayer; 8524 } 8525 } 8526 this._fireEvent(this._hoveredLayer ? [this._hoveredLayer] : false, e); 8527 this._mouseHoverThrottled = true; 8528 setTimeout(bind(function() { 8529 this._mouseHoverThrottled = false; 8530 }, this), 32); 8531 }, 8532 _fireEvent: function(layers2, e, type) { 8533 this._map._fireDOMEvent(e, type || e.type, layers2); 8534 }, 8535 _bringToFront: function(layer) { 8536 var order = layer._order; 8537 if (!order) { 8538 return; 8539 } 8540 var next = order.next; 8541 var prev = order.prev; 8542 if (next) { 8543 next.prev = prev; 8544 } else { 8545 return; 8546 } 8547 if (prev) { 8548 prev.next = next; 8549 } else if (next) { 8550 this._drawFirst = next; 8551 } 8552 order.prev = this._drawLast; 8553 this._drawLast.next = order; 8554 order.next = null; 8555 this._drawLast = order; 8556 this._requestRedraw(layer); 8557 }, 8558 _bringToBack: function(layer) { 8559 var order = layer._order; 8560 if (!order) { 8561 return; 8562 } 8563 var next = order.next; 8564 var prev = order.prev; 8565 if (prev) { 8566 prev.next = next; 8567 } else { 8568 return; 8569 } 8570 if (next) { 8571 next.prev = prev; 8572 } else if (prev) { 8573 this._drawLast = prev; 8574 } 8575 order.prev = null; 8576 order.next = this._drawFirst; 8577 this._drawFirst.prev = order; 8578 this._drawFirst = order; 8579 this._requestRedraw(layer); 8580 } 8581 }); 8582 function canvas(options) { 8583 return Browser.canvas ? new Canvas(options) : null; 8584 } 8585 var vmlCreate = function() { 8586 try { 8587 document.namespaces.add("lvml", "urn:schemas-microsoft-com:vml"); 8588 return function(name) { 8589 return document.createElement("<lvml:" + name + ' class="lvml">'); 8590 }; 8591 } catch (e) { 8592 } 8593 return function(name) { 8594 return document.createElement("<" + name + ' xmlns="urn:schemas-microsoft.com:vml" class="lvml">'); 8595 }; 8596 }(); 8597 var vmlMixin = { 8598 _initContainer: function() { 8599 this._container = create$1("div", "leaflet-vml-container"); 8600 }, 8601 _update: function() { 8602 if (this._map._animatingZoom) { 8603 return; 8604 } 8605 Renderer.prototype._update.call(this); 8606 this.fire("update"); 8607 }, 8608 _initPath: function(layer) { 8609 var container = layer._container = vmlCreate("shape"); 8610 addClass(container, "leaflet-vml-shape " + (this.options.className || "")); 8611 container.coordsize = "1 1"; 8612 layer._path = vmlCreate("path"); 8613 container.appendChild(layer._path); 8614 this._updateStyle(layer); 8615 this._layers[stamp(layer)] = layer; 8616 }, 8617 _addPath: function(layer) { 8618 var container = layer._container; 8619 this._container.appendChild(container); 8620 if (layer.options.interactive) { 8621 layer.addInteractiveTarget(container); 8622 } 8623 }, 8624 _removePath: function(layer) { 8625 var container = layer._container; 8626 remove(container); 8627 layer.removeInteractiveTarget(container); 8628 delete this._layers[stamp(layer)]; 8629 }, 8630 _updateStyle: function(layer) { 8631 var stroke = layer._stroke, fill = layer._fill, options = layer.options, container = layer._container; 8632 container.stroked = !!options.stroke; 8633 container.filled = !!options.fill; 8634 if (options.stroke) { 8635 if (!stroke) { 8636 stroke = layer._stroke = vmlCreate("stroke"); 8637 } 8638 container.appendChild(stroke); 8639 stroke.weight = options.weight + "px"; 8640 stroke.color = options.color; 8641 stroke.opacity = options.opacity; 8642 if (options.dashArray) { 8643 stroke.dashStyle = isArray(options.dashArray) ? options.dashArray.join(" ") : options.dashArray.replace(/( *, *)/g, " "); 8644 } else { 8645 stroke.dashStyle = ""; 8646 } 8647 stroke.endcap = options.lineCap.replace("butt", "flat"); 8648 stroke.joinstyle = options.lineJoin; 8649 } else if (stroke) { 8650 container.removeChild(stroke); 8651 layer._stroke = null; 8652 } 8653 if (options.fill) { 8654 if (!fill) { 8655 fill = layer._fill = vmlCreate("fill"); 8656 } 8657 container.appendChild(fill); 8658 fill.color = options.fillColor || options.color; 8659 fill.opacity = options.fillOpacity; 8660 } else if (fill) { 8661 container.removeChild(fill); 8662 layer._fill = null; 8663 } 8664 }, 8665 _updateCircle: function(layer) { 8666 var p = layer._point.round(), r = Math.round(layer._radius), r2 = Math.round(layer._radiusY || r); 8667 this._setPath(layer, layer._empty() ? "M0 0" : "AL " + p.x + "," + p.y + " " + r + "," + r2 + " 0," + 65535 * 360); 8668 }, 8669 _setPath: function(layer, path) { 8670 layer._path.v = path; 8671 }, 8672 _bringToFront: function(layer) { 8673 toFront(layer._container); 8674 }, 8675 _bringToBack: function(layer) { 8676 toBack(layer._container); 8677 } 8678 }; 8679 var create = Browser.vml ? vmlCreate : svgCreate; 8680 var SVG = Renderer.extend({ 8681 _initContainer: function() { 8682 this._container = create("svg"); 8683 this._container.setAttribute("pointer-events", "none"); 8684 this._rootGroup = create("g"); 8685 this._container.appendChild(this._rootGroup); 8686 }, 8687 _destroyContainer: function() { 8688 remove(this._container); 8689 off(this._container); 8690 delete this._container; 8691 delete this._rootGroup; 8692 delete this._svgSize; 8693 }, 8694 _update: function() { 8695 if (this._map._animatingZoom && this._bounds) { 8696 return; 8697 } 8698 Renderer.prototype._update.call(this); 8699 var b = this._bounds, size = b.getSize(), container = this._container; 8700 if (!this._svgSize || !this._svgSize.equals(size)) { 8701 this._svgSize = size; 8702 container.setAttribute("width", size.x); 8703 container.setAttribute("height", size.y); 8704 } 8705 setPosition(container, b.min); 8706 container.setAttribute("viewBox", [b.min.x, b.min.y, size.x, size.y].join(" ")); 8707 this.fire("update"); 8708 }, 8709 // methods below are called by vector layers implementations 8710 _initPath: function(layer) { 8711 var path = layer._path = create("path"); 8712 if (layer.options.className) { 8713 addClass(path, layer.options.className); 8714 } 8715 if (layer.options.interactive) { 8716 addClass(path, "leaflet-interactive"); 8717 } 8718 this._updateStyle(layer); 8719 this._layers[stamp(layer)] = layer; 8720 }, 8721 _addPath: function(layer) { 8722 if (!this._rootGroup) { 8723 this._initContainer(); 8724 } 8725 this._rootGroup.appendChild(layer._path); 8726 layer.addInteractiveTarget(layer._path); 8727 }, 8728 _removePath: function(layer) { 8729 remove(layer._path); 8730 layer.removeInteractiveTarget(layer._path); 8731 delete this._layers[stamp(layer)]; 8732 }, 8733 _updatePath: function(layer) { 8734 layer._project(); 8735 layer._update(); 8736 }, 8737 _updateStyle: function(layer) { 8738 var path = layer._path, options = layer.options; 8739 if (!path) { 8740 return; 8741 } 8742 if (options.stroke) { 8743 path.setAttribute("stroke", options.color); 8744 path.setAttribute("stroke-opacity", options.opacity); 8745 path.setAttribute("stroke-width", options.weight); 8746 path.setAttribute("stroke-linecap", options.lineCap); 8747 path.setAttribute("stroke-linejoin", options.lineJoin); 8748 if (options.dashArray) { 8749 path.setAttribute("stroke-dasharray", options.dashArray); 8750 } else { 8751 path.removeAttribute("stroke-dasharray"); 8752 } 8753 if (options.dashOffset) { 8754 path.setAttribute("stroke-dashoffset", options.dashOffset); 8755 } else { 8756 path.removeAttribute("stroke-dashoffset"); 8757 } 8758 } else { 8759 path.setAttribute("stroke", "none"); 8760 } 8761 if (options.fill) { 8762 path.setAttribute("fill", options.fillColor || options.color); 8763 path.setAttribute("fill-opacity", options.fillOpacity); 8764 path.setAttribute("fill-rule", options.fillRule || "evenodd"); 8765 } else { 8766 path.setAttribute("fill", "none"); 8767 } 8768 }, 8769 _updatePoly: function(layer, closed) { 8770 this._setPath(layer, pointsToPath(layer._parts, closed)); 8771 }, 8772 _updateCircle: function(layer) { 8773 var p = layer._point, r = Math.max(Math.round(layer._radius), 1), r2 = Math.max(Math.round(layer._radiusY), 1) || r, arc = "a" + r + "," + r2 + " 0 1,0 "; 8774 var d = layer._empty() ? "M0 0" : "M" + (p.x - r) + "," + p.y + arc + r * 2 + ",0 " + arc + -r * 2 + ",0 "; 8775 this._setPath(layer, d); 8776 }, 8777 _setPath: function(layer, path) { 8778 layer._path.setAttribute("d", path); 8779 }, 8780 // SVG does not have the concept of zIndex so we resort to changing the DOM order of elements 8781 _bringToFront: function(layer) { 8782 toFront(layer._path); 8783 }, 8784 _bringToBack: function(layer) { 8785 toBack(layer._path); 8786 } 8787 }); 8788 if (Browser.vml) { 8789 SVG.include(vmlMixin); 8790 } 8791 function svg(options) { 8792 return Browser.svg || Browser.vml ? new SVG(options) : null; 8793 } 8794 Map2.include({ 8795 // @namespace Map; @method getRenderer(layer: Path): Renderer 8796 // Returns the instance of `Renderer` that should be used to render the given 8797 // `Path`. It will ensure that the `renderer` options of the map and paths 8798 // are respected, and that the renderers do exist on the map. 8799 getRenderer: function(layer) { 8800 var renderer = layer.options.renderer || this._getPaneRenderer(layer.options.pane) || this.options.renderer || this._renderer; 8801 if (!renderer) { 8802 renderer = this._renderer = this._createRenderer(); 8803 } 8804 if (!this.hasLayer(renderer)) { 8805 this.addLayer(renderer); 8806 } 8807 return renderer; 8808 }, 8809 _getPaneRenderer: function(name) { 8810 if (name === "overlayPane" || name === void 0) { 8811 return false; 8812 } 8813 var renderer = this._paneRenderers[name]; 8814 if (renderer === void 0) { 8815 renderer = this._createRenderer({ pane: name }); 8816 this._paneRenderers[name] = renderer; 8817 } 8818 return renderer; 8819 }, 8820 _createRenderer: function(options) { 8821 return this.options.preferCanvas && canvas(options) || svg(options); 8822 } 8823 }); 8824 var Rectangle = Polygon.extend({ 8825 initialize: function(latLngBounds, options) { 8826 Polygon.prototype.initialize.call(this, this._boundsToLatLngs(latLngBounds), options); 8827 }, 8828 // @method setBounds(latLngBounds: LatLngBounds): this 8829 // Redraws the rectangle with the passed bounds. 8830 setBounds: function(latLngBounds) { 8831 return this.setLatLngs(this._boundsToLatLngs(latLngBounds)); 8832 }, 8833 _boundsToLatLngs: function(latLngBounds) { 8834 latLngBounds = toLatLngBounds(latLngBounds); 8835 return [ 8836 latLngBounds.getSouthWest(), 8837 latLngBounds.getNorthWest(), 8838 latLngBounds.getNorthEast(), 8839 latLngBounds.getSouthEast() 8840 ]; 8841 } 8842 }); 8843 function rectangle(latLngBounds, options) { 8844 return new Rectangle(latLngBounds, options); 8845 } 8846 SVG.create = create; 8847 SVG.pointsToPath = pointsToPath; 8848 GeoJSON.geometryToLayer = geometryToLayer; 8849 GeoJSON.coordsToLatLng = coordsToLatLng; 8850 GeoJSON.coordsToLatLngs = coordsToLatLngs; 8851 GeoJSON.latLngToCoords = latLngToCoords; 8852 GeoJSON.latLngsToCoords = latLngsToCoords; 8853 GeoJSON.getFeature = getFeature; 8854 GeoJSON.asFeature = asFeature; 8855 Map2.mergeOptions({ 8856 // @option boxZoom: Boolean = true 8857 // Whether the map can be zoomed to a rectangular area specified by 8858 // dragging the mouse while pressing the shift key. 8859 boxZoom: true 8860 }); 8861 var BoxZoom = Handler.extend({ 8862 initialize: function(map2) { 8863 this._map = map2; 8864 this._container = map2._container; 8865 this._pane = map2._panes.overlayPane; 8866 this._resetStateTimeout = 0; 8867 map2.on("unload", this._destroy, this); 8868 }, 8869 addHooks: function() { 8870 on(this._container, "mousedown", this._onMouseDown, this); 8871 }, 8872 removeHooks: function() { 8873 off(this._container, "mousedown", this._onMouseDown, this); 8874 }, 8875 moved: function() { 8876 return this._moved; 8877 }, 8878 _destroy: function() { 8879 remove(this._pane); 8880 delete this._pane; 8881 }, 8882 _resetState: function() { 8883 this._resetStateTimeout = 0; 8884 this._moved = false; 8885 }, 8886 _clearDeferredResetState: function() { 8887 if (this._resetStateTimeout !== 0) { 8888 clearTimeout(this._resetStateTimeout); 8889 this._resetStateTimeout = 0; 8890 } 8891 }, 8892 _onMouseDown: function(e) { 8893 if (!e.shiftKey || e.which !== 1 && e.button !== 1) { 8894 return false; 8895 } 8896 this._clearDeferredResetState(); 8897 this._resetState(); 8898 disableTextSelection(); 8899 disableImageDrag(); 8900 this._startPoint = this._map.mouseEventToContainerPoint(e); 8901 on(document, { 8902 contextmenu: stop, 8903 mousemove: this._onMouseMove, 8904 mouseup: this._onMouseUp, 8905 keydown: this._onKeyDown 8906 }, this); 8907 }, 8908 _onMouseMove: function(e) { 8909 if (!this._moved) { 8910 this._moved = true; 8911 this._box = create$1("div", "leaflet-zoom-box", this._container); 8912 addClass(this._container, "leaflet-crosshair"); 8913 this._map.fire("boxzoomstart"); 8914 } 8915 this._point = this._map.mouseEventToContainerPoint(e); 8916 var bounds = new Bounds(this._point, this._startPoint), size = bounds.getSize(); 8917 setPosition(this._box, bounds.min); 8918 this._box.style.width = size.x + "px"; 8919 this._box.style.height = size.y + "px"; 8920 }, 8921 _finish: function() { 8922 if (this._moved) { 8923 remove(this._box); 8924 removeClass(this._container, "leaflet-crosshair"); 8925 } 8926 enableTextSelection(); 8927 enableImageDrag(); 8928 off(document, { 8929 contextmenu: stop, 8930 mousemove: this._onMouseMove, 8931 mouseup: this._onMouseUp, 8932 keydown: this._onKeyDown 8933 }, this); 8934 }, 8935 _onMouseUp: function(e) { 8936 if (e.which !== 1 && e.button !== 1) { 8937 return; 8938 } 8939 this._finish(); 8940 if (!this._moved) { 8941 return; 8942 } 8943 this._clearDeferredResetState(); 8944 this._resetStateTimeout = setTimeout(bind(this._resetState, this), 0); 8945 var bounds = new LatLngBounds( 8946 this._map.containerPointToLatLng(this._startPoint), 8947 this._map.containerPointToLatLng(this._point) 8948 ); 8949 this._map.fitBounds(bounds).fire("boxzoomend", { boxZoomBounds: bounds }); 8950 }, 8951 _onKeyDown: function(e) { 8952 if (e.keyCode === 27) { 8953 this._finish(); 8954 this._clearDeferredResetState(); 8955 this._resetState(); 8956 } 8957 } 8958 }); 8959 Map2.addInitHook("addHandler", "boxZoom", BoxZoom); 8960 Map2.mergeOptions({ 8961 // @option doubleClickZoom: Boolean|String = true 8962 // Whether the map can be zoomed in by double clicking on it and 8963 // zoomed out by double clicking while holding shift. If passed 8964 // `'center'`, double-click zoom will zoom to the center of the 8965 // view regardless of where the mouse was. 8966 doubleClickZoom: true 8967 }); 8968 var DoubleClickZoom = Handler.extend({ 8969 addHooks: function() { 8970 this._map.on("dblclick", this._onDoubleClick, this); 8971 }, 8972 removeHooks: function() { 8973 this._map.off("dblclick", this._onDoubleClick, this); 8974 }, 8975 _onDoubleClick: function(e) { 8976 var map2 = this._map, oldZoom = map2.getZoom(), delta = map2.options.zoomDelta, zoom2 = e.originalEvent.shiftKey ? oldZoom - delta : oldZoom + delta; 8977 if (map2.options.doubleClickZoom === "center") { 8978 map2.setZoom(zoom2); 8979 } else { 8980 map2.setZoomAround(e.containerPoint, zoom2); 8981 } 8982 } 8983 }); 8984 Map2.addInitHook("addHandler", "doubleClickZoom", DoubleClickZoom); 8985 Map2.mergeOptions({ 8986 // @option dragging: Boolean = true 8987 // Whether the map is draggable with mouse/touch or not. 8988 dragging: true, 8989 // @section Panning Inertia Options 8990 // @option inertia: Boolean = * 8991 // If enabled, panning of the map will have an inertia effect where 8992 // the map builds momentum while dragging and continues moving in 8993 // the same direction for some time. Feels especially nice on touch 8994 // devices. Enabled by default. 8995 inertia: true, 8996 // @option inertiaDeceleration: Number = 3000 8997 // The rate with which the inertial movement slows down, in pixels/second². 8998 inertiaDeceleration: 3400, 8999 // px/s^2 9000 // @option inertiaMaxSpeed: Number = Infinity 9001 // Max speed of the inertial movement, in pixels/second. 9002 inertiaMaxSpeed: Infinity, 9003 // px/s 9004 // @option easeLinearity: Number = 0.2 9005 easeLinearity: 0.2, 9006 // TODO refactor, move to CRS 9007 // @option worldCopyJump: Boolean = false 9008 // With this option enabled, the map tracks when you pan to another "copy" 9009 // of the world and seamlessly jumps to the original one so that all overlays 9010 // like markers and vector layers are still visible. 9011 worldCopyJump: false, 9012 // @option maxBoundsViscosity: Number = 0.0 9013 // If `maxBounds` is set, this option will control how solid the bounds 9014 // are when dragging the map around. The default value of `0.0` allows the 9015 // user to drag outside the bounds at normal speed, higher values will 9016 // slow down map dragging outside bounds, and `1.0` makes the bounds fully 9017 // solid, preventing the user from dragging outside the bounds. 9018 maxBoundsViscosity: 0 9019 }); 9020 var Drag = Handler.extend({ 9021 addHooks: function() { 9022 if (!this._draggable) { 9023 var map2 = this._map; 9024 this._draggable = new Draggable(map2._mapPane, map2._container); 9025 this._draggable.on({ 9026 dragstart: this._onDragStart, 9027 drag: this._onDrag, 9028 dragend: this._onDragEnd 9029 }, this); 9030 this._draggable.on("predrag", this._onPreDragLimit, this); 9031 if (map2.options.worldCopyJump) { 9032 this._draggable.on("predrag", this._onPreDragWrap, this); 9033 map2.on("zoomend", this._onZoomEnd, this); 9034 map2.whenReady(this._onZoomEnd, this); 9035 } 9036 } 9037 addClass(this._map._container, "leaflet-grab leaflet-touch-drag"); 9038 this._draggable.enable(); 9039 this._positions = []; 9040 this._times = []; 9041 }, 9042 removeHooks: function() { 9043 removeClass(this._map._container, "leaflet-grab"); 9044 removeClass(this._map._container, "leaflet-touch-drag"); 9045 this._draggable.disable(); 9046 }, 9047 moved: function() { 9048 return this._draggable && this._draggable._moved; 9049 }, 9050 moving: function() { 9051 return this._draggable && this._draggable._moving; 9052 }, 9053 _onDragStart: function() { 9054 var map2 = this._map; 9055 map2._stop(); 9056 if (this._map.options.maxBounds && this._map.options.maxBoundsViscosity) { 9057 var bounds = toLatLngBounds(this._map.options.maxBounds); 9058 this._offsetLimit = toBounds( 9059 this._map.latLngToContainerPoint(bounds.getNorthWest()).multiplyBy(-1), 9060 this._map.latLngToContainerPoint(bounds.getSouthEast()).multiplyBy(-1).add(this._map.getSize()) 9061 ); 9062 this._viscosity = Math.min(1, Math.max(0, this._map.options.maxBoundsViscosity)); 9063 } else { 9064 this._offsetLimit = null; 9065 } 9066 map2.fire("movestart").fire("dragstart"); 9067 if (map2.options.inertia) { 9068 this._positions = []; 9069 this._times = []; 9070 } 9071 }, 9072 _onDrag: function(e) { 9073 if (this._map.options.inertia) { 9074 var time = this._lastTime = +/* @__PURE__ */ new Date(), pos = this._lastPos = this._draggable._absPos || this._draggable._newPos; 9075 this._positions.push(pos); 9076 this._times.push(time); 9077 this._prunePositions(time); 9078 } 9079 this._map.fire("move", e).fire("drag", e); 9080 }, 9081 _prunePositions: function(time) { 9082 while (this._positions.length > 1 && time - this._times[0] > 50) { 9083 this._positions.shift(); 9084 this._times.shift(); 9085 } 9086 }, 9087 _onZoomEnd: function() { 9088 var pxCenter = this._map.getSize().divideBy(2), pxWorldCenter = this._map.latLngToLayerPoint([0, 0]); 9089 this._initialWorldOffset = pxWorldCenter.subtract(pxCenter).x; 9090 this._worldWidth = this._map.getPixelWorldBounds().getSize().x; 9091 }, 9092 _viscousLimit: function(value, threshold) { 9093 return value - (value - threshold) * this._viscosity; 9094 }, 9095 _onPreDragLimit: function() { 9096 if (!this._viscosity || !this._offsetLimit) { 9097 return; 9098 } 9099 var offset = this._draggable._newPos.subtract(this._draggable._startPos); 9100 var limit = this._offsetLimit; 9101 if (offset.x < limit.min.x) { 9102 offset.x = this._viscousLimit(offset.x, limit.min.x); 9103 } 9104 if (offset.y < limit.min.y) { 9105 offset.y = this._viscousLimit(offset.y, limit.min.y); 9106 } 9107 if (offset.x > limit.max.x) { 9108 offset.x = this._viscousLimit(offset.x, limit.max.x); 9109 } 9110 if (offset.y > limit.max.y) { 9111 offset.y = this._viscousLimit(offset.y, limit.max.y); 9112 } 9113 this._draggable._newPos = this._draggable._startPos.add(offset); 9114 }, 9115 _onPreDragWrap: function() { 9116 var worldWidth = this._worldWidth, halfWidth = Math.round(worldWidth / 2), dx = this._initialWorldOffset, x = this._draggable._newPos.x, newX1 = (x - halfWidth + dx) % worldWidth + halfWidth - dx, newX2 = (x + halfWidth + dx) % worldWidth - halfWidth - dx, newX = Math.abs(newX1 + dx) < Math.abs(newX2 + dx) ? newX1 : newX2; 9117 this._draggable._absPos = this._draggable._newPos.clone(); 9118 this._draggable._newPos.x = newX; 9119 }, 9120 _onDragEnd: function(e) { 9121 var map2 = this._map, options = map2.options, noInertia = !options.inertia || e.noInertia || this._times.length < 2; 9122 map2.fire("dragend", e); 9123 if (noInertia) { 9124 map2.fire("moveend"); 9125 } else { 9126 this._prunePositions(+/* @__PURE__ */ new Date()); 9127 var direction = this._lastPos.subtract(this._positions[0]), duration = (this._lastTime - this._times[0]) / 1e3, ease = options.easeLinearity, speedVector = direction.multiplyBy(ease / duration), speed = speedVector.distanceTo([0, 0]), limitedSpeed = Math.min(options.inertiaMaxSpeed, speed), limitedSpeedVector = speedVector.multiplyBy(limitedSpeed / speed), decelerationDuration = limitedSpeed / (options.inertiaDeceleration * ease), offset = limitedSpeedVector.multiplyBy(-decelerationDuration / 2).round(); 9128 if (!offset.x && !offset.y) { 9129 map2.fire("moveend"); 9130 } else { 9131 offset = map2._limitOffset(offset, map2.options.maxBounds); 9132 requestAnimFrame(function() { 9133 map2.panBy(offset, { 9134 duration: decelerationDuration, 9135 easeLinearity: ease, 9136 noMoveStart: true, 9137 animate: true 9138 }); 9139 }); 9140 } 9141 } 9142 } 9143 }); 9144 Map2.addInitHook("addHandler", "dragging", Drag); 9145 Map2.mergeOptions({ 9146 // @option keyboard: Boolean = true 9147 // Makes the map focusable and allows users to navigate the map with keyboard 9148 // arrows and `+`/`-` keys. 9149 keyboard: true, 9150 // @option keyboardPanDelta: Number = 80 9151 // Amount of pixels to pan when pressing an arrow key. 9152 keyboardPanDelta: 80 9153 }); 9154 var Keyboard = Handler.extend({ 9155 keyCodes: { 9156 left: [37], 9157 right: [39], 9158 down: [40], 9159 up: [38], 9160 zoomIn: [187, 107, 61, 171], 9161 zoomOut: [189, 109, 54, 173] 9162 }, 9163 initialize: function(map2) { 9164 this._map = map2; 9165 this._setPanDelta(map2.options.keyboardPanDelta); 9166 this._setZoomDelta(map2.options.zoomDelta); 9167 }, 9168 addHooks: function() { 9169 var container = this._map._container; 9170 if (container.tabIndex <= 0) { 9171 container.tabIndex = "0"; 9172 } 9173 on(container, { 9174 focus: this._onFocus, 9175 blur: this._onBlur, 9176 mousedown: this._onMouseDown 9177 }, this); 9178 this._map.on({ 9179 focus: this._addHooks, 9180 blur: this._removeHooks 9181 }, this); 9182 }, 9183 removeHooks: function() { 9184 this._removeHooks(); 9185 off(this._map._container, { 9186 focus: this._onFocus, 9187 blur: this._onBlur, 9188 mousedown: this._onMouseDown 9189 }, this); 9190 this._map.off({ 9191 focus: this._addHooks, 9192 blur: this._removeHooks 9193 }, this); 9194 }, 9195 _onMouseDown: function() { 9196 if (this._focused) { 9197 return; 9198 } 9199 var body = document.body, docEl = document.documentElement, top = body.scrollTop || docEl.scrollTop, left = body.scrollLeft || docEl.scrollLeft; 9200 this._map._container.focus(); 9201 window.scrollTo(left, top); 9202 }, 9203 _onFocus: function() { 9204 this._focused = true; 9205 this._map.fire("focus"); 9206 }, 9207 _onBlur: function() { 9208 this._focused = false; 9209 this._map.fire("blur"); 9210 }, 9211 _setPanDelta: function(panDelta) { 9212 var keys = this._panKeys = {}, codes = this.keyCodes, i, len; 9213 for (i = 0, len = codes.left.length; i < len; i++) { 9214 keys[codes.left[i]] = [-1 * panDelta, 0]; 9215 } 9216 for (i = 0, len = codes.right.length; i < len; i++) { 9217 keys[codes.right[i]] = [panDelta, 0]; 9218 } 9219 for (i = 0, len = codes.down.length; i < len; i++) { 9220 keys[codes.down[i]] = [0, panDelta]; 9221 } 9222 for (i = 0, len = codes.up.length; i < len; i++) { 9223 keys[codes.up[i]] = [0, -1 * panDelta]; 9224 } 9225 }, 9226 _setZoomDelta: function(zoomDelta) { 9227 var keys = this._zoomKeys = {}, codes = this.keyCodes, i, len; 9228 for (i = 0, len = codes.zoomIn.length; i < len; i++) { 9229 keys[codes.zoomIn[i]] = zoomDelta; 9230 } 9231 for (i = 0, len = codes.zoomOut.length; i < len; i++) { 9232 keys[codes.zoomOut[i]] = -zoomDelta; 9233 } 9234 }, 9235 _addHooks: function() { 9236 on(document, "keydown", this._onKeyDown, this); 9237 }, 9238 _removeHooks: function() { 9239 off(document, "keydown", this._onKeyDown, this); 9240 }, 9241 _onKeyDown: function(e) { 9242 if (e.altKey || e.ctrlKey || e.metaKey) { 9243 return; 9244 } 9245 var key = e.keyCode, map2 = this._map, offset; 9246 if (key in this._panKeys) { 9247 if (!map2._panAnim || !map2._panAnim._inProgress) { 9248 offset = this._panKeys[key]; 9249 if (e.shiftKey) { 9250 offset = toPoint(offset).multiplyBy(3); 9251 } 9252 if (map2.options.maxBounds) { 9253 offset = map2._limitOffset(toPoint(offset), map2.options.maxBounds); 9254 } 9255 if (map2.options.worldCopyJump) { 9256 var newLatLng = map2.wrapLatLng(map2.unproject(map2.project(map2.getCenter()).add(offset))); 9257 map2.panTo(newLatLng); 9258 } else { 9259 map2.panBy(offset); 9260 } 9261 } 9262 } else if (key in this._zoomKeys) { 9263 map2.setZoom(map2.getZoom() + (e.shiftKey ? 3 : 1) * this._zoomKeys[key]); 9264 } else if (key === 27 && map2._popup && map2._popup.options.closeOnEscapeKey) { 9265 map2.closePopup(); 9266 } else { 9267 return; 9268 } 9269 stop(e); 9270 } 9271 }); 9272 Map2.addInitHook("addHandler", "keyboard", Keyboard); 9273 Map2.mergeOptions({ 9274 // @section Mouse wheel options 9275 // @option scrollWheelZoom: Boolean|String = true 9276 // Whether the map can be zoomed by using the mouse wheel. If passed `'center'`, 9277 // it will zoom to the center of the view regardless of where the mouse was. 9278 scrollWheelZoom: true, 9279 // @option wheelDebounceTime: Number = 40 9280 // Limits the rate at which a wheel can fire (in milliseconds). By default 9281 // user can't zoom via wheel more often than once per 40 ms. 9282 wheelDebounceTime: 40, 9283 // @option wheelPxPerZoomLevel: Number = 60 9284 // How many scroll pixels (as reported by [L.DomEvent.getWheelDelta](#domevent-getwheeldelta)) 9285 // mean a change of one full zoom level. Smaller values will make wheel-zooming 9286 // faster (and vice versa). 9287 wheelPxPerZoomLevel: 60 9288 }); 9289 var ScrollWheelZoom = Handler.extend({ 9290 addHooks: function() { 9291 on(this._map._container, "wheel", this._onWheelScroll, this); 9292 this._delta = 0; 9293 }, 9294 removeHooks: function() { 9295 off(this._map._container, "wheel", this._onWheelScroll, this); 9296 }, 9297 _onWheelScroll: function(e) { 9298 var delta = getWheelDelta(e); 9299 var debounce2 = this._map.options.wheelDebounceTime; 9300 this._delta += delta; 9301 this._lastMousePos = this._map.mouseEventToContainerPoint(e); 9302 if (!this._startTime) { 9303 this._startTime = +/* @__PURE__ */ new Date(); 9304 } 9305 var left = Math.max(debounce2 - (+/* @__PURE__ */ new Date() - this._startTime), 0); 9306 clearTimeout(this._timer); 9307 this._timer = setTimeout(bind(this._performZoom, this), left); 9308 stop(e); 9309 }, 9310 _performZoom: function() { 9311 var map2 = this._map, zoom2 = map2.getZoom(), snap = this._map.options.zoomSnap || 0; 9312 map2._stop(); 9313 var d2 = this._delta / (this._map.options.wheelPxPerZoomLevel * 4), d3 = 4 * Math.log(2 / (1 + Math.exp(-Math.abs(d2)))) / Math.LN2, d4 = snap ? Math.ceil(d3 / snap) * snap : d3, delta = map2._limitZoom(zoom2 + (this._delta > 0 ? d4 : -d4)) - zoom2; 9314 this._delta = 0; 9315 this._startTime = null; 9316 if (!delta) { 9317 return; 9318 } 9319 if (map2.options.scrollWheelZoom === "center") { 9320 map2.setZoom(zoom2 + delta); 9321 } else { 9322 map2.setZoomAround(this._lastMousePos, zoom2 + delta); 9323 } 9324 } 9325 }); 9326 Map2.addInitHook("addHandler", "scrollWheelZoom", ScrollWheelZoom); 9327 var tapHoldDelay = 600; 9328 Map2.mergeOptions({ 9329 // @section Touch interaction options 9330 // @option tapHold: Boolean 9331 // Enables simulation of `contextmenu` event, default is `true` for mobile Safari. 9332 tapHold: Browser.touchNative && Browser.safari && Browser.mobile, 9333 // @option tapTolerance: Number = 15 9334 // The max number of pixels a user can shift his finger during touch 9335 // for it to be considered a valid tap. 9336 tapTolerance: 15 9337 }); 9338 var TapHold = Handler.extend({ 9339 addHooks: function() { 9340 on(this._map._container, "touchstart", this._onDown, this); 9341 }, 9342 removeHooks: function() { 9343 off(this._map._container, "touchstart", this._onDown, this); 9344 }, 9345 _onDown: function(e) { 9346 clearTimeout(this._holdTimeout); 9347 if (e.touches.length !== 1) { 9348 return; 9349 } 9350 var first = e.touches[0]; 9351 this._startPos = this._newPos = new Point(first.clientX, first.clientY); 9352 this._holdTimeout = setTimeout(bind(function() { 9353 this._cancel(); 9354 if (!this._isTapValid()) { 9355 return; 9356 } 9357 on(document, "touchend", preventDefault); 9358 on(document, "touchend touchcancel", this._cancelClickPrevent); 9359 this._simulateEvent("contextmenu", first); 9360 }, this), tapHoldDelay); 9361 on(document, "touchend touchcancel contextmenu", this._cancel, this); 9362 on(document, "touchmove", this._onMove, this); 9363 }, 9364 _cancelClickPrevent: function cancelClickPrevent() { 9365 off(document, "touchend", preventDefault); 9366 off(document, "touchend touchcancel", cancelClickPrevent); 9367 }, 9368 _cancel: function() { 9369 clearTimeout(this._holdTimeout); 9370 off(document, "touchend touchcancel contextmenu", this._cancel, this); 9371 off(document, "touchmove", this._onMove, this); 9372 }, 9373 _onMove: function(e) { 9374 var first = e.touches[0]; 9375 this._newPos = new Point(first.clientX, first.clientY); 9376 }, 9377 _isTapValid: function() { 9378 return this._newPos.distanceTo(this._startPos) <= this._map.options.tapTolerance; 9379 }, 9380 _simulateEvent: function(type, e) { 9381 var simulatedEvent = new MouseEvent(type, { 9382 bubbles: true, 9383 cancelable: true, 9384 view: window, 9385 // detail: 1, 9386 screenX: e.screenX, 9387 screenY: e.screenY, 9388 clientX: e.clientX, 9389 clientY: e.clientY 9390 // button: 2, 9391 // buttons: 2 9392 }); 9393 simulatedEvent._simulated = true; 9394 e.target.dispatchEvent(simulatedEvent); 9395 } 9396 }); 9397 Map2.addInitHook("addHandler", "tapHold", TapHold); 9398 Map2.mergeOptions({ 9399 // @section Touch interaction options 9400 // @option touchZoom: Boolean|String = * 9401 // Whether the map can be zoomed by touch-dragging with two fingers. If 9402 // passed `'center'`, it will zoom to the center of the view regardless of 9403 // where the touch events (fingers) were. Enabled for touch-capable web 9404 // browsers. 9405 touchZoom: Browser.touch, 9406 // @option bounceAtZoomLimits: Boolean = true 9407 // Set it to false if you don't want the map to zoom beyond min/max zoom 9408 // and then bounce back when pinch-zooming. 9409 bounceAtZoomLimits: true 9410 }); 9411 var TouchZoom = Handler.extend({ 9412 addHooks: function() { 9413 addClass(this._map._container, "leaflet-touch-zoom"); 9414 on(this._map._container, "touchstart", this._onTouchStart, this); 9415 }, 9416 removeHooks: function() { 9417 removeClass(this._map._container, "leaflet-touch-zoom"); 9418 off(this._map._container, "touchstart", this._onTouchStart, this); 9419 }, 9420 _onTouchStart: function(e) { 9421 var map2 = this._map; 9422 if (!e.touches || e.touches.length !== 2 || map2._animatingZoom || this._zooming) { 9423 return; 9424 } 9425 var p1 = map2.mouseEventToContainerPoint(e.touches[0]), p2 = map2.mouseEventToContainerPoint(e.touches[1]); 9426 this._centerPoint = map2.getSize()._divideBy(2); 9427 this._startLatLng = map2.containerPointToLatLng(this._centerPoint); 9428 if (map2.options.touchZoom !== "center") { 9429 this._pinchStartLatLng = map2.containerPointToLatLng(p1.add(p2)._divideBy(2)); 9430 } 9431 this._startDist = p1.distanceTo(p2); 9432 this._startZoom = map2.getZoom(); 9433 this._moved = false; 9434 this._zooming = true; 9435 map2._stop(); 9436 on(document, "touchmove", this._onTouchMove, this); 9437 on(document, "touchend touchcancel", this._onTouchEnd, this); 9438 preventDefault(e); 9439 }, 9440 _onTouchMove: function(e) { 9441 if (!e.touches || e.touches.length !== 2 || !this._zooming) { 9442 return; 9443 } 9444 var map2 = this._map, p1 = map2.mouseEventToContainerPoint(e.touches[0]), p2 = map2.mouseEventToContainerPoint(e.touches[1]), scale2 = p1.distanceTo(p2) / this._startDist; 9445 this._zoom = map2.getScaleZoom(scale2, this._startZoom); 9446 if (!map2.options.bounceAtZoomLimits && (this._zoom < map2.getMinZoom() && scale2 < 1 || this._zoom > map2.getMaxZoom() && scale2 > 1)) { 9447 this._zoom = map2._limitZoom(this._zoom); 9448 } 9449 if (map2.options.touchZoom === "center") { 9450 this._center = this._startLatLng; 9451 if (scale2 === 1) { 9452 return; 9453 } 9454 } else { 9455 var delta = p1._add(p2)._divideBy(2)._subtract(this._centerPoint); 9456 if (scale2 === 1 && delta.x === 0 && delta.y === 0) { 9457 return; 9458 } 9459 this._center = map2.unproject(map2.project(this._pinchStartLatLng, this._zoom).subtract(delta), this._zoom); 9460 } 9461 if (!this._moved) { 9462 map2._moveStart(true, false); 9463 this._moved = true; 9464 } 9465 cancelAnimFrame(this._animRequest); 9466 var moveFn = bind(map2._move, map2, this._center, this._zoom, { pinch: true, round: false }, void 0); 9467 this._animRequest = requestAnimFrame(moveFn, this, true); 9468 preventDefault(e); 9469 }, 9470 _onTouchEnd: function() { 9471 if (!this._moved || !this._zooming) { 9472 this._zooming = false; 9473 return; 9474 } 9475 this._zooming = false; 9476 cancelAnimFrame(this._animRequest); 9477 off(document, "touchmove", this._onTouchMove, this); 9478 off(document, "touchend touchcancel", this._onTouchEnd, this); 9479 if (this._map.options.zoomAnimation) { 9480 this._map._animateZoom(this._center, this._map._limitZoom(this._zoom), true, this._map.options.zoomSnap); 9481 } else { 9482 this._map._resetView(this._center, this._map._limitZoom(this._zoom)); 9483 } 9484 } 9485 }); 9486 Map2.addInitHook("addHandler", "touchZoom", TouchZoom); 9487 Map2.BoxZoom = BoxZoom; 9488 Map2.DoubleClickZoom = DoubleClickZoom; 9489 Map2.Drag = Drag; 9490 Map2.Keyboard = Keyboard; 9491 Map2.ScrollWheelZoom = ScrollWheelZoom; 9492 Map2.TapHold = TapHold; 9493 Map2.TouchZoom = TouchZoom; 9494 exports2.Bounds = Bounds; 9495 exports2.Browser = Browser; 9496 exports2.CRS = CRS; 9497 exports2.Canvas = Canvas; 9498 exports2.Circle = Circle; 9499 exports2.CircleMarker = CircleMarker; 9500 exports2.Class = Class; 9501 exports2.Control = Control; 9502 exports2.DivIcon = DivIcon; 9503 exports2.DivOverlay = DivOverlay; 9504 exports2.DomEvent = DomEvent; 9505 exports2.DomUtil = DomUtil; 9506 exports2.Draggable = Draggable; 9507 exports2.Evented = Evented; 9508 exports2.FeatureGroup = FeatureGroup; 9509 exports2.GeoJSON = GeoJSON; 9510 exports2.GridLayer = GridLayer; 9511 exports2.Handler = Handler; 9512 exports2.Icon = Icon; 9513 exports2.ImageOverlay = ImageOverlay; 9514 exports2.LatLng = LatLng; 9515 exports2.LatLngBounds = LatLngBounds; 9516 exports2.Layer = Layer; 9517 exports2.LayerGroup = LayerGroup; 9518 exports2.LineUtil = LineUtil; 9519 exports2.Map = Map2; 9520 exports2.Marker = Marker; 9521 exports2.Mixin = Mixin; 9522 exports2.Path = Path; 9523 exports2.Point = Point; 9524 exports2.PolyUtil = PolyUtil; 9525 exports2.Polygon = Polygon; 9526 exports2.Polyline = Polyline; 9527 exports2.Popup = Popup; 9528 exports2.PosAnimation = PosAnimation; 9529 exports2.Projection = index; 9530 exports2.Rectangle = Rectangle; 9531 exports2.Renderer = Renderer; 9532 exports2.SVG = SVG; 9533 exports2.SVGOverlay = SVGOverlay; 9534 exports2.TileLayer = TileLayer; 9535 exports2.Tooltip = Tooltip; 9536 exports2.Transformation = Transformation; 9537 exports2.Util = Util; 9538 exports2.VideoOverlay = VideoOverlay; 9539 exports2.bind = bind; 9540 exports2.bounds = toBounds; 9541 exports2.canvas = canvas; 9542 exports2.circle = circle; 9543 exports2.circleMarker = circleMarker2; 9544 exports2.control = control; 9545 exports2.divIcon = divIcon2; 9546 exports2.extend = extend; 9547 exports2.featureGroup = featureGroup2; 9548 exports2.geoJSON = geoJSON; 9549 exports2.geoJson = geoJson; 9550 exports2.gridLayer = gridLayer; 9551 exports2.icon = icon; 9552 exports2.imageOverlay = imageOverlay; 9553 exports2.latLng = toLatLng; 9554 exports2.latLngBounds = toLatLngBounds; 9555 exports2.layerGroup = layerGroup2; 9556 exports2.map = createMap2; 9557 exports2.marker = marker2; 9558 exports2.point = toPoint; 9559 exports2.polygon = polygon; 9560 exports2.polyline = polyline; 9561 exports2.popup = popup; 9562 exports2.rectangle = rectangle; 9563 exports2.setOptions = setOptions; 9564 exports2.stamp = stamp; 9565 exports2.svg = svg; 9566 exports2.svgOverlay = svgOverlay; 9567 exports2.tileLayer = tileLayer2; 9568 exports2.tooltip = tooltip; 9569 exports2.transformation = toTransformation; 9570 exports2.version = version; 9571 exports2.videoOverlay = videoOverlay; 9572 var oldL = window.L; 9573 exports2.noConflict = function() { 9574 window.L = oldL; 9575 return this; 9576 }; 9577 window.L = exports2; 9578 }); 9579 } 9580}); 9581 9582// main.ts 9583var main_exports = {}; 9584__export(main_exports, { 9585 default: () => CalendarViewerPlugin 9586}); 9587module.exports = __toCommonJS(main_exports); 9588var import_obsidian2 = require("obsidian"); 9589 9590// calendarView.ts 9591var import_obsidian = require("obsidian"); 9592 9593// parser.ts 9594var MONTHS = { 9595 january: 0, 9596 february: 1, 9597 march: 2, 9598 april: 3, 9599 may: 4, 9600 june: 5, 9601 july: 6, 9602 august: 7, 9603 september: 8, 9604 october: 9, 9605 november: 10, 9606 december: 11 9607}; 9608var URL_RE = /^https?:\/\/\S+$/; 9609var GEO_RE = /^geo:\s*(-?\d+\.?\d*),\s*(-?\d+\.?\d*)$/; 9610var DATE_WEEKDAY_DD_MONTH_YYYY = /^(?:monday|tuesday|wednesday|thursday|friday|saturday|sunday)\s+(\d{1,2})\s+(\w+)\s+(\d{4})/i; 9611var DATE_WEEKDAY_COMMA_MONTH_DD = /^(?:monday|tuesday|wednesday|thursday|friday|saturday|sunday),?\s+(\w+)\s+(\d{1,2})(?:,?\s+(\d{4}))?(?:,?\s+(.+))?/i; 9612var DATE_MONTH_DD_YYYY = /^(\w+)\s+(\d{1,2})(?:,?\s+(\d{4}))?/i; 9613function parseMonth(s) { 9614 const m = MONTHS[s.toLowerCase()]; 9615 return m !== void 0 ? m : null; 9616} 9617function inferYear(month, day) { 9618 const now = /* @__PURE__ */ new Date(); 9619 const thisYear = now.getFullYear(); 9620 const candidate = new Date(thisYear, month, day); 9621 if (candidate.getTime() < now.getTime() - 30 * 24 * 60 * 60 * 1e3) { 9622 return thisYear + 1; 9623 } 9624 return thisYear; 9625} 9626function tryParseDate(line) { 9627 var _a; 9628 let m; 9629 m = line.match(DATE_WEEKDAY_DD_MONTH_YYYY); 9630 if (m) { 9631 const day = parseInt(m[1], 10); 9632 const month = parseMonth(m[2]); 9633 const year = parseInt(m[3], 10); 9634 if (month !== null) { 9635 return { date: new Date(year, month, day) }; 9636 } 9637 } 9638 m = line.match(DATE_WEEKDAY_COMMA_MONTH_DD); 9639 if (m) { 9640 const month = parseMonth(m[1]); 9641 const day = parseInt(m[2], 10); 9642 if (month !== null) { 9643 const year = m[3] ? parseInt(m[3], 10) : inferYear(month, day); 9644 const rawTime = ((_a = m[4]) == null ? void 0 : _a.trim()) || void 0; 9645 return { date: new Date(year, month, day), rawTime }; 9646 } 9647 } 9648 m = line.match(DATE_MONTH_DD_YYYY); 9649 if (m) { 9650 const month = parseMonth(m[1]); 9651 const day = parseInt(m[2], 10); 9652 if (month !== null) { 9653 const year = m[3] ? parseInt(m[3], 10) : inferYear(month, day); 9654 return { date: new Date(year, month, day) }; 9655 } 9656 } 9657 return null; 9658} 9659function extractMarkdownLinks(line) { 9660 const parts = []; 9661 const re = /\[([^\]]+)\]\(([^)]+)\)/g; 9662 let lastIndex = 0; 9663 let match; 9664 while ((match = re.exec(line)) !== null) { 9665 if (match.index > lastIndex) { 9666 const before = line.slice(lastIndex, match.index).trim(); 9667 if (before) 9668 parts.push({ text: before }); 9669 } 9670 parts.push({ text: match[1], url: match[2] }); 9671 lastIndex = re.lastIndex; 9672 } 9673 if (lastIndex < line.length) { 9674 const rest = line.slice(lastIndex).trim(); 9675 if (rest) 9676 parts.push({ text: rest }); 9677 } 9678 return parts; 9679} 9680function parseSoldOut(titleLine) { 9681 const soldOutRe = /\s*\(sold\s*out\)\s*/i; 9682 if (soldOutRe.test(titleLine)) { 9683 return { title: titleLine.replace(soldOutRe, "").trim(), soldOut: true }; 9684 } 9685 return { title: titleLine.trim(), soldOut: false }; 9686} 9687function parseEvents(markdown) { 9688 const lines = markdown.split("\n"); 9689 const events = []; 9690 const blocks = []; 9691 let currentBlock = null; 9692 for (let i = 0; i < lines.length; i++) { 9693 const line = lines[i]; 9694 if (/^[*\-]\s/.test(line)) { 9695 currentBlock = { 9696 lines: [line.replace(/^[*\-]\s+/, "").trim()], 9697 startLine: i, 9698 endLine: i 9699 }; 9700 blocks.push(currentBlock); 9701 } else if (currentBlock && /^\t[*\-]\s/.test(line)) { 9702 currentBlock.lines.push(line.replace(/^\t[*\-]\s+/, "").trim()); 9703 currentBlock.endLine = i; 9704 } else if (currentBlock && /^\s{2,}[*\-]\s/.test(line)) { 9705 currentBlock.lines.push(line.replace(/^\s+[*\-]\s+/, "").trim()); 9706 currentBlock.endLine = i; 9707 } 9708 } 9709 for (const block of blocks) { 9710 if (block.lines.length === 0) 9711 continue; 9712 const firstLine = block.lines[0]; 9713 if (!firstLine) 9714 continue; 9715 const event = { 9716 soldOut: false, 9717 startLine: block.startLine, 9718 endLine: block.endLine 9719 }; 9720 if (URL_RE.test(firstLine)) { 9721 event.url = firstLine; 9722 } else { 9723 const { title, soldOut } = parseSoldOut(firstLine); 9724 event.title = title; 9725 event.soldOut = soldOut; 9726 } 9727 const subs = block.lines.slice(1); 9728 let dateFound = false; 9729 for (const sub of subs) { 9730 if (!sub) 9731 continue; 9732 const geoMatch = sub.match(GEO_RE); 9733 if (geoMatch) { 9734 event.lat = parseFloat(geoMatch[1]); 9735 event.lng = parseFloat(geoMatch[2]); 9736 continue; 9737 } 9738 if (!dateFound) { 9739 const parsed = tryParseDate(sub); 9740 if (parsed) { 9741 event.date = parsed.date; 9742 event.rawTime = parsed.rawTime; 9743 dateFound = true; 9744 continue; 9745 } 9746 } 9747 if (!event.title) { 9748 const { title, soldOut } = parseSoldOut(sub); 9749 event.title = title; 9750 event.soldOut = soldOut; 9751 continue; 9752 } 9753 if (/\[.*\]\(.*\)/.test(sub)) { 9754 const allParts = extractMarkdownLinks(sub); 9755 const links = allParts.filter((p) => p.url); 9756 if (links.length >= 1) { 9757 event.venue = links[0].text; 9758 event.venueUrl = links[0].url; 9759 } 9760 if (links.length >= 2) { 9761 event.location = links[1].text; 9762 event.locationUrl = links[1].url; 9763 } 9764 continue; 9765 } 9766 if (event.notes) { 9767 event.notes += "\n" + sub; 9768 } else { 9769 event.notes = sub; 9770 } 9771 } 9772 if (event.title && event.date) { 9773 events.push(event); 9774 } 9775 } 9776 return events; 9777} 9778 9779// renderer.ts 9780var DAY_NAMES = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"]; 9781var MONTH_NAMES = [ 9782 "January", 9783 "February", 9784 "March", 9785 "April", 9786 "May", 9787 "June", 9788 "July", 9789 "August", 9790 "September", 9791 "October", 9792 "November", 9793 "December" 9794]; 9795function mondayIndex(date) { 9796 return (date.getDay() + 6) % 7; 9797} 9798function eventsByDay(events, year, month) { 9799 const map2 = /* @__PURE__ */ new Map(); 9800 for (const ev of events) { 9801 if (ev.date.getFullYear() === year && ev.date.getMonth() === month) { 9802 const day = ev.date.getDate(); 9803 if (!map2.has(day)) 9804 map2.set(day, []); 9805 map2.get(day).push(ev); 9806 } 9807 } 9808 return map2; 9809} 9810function createPopover(container, ev, anchor) { 9811 const popover = container.createDiv({ cls: "cal-popover" }); 9812 const titleEl = popover.createDiv({ cls: "cal-popover-title" }); 9813 if (ev.url) { 9814 const a = titleEl.createEl("a", { text: ev.title, href: ev.url }); 9815 a.setAttr("target", "_blank"); 9816 } else { 9817 titleEl.setText(ev.title); 9818 } 9819 if (ev.soldOut) { 9820 titleEl.createSpan({ cls: "cal-sold-out", text: " (Sold out)" }); 9821 } 9822 const dateStr = ev.date.toLocaleDateString("en-US", { 9823 weekday: "long", 9824 month: "long", 9825 day: "numeric", 9826 year: "numeric" 9827 }); 9828 const dateLine = ev.rawTime ? `${dateStr}, ${ev.rawTime}` : dateStr; 9829 popover.createDiv({ cls: "cal-popover-date", text: dateLine }); 9830 if (ev.venue) { 9831 const venueEl = popover.createDiv({ cls: "cal-popover-venue" }); 9832 if (ev.venueUrl) { 9833 const a = venueEl.createEl("a", { text: ev.venue, href: ev.venueUrl }); 9834 a.setAttr("target", "_blank"); 9835 } else { 9836 venueEl.setText(ev.venue); 9837 } 9838 if (ev.location) { 9839 venueEl.appendText(", "); 9840 if (ev.locationUrl) { 9841 const a = venueEl.createEl("a", { text: ev.location, href: ev.locationUrl }); 9842 a.setAttr("target", "_blank"); 9843 } else { 9844 venueEl.appendText(ev.location); 9845 } 9846 } 9847 } 9848 if (ev.notes) { 9849 popover.createDiv({ cls: "cal-popover-notes", text: ev.notes }); 9850 } 9851 positionPopover(popover, anchor, container); 9852 return popover; 9853} 9854function positionPopover(popover, anchor, container) { 9855 requestAnimationFrame(() => { 9856 const anchorRect = anchor.getBoundingClientRect(); 9857 const containerRect = container.getBoundingClientRect(); 9858 const popoverRect = popover.getBoundingClientRect(); 9859 let top = anchorRect.bottom - containerRect.top + 4; 9860 let left = anchorRect.left - containerRect.left; 9861 if (left + popoverRect.width > containerRect.width) { 9862 left = containerRect.width - popoverRect.width - 8; 9863 } 9864 if (left < 0) 9865 left = 4; 9866 if (top + popoverRect.height > containerRect.height) { 9867 top = anchorRect.top - containerRect.top - popoverRect.height - 4; 9868 } 9869 popover.style.top = `${top}px`; 9870 popover.style.left = `${left}px`; 9871 }); 9872} 9873function renderCalendar(container, currentMonth, events, callbacks, selectedEvent) { 9874 container.empty(); 9875 container.addClass("cal-container"); 9876 const year = currentMonth.getFullYear(); 9877 const month = currentMonth.getMonth(); 9878 const header = container.createDiv({ cls: "cal-header" }); 9879 const prevBtn = header.createEl("button", { cls: "cal-nav-btn", text: "\u2039" }); 9880 prevBtn.addEventListener("click", callbacks.onPrevMonth); 9881 header.createSpan({ cls: "cal-month-label", text: `${MONTH_NAMES[month]} ${year}` }); 9882 const nextBtn = header.createEl("button", { cls: "cal-nav-btn", text: "\u203A" }); 9883 nextBtn.addEventListener("click", callbacks.onNextMonth); 9884 const dowRow = container.createDiv({ cls: "cal-dow-row" }); 9885 for (const name of DAY_NAMES) { 9886 dowRow.createDiv({ cls: "cal-dow-cell", text: name }); 9887 } 9888 const grid = container.createDiv({ cls: "cal-grid" }); 9889 const firstOfMonth = new Date(year, month, 1); 9890 const daysInMonth = new Date(year, month + 1, 0).getDate(); 9891 const startOffset = mondayIndex(firstOfMonth); 9892 const dayEvents = eventsByDay(events, year, month); 9893 let activePopover = null; 9894 const removePopover = () => { 9895 if (activePopover) { 9896 activePopover.remove(); 9897 activePopover = null; 9898 } 9899 }; 9900 const chipMap = /* @__PURE__ */ new Map(); 9901 function eventKey(ev) { 9902 return `${ev.title}::${ev.date.getTime()}`; 9903 } 9904 for (let i = 0; i < startOffset; i++) { 9905 grid.createDiv({ cls: "cal-cell cal-cell-empty" }); 9906 } 9907 for (let day = 1; day <= daysInMonth; day++) { 9908 const cell = grid.createDiv({ cls: "cal-cell" }); 9909 cell.createDiv({ cls: "cal-day-number", text: String(day) }); 9910 const eventsForDay = dayEvents.get(day); 9911 if (eventsForDay) { 9912 cell.addClass("cal-cell-has-events"); 9913 const eventsContainer = cell.createDiv({ cls: "cal-cell-events" }); 9914 for (const ev of eventsForDay) { 9915 const isSelected = selectedEvent && eventKey(ev) === eventKey(selectedEvent); 9916 const cls = [ 9917 "cal-event-chip", 9918 ev.soldOut ? "cal-event-sold-out" : "", 9919 isSelected ? "cal-event-selected" : "" 9920 ].filter(Boolean).join(" "); 9921 const chip = eventsContainer.createDiv({ cls, text: ev.title }); 9922 chipMap.set(eventKey(ev), chip); 9923 chip.addEventListener("click", (e) => { 9924 e.stopPropagation(); 9925 container.querySelectorAll(".cal-event-selected").forEach( 9926 (el) => el.classList.remove("cal-event-selected") 9927 ); 9928 chip.classList.add("cal-event-selected"); 9929 callbacks.onEventClick(ev); 9930 }); 9931 chip.addEventListener("mouseenter", () => { 9932 removePopover(); 9933 activePopover = createPopover(container, ev, chip); 9934 }); 9935 chip.addEventListener("mouseleave", (e) => { 9936 setTimeout(() => { 9937 if (activePopover && !activePopover.contains(e.relatedTarget)) { 9938 removePopover(); 9939 } 9940 }, 100); 9941 }); 9942 } 9943 } 9944 const now = /* @__PURE__ */ new Date(); 9945 if (year === now.getFullYear() && month === now.getMonth() && day === now.getDate()) { 9946 cell.addClass("cal-cell-today"); 9947 } 9948 } 9949 const totalCells = startOffset + daysInMonth; 9950 const trailingCells = totalCells % 7 === 0 ? 0 : 7 - totalCells % 7; 9951 for (let i = 0; i < trailingCells; i++) { 9952 grid.createDiv({ cls: "cal-cell cal-cell-empty" }); 9953 } 9954 container.addEventListener("click", (e) => { 9955 if (activePopover && !activePopover.contains(e.target)) { 9956 removePopover(); 9957 } 9958 }); 9959 if (selectedEvent) { 9960 const selectedChip = chipMap.get(eventKey(selectedEvent)); 9961 if (selectedChip) { 9962 requestAnimationFrame(() => selectedChip.scrollIntoView({ block: "nearest" })); 9963 } 9964 } 9965 const controller = { 9966 selectEvent(event) { 9967 container.querySelectorAll(".cal-event-selected").forEach( 9968 (el) => el.classList.remove("cal-event-selected") 9969 ); 9970 if (!event) 9971 return false; 9972 const evYear = event.date.getFullYear(); 9973 const evMonth = event.date.getMonth(); 9974 if (evYear !== year || evMonth !== month) { 9975 return true; 9976 } 9977 const chip = chipMap.get(eventKey(event)); 9978 if (chip) { 9979 chip.classList.add("cal-event-selected"); 9980 chip.scrollIntoView({ block: "nearest" }); 9981 } 9982 return false; 9983 } 9984 }; 9985 return controller; 9986} 9987 9988// mapRenderer.ts 9989var L2 = __toESM(require_leaflet_src()); 9990function getAccentColor() { 9991 return getComputedStyle(document.body).getPropertyValue("--interactive-accent").trim() || "#7f6df2"; 9992} 9993function darkenHex(hex, factor) { 9994 const h = hex.replace("#", ""); 9995 const r = Math.max(0, Math.round(parseInt(h.slice(0, 2), 16) * (1 - factor))); 9996 const g = Math.max(0, Math.round(parseInt(h.slice(2, 4), 16) * (1 - factor))); 9997 const b = Math.max(0, Math.round(parseInt(h.slice(4, 6), 16) * (1 - factor))); 9998 return `#${r.toString(16).padStart(2, "0")}${g.toString(16).padStart(2, "0")}${b.toString(16).padStart(2, "0")}`; 9999} 10000function createPinIcon(soldOut) { 10001 const accent = getAccentColor(); 10002 const fill = soldOut ? "#999" : accent; 10003 const stroke = soldOut ? "#777" : darkenHex(accent, 0.25); 10004 const svg = `<svg xmlns="http://www.w3.org/2000/svg" width="28" height="40" viewBox="0 0 28 40"><path d="M14 0C6.3 0 0 6.3 0 14c0 10.5 14 26 14 26s14-15.5 14-26C28 6.3 21.7 0 14 0z" fill="${fill}" stroke="${stroke}" stroke-width="1.5"/><circle cx="14" cy="14" r="5" fill="white" opacity="0.85"/></svg>`; 10005 return L2.divIcon({ 10006 html: svg, 10007 className: "cal-pin-icon", 10008 iconSize: [28, 40], 10009 iconAnchor: [14, 40], 10010 popupAnchor: [0, -36] 10011 }); 10012} 10013var WATERCOLOR_URL = "https://watercolormaps.collection.cooperhewitt.org/tile/watercolor/{z}/{x}/{y}.jpg"; 10014var LABELS_URL = "https://{s}.basemaps.cartocdn.com/light_only_labels/{z}/{x}/{y}{r}.png"; 10015var TILE_ATTRIBUTION = 'Map tiles by <a href="https://stamen.com/">Stamen Design</a>, hosted by <a href="https://collection.cooperhewitt.org/">Cooper Hewitt</a>. Labels by <a href="https://carto.com/">CARTO</a>. Data &copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a>'; 10016function popupContent(events) { 10017 return events.map((ev) => { 10018 const title = ev.url ? `<a href="${ev.url}" target="_blank">${escapeHtml(ev.title)}</a>` : escapeHtml(ev.title); 10019 const soldOut = ev.soldOut ? ' <span class="cal-sold-out">(Sold out)</span>' : ""; 10020 const date = ev.date.toLocaleDateString("en-US", { 10021 weekday: "short", 10022 month: "short", 10023 day: "numeric" 10024 }); 10025 const time = ev.rawTime ? `, ${escapeHtml(ev.rawTime)}` : ""; 10026 const venue = ev.venue ? `<br><span class="cal-popup-venue">${escapeHtml(ev.venue)}</span>` : ""; 10027 return `<div class="cal-popup-event"><strong>${title}</strong>${soldOut}<br>${date}${time}${venue}</div>`; 10028 }).join(""); 10029} 10030function escapeHtml(s) { 10031 return s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;"); 10032} 10033function groupByLocation(events) { 10034 const groups = /* @__PURE__ */ new Map(); 10035 for (const ev of events) { 10036 if (ev.lat === void 0 || ev.lng === void 0) 10037 continue; 10038 const key = `${ev.lat.toFixed(6)},${ev.lng.toFixed(6)}`; 10039 if (!groups.has(key)) 10040 groups.set(key, []); 10041 groups.get(key).push(ev); 10042 } 10043 return groups; 10044} 10045function createMap(container, events, callbacks) { 10046 if (!document.getElementById("leaflet-css")) { 10047 const link = document.createElement("style"); 10048 link.id = "leaflet-css"; 10049 link.textContent = leafletCss(); 10050 document.head.appendChild(link); 10051 } 10052 const mapDiv = container.createDiv({ cls: "cal-map" }); 10053 const map2 = L2.map(mapDiv, { 10054 zoomControl: true, 10055 attributionControl: true 10056 }); 10057 L2.tileLayer(WATERCOLOR_URL, { 10058 attribution: TILE_ATTRIBUTION, 10059 maxZoom: 19 10060 }).addTo(map2); 10061 L2.tileLayer(LABELS_URL, { 10062 maxZoom: 19, 10063 subdomains: "abcd", 10064 pane: "overlayPane" 10065 }).addTo(map2); 10066 const markerLayer = L2.layerGroup().addTo(map2); 10067 let markerMap = /* @__PURE__ */ new Map(); 10068 let selectedMarker = null; 10069 let highlightCircle = null; 10070 function clearHighlight() { 10071 if (highlightCircle) { 10072 highlightCircle.remove(); 10073 highlightCircle = null; 10074 } 10075 selectedMarker = null; 10076 } 10077 function highlightMarker(marker2) { 10078 clearHighlight(); 10079 selectedMarker = marker2; 10080 const latlng = marker2.getLatLng(); 10081 highlightCircle = L2.circleMarker(latlng, { 10082 radius: 18, 10083 color: "var(--interactive-accent, #7b6cd9)", 10084 fillColor: "var(--interactive-accent, #7b6cd9)", 10085 fillOpacity: 0.2, 10086 weight: 2, 10087 className: "cal-marker-highlight" 10088 }).addTo(map2); 10089 } 10090 function buildMarkers(evts) { 10091 markerLayer.clearLayers(); 10092 markerMap.clear(); 10093 clearHighlight(); 10094 const groups = groupByLocation(evts); 10095 for (const [key, groupEvents] of groups) { 10096 const [lat, lng] = key.split(",").map(Number); 10097 const allSoldOut = groupEvents.every((e) => e.soldOut); 10098 const marker2 = L2.marker([lat, lng], { 10099 icon: createPinIcon(allSoldOut) 10100 }); 10101 marker2.bindPopup(popupContent(groupEvents), { 10102 maxWidth: 240, 10103 className: "cal-map-popup" 10104 }); 10105 marker2.on("click", () => { 10106 highlightMarker(marker2); 10107 callbacks.onMarkerClick(groupEvents[0]); 10108 }); 10109 marker2.addTo(markerLayer); 10110 markerMap.set(key, { marker: marker2, events: groupEvents }); 10111 } 10112 } 10113 buildMarkers(events); 10114 function doFitBounds() { 10115 const allMarkers = Array.from(markerMap.values()).map((m) => m.marker); 10116 if (allMarkers.length === 0) { 10117 map2.setView([20, 0], 2); 10118 return; 10119 } 10120 if (allMarkers.length === 1) { 10121 map2.setView(allMarkers[0].getLatLng(), 13); 10122 return; 10123 } 10124 const group = L2.featureGroup(allMarkers); 10125 map2.fitBounds(group.getBounds().pad(0.15)); 10126 } 10127 setTimeout(() => { 10128 map2.invalidateSize(); 10129 doFitBounds(); 10130 }, 50); 10131 const controller = { 10132 updateMarkers(evts, fit = true) { 10133 buildMarkers(evts); 10134 if (fit) { 10135 doFitBounds(); 10136 } 10137 }, 10138 selectEvent(event) { 10139 if (!event) { 10140 clearHighlight(); 10141 map2.closePopup(); 10142 return; 10143 } 10144 if (event.lat === void 0 || event.lng === void 0) 10145 return; 10146 const key = `${event.lat.toFixed(6)},${event.lng.toFixed(6)}`; 10147 const entry = markerMap.get(key); 10148 if (entry) { 10149 highlightMarker(entry.marker); 10150 entry.marker.openPopup(); 10151 map2.panTo(entry.marker.getLatLng(), { animate: true }); 10152 } 10153 }, 10154 fitBounds() { 10155 doFitBounds(); 10156 }, 10157 invalidateSize() { 10158 map2.invalidateSize(); 10159 }, 10160 destroy() { 10161 map2.remove(); 10162 } 10163 }; 10164 return controller; 10165} 10166function leafletCss() { 10167 return ` 10168/* Leaflet 1.9.4 \u2014 essential styles */ 10169.leaflet-pane, .leaflet-tile, .leaflet-marker-icon, .leaflet-marker-shadow, 10170.leaflet-tile-container, .leaflet-pane > svg, .leaflet-pane > canvas, 10171.leaflet-zoom-box, .leaflet-image-layer, .leaflet-layer { 10172 position: absolute; left: 0; top: 0; 10173} 10174.leaflet-container { overflow: hidden; } 10175.leaflet-tile, .leaflet-marker-icon, .leaflet-marker-shadow { user-select: none; -webkit-user-select: none; } 10176.leaflet-tile::selection { background: transparent; } 10177.leaflet-safari .leaflet-tile { image-rendering: -webkit-optimize-contrast; } 10178.leaflet-safari .leaflet-tile-container { width: 1600px; height: 1600px; -webkit-transform-origin: 0 0; } 10179.leaflet-marker-icon, .leaflet-marker-shadow { display: block; } 10180.leaflet-container .leaflet-overlay-pane svg { max-width: none !important; max-height: none !important; } 10181.leaflet-container .leaflet-marker-pane img, 10182.leaflet-container .leaflet-shadow-pane img, 10183.leaflet-container .leaflet-tile-pane img, 10184.leaflet-container img.leaflet-image-layer, 10185.leaflet-container .leaflet-tile { max-width: none !important; max-height: none !important; width: auto; padding: 0; } 10186.leaflet-container.leaflet-touch-zoom { touch-action: pan-x pan-y; } 10187.leaflet-container.leaflet-touch-drag { touch-action: none; touch-action: pinch-zoom; } 10188.leaflet-container.leaflet-touch-drag.leaflet-touch-zoom { touch-action: none; } 10189.leaflet-container { -webkit-tap-highlight-color: transparent; } 10190.leaflet-container a { -webkit-tap-highlight-color: rgba(51,181,229,0.4); } 10191.leaflet-tile { filter: inherit; visibility: hidden; } 10192.leaflet-tile-loaded { visibility: inherit; } 10193.leaflet-zoom-box { width: 0; height: 0; box-sizing: border-box; z-index: 800; } 10194.leaflet-overlay-pane svg { -moz-user-select: none; } 10195.leaflet-pane { z-index: 400; } 10196.leaflet-tile-pane { z-index: 200; } 10197.leaflet-overlay-pane { z-index: 400; } 10198.leaflet-shadow-pane { z-index: 500; } 10199.leaflet-marker-pane { z-index: 600; } 10200.leaflet-tooltip-pane { z-index: 650; } 10201.leaflet-popup-pane { z-index: 700; } 10202.leaflet-map-pane canvas { z-index: 100; } 10203.leaflet-map-pane svg { z-index: 200; } 10204.leaflet-vml-shape { width: 1px; height: 1px; } 10205.lvml { behavior: url(#default#VML); display: inline-block; position: absolute; } 10206.leaflet-control { position: relative; z-index: 800; pointer-events: visiblePainted; pointer-events: auto; } 10207.leaflet-top, .leaflet-bottom { position: absolute; z-index: 1000; pointer-events: none; } 10208.leaflet-top { top: 0; } 10209.leaflet-right { right: 0; } 10210.leaflet-bottom { bottom: 0; } 10211.leaflet-left { left: 0; } 10212.leaflet-control { float: left; clear: both; } 10213.leaflet-right .leaflet-control { float: right; } 10214.leaflet-top .leaflet-control { margin-top: 10px; } 10215.leaflet-bottom .leaflet-control { margin-bottom: 10px; } 10216.leaflet-left .leaflet-control { margin-left: 10px; } 10217.leaflet-right .leaflet-control { margin-right: 10px; } 10218.leaflet-fade-anim .leaflet-popup { opacity: 1; transition: opacity 0.2s linear; } 10219.leaflet-fade-anim .leaflet-map-pane .leaflet-popup { opacity: 1; } 10220.leaflet-zoom-animated { transform-origin: 0 0; } 10221.leaflet-zoom-anim .leaflet-zoom-animated { will-change: transform; transition: transform 0.25s cubic-bezier(0,0,0.25,1); } 10222.leaflet-zoom-anim .leaflet-tile, .leaflet-pan-anim .leaflet-tile { transition: none; } 10223.leaflet-zoom-anim .leaflet-zoom-hide { visibility: hidden; } 10224.leaflet-interactive { cursor: pointer; } 10225.leaflet-grab { cursor: grab; } 10226.leaflet-crosshair, .leaflet-crosshair .leaflet-interactive { cursor: crosshair; } 10227.leaflet-popup-pane, .leaflet-control { cursor: auto; } 10228.leaflet-dragging .leaflet-grab, .leaflet-dragging .leaflet-grab .leaflet-interactive, 10229.leaflet-dragging .leaflet-marker-draggable { cursor: move; cursor: grabbing; } 10230.leaflet-marker-icon, .leaflet-marker-shadow, .leaflet-image-layer, 10231.leaflet-pane > svg path, .leaflet-tile-container { pointer-events: none; } 10232.leaflet-marker-icon.leaflet-interactive, .leaflet-image-layer.leaflet-interactive, 10233.leaflet-pane > svg path.leaflet-interactive, svg.leaflet-image-layer.leaflet-interactive path { pointer-events: visiblePainted; pointer-events: auto; } 10234.leaflet-container a.leaflet-active { outline: 2px solid orange; } 10235.leaflet-zoom-box { border: 2px dotted #38f; background: rgba(255,255,255,0.5); } 10236.leaflet-bar { box-shadow: 0 1px 5px rgba(0,0,0,0.65); border-radius: 4px; } 10237.leaflet-bar a, .leaflet-bar a:hover { background-color: #fff; border-bottom: 1px solid #ccc; width: 26px; height: 26px; line-height: 26px; display: block; text-align: center; text-decoration: none; color: black; } 10238.leaflet-bar a, .leaflet-control-layers-toggle { background-position: 50% 50%; background-repeat: no-repeat; display: block; } 10239.leaflet-bar a:hover, .leaflet-bar a:focus { background-color: #f4f4f4; } 10240.leaflet-bar a:first-child { border-top-left-radius: 4px; border-top-right-radius: 4px; } 10241.leaflet-bar a:last-child { border-bottom-left-radius: 4px; border-bottom-right-radius: 4px; border-bottom: none; } 10242.leaflet-bar a.leaflet-disabled { cursor: default; background-color: #f4f4f4; color: #bbb; } 10243.leaflet-touch .leaflet-bar a { width: 30px; height: 30px; line-height: 30px; } 10244.leaflet-touch .leaflet-bar a:first-child { border-top-left-radius: 2px; border-top-right-radius: 2px; } 10245.leaflet-touch .leaflet-bar a:last-child { border-bottom-left-radius: 2px; border-bottom-right-radius: 2px; } 10246.leaflet-control-zoom-in, .leaflet-control-zoom-out { font: bold 18px 'Lucida Console', Monaco, monospace; text-indent: 1px; } 10247.leaflet-touch .leaflet-control-zoom-in, .leaflet-touch .leaflet-control-zoom-out { font-size: 22px; } 10248.leaflet-control-layers { box-shadow: 0 1px 5px rgba(0,0,0,0.4); background: #fff; border-radius: 5px; } 10249.leaflet-control-layers-toggle { background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABoAAAAaCAQAAAADQ4RFAAACf0lEQVR4AY1TA5AVURDsmc22bdu2bdu2bdu2bdu2bZ3v3P3f1t2q7mL6AQBSNG03EMHQF8MwEiNxBmfxMz7FJ/iROI0zOINfCF2RC9lRG8MxFRdwSw0EcBu3cBEXcRt31ECq4AFMI3RFLVRHZ3RBf0zAHJzBDdzEHdzFfTzAQzzCYzzBUzzDc7zASwjBV3iDb/AO3+MHkiSf8dPf4mt8ha/xJb7AF/gcn+FTfIJP8DE+wof4AO/jPbyLd/A23sKbeAOv43W8hlfxCl7Gy3gJL+IFPIdncQ+P8RRP8ARP8BSvwGOAkBm50AsrsR+38BCv4BLO4DTu4h6e4SVe4CXe4CU+wPt4D+/iHbyNt/Am3sDreA2v4hW8jJfwIl7Ac3gWz+AuHuMpnuAJnuI5eMQjP4cLauA+buE6LuMCzuE0TuE8ruEObuMhHuEWHuABnuExnuIRnuAJnuEZ+FfFa3gVr+BlvISX8CJewPN4Ds/iGdzFYzzFEzzBUzzHSwjBV/gG3+I7fI8fCKLkM37+W3yNr/A1vsSX+AKf4zN8ik/wMT7Ch/gA7+M9vIt38DbewsN4C2/iDbyO1/EaXsUreBkv4SW8iBfwPJ7DU7iHx3iKx3iCJ3iGF+CRH+Ey7uEOLuMMzuMiruEGbuI2buMO7uE+HuIhHuMpHuMJnuI5XoJHvIJX8Rpexat4Ba/gZbyEl/ACnsdzeBbP4C4e4yme4Ame4hlewZ8+DJmRC30xAbtxDQ/xGu7gNE7hMu7gLu7jAR7iMZ7gKR7jCZ7hOV6AAG/hTbyBt/AWHsJbeAsP4x28i3fwNt7CG3gdr+E1vIpX8DJewot4Ac/hWTyNu3iEx3j67w8P/z9FWIJUIAAAAABJRU5ErkJggg==); width: 36px; height: 36px; } 10250.leaflet-retina .leaflet-control-layers-toggle { background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADQAAAA0CAQAAABvcdNgAAAEsklEQVR4AWL4TydIhpZK1kAAAABJRU5ErkJggg==); background-size: 26px 26px; } 10251.leaflet-touch .leaflet-control-layers-toggle { width: 44px; height: 44px; } 10252.leaflet-control-layers .leaflet-control-layers-list, .leaflet-control-layers-expanded .leaflet-control-layers-toggle { display: none; } 10253.leaflet-control-layers-expanded .leaflet-control-layers-list { display: block; position: relative; } 10254.leaflet-control-layers-expanded { padding: 6px 10px 6px 6px; color: #333; background: #fff; } 10255.leaflet-control-layers-scrollbar { overflow-y: scroll; overflow-x: hidden; padding-right: 5px; } 10256.leaflet-control-layers-selector { margin-top: 2px; position: relative; top: 1px; } 10257.leaflet-control-layers label { display: block; font-size: 13px; font-size: 1.08333em; } 10258.leaflet-control-layers-separator { height: 0; border-top: 1px solid #ddd; margin: 5px -10px 5px -6px; } 10259.leaflet-default-icon-path { background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABkAAAApCAYAAADAk4LOAAAFgUlEQVR4Aa1XA5BjWRTN2oW17d3YassW17LFNesR17LWtAnOez/J5tsupervised/cLOGeli2lGZjUtMPail370a57y6u6deuj75NAfO/6fOM0M0SUR4JYAAAAABJRU5ErkJggg==); } 10260.leaflet-container .leaflet-control-attribution { background: #fff; background: rgba(255,255,255,0.8); margin: 0; } 10261.leaflet-control-attribution, .leaflet-control-scale-line { padding: 0 5px; color: #333; line-height: 1.4; } 10262.leaflet-control-attribution a { text-decoration: none; } 10263.leaflet-control-attribution a:hover, .leaflet-control-attribution a:focus { text-decoration: underline; } 10264.leaflet-attribution-flag { display: none !important; } 10265.leaflet-left .leaflet-control-scale { margin-left: 5px; } 10266.leaflet-bottom .leaflet-control-scale { margin-bottom: 5px; } 10267.leaflet-control-scale-line { border: 2px solid #777; border-top: none; line-height: 1.1; padding: 2px 5px 1px; white-space: nowrap; overflow: hidden; box-sizing: border-box; background: rgba(255,255,255,0.5); } 10268.leaflet-control-scale-line:not(:first-child) { border-top: 2px solid #777; border-bottom: none; margin-top: -2px; } 10269.leaflet-control-scale-line:not(:first-child):not(:last-child) { border-bottom: 2px solid #777; } 10270.leaflet-touch .leaflet-control-attribution, .leaflet-touch .leaflet-control-layers, 10271.leaflet-touch .leaflet-bar { box-shadow: none; } 10272.leaflet-touch .leaflet-control-layers, .leaflet-touch .leaflet-bar { border: 2px solid rgba(0,0,0,0.2); background-clip: padding-box; } 10273.leaflet-popup { position: absolute; text-align: center; margin-bottom: 20px; } 10274.leaflet-popup-content-wrapper { padding: 1px; text-align: left; border-radius: 12px; } 10275.leaflet-popup-content { margin: 13px 24px 13px 20px; line-height: 1.3; font-size: 13px; font-size: 1.08333em; min-height: 1px; } 10276.leaflet-popup-content p { margin: 17px 0; margin: 1.3em 0; } 10277.leaflet-popup-tip-container { width: 40px; height: 20px; position: absolute; left: 50%; margin-top: -1px; margin-left: -20px; overflow: hidden; pointer-events: none; } 10278.leaflet-popup-tip { width: 17px; height: 17px; padding: 1px; margin: -10px auto 0; pointer-events: auto; transform: rotate(45deg); } 10279.leaflet-popup-content-wrapper, .leaflet-popup-tip { background: white; color: #333; box-shadow: 0 3px 14px rgba(0,0,0,0.4); } 10280.leaflet-container a.leaflet-popup-close-button { position: absolute; top: 0; right: 0; border: none; text-align: center; width: 24px; height: 24px; font: 16px/24px Tahoma, Verdana, sans-serif; color: #757575; text-decoration: none; background: transparent; } 10281.leaflet-container a.leaflet-popup-close-button:hover, .leaflet-container a.leaflet-popup-close-button:focus { color: #585858; } 10282.leaflet-popup-scrolled { overflow: auto; } 10283.leaflet-oldie .leaflet-popup-content-wrapper { -ms-zoom: 1; } 10284.leaflet-oldie .leaflet-popup-tip { width: 24px; margin: 0 auto; -ms-filter: "progid:DXImageTransform.Microsoft.Matrix(M11=0.70710678, M12=0.70710678, M21=-0.70710678, M22=0.70710678)"; filter: progid:DXImageTransform.Microsoft.Matrix(M11=0.70710678, M12=0.70710678, M21=-0.70710678, M22=0.70710678); } 10285.leaflet-oldie .leaflet-control-zoom, .leaflet-oldie .leaflet-control-layers, 10286.leaflet-oldie .leaflet-popup-content-wrapper, .leaflet-oldie .leaflet-popup-tip { border: 1px solid #999; } 10287.leaflet-div-icon { background: #fff; border: 1px solid #666; } 10288.leaflet-tooltip { position: absolute; padding: 6px; background-color: #fff; border: 1px solid #fff; border-radius: 3px; color: #222; white-space: nowrap; user-select: none; pointer-events: none; box-shadow: 0 1px 3px rgba(0,0,0,0.4); } 10289.leaflet-tooltip.leaflet-interactive { cursor: pointer; pointer-events: auto; } 10290.leaflet-tooltip-top:before, .leaflet-tooltip-bottom:before, 10291.leaflet-tooltip-left:before, .leaflet-tooltip-right:before { position: absolute; pointer-events: none; border: 6px solid transparent; background: transparent; content: ""; } 10292.leaflet-tooltip-bottom { margin-top: 6px; } 10293.leaflet-tooltip-top { margin-top: -6px; } 10294.leaflet-tooltip-bottom:before, .leaflet-tooltip-top:before { left: 50%; margin-left: -6px; } 10295.leaflet-tooltip-top:before { bottom: 0; margin-bottom: -12px; border-top-color: #fff; } 10296.leaflet-tooltip-bottom:before { top: 0; margin-top: -12px; margin-left: -6px; border-bottom-color: #fff; } 10297.leaflet-tooltip-left { margin-left: -6px; } 10298.leaflet-tooltip-right { margin-left: 6px; } 10299.leaflet-tooltip-left:before, .leaflet-tooltip-right:before { top: 50%; margin-top: -6px; } 10300.leaflet-tooltip-left:before { right: 0; margin-right: -12px; border-left-color: #fff; } 10301.leaflet-tooltip-right:before { left: 0; margin-left: -12px; border-right-color: #fff; } 10302@media print { .leaflet-control { -webkit-print-color-adjust: exact; print-color-adjust: exact; } } 10303`; 10304} 10305 10306// geocoder.ts 10307var NOMINATIM_URL = "https://nominatim.openstreetmap.org/search"; 10308var RATE_LIMIT_MS = 1100; 10309function buildQuery(event) { 10310 const parts = []; 10311 if (event.venue) 10312 parts.push(event.venue); 10313 if (event.location) 10314 parts.push(event.location); 10315 if (parts.length === 0) 10316 return null; 10317 return parts.join(", "); 10318} 10319function normalizeQuery(query) { 10320 return query.toLowerCase().trim(); 10321} 10322async function fetchGeocode(query) { 10323 try { 10324 console.debug(`[CalendarViewer] Geocoding: "${query}"`); 10325 const params = new URLSearchParams({ 10326 q: query, 10327 format: "json", 10328 limit: "1" 10329 }); 10330 const response = await fetch(`${NOMINATIM_URL}?${params}`, { 10331 headers: { 10332 "User-Agent": "ObsidianCalendarViewer/1.0" 10333 } 10334 }); 10335 if (!response.ok) { 10336 console.warn(`[CalendarViewer] Geocoding failed for "${query}": HTTP ${response.status}`); 10337 return null; 10338 } 10339 const results = await response.json(); 10340 if (results.length > 0) { 10341 const loc = { 10342 lat: parseFloat(results[0].lat), 10343 lng: parseFloat(results[0].lon) 10344 }; 10345 console.debug(`[CalendarViewer] Geocoded "${query}" \u2192 ${loc.lat}, ${loc.lng}`); 10346 return loc; 10347 } 10348 console.warn(`[CalendarViewer] Geocoding returned no results for "${query}"`); 10349 return null; 10350 } catch (e) { 10351 console.warn(`[CalendarViewer] Geocoding error for "${query}":`, e); 10352 return null; 10353 } 10354} 10355function sleep(ms) { 10356 return new Promise((resolve) => setTimeout(resolve, ms)); 10357} 10358async function geocodeEvents(events, onProgress) { 10359 const needsGeocoding = []; 10360 for (const event of events) { 10361 if (event.lat !== void 0 && event.lng !== void 0) { 10362 console.debug(`[CalendarViewer] Skipping "${event.title}" \u2014 already has coords`); 10363 continue; 10364 } 10365 const query = buildQuery(event); 10366 if (!query) { 10367 console.debug(`[CalendarViewer] Skipping "${event.title}" \u2014 no venue/location`); 10368 continue; 10369 } 10370 needsGeocoding.push({ event, query, key: normalizeQuery(query) }); 10371 } 10372 const seen = /* @__PURE__ */ new Set(); 10373 const unique = []; 10374 for (const item of needsGeocoding) { 10375 if (!seen.has(item.key)) { 10376 seen.add(item.key); 10377 unique.push(item); 10378 } 10379 } 10380 if (unique.length > 0) { 10381 console.debug(`[CalendarViewer] Geocoding ${unique.length} unique location(s)...`); 10382 } 10383 const newlyGeocoded = []; 10384 for (let i = 0; i < unique.length; i++) { 10385 const { query, key } = unique[i]; 10386 if (i > 0) { 10387 await sleep(RATE_LIMIT_MS); 10388 } 10389 const result = await fetchGeocode(query); 10390 if (result) { 10391 for (const item of needsGeocoding) { 10392 if (item.key === key) { 10393 item.event.lat = result.lat; 10394 item.event.lng = result.lng; 10395 newlyGeocoded.push(item.event); 10396 } 10397 } 10398 } 10399 onProgress(); 10400 } 10401 return newlyGeocoded; 10402} 10403 10404// calendarView.ts 10405var VIEW_TYPE_CALENDAR = "calendar-viewer"; 10406var CalendarView = class extends import_obsidian.ItemView { 10407 constructor(leaf, plugin) { 10408 super(leaf); 10409 this.events = []; 10410 this.selectedEvent = null; 10411 this.hasUserNavigated = false; 10412 this.calendarEl = null; 10413 this.mapEl = null; 10414 this.calendarController = null; 10415 this.mapController = null; 10416 this.resizeObserver = null; 10417 /** Guard: true while we're writing geo lines back to the doc */ 10418 this.isWritingGeoLines = false; 10419 this.eventsFingerprint = ""; 10420 this.cursorPollInterval = null; 10421 this.lastCursorLine = -1; 10422 /** 10423 * Re-parse the active note, geocode events, and re-render everything. 10424 */ 10425 this.refresh = (0, import_obsidian.debounce)(async () => { 10426 if (this.isWritingGeoLines) 10427 return; 10428 const file = this.app.workspace.getActiveFile(); 10429 if (file) { 10430 const content = await this.app.vault.read(file); 10431 this.events = parseEvents(content); 10432 if (this.events.length > 0 && !this.hasUserNavigated) { 10433 this.jumpToNearestMonth(); 10434 } 10435 } else { 10436 this.events = []; 10437 } 10438 const newFingerprint = this.computeFingerprint(this.events); 10439 const eventsChanged = newFingerprint !== this.eventsFingerprint; 10440 this.eventsFingerprint = newFingerprint; 10441 this.selectedEvent = null; 10442 this.renderCalendar(); 10443 if (eventsChanged) { 10444 this.initOrUpdateMap(); 10445 await this.geocodeAndUpdateMap(); 10446 } 10447 }, 300, true); 10448 this.plugin = plugin; 10449 this.currentMonth = /* @__PURE__ */ new Date(); 10450 this.currentMonth.setDate(1); 10451 } 10452 getViewType() { 10453 return VIEW_TYPE_CALENDAR; 10454 } 10455 getDisplayText() { 10456 return "Calendar"; 10457 } 10458 getIcon() { 10459 return "calendar"; 10460 } 10461 async onOpen() { 10462 const content = this.containerEl.children[1]; 10463 content.empty(); 10464 content.addClass("cal-view-root"); 10465 this.calendarEl = content.createDiv({ cls: "cal-pane-top" }); 10466 this.mapEl = content.createDiv({ cls: "cal-pane-bottom" }); 10467 this.resizeObserver = new ResizeObserver(() => { 10468 var _a; 10469 (_a = this.mapController) == null ? void 0 : _a.invalidateSize(); 10470 }); 10471 this.resizeObserver.observe(this.mapEl); 10472 this.cursorPollInterval = setInterval(() => { 10473 this.pollCursorPosition(); 10474 }, 200); 10475 await this.refresh(); 10476 } 10477 async onClose() { 10478 var _a, _b; 10479 if (this.cursorPollInterval !== null) { 10480 clearInterval(this.cursorPollInterval); 10481 this.cursorPollInterval = null; 10482 } 10483 (_a = this.mapController) == null ? void 0 : _a.destroy(); 10484 this.mapController = null; 10485 (_b = this.resizeObserver) == null ? void 0 : _b.disconnect(); 10486 this.resizeObserver = null; 10487 } 10488 /** 10489 * Reset the user-navigated flag when switching notes. 10490 */ 10491 resetNavigation() { 10492 this.hasUserNavigated = false; 10493 } 10494 // ── Private ── 10495 /** 10496 * Compute a simple fingerprint of the event list (titles + dates). 10497 * Used to detect whether events actually changed between refreshes. 10498 */ 10499 computeFingerprint(events) { 10500 return events.map((e) => { 10501 var _a, _b; 10502 return `${e.title}::${e.date.getTime()}::${(_a = e.venue) != null ? _a : ""}::${(_b = e.location) != null ? _b : ""}`; 10503 }).join("|"); 10504 } 10505 jumpToNearestMonth() { 10506 const now = /* @__PURE__ */ new Date(); 10507 const sorted = [...this.events].sort( 10508 (a, b) => a.date.getTime() - b.date.getTime() 10509 ); 10510 const upcoming = sorted.find((e) => e.date.getTime() >= now.getTime()); 10511 const target = upcoming != null ? upcoming : sorted[sorted.length - 1]; 10512 if (target) { 10513 this.currentMonth = new Date(target.date.getFullYear(), target.date.getMonth(), 1); 10514 } 10515 } 10516 renderCalendar() { 10517 if (!this.calendarEl) 10518 return; 10519 this.calendarController = renderCalendar( 10520 this.calendarEl, 10521 this.currentMonth, 10522 this.events, 10523 { 10524 onPrevMonth: () => { 10525 this.hasUserNavigated = true; 10526 this.currentMonth = new Date( 10527 this.currentMonth.getFullYear(), 10528 this.currentMonth.getMonth() - 1, 10529 1 10530 ); 10531 this.renderCalendar(); 10532 }, 10533 onNextMonth: () => { 10534 this.hasUserNavigated = true; 10535 this.currentMonth = new Date( 10536 this.currentMonth.getFullYear(), 10537 this.currentMonth.getMonth() + 1, 10538 1 10539 ); 10540 this.renderCalendar(); 10541 }, 10542 onEventClick: (event) => { 10543 var _a; 10544 this.selectedEvent = event; 10545 (_a = this.mapController) == null ? void 0 : _a.selectEvent(event); 10546 this.scrollEditorToEvent(event); 10547 } 10548 }, 10549 this.selectedEvent 10550 ); 10551 } 10552 initOrUpdateMap() { 10553 if (!this.mapEl) 10554 return; 10555 if (this.mapController) { 10556 this.mapController.updateMarkers(this.events); 10557 } else { 10558 this.mapController = createMap(this.mapEl, this.events, { 10559 onMarkerClick: (event) => { 10560 this.selectedEvent = event; 10561 const evMonth = event.date.getMonth(); 10562 const evYear = event.date.getFullYear(); 10563 if (evYear !== this.currentMonth.getFullYear() || evMonth !== this.currentMonth.getMonth()) { 10564 this.hasUserNavigated = true; 10565 this.currentMonth = new Date(evYear, evMonth, 1); 10566 } 10567 this.renderCalendar(); 10568 this.scrollEditorToEvent(event); 10569 } 10570 }); 10571 } 10572 } 10573 async geocodeAndUpdateMap() { 10574 if (this.events.length === 0) 10575 return; 10576 const newlyGeocoded = await geocodeEvents( 10577 this.events, 10578 // onProgress: update the map with new markers but don't re-fit bounds 10579 () => { 10580 var _a; 10581 (_a = this.mapController) == null ? void 0 : _a.updateMarkers(this.events, false); 10582 } 10583 ); 10584 if (newlyGeocoded.length > 0) { 10585 await this.writeGeoLinesToDoc(newlyGeocoded); 10586 } 10587 } 10588 /** 10589 * Poll the active editor's cursor position and sync selection 10590 * to the event whose block contains the cursor line. 10591 */ 10592 pollCursorPosition() { 10593 const mdView = this.app.workspace.getActiveViewOfType(import_obsidian.MarkdownView); 10594 if (!mdView) 10595 return; 10596 const cursor = mdView.editor.getCursor(); 10597 const line = cursor.line; 10598 if (line === this.lastCursorLine) 10599 return; 10600 this.lastCursorLine = line; 10601 this.selectEventByLine(line); 10602 } 10603 /** 10604 * Find the event whose startLine..endLine range contains the given 10605 * line number and select it in the calendar and map. 10606 */ 10607 selectEventByLine(line) { 10608 var _a, _b, _c, _d; 10609 const event = this.events.find( 10610 (e) => e.startLine !== void 0 && e.endLine !== void 0 && line >= e.startLine && line <= e.endLine 10611 ); 10612 if (!event) { 10613 if (this.selectedEvent) { 10614 this.selectedEvent = null; 10615 (_a = this.calendarController) == null ? void 0 : _a.selectEvent(null); 10616 (_b = this.mapController) == null ? void 0 : _b.selectEvent(null); 10617 } 10618 return; 10619 } 10620 if (this.selectedEvent && this.selectedEvent.title === event.title && this.selectedEvent.date.getTime() === event.date.getTime()) { 10621 return; 10622 } 10623 this.selectedEvent = event; 10624 const evMonth = event.date.getMonth(); 10625 const evYear = event.date.getFullYear(); 10626 if (evYear !== this.currentMonth.getFullYear() || evMonth !== this.currentMonth.getMonth()) { 10627 this.currentMonth = new Date(evYear, evMonth, 1); 10628 this.renderCalendar(); 10629 } else { 10630 (_c = this.calendarController) == null ? void 0 : _c.selectEvent(event); 10631 } 10632 (_d = this.mapController) == null ? void 0 : _d.selectEvent(event); 10633 } 10634 /** 10635 * Scroll the editor to the event's start line. 10636 */ 10637 scrollEditorToEvent(event) { 10638 if (event.startLine === void 0) 10639 return; 10640 const mdView = this.app.workspace.getActiveViewOfType(import_obsidian.MarkdownView); 10641 if (!mdView) 10642 return; 10643 const editor = mdView.editor; 10644 editor.setCursor({ line: event.startLine, ch: 0 }); 10645 editor.scrollIntoView( 10646 { 10647 from: { line: event.startLine, ch: 0 }, 10648 to: { line: event.startLine, ch: 0 } 10649 }, 10650 true 10651 ); 10652 this.lastCursorLine = event.startLine; 10653 } 10654 /** 10655 * Write "geo: lat,lng" sub-bullets back into the active document 10656 * for events that were just geocoded. Inserts after each event's 10657 * last line (endLine), processing from bottom to top so line 10658 * numbers don't shift for earlier insertions. 10659 */ 10660 async writeGeoLinesToDoc(newlyGeocoded) { 10661 const file = this.app.workspace.getActiveFile(); 10662 if (!file) 10663 return; 10664 const toWrite = newlyGeocoded.filter( 10665 (e) => e.endLine !== void 0 && e.lat !== void 0 && e.lng !== void 0 10666 ); 10667 if (toWrite.length === 0) 10668 return; 10669 toWrite.sort((a, b) => b.endLine - a.endLine); 10670 this.isWritingGeoLines = true; 10671 try { 10672 await this.app.vault.process(file, (content) => { 10673 var _a; 10674 const lines = content.split("\n"); 10675 for (const event of toWrite) { 10676 const insertAfter = event.endLine; 10677 const geoLine = ` * geo: ${event.lat},${event.lng}`; 10678 let alreadyHasGeo = false; 10679 for (let i = (_a = event.startLine) != null ? _a : insertAfter; i <= insertAfter; i++) { 10680 if (/^\t[*\-]\s+geo:\s/.test(lines[i]) || /^\s{2,}[*\-]\s+geo:\s/.test(lines[i])) { 10681 alreadyHasGeo = true; 10682 break; 10683 } 10684 } 10685 if (alreadyHasGeo) 10686 continue; 10687 lines.splice(insertAfter + 1, 0, geoLine); 10688 } 10689 return lines.join("\n"); 10690 }); 10691 } finally { 10692 setTimeout(() => { 10693 this.isWritingGeoLines = false; 10694 }, 500); 10695 } 10696 } 10697}; 10698 10699// main.ts 10700var CalendarViewerPlugin = class extends import_obsidian2.Plugin { 10701 constructor() { 10702 super(...arguments); 10703 this.lastActiveFilePath = null; 10704 } 10705 async onload() { 10706 this.registerView( 10707 VIEW_TYPE_CALENDAR, 10708 (leaf) => new CalendarView(leaf, this) 10709 ); 10710 this.addRibbonIcon("calendar", "Open Calendar View", () => { 10711 this.activateView(); 10712 }); 10713 this.addCommand({ 10714 id: "open-calendar-view", 10715 name: "Open Calendar View", 10716 callback: () => { 10717 this.activateView(); 10718 } 10719 }); 10720 this.registerEvent( 10721 this.app.workspace.on("active-leaf-change", () => { 10722 var _a; 10723 const activeFile = this.app.workspace.getActiveFile(); 10724 const activePath = (_a = activeFile == null ? void 0 : activeFile.path) != null ? _a : null; 10725 if (activePath === this.lastActiveFilePath) 10726 return; 10727 this.lastActiveFilePath = activePath; 10728 const view = this.getCalendarView(); 10729 if (view) { 10730 view.resetNavigation(); 10731 view.refresh(); 10732 } 10733 }) 10734 ); 10735 this.registerEvent( 10736 this.app.vault.on("modify", (file) => { 10737 const activeFile = this.app.workspace.getActiveFile(); 10738 if (activeFile && file.path === activeFile.path) { 10739 const view = this.getCalendarView(); 10740 if (view) { 10741 view.refresh(); 10742 } 10743 } 10744 }) 10745 ); 10746 } 10747 onunload() { 10748 } 10749 getCalendarView() { 10750 const leaves = this.app.workspace.getLeavesOfType(VIEW_TYPE_CALENDAR); 10751 if (leaves.length > 0) { 10752 return leaves[0].view; 10753 } 10754 return null; 10755 } 10756 async activateView() { 10757 const existing = this.app.workspace.getLeavesOfType(VIEW_TYPE_CALENDAR); 10758 if (existing.length === 0) { 10759 const leaf = this.app.workspace.getRightLeaf(false); 10760 if (leaf) { 10761 await leaf.setViewState({ 10762 type: VIEW_TYPE_CALENDAR, 10763 active: true 10764 }); 10765 this.app.workspace.revealLeaf(leaf); 10766 } 10767 } else { 10768 this.app.workspace.revealLeaf(existing[0]); 10769 } 10770 } 10771}; 10772/*! Bundled license information: 10773 10774leaflet/dist/leaflet-src.js: 10775 (* @preserve 10776 * Leaflet 1.9.4, a JS library for interactive maps. https://leafletjs.com 10777 * (c) 2010-2023 Vladimir Agafonkin, (c) 2010-2011 CloudMade 10778 *) 10779*/