diff --git a/plugin.php b/plugin.php index 76993d09d..0fbcd407d 100644 --- a/plugin.php +++ b/plugin.php @@ -334,10 +334,6 @@ function customFieldCallback( $pDI, $format, $limitEllipsis, $meta_key ) { } }); -add_filter('oo_is_detailpage_redirection', function($value) { - return $value; -}); - $pEstateRedirection = apply_filters('oo_is_detailpage_redirection', true); add_action('parse_request', function(WP $pWP) use ($pDI, $pEstateRedirection) { diff --git a/plugin/Controller/EstateDetailUrl.php b/plugin/Controller/EstateDetailUrl.php index e41ef6769..648223e62 100644 --- a/plugin/Controller/EstateDetailUrl.php +++ b/plugin/Controller/EstateDetailUrl.php @@ -146,13 +146,9 @@ public function getUrlWithEstateTitle(int $estateId, string $title = null, strin $urlLsSwitcher = $urlElements['scheme'] . '://' . $urlElements['host'] . $newPath . '/' . $urlTemp; if (!empty($getParameters)) { - if ($pEstateRedirection) { - $urlLsSwitcher .= ''; - } elseif(!$tickerUrlHasTitleFlag) { - $urlLsSwitcher .= '?' . http_build_query($getParameters); - } + $urlLsSwitcher = add_query_arg($getParameters,$urlLsSwitcher); } return $urlLsSwitcher; } -} \ No newline at end of file +} diff --git a/plugin/Utility/Redirector.php b/plugin/Utility/Redirector.php index 580955486..9cde7ec07 100644 --- a/plugin/Utility/Redirector.php +++ b/plugin/Utility/Redirector.php @@ -117,6 +117,6 @@ public function getUri() public function getCurrentLink(): string { global $wp; - return home_url(add_query_arg($_GET, trailingslashit($wp->request))); + return home_url(add_query_arg($_GET, $wp->request)); } } diff --git a/tests/TestClassEstateDetailUrl.php b/tests/TestClassEstateDetailUrl.php index f5d652310..75943deba 100644 --- a/tests/TestClassEstateDetailUrl.php +++ b/tests/TestClassEstateDetailUrl.php @@ -153,7 +153,7 @@ public function testGetUrlWithFilterTrueAndEnableOptionShowTitleWithParameter() $title = 'test-title'; $pInstance = $this->_pContainer->get(EstateDetailUrl::class); $url = 'https://www.onoffice.de/detail/123?lang=en'; - $expectedUrl = 'https://www.onoffice.de/detail/123-test-title'; + $expectedUrl = 'https://www.onoffice.de/detail/123-test-title?lang=en'; $this->assertEquals($expectedUrl, $pInstance->getUrlWithEstateTitle($estateId, $title, $url, false, $pEstateRedirection)); } @@ -169,7 +169,7 @@ public function testGetUrlWithFilterTrueAndDisableOptionShowTitleWithParameter() $title = 'test-title'; $pInstance = $this->_pContainer->get(EstateDetailUrl::class); $url = 'https://www.onoffice.de/detail/123?lang=en'; - $expectedUrl = 'https://www.onoffice.de/detail/123'; + $expectedUrl = 'https://www.onoffice.de/detail/123?lang=en'; $this->assertEquals($expectedUrl, $pInstance->getUrlWithEstateTitle($estateId, $title, $url, false, $pEstateRedirection)); } @@ -210,16 +210,16 @@ public function testGetUrlWithFilterFalseAndEnableOptionShowTitleWithParameter() $expectedUrlHasTitleWithoutParamster = 'https://www.onoffice.de/detail/123-test-title'; $this->assertEquals($expectedUrlHasTitleWithoutParamster, $pInstance->getUrlWithEstateTitle($estateId, $title, $urlHasTitleWithoutParamster, false, $pEstateRedirection)); - $urlWithoutTitle = 'https://www.onoffice.de/detail/123/?lang=en'; + $urlWithoutTitle = 'https://www.onoffice.de/detail/123?lang=en'; $expectedUrlWithoutTitle = 'https://www.onoffice.de/detail/123?lang=en'; $this->assertEquals($expectedUrlWithoutTitle, $pInstance->getUrlWithEstateTitle($estateId, $title, $urlWithoutTitle, false, $pEstateRedirection)); - $urlHasTitleHasParamster = 'https://www.onoffice.de/detail/123-test-title'; - $expectedUrlHasTitleHasParamster = 'https://www.onoffice.de/detail/123-test-title'; - $this->assertEquals($expectedUrlHasTitleHasParamster, $pInstance->getUrlWithEstateTitle($estateId, $title, $urlHasTitleHasParamster, true, $pEstateRedirection)); + $urlHasTitleHasParameter = 'https://www.onoffice.de/detail/123-test-title?lang=en'; + $expectedUrlHasTitleHasParameter = 'https://www.onoffice.de/detail/123-test-title?lang=en'; + $this->assertEquals($expectedUrlHasTitleHasParameter, $pInstance->getUrlWithEstateTitle($estateId, $title, $urlHasTitleHasParameter, true, $pEstateRedirection)); - $urlHasTitle = 'https://www.onoffice.de/detail/123-test-title/?lang=en'; - $expectedUrlHasTitle = 'https://www.onoffice.de/detail/123-test-title'; + $urlHasTitle = 'https://www.onoffice.de/detail/123-test-title?lang=en'; + $expectedUrlHasTitle = 'https://www.onoffice.de/detail/123-test-title?lang=en'; $this->assertEquals($expectedUrlHasTitle, $pInstance->getUrlWithEstateTitle($estateId, $title, $urlHasTitle, true, $pEstateRedirection)); } -} \ No newline at end of file +} diff --git a/tests/TestClassRedirectIfOldUrl.php b/tests/TestClassRedirectIfOldUrl.php index 7d8e39021..c49d90108 100644 --- a/tests/TestClassRedirectIfOldUrl.php +++ b/tests/TestClassRedirectIfOldUrl.php @@ -67,7 +67,7 @@ public function stestInstance() */ public function testGetCurrentLink() { - $this->assertEquals( 'http://example.org/detail-view/123/', $this->_pRedirectIfOldUrl->getCurrentLink() ); + $this->assertEquals( 'http://example.org/detail-view/123', $this->_pRedirectIfOldUrl->getCurrentLink() ); } /** diff --git a/third_party/leaflet/leaflet-src.esm.js b/third_party/leaflet/leaflet-src.esm.js index 1a513452e..8f91d5ddf 100644 --- a/third_party/leaflet/leaflet-src.esm.js +++ b/third_party/leaflet/leaflet-src.esm.js @@ -1,9 +1,9 @@ /* @preserve - * Leaflet 1.3.4+Detached: 0e566b2ad5e696ba9f79a9d48a7e51c8f4892441.0e566b2, a JS library for interactive maps. http://leafletjs.com - * (c) 2010-2018 Vladimir Agafonkin, (c) 2010-2011 CloudMade + * Leaflet 1.9.4, a JS library for interactive maps. https://leafletjs.com + * (c) 2010-2023 Vladimir Agafonkin, (c) 2010-2011 CloudMade */ -var version = "1.3.4+HEAD.0e566b2"; +var version = "1.9.4"; /* * @namespace Util @@ -11,9 +11,6 @@ var version = "1.3.4+HEAD.0e566b2"; * Various utility functions, used by Leaflet internally. */ -var freeze = Object.freeze; -Object.freeze = function (obj) { return obj; }; - // @function extend(dest: Object, src?: Object): Object // Merges the properties of the `src` object (or multiple objects) into `dest` object and returns the latter. Has an `L.extend` shortcut. function extend(dest) { @@ -30,7 +27,7 @@ function extend(dest) { // @function create(proto: Object, properties?: Object): Object // Compatibility polyfill for [Object.create](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/create) -var create = Object.create || (function () { +var create$2 = Object.create || (function () { function F() {} return function (proto) { F.prototype = proto; @@ -62,10 +59,10 @@ var lastId = 0; // @function stamp(obj: Object): Number // Returns the unique ID of an object, assigning it one if it doesn't have it. function stamp(obj) { - /*eslint-disable */ - obj._leaflet_id = obj._leaflet_id || ++lastId; + if (!('_leaflet_id' in obj)) { + obj['_leaflet_id'] = ++lastId; + } return obj._leaflet_id; - /* eslint-enable */ } // @function throttle(fn: Function, time: Number, context: Object): Function @@ -118,10 +115,13 @@ function wrapNum(x, range, includeMax) { // Returns a function which always returns `false`. function falseFn() { return false; } -// @function formatNum(num: Number, digits?: Number): Number -// Returns the number `num` rounded to `digits` decimals, or to 6 decimals by default. -function formatNum(num, digits) { - var pow = Math.pow(10, (digits === undefined ? 6 : digits)); +// @function formatNum(num: Number, precision?: Number|false): Number +// Returns the number `num` rounded with specified `precision`. +// The default `precision` value is 6 decimal places. +// `false` can be passed to skip any processing (can be useful to avoid round-off errors). +function formatNum(num, precision) { + if (precision === false) { return num; } + var pow = Math.pow(10, precision === undefined ? 6 : precision); return Math.round(num * pow) / pow; } @@ -140,8 +140,8 @@ function splitWords(str) { // @function setOptions(obj: Object, options: Object): Object // Merges the given properties to the `options` of the `obj` object, returning the resulting options. See `Class options`. Has an `L.setOptions` shortcut. function setOptions(obj, options) { - if (!obj.hasOwnProperty('options')) { - obj.options = obj.options ? create(obj.options) : {}; + if (!Object.prototype.hasOwnProperty.call(obj, 'options')) { + obj.options = obj.options ? create$2(obj.options) : {}; } for (var i in options) { obj.options[i] = options[i]; @@ -162,7 +162,7 @@ function getParamString(obj, existingUrl, uppercase) { return ((!existingUrl || existingUrl.indexOf('?') === -1) ? '?' : '&') + params.join('&'); } -var templateRe = /\{ *([\w_-]+) *\}/g; +var templateRe = /\{ *([\w_ -]+) *\}/g; // @function template(str: String, data: Object): String // Simple templating facility, accepts a template string of the form `'Hello {a}, {b}'` @@ -204,7 +204,7 @@ function indexOf(array, el) { // mobile devices (by setting image `src` to this string). var emptyImageUrl = 'data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs='; -// inspired by http://paulirish.com/2011/requestanimationframe-for-smart-animating/ +// inspired by https://paulirish.com/2011/requestanimationframe-for-smart-animating/ function getPrefixed(name) { return window['webkit' + name] || window['moz' + name] || window['ms' + name]; @@ -245,33 +245,32 @@ function cancelAnimFrame(id) { if (id) { cancelFn.call(window, id); } -} - +} -var Util = (Object.freeze || Object)({ - freeze: freeze, - extend: extend, - create: create, - bind: bind, - lastId: lastId, - stamp: stamp, - throttle: throttle, - wrapNum: wrapNum, - falseFn: falseFn, - formatNum: formatNum, - trim: trim, - splitWords: splitWords, - setOptions: setOptions, - getParamString: getParamString, - template: template, - isArray: isArray, - indexOf: indexOf, - emptyImageUrl: emptyImageUrl, - requestFn: requestFn, - cancelFn: cancelFn, - requestAnimFrame: requestAnimFrame, - cancelAnimFrame: cancelAnimFrame -}); +var Util = { + __proto__: null, + extend: extend, + create: create$2, + bind: bind, + get lastId () { return lastId; }, + stamp: stamp, + throttle: throttle, + wrapNum: wrapNum, + falseFn: falseFn, + formatNum: formatNum, + trim: trim, + splitWords: splitWords, + setOptions: setOptions, + getParamString: getParamString, + template: template, + isArray: isArray, + indexOf: indexOf, + emptyImageUrl: emptyImageUrl, + requestFn: requestFn, + cancelFn: cancelFn, + requestAnimFrame: requestAnimFrame, + cancelAnimFrame: cancelAnimFrame +}; // @class Class // @aka L.Class @@ -290,6 +289,8 @@ Class.extend = function (props) { // Returns a Javascript function that is a class constructor (to be called with `new`). var NewClass = function () { + setOptions(this); + // call the constructor if (this.initialize) { this.initialize.apply(this, arguments); @@ -301,14 +302,14 @@ Class.extend = function (props) { var parentProto = NewClass.__super__ = this.prototype; - var proto = create(parentProto); + var proto = create$2(parentProto); proto.constructor = NewClass; NewClass.prototype = proto; // inherit parent's statics for (var i in this) { - if (this.hasOwnProperty(i) && i !== 'prototype' && i !== '__super__') { + if (Object.prototype.hasOwnProperty.call(this, i) && i !== 'prototype' && i !== '__super__') { NewClass[i] = this[i]; } } @@ -316,24 +317,25 @@ Class.extend = function (props) { // mix static properties into the class if (props.statics) { extend(NewClass, props.statics); - delete props.statics; } // mix includes into the prototype if (props.includes) { checkDeprecatedMixinEvents(props.includes); extend.apply(null, [proto].concat(props.includes)); - delete props.includes; } + // mix given properties into the prototype + extend(proto, props); + delete proto.statics; + delete proto.includes; + // merge options if (proto.options) { - props.options = extend(create(proto.options), props.options); + proto.options = parentProto.options ? create$2(parentProto.options) : {}; + extend(proto.options, props.options); } - // mix given properties into the prototype - extend(proto, props); - proto._initHooks = []; // add method for calling all hooks @@ -359,7 +361,12 @@ Class.extend = function (props) { // @function include(properties: Object): this // [Includes a mixin](#class-includes) into the current class. Class.include = function (props) { + var parentOptions = this.prototype.options; extend(this.prototype, props); + if (props.options) { + this.prototype.options = parentOptions; + this.mergeOptions(props.options); + } return this; }; @@ -385,6 +392,7 @@ Class.addInitHook = function (fn) { // (Function) || (String, args...) }; function checkDeprecatedMixinEvents(includes) { + /* global L: true */ if (typeof L === 'undefined' || !L || !L.Mixin) { return; } includes = isArray(includes) ? includes : [includes]; @@ -462,11 +470,11 @@ var Events = { * * @alternative * @method off: this - * Removes all listeners to all events on the object. + * Removes all listeners to all events on the object. This includes implicitly attached events. */ off: function (types, fn, context) { - if (!types) { + if (!arguments.length) { // clear all listeners if called without arguments delete this._events; @@ -478,8 +486,13 @@ var Events = { } else { types = splitWords(types); + var removeAll = arguments.length === 1; for (var i = 0, len = types.length; i < len; i++) { - this._off(types[i], fn, context); + if (removeAll) { + this._off(types[i]); + } else { + this._off(types[i], fn, context); + } } } @@ -487,31 +500,30 @@ var Events = { }, // attach listener (without syntactic sugar now) - _on: function (type, fn, context) { - this._events = this._events || {}; + _on: function (type, fn, context, _once) { + if (typeof fn !== 'function') { + console.warn('wrong listener type: ' + typeof fn); + return; + } - /* get/init listeners for type */ - var typeListeners = this._events[type]; - if (!typeListeners) { - typeListeners = []; - this._events[type] = typeListeners; + // check if fn already there + if (this._listens(type, fn, context) !== false) { + return; } if (context === this) { // Less memory footprint. context = undefined; } - var newListener = {fn: fn, ctx: context}, - listeners = typeListeners; - // check if fn already there - for (var i = 0, len = listeners.length; i < len; i++) { - if (listeners[i].fn === fn && listeners[i].ctx === context) { - return; - } + var newListener = {fn: fn, ctx: context}; + if (_once) { + newListener.once = true; } - listeners.push(newListener); + this._events = this._events || {}; + this._events[type] = this._events[type] || []; + this._events[type].push(newListener); }, _off: function (type, fn, context) { @@ -519,53 +531,50 @@ var Events = { i, len; - if (!this._events) { return; } + if (!this._events) { + return; + } listeners = this._events[type]; - if (!listeners) { return; } - if (!fn) { - // Set all removed listeners to noop so they are not called if remove happens in fire - for (i = 0, len = listeners.length; i < len; i++) { - listeners[i].fn = falseFn; + if (arguments.length === 1) { // remove all + if (this._firingCount) { + // Set all removed listeners to noop + // so they are not called if remove happens in fire + for (i = 0, len = listeners.length; i < len; i++) { + listeners[i].fn = falseFn; + } } // clear all listeners for a type if function isn't specified delete this._events[type]; return; } - if (context === this) { - context = undefined; + if (typeof fn !== 'function') { + console.warn('wrong listener type: ' + typeof fn); + return; } - if (listeners) { - - // find fn and remove it - for (i = 0, len = listeners.length; i < len; i++) { - var l = listeners[i]; - if (l.ctx !== context) { continue; } - if (l.fn === fn) { - - // set the removed listener to noop so that's not called if remove happens in fire - l.fn = falseFn; + // find fn and remove it + var index = this._listens(type, fn, context); + if (index !== false) { + var listener = listeners[index]; + if (this._firingCount) { + // set the removed listener to noop so that's not called if remove happens in fire + listener.fn = falseFn; - if (this._firingCount) { - /* copy array in case events are being fired */ - this._events[type] = listeners = listeners.slice(); - } - listeners.splice(i, 1); - - return; - } + /* copy array in case events are being fired */ + this._events[type] = listeners = listeners.slice(); } + listeners.splice(index, 1); } }, // @method fire(type: String, data?: Object, propagate?: Boolean): this - // Fires an event of the specified type. You can optionally provide an data + // Fires an event of the specified type. You can optionally provide a data // object — the first argument of the listener function will contain its // properties. The event can optionally be propagated to event parents. fire: function (type, data, propagate) { @@ -579,12 +588,16 @@ var Events = { if (this._events) { var listeners = this._events[type]; - if (listeners) { this._firingCount = (this._firingCount + 1) || 1; for (var i = 0, len = listeners.length; i < len; i++) { var l = listeners[i]; - l.fn.call(l.ctx || this, event); + // off overwrites l.fn, so we need to copy fn to a var + var fn = l.fn; + if (l.once) { + this.off(type, fn, l.ctx); + } + fn.call(l.ctx || this, event); } this._firingCount--; @@ -599,42 +612,86 @@ var Events = { return this; }, - // @method listens(type: String): Boolean + // @method listens(type: String, propagate?: Boolean): Boolean + // @method listens(type: String, fn: Function, context?: Object, propagate?: Boolean): Boolean // Returns `true` if a particular event type has any listeners attached to it. - listens: function (type, propagate) { + // The verification can optionally be propagated, it will return `true` if parents have the listener attached to it. + listens: function (type, fn, context, propagate) { + if (typeof type !== 'string') { + console.warn('"string" type argument expected'); + } + + // we don't overwrite the input `fn` value, because we need to use it for propagation + var _fn = fn; + if (typeof fn !== 'function') { + propagate = !!fn; + _fn = undefined; + context = undefined; + } + var listeners = this._events && this._events[type]; - if (listeners && listeners.length) { return true; } + if (listeners && listeners.length) { + if (this._listens(type, _fn, context) !== false) { + return true; + } + } if (propagate) { // also check parents for listeners if event propagates for (var id in this._eventParents) { - if (this._eventParents[id].listens(type, propagate)) { return true; } + if (this._eventParents[id].listens(type, fn, context, propagate)) { return true; } + } + } + return false; + }, + + // returns the index (number) or false + _listens: function (type, fn, context) { + if (!this._events) { + return false; + } + + var listeners = this._events[type] || []; + if (!fn) { + return !!listeners.length; + } + + if (context === this) { + // Less memory footprint. + context = undefined; + } + + for (var i = 0, len = listeners.length; i < len; i++) { + if (listeners[i].fn === fn && listeners[i].ctx === context) { + return i; } } return false; + }, // @method once(…): this // Behaves as [`on(…)`](#evented-on), except the listener will only get fired once and then removed. once: function (types, fn, context) { + // types can be a map of types/handlers if (typeof types === 'object') { for (var type in types) { - this.once(type, types[type], fn); + // we don't process space-separated events here for performance; + // it's a hot path since Layer uses the on(obj) syntax + this._on(type, types[type], fn, true); } - return this; - } - var handler = bind(function () { - this - .off(types, fn, context) - .off(types, handler, context); - }, this); + } else { + // types can be a string of space-separated words + types = splitWords(types); + + for (var i = 0, len = types.length; i < len; i++) { + this._on(types[i], fn, context, true); + } + } - // add a listener that's executed once and removed after that - return this - .on(types, fn, context) - .on(types, handler, context); + return this; }, // @method addEventParent(obj: Evented): this @@ -710,7 +767,7 @@ var Evented = Class.extend(Events); * map.panBy(L.point(200, 300)); * ``` * - * Note that `Point` does not inherit from Leafet's `Class` object, + * Note that `Point` does not inherit from Leaflet's `Class` object, * which means new classes can't inherit from it, and new methods * can't be added to it with the `include` function. */ @@ -932,7 +989,7 @@ function toPoint(x, y, round) { * otherBounds.intersects([[10, 10], [40, 60]]); * ``` * - * Note that `Bounds` does not inherit from Leafet's `Class` object, + * Note that `Bounds` does not inherit from Leaflet's `Class` object, * which means new classes can't inherit from it, and new methods * can't be added to it with the `include` function. */ @@ -950,21 +1007,36 @@ function Bounds(a, b) { Bounds.prototype = { // @method extend(point: Point): this // Extends the bounds to contain the given point. - extend: function (point) { // (Point) - point = toPoint(point); + + // @alternative + // @method extend(otherBounds: Bounds): this + // Extend the bounds to contain the given bounds + extend: function (obj) { + var min2, max2; + if (!obj) { return this; } + + if (obj instanceof Point || typeof obj[0] === 'number' || 'x' in obj) { + min2 = max2 = toPoint(obj); + } else { + obj = toBounds(obj); + min2 = obj.min; + max2 = obj.max; + + if (!min2 || !max2) { return this; } + } // @property min: Point // The top left corner of the rectangle. // @property max: Point // The bottom right corner of the rectangle. if (!this.min && !this.max) { - this.min = point.clone(); - this.max = point.clone(); + this.min = min2.clone(); + this.max = max2.clone(); } else { - this.min.x = Math.min(point.x, this.min.x); - this.max.x = Math.max(point.x, this.max.x); - this.min.y = Math.min(point.y, this.min.y); - this.max.y = Math.max(point.y, this.max.y); + this.min.x = Math.min(min2.x, this.min.x); + this.max.x = Math.max(max2.x, this.max.x); + this.min.y = Math.min(min2.y, this.min.y); + this.max.y = Math.max(max2.y, this.max.y); } return this; }, @@ -972,7 +1044,7 @@ Bounds.prototype = { // @method getCenter(round?: Boolean): Point // Returns the center point of the bounds. getCenter: function (round) { - return new Point( + return toPoint( (this.min.x + this.max.x) / 2, (this.min.y + this.max.y) / 2, round); }, @@ -980,13 +1052,13 @@ Bounds.prototype = { // @method getBottomLeft(): Point // Returns the bottom-left point of the bounds. getBottomLeft: function () { - return new Point(this.min.x, this.max.y); + return toPoint(this.min.x, this.max.y); }, // @method getTopRight(): Point // Returns the top-right point of the bounds. getTopRight: function () { // -> Point - return new Point(this.max.x, this.min.y); + return toPoint(this.max.x, this.min.y); }, // @method getTopLeft(): Point @@ -1066,9 +1138,40 @@ Bounds.prototype = { return xOverlaps && yOverlaps; }, + // @method isValid(): Boolean + // Returns `true` if the bounds are properly initialized. isValid: function () { return !!(this.min && this.max); - } + }, + + + // @method pad(bufferRatio: Number): Bounds + // Returns bounds created by extending or retracting the current bounds by a given ratio in each direction. + // For example, a ratio of 0.5 extends the bounds by 50% in each direction. + // Negative values will retract the bounds. + pad: function (bufferRatio) { + var min = this.min, + max = this.max, + heightBuffer = Math.abs(min.x - max.x) * bufferRatio, + widthBuffer = Math.abs(min.y - max.y) * bufferRatio; + + + return toBounds( + toPoint(min.x - heightBuffer, min.y - widthBuffer), + toPoint(max.x + heightBuffer, max.y + widthBuffer)); + }, + + + // @method equals(otherBounds: Bounds): Boolean + // Returns `true` if the rectangle is equivalent to the given bounds. + equals: function (bounds) { + if (!bounds) { return false; } + + bounds = toBounds(bounds); + + return this.min.equals(bounds.getTopLeft()) && + this.max.equals(bounds.getBottomRight()); + }, }; @@ -1109,7 +1212,7 @@ function toBounds(a, b) { * * Caution: if the area crosses the antimeridian (often confused with the International Date Line), you must specify corners _outside_ the [-180, 180] degrees longitude range. * - * Note that `LatLngBounds` does not inherit from Leafet's `Class` object, + * Note that `LatLngBounds` does not inherit from Leaflet's `Class` object, * which means new classes can't inherit from it, and new methods * can't be added to it with the `include` function. */ @@ -1279,7 +1382,7 @@ LatLngBounds.prototype = { return latIntersects && lngIntersects; }, - // @method overlaps(otherBounds: Bounds): Boolean + // @method overlaps(otherBounds: LatLngBounds): Boolean // Returns `true` if the rectangle overlaps the given bounds. Two bounds overlap if their intersection is an area. overlaps: function (bounds) { bounds = toLatLngBounds(bounds); @@ -1474,13 +1577,13 @@ function toLatLng(a, b, c) { * Object that defines coordinate reference systems for projecting * geographical points into pixel (screen) coordinates and back (and to * coordinates in other units for [WMS](https://en.wikipedia.org/wiki/Web_Map_Service) services). See - * [spatial reference system](http://en.wikipedia.org/wiki/Coordinate_reference_system). + * [spatial reference system](https://en.wikipedia.org/wiki/Spatial_reference_system). * * Leaflet defines the most usual CRSs by default. If you want to use a * CRS not defined by default, take a look at the * [Proj4Leaflet](https://github.com/kartena/Proj4Leaflet) plugin. * - * Note that the CRS instances do not inherit from Leafet's `Class` object, + * Note that the CRS instances do not inherit from Leaflet's `Class` object, * and can't be instantiated. Also, new classes can't inherit from them, * and methods can't be added to them with the `include` function. */ @@ -1617,7 +1720,7 @@ var Earth = extend({}, CRS, { // Mean Earth Radius, as recommended for use by // the International Union of Geodesy and Geophysics, - // see http://rosettacode.org/wiki/Haversine_formula + // see https://rosettacode.org/wiki/Haversine_formula R: 6371000, // distance between two geographical points using spherical law of cosines approximation @@ -1642,9 +1745,11 @@ var Earth = extend({}, CRS, { * a sphere. Used by the `EPSG:3857` CRS. */ +var earthRadius = 6378137; + var SphericalMercator = { - R: 6378137, + R: earthRadius, MAX_LATITUDE: 85.0511287798, project: function (latlng) { @@ -1667,7 +1772,7 @@ var SphericalMercator = { }, bounds: (function () { - var d = 6378137 * Math.PI; + var d = earthRadius * Math.PI; return new Bounds([-d, -d], [d, d]); })() }; @@ -1799,7 +1904,7 @@ function pointsToPath(rings, closed) { } // closes the ring for polygons; "x" is VML syntax - str += closed ? (svg ? 'z' : 'x') : ''; + str += closed ? (Browser.svg ? 'z' : 'x') : ''; } // SVG complains about empty path strings @@ -1821,7 +1926,7 @@ function pointsToPath(rings, closed) { * ``` */ -var style$1 = document.documentElement.style; +var style = document.documentElement.style; // @property ie: Boolean; `true` for all Internet Explorer versions (not Edge). var ie = 'ActiveXObject' in window; @@ -1837,22 +1942,22 @@ var edge = 'msLaunchUri' in navigator && !('documentMode' in document); var webkit = userAgentContains('webkit'); // @property android: Boolean -// `true` for any browser running on an Android platform. +// **Deprecated.** `true` for any browser running on an Android platform. var android = userAgentContains('android'); -// @property android23: Boolean; `true` for browsers running on Android 2 or Android 3. +// @property android23: Boolean; **Deprecated.** `true` for browsers running on Android 2 or Android 3. var android23 = userAgentContains('android 2') || userAgentContains('android 3'); /* See https://stackoverflow.com/a/17961266 for details on detecting stock Android */ var webkitVer = parseInt(/WebKit\/([0-9]+)|$/.exec(navigator.userAgent)[1], 10); // also matches AppleWebKit -// @property androidStock: Boolean; `true` for the Android stock browser (i.e. not Chrome) +// @property androidStock: Boolean; **Deprecated.** `true` for the Android stock browser (i.e. not Chrome) var androidStock = android && userAgentContains('Google') && webkitVer < 537 && !('AudioNode' in window); // @property opera: Boolean; `true` for the Opera browser var opera = !!window.opera; // @property chrome: Boolean; `true` for the Chrome browser. -var chrome = userAgentContains('chrome'); +var chrome = !edge && userAgentContains('chrome'); // @property gecko: Boolean; `true` for gecko-based browsers like Firefox. var gecko = userAgentContains('gecko') && !webkit && !opera && !ie; @@ -1864,19 +1969,19 @@ var phantom = userAgentContains('phantom'); // @property opera12: Boolean // `true` for the Opera browser supporting CSS transforms (version 12 or later). -var opera12 = 'OTransition' in style$1; +var opera12 = 'OTransition' in style; // @property win: Boolean; `true` when the browser is running in a Windows platform var win = navigator.platform.indexOf('Win') === 0; // @property ie3d: Boolean; `true` for all Internet Explorer versions supporting CSS transforms. -var ie3d = ie && ('transition' in style$1); +var ie3d = ie && ('transition' in style); // @property webkit3d: Boolean; `true` for webkit-based browsers supporting CSS transforms. var webkit3d = ('WebKitCSSMatrix' in window) && ('m11' in new window.WebKitCSSMatrix()) && !android23; // @property gecko3d: Boolean; `true` for gecko-based browsers supporting CSS transforms. -var gecko3d = 'MozPerspective' in style$1; +var gecko3d = 'MozPerspective' in style; // @property any3d: Boolean // `true` for all browsers supporting CSS transforms. @@ -1900,13 +2005,17 @@ var msPointer = !window.PointerEvent && window.MSPointerEvent; // `true` for all browsers supporting [pointer events](https://msdn.microsoft.com/en-us/library/dn433244%28v=vs.85%29.aspx). var pointer = !!(window.PointerEvent || msPointer); -// @property touch: Boolean +// @property touchNative: Boolean // `true` for all browsers supporting [touch events](https://developer.mozilla.org/docs/Web/API/Touch_events). -// This does not necessarily mean that the browser is running in a computer with +// **This does not necessarily mean** that the browser is running in a computer with // a touchscreen, it only means that the browser is capable of understanding // touch events. -var touch = !window.L_NO_TOUCH && (pointer || 'ontouchstart' in window || - (window.DocumentTouch && document instanceof window.DocumentTouch)); +var touchNative = 'ontouchstart' in window || !!window.TouchEvent; + +// @property touch: Boolean +// `true` for all browsers supporting either [touch](#browser-touch) or [pointer](#browser-pointer) events. +// Note: pointer events will be preferred (if available), and processed for all `touch*` listeners. +var touch = !window.L_NO_TOUCH && (touchNative || pointer); // @property mobileOpera: Boolean; `true` for the Opera browser in a mobile device. var mobileOpera = mobile && opera; @@ -1919,20 +2028,43 @@ var mobileGecko = mobile && gecko; // `true` for browsers on a high-resolution "retina" screen or on any screen when browser's display zoom is more than 100%. var retina = (window.devicePixelRatio || (window.screen.deviceXDPI / window.screen.logicalXDPI)) > 1; +// @property passiveEvents: Boolean +// `true` for browsers that support passive events. +var passiveEvents = (function () { + var supportsPassiveOption = false; + try { + var opts = Object.defineProperty({}, 'passive', { + get: function () { // eslint-disable-line getter-return + supportsPassiveOption = true; + } + }); + window.addEventListener('testPassiveEventSupport', falseFn, opts); + window.removeEventListener('testPassiveEventSupport', falseFn, opts); + } catch (e) { + // Errors can safely be ignored since this is only a browser support test. + } + return supportsPassiveOption; +}()); // @property canvas: Boolean // `true` when the browser supports [``](https://developer.mozilla.org/docs/Web/API/Canvas_API). -var canvas = (function () { +var canvas$1 = (function () { return !!document.createElement('canvas').getContext; }()); // @property svg: Boolean // `true` when the browser supports [SVG](https://developer.mozilla.org/docs/Web/SVG). -var svg = !!(document.createElementNS && svgCreate('svg').createSVGRect); +var svg$1 = !!(document.createElementNS && svgCreate('svg').createSVGRect); + +var inlineSvg = !!svg$1 && (function () { + var div = document.createElement('div'); + div.innerHTML = ''; + return (div.firstChild && div.firstChild.namespaceURI) === 'http://www.w3.org/2000/svg'; +})(); // @property vml: Boolean // `true` if the browser supports [VML](https://en.wikipedia.org/wiki/Vector_Markup_Language). -var vml = !svg && (function () { +var vml = !svg$1 && (function () { try { var div = document.createElement('div'); div.innerHTML = ''; @@ -1948,129 +2080,104 @@ var vml = !svg && (function () { }()); +// @property mac: Boolean; `true` when the browser is running in a Mac platform +var mac = navigator.platform.indexOf('Mac') === 0; + +// @property mac: Boolean; `true` when the browser is running in a Linux platform +var linux = navigator.platform.indexOf('Linux') === 0; + function userAgentContains(str) { return navigator.userAgent.toLowerCase().indexOf(str) >= 0; } - - -var Browser = (Object.freeze || Object)({ - ie: ie, - ielt9: ielt9, - edge: edge, - webkit: webkit, - android: android, - android23: android23, - androidStock: androidStock, - opera: opera, - chrome: chrome, - gecko: gecko, - safari: safari, - phantom: phantom, - opera12: opera12, - win: win, - ie3d: ie3d, - webkit3d: webkit3d, - gecko3d: gecko3d, - any3d: any3d, - mobile: mobile, - mobileWebkit: mobileWebkit, - mobileWebkit3d: mobileWebkit3d, - msPointer: msPointer, - pointer: pointer, - touch: touch, - mobileOpera: mobileOpera, - mobileGecko: mobileGecko, - retina: retina, - canvas: canvas, - svg: svg, - vml: vml -}); + + +var Browser = { + ie: ie, + ielt9: ielt9, + edge: edge, + webkit: webkit, + android: android, + android23: android23, + androidStock: androidStock, + opera: opera, + chrome: chrome, + gecko: gecko, + safari: safari, + phantom: phantom, + opera12: opera12, + win: win, + ie3d: ie3d, + webkit3d: webkit3d, + gecko3d: gecko3d, + any3d: any3d, + mobile: mobile, + mobileWebkit: mobileWebkit, + mobileWebkit3d: mobileWebkit3d, + msPointer: msPointer, + pointer: pointer, + touch: touch, + touchNative: touchNative, + mobileOpera: mobileOpera, + mobileGecko: mobileGecko, + retina: retina, + passiveEvents: passiveEvents, + canvas: canvas$1, + svg: svg$1, + vml: vml, + inlineSvg: inlineSvg, + mac: mac, + linux: linux +}; /* * Extends L.DomEvent to provide touch support for Internet Explorer and Windows-based devices. */ - -var POINTER_DOWN = msPointer ? 'MSPointerDown' : 'pointerdown'; -var POINTER_MOVE = msPointer ? 'MSPointerMove' : 'pointermove'; -var POINTER_UP = msPointer ? 'MSPointerUp' : 'pointerup'; -var POINTER_CANCEL = msPointer ? 'MSPointerCancel' : 'pointercancel'; -var TAG_WHITE_LIST = ['INPUT', 'SELECT', 'OPTION']; - +var POINTER_DOWN = Browser.msPointer ? 'MSPointerDown' : 'pointerdown'; +var POINTER_MOVE = Browser.msPointer ? 'MSPointerMove' : 'pointermove'; +var POINTER_UP = Browser.msPointer ? 'MSPointerUp' : 'pointerup'; +var POINTER_CANCEL = Browser.msPointer ? 'MSPointerCancel' : 'pointercancel'; +var pEvent = { + touchstart : POINTER_DOWN, + touchmove : POINTER_MOVE, + touchend : POINTER_UP, + touchcancel : POINTER_CANCEL +}; +var handle = { + touchstart : _onPointerStart, + touchmove : _handlePointer, + touchend : _handlePointer, + touchcancel : _handlePointer +}; var _pointers = {}; var _pointerDocListener = false; -// DomEvent.DoubleTap needs to know about this -var _pointersCount = 0; - // Provides a touch events wrapper for (ms)pointer events. -// ref http://www.w3.org/TR/pointerevents/ https://www.w3.org/Bugs/Public/show_bug.cgi?id=22890 +// ref https://www.w3.org/TR/pointerevents/ https://www.w3.org/Bugs/Public/show_bug.cgi?id=22890 -function addPointerListener(obj, type, handler, id) { +function addPointerListener(obj, type, handler) { if (type === 'touchstart') { - _addPointerStart(obj, handler, id); - - } else if (type === 'touchmove') { - _addPointerMove(obj, handler, id); - - } else if (type === 'touchend') { - _addPointerEnd(obj, handler, id); + _addPointerDocListener(); } - - return this; -} - -function removePointerListener(obj, type, id) { - var handler = obj['_leaflet_' + type + id]; - - if (type === 'touchstart') { - obj.removeEventListener(POINTER_DOWN, handler, false); - - } else if (type === 'touchmove') { - obj.removeEventListener(POINTER_MOVE, handler, false); - - } else if (type === 'touchend') { - obj.removeEventListener(POINTER_UP, handler, false); - obj.removeEventListener(POINTER_CANCEL, handler, false); + if (!handle[type]) { + console.warn('wrong event specified:', type); + return falseFn; } - - return this; + handler = handle[type].bind(this, handler); + obj.addEventListener(pEvent[type], handler, false); + return handler; } -function _addPointerStart(obj, handler, id) { - var onDown = bind(function (e) { - if (e.pointerType !== 'mouse' && e.MSPOINTER_TYPE_MOUSE && e.pointerType !== e.MSPOINTER_TYPE_MOUSE) { - // In IE11, some touch events needs to fire for form controls, or - // the controls will stop working. We keep a whitelist of tag names that - // need these events. For other target tags, we prevent default on the event. - if (TAG_WHITE_LIST.indexOf(e.target.tagName) < 0) { - preventDefault(e); - } else { - return; - } - } - - _handlePointer(e, handler); - }); - - obj['_leaflet_touchstart' + id] = onDown; - obj.addEventListener(POINTER_DOWN, onDown, false); - - // need to keep track of what pointers and how many are active to provide e.touches emulation - if (!_pointerDocListener) { - // we listen documentElement as any drags that end by moving the touch off the screen get fired there - document.documentElement.addEventListener(POINTER_DOWN, _globalPointerDown, true); - document.documentElement.addEventListener(POINTER_MOVE, _globalPointerMove, true); - document.documentElement.addEventListener(POINTER_UP, _globalPointerUp, true); - document.documentElement.addEventListener(POINTER_CANCEL, _globalPointerUp, true); - - _pointerDocListener = true; +function removePointerListener(obj, type, handler) { + if (!pEvent[type]) { + console.warn('wrong event specified:', type); + return; } + obj.removeEventListener(pEvent[type], handler, false); } function _globalPointerDown(e) { _pointers[e.pointerId] = e; - _pointersCount++; } function _globalPointerMove(e) { @@ -2081,10 +2188,24 @@ function _globalPointerMove(e) { function _globalPointerUp(e) { delete _pointers[e.pointerId]; - _pointersCount--; } -function _handlePointer(e, handler) { +function _addPointerDocListener() { + // need to keep track of what pointers and how many are active to provide e.touches emulation + if (!_pointerDocListener) { + // we listen document as any drags that end by moving the touch off the screen get fired there + document.addEventListener(POINTER_DOWN, _globalPointerDown, true); + document.addEventListener(POINTER_MOVE, _globalPointerMove, true); + document.addEventListener(POINTER_UP, _globalPointerUp, true); + document.addEventListener(POINTER_CANCEL, _globalPointerUp, true); + + _pointerDocListener = true; + } +} + +function _handlePointer(handler, e) { + if (e.pointerType === (e.MSPOINTER_TYPE_MOUSE || 'mouse')) { return; } + e.touches = []; for (var i in _pointers) { e.touches.push(_pointers[i]); @@ -2094,110 +2215,102 @@ function _handlePointer(e, handler) { handler(e); } -function _addPointerMove(obj, handler, id) { - var onMove = function (e) { - // don't fire touch moves when mouse isn't down - if ((e.pointerType === e.MSPOINTER_TYPE_MOUSE || e.pointerType === 'mouse') && e.buttons === 0) { return; } - - _handlePointer(e, handler); - }; - - obj['_leaflet_touchmove' + id] = onMove; - obj.addEventListener(POINTER_MOVE, onMove, false); -} - -function _addPointerEnd(obj, handler, id) { - var onUp = function (e) { - _handlePointer(e, handler); - }; - - obj['_leaflet_touchend' + id] = onUp; - obj.addEventListener(POINTER_UP, onUp, false); - obj.addEventListener(POINTER_CANCEL, onUp, false); +function _onPointerStart(handler, e) { + // IE10 specific: MsTouch needs preventDefault. See #2000 + if (e.MSPOINTER_TYPE_TOUCH && e.pointerType === e.MSPOINTER_TYPE_TOUCH) { + preventDefault(e); + } + _handlePointer(handler, e); } /* * Extends the event handling code with double tap support for mobile browsers. + * + * Note: currently most browsers fire native dblclick, with only a few exceptions + * (see https://github.com/Leaflet/Leaflet/issues/7012#issuecomment-595087386) */ -var _touchstart = msPointer ? 'MSPointerDown' : pointer ? 'pointerdown' : 'touchstart'; -var _touchend = msPointer ? 'MSPointerUp' : pointer ? 'pointerup' : 'touchend'; -var _pre = '_leaflet_'; - -// inspired by Zepto touch code by Thomas Fuchs -function addDoubleTapListener(obj, handler, id) { - var last, touch$$1, - doubleTap = false, - delay = 250; - - function onTouchStart(e) { - var count; +function makeDblclick(event) { + // in modern browsers `type` cannot be just overridden: + // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Errors/Getter_only + var newEvent = {}, + prop, i; + for (i in event) { + prop = event[i]; + newEvent[i] = prop && prop.bind ? prop.bind(event) : prop; + } + event = newEvent; + newEvent.type = 'dblclick'; + newEvent.detail = 2; + newEvent.isTrusted = false; + newEvent._simulated = true; // for debug purposes + return newEvent; +} - if (pointer) { - if ((!edge) || e.pointerType === 'mouse') { return; } - count = _pointersCount; - } else { - count = e.touches.length; +var delay = 200; +function addDoubleTapListener(obj, handler) { + // Most browsers handle double tap natively + obj.addEventListener('dblclick', handler); + + // On some platforms the browser doesn't fire native dblclicks for touch events. + // It seems that in all such cases `detail` property of `click` event is always `1`. + // So here we rely on that fact to avoid excessive 'dblclick' simulation when not needed. + var last = 0, + detail; + function simDblclick(e) { + if (e.detail !== 1) { + detail = e.detail; // keep in sync to avoid false dblclick in some cases + return; } - if (count > 1) { return; } - - var now = Date.now(), - delta = now - (last || now); + if (e.pointerType === 'mouse' || + (e.sourceCapabilities && !e.sourceCapabilities.firesTouchEvents)) { - touch$$1 = e.touches ? e.touches[0] : e; - doubleTap = (delta > 0 && delta <= delay); - last = now; - } + return; + } - function onTouchEnd(e) { - if (doubleTap && !touch$$1.cancelBubble) { - if (pointer) { - if ((!edge) || e.pointerType === 'mouse') { return; } - // work around .type being readonly with MSPointer* events - var newTouch = {}, - prop, i; + // When clicking on an , the browser generates a click on its + //