From 0899a6397acac10b9d1e0dcf1dcb530052859d49 Mon Sep 17 00:00:00 2001 From: Karim Naaji Date: Tue, 7 Apr 2020 16:05:38 -0700 Subject: [PATCH 01/50] Prepare for next release cycle (#9506) --- package.json | 2 +- src/style-spec/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 6fb990e75d7..ff4d5aa6e2c 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "mapbox-gl", "description": "A WebGL interactive maps library", - "version": "1.10.0-dev", + "version": "1.11.0-dev", "main": "dist/mapbox-gl.js", "style": "dist/mapbox-gl.css", "license": "SEE LICENSE IN LICENSE.txt", diff --git a/src/style-spec/package.json b/src/style-spec/package.json index 5d0fd6ad889..6cc58c1b784 100644 --- a/src/style-spec/package.json +++ b/src/style-spec/package.json @@ -1,7 +1,7 @@ { "name": "@mapbox/mapbox-gl-style-spec", "description": "a specification for mapbox gl styles", - "version": "13.14.0-dev", + "version": "13.15.0-dev", "author": "Mapbox", "keywords": [ "mapbox", From 8c7c88332ecf515555a308d38117440fa22be126 Mon Sep 17 00:00:00 2001 From: Asheem Mamoowala Date: Tue, 7 Apr 2020 16:45:20 -0700 Subject: [PATCH 02/50] Fix minor typo (#9507) --- src/ui/camera.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ui/camera.js b/src/ui/camera.js index b5d09d89bc7..e78443f14d9 100644 --- a/src/ui/camera.js +++ b/src/ui/camera.js @@ -37,7 +37,7 @@ import type {PaddingOptions} from '../geo/edge_insets'; * is "up"; for example, a bearing of 90° orients the map so that east is up. * @property {number} pitch The desired pitch, in degrees. * @property {LngLatLike} around If `zoom` is specified, `around` determines the point around which the zoom is centered. - * @property {PaddingOptions} padding Dimensions in pixels applied on eachs side of the viewport for shifting the vanishing point. + * @property {PaddingOptions} padding Dimensions in pixels applied on each side of the viewport for shifting the vanishing point. */ export type CameraOptions = { center?: LngLatLike, From d9272f8ff469256d861620da60b7e87f1e33739c Mon Sep 17 00:00:00 2001 From: Mikhail Pozdnyakov Date: Wed, 8 Apr 2020 12:29:26 +0300 Subject: [PATCH 03/50] [render tests][tile mode] Add `icon-text-fit/text-variable-anchor-tile-map-mode` test --- test/ignores.json | 3 +- .../expected.png | Bin 0 -> 192185 bytes .../style.json | 60 ++++++++++++++++++ 3 files changed, 62 insertions(+), 1 deletion(-) create mode 100644 test/integration/render-tests/icon-text-fit/text-variable-anchor-tile-map-mode/expected.png create mode 100644 test/integration/render-tests/icon-text-fit/text-variable-anchor-tile-map-mode/style.json diff --git a/test/ignores.json b/test/ignores.json index 025342d3e2f..115a3de6e1f 100644 --- a/test/ignores.json +++ b/test/ignores.json @@ -22,5 +22,6 @@ "render-tests/text-size/zero": "https://github.com/mapbox/mapbox-gl-js/issues/9161", "render-tests/text-variable-anchor/left-top-right-bottom-offset-tile-map-mode": "skip - mapbox-gl-js does not support tile-mode", "render-tests/tile-mode/streets-v11": "skip - mapbox-gl-js does not support tile-mode", - "render-tests/within/paint-line": "https://github.com/mapbox/mapbox-gl-js/issues/7023" + "render-tests/within/paint-line": "https://github.com/mapbox/mapbox-gl-js/issues/7023", + "render-tests/icon-text-fit/text-variable-anchor-tile-map-mode": "skip - mapbox-gl-js does not support tile-mode" } diff --git a/test/integration/render-tests/icon-text-fit/text-variable-anchor-tile-map-mode/expected.png b/test/integration/render-tests/icon-text-fit/text-variable-anchor-tile-map-mode/expected.png new file mode 100644 index 0000000000000000000000000000000000000000..1017418a59b3ef16ef5fc23dd5b57e36648f68bc GIT binary patch literal 192185 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 z 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%0AfBc%QVki2#6^iME3fxH>a^&1_JvN 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 literal 0 HcmV?d00001 diff --git a/test/integration/render-tests/icon-text-fit/text-variable-anchor-tile-map-mode/style.json b/test/integration/render-tests/icon-text-fit/text-variable-anchor-tile-map-mode/style.json new file mode 100644 index 00000000000..5cb9c085643 --- /dev/null +++ b/test/integration/render-tests/icon-text-fit/text-variable-anchor-tile-map-mode/style.json @@ -0,0 +1,60 @@ +{ + "version": 8, + "metadata": { + "test": { + "allowed": 0.0005, + "mapMode": "tile", + "debug": true + } + }, + "center": [ + 13.417, + 52.502 + ], + "zoom": 14, + "sources": { + "mapbox": { + "type": "vector", + "maxzoom": 14, + "tiles": [ + "local://tiles/{z}-{x}-{y}.mvt" + ] + } + }, + "sprite": "local://sprites/icon-text-fit", + "glyphs": "local://glyphs/{fontstack}/{range}.pbf", + "layers": [ + { + "id": "background", + "type": "background", + "paint": { + "background-color": "white" + } + }, + { + "id": "road", + "type": "symbol", + "source": "mapbox", + "source-layer": "road_label", + "layout": { + "text-variable-anchor": ["left", "right", "bottom", "top"], + "text-field": "{name}", + "text-font": [ + "Open Sans Semibold", + "Arial Unicode MS Bold" + ], + "icon-image": "label", + "icon-text-fit": "both", + "icon-text-fit-padding": [ + 5, + 10, + 5, + 10 + ] + }, + "paint": { + "icon-opacity": 1 + } + } + ] +} From d585d409eb820907af5a4cda8079f286b424cedb Mon Sep 17 00:00:00 2001 From: Ansis Brammanis Date: Wed, 8 Apr 2020 11:41:15 -0400 Subject: [PATCH 04/50] fix DragRotateHandler#isActive (#9511) --- src/ui/handler/shim/drag_rotate.js | 2 +- test/unit/ui/handler/drag_rotate.test.js | 24 ++++++++++++++++++++++++ 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/src/ui/handler/shim/drag_rotate.js b/src/ui/handler/shim/drag_rotate.js index 5d3878b9f7d..22141fa5116 100644 --- a/src/ui/handler/shim/drag_rotate.js +++ b/src/ui/handler/shim/drag_rotate.js @@ -62,6 +62,6 @@ export default class DragRotateHandler { * @returns {boolean} `true` if the "drag to rotate" interaction is active. */ isActive() { - return this._mouseRotate.isEnabled() || this._mousePitch.isEnabled(); + return this._mouseRotate.isActive() || this._mousePitch.isActive(); } } diff --git a/test/unit/ui/handler/drag_rotate.test.js b/test/unit/ui/handler/drag_rotate.test.js index 84235f1d20c..d1107828606 100644 --- a/test/unit/ui/handler/drag_rotate.test.js +++ b/test/unit/ui/handler/drag_rotate.test.js @@ -11,6 +11,30 @@ function createMap(t, options) { return new Map(extend({container: DOM.create('div', '', window.document.body)}, options)); } +test('DragRotateHandler#isActive', (t) => { + const map = createMap(t); + + // Prevent inertial rotation. + t.stub(browser, 'now').returns(0); + + t.equal(map.dragRotate.isActive(), false); + + simulate.mousedown(map.getCanvas(), {buttons: 2, button: 2}); + map._renderTaskQueue.run(); + t.equal(map.dragRotate.isActive(), false); + + simulate.mousemove(map.getCanvas(), {buttons: 2, clientX: 10, clientY: 10}); + map._renderTaskQueue.run(); + t.equal(map.dragRotate.isActive(), true); + + simulate.mouseup(map.getCanvas(), {buttons: 0, button: 2}); + map._renderTaskQueue.run(); + t.equal(map.dragRotate.isActive(), false); + + map.remove(); + t.end(); +}); + test('DragRotateHandler fires rotatestart, rotate, and rotateend events at appropriate times in response to a right-click drag', (t) => { const map = createMap(t); From d311ace9f32261da013a0c5167ce17ec643f1505 Mon Sep 17 00:00:00 2001 From: Karim Naaji Date: Wed, 8 Apr 2020 09:06:22 -0700 Subject: [PATCH 05/50] fix DragRotateHandler#isActive (#9514) Co-authored-by: Ansis Brammanis From 0754dcc031674bab2c3f4556ff3130054d1b6463 Mon Sep 17 00:00:00 2001 From: Ansis Brammanis Date: Wed, 8 Apr 2020 13:49:46 -0400 Subject: [PATCH 06/50] remove handler event listeners when map is removed (#9508) --- src/ui/control/navigation_control.js | 8 +-- src/ui/handler/box_zoom.js | 4 +- src/ui/handler/mouse.js | 4 +- src/ui/handler_manager.js | 101 ++++++++++++++++----------- src/ui/map.js | 2 + 5 files changed, 71 insertions(+), 48 deletions(-) diff --git a/src/ui/control/navigation_control.js b/src/ui/control/navigation_control.js index 63aece262f7..743eb21f1b6 100644 --- a/src/ui/control/navigation_control.js +++ b/src/ui/control/navigation_control.js @@ -173,10 +173,10 @@ class MouseRotateWrapper { move(e: MouseEvent, point: Point) { const map = this.map; - const r = this.mouseRotate.windowMousemove(e, point); + const r = this.mouseRotate.mousemoveWindow(e, point); if (r && r.bearingDelta) map.setBearing(map.getBearing() + r.bearingDelta); if (this.mousePitch) { - const p = this.mousePitch.windowMousemove(e, point); + const p = this.mousePitch.mousemoveWindow(e, point); if (p && p.pitchDelta) map.setPitch(map.getPitch() + p.pitchDelta); } } @@ -208,8 +208,8 @@ class MouseRotateWrapper { } mouseup(e: MouseEvent) { - this.mouseRotate.windowMouseup(e); - if (this.mousePitch) this.mousePitch.windowMouseup(e); + this.mouseRotate.mouseupWindow(e); + if (this.mousePitch) this.mousePitch.mouseupWindow(e); this.offTemp(); } diff --git a/src/ui/handler/box_zoom.js b/src/ui/handler/box_zoom.js index 5665e9a1d6f..be8415ab7e3 100644 --- a/src/ui/handler/box_zoom.js +++ b/src/ui/handler/box_zoom.js @@ -82,7 +82,7 @@ class BoxZoomHandler { this._active = true; } - windowMousemove(e: MouseEvent, point: Point) { + mousemoveWindow(e: MouseEvent, point: Point) { if (!this._active) return; const pos = point; @@ -111,7 +111,7 @@ class BoxZoomHandler { this._box.style.height = `${maxY - minY}px`; } - windowMouseup(e: MouseEvent, point: Point) { + mouseupWindow(e: MouseEvent, point: Point) { if (!this._active) return; if (e.button !== 0) return; diff --git a/src/ui/handler/mouse.js b/src/ui/handler/mouse.js index 8c8d42d2ee1..a2d4f41eeed 100644 --- a/src/ui/handler/mouse.js +++ b/src/ui/handler/mouse.js @@ -45,7 +45,7 @@ class MouseHandler { this._eventButton = eventButton; } - windowMousemove(e: MouseEvent, point: Point) { + mousemoveWindow(e: MouseEvent, point: Point) { const lastPoint = this._lastPoint; if (!lastPoint) return; e.preventDefault(); @@ -58,7 +58,7 @@ class MouseHandler { return this._move(lastPoint, point); } - windowMouseup(e: MouseEvent) { + mouseupWindow(e: MouseEvent) { const eventButton = DOM.mouseButton(e); if (eventButton !== this._eventButton) return; if (this._moved) DOM.suppressClick(); diff --git a/src/ui/handler_manager.js b/src/ui/handler_manager.js index 0a617bd41aa..1589e1d0f59 100644 --- a/src/ui/handler_manager.js +++ b/src/ui/handler_manager.js @@ -18,7 +18,7 @@ import TapDragZoomHandler from './handler/tap_drag_zoom'; import DragPanHandler from './handler/shim/drag_pan'; import DragRotateHandler from './handler/shim/drag_rotate'; import TouchZoomRotateHandler from './handler/shim/touch_zoom_rotate'; -import {extend} from '../util/util'; +import {bindAll, extend} from '../util/util'; import window from '../util/window'; import Point from '@mapbox/point-geometry'; import assert from 'assert'; @@ -105,6 +105,7 @@ class HandlerManager { _changes: Array<[HandlerResult, Object, any]>; _previousActiveHandlers: { [string]: Handler }; _bearingChanged: boolean; + _listeners: Array<[HTMLElement, string, void | {passive?: boolean, capture?: boolean}]>; constructor(map: Map, options: { interactive: boolean, pitchWithRotate: boolean, clickTolerance: number, bearingSnap: number}) { this._map = map; @@ -122,45 +123,56 @@ class HandlerManager { this._addDefaultHandlers(options); - // Bind touchstart and touchmove with passive: false because, even though - // they only fire a map events and therefore could theoretically be - // passive, binding with passive: true causes iOS not to respect - // e.preventDefault() in _other_ handlers, even if they are non-passive - // (see https://bugs.webkit.org/show_bug.cgi?id=184251) - this._addListener(this._el, 'touchstart', {passive: false}); - this._addListener(this._el, 'touchmove', {passive: false}); - this._addListener(this._el, 'touchend'); - this._addListener(this._el, 'touchcancel'); - - this._addListener(this._el, 'mousedown'); - this._addListener(this._el, 'mousemove'); - this._addListener(this._el, 'mouseup'); - - // Bind window-level event listeners for move and up/end events. In the absence of - // the pointer capture API, which is not supported by all necessary platforms, - // window-level event listeners give us the best shot at capturing events that - // fall outside the map canvas element. Use `{capture: true}` for the move event - // to prevent map move events from being fired during a drag. - this._addListener(window.document, 'mousemove', {capture: true}, 'windowMousemove'); - this._addListener(window.document, 'mouseup', undefined, 'windowMouseup'); - - this._addListener(this._el, 'mouseover'); - this._addListener(this._el, 'mouseout'); - this._addListener(this._el, 'dblclick'); - this._addListener(this._el, 'click'); - - this._addListener(this._el, 'keydown', {capture: false}); - this._addListener(this._el, 'keyup'); - - this._addListener(this._el, 'wheel', {passive: false}); - this._addListener(this._el, 'contextmenu'); - - DOM.addEventListener(window, 'blur', () => this.stop()); + bindAll(['handleEvent', 'handleWindowEvent'], this); + + const el = this._el; + + this._listeners = [ + // Bind touchstart and touchmove with passive: false because, even though + // they only fire a map events and therefore could theoretically be + // passive, binding with passive: true causes iOS not to respect + // e.preventDefault() in _other_ handlers, even if they are non-passive + // (see https://bugs.webkit.org/show_bug.cgi?id=184251) + [el, 'touchstart', {passive: false}], + [el, 'touchmove', {passive: false}], + [el, 'touchend', undefined], + [el, 'touchcancel', undefined], + + [el, 'mousedown', undefined], + [el, 'mousemove', undefined], + [el, 'mouseup', undefined], + + // Bind window-level event listeners for move and up/end events. In the absence of + // the pointer capture API, which is not supported by all necessary platforms, + // window-level event listeners give us the best shot at capturing events that + // fall outside the map canvas element. Use `{capture: true}` for the move event + // to prevent map move events from being fired during a drag. + [window.document, 'mousemove', {capture: true}], + [window.document, 'mouseup', undefined], + + [el, 'mouseover', undefined], + [el, 'mouseout', undefined], + [el, 'dblclick', undefined], + [el, 'click', undefined], + + [el, 'keydown', {capture: false}], + [el, 'keyup', undefined], + + [el, 'wheel', {passive: false}], + [el, 'contextmenu', undefined], + + [window, 'blur', undefined] + ]; + + for (const [target, type, listenerOptions] of this._listeners) { + DOM.addEventListener(target, type, target === window.document ? this.handleWindowEvent : this.handleEvent, listenerOptions); + } } - _addListener(element: Element, eventType: string, options: Object, name_?: string) { - const name = name_ || eventType; - DOM.addEventListener(element, eventType, e => this._processInputEvent(e, name), options); + destroy() { + for (const [target, type, listenerOptions] of this._listeners) { + DOM.removeEventListener(target, type, target === window.document ? this.handleWindowEvent : this.handleEvent, listenerOptions); + } } _addDefaultHandlers(options: { interactive: boolean, pitchWithRotate: boolean, clickTolerance: number }) { @@ -257,7 +269,16 @@ class HandlerManager { return false; } - _processInputEvent(e: InputEvent | RenderFrameEvent, eventName?: string) { + handleWindowEvent(e: InputEvent) { + this.handleEvent(e, `${e.type}Window`); + } + + handleEvent(e: InputEvent | RenderFrameEvent, eventName?: string) { + + if (e.type === 'blur') { + this.stop(); + return; + } this._updatingCamera = true; assert(e.timeStamp !== undefined); @@ -475,7 +496,7 @@ class HandlerManager { if (this._frameId === undefined) { this._frameId = this._map._requestRenderFrame(timeStamp => { delete this._frameId; - this._processInputEvent(new RenderFrameEvent('renderFrame', {timeStamp})); + this.handleEvent(new RenderFrameEvent('renderFrame', {timeStamp})); this._applyChanges(); }); } diff --git a/src/ui/map.js b/src/ui/map.js index cac2ff4b32b..c1e5eee4dad 100755 --- a/src/ui/map.js +++ b/src/ui/map.js @@ -2319,6 +2319,8 @@ class Map extends Camera { } this._renderTaskQueue.clear(); this.painter.destroy(); + this.handlers.destroy(); + delete this.handlers; this.setStyle(null); if (typeof window !== 'undefined') { window.removeEventListener('resize', this._onWindowResize, false); From 99e9a24bb982893e0b80af2314d5ce9e1e4cfbea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Minh=20Nguy=E1=BB=85n?= Date: Wed, 8 Apr 2020 17:22:39 -0700 Subject: [PATCH 07/50] Update style specification compatibility tables (release-vanillashake) (#9509) Updated the style specification compatibility tables for Android map SDK v9.1.0, iOS map SDK v5.8.0, and macOS map SDK v0.15.0. --- src/style-spec/reference/v8.json | 44 +++++++++++++++++++++++++------- 1 file changed, 35 insertions(+), 9 deletions(-) diff --git a/src/style-spec/reference/v8.json b/src/style-spec/reference/v8.json index 0ced8c311e0..b7a7b35c70b 100644 --- a/src/style-spec/reference/v8.json +++ b/src/style-spec/reference/v8.json @@ -645,10 +645,16 @@ "doc": "Sorts features in ascending order based on this value. Features with a higher sort key will appear above features with a lower sort key.", "sdk-support": { "basic functionality": { - "js": "1.2.0" + "js": "1.2.0", + "android": "9.1.0", + "ios": "5.8.0", + "macos": "0.15.0" }, "data-driven styling": { - "js": "1.2.0" + "js": "1.2.0", + "android": "9.1.0", + "ios": "5.8.0", + "macos": "0.15.0" } }, "expression": { @@ -900,10 +906,16 @@ "doc": "Sorts features in ascending order based on this value. Features with a higher sort key will appear above features with a lower sort key.", "sdk-support": { "basic functionality": { - "js": "1.2.0" + "js": "1.2.0", + "android": "9.1.0", + "ios": "5.8.0", + "macos": "0.15.0" }, "data-driven styling": { - "js": "1.2.0" + "js": "1.2.0", + "android": "9.1.0", + "ios": "5.8.0", + "macos": "0.15.0" } }, "expression": { @@ -1250,6 +1262,11 @@ "android": "4.2.0", "ios": "3.4.0", "macos": "0.2.1" + }, + "stretchable icons": { + "js": "1.6.0", + "ios": "5.8.0", + "macos": "0.15.0" } }, "expression": { @@ -2059,7 +2076,7 @@ "js": "1.3.0", "android": "8.3.0", "ios": "5.3.0", - "macos": "0.14.0" + "macos": "0.15.0" } }, "expression": { @@ -2593,7 +2610,10 @@ "group": "Lookup", "sdk-support": { "basic functionality": { - "js": "1.6.0" + "js": "1.6.0", + "android": "9.1.0", + "ios": "5.8.0", + "macos": "0.15.0" } } }, @@ -2821,7 +2841,9 @@ "macos": "0.14.0" }, "image": { - "js": "1.6.0" + "js": "1.6.0", + "ios": "5.7.0", + "macos": "0.15.0" } } }, @@ -2832,7 +2854,8 @@ "basic functionality": { "js": "1.4.0", "android": "8.6.0", - "ios": "5.6.0" + "ios": "5.7.0", + "macos": "0.15.0" } } }, @@ -3468,7 +3491,10 @@ "group": "Decision", "sdk-support": { "basic functionality": { - "js": "1.9.0" + "js": "1.9.0", + "android": "9.1.0", + "ios": "5.8.0", + "macos": "0.15.0" } } }, From 2ed1e1337384f8dd04898f1717d4b395ef794581 Mon Sep 17 00:00:00 2001 From: Karim Naaji Date: Thu, 9 Apr 2020 07:47:02 -0700 Subject: [PATCH 08/50] Fix for issue 9518 (#9520) * Add regression test for https://github.com/mapbox/mapbox-gl-js/issues/9518 * More thorough input checks for isPatternMissing function Fixes regression introduced by https://github.com/mapbox/mapbox-gl-js/pull/9380 ResolvedImage.fromString may now return null, which potentially leaves CrossFaded with missing image patterns I have traced other code paths using ResolvedImage.fromString and have not seen any other potential offenders --- src/render/painter.js | 1 + .../mapbox-gl-js#9518/expected.png | Bin 0 -> 69 bytes .../regressions/mapbox-gl-js#9518/style.json | 27 ++++++++++++++++++ 3 files changed, 28 insertions(+) create mode 100644 test/integration/render-tests/regressions/mapbox-gl-js#9518/expected.png create mode 100644 test/integration/render-tests/regressions/mapbox-gl-js#9518/style.json diff --git a/src/render/painter.js b/src/render/painter.js index 32fbe08548b..165a1e6e91c 100644 --- a/src/render/painter.js +++ b/src/render/painter.js @@ -589,6 +589,7 @@ class Painter { */ isPatternMissing(image: ?CrossFaded): boolean { if (!image) return false; + if (!image.from || !image.to) return true; const imagePosA = this.imageManager.getPattern(image.from.toString()); const imagePosB = this.imageManager.getPattern(image.to.toString()); return !imagePosA || !imagePosB; diff --git a/test/integration/render-tests/regressions/mapbox-gl-js#9518/expected.png b/test/integration/render-tests/regressions/mapbox-gl-js#9518/expected.png new file mode 100644 index 0000000000000000000000000000000000000000..1ed60094c40c52c61d4fb681d534520df195e0af GIT binary patch literal 69 zcmeAS@N?(olHy`uVBq!ia0vp^93afW1|*O0@9PFqJf1F&Ar*|t608Xf42&EMjBQR= QMSzopr0CnFCO#lD@ literal 0 HcmV?d00001 diff --git a/test/integration/render-tests/regressions/mapbox-gl-js#9518/style.json b/test/integration/render-tests/regressions/mapbox-gl-js#9518/style.json new file mode 100644 index 00000000000..a7d66de5770 --- /dev/null +++ b/test/integration/render-tests/regressions/mapbox-gl-js#9518/style.json @@ -0,0 +1,27 @@ +{ + "version": 8, + "metadata": { + "test": { + "width": 8, + "height": 8 + } + }, + "zoom": 3, + "sources": {}, + "sprite": "local://sprites/emerald", + "layers": [ + { + "id": "background", + "type": "background", + "paint": { + "background-pattern": [ + "step", + ["zoom"], + "", + 5, + "cemetery_icon" + ] + } + } + ] +} From 3de1b9fa19f4fe435f622b639a0e495167455a6e Mon Sep 17 00:00:00 2001 From: Ansis Brammanis Date: Thu, 9 Apr 2020 16:33:55 -0400 Subject: [PATCH 09/50] fix #9519 click map event on touch devices (#9526) --- src/ui/handler/tap_recognizer.js | 1 - src/ui/handler/tap_zoom.js | 2 ++ 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/ui/handler/tap_recognizer.js b/src/ui/handler/tap_recognizer.js index 1d9f11121a1..40328e19764 100644 --- a/src/ui/handler/tap_recognizer.js +++ b/src/ui/handler/tap_recognizer.js @@ -125,7 +125,6 @@ export class TapRecognizer { this.lastTap = tap; if (this.count === this.numTaps) { - e.preventDefault(); this.reset(); return tap; } diff --git a/src/ui/handler/tap_zoom.js b/src/ui/handler/tap_zoom.js index 060a40af9d8..0249c1069e9 100644 --- a/src/ui/handler/tap_zoom.js +++ b/src/ui/handler/tap_zoom.js @@ -47,6 +47,7 @@ export default class TapZoomHandler { if (zoomInPoint) { this._active = true; + e.preventDefault(); setTimeout(() => this.reset(), 0); return { cameraAnimation: (map: Map) => map.easeTo({ @@ -57,6 +58,7 @@ export default class TapZoomHandler { }; } else if (zoomOutPoint) { this._active = true; + e.preventDefault(); setTimeout(() => this.reset(), 0); return { cameraAnimation: (map: Map) => map.easeTo({ From 2622b145d07018f80f7aef3802cabe35c805f8e9 Mon Sep 17 00:00:00 2001 From: "Thiago Marcos P. Santos" Date: Fri, 10 Apr 2020 00:01:10 +0300 Subject: [PATCH 10/50] Add render-tests for line-dasharray case (#9525) Trick to mimic an outline with a different color. --- .../line-dasharray/case/butt/expected.png | Bin 0 -> 1417 bytes .../line-dasharray/case/butt/style.json | 70 ++++++++++++++++ .../line-dasharray/case/round/expected.png | Bin 0 -> 2499 bytes .../line-dasharray/case/round/style.json | 76 ++++++++++++++++++ .../line-dasharray/case/square/expected.png | Bin 0 -> 1571 bytes .../line-dasharray/case/square/style.json | 76 ++++++++++++++++++ 6 files changed, 222 insertions(+) create mode 100644 test/integration/render-tests/line-dasharray/case/butt/expected.png create mode 100644 test/integration/render-tests/line-dasharray/case/butt/style.json create mode 100644 test/integration/render-tests/line-dasharray/case/round/expected.png create mode 100644 test/integration/render-tests/line-dasharray/case/round/style.json create mode 100644 test/integration/render-tests/line-dasharray/case/square/expected.png create mode 100644 test/integration/render-tests/line-dasharray/case/square/style.json diff --git a/test/integration/render-tests/line-dasharray/case/butt/expected.png b/test/integration/render-tests/line-dasharray/case/butt/expected.png new file mode 100644 index 0000000000000000000000000000000000000000..1b532d4b7bebc13f6f34c8c8e12b7ee0f2a2c542 GIT binary patch literal 1417 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 Date: Fri, 10 Apr 2020 02:07:39 +0300 Subject: [PATCH 11/50] Follow-up of adding test cases for line-dasharray case This test should be ignored because the rendering result is wrong. --- test/ignores.json | 1 + .../line-dasharray/case/square/expected.png | Bin 1571 -> 9458 bytes 2 files changed, 1 insertion(+) diff --git a/test/ignores.json b/test/ignores.json index 115a3de6e1f..60f94aea2f9 100644 --- a/test/ignores.json +++ b/test/ignores.json @@ -1,6 +1,7 @@ { "query-tests/regressions/mapbox-gl-js#4494": "https://github.com/mapbox/mapbox-gl-js/issues/2716", "render-tests/geojson/inline-linestring-fill": "current behavior is arbitrary", + "render-tests/line-dasharray/case/square": "https://github.com/mapbox/mapbox-gl-js/issues/9531", "render-tests/map-mode/static": "https://github.com/mapbox/mapbox-gl-js/issues/5649", "render-tests/map-mode/tile": "skip - mapbox-gl-js does not support tile-mode", "render-tests/map-mode/tile-avoid-edges": "skip - mapbox-gl-js does not support tile-mode", diff --git a/test/integration/render-tests/line-dasharray/case/square/expected.png b/test/integration/render-tests/line-dasharray/case/square/expected.png index b2f71ec81ccdae14509030856e6449ace24bb858..950a308bc81edef8c4a2812c8703f3782cd372d7 100644 GIT binary patch literal 9458 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 D&Q-)q literal 1571 zcmeAS@N?(olHy`uVBq!ia0vp^CqS5k4M?tyST~P>f%TQAi(^Q|oVR!O_fK(^IPlR{ zsnNsHB*Hme;h;pPVNm3(01sEzW7nFF{9vE(WAlck6OUbT;>y-aTCUb5qBVMLurM(; zIyfi@2+)mrp{rYb`QE0J0vySw3*X$`{Uv$ThwC8^l`k$mXz{Dwbmg1E7nc%b)_*-0 zCh6(g?==0|v5)!hUq0pE>Kb%1`NGoPM=$_CC!SL zB`Y&!wz>qp*!1$!+uNG5HaBeg{_Oo%qJ2E{QQ9Jh8&}pwYtFTpcuCs&xptqA0?*U; z`sbG&UHe(oT}{BO{Cl3)@xG;7QcuSmoyghpaE|r#OB0n7Rknh8tP`(D09DP3RG$>} z_ymhnG4sSH;@@rVt}+A~)T8FU>C2;e%cCn<=6v_e>Q)x!SbX~O+AW*>Cz%Ups|av# zurM(-HaI9K0GTO%HdQ8XMW=Ww6hFAMbc)|xub1cMX0Bgi!?ZHyzsiF?SUTSdA+Imm&)91b7m!famiF-0LtqdqR*yJ%w&?B{N zXRV9MVPOx~SutmoCq3j?BDDBU^i<`O`tO!SOAC9jy6wH9Td(iG{S$}9`^h(aY9={w znfu8!A5Zz5rsOZ=aqw2x;$xE=as@pW9=XWdDB>Y=SI{Hz$VJ^o5f_=e!XAow_k3O+ zU@R>0RcPH&WxDk7@!+c3-4?r4CN%x|cUJ!8tz!MI#m-EcCm$vKdOi94rTOP=^!xfa zmYB?TQR*w>T;d`Pq`z@43CUIxJf6a_BxN-u0EOItvjpv0>hY;WyT9SHU8lt@)d^lt zYA5ZP>9HFvZI-U#2b{-tL5 zx_RFwNlbE3%Xw>fvtF!hS$ur0vF(q4$20YN6**Fx%&eUE-MFna$%!My$!vFmqe`m4 z2^OB}-yI%l^BtnDeaV{lU-{!ByU8q<76ravdrJRZ<*rl~n`)LzlM3}qqvaUqZ<}e6 z&2p(}Tb|9C*_~&^8hcKbE+`c~A#o(ozOlzc=Ch-MvcT~_0v^tfX6Q3zJ~-Ap<=cDl z<2HgG%;I`WO1@-x{hs$r_3kTM?LJF|32o=*`@fufc>1N~(f9nmSKmGwXxZ4K;Qr~| z!~2Ikjs&td_Gomu$b1zzA#ttSMW$5n#0)l2oIYC7?GSaLzVQ5I-NGxb3c4pJJ`pUq zr!MGj3v~7#P`X>$4oPo_q<1D%A$;AEl4m|%{PKFWJIZvIR)1f%S0(Mv9hOV>oI9$^ zz2f(lmpwgva^X@>dh<#0`yE%$YU7 zFpF@X+SpTNdj6CB)0bjvR_RR$e0%~FWLs^R+CoEFjovNI1Lukb>t{LK+V#;OU(G3A zv~dUD6i-b%fkFdMS9T>nwY4`KjyQ^U@wmv8EZyO-F5W-${=TXA|9J0xf8?NxsJKVj zivTZfZteYl*H^FaXztWBnviqDlQJeGy?|HmW4kYHf^2Q(Y!50 z(8El^b6#F&vss8RklAUG;T+nSVmWD#zY^cAfLMjddp<2@A(9+hT Date: Thu, 9 Apr 2020 18:40:13 -0700 Subject: [PATCH 12/50] [master] Fix style-spec isolation for within expression (#9522) * - Add a build-time check for style-spec referencing external files - Refactor within to NOT import external files * Address review comments --- .../expression/definitions/within.js | 17 ++++++++++++---- src/style-spec/rollup.config.js | 20 +++++++++++++++++++ 2 files changed, 33 insertions(+), 4 deletions(-) diff --git a/src/style-spec/expression/definitions/within.js b/src/style-spec/expression/definitions/within.js index 7c0b17037b1..67d60beec45 100644 --- a/src/style-spec/expression/definitions/within.js +++ b/src/style-spec/expression/definitions/within.js @@ -7,8 +7,6 @@ import type {Expression} from '../expression'; import type ParsingContext from '../parsing_context'; import type EvaluationContext from '../evaluation_context'; import type {GeoJSON, GeoJSONPolygon, GeoJSONMultiPolygon} from '@mapbox/geojson-types'; -import MercatorCoordinate from '../../../geo/mercator_coordinate'; -import EXTENT from '../../../data/extent'; import Point from '@mapbox/point-geometry'; import type {CanonicalTileID} from '../../../source/tile_id'; @@ -16,6 +14,8 @@ type GeoJSONPolygons =| GeoJSONPolygon | GeoJSONMultiPolygon; // minX, minY, maxX, maxY type BBox = [number, number, number, number]; +const EXTENT = 8192; + function updateBBox(bbox: BBox, coord: Point) { bbox[0] = Math.min(bbox[0], coord[0]); bbox[1] = Math.min(bbox[1], coord[1]); @@ -23,6 +23,14 @@ function updateBBox(bbox: BBox, coord: Point) { bbox[3] = Math.max(bbox[3], coord[1]); } +function mercatorXfromLng(lng: number) { + return (180 + lng) / 360; +} + +function mercatorYfromLat(lat: number) { + return (180 - (180 / Math.PI * Math.log(Math.tan(Math.PI / 4 + lat * Math.PI / 360)))) / 360; +} + function boxWithinBox(bbox1: BBox, bbox2: BBox) { if (bbox1[0] <= bbox2[0]) return false; if (bbox1[2] >= bbox2[2]) return false; @@ -32,9 +40,10 @@ function boxWithinBox(bbox1: BBox, bbox2: BBox) { } function getTileCoordinates(p, canonical: CanonicalTileID) { - const coord = MercatorCoordinate.fromLngLat({lng: p[0], lat: p[1]}, 0); + const x = mercatorXfromLng(p[0]); + const y = mercatorYfromLat(p[1]); const tilesAtZoom = Math.pow(2, canonical.z); - return [Math.round(coord.x * tilesAtZoom * EXTENT), Math.round(coord.y * tilesAtZoom * EXTENT)]; + return [Math.round(x * tilesAtZoom * EXTENT), Math.round(y * tilesAtZoom * EXTENT)]; } function onBoundary(p, p1, p2) { diff --git a/src/style-spec/rollup.config.js b/src/style-spec/rollup.config.js index 21105dec504..f0829ad896f 100644 --- a/src/style-spec/rollup.config.js +++ b/src/style-spec/rollup.config.js @@ -1,3 +1,4 @@ +import path from 'path'; import replace from 'rollup-plugin-replace'; import buble from 'rollup-plugin-buble'; import resolve from 'rollup-plugin-node-resolve'; @@ -14,6 +15,8 @@ const transforms = { modules: esm ? false : undefined }; +const ROOT_DIR = __dirname; + const config = [{ input: `${__dirname}/style-spec.js`, output: { @@ -23,6 +26,23 @@ const config = [{ sourcemap: true }, plugins: [ + { + name: 'dep-checker', + resolveId(source, importer) { + // Some users reference modules within style-spec package directly, instead of the bundle + // This means that files within the style-spec package should NOT import files from the parent mapbox-gl-js tree. + // This check will cause the build to fail on CI allowing these issues to be caught. + if (importer && !importer.includes('node_modules')) { + const resolvedPath = path.join(importer, source); + const fromRoot = path.relative(ROOT_DIR, resolvedPath); + if (fromRoot.length > 2 && fromRoot.slice(0, 2) === '..') { + throw new Error(`Module ${importer} imports ${source} from outside the style-spec package root directory.`); + } + } + + return null; + } + }, // https://github.com/zaach/jison/issues/351 replace({ include: /\/jsonlint-lines-primitives\/lib\/jsonlint.js/, From 7ede7d07c5232959328b1d6bef3c32ffa6c4a8ab Mon Sep 17 00:00:00 2001 From: Saman Bemel-Benrud Date: Fri, 10 Apr 2020 10:21:24 -0700 Subject: [PATCH 13/50] export isExpressionFilter from spec (#9530) --- src/style-spec/style-spec.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/style-spec/style-spec.js b/src/style-spec/style-spec.js index 66e039f9fc7..7bf17892353 100644 --- a/src/style-spec/style-spec.js +++ b/src/style-spec/style-spec.js @@ -69,7 +69,8 @@ import diff from './diff'; import ValidationError from './error/validation_error'; import ParsingError from './error/parsing_error'; import {StyleExpression, isExpression, createExpression, createPropertyExpression, normalizePropertyExpression, ZoomConstantExpression, ZoomDependentExpression, StylePropertyFunction} from './expression'; -import featureFilter from './feature_filter'; +import featureFilter, {isExpressionFilter} from './feature_filter'; + import convertFilter from './feature_filter/convert'; import Color from './util/color'; import {createFunction, isFunction} from './function'; @@ -82,6 +83,7 @@ import validateMapboxApiSupported from './validate_mapbox_api_supported'; const expression = { StyleExpression, isExpression, + isExpressionFilter, createExpression, createPropertyExpression, normalizePropertyExpression, From 9fd590e0bd0d6e755c5cd5425fdff99a249dc8cd Mon Sep 17 00:00:00 2001 From: Ansis Brammanis Date: Fri, 10 Apr 2020 16:19:18 -0400 Subject: [PATCH 14/50] fix mapTouchEvent.point for touchend events (#9536) --- src/ui/events.js | 3 +- test/unit/ui/handler/map_event.test.js | 48 ++++++++++++++++++++++++++ 2 files changed, 50 insertions(+), 1 deletion(-) create mode 100644 test/unit/ui/handler/map_event.test.js diff --git a/src/ui/events.js b/src/ui/events.js index 2cbe7bb047c..cf261c396fb 100644 --- a/src/ui/events.js +++ b/src/ui/events.js @@ -157,7 +157,8 @@ export class MapTouchEvent extends Event { * @private */ constructor(type: string, map: Map, originalEvent: TouchEvent) { - const points = DOM.touchPos(map.getCanvasContainer(), originalEvent.touches); + const touches = type === "touchend" ? originalEvent.changedTouches : originalEvent.touches; + const points = DOM.touchPos(map.getCanvasContainer(), touches); const lngLats = points.map((t) => map.unproject(t)); const point = points.reduce((prev, curr, i, arr) => { return prev.add(curr.div(arr.length)); diff --git a/test/unit/ui/handler/map_event.test.js b/test/unit/ui/handler/map_event.test.js new file mode 100644 index 00000000000..5e41314192e --- /dev/null +++ b/test/unit/ui/handler/map_event.test.js @@ -0,0 +1,48 @@ +import {test} from '../../../util/test'; +import window from '../../../../src/util/window'; +import Map from '../../../../src/ui/map'; +import DOM from '../../../../src/util/dom'; +import simulate from '../../../util/simulate_interaction'; + +function createMap(t) { + t.stub(Map.prototype, '_detectMissingCSS'); + return new Map({interactive: false, container: DOM.create('div', '', window.document.body)}); +} + +test('MapEvent handler fires touch events with correct values', (t) => { + const map = createMap(t); + + const touchstart = t.spy(); + const touchmove = t.spy(); + const touchend = t.spy(); + + map.on('touchstart', touchstart); + map.on('touchmove', touchmove); + map.on('touchend', touchend); + + const touchesStart = [{identifier: 1, clientX: 0, clientY: 50}]; + const touchesMove = [{identifier: 1, clientX: 0, clientY: 60}]; + const touchesEnd = [{identifier: 1, clientX: 0, clientY: 60}]; + + simulate.touchstart(map.getCanvas(), {touches: touchesStart, targetTouches: touchesStart}); + t.equal(touchstart.callCount, 1); + t.deepEqual(touchstart.getCall(0).args[0].point, {x: 0, y: 50}); + t.equal(touchmove.callCount, 0); + t.equal(touchend.callCount, 0); + console.log(touchstart); + + simulate.touchmove(map.getCanvas(), {touches: touchesMove, targetTouches: touchesMove}); + t.equal(touchstart.callCount, 1); + t.equal(touchmove.callCount, 1); + t.deepEqual(touchmove.getCall(0).args[0].point, {x: 0, y: 60}); + t.equal(touchend.callCount, 0); + + simulate.touchend(map.getCanvas(), {touches: [], targetTouches: [], changedTouches: touchesEnd}); + t.equal(touchstart.callCount, 1); + t.equal(touchmove.callCount, 1); + t.equal(touchend.callCount, 1); + t.deepEqual(touchend.getCall(0).args[0].point, {x: 0, y: 60}); + + map.remove(); + t.end(); +}); From 2b4a3373313c54815f2351233711cce4d5eb2134 Mon Sep 17 00:00:00 2001 From: Arindam Bose Date: Fri, 10 Apr 2020 14:23:32 -0700 Subject: [PATCH 15/50] update changlogs to add 1.9.1 and style-spec@v13.13.1 (#9540) --- CHANGELOG.md | 7 +++++++ src/style-spec/CHANGELOG.md | 8 ++++++++ 2 files changed, 15 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index ce6ecc2d8e9..c197eb097c8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +## 1.9.1 + +### 🐞 Bug fixes +* Fix a bug [#9477](https://github.com/mapbox/mapbox-gl-js/issues/9477) in `Map#fitBounds(..)` wherein the `padding` passed to options would get applied twice. +* Fix rendering bug [#9479](https://github.com/mapbox/mapbox-gl-js/issues/9479) caused when data-driven `*-pattern` properties reference images added with `Map#addImage(..)`. +* Fix a bug [#9468](https://github.com/mapbox/mapbox-gl-js/issues/9468) in which an exception would get thrown when updating symbol layer paint property using `setPaintProperty`. + ## 1.9.0 With this release, we're adding [a new changelog policy](https://github.com/mapbox/mapbox-gl-js/blob/master/CONTRIBUTING.md#changelog-conventions) to our contribution guidelines. diff --git a/src/style-spec/CHANGELOG.md b/src/style-spec/CHANGELOG.md index 6bce8a3ebc4..c6588db7409 100644 --- a/src/style-spec/CHANGELOG.md +++ b/src/style-spec/CHANGELOG.md @@ -1,3 +1,11 @@ +## 13.13.1 + +### ✨ Features and improvements +* Expose `expression.isExpressionFilter(..)` from the bundle. ([#9530](https://github.com/mapbox/mapbox-gl-js/pull/9530)) + +### 🐛 Bug fixes +* Fix a broken module import where the `style-spec` package was importing files from `mapbox-gl-js`, it's parent repo, causing downstream build systems to break. ([#9522](https://github.com/mapbox/mapbox-gl-js/pull/9522)) + ## 13.13.0 ### ✨ Features and improvements From d9696a0e6de0d3fb6746cf041bdc77f81318c7d8 Mon Sep 17 00:00:00 2001 From: Ryan Hamley Date: Fri, 10 Apr 2020 16:23:51 -0700 Subject: [PATCH 16/50] Refactor format_section_override out of style-spec (#9543) --- .../format_section_override.js | 18 ++++++++++-------- src/style/style_layer/symbol_style_layer.js | 2 +- .../format_section_override.test.js | 2 +- test/unit/symbol/symbol_style_layer.test.js | 2 +- 4 files changed, 13 insertions(+), 11 deletions(-) rename src/{style-spec/expression/definitions => style}/format_section_override.js (71%) rename test/unit/{symbol => style}/format_section_override.test.js (95%) diff --git a/src/style-spec/expression/definitions/format_section_override.js b/src/style/format_section_override.js similarity index 71% rename from src/style-spec/expression/definitions/format_section_override.js rename to src/style/format_section_override.js index 86952258263..fd69be19ee1 100644 --- a/src/style-spec/expression/definitions/format_section_override.js +++ b/src/style/format_section_override.js @@ -1,14 +1,16 @@ // @flow import assert from 'assert'; -import type {Expression} from '../expression'; -import type EvaluationContext from '../evaluation_context'; -import type {Type} from '../types'; -import type {ZoomConstantExpression} from '../../expression'; -import {NullType} from '../types'; -import {PossiblyEvaluatedPropertyValue} from '../../../style/properties'; -import {register} from '../../../util/web_worker_transfer'; - +import type {Expression} from '../style-spec/expression/expression'; +import type EvaluationContext from '../style-spec/expression/evaluation_context'; +import type {Type} from '../style-spec/expression/types'; +import type {ZoomConstantExpression} from '../style-spec/expression'; +import {NullType} from '../style-spec/expression/types'; +import {PossiblyEvaluatedPropertyValue} from './properties'; +import {register} from '../util/web_worker_transfer'; + +// This is an internal expression class. It is only used in GL JS and +// has GL JS dependencies which can break the standalone style-spec module export default class FormatSectionOverride implements Expression { type: Type; defaultValue: PossiblyEvaluatedPropertyValue; diff --git a/src/style/style_layer/symbol_style_layer.js b/src/style/style_layer/symbol_style_layer.js index 249f0f4b878..37254264509 100644 --- a/src/style/style_layer/symbol_style_layer.js +++ b/src/style/style_layer/symbol_style_layer.js @@ -33,7 +33,7 @@ import type {CanonicalTileID} from '../../source/tile_id'; import {FormattedType} from '../../style-spec/expression/types'; import {typeOf} from '../../style-spec/expression/values'; import Formatted from '../../style-spec/expression/types/formatted'; -import FormatSectionOverride from '../../style-spec/expression/definitions/format_section_override'; +import FormatSectionOverride from '../format_section_override'; import FormatExpression from '../../style-spec/expression/definitions/format'; import Literal from '../../style-spec/expression/definitions/literal'; diff --git a/test/unit/symbol/format_section_override.test.js b/test/unit/style/format_section_override.test.js similarity index 95% rename from test/unit/symbol/format_section_override.test.js rename to test/unit/style/format_section_override.test.js index 3364ba2fb06..292f9374893 100644 --- a/test/unit/symbol/format_section_override.test.js +++ b/test/unit/style/format_section_override.test.js @@ -3,7 +3,7 @@ import {createExpression, ZoomConstantExpression} from '../../../src/style-spec/ import EvaluationContext from '../../../src/style-spec/expression/evaluation_context'; import properties from '../../../src/style/style_layer/symbol_style_layer_properties'; import {PossiblyEvaluatedPropertyValue} from '../../../src/style/properties'; -import FormatSectionOverride from '../../../src/style-spec/expression/definitions/format_section_override'; +import FormatSectionOverride from '../../../src/style/format_section_override'; test('evaluate', (t) => { diff --git a/test/unit/symbol/symbol_style_layer.test.js b/test/unit/symbol/symbol_style_layer.test.js index 34ca2755960..982d912c996 100644 --- a/test/unit/symbol/symbol_style_layer.test.js +++ b/test/unit/symbol/symbol_style_layer.test.js @@ -1,6 +1,6 @@ import {test} from '../../util/test'; import SymbolStyleLayer from '../../../src/style/style_layer/symbol_style_layer'; -import FormatSectionOverride from '../../../src/style-spec/expression/definitions/format_section_override'; +import FormatSectionOverride from '../../../src/style/format_section_override'; import properties from '../../../src/style/style_layer/symbol_style_layer_properties'; function createSymbolLayer(layerProperties) { From 7312e5871caed815a44813a5c71f08098f0c0721 Mon Sep 17 00:00:00 2001 From: Ansis Brammanis Date: Mon, 13 Apr 2020 19:03:46 -0400 Subject: [PATCH 17/50] fix DragRotate when mouseup occurs outside window or iframe (#9512) fix #4622 --- src/ui/handler/mouse.js | 23 +++++++++ test/unit/ui/handler/drag_pan.test.js | 35 +++++++------ test/unit/ui/handler/mouse_rotate.test.js | 60 +++++++++++++++++++++++ test/unit/ui/map/isMoving.test.js | 7 ++- 4 files changed, 107 insertions(+), 18 deletions(-) create mode 100644 test/unit/ui/handler/mouse_rotate.test.js diff --git a/src/ui/handler/mouse.js b/src/ui/handler/mouse.js index a2d4f41eeed..3cf5c532816 100644 --- a/src/ui/handler/mouse.js +++ b/src/ui/handler/mouse.js @@ -6,6 +6,17 @@ import type Point from '@mapbox/point-geometry'; const LEFT_BUTTON = 0; const RIGHT_BUTTON = 2; +// the values for each button in MouseEvent.buttons +const BUTTONS_FLAGS = { + [LEFT_BUTTON]: 1, + [RIGHT_BUTTON]: 2 +}; + +function buttonStillPressed(e: MouseEvent, button: number) { + const flag = BUTTONS_FLAGS[button]; + return e.buttons === undefined || (e.buttons & flag) !== flag; +} + class MouseHandler { _enabled: boolean; @@ -50,6 +61,17 @@ class MouseHandler { if (!lastPoint) return; e.preventDefault(); + if (buttonStillPressed(e, this._eventButton)) { + // Some browsers don't fire a `mouseup` when the mouseup occurs outside + // the window or iframe: + // https://github.com/mapbox/mapbox-gl-js/issues/4622 + // + // If the button is no longer pressed during this `mousemove` it may have + // been released outside of the window or iframe. + this.reset(); + return; + } + if (!this._moved && point.dist(lastPoint) < this._clickTolerance) return; this._moved = true; this._lastPoint = point; @@ -59,6 +81,7 @@ class MouseHandler { } mouseupWindow(e: MouseEvent) { + if (!this._lastPoint) return; const eventButton = DOM.mouseButton(e); if (eventButton !== this._eventButton) return; if (this._moved) DOM.suppressClick(); diff --git a/test/unit/ui/handler/drag_pan.test.js b/test/unit/ui/handler/drag_pan.test.js index 971c981186c..6f364ae3e8e 100644 --- a/test/unit/ui/handler/drag_pan.test.js +++ b/test/unit/ui/handler/drag_pan.test.js @@ -13,6 +13,9 @@ function createMap(t, clickTolerance, dragPan) { }); } +// MouseEvent.buttons = 1 // left button +const buttons = 1; + test('DragPanHandler fires dragstart, drag, and dragend events at appropriate times in response to a mouse-triggered drag', (t) => { const map = createMap(t); @@ -30,7 +33,7 @@ test('DragPanHandler fires dragstart, drag, and dragend events at appropriate ti t.equal(drag.callCount, 0); t.equal(dragend.callCount, 0); - simulate.mousemove(map.getCanvas(), {clientX: 10, clientY: 10}); + simulate.mousemove(map.getCanvas(), {buttons, clientX: 10, clientY: 10}); map._renderTaskQueue.run(); t.equal(dragstart.callCount, 1); t.equal(drag.callCount, 1); @@ -63,7 +66,7 @@ test('DragPanHandler captures mousemove events during a mouse-triggered drag (re t.equal(drag.callCount, 0); t.equal(dragend.callCount, 0); - simulate.mousemove(window.document.body, {clientX: 10, clientY: 10}); + simulate.mousemove(window.document.body, {buttons, clientX: 10, clientY: 10}); map._renderTaskQueue.run(); t.equal(dragstart.callCount, 1); t.equal(drag.callCount, 1); @@ -121,7 +124,7 @@ test('DragPanHandler prevents mousemove events from firing during a drag (#1555) simulate.mousedown(map.getCanvasContainer()); map._renderTaskQueue.run(); - simulate.mousemove(map.getCanvasContainer(), {clientX: 10, clientY: 10}); + simulate.mousemove(map.getCanvasContainer(), {buttons, clientX: 10, clientY: 10}); map._renderTaskQueue.run(); simulate.mouseup(map.getCanvasContainer()); @@ -142,7 +145,7 @@ test('DragPanHandler ends a mouse-triggered drag if the window blurs', (t) => { simulate.mousedown(map.getCanvas()); map._renderTaskQueue.run(); - simulate.mousemove(map.getCanvas(), {clientX: 10, clientY: 10}); + simulate.mousemove(map.getCanvas(), {buttons, clientX: 10, clientY: 10}); map._renderTaskQueue.run(); simulate.blur(window); @@ -176,14 +179,14 @@ test('DragPanHandler requests a new render frame after each mousemove event', (t const requestFrame = t.spy(map, '_requestRenderFrame'); simulate.mousedown(map.getCanvas()); - simulate.mousemove(map.getCanvas(), {clientX: 10, clientY: 10}); + simulate.mousemove(map.getCanvas(), {buttons, clientX: 10, clientY: 10}); t.ok(requestFrame.callCount > 0); map._renderTaskQueue.run(); // https://github.com/mapbox/mapbox-gl-js/issues/6063 requestFrame.resetHistory(); - simulate.mousemove(map.getCanvas(), {clientX: 20, clientY: 20}); + simulate.mousemove(map.getCanvas(), {buttons, clientX: 20, clientY: 20}); t.equal(requestFrame.callCount, 1); map.remove(); @@ -208,7 +211,7 @@ test('DragPanHandler can interleave with another handler', (t) => { t.equal(drag.callCount, 0); t.equal(dragend.callCount, 0); - simulate.mousemove(map.getCanvas(), {clientX: 10, clientY: 10}); + simulate.mousemove(map.getCanvas(), {buttons, clientX: 10, clientY: 10}); map._renderTaskQueue.run(); t.equal(dragstart.callCount, 1); t.equal(drag.callCount, 1); @@ -221,7 +224,7 @@ test('DragPanHandler can interleave with another handler', (t) => { t.equal(drag.callCount, 1); t.equal(dragend.callCount, 0); - simulate.mousemove(map.getCanvas(), {clientX: 20, clientY: 20}); + simulate.mousemove(map.getCanvas(), {buttons, clientX: 20, clientY: 20}); map._renderTaskQueue.run(); t.equal(dragstart.callCount, 1); t.equal(drag.callCount, 2); @@ -250,13 +253,13 @@ test('DragPanHandler can interleave with another handler', (t) => { map.on('drag', drag); map.on('dragend', dragend); - simulate.mousedown(map.getCanvas(), {[`${modifier}Key`]: true}); + simulate.mousedown(map.getCanvas(), {buttons, [`${modifier}Key`]: true}); map._renderTaskQueue.run(); t.equal(dragstart.callCount, 0); t.equal(drag.callCount, 0); t.equal(dragend.callCount, 0); - simulate.mousemove(map.getCanvas(), {[`${modifier}Key`]: true, clientX: 10, clientY: 10}); + simulate.mousemove(map.getCanvas(), {buttons, [`${modifier}Key`]: true, clientX: 10, clientY: 10}); map._renderTaskQueue.run(); t.equal(dragstart.callCount, 0); t.equal(drag.callCount, 0); @@ -296,7 +299,7 @@ test('DragPanHandler can interleave with another handler', (t) => { t.equal(drag.callCount, 0); t.equal(dragend.callCount, 0); - simulate.mousemove(map.getCanvas(), {clientX: 10, clientY: 10}); + simulate.mousemove(map.getCanvas(), {buttons, clientX: 10, clientY: 10}); map._renderTaskQueue.run(); t.equal(dragstart.callCount, 0); t.equal(drag.callCount, 0); @@ -359,25 +362,25 @@ test('DragPanHandler does not end a drag on right button mouseup', (t) => { t.equal(drag.callCount, 0); t.equal(dragend.callCount, 0); - simulate.mousemove(map.getCanvas(), {clientX: 10, clientY: 10}); + simulate.mousemove(map.getCanvas(), {buttons, clientX: 10, clientY: 10}); map._renderTaskQueue.run(); t.equal(dragstart.callCount, 1); t.equal(drag.callCount, 1); t.equal(dragend.callCount, 0); - simulate.mousedown(map.getCanvas(), {buttons: 2, button: 2}); + simulate.mousedown(map.getCanvas(), {buttons: buttons + 2, button: 2}); map._renderTaskQueue.run(); t.equal(dragstart.callCount, 1); t.equal(drag.callCount, 1); t.equal(dragend.callCount, 0); - simulate.mouseup(map.getCanvas(), {buttons: 0, button: 2}); + simulate.mouseup(map.getCanvas(), {buttons, button: 2}); map._renderTaskQueue.run(); t.equal(dragstart.callCount, 1); t.equal(drag.callCount, 1); t.equal(dragend.callCount, 0); - simulate.mousemove(map.getCanvas(), {clientX: 20, clientY: 20}); + simulate.mousemove(map.getCanvas(), {buttons, clientX: 20, clientY: 20}); map._renderTaskQueue.run(); t.equal(dragstart.callCount, 1); t.equal(drag.callCount, 2); @@ -409,7 +412,7 @@ test('DragPanHandler does not begin a drag if preventDefault is called on the mo simulate.mousedown(map.getCanvas()); map._renderTaskQueue.run(); - simulate.mousemove(map.getCanvas(), {clientX: 10, clientY: 10}); + simulate.mousemove(map.getCanvas(), {buttons, clientX: 10, clientY: 10}); map._renderTaskQueue.run(); simulate.mouseup(map.getCanvas()); diff --git a/test/unit/ui/handler/mouse_rotate.test.js b/test/unit/ui/handler/mouse_rotate.test.js new file mode 100644 index 00000000000..0a587b7a0dd --- /dev/null +++ b/test/unit/ui/handler/mouse_rotate.test.js @@ -0,0 +1,60 @@ +import {test} from '../../../util/test'; +import {extend} from '../../../../src/util/util'; +import window from '../../../../src/util/window'; +import Map from '../../../../src/ui/map'; +import DOM from '../../../../src/util/dom'; +import simulate from '../../../util/simulate_interaction'; +import browser from '../../../../src/util/browser'; + +function createMap(t, options) { + t.stub(Map.prototype, '_detectMissingCSS'); + return new Map(extend({container: DOM.create('div', '', window.document.body)}, options)); +} + +test('MouseRotateHandler#isActive', (t) => { + const map = createMap(t); + const mouseRotate = map.handlers._handlersById.mouseRotate; + + // Prevent inertial rotation. + t.stub(browser, 'now').returns(0); + t.equal(mouseRotate.isActive(), false); + + simulate.mousedown(map.getCanvas(), {buttons: 2, button: 2}); + map._renderTaskQueue.run(); + t.equal(mouseRotate.isActive(), false); + + simulate.mousemove(map.getCanvas(), {buttons: 2, clientX: 10, clientY: 10}); + map._renderTaskQueue.run(); + t.equal(mouseRotate.isActive(), true); + + simulate.mouseup(map.getCanvas(), {buttons: 0, button: 2}); + map._renderTaskQueue.run(); + t.equal(mouseRotate.isActive(), false); + + map.remove(); + t.end(); +}); + +test('MouseRotateHandler#isActive #4622 regression test', (t) => { + const map = createMap(t); + const mouseRotate = map.handlers._handlersById.mouseRotate; + + // Prevent inertial rotation. + simulate.mousedown(map.getCanvas(), {buttons: 2, button: 2}); + map._renderTaskQueue.run(); + t.equal(mouseRotate.isActive(), false); + + simulate.mousemove(map.getCanvas(), {buttons: 2, clientX: 10, clientY: 10}); + map._renderTaskQueue.run(); + t.equal(mouseRotate.isActive(), true); + + // Some browsers don't fire mouseup when it happens outside the window. + // Make the handler in active when it encounters a mousemove without the button pressed. + + simulate.mousemove(map.getCanvas(), {buttons: 0, clientX: 10, clientY: 10}); + map._renderTaskQueue.run(); + t.equal(mouseRotate.isActive(), false); + + map.remove(); + t.end(); +}); diff --git a/test/unit/ui/map/isMoving.test.js b/test/unit/ui/map/isMoving.test.js index ffc7d1ad324..0cf88158a63 100644 --- a/test/unit/ui/map/isMoving.test.js +++ b/test/unit/ui/map/isMoving.test.js @@ -10,6 +10,9 @@ function createMap(t) { return new Map({container: DOM.create('div', '', window.document.body)}); } +// MouseEvent.buttons +const buttons = 1; + test('Map#isMoving returns false by default', (t) => { const map = createMap(t); t.equal(map.isMoving(), false); @@ -49,7 +52,7 @@ test('Map#isMoving returns true when drag panning', (t) => { simulate.mousedown(map.getCanvas()); map._renderTaskQueue.run(); - simulate.mousemove(map.getCanvas(), {clientX: 10, clientY: 10}); + simulate.mousemove(map.getCanvas(), {buttons, clientX: 10, clientY: 10}); map._renderTaskQueue.run(); simulate.mouseup(map.getCanvas()); @@ -139,7 +142,7 @@ test('Map#isMoving returns true when drag panning and scroll zooming interleave' simulate.mousedown(map.getCanvas()); map._renderTaskQueue.run(); - simulate.mousemove(map.getCanvas(), {clientX: 10, clientY: 10}); + simulate.mousemove(map.getCanvas(), {buttons, clientX: 10, clientY: 10}); map._renderTaskQueue.run(); const browserNow = t.stub(browser, 'now'); From 1deb45797949f9dd568b7eb4e1d67a0e07967727 Mon Sep 17 00:00:00 2001 From: Karim Naaji Date: Wed, 15 Apr 2020 09:17:37 -0700 Subject: [PATCH 18/50] Add diff-tarball script and more explicit files property in package.json (#9550) * More explicit files property in package.json To prevent potential upload error when publishing to npm Refer https://docs.npmjs.com/files/package.json\#files * Add a way to diff the new tarball content with previously published version This is intended to be used as part of our release process before publishing to NPM --dry-run is a useful step, but can be fairly noisy, yarn run v1.21.1 to go along the fake deploy and more clearly shows what would be added and deleted * Run the diff as part of prepublishOnly * Prompt to ensure user check before going further with publishing when running 'npm publish' --- .npmignore | 1 + build/diff-tarball.js | 18 ++ package.json | 10 +- yarn.lock | 379 ++++++++++++++++++++++++++++++++++++++---- 4 files changed, 376 insertions(+), 32 deletions(-) create mode 100644 .npmignore create mode 100644 build/diff-tarball.js diff --git a/.npmignore b/.npmignore new file mode 100644 index 00000000000..e43b0f98895 --- /dev/null +++ b/.npmignore @@ -0,0 +1 @@ +.DS_Store diff --git a/build/diff-tarball.js b/build/diff-tarball.js new file mode 100644 index 00000000000..43970decde2 --- /dev/null +++ b/build/diff-tarball.js @@ -0,0 +1,18 @@ +const packlist = require('npm-packlist') +const npmContent = require('list-npm-contents'); + +npmContent('mapbox-gl').then(function(last_version_files) { + packlist({ path: '.' }).then(function(new_version_files) { + new_version_files = new_version_files.map(file => file.replace(/\/\/+/g, '/')); + let diff_new = new_version_files.filter(x => !last_version_files.includes(x)); + let diff_last = last_version_files.filter(x => !new_version_files.includes(x)); + console.log(`${diff_new.length} files are about to be added in the new tarball`) + diff_new.forEach(file => { + console.log('+', file); + }); + console.log(`${diff_last.length} files are about to be deleted in the new tarball`) + diff_last.forEach(file => { + console.log('-', file); + }); + }); +}); \ No newline at end of file diff --git a/package.json b/package.json index ff4d5aa6e2c..3593663a6e5 100644 --- a/package.json +++ b/package.json @@ -73,11 +73,13 @@ "jsdom": "^13.0.0", "json-stringify-pretty-compact": "^2.0.0", "jsonwebtoken": "^8.3.0", + "list-npm-contents": "^1.0.2", "lodash.template": "^4.5.0", "mapbox-gl-styles": "^2.0.2", "mock-geolocation": "^1.0.11", "node-notifier": "^5.4.3", "npm-font-open-sans": "^1.1.0", + "npm-packlist": "^2.1.1", "npm-run-all": "^4.1.5", "nyc": "^13.3.0", "pirates": "^4.0.1", @@ -138,6 +140,7 @@ "start-tests": "run-p build-token watch-css watch-query start-server", "start-bench": "run-p build-token watch-benchmarks start-server", "start-release": "run-s build-token build-prod-min build-css print-release-url start-server", + "diff-tarball": "build/run-node build/diff-tarball && echo \"Please confirm the above is correct [y/n]? \"; read answer; if [ \"$answer\" = \"${answer#[Yy]}\" ]; then false; fi", "lint": "eslint --cache --ignore-path .gitignore src test bench debug/*.html", "lint-docs": "documentation lint src/index.js", "lint-css": "stylelint 'src/css/mapbox-gl.css'", @@ -154,14 +157,15 @@ "test-expressions": "build/run-node test/expression.test.js", "test-flow": "build/run-node build/generate-flow-typed-style-spec && flow .", "test-cov": "nyc --require=@mapbox/flow-remove-types/register --reporter=text-summary --reporter=lcov --cache run-s test-unit test-expressions test-query test-render", - "prepublishOnly": "run-s build-flow-types build-dev build-prod-min build-prod build-csp build-css build-style-spec test-build", + "prepublishOnly": "run-s build-flow-types build-dev build-prod-min build-prod build-csp build-css build-style-spec test-build diff-tarball", "print-release-url": "node build/print-release-url.js", "codegen": "build/run-node build/generate-style-code.js && build/run-node build/generate-struct-arrays.js" }, "files": [ "build/", - "dist/", - "flow-typed/", + "dist/mapbox-gl*", + "dist/style-spec/", + "flow-typed/*.js", "src/", ".flowconfig" ] diff --git a/yarn.lock b/yarn.lock index 60a091a99e7..9d306d9edeb 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1914,7 +1914,14 @@ blob@0.0.5: resolved "https://registry.yarnpkg.com/blob/-/blob-0.0.5.tgz#d680eeef25f8cd91ad533f5b01eed48e64caf683" integrity sha512-gaqbzQPqOoamawKg0LGVd7SzLgXS+JH61oWprSLH+P+abTczqJbhTR8CmJ2u9/bUYNmHTGJx/UEmn6doAvvuig== -bluebird@^3.1.1, bluebird@^3.4.6: +block-stream@*: + version "0.0.9" + resolved "https://registry.yarnpkg.com/block-stream/-/block-stream-0.0.9.tgz#13ebfe778a03205cfe03751481ebb4b3300c126a" + integrity sha1-E+v+d4oDIFz+A3UUgeu0szAMEmo= + dependencies: + inherits "~2.0.0" + +bluebird@^3.1.1, bluebird@^3.4.6, bluebird@^3.5.0: version "3.7.2" resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.7.2.tgz#9f229c15be272454ffa973ace0dbee79a1b0c36f" integrity sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg== @@ -2293,6 +2300,14 @@ callsites@^3.0.0: resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== +camelcase-keys@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/camelcase-keys/-/camelcase-keys-2.1.0.tgz#308beeaffdf28119051efa1d932213c91b8f92e7" + integrity sha1-MIvur/3ygRkFHvodkyITyRuPkuc= + dependencies: + camelcase "^2.0.0" + map-obj "^1.0.0" + camelcase-keys@^4.0.0: version "4.2.0" resolved "https://registry.yarnpkg.com/camelcase-keys/-/camelcase-keys-4.2.0.tgz#a2aa5fb1af688758259c32c141426d78923b9b77" @@ -2302,6 +2317,11 @@ camelcase-keys@^4.0.0: map-obj "^2.0.0" quick-lru "^1.0.0" +camelcase@^2.0.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-2.1.1.tgz#7c1d16d679a1bbe59ca02cacecfb011e201f5a1f" + integrity sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8= + camelcase@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-4.1.0.tgz#d545635be1e33c542649c69173e5de6acfae34dd" @@ -2596,6 +2616,11 @@ color@^3.0.0: color-convert "^1.9.1" color-string "^1.5.2" +colors@~0.6.2: + version "0.6.2" + resolved "https://registry.yarnpkg.com/colors/-/colors-0.6.2.tgz#2423fe6678ac0c5dae8852e5d0e5be08c997abcc" + integrity sha1-JCP+ZnisDF2uiFLl0OW+CMmXq8w= + combine-source-map@^0.8.0, combine-source-map@~0.8.0: version "0.8.0" resolved "https://registry.yarnpkg.com/combine-source-map/-/combine-source-map-0.8.0.tgz#a58d0df042c186fcf822a8e8015f5450d2d79a8b" @@ -2830,6 +2855,13 @@ create-ecdh@^4.0.0: bn.js "^4.1.0" elliptic "^6.0.0" +create-error-class@^3.0.0: + version "3.0.2" + resolved "https://registry.yarnpkg.com/create-error-class/-/create-error-class-3.0.2.tgz#06be7abef947a3f14a30fd610671d401bca8b7b6" + integrity sha1-Br56vvlHo/FKMP1hBnHUAbyot7Y= + dependencies: + capture-stack-trace "^1.0.0" + create-hash@^1.1.0, create-hash@^1.1.2: version "1.2.0" resolved "https://registry.yarnpkg.com/create-hash/-/create-hash-1.2.0.tgz#889078af11a63756bcfb59bd221996be3a9ef196" @@ -3458,7 +3490,7 @@ decamelize-keys@^1.0.0: decamelize "^1.1.0" map-obj "^1.0.0" -decamelize@^1.1.0, decamelize@^1.2.0: +decamelize@^1.1.0, decamelize@^1.1.2, decamelize@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" integrity sha1-9lNNFRSCabIDUue+4m9QH5oZEpA= @@ -3828,6 +3860,11 @@ duplexer2@^0.1.2, duplexer2@~0.1.0, duplexer2@~0.1.2: dependencies: readable-stream "^2.0.2" +duplexer3@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/duplexer3/-/duplexer3-0.1.4.tgz#ee01dd1cac0ed3cbc7fdbea37dc0a8f1ce002ce2" + integrity sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI= + duplexify@^3.6.0: version "3.7.1" resolved "https://registry.yarnpkg.com/duplexify/-/duplexify-3.7.1.tgz#2a4df5317f6ccfd91f86d6fd25d8d8a103b88309" @@ -4531,6 +4568,14 @@ find-cache-dir@^2.0.0: make-dir "^2.0.0" pkg-dir "^3.0.0" +find-up@^1.0.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-1.1.2.tgz#6b2e9822b1a2ce0a60ab64d610eccad53cb24d0f" + integrity sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8= + dependencies: + path-exists "^2.0.0" + pinkie-promise "^2.0.0" + find-up@^2.0.0, find-up@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/find-up/-/find-up-2.1.0.tgz#45d1b7e506c717ddd482775a2b77920a3c0c57a7" @@ -4693,6 +4738,16 @@ fsevents@~2.1.2: resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.1.2.tgz#4c0a1fb34bc68e543b4b82a9ec392bfbda840805" integrity sha512-R4wDiBwZ0KzpgOWetKDug1FZcYhqYnUYKtfZYt4mD5SBz76q0KR4Q9o7GIPamsVPGmW3EYPPJ0dOOjvx32ldZA== +fstream@^1.0.12: + version "1.0.12" + resolved "https://registry.yarnpkg.com/fstream/-/fstream-1.0.12.tgz#4e8ba8ee2d48be4f7d0de505455548eae5932045" + integrity sha512-WvJ193OHa0GHPEL+AycEJgxvBEwyfRkN1vhjca23OaPVMCaLCXTd5qAu82AjTcgP1UJmytkOKb63Ypde7raDIg== + dependencies: + graceful-fs "^4.1.2" + inherits "~2.0.0" + mkdirp ">=0.5 0" + rimraf "2" + function-bind@^1.1.1, function-bind@~1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" @@ -4742,16 +4797,33 @@ get-caller-file@^1.0.1: resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-1.0.3.tgz#f978fa4c90d1dfe7ff2d6beda2a515e713bdcf4a" integrity sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w== +get-npm-tarball-url@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/get-npm-tarball-url/-/get-npm-tarball-url-2.0.1.tgz#43c15223c35096e3e4068d8a6c6747bbdfc23462" + integrity sha512-POrVRGyS9X5w+855/H46JGVYBGuVgJXyIkbsTCzW+sv5x2qH+rfQjc7652DzkgOskF+cqLevA2En7V0hu0gZCg== + dependencies: + normalize-registry-url "^1.0.0" + get-port@^4.0.0: version "4.2.0" resolved "https://registry.yarnpkg.com/get-port/-/get-port-4.2.0.tgz#e37368b1e863b7629c43c5a323625f95cf24b119" integrity sha512-/b3jarXkH8KJoOMQc3uVGHASwGLPq3gSFJ7tgJm2diza+bydJPTGOibin2steecKeOylE8oY2JERlVWkAJO6yw== +get-stdin@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-4.0.1.tgz#b968c6b0a04384324902e8bf1a5df32579a450fe" + integrity sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4= + get-stdin@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-6.0.0.tgz#9e09bf712b360ab9225e812048f71fde9c89657b" integrity sha512-jp4tHawyV7+fkkSKyvjuLZswblUtz+SQKzSWnBbii16BuZksJlU1wuBYXY75r+duh/llF1ur6oNwi+2ZzjKZ7g== +get-stream@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-3.0.0.tgz#8e943d1358dc37555054ecbe2edb05aa174ede14" + integrity sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ= + get-stream@^4.0.0: version "4.1.0" resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-4.1.0.tgz#c1b255575f3dc21d59bfc79cd3d2b46b1c3a54b5" @@ -4869,7 +4941,7 @@ glob@^5.0.14: once "^1.3.0" path-is-absolute "^1.0.0" -glob@^7.0.3, glob@^7.0.4, glob@^7.0.5, glob@^7.1.0, glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4, glob@~7.1.6: +glob@^7.0.3, glob@^7.0.4, glob@^7.0.5, glob@^7.1.0, glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6, glob@~7.1.6: version "7.1.6" resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6" integrity sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA== @@ -4940,6 +5012,23 @@ gonzales-pe@^4.2.3: dependencies: minimist "1.1.x" +got@^6.7.1: + version "6.7.1" + resolved "https://registry.yarnpkg.com/got/-/got-6.7.1.tgz#240cd05785a9a18e561dc1b44b41c763ef1e8db0" + integrity sha1-JAzQV4WpoY5WHcG0S0HHY+8ejbA= + dependencies: + create-error-class "^3.0.0" + duplexer3 "^0.1.4" + get-stream "^3.0.0" + is-redirect "^1.0.0" + is-retry-allowed "^1.0.0" + is-stream "^1.0.0" + lowercase-keys "^1.0.0" + safe-buffer "^5.0.1" + timed-out "^4.0.0" + unzip-response "^2.0.1" + url-parse-lax "^1.0.0" + graceful-fs@^4.0.0, graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.1.6: version "4.2.3" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.3.tgz#4a12ff1b60376ef09862c2093edd908328be8423" @@ -5257,7 +5346,7 @@ ieee754@^1.1.12, ieee754@^1.1.4: resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.1.13.tgz#ec168558e95aa181fd87d37f55c32bbcb6708b84" integrity sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg== -ignore-walk@^3.0.1: +ignore-walk@^3.0.1, ignore-walk@^3.0.3: version "3.0.3" resolved "https://registry.yarnpkg.com/ignore-walk/-/ignore-walk-3.0.3.tgz#017e2447184bfeade7c238e4aefdd1e8f95b1e37" integrity sha512-m7o6xuOaT1aqheYHKf8W6J5pYH85ZI9w077erOzLje3JsB1gkafkAhHHY19dqjulgIZHFm32Cp5uNZgcQqdJKw== @@ -5319,6 +5408,13 @@ imurmurhash@^0.1.4: resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" integrity sha1-khi5srkoojixPcT7a21XbyMUU+o= +indent-string@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-2.1.0.tgz#8e2d48348742121b4a8218b7a137e9a52049dc80" + integrity sha1-ji1INIdCEhtKghi3oTfppSBJ3IA= + dependencies: + repeating "^2.0.0" + indent-string@^3.0.0: version "3.2.0" resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-3.2.0.tgz#4a5fd6d27cc332f37e5419a504dbb837105c9289" @@ -5342,7 +5438,7 @@ inflight@^1.0.4: once "^1.3.0" wrappy "1" -inherits@2, inherits@2.0.4, inherits@^2.0.0, inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.1, inherits@~2.0.3, inherits@~2.0.4: +inherits@2, inherits@2.0.4, inherits@^2.0.0, inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.0, inherits@~2.0.1, inherits@~2.0.3, inherits@~2.0.4: version "2.0.4" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== @@ -5602,6 +5698,11 @@ is-extglob@^2.1.0, is-extglob@^2.1.1: resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" integrity sha1-qIwCU1eR8C7TfHahueqXc8gz+MI= +is-finite@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-finite/-/is-finite-1.1.0.tgz#904135c77fb42c0641d6aa1bcdbc4daa8da082f3" + integrity sha512-cdyMtqX/BOqqNBBiKlIVkytNHm49MtMlYyn1zxzvJKWmFMlGzm+ry5BBfYyeY9YmNKbRSo/o7OX9w9ale0wg3w== + is-fullwidth-code-point@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz#ef9e31386f031a7f0d643af82fde50c457ef00cb" @@ -5689,6 +5790,11 @@ is-promise@^2.1.0: resolved "https://registry.yarnpkg.com/is-promise/-/is-promise-2.1.0.tgz#79a2a9ece7f096e80f36d2b2f3bc16c1ff4bf3fa" integrity sha1-eaKp7OfwlugPNtKy87wWwf9L8/o= +is-redirect@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-redirect/-/is-redirect-1.0.0.tgz#1d03dded53bd8db0f30c26e4f95d36fc7c87dc24" + integrity sha1-HQPd7VO9jbDzDCbk+V02/HyH3CQ= + is-reference@^1.1.2: version "1.1.4" resolved "https://registry.yarnpkg.com/is-reference/-/is-reference-1.1.4.tgz#3f95849886ddb70256a3e6d062b1a68c13c51427" @@ -5720,6 +5826,11 @@ is-resolvable@^1.0.0: resolved "https://registry.yarnpkg.com/is-resolvable/-/is-resolvable-1.1.0.tgz#fb18f87ce1feb925169c9a407c19318a3206ed88" integrity sha512-qgDYXFSR5WvEfuS5dMj6oTMEbrrSaM0CrFk2Yiq/gXnBvD9pMa2jGXxyhGLfvhZpuMZe18CJpFxAt3CRs42NMg== +is-retry-allowed@^1.0.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/is-retry-allowed/-/is-retry-allowed-1.2.0.tgz#d778488bd0a4666a3be8a1482b9f2baafedea8b4" + integrity sha512-RUbUeKwvm3XG2VYamhJL1xFktgjvPzL0Hq8C+6yrWIswDy3BIXGqCxhxkc30N9jqK311gVU137K8Ei55/zVJRg== + is-ssh@^1.3.0: version "1.3.1" resolved "https://registry.yarnpkg.com/is-ssh/-/is-ssh-1.3.1.tgz#f349a8cadd24e65298037a522cf7520f2e81a0f3" @@ -5727,7 +5838,7 @@ is-ssh@^1.3.0: dependencies: protocols "^1.1.0" -is-stream@^1.0.1, is-stream@^1.1.0: +is-stream@^1.0.0, is-stream@^1.0.1, is-stream@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" integrity sha1-EtSj3U5o4Lec6428hBc66A2RykQ= @@ -5775,7 +5886,12 @@ is-unc-path@^1.0.0: dependencies: unc-path-regex "^0.1.2" -is-utf8@^0.2.1: +is-url@^1.2.2: + version "1.2.4" + resolved "https://registry.yarnpkg.com/is-url/-/is-url-1.2.4.tgz#04a4df46d28c4cff3d73d01ff06abeb318a1aa52" + integrity sha512-ITvGim8FhRiYe4IQ5uHSkj7pVaPDrCTkNd3yq3cV7iZAcJdHTUMPMEHcqSOy9xZ9qFenQCvi+2wjH9a1nXqHww== + +is-utf8@^0.2.0, is-utf8@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/is-utf8/-/is-utf8-0.2.1.tgz#4b0da1442104d1b336340e80797e865cf39f7d72" integrity sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI= @@ -6207,11 +6323,36 @@ lie@~3.3.0: dependencies: immediate "~3.0.5" +list-npm-contents@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/list-npm-contents/-/list-npm-contents-1.0.2.tgz#4fadf7a0e2c894fad92fcfc0092b0cec69444683" + integrity sha512-cpAkA9+ioEqHnxTuh3UDRewX+obC3mTr9dlYRVnTt0riggK+0IdKIed7BPn1BgkBQP+TVHiso4Rj0ZxGaXzh1Q== + dependencies: + bluebird "^3.5.0" + get-npm-tarball-url "^2.0.0" + got "^6.7.1" + is-url "^1.2.2" + ls-archive "^1.2.3" + meow "^3.3.0" + registry-url "^4.0.0" + tmp "0.0.31" + livereload-js@^2.3.0: version "2.4.0" resolved "https://registry.yarnpkg.com/livereload-js/-/livereload-js-2.4.0.tgz#447c31cf1ea9ab52fc20db615c5ddf678f78009c" integrity sha512-XPQH8Z2GDP/Hwz2PCDrh2mth4yFejwA1OZ/81Ti3LgKyhDcEjsSsqFWZojHG0va/duGd+WyosY7eXLDoOyqcPw== +load-json-file@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-1.1.0.tgz#956905708d58b4bab4c2261b04f59f31c99374c0" + integrity sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA= + dependencies: + graceful-fs "^4.1.2" + parse-json "^2.2.0" + pify "^2.0.0" + pinkie-promise "^2.0.0" + strip-bom "^2.0.0" + load-json-file@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-2.0.0.tgz#7947e42149af80d696cbf797bcaabcfe1fe29ca8" @@ -6455,6 +6596,11 @@ loud-rejection@^1.0.0: currently-unhandled "^0.4.1" signal-exit "^3.0.0" +lowercase-keys@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-1.0.1.tgz#6f9e30b47084d971a7c820ff15a6c5167b74c26f" + integrity sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA== + lru-cache@^4.0.0, lru-cache@^4.0.1: version "4.1.5" resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.1.5.tgz#8bbe50ea85bed59bc9e33dcab8235ee9bcf443cd" @@ -6463,6 +6609,18 @@ lru-cache@^4.0.0, lru-cache@^4.0.1: pseudomap "^1.0.2" yallist "^2.1.2" +ls-archive@^1.2.3: + version "1.3.4" + resolved "https://registry.yarnpkg.com/ls-archive/-/ls-archive-1.3.4.tgz#52150919dab1acb094cdcef9dde9c66934a4650f" + integrity sha512-7GmjZOckV+gzm4PM1/LcWIsZIRsSkAVmIchoEf5xjquNKU0Ti5KUvGQ3dl/7VsbZIduMOPwRDXrvpo3LVJ0Pmg== + dependencies: + async "~0.2.9" + colors "~0.6.2" + optimist "~0.5.2" + rimraf "~2.2.6" + tar "^2.2.1" + yauzl "^2.9.1" + macos-release@^2.2.0: version "2.3.0" resolved "https://registry.yarnpkg.com/macos-release/-/macos-release-2.3.0.tgz#eb1930b036c0800adebccd5f17bc4c12de8bb71f" @@ -6507,7 +6665,7 @@ map-cache@^0.2.0, map-cache@^0.2.2: resolved "https://registry.yarnpkg.com/map-cache/-/map-cache-0.2.2.tgz#c32abd0bd6525d9b051645bb4f26ac5dc98a0dbf" integrity sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8= -map-obj@^1.0.0: +map-obj@^1.0.0, map-obj@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/map-obj/-/map-obj-1.0.1.tgz#d933ceb9205d82bdcf4886f6742bdc2b4dea146d" integrity sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0= @@ -6642,6 +6800,22 @@ memorystream@^0.3.1: resolved "https://registry.yarnpkg.com/memorystream/-/memorystream-0.3.1.tgz#86d7090b30ce455d63fbae12dda51a47ddcaf9b2" integrity sha1-htcJCzDORV1j+64S3aUaR93K+bI= +meow@^3.3.0: + version "3.7.0" + resolved "https://registry.yarnpkg.com/meow/-/meow-3.7.0.tgz#72cb668b425228290abbfa856892587308a801fb" + integrity sha1-cstmi0JSKCkKu/qFaJJYcwioAfs= + dependencies: + camelcase-keys "^2.0.0" + decamelize "^1.1.2" + loud-rejection "^1.0.0" + map-obj "^1.0.1" + minimist "^1.1.3" + normalize-package-data "^2.3.4" + object-assign "^4.0.1" + read-pkg-up "^1.0.1" + redent "^1.0.0" + trim-newlines "^1.0.0" + meow@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/meow/-/meow-5.0.0.tgz#dfc73d63a9afc714a5e371760eb5c88b91078aa4" @@ -6788,7 +6962,7 @@ minimist@1.2.0: resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284" integrity sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ= -minimist@^1.1.0, minimist@^1.1.1, minimist@^1.2.0, minimist@^1.2.5, minimist@~1.2.0: +minimist@^1.1.0, minimist@^1.1.1, minimist@^1.1.3, minimist@^1.2.0, minimist@^1.2.5, minimist@~1.2.0: version "1.2.5" resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602" integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw== @@ -6828,6 +7002,13 @@ mkdirp-classic@^0.5.2: resolved "https://registry.yarnpkg.com/mkdirp-classic/-/mkdirp-classic-0.5.2.tgz#54c441ce4c96cd7790e10b41a87aa51068ecab2b" integrity sha512-ejdnDQcR75gwknmMw/tx02AuRs8jCtqFoFqDZMjiNxsu85sRIJVXDKHuLYvUUPRBUtV2FpSZa9bL1BUa3BdR2g== +"mkdirp@>=0.5 0": + version "0.5.5" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.5.tgz#d91cefd62d1436ca0f41620e251288d420099def" + integrity sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ== + dependencies: + minimist "^1.2.5" + mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@^0.5.4, mkdirp@~0.5.1: version "0.5.4" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.4.tgz#fd01504a6797ec5c9be81ff43d204961ed64a512" @@ -7025,22 +7206,6 @@ node-notifier@^5.0.1, node-notifier@^5.4.3: shellwords "^0.1.1" which "^1.3.0" -node-pre-gyp@*: - version "0.14.0" - resolved "https://registry.yarnpkg.com/node-pre-gyp/-/node-pre-gyp-0.14.0.tgz#9a0596533b877289bcad4e143982ca3d904ddc83" - integrity sha512-+CvDC7ZttU/sSt9rFjix/P05iS43qHCOOGzcr3Ry99bXG7VX953+vFyEuph/tfqoYu8dttBkE86JSKBO2OzcxA== - dependencies: - detect-libc "^1.0.2" - mkdirp "^0.5.1" - needle "^2.2.1" - nopt "^4.0.1" - npm-packlist "^1.1.6" - npmlog "^4.0.2" - rc "^1.2.7" - rimraf "^2.6.1" - semver "^5.3.0" - tar "^4.4.2" - node-pre-gyp@^0.11.0: version "0.11.0" resolved "https://registry.yarnpkg.com/node-pre-gyp/-/node-pre-gyp-0.11.0.tgz#db1f33215272f692cd38f03238e3e9b47c5dd054" @@ -7119,6 +7284,11 @@ normalize-range@^0.1.2: resolved "https://registry.yarnpkg.com/normalize-range/-/normalize-range-0.1.2.tgz#2d10c06bdfd312ea9777695a4d28439456b75942" integrity sha1-LRDAa9/TEuqXd2laTShDlFa3WUI= +normalize-registry-url@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/normalize-registry-url/-/normalize-registry-url-1.0.0.tgz#f75d2c48373da780c76f1f0eeb6382c06e784d13" + integrity sha512-0v6T4851b72ykk5zEtFoN4QX/Fqyk7pouIj9xZyAvAe9jlDhAwT4z6FlwsoQCHjeuK2EGUoAwy/F4y4B1uZq9A== + normalize-selector@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/normalize-selector/-/normalize-selector-0.2.0.tgz#d0b145eb691189c63a78d201dc4fdb1293ef0c03" @@ -7136,7 +7306,7 @@ now-and-later@^2.0.0: dependencies: once "^1.3.2" -npm-bundled@^1.0.1: +npm-bundled@^1.0.1, npm-bundled@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/npm-bundled/-/npm-bundled-1.1.1.tgz#1edd570865a94cdb1bc8220775e29466c9fb234b" integrity sha512-gqkfgGePhTpAEgUsGEgcq1rqPXA+tv/aVBlgEzfXwA1yiUJF7xtEt3CtVwOjNYQOVknDk0F20w58Fnm3EtG0fA== @@ -7162,6 +7332,16 @@ npm-packlist@^1.1.6: npm-bundled "^1.0.1" npm-normalize-package-bin "^1.0.1" +npm-packlist@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/npm-packlist/-/npm-packlist-2.1.1.tgz#08806a1df79acdc43d02d20c83a3d5472d96c90c" + integrity sha512-95TSDvGwujIhqfSpIiRRLodEF+y6mJMopuZdahoGzqtRDFZXGav46S0p6ngeWaiAkb5R72w6eVARhzej0HvZeQ== + dependencies: + glob "^7.1.6" + ignore-walk "^3.0.3" + npm-bundled "^1.1.1" + npm-normalize-package-bin "^1.0.1" + npm-run-all@^4.1.5: version "4.1.5" resolved "https://registry.yarnpkg.com/npm-run-all/-/npm-run-all-4.1.5.tgz#04476202a15ee0e2e214080861bff12a51d98fba" @@ -7251,7 +7431,7 @@ oauth-sign@~0.9.0: resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.9.0.tgz#47a7b016baa68b5fa0ecf3dee08a85c679ac6455" integrity sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ== -object-assign@^4.1.0, object-assign@^4.1.1: +object-assign@^4.0.1, object-assign@^4.1.0, object-assign@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM= @@ -7388,6 +7568,13 @@ opener@^1.5.1: resolved "https://registry.yarnpkg.com/opener/-/opener-1.5.1.tgz#6d2f0e77f1a0af0032aca716c2c1fbb8e7e8abed" integrity sha512-goYSy5c2UXE4Ra1xixabeVh1guIX/ZV/YokJksb6q2lubWu6UbvPQ20p542/sFIll1nl8JnCyK9oBaOcCWXwvA== +optimist@~0.5.2: + version "0.5.2" + resolved "https://registry.yarnpkg.com/optimist/-/optimist-0.5.2.tgz#85c8c1454b3315e4a78947e857b1df033450bfbc" + integrity sha1-hcjBRUszFeSniUfoV7HfAzRQv7w= + dependencies: + wordwrap "~0.0.2" + optionator@^0.8.1, optionator@^0.8.2: version "0.8.3" resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.3.tgz#84fa1d036fe9d3c7e21d99884b601167ec8fb495" @@ -7651,6 +7838,13 @@ path-dirname@^1.0.0: resolved "https://registry.yarnpkg.com/path-dirname/-/path-dirname-1.0.2.tgz#cc33d24d525e099a5388c0336c6e32b9160609e0" integrity sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA= +path-exists@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-2.1.0.tgz#0feb6c64f0fc518d9a754dd5efb62c7022761f4b" + integrity sha1-D+tsZPD8UY2adU3V77YscCJ2H0s= + dependencies: + pinkie-promise "^2.0.0" + path-exists@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-3.0.0.tgz#ce0ebeaa5f78cb18925ea7d810d7b59b010fd515" @@ -7705,6 +7899,15 @@ path-to-regexp@^1.7.0: dependencies: isarray "0.0.1" +path-type@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/path-type/-/path-type-1.1.0.tgz#59c44f7ee491da704da415da5a4070ba4f8fe441" + integrity sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE= + dependencies: + graceful-fs "^4.1.2" + pify "^2.0.0" + pinkie-promise "^2.0.0" + path-type@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/path-type/-/path-type-2.0.0.tgz#f012ccb8415b7096fc2daa1054c3d72389594c73" @@ -7773,6 +7976,18 @@ pify@^4.0.0, pify@^4.0.1: resolved "https://registry.yarnpkg.com/pify/-/pify-4.0.1.tgz#4b2cd25c50d598735c50292224fd8c6df41e3231" integrity sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g== +pinkie-promise@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/pinkie-promise/-/pinkie-promise-2.0.1.tgz#2135d6dfa7a358c069ac9b178776288228450ffa" + integrity sha1-ITXW36ejWMBprJsXh3YogihFD/o= + dependencies: + pinkie "^2.0.0" + +pinkie@^2.0.0: + version "2.0.4" + resolved "https://registry.yarnpkg.com/pinkie/-/pinkie-2.0.4.tgz#72556b80cfa0d48a974e80e77248e80ed4f7f870" + integrity sha1-clVrgM+g1IqXToDnckjoDtT3+HA= + pirates@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/pirates/-/pirates-3.0.2.tgz#7e6f85413fd9161ab4e12b539b06010d85954bb9" @@ -8272,6 +8487,11 @@ prelude-ls@~1.1.2: resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" integrity sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ= +prepend-http@^1.0.1: + version "1.0.4" + resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-1.0.4.tgz#d4f4562b0ce3696e41ac52d0e002e57a635dc6dc" + integrity sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw= + pretty-bytes@^5.1.0: version "5.3.0" resolved "https://registry.yarnpkg.com/pretty-bytes/-/pretty-bytes-5.3.0.tgz#f2849e27db79fb4d6cfe24764fc4134f165989f2" @@ -8558,6 +8778,14 @@ read-only-stream@^2.0.0: dependencies: readable-stream "^2.0.2" +read-pkg-up@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-1.0.1.tgz#9d63c13276c065918d57f002a57f40a1b643fb02" + integrity sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI= + dependencies: + find-up "^1.0.0" + read-pkg "^1.0.0" + read-pkg-up@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-2.0.0.tgz#6b72a8048984e0c41e79510fd5e9fa99b3b549be" @@ -8582,6 +8810,15 @@ read-pkg-up@^4.0.0: find-up "^3.0.0" read-pkg "^3.0.0" +read-pkg@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-1.1.0.tgz#f5ffaa5ecd29cb31c0474bca7d756b6bb29e3f28" + integrity sha1-9f+qXs0pyzHAR0vKfXVra7KePyg= + dependencies: + load-json-file "^1.0.0" + normalize-package-data "^2.3.2" + path-type "^1.0.0" + read-pkg@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-2.0.0.tgz#8ef1c0623c6a6db0dc6713c4bfac46332b2368f8" @@ -8673,6 +8910,14 @@ readdirp@~3.3.0: dependencies: picomatch "^2.0.7" +redent@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/redent/-/redent-1.0.0.tgz#cf916ab1fd5f1f16dfb20822dd6ec7f730c2afde" + integrity sha1-z5Fqsf1fHxbfsggi3W7H9zDCr94= + dependencies: + indent-string "^2.1.0" + strip-indent "^1.0.1" + redent@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/redent/-/redent-2.0.0.tgz#c1b2007b42d57eb1389079b3c8333639d5e1ccaa" @@ -8744,6 +8989,13 @@ regextras@^0.6.1: resolved "https://registry.yarnpkg.com/regextras/-/regextras-0.6.1.tgz#9689641bbb338e0ff7001a5c507c6a2008df7b36" integrity sha512-EzIHww9xV2Kpqx+corS/I7OBmf2rZ0pKKJPsw5Dc+l6Zq1TslDmtRIP9maVn3UH+72MIXmn8zzDgP07ihQogUA== +registry-url@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/registry-url/-/registry-url-4.0.0.tgz#7dc344ef0f1496fc95a6ad04ccb9a491df11c025" + integrity sha512-WAfGLywivb8s2+Cfblq1UV+kOyzURHzWSJmciDvrmstr4bv/0lnVSB9jfoOfkxx5xNJ1OGlSFmZh9WYBLFJOPg== + dependencies: + rc "^1.2.7" + regjsgen@^0.5.1: version "0.5.1" resolved "https://registry.yarnpkg.com/regjsgen/-/regjsgen-0.5.1.tgz#48f0bf1a5ea205196929c0d9798b42d1ed98443c" @@ -8929,6 +9181,13 @@ repeat-string@^1.5.0, repeat-string@^1.5.4, repeat-string@^1.6.1: resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637" integrity sha1-jcrkcOHIirwtYA//Sndihtp15jc= +repeating@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/repeating/-/repeating-2.0.1.tgz#5214c53a926d3552707527fbab415dbc08d06dda" + integrity sha1-UhTFOpJtNVJwdSf7q0FdvAjQbdo= + dependencies: + is-finite "^1.0.0" + replace-ext@1.0.0, replace-ext@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/replace-ext/-/replace-ext-1.0.0.tgz#de63128373fcbf7c3ccfa4de5a480c45a67958eb" @@ -9081,6 +9340,11 @@ rimraf@2.6.3: dependencies: glob "^7.1.3" +rimraf@~2.2.6: + version "2.2.8" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.2.8.tgz#e439be2aaee327321952730f99a8929e4fc50582" + integrity sha1-5Dm+Kq7jJzIZUnMPmaiSnk/FBYI= + ripemd160@^2.0.0, ripemd160@^2.0.1: version "2.0.2" resolved "https://registry.yarnpkg.com/ripemd160/-/ripemd160-2.0.2.tgz#a1c1a6f624751577ba5d07914cbc92850585890c" @@ -9903,6 +10167,13 @@ strip-ansi@~0.1.0: resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-0.1.1.tgz#39e8a98d044d150660abe4a6808acf70bb7bc991" integrity sha1-OeipjQRNFQZgq+SmgIrPcLt7yZE= +strip-bom@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-2.0.0.tgz#6219a85616520491f35788bdbf1447a99c7e6b0e" + integrity sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4= + dependencies: + is-utf8 "^0.2.0" + strip-bom@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3" @@ -9913,6 +10184,13 @@ strip-eof@^1.0.0: resolved "https://registry.yarnpkg.com/strip-eof/-/strip-eof-1.0.0.tgz#bb43ff5598a6eb05d89b59fcd129c983313606bf" integrity sha1-u0P/VZim6wXYm1n80SnJgzE2Br8= +strip-indent@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/strip-indent/-/strip-indent-1.0.1.tgz#0c7962a6adefa7bbd4ac366460a638552ae1a0a2" + integrity sha1-DHlipq3vp7vUrDZkYKY4VSrhoKI= + dependencies: + get-stdin "^4.0.1" + strip-indent@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/strip-indent/-/strip-indent-2.0.0.tgz#5ef8db295d01e6ed6cbf7aab96998d7822527b68" @@ -10234,7 +10512,16 @@ tar-stream@^2.0.0: inherits "^2.0.3" readable-stream "^3.1.1" -tar@^4, tar@^4.4.2, tar@^4.4.8: +tar@^2.2.1: + version "2.2.2" + resolved "https://registry.yarnpkg.com/tar/-/tar-2.2.2.tgz#0ca8848562c7299b8b446ff6a4d60cdbb23edc40" + integrity sha512-FCEhQ/4rE1zYv9rYXJw/msRqsnmlje5jHP6huWeBZ704jUTy02c5AZyWujpMR1ax6mVw9NyJMfuK2CMDWVIfgA== + dependencies: + block-stream "*" + fstream "^1.0.12" + inherits "2" + +tar@^4, tar@^4.4.8: version "4.4.13" resolved "https://registry.yarnpkg.com/tar/-/tar-4.4.13.tgz#43b364bc52888d555298637b10d60790254ab525" integrity sha512-w2VwSrBoHa5BsSyH+KxEqeQBAllHhccyMFVHtGtdMpF4W7IRWfZjFiQceJPChOeTsSDVUpER2T8FA93pr0L+QA== @@ -10335,6 +10622,11 @@ through2@^2.0.0, through2@^2.0.3, through2@~2.0.0: resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" integrity sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU= +timed-out@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/timed-out/-/timed-out-4.0.1.tgz#f32eacac5a175bea25d7fab565ab3ed8741ef56f" + integrity sha1-8y6srFoXW+ol1/q1Zas+2HQe9W8= + timers-browserify@^1.0.1: version "1.4.2" resolved "https://registry.yarnpkg.com/timers-browserify/-/timers-browserify-1.4.2.tgz#c9c58b575be8407375cb5e2462dacee74359f41d" @@ -10376,6 +10668,13 @@ tmp@0.0.30: dependencies: os-tmpdir "~1.0.1" +tmp@0.0.31: + version "0.0.31" + resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.31.tgz#8f38ab9438e17315e5dbd8b3657e8bfb277ae4a7" + integrity sha1-jzirlDjhcxXl29izZX6L+yd65Kc= + dependencies: + os-tmpdir "~1.0.1" + tmp@0.0.33, tmp@^0.0.33: version "0.0.33" resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9" @@ -10465,6 +10764,11 @@ trim-lines@^1.0.0: resolved "https://registry.yarnpkg.com/trim-lines/-/trim-lines-1.1.3.tgz#839514be82428fd9e7ec89e35081afe8f6f93115" integrity sha512-E0ZosSWYK2mkSu+KEtQ9/KqarVjA9HztOSX+9FDdNacRAq29RRV6ZQNgob3iuW8Htar9vAfEa6yyt5qBAHZDBA== +trim-newlines@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/trim-newlines/-/trim-newlines-1.0.0.tgz#5887966bb582a4503a41eb524f7d35011815a613" + integrity sha1-WIeWa7WCpFA6QetST301ARgVphM= + trim-newlines@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/trim-newlines/-/trim-newlines-2.0.0.tgz#b403d0b91be50c331dfc4b82eeceb22c3de16d20" @@ -10799,6 +11103,11 @@ unset-value@^1.0.0: has-value "^0.3.1" isobject "^3.0.0" +unzip-response@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/unzip-response/-/unzip-response-2.0.1.tgz#d2f0f737d16b0615e72a6935ed04214572d56f97" + integrity sha1-0vD3N9FrBhXnKmk17QQhRXLVb5c= + upath@^1.1.1: version "1.2.0" resolved "https://registry.yarnpkg.com/upath/-/upath-1.2.0.tgz#8f66dbcd55a883acdae4408af8b035a5044c1894" @@ -10816,6 +11125,13 @@ urix@^0.1.0: resolved "https://registry.yarnpkg.com/urix/-/urix-0.1.0.tgz#da937f7a62e21fec1fd18d49b35c2935067a6c72" integrity sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI= +url-parse-lax@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/url-parse-lax/-/url-parse-lax-1.0.0.tgz#7af8f303645e9bd79a272e7a14ac68bc0609da73" + integrity sha1-evjzA2Rem9eaJy56FKxovAYJ2nM= + dependencies: + prepend-http "^1.0.1" + url@~0.11.0: version "0.11.0" resolved "https://registry.yarnpkg.com/url/-/url-0.11.0.tgz#3838e97cfc60521eb73c525a8e55bfdd9e2e28f1" @@ -11152,6 +11468,11 @@ word-wrap@~1.2.3: resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c" integrity sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ== +wordwrap@~0.0.2: + version "0.0.3" + resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-0.0.3.tgz#a3d5da6cd5c0bc0008d37234bbaf1bed63059107" + integrity sha1-o9XabNXAvAAI03I0u68b7WMFkQc= + wrap-ansi@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-2.1.0.tgz#d8fc3d284dd05794fe84973caecdd1cf824fdd85" @@ -11307,7 +11628,7 @@ yargs@^12.0.1, yargs@^12.0.2, yargs@^12.0.5: y18n "^3.2.1 || ^4.0.0" yargs-parser "^11.1.1" -yauzl@^2.10.0: +yauzl@^2.10.0, yauzl@^2.9.1: version "2.10.0" resolved "https://registry.yarnpkg.com/yauzl/-/yauzl-2.10.0.tgz#c7eb17c93e112cb1086fa6d8e51fb0667b79a5f9" integrity sha1-x+sXyT4RLLEIb6bY5R+wZnt5pfk= From ced93c1624e37e618024f621a1744ff24b279c26 Mon Sep 17 00:00:00 2001 From: Brave Cow Date: Wed, 15 Apr 2020 20:56:22 +0300 Subject: [PATCH 19/50] fix example typo (#9568) --- src/ui/handler/shim/drag_pan.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ui/handler/shim/drag_pan.js b/src/ui/handler/shim/drag_pan.js index 97e9281b773..2b55e42539f 100644 --- a/src/ui/handler/shim/drag_pan.js +++ b/src/ui/handler/shim/drag_pan.js @@ -42,7 +42,7 @@ export default class DragPanHandler { * @example * map.dragPan.enable(); * @example - * map.dragpan.enable({ + * map.dragPan.enable({ * linearity: 0.3, * easing: bezier(0, 0, 0.3, 1), * maxSpeed: 1400, From 01a9929f2027484842eb91908ec18d07cdfb02bd Mon Sep 17 00:00:00 2001 From: Karim Naaji Date: Tue, 21 Apr 2020 09:28:09 -0700 Subject: [PATCH 20/50] Clean check in `prepublishOnly` (#9590) * Clean check in prepublishOnly Runs clean check as a first step in prepublishOnly. This ensures a clean directory before building the build content and before the manual diffing with previously published tarball. * Address comments --- package.json | 5 +++-- src/style-spec/package.json | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index 3593663a6e5..c0530b568b7 100644 --- a/package.json +++ b/package.json @@ -127,7 +127,7 @@ "build-prod-min": "rollup -c --environment BUILD:production,MINIFY:true", "build-csp": "rollup -c rollup.config.csp.js", "build-query-suite": "rollup -c test/integration/rollup.config.test.js", - "build-flow-types": "cp build/mapbox-gl.js.flow dist/mapbox-gl.js.flow && cp build/mapbox-gl.js.flow dist/mapbox-gl-dev.js.flow", + "build-flow-types": "mkdir -p dist && cp build/mapbox-gl.js.flow dist/mapbox-gl.js.flow && cp build/mapbox-gl.js.flow dist/mapbox-gl-dev.js.flow", "build-css": "postcss -o dist/mapbox-gl.css src/css/mapbox-gl.css", "build-style-spec": "cd src/style-spec && npm run build && cd ../.. && mkdir -p dist/style-spec && cp src/style-spec/dist/* dist/style-spec", "watch-css": "postcss --watch -o dist/mapbox-gl.css src/css/mapbox-gl.css", @@ -141,6 +141,7 @@ "start-bench": "run-p build-token watch-benchmarks start-server", "start-release": "run-s build-token build-prod-min build-css print-release-url start-server", "diff-tarball": "build/run-node build/diff-tarball && echo \"Please confirm the above is correct [y/n]? \"; read answer; if [ \"$answer\" = \"${answer#[Yy]}\" ]; then false; fi", + "prepare-publish": "git clean -fdx && yarn install", "lint": "eslint --cache --ignore-path .gitignore src test bench debug/*.html", "lint-docs": "documentation lint src/index.js", "lint-css": "stylelint 'src/css/mapbox-gl.css'", @@ -157,7 +158,7 @@ "test-expressions": "build/run-node test/expression.test.js", "test-flow": "build/run-node build/generate-flow-typed-style-spec && flow .", "test-cov": "nyc --require=@mapbox/flow-remove-types/register --reporter=text-summary --reporter=lcov --cache run-s test-unit test-expressions test-query test-render", - "prepublishOnly": "run-s build-flow-types build-dev build-prod-min build-prod build-csp build-css build-style-spec test-build diff-tarball", + "prepublishOnly": "run-s prepare-publish build-flow-types build-dev build-prod-min build-prod build-csp build-css build-style-spec test-build diff-tarball", "print-release-url": "node build/print-release-url.js", "codegen": "build/run-node build/generate-style-code.js && build/run-node build/generate-struct-arrays.js" }, diff --git a/src/style-spec/package.json b/src/style-spec/package.json index 6cc58c1b784..0ee3833018a 100644 --- a/src/style-spec/package.json +++ b/src/style-spec/package.json @@ -14,7 +14,7 @@ "scripts": { "copy-flow-typed": "cp -R ../../flow-typed .", "build": "../../node_modules/.bin/rollup -c && ../../node_modules/.bin/rollup -c --environment esm", - "prepublish": "yarn copy-flow-typed && yarn build", + "prepublish": "git clean -fdx && yarn copy-flow-typed && yarn build", "postpublish": "rm -r flow-typed dist/index.js" }, "repository": { From 9f632602fbd14940204025dbccb5b53eb058ae45 Mon Sep 17 00:00:00 2001 From: Adriana Babakanian Date: Tue, 21 Apr 2020 14:46:57 -0700 Subject: [PATCH 21/50] Add scale to Marker options (#9414) --- debug/markers.html | 3 +++ src/ui/marker.js | 15 ++++++++++++--- test/unit/ui/marker.test.js | 29 +++++++++++++++++++++++++++++ 3 files changed, 44 insertions(+), 3 deletions(-) diff --git a/debug/markers.html b/debug/markers.html index 3e04b46804f..3f125d141f9 100644 --- a/debug/markers.html +++ b/debug/markers.html @@ -45,8 +45,11 @@ var g = Math.round(Math.random() * 255); var b = Math.round(Math.random() * 255); + var sc = Math.random() * 2.5 + 0.5; + var marker = new mapboxgl.Marker({ color: `rgb(${r}, ${g}, ${b})`, + scale: sc, draggable: true, rotationAlignment, pitchAlignment diff --git a/src/ui/marker.js b/src/ui/marker.js index 55484adb013..8c52a04aa31 100644 --- a/src/ui/marker.js +++ b/src/ui/marker.js @@ -19,6 +19,7 @@ type Options = { offset?: PointLike, anchor?: Anchor, color?: string, + scale?: number, draggable?: boolean, rotation?: number, rotationAlignment?: string, @@ -33,6 +34,7 @@ type Options = { * Options are `'center'`, `'top'`, `'bottom'`, `'left'`, `'right'`, `'top-left'`, `'top-right'`, `'bottom-left'`, and `'bottom-right'`. * @param {PointLike} [options.offset] The offset in pixels as a {@link PointLike} object to apply relative to the element's center. Negatives indicate left and up. * @param {string} [options.color='#3FB1CE'] The color to use for the default marker if options.element is not provided. The default is light blue. + * @param {number} [options.scale=1] The scale to use for the default marker if options.element is not provided. The default scale corresponds to a height of `41px` and a width of `27px`. * @param {boolean} [options.draggable=false] A boolean indicating whether or not a marker is able to be dragged to a new position on the map. * @param {number} [options.rotation=0] The rotation angle of the marker in degrees, relative to its respective `rotationAlignment` setting. A positive value will rotate the marker clockwise. * @param {string} [options.pitchAlignment='auto'] `map` aligns the `Marker` to the plane of the map. `viewport` aligns the `Marker` to the plane of the viewport. `auto` automatically matches the value of `rotationAlignment`. @@ -53,6 +55,7 @@ export default class Marker extends Evented { _lngLat: LngLat; _pos: ?Point; _color: ?string; + _scale: number; _defaultMarker: boolean; _draggable: boolean; _state: 'inactive' | 'pending' | 'active'; // used for handling drag events @@ -81,6 +84,7 @@ export default class Marker extends Evented { this._anchor = options && options.anchor || 'center'; this._color = options && options.color || '#3FB1CE'; + this._scale = options && options.scale || 1; this._draggable = options && options.draggable || false; this._state = 'inactive'; this._rotation = options && options.rotation || 0; @@ -94,10 +98,12 @@ export default class Marker extends Evented { // create default map marker SVG const svg = DOM.createNS('http://www.w3.org/2000/svg', 'svg'); + const defaultHeight = 41; + const defaultWidth = 27; svg.setAttributeNS(null, 'display', 'block'); - svg.setAttributeNS(null, 'height', '41px'); - svg.setAttributeNS(null, 'width', '27px'); - svg.setAttributeNS(null, 'viewBox', '0 0 27 41'); + svg.setAttributeNS(null, 'height', `${defaultHeight}px`); + svg.setAttributeNS(null, 'width', `${defaultWidth}px`); + svg.setAttributeNS(null, 'viewBox', `0 0 ${defaultWidth} ${defaultHeight}`); const markerLarge = DOM.createNS('http://www.w3.org/2000/svg', 'g'); markerLarge.setAttributeNS(null, 'stroke', 'none'); @@ -181,6 +187,9 @@ export default class Marker extends Evented { svg.appendChild(page1); + svg.setAttributeNS(null, 'height', `${defaultHeight * this._scale}px`); + svg.setAttributeNS(null, 'width', `${defaultWidth * this._scale}px`); + this._element.appendChild(svg); // if no element and no offset option given apply an offset for the default marker diff --git a/test/unit/ui/marker.test.js b/test/unit/ui/marker.test.js index 3fed1cf3bfd..20376e22dd4 100644 --- a/test/unit/ui/marker.test.js +++ b/test/unit/ui/marker.test.js @@ -27,6 +27,35 @@ test('Marker uses a default marker element with custom color', (t) => { t.end(); }); +test('Marker uses a default marker element with custom scale', (t) => { + const map = createMap(t); + const defaultMarker = new Marker() + .setLngLat([0, 0]) + .addTo(map); + // scale smaller than default + const smallerMarker = new Marker({scale: 0.8}) + .setLngLat([0, 0]) + .addTo(map); + // scale larger than default + const largerMarker = new Marker({scale: 2}) + .setLngLat([0, 0]) + .addTo(map); + + // initial dimensions of svg element + t.ok(defaultMarker.getElement().firstChild.getAttribute('height').includes('41')); + t.ok(defaultMarker.getElement().firstChild.getAttribute('width').includes('27')); + + // (41 * 0.8) = 32.8, (27 * 0.8) = 21.6 + t.ok(smallerMarker.getElement().firstChild.getAttribute('height').includes(`32.8`)); + t.ok(smallerMarker.getElement().firstChild.getAttribute('width').includes(`21.6`)); + + // (41 * 2) = 82, (27 * 2) = 54 + t.ok(largerMarker.getElement().firstChild.getAttribute('height').includes('82')); + t.ok(largerMarker.getElement().firstChild.getAttribute('width').includes('54')); + + t.end(); +}); + test('Marker uses a default marker with custom offset', (t) => { const marker = new Marker({offset: [1, 2]}); t.ok(marker.getElement()); From a732f3b102335fec9665223eca3eba5df26fd6e3 Mon Sep 17 00:00:00 2001 From: Adriana Babakanian Date: Wed, 22 Apr 2020 09:42:11 -0700 Subject: [PATCH 22/50] Change return type to boolean for StyleImageInterface type alias (#9604) --- src/style/style_image.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/style/style_image.js b/src/style/style_image.js index 6905a196998..6c327fa6f42 100644 --- a/src/style/style_image.js +++ b/src/style/style_image.js @@ -25,7 +25,7 @@ export type StyleImageInterface = { width: number, height: number, data: Uint8Array | Uint8ClampedArray, - render?: () => void, + render?: () => boolean, onAdd?: (map: Map, id: string) => void, onRemove?: () => void }; From 459566a08a1c91875bb6bcaff9952043d783201a Mon Sep 17 00:00:00 2001 From: zmiao Date: Wed, 22 Apr 2020 21:06:16 +0300 Subject: [PATCH 23/50] Fix using `within` expression with complex `filter` (#9611) * Fix within usage with othere expressions in the same filter * Add render tests * Fix filter operator checking for nested condition --- src/style-spec/feature_filter/index.js | 12 ++- src/style-spec/reference/v8.json | 3 + src/style-spec/validate/validate_filter.js | 10 +- .../within-in-complex-filter/expected.png | Bin 0 -> 393 bytes .../within-in-complex-filter/style.json | 102 ++++++++++++++++++ 5 files changed, 124 insertions(+), 3 deletions(-) create mode 100644 test/integration/render-tests/within/within-in-complex-filter/expected.png create mode 100644 test/integration/render-tests/within/within-in-complex-filter/style.json diff --git a/src/style-spec/feature_filter/index.js b/src/style-spec/feature_filter/index.js index ffe1662d3b5..a096d14ed73 100644 --- a/src/style-spec/feature_filter/index.js +++ b/src/style-spec/feature_filter/index.js @@ -85,7 +85,7 @@ function createFilter(filter: any): FeatureFilter { if (compiled.result === 'error') { throw new Error(compiled.value.map(err => `${err.key}: ${err.message}`).join(', ')); } else { - const needGeometry = Array.isArray(filter) && filter.length !== 0 && filter[0] === 'within'; + const needGeometry = geometryNeeded(filter); return {filter: (globalProperties: GlobalProperties, feature: Feature, canonical?: CanonicalTileID) => compiled.value.evaluate(globalProperties, feature, {}, canonical), needGeometry}; } @@ -96,6 +96,15 @@ function compare(a, b) { return a < b ? -1 : a > b ? 1 : 0; } +function geometryNeeded(filter) { + if (!Array.isArray(filter)) return false; + if (filter[0] === 'within') return true; + for (let index = 1; index < filter.length; index++) { + if (geometryNeeded(filter[index])) return true; + } + return false; +} + function convertFilter(filter: ?Array): mixed { if (!filter) return true; const op = filter[0]; @@ -114,6 +123,7 @@ function convertFilter(filter: ?Array): mixed { op === '!in' ? convertNegation(convertInOp(filter[1], filter.slice(2))) : op === 'has' ? convertHasOp(filter[1]) : op === '!has' ? convertNegation(convertHasOp(filter[1])) : + op === 'within' ? filter : true; return converted; } diff --git a/src/style-spec/reference/v8.json b/src/style-spec/reference/v8.json index b7a7b35c70b..8b12accf811 100644 --- a/src/style-spec/reference/v8.json +++ b/src/style-spec/reference/v8.json @@ -2443,6 +2443,9 @@ }, "!has": { "doc": "`[\"!has\", key]` `feature[key]` does not exist" + }, + "within": { + "doc": "`[\"within\", object]` feature geometry is within object geometry" } }, "doc": "The filter operator." diff --git a/src/style-spec/validate/validate_filter.js b/src/style-spec/validate/validate_filter.js index 470ce40731a..804b7baac38 100644 --- a/src/style-spec/validate/validate_filter.js +++ b/src/style-spec/validate/validate_filter.js @@ -104,8 +104,14 @@ function validateNonExpressionFilter(options) { errors.push(new ValidationError(`${key}[1]`, value[1], `string expected, ${type} found`)); } break; - + case 'within': + type = getType(value[1]); + if (value.length !== 2) { + errors.push(new ValidationError(key, value, `filter array for "${value[0]}" operator must have 2 elements`)); + } else if (type !== 'object') { + errors.push(new ValidationError(`${key}[1]`, value[1], `object expected, ${type} found`)); + } + break; } - return errors; } diff --git a/test/integration/render-tests/within/within-in-complex-filter/expected.png b/test/integration/render-tests/within/within-in-complex-filter/expected.png new file mode 100644 index 0000000000000000000000000000000000000000..7f3f172cee2d680e5ec6ae12740877da4e81e079 GIT binary patch literal 393 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7T#lEU<~kdaSW+oe0xwa`%0k<`-A6p zzol8*#Ud?I9eLThk6u?`Yfgw1Xui85+UNGCadCmjh zW1|0mJl^;JxuL`rvrRAdT{S Date: Fri, 24 Apr 2020 09:56:38 -0700 Subject: [PATCH 24/50] Trigger memory metrics build on CircleCI (#9622) --- .circleci/config.yml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/.circleci/config.yml b/.circleci/config.yml index 51cb3e1dd8f..ae559eb988a 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -157,6 +157,14 @@ jobs: - run: yarn run build-style-spec - run: yarn run build-flow-types - run: yarn run test-build + - deploy: + name: Trigger memory metrics when merging to master + command: | + if [ -n "${WEB_METRICS_TOKEN}" ]; then + if [[ $CIRCLE_BRANCH == master ]]; then + curl -X POST https://circleci.com/api/v1.1/project/github/mapbox/web-metrics/build?circle-token=${WEB_METRICS_TOKEN} + fi + fi - store_artifacts: path: "dist" - store_artifacts: From d0f42e4274a0972f27c1e7f1546aea58491d2a4c Mon Sep 17 00:00:00 2001 From: Dan Swick Date: Fri, 24 Apr 2020 14:24:20 -0700 Subject: [PATCH 25/50] Documentation improvements sprint (#9607) Various documentation enhancements from the docs sprints: * [docsprint] Clarify meaning of Map#isSourceLoaded (#9589) * [docsprint] Add geolocate trigger example (#9552) * [docsprint] Add inline snippet to marker#addTo (#9592) * [docsprint] Add inline snippet and related examples to popup.setHTML (#9538) * add inline snippet and related examples to popup.setHTML * update see URLs to docs subdomain * Add inline and related example for panTo (#9547) * Add inline snippet to LngLatBounds.contains (#9548) * [docsprint] Add inline snippet and examples to map.jumpTo (#9549) * add inline snippet and examples to map.jumpTo * minor reorder to match options and text * added inline and tutorial examples for map.getCenter (#9551) * add example for Marker class setLngLat (#9553) * added examples to get- and setLayoutProperty (#9554) * add inline snippet and examples to map.triggerRepaint (#9555) * CameraOptions - update pitch and bearing definitions, add inline code example, add example links (#9556) * add inline code snippet to map.showTileBoundaries (#9557) * Mark Debug namespace as private to hide it from the docs (#9558) * [docsprint] Add detail to docs on setFeatureState, removeFeatureState, getFeatureState (#9559) * add detail to docs on setFeatureState, removeFeatureState, getFeatureState * remove trailing spaces * [docsprint] map.on: remove extra on section, link to event types, add code snippet, add related (#9560) * add examples to open and close popup events (#9565) * [docsprint] Clarify how map.moveLayer works (#9566) * clarify how map.moveLayer works * fix id capitalization * [doscprint] add links to EPSG and minor example changes for LngLat (#9570) * add links to EPSG and minor example changes for LngLat * document the layer object's properties in addLayer (#9571) * [docsprint] Add inline snippet and examples to popup.addTo (#9572) * added inline example to popup-addto * added relevant examples to popup.addto * Add inline example for trackPointer (#9575) * Add inline example for Popup#getElement (#9576) * [docsprint] Add inline examples for Point & PointLike types (#9577) * Add inline examples for Point & PointLike types * Correct syntax * [docsprint] Cleanup doc for MapBoxZoomEvent (#9564) * Describe map style object returned by Map#getStyle (#9579) * [docsprint] Add inline example for getClusterLeaves (#9580) * [docsprint] PaddingOptions - update definition, example, and related (#9581) * PaddingOptions - update definition, example, and related * Fixes formatting issues * [docsprint] Add inline snippet to marker#setPopup, marker#getPopup, and marker#togglePopup (#9582) * update setPopup * update togglePopup * update formatting * update setFilter jsdoc (#9586) * add more details to getSource (#9587) * add clearStorage example (#9588) * [docsprint] Add documentation for RequestParameters (#9573) * [docsprint] Add inline example for setZoomRate and setWheelZoomRate of scrollZoomHandler (#9593) * Add inline example for setZoomRate of scrollZoomHandler * add setWheelZoomRate inline example * Add inline examples for map zoom-related methods (#9594) * [docsprint] Add example to MapMouseEvent (#9595) * add example to MapMouseEvent * Add examples for GeolocateControl events (#9596) * [docsprint] Add example to MapDataEvent (#9597) * add example to MapDataEvent * add example for getLngLat (#9591) * suggested edits for clarifying mouse events * Small grammar fix: "optional the `layerId`" -> "the optional `layerId" * Update pitch param definition; add Display buildings in 3D example * remove example object, light formatting and copyediting Co-Authored-By: Dan Swick Co-authored-by: Adriana Babakanian Co-authored-by: Sam Fader Co-authored-by: Heather Stenson Co-authored-by: Katy DeCorah Co-authored-by: jbranigan Co-authored-by: Jeremy Stratman Co-authored-by: Mal Wood-Santoro Co-authored-by: Asheem Mamoowala Co-authored-by: Colleen McGinnis Co-authored-by: geografa Co-authored-by: Patrick Leonard Co-authored-by: David Wicks Co-authored-by: Deven Diliberto Co-authored-by: Colleen Co-authored-by: Karim Naaji --- src/geo/lng_lat.js | 7 +- src/geo/lng_lat_bounds.js | 9 + src/index.js | 2 + src/source/geojson_source.js | 16 + src/ui/camera.js | 85 ++++- src/ui/control/geolocate_control.js | 96 ++++- src/ui/events.js | 527 +++++++++++++++++++++++++++- src/ui/handler/scroll_zoom.js | 12 +- src/ui/map.js | 391 +++++++++++++++++---- src/ui/marker.js | 60 +++- src/ui/popup.js | 55 ++- src/util/ajax.js | 16 + src/util/debug.js | 2 + 13 files changed, 1159 insertions(+), 119 deletions(-) diff --git a/src/geo/lng_lat.js b/src/geo/lng_lat.js index 971214994a3..e978f348cee 100644 --- a/src/geo/lng_lat.js +++ b/src/geo/lng_lat.js @@ -12,8 +12,10 @@ export const earthRadius = 6371008.8; /** * A `LngLat` object represents a given longitude and latitude coordinate, measured in degrees. + * These coordinates are based on the [WGS84 (EPSG:4326) standard](https://en.wikipedia.org/wiki/World_Geodetic_System#WGS84). * - * Mapbox GL uses longitude, latitude coordinate order (as opposed to latitude, longitude) to match GeoJSON. + * Mapbox GL uses longitude, latitude coordinate order (as opposed to latitude, longitude) to match the + * [GeoJSON specification](https://tools.ietf.org/html/rfc7946). * * Note that any Mapbox GL method that accepts a `LngLat` object as an argument or option * can also accept an `Array` of two numbers and will perform an implicit conversion. @@ -22,7 +24,8 @@ export const earthRadius = 6371008.8; * @param {number} lng Longitude, measured in degrees. * @param {number} lat Latitude, measured in degrees. * @example - * var ll = new mapboxgl.LngLat(-73.9749, 40.7736); + * var ll = new mapboxgl.LngLat(-123.9749, 40.7736); + * ll.lng; // = -123.9749 * @see [Get coordinates of the mouse pointer](https://www.mapbox.com/mapbox-gl-js/example/mouse-position/) * @see [Display a popup](https://www.mapbox.com/mapbox-gl-js/example/popup/) * @see [Highlight features within a bounding box](https://www.mapbox.com/mapbox-gl-js/example/using-box-queryrenderedfeatures/) diff --git a/src/geo/lng_lat_bounds.js b/src/geo/lng_lat_bounds.js index 7fbf4db9c4a..f2360e96db6 100644 --- a/src/geo/lng_lat_bounds.js +++ b/src/geo/lng_lat_bounds.js @@ -216,6 +216,15 @@ class LngLatBounds { * * @param {LngLatLike} lnglat geographic point to check against. * @returns {boolean} True if the point is within the bounding box. + * @example + * var llb = new mapboxgl.LngLatBounds( + * new mapboxgl.LngLat(-73.9876, 40.7661), + * new mapboxgl.LngLat(-73.9397, 40.8002) + * ); + * + * var ll = new mapboxgl.LngLat(-73.9567, 40.7789); + * + * console.log(llb.contains(ll)); // = true */ contains(lnglat: LngLatLike) { const {lng, lat} = LngLat.convert(lnglat); diff --git a/src/index.js b/src/index.js index c85f5def198..bdc8b97f348 100644 --- a/src/index.js +++ b/src/index.js @@ -163,6 +163,8 @@ const exported = { * * @function clearStorage * @param {Function} callback Called with an error argument if there is an error. + * @example + * mapboxgl.clearStorage(); */ clearStorage(callback?: (err: ?Error) => void) { clearTileCache(callback); diff --git a/src/source/geojson_source.js b/src/source/geojson_source.js index 2b043f1ef91..67cfab6f8a8 100644 --- a/src/source/geojson_source.js +++ b/src/source/geojson_source.js @@ -230,6 +230,22 @@ class GeoJSONSource extends Evented implements Source { * @param offset The number of features to skip (e.g. for pagination). * @param callback A callback to be called when the features are retrieved (`(error, features) => { ... }`). * @returns {GeoJSONSource} this + * @example + * // Retrieve cluster leaves on click + * map.on('click', 'clusters', function(e) { + * var features = map.queryRenderedFeatures(e.point, { + * layers: ['clusters'] + * }); + * + * var clusterId = features[0].properties.cluster_id; + * var pointCount = features[0].properties.point_count; + * var clusterSource = map.getSource('clusters'); + * + * clusterSource.getClusterLeaves(clusterId, pointCount, 0, function(error, features) { + * // Print cluster leaves in the console + * console.log('Cluster leaves:', error, features); + * }) + * }); */ getClusterLeaves(clusterId: number, limit: number, offset: number, callback: Callback>) { this.actor.send('geojson.getClusterLeaves', { diff --git a/src/ui/camera.js b/src/ui/camera.js index e78443f14d9..40bfad0dc9e 100644 --- a/src/ui/camera.js +++ b/src/ui/camera.js @@ -33,11 +33,28 @@ import type {PaddingOptions} from '../geo/edge_insets'; * @typedef {Object} CameraOptions * @property {LngLatLike} center The desired center. * @property {number} zoom The desired zoom level. - * @property {number} bearing The desired bearing, in degrees. The bearing is the compass direction that - * is "up"; for example, a bearing of 90° orients the map so that east is up. - * @property {number} pitch The desired pitch, in degrees. + * @property {number} bearing The desired bearing in degrees. The bearing is the compass direction that + * is "up". For example, `bearing: 90` orients the map so that east is up. + * @property {number} pitch The desired pitch in degrees. The pitch is the angle towards the horizon + * measured in degrees with a range between 0 and 60 degrees. For example, pitch: 0 provides the appearance + * of looking straight down at the map, while pitch: 60 tilts the user's perspective towards the horizon. + * Increasing the pitch value is often used to display 3D objects. * @property {LngLatLike} around If `zoom` is specified, `around` determines the point around which the zoom is centered. * @property {PaddingOptions} padding Dimensions in pixels applied on each side of the viewport for shifting the vanishing point. + * @example + * // set the map's initial perspective with CameraOptions + * var map = new mapboxgl.Map({ + * container: 'map', + * style: 'mapbox://styles/mapbox/streets-v11', + * center: [-73.5804, 45.53483], + * pitch: 60, + * bearing: -60, + * zoom: 10 + * }); + * @see [Set pitch and bearing](https://docs.mapbox.com/mapbox-gl-js/example/set-perspective/) + * @see [Jump to a series of locations](https://docs.mapbox.com/mapbox-gl-js/example/jump-to/) + * @see [Fly to a location](https://docs.mapbox.com/mapbox-gl-js/example/flyto/) + * @see [Display buildings in 3D](https://docs.mapbox.com/mapbox-gl-js/example/3d-buildings/) */ export type CameraOptions = { center?: LngLatLike, @@ -71,7 +88,7 @@ export type AnimationOptions = { }; /** - * Options for setting padding on a call to {@link Map#fitBounds}. All properties of this object must be + * Options for setting padding on calls to methods such as {@link Map#fitBounds}, {@link Map#fitScreenCoordinates}, and {@link Map#setPadding}. Adjust these options to set the amount of padding in pixels added to the edges of the canvas. Set a uniform padding on all edges or individual values for each edge. All properties of this object must be * non-negative integers. * * @typedef {Object} PaddingOptions @@ -79,6 +96,20 @@ export type AnimationOptions = { * @property {number} bottom Padding in pixels from the bottom of the map canvas. * @property {number} left Padding in pixels from the left of the map canvas. * @property {number} right Padding in pixels from the right of the map canvas. + * + * @example + * var bbox = [[-79, 43], [-73, 45]]; + * map.fitBounds(bbox, { + * padding: {top: 10, bottom:25, left: 15, right: 5} + * }); + * + * @example + * var bbox = [[-79, 43], [-73, 45]]; + * map.fitBounds(bbox, { + * padding: 20 + * }); + * @see [Fit to the bounds of a LineString](https://docs.mapbox.com/mapbox-gl-js/example/zoomto-linestring/) + * @see [Fit a map to a bounding box](https://docs.mapbox.com/mapbox-gl-js/example/fitbounds/) */ class Camera extends Evented { @@ -119,6 +150,12 @@ class Camera extends Evented { * * @memberof Map# * @returns The map's geographical centerpoint. + * @example + * // return a LngLat object such as {lng: 0, lat: 0} + * var center = map.getCenter(); + * // access longitude and latitude values directly + * var {longitude, latitude} = map.getCenter(); + * @see Tutorial: [Use Mapbox GL JS in a React app](https://docs.mapbox.com/help/tutorials/use-mapbox-gl-js-with-react/#store-the-new-coordinates) */ getCenter(): LngLat { return new LngLat(this.transform.center.lng, this.transform.center.lat); } @@ -156,15 +193,21 @@ class Camera extends Evented { } /** - * Pans the map to the specified location, with an animated transition. + * Pans the map to the specified location with an animated transition. * * @memberof Map# * @param lnglat The location to pan the map to. - * @param options Options object + * @param options Options describing the destination and animation of the transition. * @param eventData Additional properties to be added to event objects of events triggered by this method. * @fires movestart * @fires moveend * @returns {Map} `this` + * @example + * map.panTo([-74, 38]); + * @example + * // Specify that the panTo animation should last 5000 milliseconds. + * map.panTo([-74, 38], {duration: 5000}); + * @see [Update a feature in realtime](https://docs.mapbox.com/mapbox-gl-js/example/live-update-feature/) */ panTo(lnglat: LngLatLike, options?: AnimationOptions, eventData?: Object) { return this.easeTo(extend({ @@ -177,6 +220,8 @@ class Camera extends Evented { * * @memberof Map# * @returns The map's current zoom level. + * @example + * map.getZoom(); */ getZoom(): number { return this.transform.zoom; } @@ -194,7 +239,7 @@ class Camera extends Evented { * @fires zoomend * @returns {Map} `this` * @example - * // zoom the map to 5 + * // Zoom to the zoom level 5 without an animated transition * map.setZoom(5); */ setZoom(zoom: number, eventData?: Object) { @@ -216,6 +261,14 @@ class Camera extends Evented { * @fires moveend * @fires zoomend * @returns {Map} `this` + * @example + * // Zoom to the zoom level 5 without an animated transition + * map.zoomTo(5); + * // Zoom to the zoom level 8 with an animated transition + * map.zoomTo(8, { + * duration: 2000, + * offset: [100, 50] + * }); */ zoomTo(zoom: number, options: ? AnimationOptions, eventData?: Object) { return this.easeTo(extend({ @@ -236,6 +289,9 @@ class Camera extends Evented { * @fires moveend * @fires zoomend * @returns {Map} `this` + * @example + * // zoom the map in one level with a custom animation duration + * map.zoomIn({duration: 1000}); */ zoomIn(options?: AnimationOptions, eventData?: Object) { this.zoomTo(this.getZoom() + 1, options, eventData); @@ -255,6 +311,9 @@ class Camera extends Evented { * @fires moveend * @fires zoomend * @returns {Map} `this` + * @example + * // zoom the map out one level with a custom animation offset + * map.zoomOut({offset: [80, 60]}); */ zoomOut(options?: AnimationOptions, eventData?: Object) { this.zoomTo(this.getZoom() - 1, options, eventData); @@ -631,6 +690,18 @@ class Camera extends Evented { * @fires zoomend * @fires pitchend * @returns {Map} `this` + * @example + * // jump to coordinates at current zoom + * map.jumpTo({center: [0, 0]}); + * // jump with zoom, pitch, and bearing options + * map.jumpTo({ + * center: [0, 0], + * zoom: 8, + * pitch: 45, + * bearing: 90 + * }); + * @see [Jump to a series of locations](https://docs.mapbox.com/mapbox-gl-js/example/jump-to/) + * @see [Update a feature in realtime](https://docs.mapbox.com/mapbox-gl-js/example/live-update-feature/) */ jumpTo(options: CameraOptions, eventData?: Object) { this.stop(); diff --git a/src/ui/control/geolocate_control.js b/src/ui/control/geolocate_control.js index 0e5f5e80736..87a39f3848f 100644 --- a/src/ui/control/geolocate_control.js +++ b/src/ui/control/geolocate_control.js @@ -406,10 +406,23 @@ class GeolocateControl extends Evented { } /** - * Trigger a geolocation - * - * @returns {boolean} Returns `false` if called before control was added to a map, otherwise returns `true`. - */ + * Programmatically request and move the map to the user's location. + * + * @returns {boolean} Returns `false` if called before control was added to a map, otherwise returns `true`. + * @example + * // Initialize the geolocate control. + * var geolocate = new mapboxgl.GeolocateControl({ + * positionOptions: { + * enableHighAccuracy: true + * }, + * trackUserLocation: true + * }); + * // Add the control to the map. + * map.addControl(geolocate); + * map.on('load', function() { + * geolocate.trigger(); + * }); + */ trigger() { if (!this._setup) { warnOnce('Geolocate control triggered before added to a map'); @@ -552,6 +565,21 @@ export default GeolocateControl; * @memberof GeolocateControl * @instance * @property {Position} data The returned [Position](https://developer.mozilla.org/en-US/docs/Web/API/Position) object from the callback in [Geolocation.getCurrentPosition()](https://developer.mozilla.org/en-US/docs/Web/API/Geolocation/getCurrentPosition) or [Geolocation.watchPosition()](https://developer.mozilla.org/en-US/docs/Web/API/Geolocation/watchPosition). + * @example + * // Initialize the geolocate control. + * var geolocate = new mapboxgl.GeolocateControl({ + * positionOptions: { + * enableHighAccuracy: true + * }, + * trackUserLocation: true + * }); + * // Add the control to the map. + * map.addControl(geolocate); + * // Set an event listener that fires + * // when a geolocate event occurs. + * geolocate.on('geolocate', function() { + * console.log('A geolocate event has occurred.') + * }); * */ @@ -562,6 +590,21 @@ export default GeolocateControl; * @memberof GeolocateControl * @instance * @property {PositionError} data The returned [PositionError](https://developer.mozilla.org/en-US/docs/Web/API/PositionError) object from the callback in [Geolocation.getCurrentPosition()](https://developer.mozilla.org/en-US/docs/Web/API/Geolocation/getCurrentPosition) or [Geolocation.watchPosition()](https://developer.mozilla.org/en-US/docs/Web/API/Geolocation/watchPosition). + * @example + * // Initialize the geolocate control. + * var geolocate = new mapboxgl.GeolocateControl({ + * positionOptions: { + * enableHighAccuracy: true + * }, + * trackUserLocation: true + * }); + * // Add the control to the map. + * map.addControl(geolocate); + * // Set an event listener that fires + * // when an error event occurs. + * geolocate.on('error', function() { + * console.log('An error event has occurred.') + * }); * */ @@ -572,6 +615,21 @@ export default GeolocateControl; * @memberof GeolocateControl * @instance * @property {Position} data The returned [Position](https://developer.mozilla.org/en-US/docs/Web/API/Position) object from the callback in [Geolocation.getCurrentPosition()](https://developer.mozilla.org/en-US/docs/Web/API/Geolocation/getCurrentPosition) or [Geolocation.watchPosition()](https://developer.mozilla.org/en-US/docs/Web/API/Geolocation/watchPosition). + * @example + * // Initialize the geolocate control. + * var geolocate = new mapboxgl.GeolocateControl({ + * positionOptions: { + * enableHighAccuracy: true + * }, + * trackUserLocation: true + * }); + * // Add the control to the map. + * map.addControl(geolocate); + * // Set an event listener that fires + * // when an outofmaxbounds event occurs. + * geolocate.on('outofmaxbounds', function() { + * console.log('An outofmaxbounds event has occurred.') + * }); * */ @@ -581,6 +639,21 @@ export default GeolocateControl; * @event trackuserlocationstart * @memberof GeolocateControl * @instance + * @example + * // Initialize the geolocate control. + * var geolocate = new mapboxgl.GeolocateControl({ + * positionOptions: { + * enableHighAccuracy: true + * }, + * trackUserLocation: true + * }); + * // Add the control to the map. + * map.addControl(geolocate); + * // Set an event listener that fires + * // when a trackuserlocationstart event occurs. + * geolocate.on('trackuserlocationstart', function() { + * console.log('A trackuserlocationstart event has occurred.') + * }); * */ @@ -590,5 +663,20 @@ export default GeolocateControl; * @event trackuserlocationend * @memberof GeolocateControl * @instance + * @example + * // Initialize the geolocate control. + * var geolocate = new mapboxgl.GeolocateControl({ + * positionOptions: { + * enableHighAccuracy: true + * }, + * trackUserLocation: true + * }); + * // Add the control to the map. + * map.addControl(geolocate); + * // Set an event listener that fires + * // when a trackuserlocationend event occurs. + * geolocate.on('trackuserlocationend', function() { + * console.log('A trackuserlocationend event has occurred.') + * }); * */ diff --git a/src/ui/events.js b/src/ui/events.js index cf261c396fb..66abb6d81fd 100644 --- a/src/ui/events.js +++ b/src/ui/events.js @@ -12,10 +12,27 @@ import type LngLat from '../geo/lng_lat'; /** * `MapMouseEvent` is the event type for mouse-related map events. * @extends {Object} + * @example + * // The `click` event is an example of a `MapMouseEvent`. + * // Set up an event listener on the map. + * map.on('click', function(e) { + * // The event object (e) contains information like the + * // coordinates of the point on the map that was clicked. + * console.log('A click event has occurred at ' + e.lngLat); + * }); */ export class MapMouseEvent extends Event { /** - * The event type. + * The event type (one of [`mousedown`](#map.event:mousedown), + * [`mouseup`](#map.event:mouseup), + * [`click`](#map.event:click), + * [`dblclick`](#map.event:dblclick), + * [`mousemove`](#map.event:mousemove), + * [`mouseover`](#map.event:mouseover), + * [`mouseenter`](#map.event:mouseenter), + * [`mouseleave`](#map.event:mouseleave), + * [`mouseout`](#map.event:mouseout), + * [`contextmenu`](#map.event:contextmenu)). */ type: 'mousedown' | 'mouseup' @@ -218,17 +235,18 @@ export class MapWheelEvent extends Event { } /** - * A `MapBoxZoomEvent` is the event type for boxzoom-related map events. - * `originalEvent` can be a {@link Map.event:click} when the zoom is triggered by a UI event. + * A `MapBoxZoomEvent` is the event type for the boxzoom-related map events emitted by the {@link BoxZoomHandler}. * * @typedef {Object} MapBoxZoomEvent - * @property {MouseEvent} originalEvent + * @property {MouseEvent} originalEvent The DOM event that triggered the boxzoom event. Can be a `MouseEvent` or `KeyboardEvent` + * @property {string} type The type of boxzoom event. One of `boxzoomstart`, `boxzoomend` or `boxzoomcancel` + * @property {Map} target The `Map` instance that triggerred the event */ export type MapBoxZoomEvent = { type: 'boxzoomstart' | 'boxzoomend' | 'boxzoomcancel', - map: Map, + target: Map, originalEvent: MouseEvent }; @@ -251,6 +269,14 @@ export type MapBoxZoomEvent = { * the event is related to loading of a tile. * @property {Coordinate} [coord] The coordinate of the tile if the event has a `dataType` of `source` and * the event is related to loading of a tile. + * @example + * // The sourcedata event is an example of MapDataEvent. + * // Set up an event listener on the map. + * map.on('sourcedata', function(e) { + * if (e.isSourceLoaded) { + * // Do something when the source has finished loading + * } + * }); */ export type MapDataEvent = { type: string, @@ -266,34 +292,90 @@ export type MapEvent = /** * Fired when a pointing device (usually a mouse) is pressed within the map. * + * **Note:** This event is compatible with the optional `layerId` parameter. + * If `layerId` is included as the second argument in {@link Map#on}, the event listener will fire only when the + * the cursor is pressed while inside a visible portion of the specifed layer. + * * @event mousedown * @memberof Map * @instance * @property {MapMouseEvent} data - * @see [Highlight features within a bounding box](https://www.mapbox.com/mapbox-gl-js/example/using-box-queryrenderedfeatures/) - * @see [Create a draggable point](https://www.mapbox.com/mapbox-gl-js/example/drag-a-point/) + * @example + * // Initialize the map + * var map = new mapboxgl.Map({ // map options }); + * // Set an event listener + * map.on('mousedown', function() { + * console.log('A mousedown event has occurred.'); + * }); + * @example + * // Initialize the map + * var map = new mapboxgl.Map({ // map options }); + * // Set an event listener for a specific layer + * map.on('mousedown', 'poi-label', function() { + * console.log('A mousedown event has occurred on a visible portion of the poi-label layer.'); + * }); + * @see [Highlight features within a bounding box](https://docs.mapbox.com/mapbox-gl-js/example/using-box-queryrenderedfeatures/) + * @see [Create a draggable point](https://docs.mapbox.com/mapbox-gl-js/example/drag-a-point/) */ | 'mousedown' /** * Fired when a pointing device (usually a mouse) is released within the map. * + * **Note:** This event is compatible with the optional `layerId` parameter. + * If `layerId` is included as the second argument in {@link Map#on}, the event listener will fire only when the + * the cursor is released while inside a visible portion of the specifed layer. + * * @event mouseup * @memberof Map * @instance * @property {MapMouseEvent} data - * @see [Highlight features within a bounding box](https://www.mapbox.com/mapbox-gl-js/example/using-box-queryrenderedfeatures/) - * @see [Create a draggable point](https://www.mapbox.com/mapbox-gl-js/example/drag-a-point/) + * @example + * // Initialize the map + * var map = new mapboxgl.Map({ // map options }); + * // Set an event listener + * map.on('mouseup', function() { + * console.log('A mouseup event has occurred.'); + * }); + * @example + * // Initialize the map + * var map = new mapboxgl.Map({ // map options }); + * // Set an event listener for a specific layer + * map.on('mouseup', 'poi-label', function() { + * console.log('A mouseup event has occurred on a visible portion of the poi-label layer.'); + * }); + * @see [Highlight features within a bounding box](https://docs.mapbox.com/mapbox-gl-js/example/using-box-queryrenderedfeatures/) + * @see [Create a draggable point](https://docs.mapbox.com/mapbox-gl-js/example/drag-a-point/) */ | 'mouseup' /** * Fired when a pointing device (usually a mouse) is moved within the map. + * As you move the cursor across a web page containing a map, + * the event will fire each time it enters the map or any child elements. + * + * **Note:** This event is compatible with the optional `layerId` parameter. + * If `layerId` is included as the second argument in {@link Map#on}, the event listener will fire only when the + * the cursor is moved inside a visible portion of the specifed layer. * * @event mouseover * @memberof Map * @instance * @property {MapMouseEvent} data + * @example + * // Initialize the map + * var map = new mapboxgl.Map({ // map options }); + * // Set an event listener + * map.on('mouseover', function() { + * console.log('A mouseover event has occurred.'); + * }); + * @example + * // Initialize the map + * var map = new mapboxgl.Map({ // map options }); + * // Set an event listener for a specific layer + * map.on('mouseover', 'poi-label', function() { + * console.log('A mouseover event has occurred on a visible portion of the poi-label layer.'); + * }); * @see [Get coordinates of the mouse pointer](https://www.mapbox.com/mapbox-gl-js/example/mouse-position/) * @see [Highlight features under the mouse pointer](https://www.mapbox.com/mapbox-gl-js/example/hover-styles/) * @see [Display a popup on hover](https://www.mapbox.com/mapbox-gl-js/example/popup-on-hover/) @@ -301,12 +383,31 @@ export type MapEvent = | 'mouseover' /** - * Fired when a pointing device (usually a mouse) is moved within the map. + * Fired when a pointing device (usually a mouse) is moved while the cursor is inside the map. + * As you move the cursor across the map, the event will fire every time the cursor changes position within the map. + * + * **Note:** This event is compatible with the optional `layerId` parameter. + * If `layerId` is included as the second argument in {@link Map#on}, the event listener will fire only when the + * the cursor is inside a visible portion of the specified layer. * * @event mousemove * @memberof Map * @instance * @property {MapMouseEvent} data + * @example + * // Initialize the map + * var map = new mapboxgl.Map({ // map options }); + * // Set an event listener + * map.on('mousemove', function() { + * console.log('A mousemove event has occurred.'); + * }); + * @example + * // Initialize the map + * var map = new mapboxgl.Map({ // map options }); + * // Set an event listener for a specific layer + * map.on('mousemove', 'poi-label', function() { + * console.log('A mousemove event has occurred on a visible portion of the poi-label layer.'); + * }); * @see [Get coordinates of the mouse pointer](https://www.mapbox.com/mapbox-gl-js/example/mouse-position/) * @see [Highlight features under the mouse pointer](https://www.mapbox.com/mapbox-gl-js/example/hover-styles/) * @see [Display a popup on over](https://www.mapbox.com/mapbox-gl-js/example/popup-on-hover/) @@ -316,47 +417,107 @@ export type MapEvent = /** * Fired when a pointing device (usually a mouse) is pressed and released at the same point on the map. * + * **Note:** This event is compatible with the optional `layerId` parameter. + * If `layerId` is included as the second argument in {@link Map#on}, the event listener will fire only when the + * point that is pressed and released contains a visible portion of the specifed layer. + * * @event click * @memberof Map * @instance * @property {MapMouseEvent} data + * @example + * // Initialize the map + * var map = new mapboxgl.Map({ // map options }); + * // Set an event listener + * map.on('click', function(e) { + * console.log('A click event has occurred at ' + e.lngLat); + * }); + * @example + * // Initialize the map + * var map = new mapboxgl.Map({ // map options }); + * // Set an event listener for a specific layer + * map.on('click', 'poi-label', function(e) { + * console.log('A click event has occurred on a visible portion of the poi-label layer at ' + e.lngLat); + * }); * @see [Measure distances](https://www.mapbox.com/mapbox-gl-js/example/measure/) * @see [Center the map on a clicked symbol](https://www.mapbox.com/mapbox-gl-js/example/center-on-symbol/) */ | 'click' /** - * Fired when a pointing device (usually a mouse) is clicked twice at the same point on the map. + * Fired when a pointing device (usually a mouse) is pressed and released twice at the same point on + * the map in rapid succession. + * + * **Note:** This event is compatible with the optional `layerId` parameter. + * If `layerId` is included as the second argument in {@link Map#on}, the event listener will fire only + * when the point that is clicked twice contains a visible portion of the specifed layer. * * @event dblclick * @memberof Map * @instance * @property {MapMouseEvent} data + * @example + * // Initialize the map + * var map = new mapboxgl.Map({ // map options }); + * // Set an event listener + * map.on('dblclick', function(e) { + * console.log('A dblclick event has occurred at ' + e.lngLat); + * }); + * @example + * // Initialize the map + * var map = new mapboxgl.Map({ // map options }); + * // Set an event listener for a specific layer + * map.on('dblclick', 'poi-label', function(e) { + * console.log('A dblclick event has occurred on a visible portion of the poi-label layer at ' + e.lngLat); + * }); */ | 'dblclick' /** * Fired when a pointing device (usually a mouse) enters a visible portion of a specified layer from - * outside that layer or outside the map canvas. This event can only be listened for via the three-argument - * version of {@link Map#on}, where the second argument specifies the desired layer. + * outside that layer or outside the map canvas. + * + * **Important:** This event can only be listened for when {@link Map#on} includes three arguements, + * where the second argument specifies the desired layer. * * @event mouseenter * @memberof Map * @instance * @property {MapMouseEvent} data + * @example + * // Initialize the map + * var map = new mapboxgl.Map({ // map options }); + * // Set an event listener + * map.on('mouseenter', 'water', function() { + * console.log('A mouseenter event occurred on a visible portion of the water layer.'); + * }); + * @see [Center the map on a clicked symbol](https://docs.mapbox.com/mapbox-gl-js/example/center-on-symbol/) + * @see [Display a popup on click](https://docs.mapbox.com/mapbox-gl-js/example/popup-on-click/) */ | 'mouseenter' /** * Fired when a pointing device (usually a mouse) leaves a visible portion of a specified layer, or leaves - * the map canvas. This event can only be listened for via the three-argument version of {@link Map#on}, + * the map canvas. + * + * **Important:** This event can only be listened for when {@link Map#on} includes three arguements, * where the second argument specifies the desired layer. * * @event mouseleave * @memberof Map * @instance * @property {MapMouseEvent} data + * @example + * // Initialize the map + * var map = new mapboxgl.Map({ // map options }); + * // Set an event listener that fires + * // when the pointing device leaves + * // a visible portion of the specified layer. + * map.on('mouseleave', 'water', function() { + * console.log('A mouseleave event occurred.'); + * }); * @see [Highlight features under the mouse pointer](https://www.mapbox.com/mapbox-gl-js/example/hover-styles/) + * @see [Display a popup on click](https://docs.mapbox.com/mapbox-gl-js/example/popup-on-click/) */ | 'mouseleave' @@ -367,6 +528,15 @@ export type MapEvent = * @memberof Map * @instance * @property {MapMouseEvent} data + * @example + * // Initialize the map + * var map = new mapboxgl.Map({ // map options }); + * // Set an event listener that fires + * // when the pointing device leave's + * // the map's canvas. + * map.on('mouseout', function() { + * console.log('A mouseout event occurred.'); + * }); */ | 'mouseout' @@ -377,6 +547,15 @@ export type MapEvent = * @memberof Map * @instance * @property {MapMouseEvent} data + * @example + * // Initialize the map + * var map = new mapboxgl.Map({ // map options }); + * // Set an event listener that fires + * // when the right mouse button is + * // pressed within the map. + * map.on('contextmenu', function() { + * console.log('A contextmenu event occurred.'); + * }); */ | 'contextmenu' @@ -387,6 +566,14 @@ export type MapEvent = * @memberof Map * @instance * @property {MapWheelEvent} data + * @example + * // Initialize the map + * var map = new mapboxgl.Map({ // map options }); + * // Set an event listener that fires + * // when a wheel event occurs within the map. + * map.on('wheel', function() { + * console.log('A wheel event occurred.'); + * }); */ | 'wheel' @@ -397,6 +584,14 @@ export type MapEvent = * @memberof Map * @instance * @property {MapTouchEvent} data + * // Initialize the map + * var map = new mapboxgl.Map({ // map options }); + * // Set an event listener that fires + * // when a touchstart event occurs within the map. + * map.on('touchstart', function() { + * console.log('A touchstart event occurred.'); + * }); + * @see [Create a draggable point](https://docs.mapbox.com/mapbox-gl-js/example/drag-a-point/) */ | 'touchstart' @@ -407,6 +602,15 @@ export type MapEvent = * @memberof Map * @instance * @property {MapTouchEvent} data + * @example + * // Initialize the map + * var map = new mapboxgl.Map({ // map options }); + * // Set an event listener that fires + * // when a touchstart event occurs within the map. + * map.on('touchstart', function() { + * console.log('A touchstart event occurred.'); + * }); + * @see [Create a draggable point](https://docs.mapbox.com/mapbox-gl-js/example/drag-a-point/) */ | 'touchend' @@ -417,6 +621,15 @@ export type MapEvent = * @memberof Map * @instance * @property {MapTouchEvent} data + * @example + * // Initialize the map + * var map = new mapboxgl.Map({ // map options }); + * // Set an event listener that fires + * // when a touchmove event occurs within the map. + * map.on('touchmove', function() { + * console.log('A touchmove event occurred.'); + * }); + * @see [Create a draggable point](https://docs.mapbox.com/mapbox-gl-js/example/drag-a-point/) */ | 'touchmove' @@ -427,6 +640,14 @@ export type MapEvent = * @memberof Map * @instance * @property {MapTouchEvent} data + * @example + * // Initialize the map + * var map = new mapboxgl.Map({ // map options }); + * // Set an event listener that fires + * // when a touchcancel event occurs within the map. + * map.on('touchcancel', function() { + * console.log('A touchcancel event occurred.'); + * }); */ | 'touchcancel' @@ -438,6 +659,15 @@ export type MapEvent = * @memberof Map * @instance * @property {{originalEvent: DragEvent}} data + * @example + * // Initialize the map + * var map = new mapboxgl.Map({ // map options }); + * // Set an event listener that fires + * // just before the map begins a transition + * // from one view to another. + * map.on('movestart', function() { + * console.log('A movestart` event occurred.'); + * }); */ | 'movestart' @@ -449,6 +679,16 @@ export type MapEvent = * @memberof Map * @instance * @property {MapMouseEvent | MapTouchEvent} data + * @example + * // Initialize the map + * var map = new mapboxgl.Map({ // map options }); + * // Set an event listener that fires + * // repeatedly during an animated transition. + * map.on('move', function() { + * console.log('A move event occurred.'); + * }); + * @see [Display HTML clusters with custom properties](https://docs.mapbox.com/mapbox-gl-js/example/cluster-html/) + * @see [Filter features within map view](https://docs.mapbox.com/mapbox-gl-js/example/filter-features-within-map-view/) */ | 'move' @@ -460,8 +700,17 @@ export type MapEvent = * @memberof Map * @instance * @property {{originalEvent: DragEvent}} data + * @example + * // Initialize the map + * var map = new mapboxgl.Map({ // map options }); + * // Set an event listener that fires + * // just after the map completes a transition. + * map.on('moveend', function() { + * console.log('A moveend event occurred.'); + * }); * @see [Play map locations as a slideshow](https://www.mapbox.com/mapbox-gl-js/example/playback-locations/) * @see [Filter features within map view](https://www.mapbox.com/mapbox-gl-js/example/filter-features-within-map-view/) + * @see [Display HTML clusters with custom properties](https://docs.mapbox.com/mapbox-gl-js/example/cluster-html/) */ | 'moveend' @@ -472,6 +721,14 @@ export type MapEvent = * @memberof Map * @instance * @property {{originalEvent: DragEvent}} data + * @example + * // Initialize the map + * var map = new mapboxgl.Map({ // map options }); + * // Set an event listener that fires + * // when a "drag to pan" interaction starts. + * map.on('dragstart', function() { + * console.log('A dragstart event occurred.'); + * }); */ | 'dragstart' @@ -482,6 +739,14 @@ export type MapEvent = * @memberof Map * @instance * @property {MapMouseEvent | MapTouchEvent} data + * @example + * // Initialize the map + * var map = new mapboxgl.Map({ // map options }); + * // Set an event listener that fires + * // repeatedly during a "drag to pan" interaction. + * map.on('drag', function() { + * console.log('A drag event occurred.'); + * }); */ | 'drag' @@ -492,6 +757,15 @@ export type MapEvent = * @memberof Map * @instance * @property {{originalEvent: DragEvent}} data + * @example + * // Initialize the map + * var map = new mapboxgl.Map({ // map options }); + * // Set an event listener that fires + * // when a "drag to pan" interaction ends. + * map.on('dragend', function() { + * console.log('A dragend event occurred.'); + * }); + * @see [Create a draggable marker](https://docs.mapbox.com/mapbox-gl-js/example/drag-a-marker/) */ | 'dragend' @@ -503,6 +777,14 @@ export type MapEvent = * @memberof Map * @instance * @property {MapMouseEvent | MapTouchEvent} data + * @example + * // Initialize the map + * var map = new mapboxgl.Map({ // map options }); + * // Set an event listener that fires + * // just before a zoom transition starts. + * map.on('zoomstart', function() { + * console.log('A zoomstart event occurred.'); + * }); */ | 'zoomstart' @@ -514,6 +796,13 @@ export type MapEvent = * @memberof Map * @instance * @property {MapMouseEvent | MapTouchEvent} data + * // Initialize the map + * var map = new mapboxgl.Map({ // map options }); + * // Set an event listener that fires + * // repeatedly during a zoom transition. + * map.on('zoom', function() { + * console.log('A zoom event occurred.'); + * }); * @see [Update a choropleth layer by zoom level](https://www.mapbox.com/mapbox-gl-js/example/updating-choropleth/) */ | 'zoom' @@ -526,6 +815,14 @@ export type MapEvent = * @memberof Map * @instance * @property {MapMouseEvent | MapTouchEvent} data + * @example + * // Initialize the map + * var map = new mapboxgl.Map({ // map options }); + * // Set an event listener that fires + * // just after a zoom transition finishes. + * map.on('zoomend', function() { + * console.log('A zoomend event occurred.'); + * }); */ | 'zoomend' @@ -536,6 +833,14 @@ export type MapEvent = * @memberof Map * @instance * @property {MapMouseEvent | MapTouchEvent} data + * @example + * // Initialize the map + * var map = new mapboxgl.Map({ // map options }); + * // Set an event listener that fires + * // just before a "drag to rotate" interaction starts. + * map.on('rotatestart', function() { + * console.log('A rotatestart event occurred.'); + * }); */ | 'rotatestart' @@ -546,6 +851,14 @@ export type MapEvent = * @memberof Map * @instance * @property {MapMouseEvent | MapTouchEvent} data + * @example + * // Initialize the map + * var map = new mapboxgl.Map({ // map options }); + * // Set an event listener that fires + * // repeatedly during "drag to rotate" interaction. + * map.on('rotate', function() { + * console.log('A rotate event occurred.'); + * }); */ | 'rotate' @@ -556,6 +869,14 @@ export type MapEvent = * @memberof Map * @instance * @property {MapMouseEvent | MapTouchEvent} data + * @example + * // Initialize the map + * var map = new mapboxgl.Map({ // map options }); + * // Set an event listener that fires + * // just after a "drag to rotate" interaction ends. + * map.on('rotateend', function() { + * console.log('A rotateend event occurred.'); + * }); */ | 'rotateend' @@ -567,17 +888,34 @@ export type MapEvent = * @memberof Map * @instance * @property {MapEventData} data + * @example + * // Initialize the map + * var map = new mapboxgl.Map({ // map options }); + * // Set an event listener that fires + * // just before a pitch (tilt) transition starts. + * map.on('pitchstart', function() { + * console.log('A pitchstart event occurred.'); + * }); */ | 'pitchstart' /** - * Fired whenever the map's pitch (tilt) changes as - * the result of either user interaction or methods such as {@link Map#flyTo}. + * Fired repeatedly during the map's pitch (tilt) animation between + * one state and another as the result of either user interaction + * or methods such as {@link Map#flyTo}. * * @event pitch * @memberof Map * @instance * @property {MapEventData} data + * @example + * // Initialize the map + * var map = new mapboxgl.Map({ // map options }); + * // Set an event listener that fires + * // repeatedly during a pitch (tilt) transition. + * map.on('pitch', function() { + * console.log('A pitch event occurred.'); + * }); */ | 'pitch' @@ -589,6 +927,14 @@ export type MapEvent = * @memberof Map * @instance * @property {MapEventData} data + * @example + * // Initialize the map + * var map = new mapboxgl.Map({ // map options }); + * // Set an event listener that fires + * // just after a pitch (tilt) transition ends. + * map.on('pitchend', function() { + * console.log('A pitchend event occurred.'); + * }); */ | 'pitchend' @@ -599,6 +945,14 @@ export type MapEvent = * @memberof Map * @instance * @property {MapBoxZoomEvent} data + * @example + * // Initialize the map + * var map = new mapboxgl.Map({ // map options }); + * // Set an event listener that fires + * // just before a "box zoom" interaction starts. + * map.on('boxzoomstart', function() { + * console.log('A boxzoomstart event occurred.'); + * }); */ | 'boxzoomstart' @@ -610,6 +964,14 @@ export type MapEvent = * @instance * @type {Object} * @property {MapBoxZoomEvent} data + * @example + * // Initialize the map + * var map = new mapboxgl.Map({ // map options }); + * // Set an event listener that fires + * // just after a "box zoom" interaction ends. + * map.on('boxzoomend', function() { + * console.log('A boxzoomend event occurred.'); + * }); */ | 'boxzoomend' @@ -621,6 +983,14 @@ export type MapEvent = * @memberof Map * @instance * @property {MapBoxZoomEvent} data + * @example + * // Initialize the map + * var map = new mapboxgl.Map({ // map options }); + * // Set an event listener that fires + * // the user cancels a "box zoom" interaction. + * map.on('boxzoomcancel', function() { + * console.log('A boxzoomcancel event occurred.'); + * }); */ | 'boxzoomcancel' @@ -630,6 +1000,14 @@ export type MapEvent = * @event resize * @memberof Map * @instance + * @example + * // Initialize the map + * var map = new mapboxgl.Map({ // map options }); + * // Set an event listener that fires + * // immediately after the map has been resized. + * map.on('resize', function() { + * console.log('A resize event occurred.'); + * }); */ | 'resize' @@ -639,6 +1017,14 @@ export type MapEvent = * @event webglcontextlost * @memberof Map * @instance + * @example + * // Initialize the map + * var map = new mapboxgl.Map({ // map options }); + * // Set an event listener that fires + * // when the WebGL context is lost. + * map.on('webglcontextlost', function() { + * console.log('A webglcontextlost event occurred.'); + * }); */ | 'webglcontextlost' @@ -648,6 +1034,14 @@ export type MapEvent = * @event webglcontextrestored * @memberof Map * @instance + * @example + * // Initialize the map + * var map = new mapboxgl.Map({ // map options }); + * // Set an event listener that fires + * // when the WebGL context is restored. + * map.on('webglcontextrestored', function() { + * console.log('A webglcontextrestored event occurred.'); + * }); */ | 'webglcontextrestored' @@ -659,6 +1053,14 @@ export type MapEvent = * @memberof Map * @instance * @type {Object} + * @example + * // Initialize the map + * var map = new mapboxgl.Map({ // map options }); + * // Set an event listener that fires + * // when the map has finished loading. + * map.on('load', function() { + * console.log('A load event occurred.'); + * }); * @see [Draw GeoJSON points](https://www.mapbox.com/mapbox-gl-js/example/geojson-markers/) * @see [Add live realtime data](https://www.mapbox.com/mapbox-gl-js/example/live-geojson/) * @see [Animate a point](https://www.mapbox.com/mapbox-gl-js/example/animate-point-along-line/) @@ -676,6 +1078,14 @@ export type MapEvent = * @event render * @memberof Map * @instance + * @example + * // Initialize the map + * var map = new mapboxgl.Map({ // map options }); + * // Set an event listener that fires + * // whenever the map is drawn to the screen. + * map.on('render', function() { + * console.log('A render event occurred.'); + * }); */ | 'render' @@ -690,6 +1100,14 @@ export type MapEvent = * @event idle * @memberof Map * @instance + * @example + * // Initialize the map + * var map = new mapboxgl.Map({ // map options }); + * // Set an event listener that fires + * // just before the map enters an "idle" state. + * map.on('idle', function() { + * console.log('A idle event occurred.'); + * }); */ | 'idle' @@ -699,6 +1117,14 @@ export type MapEvent = * @event remove * @memberof Map * @instance + * @example + * // Initialize the map + * var map = new mapboxgl.Map({ // map options }); + * // Set an event listener that fires + * // just after the map is removed. + * map.on('remove', function() { + * console.log('A remove event occurred.'); + * }); */ | 'remove' @@ -712,6 +1138,14 @@ export type MapEvent = * @memberof Map * @instance * @property {{error: {message: string}}} data + * @example + * // Initialize the map + * var map = new mapboxgl.Map({ // map options }); + * // Set an event listener that fires + * // when an error occurs. + * map.on('error', function() { + * console.log('A error event occurred.'); + * }); */ | 'error' @@ -723,6 +1157,15 @@ export type MapEvent = * @memberof Map * @instance * @property {MapDataEvent} data + * @example + * // Initialize the map + * var map = new mapboxgl.Map({ // map options }); + * // Set an event listener that fires + * // when map data loads or changes. + * map.on('data', function() { + * console.log('A data event occurred.'); + * }); + * @see [Display HTML clusters with custom properties](https://docs.mapbox.com/mapbox-gl-js/example/cluster-html/) */ | 'data' @@ -734,6 +1177,14 @@ export type MapEvent = * @memberof Map * @instance * @property {MapDataEvent} data + * @example + * // Initialize the map + * var map = new mapboxgl.Map({ // map options }); + * // Set an event listener that fires + * // when the map's style loads or changes. + * map.on('styledata', function() { + * console.log('A styledata event occurred.'); + * }); */ | 'styledata' @@ -745,6 +1196,14 @@ export type MapEvent = * @memberof Map * @instance * @property {MapDataEvent} data + * @example + * // Initialize the map + * var map = new mapboxgl.Map({ // map options }); + * // Set an event listener that fires + * // when one of the map's sources loads or changes. + * map.on('sourcedata', function() { + * console.log('A sourcedata event occurred.'); + * }); */ | 'sourcedata' @@ -757,6 +1216,15 @@ export type MapEvent = * @memberof Map * @instance * @property {MapDataEvent} data + * @example + * // Initialize the map + * var map = new mapboxgl.Map({ // map options }); + * // Set an event listener that fires + * // when any map data begins loading + * // or changing asynchronously. + * map.on('dataloading', function() { + * console.log('A dataloading event occurred.'); + * }); */ | 'dataloading' @@ -769,6 +1237,15 @@ export type MapEvent = * @memberof Map * @instance * @property {MapDataEvent} data + * @example + * // Initialize the map + * var map = new mapboxgl.Map({ // map options }); + * // Set an event listener that fires + * // map's style begins loading or + * // changing asyncronously. + * map.on('styledataloading', function() { + * console.log('A styledataloading event occurred.'); + * }); */ | 'styledataloading' @@ -781,6 +1258,15 @@ export type MapEvent = * @memberof Map * @instance * @property {MapDataEvent} data + * @example + * // Initialize the map + * var map = new mapboxgl.Map({ // map options }); + * // Set an event listener that fires + * // map's sources begin loading or + * // changing asyncronously. + * map.on('sourcedataloading', function() { + * console.log('A sourcedataloading event occurred.'); + * }); */ | 'sourcedataloading' @@ -793,7 +1279,14 @@ export type MapEvent = * @memberof Map * @instance * @property {string} id The id of the missing image. - * + * @example + * // Initialize the map + * var map = new mapboxgl.Map({ // map options }); + * // Set an event listener that fires + * // an icon or pattern is missing. + * map.on('styleimagemissing', function() { + * console.log('A styleimagemissing event occurred.'); + * }); * @see [Generate and add a missing icon to the map](https://mapbox.com/mapbox-gl-js/example/add-image-missing-generated/) */ | 'styleimagemissing' diff --git a/src/ui/handler/scroll_zoom.js b/src/ui/handler/scroll_zoom.js index a123a1e711e..a8fc61d109f 100644 --- a/src/ui/handler/scroll_zoom.js +++ b/src/ui/handler/scroll_zoom.js @@ -81,15 +81,21 @@ class ScrollZoomHandler { /** * Set the zoom rate of a trackpad * @param {number} [zoomRate=1/100] The rate used to scale trackpad movement to a zoom value. + * @example + * // Speed up trackpad zoom + * map.scrollZoom.setZoomRate(1/25); */ setZoomRate(zoomRate: number) { this._defaultZoomRate = zoomRate; } /** - * Set the zoom rate of a mouse wheel - * @param {number} [wheelZoomRate=1/450] The rate used to scale mouse wheel movement to a zoom value. - */ + * Set the zoom rate of a mouse wheel + * @param {number} [wheelZoomRate=1/450] The rate used to scale mouse wheel movement to a zoom value. + * @example + * // Slow down zoom of mouse wheel + * map.scrollZoom.setWheelZoomRate(1/600); + */ setWheelZoomRate(wheelZoomRate: number) { this._wheelZoomRate = wheelZoomRate; } diff --git a/src/ui/map.js b/src/ui/map.js index c1e5eee4dad..613bd549f47 100755 --- a/src/ui/map.js +++ b/src/ui/map.js @@ -925,33 +925,103 @@ class Map extends Camera { } /** - * Adds a listener for events of a specified type. - * - * @method - * @name on - * @memberof Map - * @instance - * @param {string} type The event type to add a listen for. - * @param {Function} listener The function to be called when the event is fired. - * The listener function is called with the data object passed to `fire`, - * extended with `target` and `type` properties. - * @returns {Map} `this` - */ - - /** - * Adds a listener for events of a specified type occurring on features in a specified style layer. - * - * @param {string} type The event type to listen for; one of `'mousedown'`, `'mouseup'`, `'click'`, `'dblclick'`, - * `'mousemove'`, `'mouseenter'`, `'mouseleave'`, `'mouseover'`, `'mouseout'`, `'contextmenu'`, `'touchstart'`, - * `'touchend'`, or `'touchcancel'`. `mouseenter` and `mouseover` events are triggered when the cursor enters - * a visible portion of the specified layer from outside that layer or outside the map canvas. `mouseleave` - * and `mouseout` events are triggered when the cursor leaves a visible portion of the specified layer, or leaves - * the map canvas. - * @param {string} layerId The ID of a style layer. Only events whose location is within a visible - * feature in this layer will trigger the listener. The event will have a `features` property containing - * an array of the matching features. + * Adds a listener for events of a specified type, optionally limited to features in a specified style layer. + * + * @param {string} type The event type to listen for. Events compatible with the optional `layerId` parameter are triggered + * when the cursor enters a visible portion of the specified layer from outside that layer or outside the map canvas. + * + * | Event | Compatible with `layerId` | + * |-----------------------------------------------------------|---------------------------| + * | [`mousedown`](#map.event:mousedown) | yes | + * | [`mouseup`](#map.event:mouseup) | yes | + * | [`mouseover`](#map.event:mouseover) | yes | + * | [`mouseout`](#map.event:mouseout) | yes | + * | [`mousemove`](#map.event:mousemove) | yes | + * | [`mouseenter`](#map.event:mouseenter) | yes (required) | + * | [`mouseleave`](#map.event:mouseleave) | yes (required) | + * | [`click`](#map.event:click) | yes | + * | [`dblclick`](#map.event:dblclick) | yes | + * | [`contextmenu`](#map.event:contextmenu) | yes | + * | [`touchstart`](#map.event:touchstart) | yes | + * | [`touchend`](#map.event:touchend) | yes | + * | [`touchcancel`](#map.event:touchcancel) | yes | + * | [`wheel`](#map.event:wheel) | | + * | [`resize`](#map.event:resize) | | + * | [`remove`](#map.event:remove) | | + * | [`touchmove`](#map.event:touchmove) | | + * | [`movestart`](#map.event:movestart) | | + * | [`move`](#map.event:move) | | + * | [`moveend`](#map.event:moveend) | | + * | [`dragstart`](#map.event:dragstart) | | + * | [`drag`](#map.event:drag) | | + * | [`dragend`](#map.event:dragend) | | + * | [`zoomstart`](#map.event:zoomstart) | | + * | [`zoom`](#map.event:zoom) | | + * | [`zoomend`](#map.event:zoomend) | | + * | [`rotatestart`](#map.event:rotatestart) | | + * | [`rotate`](#map.event:rotate) | | + * | [`rotateend`](#map.event:rotateend) | | + * | [`pitchstart`](#map.event:pitchstart) | | + * | [`pitch`](#map.event:pitch) | | + * | [`pitchend`](#map.event:pitchend) | | + * | [`boxzoomstart`](#map.event:boxzoomstart) | | + * | [`boxzoomend`](#map.event:boxzoomend) | | + * | [`boxzoomcancel`](#map.event:boxzoomcancel) | | + * | [`webglcontextlost`](#map.event:webglcontextlost) | | + * | [`webglcontextrestored`](#map.event:webglcontextrestored) | | + * | [`load`](#map.event:load) | | + * | [`render`](#map.event:render) | | + * | [`idle`](#map.event:idle) | | + * | [`error`](#map.event:error) | | + * | [`data`](#map.event:data) | | + * | [`styledata`](#map.event:styledata) | | + * | [`sourcedata`](#map.event:sourcedata) | | + * | [`dataloading`](#map.event:dataloading) | | + * | [`styledataloading`](#map.event:styledataloading) | | + * | [`sourcedataloading`](#map.event:sourcedataloading) | | + * | [`styleimagemissing`](#map.event:styleimagemissing) | | + * + * @param {string} layerId (optional) The ID of a style layer. Event will only be triggered if its location + * is within a visible feature in this layer. The event will have a `features` property containing + * an array of the matching features. If `layerId` is not supplied, the event will not have a `features` property. + * Please note that many event types are not compatible with the optional `layerId` parameter. * @param {Function} listener The function to be called when the event is fired. * @returns {Map} `this` + * @example + * // Set an event listener that will fire + * // when the map has finished loading + * map.on('load', function() { + * // Once the map has finished loading, + * // add a new layer + * map.addLayer({ + * id: 'points-of-interest', + * source: { + * type: 'vector', + * url: 'mapbox://mapbox.mapbox-streets-v8' + * }, + * 'source-layer': 'poi_label', + * type: 'circle', + * paint: { + * // Mapbox Style Specification paint properties + * }, + * layout: { + * // Mapbox Style Specification layout properties + * } + * }); + * }); + * @example + * // Set an event listener that will fire + * // when a feature on the countries layer of the map is clicked + * map.on('click', 'countries', function(e) { + * new mapboxgl.Popup() + * .setLngLat(e.lngLat) + * .setHTML(`Country name: ${e.features[0].properties.name}`) + * .addTo(map); + * }); + * @see [Display popup on click](https://docs.mapbox.com/mapbox-gl-js/example/popup-on-click/) + * @see [Center the map on a clicked symbol](https://docs.mapbox.com/mapbox-gl-js/example/center-on-symbol/) + * @see [Create a hover effect](https://docs.mapbox.com/mapbox-gl-js/example/hover-styles/) + * @see [Create a draggable marker](https://docs.mapbox.com/mapbox-gl-js/example/drag-a-point/) */ on(type: MapEvent, layerId: any, listener: any) { if (listener === undefined) { @@ -1326,9 +1396,9 @@ class Map extends Camera { } /** - * Returns the map's Mapbox style object, which can be used to recreate the map's style. + * Returns the map's Mapbox [style](https://docs.mapbox.com/help/glossary/style/) object, a JSON object which can be used to recreate the map's style. * - * @returns {Object} The map's style object. + * @returns {Object} The map's style JSON object. * * @example * var styleJson = map.getStyle(); @@ -1393,7 +1463,8 @@ class Map extends Camera { } /** - * Returns a Boolean indicating whether the source is loaded. + * Returns a Boolean indicating whether the source is loaded. Returns `true` if the source with + * the given ID in the map's style has no outstanding network requests, otherwise `false`. * * @param {string} id The ID of the source to be checked. * @returns {boolean} A Boolean indicating whether the source is loaded. @@ -1460,14 +1531,22 @@ class Map extends Camera { /** * Returns the source with the specified ID in the map's style. * + * This method is often used to update a source using the instance members for the relevant + * source type as defined in [Sources](#sources). + * For example, setting the `data` for a GeoJSON source or updating the `url` and `coordinates` + * of an image source. + * * @param {string} id The ID of the source to get. - * @returns {?Object} The style source with the specified ID, or `undefined` - * if the ID corresponds to no existing sources. + * @returns {?Object} The style source with the specified ID or `undefined` if the ID + * corresponds to no existing sources. + * The shape of the object varies by source type. + * A list of options for each source type is available on the Mapbox Style Specification's + * [Sources](https://docs.mapbox.com/mapbox-gl-js/style-spec/sources/) page. * @example * var sourceObject = map.getSource('points'); - * @see [Create a draggable point](https://www.mapbox.com/mapbox-gl-js/example/drag-a-point/) - * @see [Animate a point](https://www.mapbox.com/mapbox-gl-js/example/animate-point-along-line/) - * @see [Add live realtime data](https://www.mapbox.com/mapbox-gl-js/example/live-geojson/) + * @see [Create a draggable point](https://docs.mapbox.com/mapbox-gl-js/example/drag-a-point/) + * @see [Animate a point](https://docs.mapbox.com/mapbox-gl-js/example/animate-point-along-line/) + * @see [Add live realtime data](https://docs.mapbox.com/mapbox-gl-js/example/live-geojson/) */ getSource(id: string) { return this.style.getSource(id); @@ -1680,15 +1759,51 @@ class Map extends Camera { * A layer defines how data from a specified source will be styled. Read more about layer types * and available paint and layout properties in the [Mapbox Style Specification](https://docs.mapbox.com/mapbox-gl-js/style-spec/#layers). * - * @param {Object | CustomLayerInterface} layer The style layer to add, conforming to the Mapbox Style Specification's - * [layer definition](https://docs.mapbox.com/mapbox-gl-js/style-spec/#layers). + * @param {Object | CustomLayerInterface} layer The layer to add, conforming to either the Mapbox Style Specification's [layer definition](https://docs.mapbox.com/mapbox-gl-js/style-spec/#layers) or, less commonly, the [`CustomLayerInterface`](https://docs.mapbox.com/mapbox-gl-js/api/#customlayerinterface) specification. + * The Mapbox Style Specification's layer definition is appropriate for most layers. + * + * @param {string} layer.id A unique idenfier that you define. + * @param {string} layer.type The type of layer (for example `fill` or `symbol`). + * A list of layer types is available in the [Mapbox Style Specification](https://docs.mapbox.com/mapbox-gl-js/style-spec/layers/#type). + * + * (This can also be `custom`. For more information, see [`CustomLayerInterface`](https://docs.mapbox.com/mapbox-gl-js/api/#customlayerinterface).) + * @param {string | Object} [layer.source] The data source for the layer. + * Reference a source that has _already been defined_ using the source's unique id. + * Reference a _new source_ using a source object (as defined in the [Mapbox Style Specification](https://docs.mapbox.com/mapbox-gl-js/style-spec/sources/)) directly. + * This is **required** for all `layer.type` options _except_ for `custom`. + * @param {string} [layer.sourceLayer] (optional) The name of the [source layer](https://docs.mapbox.com/help/glossary/source-layer/) within the specified `layer.source` to use for this style layer. + * This is only applicable for vector tile sources and is **required** when `layer.source` is of the type `vector`. + * @param {array} [layer.filter] (optional) An expression specifying conditions on source features. + * Only features that match the filter are displayed. + * The Mapbox Style Specification includes more information on the limitations of the [`filter`](https://docs.mapbox.com/mapbox-gl-js/style-spec/layers/#filter) parameter + * and a complete list of available [expressions](https://docs.mapbox.com/mapbox-gl-js/style-spec/expressions/). + * If no filter is provided, all features in the source (or source layer for vector tilesets) will be displayed. + * @param {Object} [layer.paint] (optional) Paint properties for the layer. + * Available paint properties vary by `layer.type`. + * A full list of paint properties for each layer type is available in the [Mapbox Style Specification](https://docs.mapbox.com/mapbox-gl-js/style-spec/layers/). + * If no paint properties are specified, default values will be used. + * @param {Object} [layer.layout] (optional) Layout properties for the layer. + * Available layout properties vary by `layer.type`. + * A full list of layout properties for each layer type is available in the [Mapbox Style Specification](https://docs.mapbox.com/mapbox-gl-js/style-spec/layers/). + * If no layout properties are specified, default values will be used. + * @param {number} [layer.maxzoom] (optional) The maximum zoom level for the layer. + * At zoom levels equal to or greater than the maxzoom, the layer will be hidden. + * The value can be any number between `0` and `24` (inclusive). + * If no maxzoom is provided, the layer will be visible at all zoom levels for which there are tiles available. + * @param {number} [layer.minzoom] (optional) The minimum zoom level for the layer. + * At zoom levels less than the minzoom, the layer will be hidden. + * The value can be any number between `0` and `24` (inclusive). + * If no minzoom is provided, the layer will be visible at all zoom levels for which there are tiles available. + * @param {Object} [layer.metadata] (optional) Arbitrary properties useful to track with the layer, but do not influence rendering. + * @param {string} [layer.renderingMode] This is only applicable for layers with the type `custom`. + * See [`CustomLayerInterface`](https://docs.mapbox.com/mapbox-gl-js/api/#customlayerinterface) for more information. * @param {string} [beforeId] The ID of an existing layer to insert the new layer before. - * If this argument is omitted, the layer will be appended to the end of the layers array. + * If this argument is not specified, the layer will be appended to the end of the layers array. * * @returns {Map} `this` * * @example - * // Add a circle layer with a vector source. + * // Add a circle layer with a vector source * map.addLayer({ * id: 'points-of-interest', * source: { @@ -1705,9 +1820,44 @@ class Map extends Camera { * } * }); * - * @see [Create and style clusters](https://www.mapbox.com/mapbox-gl-js/example/cluster/) - * @see [Add a vector tile source](https://www.mapbox.com/mapbox-gl-js/example/vector-source/) - * @see [Add a WMS source](https://www.mapbox.com/mapbox-gl-js/example/wms/) + * @example + * // Define a source before using it to create a new layer + * map.addSource('state-data', { + * type: 'geojson', + * data: 'path/to/data.geojson' + * }); + * + * map.addLayer({ + * id: 'states', + * // References the GeoJSON source defined above + * // and does not require a `source-layer` + * source: 'state-data', + * type: 'symbol', + * layout: { + * // Set the label content to the + * // feature's `name` property + * text-field: ['get', 'name'] + * } + * }); + * + * @example + * // Add a new symbol layer before an existing layer + * map.addLayer({ + * id: 'states', + * // References a source that's already been defined + * source: 'state-data', + * type: 'symbol', + * layout: { + * // Set the label content to the + * // feature's `name` property + * text-field: ['get', 'name'] + * } + * // Add the layer before the existing `cities` layer + * }, 'cities'); + * + * @see [Create and style clusters](https://docs.mapbox.com/mapbox-gl-js/example/cluster/) + * @see [Add a vector tile source](https://docs.mapbox.com/mapbox-gl-js/example/vector-source/) + * @see [Add a WMS source](https://docs.mapbox.com/mapbox-gl-js/example/wms/) */ addLayer(layer: LayerSpecification | CustomLayerInterface, beforeId?: string) { this._lazyInitEmptyStyle(); @@ -1719,13 +1869,12 @@ class Map extends Camera { * Moves a layer to a different z-position. * * @param {string} id The ID of the layer to move. - * @param {string} [beforeId] The ID of an existing layer to insert the new layer before. - * If this argument is omitted, the layer will be appended to the end of the layers array. + * @param {string} [beforeId] The ID of an existing layer to insert the new layer before. When viewing the map, the `id` layer will appear beneath the `beforeId` layer. If `beforeId` is omitted, the layer will be appended to the end of the layers array and appear above all other layers on the map. * @returns {Map} `this` * * @example - * // Move a layer with ID 'label' before the layer with ID 'waterways'. - * map.moveLayer('label', 'waterways'); + * // Move a layer with ID 'polygon' before the layer with ID 'country-label'. The `polygon` layer will appear beneath the `country-label` layer on the map. + * map.moveLayer('polygon', 'country-label'); */ moveLayer(id: string, beforeId?: string) { this.style.moveLayer(id, beforeId); @@ -1795,19 +1944,35 @@ class Map extends Camera { /** * Sets the filter for the specified style layer. * + * Filters control which features a style layer renders from its source. + * Any feature for which the filter expression evaluates to `true` will be + * rendered on the map. Those that are false will be hidden. + * + * Use `setFilter` to show a subset of your source data. + * + * To clear the filter, pass `null` or `undefined` as the second parameter. + * * @param {string} layerId The ID of the layer to which the filter will be applied. * @param {Array | null | undefined} filter The filter, conforming to the Mapbox Style Specification's * [filter definition](https://docs.mapbox.com/mapbox-gl-js/style-spec/layers/#filter). If `null` or `undefined` is provided, the function removes any existing filter from the layer. * @param {Object} [options] Options object. * @param {boolean} [options.validate=true] Whether to check if the filter conforms to the Mapbox GL Style Specification. Disabling validation is a performance optimization that should only be used if you have previously validated the values you will be passing to this function. - * * @returns {Map} `this` + * * @example - * map.setFilter('my-layer', ['==', 'name', 'USA']); + * // display only features with the 'name' property 'USA' + * map.setFilter('my-layer', ['==', ['get', 'name'], 'USA']); + * @example + * // display only features with five or more 'available-spots' + * map.setFilter('bike-docks', ['>=', ['get', 'available-spots'], 5]); + * @example + * // remove the filter for the 'bike-docks' style layer + * map.setFilter('bike-docks', null); * * @see [Filter features within map view](https://www.mapbox.com/mapbox-gl-js/example/filter-features-within-map-view/) * @see [Highlight features containing similar data](https://www.mapbox.com/mapbox-gl-js/example/query-similar-features/) * @see [Create a timeline animation](https://www.mapbox.com/mapbox-gl-js/example/timeline-animation/) + * @see Tutorial: [Show changes over time](https://docs.mapbox.com/help/tutorials/show-changes-over-time/) */ setFilter(layerId: string, filter: ?FilterSpecification, options: StyleSetterOptions = {}) { this.style.setFilter(layerId, filter, options); @@ -1867,6 +2032,7 @@ class Map extends Camera { * @returns {Map} `this` * @example * map.setLayoutProperty('my-layer', 'visibility', 'none'); + * @see [Show and hide layers](https://docs.mapbox.com/mapbox-gl-js/example/toggle-layers/) */ setLayoutProperty(layerId: string, name: string, value: any, options: StyleSetterOptions = {}) { this.style.setLayoutProperty(layerId, name, value, options); @@ -1891,6 +2057,9 @@ class Map extends Camera { * @param {Object} [options] Options object. * @param {boolean} [options.validate=true] Whether to check if the filter conforms to the Mapbox GL Style Specification. Disabling validation is a performance optimization that should only be used if you have previously validated the values you will be passing to this function. * @returns {Map} `this` + * @example + * var layerVisibility = map.getLayoutProperty('my-layer', 'visibility'); + * @see [Show and hide layers](https://docs.mapbox.com/mapbox-gl-js/example/toggle-layers/) */ setLight(light: LightSpecification, options: StyleSetterOptions = {}) { this._lazyInitEmptyStyle(); @@ -1909,23 +2078,42 @@ class Map extends Camera { // eslint-disable-next-line jsdoc/require-returns /** - * Sets the state of a feature. The `state` object is merged in with the existing state of the feature. - * Features are identified by their `id` attribute, which must be an integer or a string that can be - * cast to an integer. + * Sets the `state` of a feature. + * A feature's `state` is a set of user-defined key-value pairs that are assigned to a feature at runtime. + * When using this method, the `state` object is merged with any existing key-value pairs in the feature's state. + * Features are identified by their `feature.id` attribute, which can be any number or string. + * + * This method can only be used with sources that have a `feature.id` attribute. The `feature.id` attribute can be defined in three ways: + * - For vector or GeoJSON sources, including an `id` attribute in the original data file. + * - For vector or GeoJSON sources, using the [`promoteId`](https://docs.mapbox.com/mapbox-gl-js/style-spec/sources/#vector-promoteId) option at the time the source is defined. + * - For GeoJSON sources, using the [`generateId`](https://docs.mapbox.com/mapbox-gl-js/style-spec/sources/#geojson-generateId) option to auto-assign an `id` based on the feature's index in the source data. If you change feature data using `map.getSource('some id').setData(..)`, you may need to re-apply state taking into account updated `id` values. + * + * _Note: You can use the [`feature-state` expression](https://docs.mapbox.com/mapbox-gl-js/style-spec/expressions/#feature-state) to access the values in a feature's state object for the purposes of styling._ * * @param {Object} feature Feature identifier. Feature objects returned from * {@link Map#queryRenderedFeatures} or event handlers can be used as feature identifiers. * @param {string | number} feature.id Unique id of the feature. - * @param {string} feature.source The Id of the vector source or GeoJSON source for the feature. - * @param {string} [feature.sourceLayer] (optional) *For vector tile sources, the sourceLayer is - * required.* + * @param {string} feature.source The id of the vector or GeoJSON source for the feature. + * @param {string} [feature.sourceLayer] (optional) *For vector tile sources, `sourceLayer` is required.* * @param {Object} state A set of key-value pairs. The values should be valid JSON types. * - * This method requires the `feature.id` attribute on data sets. For GeoJSON sources without - * feature ids, set the `generateId` option in the `GeoJSONSourceSpecification` to auto-assign them. This - * option assigns ids based on a feature's index in the source data. If you change feature data using - * `map.getSource('some id').setData(..)`, you may need to re-apply state taking into account updated `id` values. + * @example + * // When the mouse moves over the `my-layer` layer, update + * // the feature state for the feature under the mouse + * map.on('mousemove', 'my-layer', function(e) { + * if (e.features.length > 0) { + * map.setFeatureState({ + * source: 'my-source', + * sourceLayer: 'my-source-layer', + * id: e.features[0].id, + * }, { + * hover: true + * }); + * } + * }); + * * @see [Create a hover effect](https://docs.mapbox.com/mapbox-gl-js/example/hover-styles/) + * @see Tutorial: [Create interactive hover effects with Mapbox GL JS](https://docs.mapbox.com/help/tutorials/create-interactive-hover-effects-with-mapbox-gl-js/) */ setFeatureState(feature: { source: string; sourceLayer?: string; id: string | number; }, state: Object) { this.style.setFeatureState(feature, state); @@ -1934,19 +2122,50 @@ class Map extends Camera { // eslint-disable-next-line jsdoc/require-returns /** - * Removes feature state, setting it back to the default behavior. If only - * source is specified, removes all states of that source. If - * target.id is also specified, removes all keys for that feature's state. - * If key is also specified, removes that key from that feature's state. - * Features are identified by their `id` attribute, which must be an integer or a string that can be - * cast to an integer. - * @param {Object} target Identifier of where to set state: can be a source, a feature, or a specific key of feature. + * Removes the `state` of a feature, setting it back to the default behavior. + * If only a `target.source` is specified, it will remove the state for all features from that source. + * If `target.id` is also specified, it will remove all keys for that feature's state. + * If `key` is also specified, it removes only that key from that feature's state. + * Features are identified by their `feature.id` attribute, which can be any number or string. + * + * @param {Object} target Identifier of where to remove state. It can be a source, a feature, or a specific key of feature. * Feature objects returned from {@link Map#queryRenderedFeatures} or event handlers can be used as feature identifiers. * @param {string | number} target.id (optional) Unique id of the feature. Optional if key is not specified. - * @param {string} target.source The Id of the vector source or GeoJSON source for the feature. - * @param {string} [target.sourceLayer] (optional) *For vector tile sources, the sourceLayer is - * required.* + * @param {string} target.source The id of the vector or GeoJSON source for the feature. + * @param {string} [target.sourceLayer] (optional) *For vector tile sources, `sourceLayer` is required.* * @param {string} key (optional) The key in the feature state to reset. + * + * @example + * // Reset the entire state object for all features + * // in the `my-source` source + * map.removeFeatureState({ + * source: 'my-source' + * }); + * + * @example + * // When the mouse leaves the `my-layer` layer, + * // reset the entire state object for the + * // feature under the mouse + * map.on('mouseleave', 'my-layer', function(e) { + * map.removeFeatureState({ + * source: 'my-source', + * sourceLayer: 'my-source-layer', + * id: e.features[0].id + * }); + * }); + * + * @example + * // When the mouse leaves the `my-layer` layer, + * // reset only the `hover` key-value pair in the + * // state for the feature under the mouse + * map.on('mouseleave', 'my-layer', function(e) { + * map.removeFeatureState({ + * source: 'my-source', + * sourceLayer: 'my-source-layer', + * id: e.features[0].id + * }, 'hover'); + * }); + * */ removeFeatureState(target: { source: string; sourceLayer?: string; id?: string | number; }, key?: string) { this.style.removeFeatureState(target, key); @@ -1954,18 +2173,33 @@ class Map extends Camera { } /** - * Gets the state of a feature. - * Features are identified by their `id` attribute, which must be an integer or a string that can be - * cast to an integer. + * Gets the `state` of a feature. + * A feature's `state` is a set of user-defined key-value pairs that are assigned to a feature at runtime. + * Features are identified by their `feature.id` attribute, which can be any number or string. + * + * _Note: To access the values in a feature's state object for the purposes of styling the feature, use the [`feature-state` expression](https://docs.mapbox.com/mapbox-gl-js/style-spec/expressions/#feature-state)._ * * @param {Object} feature Feature identifier. Feature objects returned from * {@link Map#queryRenderedFeatures} or event handlers can be used as feature identifiers. * @param {string | number} feature.id Unique id of the feature. - * @param {string} feature.source The Id of the vector source or GeoJSON source for the feature. - * @param {string} [feature.sourceLayer] (optional) *For vector tile sources, the sourceLayer is - * required.* + * @param {string} feature.source The id of the vector or GeoJSON source for the feature. + * @param {string} [feature.sourceLayer] (optional) *For vector tile sources, `sourceLayer` is required.* + * + * @returns {Object} The state of the feature: a set of key-value pairs that was assigned to the feature at runtime. + * + * @example + * // When the mouse moves over the `my-layer` layer, + * // get the feature state for the feature under the mouse + * map.on('mousemove', 'my-layer', function(e) { + * if (e.features.length > 0) { + * map.getFeatureState({ + * source: 'my-source', + * sourceLayer: 'my-source-layer' + * id: e.features[0].id + * }); + * } + * }); * - * @returns {Object} The state of the feature. */ getFeatureState(feature: { source: string; sourceLayer?: string; id: string | number; }): any { return this.style.getFeatureState(feature); @@ -2344,6 +2578,10 @@ class Map extends Camera { * Trigger the rendering of a single frame. Use this method with custom layers to * repaint the map when the layer changes. Calling this multiple times before the * next frame is rendered will still result in only a single frame being rendered. + * @example + * map.triggerRepaint(); + * @see [Add a 3D model](https://docs.mapbox.com/mapbox-gl-js/example/add-3d-model/) + * @see [Add an animated icon to the map](https://docs.mapbox.com/mapbox-gl-js/example/add-image-animated/) */ triggerRepaint() { if (this.style && !this._frame) { @@ -2377,6 +2615,8 @@ class Map extends Camera { * @type {boolean} * @instance * @memberof Map + * @example + * map.showTileBoundaries = true; */ get showTileBoundaries(): boolean { return !!this._showTileBoundaries; } set showTileBoundaries(value: boolean) { @@ -2582,10 +2822,15 @@ function removeNode(node) { * `x` and `y` properties representing screen coordinates in pixels. * * @typedef {Object} Point + * @example + * var point = new mapboxgl.Point(-77, 38); */ /** * A {@link Point} or an array of two numbers representing `x` and `y` screen coordinates in pixels. * * @typedef {(Point | Array)} PointLike + * @example + * var p1 = new mapboxgl.Point(-77, 38); // a PointLike which is a Point + * var p2 = [-77, 38]; // a PointLike which is an array of two numbers */ diff --git a/src/ui/marker.js b/src/ui/marker.js index 8c52a04aa31..7dcdc22670c 100644 --- a/src/ui/marker.js +++ b/src/ui/marker.js @@ -225,9 +225,13 @@ export default class Marker extends Evented { } /** - * Attaches the marker to a map + * Attaches the `Marker` to a `Map` object. * @param {Map} map The Mapbox GL JS map to add the marker to. * @returns {Marker} `this` + * @example + * var marker = new mapboxgl.Marker() + * .setLngLat([30.5, 50.5]) + * .addTo(map); // add the marker to the map */ addTo(map: Map) { this.remove(); @@ -279,16 +283,30 @@ export default class Marker extends Evented { * the marker on screen. * * @returns {LngLat} A {@link LngLat} describing the marker's location. - */ + * @example + * // Store the marker's longitude and latitude coordinates in a variable + * var lngLat = marker.getLngLat(); + * // Print the marker's longitude and latitude values in the console + * console.log('Longitude: ' + lngLat.lng + ', Latitude: ' + lngLat.lat ) + * @see [Create a draggable Marker](https://docs.mapbox.com/mapbox-gl-js/example/drag-a-marker/) + */ getLngLat() { return this._lngLat; } /** - * Set the marker's geographical position and move it. - * @param {LngLat} lnglat A {@link LngLat} describing where the marker should be located. - * @returns {Marker} `this` - */ + * Set the marker's geographical position and move it. + * @param {LngLat} lnglat A {@link LngLat} describing where the marker should be located. + * @returns {Marker} `this` + * @example + * // Create a new marker, set the longitude and latitude, and add it to the map + * new mapboxgl.Marker() + * .setLngLat([-65.017, -16.457]) + * .addTo(map); + * @see [Add custom icons with Markers](https://docs.mapbox.com/mapbox-gl-js/example/custom-marker-icons/) + * @see [Create a draggable Marker](https://docs.mapbox.com/mapbox-gl-js/example/drag-a-marker/) + * @see [Add a marker using a place name](https://docs.mapbox.com/mapbox-gl-js/example/marker-from-geocode/) + */ setLngLat(lnglat: LngLatLike) { this._lngLat = LngLat.convert(lnglat); this._pos = null; @@ -306,10 +324,16 @@ export default class Marker extends Evented { } /** - * Binds a Popup to the Marker - * @param popup an instance of the `Popup` class. If undefined or null, any popup - * set on this `Marker` instance is unset + * Binds a {@link Popup} to the {@link Marker}. + * @param popup An instance of the {@link Popup} class. If undefined or null, any popup + * set on this {@link Marker} instance is unset. * @returns {Marker} `this` + * @example + * var marker = new mapboxgl.Marker() + * .setLngLat([0, 0]) + * .setPopup(new mapboxgl.Popup().setHTML("

Hello World!

")) // add popup + * .addTo(map); + * @see [Attach a popup to a marker instance](https://docs.mapbox.com/mapbox-gl-js/example/set-popup/) */ setPopup(popup: ?Popup) { if (this._popup) { @@ -373,16 +397,30 @@ export default class Marker extends Evented { } /** - * Returns the Popup instance that is bound to the Marker + * Returns the {@link Popup} instance that is bound to the {@link Marker}. * @returns {Popup} popup + * @example + * var marker = new mapboxgl.Marker() + * .setLngLat([0, 0]) + * .setPopup(new mapboxgl.Popup().setHTML("

Hello World!

")) + * .addTo(map); + * + * console.log(marker.getPopup()); // return the popup instance */ getPopup() { return this._popup; } /** - * Opens or closes the bound popup, depending on the current state + * Opens or closes the {@link Popup} instance that is bound to the {@link Marker}, depending on the current state of the {@link Popup}. * @returns {Marker} `this` + * @example + * var marker = new mapboxgl.Marker() + * .setLngLat([0, 0]) + * .setPopup(new mapboxgl.Popup().setHTML("

Hello World!

")) + * .addTo(map); + * + * marker.togglePopup(); // toggle popup open or closed */ togglePopup() { const popup = this._popup; diff --git a/src/ui/popup.js b/src/ui/popup.js index 8acb87e2192..2732eb82857 100644 --- a/src/ui/popup.js +++ b/src/ui/popup.js @@ -104,6 +104,15 @@ export default class Popup extends Evented { * * @param {Map} map The Mapbox GL JS map to add the popup to. * @returns {Popup} `this` + * @example + * new mapboxgl.Popup() + * .setLngLat([0, 0]) + * .setHTML("

Null Island

") + * .addTo(map); + * @see [Display a popup](https://docs.mapbox.com/mapbox-gl-js/example/popup/) + * @see [Display a popup on hover](https://docs.mapbox.com/mapbox-gl-js/example/popup-on-hover/) + * @see [Display a popup on click](https://docs.mapbox.com/mapbox-gl-js/example/popup-on-click/) + * @see [Show polygon information on click](https://docs.mapbox.com/mapbox-gl-js/example/polygon-popup-on-click/) */ addTo(map: Map) { if (this._map) this.remove(); @@ -139,6 +148,16 @@ export default class Popup extends Evented { * @instance * @type {Object} * @property {Popup} popup object that was opened + * + * @example + * // Create a popup + * var popup = new mapboxgl.Popup(); + * // Set an event listener that will fire + * // any time the popup is opened + * popup.on('open', function(){ + * console.log('popup was opened'); + * }); + * */ this.fire(new Event('open')); @@ -189,6 +208,16 @@ export default class Popup extends Evented { * @instance * @type {Object} * @property {Popup} popup object that was closed + * + * @example + * // Create a popup + * var popup = new mapboxgl.Popup(); + * // Set an event listener that will fire + * // any time the popup is closed + * popup.on('close', function(){ + * console.log('popup was closed'); + * }); + * */ this.fire(new Event('close')); @@ -235,8 +264,13 @@ export default class Popup extends Evented { } /** - * Tracks the popup anchor to the cursor position, on screens with a pointer device (will be hidden on touchscreens). Replaces the setLngLat behavior. - * For most use cases, `closeOnClick` and `closeButton` should also be set to `false` here. + * Tracks the popup anchor to the cursor position on screens with a pointer device (it will be hidden on touchscreens). Replaces the `setLngLat` behavior. + * For most use cases, set `closeOnClick` and `closeButton` to `false`. + * @example + * var popup = new mapboxgl.Popup({ closeOnClick: false, closeButton: false }) + * .setHTML("

Hello World!

") + * .trackPointer() + * .addTo(map); * @returns {Popup} `this` */ trackPointer() { @@ -259,6 +293,14 @@ export default class Popup extends Evented { /** * Returns the `Popup`'s HTML element. + * @example + * // Change the `Popup` element's font size + * var popup = new mapboxgl.Popup() + * .setLngLat([-96, 37.8]) + * .setHTML("

Hello World!

") + * .addTo(map); + * var popupElem = popup.getElement(); + * popupElem.style.fontSize = "25px"; * @returns {HTMLElement} element */ getElement() { @@ -293,6 +335,15 @@ export default class Popup extends Evented { * * @param html A string representing HTML content for the popup. * @returns {Popup} `this` + * @example + * var popup = new mapboxgl.Popup() + * .setLngLat(e.lngLat) + * .setHTML("

Hello World!

") + * .addTo(map); + * @see [Display a popup](https://docs.mapbox.com/mapbox-gl-js/example/popup/) + * @see [Display a popup on hover](https://docs.mapbox.com/mapbox-gl-js/example/popup-on-hover/) + * @see [Display a popup on click](https://docs.mapbox.com/mapbox-gl-js/example/popup-on-click/) + * @see [Attach a popup to a marker instance](https://docs.mapbox.com/mapbox-gl-js/example/set-popup/) */ setHTML(html: string) { const frag = window.document.createDocumentFragment(); diff --git a/src/util/ajax.js b/src/util/ajax.js index be730d0ed93..7c2d4445251 100644 --- a/src/util/ajax.js +++ b/src/util/ajax.js @@ -39,7 +39,23 @@ if (typeof Object.freeze == 'function') { * @typedef {Object} RequestParameters * @property {string} url The URL to be requested. * @property {Object} headers The headers to be sent with the request. + * @property {string} method Request method `'GET' | 'POST' | 'PUT'`. + * @property {string} body Request body. + * @property {string} type Response body type to be returned `'string' | 'json' | 'arrayBuffer'`. * @property {string} credentials `'same-origin'|'include'` Use 'include' to send cookies with cross-origin requests. + * @property {boolean} collectResourceTiming If true, Resource Timing API information will be collected for these transformed requests and returned in a resourceTiming property of relevant data events. + * @example + * // use transformRequest to modify requests that begin with `http://myHost` + * transformRequest: function(url, resourceType) { + * if (resourceType === 'Source' && url.indexOf('http://myHost') > -1) { + * return { + * url: url.replace('http', 'https'), + * headers: { 'my-custom-header': true }, + * credentials: 'include' // Include cookies for cross-origin requests + * } + * } + * } + * */ export type RequestParameters = { url: string, diff --git a/src/util/debug.js b/src/util/debug.js index 0255172726f..e26672e7472 100644 --- a/src/util/debug.js +++ b/src/util/debug.js @@ -5,6 +5,8 @@ import window from './window'; /** * This is a private namespace for utility functions that will get automatically stripped * out in production builds. + * + * @private */ export const Debug = { extend(dest: Object, ...sources: Array): Object { From cd7d14bfe2414c39f7034971f9dbb974212273f3 Mon Sep 17 00:00:00 2001 From: Joakim Berglund Date: Mon, 27 Apr 2020 21:30:27 +0200 Subject: [PATCH 26/50] Reverse tap-drag-zoom direction (#9618) fixes #9612 --- src/ui/handler/tap_drag_zoom.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ui/handler/tap_drag_zoom.js b/src/ui/handler/tap_drag_zoom.js index 7f651f6b99e..c1e57bf931f 100644 --- a/src/ui/handler/tap_drag_zoom.js +++ b/src/ui/handler/tap_drag_zoom.js @@ -62,7 +62,7 @@ export default class TapDragZoomHandler { this._active = true; return { - zoomDelta: dist / -128 + zoomDelta: dist / 128 }; } } From ca489f0c4312c0eb514e3bb2388249d687b6c0b6 Mon Sep 17 00:00:00 2001 From: Mikhail Pozdnyakov Date: Tue, 28 Apr 2020 13:19:08 +0300 Subject: [PATCH 27/50] [render tests] Add `all-anchors-labels-priority-tile-map-mode` test --- test/ignores.json | 3 +- .../expected.png | Bin 0 -> 111151 bytes .../style.json | 99 ++++++++++++++++++ 3 files changed, 101 insertions(+), 1 deletion(-) create mode 100644 test/integration/render-tests/text-variable-anchor/all-anchors-labels-priority-tile-map-mode/expected.png create mode 100644 test/integration/render-tests/text-variable-anchor/all-anchors-labels-priority-tile-map-mode/style.json diff --git a/test/ignores.json b/test/ignores.json index 60f94aea2f9..40fb7d909fa 100644 --- a/test/ignores.json +++ b/test/ignores.json @@ -24,5 +24,6 @@ "render-tests/text-variable-anchor/left-top-right-bottom-offset-tile-map-mode": "skip - mapbox-gl-js does not support tile-mode", "render-tests/tile-mode/streets-v11": "skip - mapbox-gl-js does not support tile-mode", "render-tests/within/paint-line": "https://github.com/mapbox/mapbox-gl-js/issues/7023", - "render-tests/icon-text-fit/text-variable-anchor-tile-map-mode": "skip - mapbox-gl-js does not support tile-mode" + "render-tests/icon-text-fit/text-variable-anchor-tile-map-mode": "skip - mapbox-gl-js does not support tile-mode", + "render-tests/text-variable-anchor/all-anchors-labels-priority-tile-map-mode": "skip - mapbox-gl-js does not support tile-mode" } diff --git a/test/integration/render-tests/text-variable-anchor/all-anchors-labels-priority-tile-map-mode/expected.png b/test/integration/render-tests/text-variable-anchor/all-anchors-labels-priority-tile-map-mode/expected.png new file mode 100644 index 0000000000000000000000000000000000000000..22bb9c879530740e4871db3b6ebe06f5225ce77b GIT binary patch literal 111151 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%0AK}PAh+fISx@ziiR6}eF&*IDo{@~s_8=uK! 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 Q0RKCvV?=wRWqalS0HE1KGXMYp literal 0 HcmV?d00001 diff --git a/test/integration/render-tests/text-variable-anchor/all-anchors-labels-priority-tile-map-mode/style.json b/test/integration/render-tests/text-variable-anchor/all-anchors-labels-priority-tile-map-mode/style.json new file mode 100644 index 00000000000..7fb8b4faf3b --- /dev/null +++ b/test/integration/render-tests/text-variable-anchor/all-anchors-labels-priority-tile-map-mode/style.json @@ -0,0 +1,99 @@ +{ + "version": 8, + "metadata": { + "test": { + "debug": true, + "collisionDebug": true, + "mapMode": "tile" + } + }, + "center": [ + 13.418056, + 52.499167 + ], + "zoom": 14, + "sources": { + "mapbox": { + "type": "vector", + "maxzoom": 14, + "tiles": [ + "local://tiles/{z}-{x}-{y}.mvt" + ] + } + }, + "sprite": "local://sprites/sprite", + "glyphs": "local://glyphs/{fontstack}/{range}.pbf", + "layers": [ + { + "id": "background", + "type": "background", + "paint": { + "background-color": "white" + } + }, + { + "id": "low-priority", + "type": "symbol", + "source": "mapbox", + "source-layer": "poi_label", + "filter": [ + "==", + "maki", + "restaurant" + ], + "layout": { + "text-field": "Low", + "text-font": [ + "Open Sans Semibold", + "Arial Unicode MS Bold" + ], + "text-max-width": 5, + "text-justify": "auto", + "text-variable-anchor": [ + "center", + "top", + "bottom", + "left", + "right", + "top-left", + "top-right", + "bottom-left", + "bottom-right" + ] + }, + "paint": { + "text-color": "red" + } + }, + { + "id": "high-priority", + "type": "symbol", + "source": "mapbox", + "source-layer": "poi_label", + "filter": [ + "==", + "maki", + "restaurant" + ], + "layout": { + "text-field": "High", + "text-font": [ + "Open Sans Semibold", + "Arial Unicode MS Bold" + ], + "text-max-width": 5, + "text-justify": "auto", + "text-variable-anchor": [ + "center", + "top", + "bottom", + "left", + "right", + "top-left", + "top-right", + "bottom-left", + "bottom-right" + ] + } + }] +} From ec726ef6e4fc0b4ecfd1cbbd5a1f8e6835b89d9c Mon Sep 17 00:00:00 2001 From: Karim Naaji Date: Tue, 28 Apr 2020 08:19:46 -0700 Subject: [PATCH 28/50] Pull changelog from v1.10.0 in master (#9635) * Pull changelog from v1.10.0 in master * Add stylespec CHANGELOG.md --- CHANGELOG.md | 30 ++++++++++++++++++++++++++++++ src/style-spec/CHANGELOG.md | 5 +++++ 2 files changed, 35 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index c197eb097c8..1adef5386dd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,33 @@ +## 1.10.0 + +### ✨ Features +* Add `mapboxgl.prewarm()` and `mapboxgl.clearPrewarmedResources()` methods to allow developers to optimize load times for their maps ([#9391](https://github.com/mapbox/mapbox-gl-js/pull/9391)) +* Add `index-of` and `slice` expressions to search arrays and strings for the first occurrence of a specified value and return a section of the original array or string ([#9450](https://github.com/mapbox/mapbox-gl-js/pull/9450)) (h/t [lbutler](https://github.com/lbutler)) +* Correctly set RTL text plugin status if the plugin URL could not be loaded. This allows developers to add retry logic on network errors when loading the plugin ([#9489](https://github.com/mapbox/mapbox-gl-js/pull/9489)) + +### 🍏 Gestures +This release significantly refactors and improves gesture handling on desktop and mobile. Three new touch gestures have been added: `two-finger swipe` to adjust pitch, `two-finger double tap` to zoom out, and `tap then drag` to adjust zoom with one finger ([#9365](https://github.com/mapbox/mapbox-gl-js/pull/9365)). In addition, this release brings the following changes and bug fixes: + +- It's now possible to interact with multiple maps on the same page at the same time ([#9365](https://github.com/mapbox/mapbox-gl-js/pull/9365)) +- Fix map jump when releasing one finger after pinch zoom ([#9136](https://github.com/mapbox/mapbox-gl-js/issues/9136)) +- Stop mousedown and touchstart from interrupting `easeTo` animations when interaction handlers are disabled ([#8725](https://github.com/mapbox/mapbox-gl-js/issues/8725)) +- Stop mouse wheel from interrupting animations when `map.scrollZoom` is disabled ([#9230](https://github.com/mapbox/mapbox-gl-js/issues/9230)) +- A camera change can no longer be prevented by disabling the interaction handler within the camera change event. Selectively prevent camera changes by listening to the `mousedown` or `touchstart` map event and calling [.preventDefault()](https://docs.mapbox.com/mapbox-gl-js/api/#mapmouseevent#preventdefault) ([#9365](https://github.com/mapbox/mapbox-gl-js/pull/9365)) +- Undocumented properties on the camera change events fired by the doubleClickZoom handler have been removed ([#9365](https://github.com/mapbox/mapbox-gl-js/pull/9365)) + +### 🐞 Improvements and bug fixes +* Line labels now have improved collision detection, with greater precision in placement, reduced memory footprint, better placement under pitched camera orientations ([#9219](https://github.com/mapbox/mapbox-gl-js/pull/9219)) +* Fix `GlyphManager` continually re-requesting missing glyph ranges ([#8027](https://github.com/mapbox/mapbox-gl-js/issues/8027), fixed by [#9375](https://github.com/mapbox/mapbox-gl-js/pull/9375)) (h/t [oterral](https://github.com/oterral)) +* Avoid throwing errors when calling certain popup methods before the popup element is created ([#9433](https://github.com/mapbox/mapbox-gl-js/pull/9433)) +* Fix a bug where fill-extrusion features with colinear points were not returned by `map.queryRenderedFeatures(...)` ([#9454](https://github.com/mapbox/mapbox-gl-js/pull/9454)) +* Fix a bug where using feature state on a large input could cause a stack overflow error ([#9463](https://github.com/mapbox/mapbox-gl-js/pull/9463)) +* Fix exception when using `background-pattern` with data driven expressions ([#9518](https://github.com/mapbox/mapbox-gl-js/issues/9518), fixed by [#9520](https://github.com/mapbox/mapbox-gl-js/pull/9520)) +* Fix a bug where UI popups were potentially leaking event listeners ([#9498](https://github.com/mapbox/mapbox-gl-js/pull/9498)) (h/t [mbell697](https://github.com/mbell697)) +* Fix a bug where the `within` expression would return inconsistent values for points on tile boundaries ([#9411](https://github.com/mapbox/mapbox-gl-js/issues/9411), [#9428](https://github.com/mapbox/mapbox-gl-js/pull/9428)) +* Fix a bug where the `within` expression would incorrectly evaluate geometries that cross the antimeridian ([#9440](https://github.com/mapbox/mapbox-gl-js/pull/9440)) +* Fix possible undefined exception on paint variable of style layer ([#9437](https://github.com/mapbox/mapbox-gl-js/pull/9437)) (h/t [mannnick24](https://github.com/mannnick24)) +* Upgrade minimist to ^1.2.5 to get fix for security issue [CVE-2020-7598](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-7598) upstream ([#9425](https://github.com/mapbox/mapbox-gl-js/issues/9431), fixed by [#9425](https://github.com/mapbox/mapbox-gl-js/pull/9425)) (h/t [watson](https://github.com/watson)) + ## 1.9.1 ### 🐞 Bug fixes diff --git a/src/style-spec/CHANGELOG.md b/src/style-spec/CHANGELOG.md index c6588db7409..90d9917e73c 100644 --- a/src/style-spec/CHANGELOG.md +++ b/src/style-spec/CHANGELOG.md @@ -1,3 +1,8 @@ +## 13.14.0 + +### ✨ Features and improvements +* Add `index-of` and `slice` expressions to search arrays and strings for the first occurrence of a specified value and return a section of the original array or string ([#9450](https://github.com/mapbox/mapbox-gl-js/pull/9450)) (h/t [lbutler](https://github.com/lbutler)) + ## 13.13.1 ### ✨ Features and improvements From aa6b4b2e51ac19156ba1537baf32a63551313612 Mon Sep 17 00:00:00 2001 From: Sanaz Golbabaei <32684731+sgolbabaei@users.noreply.github.com> Date: Fri, 1 May 2020 13:33:51 -0700 Subject: [PATCH 29/50] shader compilation speedup (#9497) * first draft getting Attribute info some clean-up Code clean-up Addressing PR comments move Regex into the parameter list Remove using Set since some the functionalities of Set is not supported in IE11. * Fixing a render-test failure --- src/data/bucket/circle_bucket.js | 2 +- src/data/bucket/fill_bucket.js | 2 +- src/data/bucket/fill_extrusion_bucket.js | 2 +- src/data/bucket/line_bucket.js | 2 +- src/data/bucket/symbol_bucket.js | 4 +- src/data/program_configuration.js | 38 ++++++++++-- src/render/painter.js | 2 +- src/render/program.js | 74 +++++++++++++++--------- src/shaders/shaders.js | 7 ++- 9 files changed, 92 insertions(+), 41 deletions(-) diff --git a/src/data/bucket/circle_bucket.js b/src/data/bucket/circle_bucket.js index e9f86df667c..f86e63f53de 100644 --- a/src/data/bucket/circle_bucket.js +++ b/src/data/bucket/circle_bucket.js @@ -72,7 +72,7 @@ class CircleBucket implements Bucke this.layoutVertexArray = new CircleLayoutArray(); this.indexArray = new TriangleIndexArray(); this.segments = new SegmentVector(); - this.programConfigurations = new ProgramConfigurationSet(layoutAttributes, options.layers, options.zoom); + this.programConfigurations = new ProgramConfigurationSet(options.layers, options.zoom); this.stateDependentLayerIds = this.layers.filter((l) => l.isStateDependent()).map((l) => l.id); } diff --git a/src/data/bucket/fill_bucket.js b/src/data/bucket/fill_bucket.js index 297ee0a872c..d46c7e0af09 100644 --- a/src/data/bucket/fill_bucket.js +++ b/src/data/bucket/fill_bucket.js @@ -68,7 +68,7 @@ class FillBucket implements Bucket { this.layoutVertexArray = new FillLayoutArray(); this.indexArray = new TriangleIndexArray(); this.indexArray2 = new LineIndexArray(); - this.programConfigurations = new ProgramConfigurationSet(layoutAttributes, options.layers, options.zoom); + this.programConfigurations = new ProgramConfigurationSet(options.layers, options.zoom); this.segments = new SegmentVector(); this.segments2 = new SegmentVector(); this.stateDependentLayerIds = this.layers.filter((l) => l.isStateDependent()).map((l) => l.id); diff --git a/src/data/bucket/fill_extrusion_bucket.js b/src/data/bucket/fill_extrusion_bucket.js index bf507349946..0378758ba62 100644 --- a/src/data/bucket/fill_extrusion_bucket.js +++ b/src/data/bucket/fill_extrusion_bucket.js @@ -82,7 +82,7 @@ class FillExtrusionBucket implements Bucket { this.layoutVertexArray = new FillExtrusionLayoutArray(); this.indexArray = new TriangleIndexArray(); - this.programConfigurations = new ProgramConfigurationSet(layoutAttributes, options.layers, options.zoom); + this.programConfigurations = new ProgramConfigurationSet(options.layers, options.zoom); this.segments = new SegmentVector(); this.stateDependentLayerIds = this.layers.filter((l) => l.isStateDependent()).map((l) => l.id); diff --git a/src/data/bucket/line_bucket.js b/src/data/bucket/line_bucket.js index d8a99723174..5764ddd4a9e 100644 --- a/src/data/bucket/line_bucket.js +++ b/src/data/bucket/line_bucket.js @@ -111,7 +111,7 @@ class LineBucket implements Bucket { this.layoutVertexArray = new LineLayoutArray(); this.indexArray = new TriangleIndexArray(); - this.programConfigurations = new ProgramConfigurationSet(layoutAttributes, options.layers, options.zoom); + this.programConfigurations = new ProgramConfigurationSet(options.layers, options.zoom); this.segments = new SegmentVector(); this.stateDependentLayerIds = this.layers.filter((l) => l.isStateDependent()).map((l) => l.id); diff --git a/src/data/bucket/symbol_bucket.js b/src/data/bucket/symbol_bucket.js index cd7aadfaf66..02d96009206 100644 --- a/src/data/bucket/symbol_bucket.js +++ b/src/data/bucket/symbol_bucket.js @@ -382,8 +382,8 @@ class SymbolBucket implements Bucket { } createArrays() { - this.text = new SymbolBuffers(new ProgramConfigurationSet(symbolLayoutAttributes.members, this.layers, this.zoom, property => /^text/.test(property))); - this.icon = new SymbolBuffers(new ProgramConfigurationSet(symbolLayoutAttributes.members, this.layers, this.zoom, property => /^icon/.test(property))); + this.text = new SymbolBuffers(new ProgramConfigurationSet(this.layers, this.zoom, property => /^text/.test(property))); + this.icon = new SymbolBuffers(new ProgramConfigurationSet(this.layers, this.zoom, property => /^icon/.test(property))); this.glyphOffsetArray = new GlyphOffsetArray(); this.lineVertexArray = new SymbolLineVertexArray(); diff --git a/src/data/program_configuration.js b/src/data/program_configuration.js index 2d8c1578041..0ea6ee93895 100644 --- a/src/data/program_configuration.js +++ b/src/data/program_configuration.js @@ -398,13 +398,11 @@ class CrossFadedCompositeBinder implements AttributeBinder { export default class ProgramConfiguration { binders: {[_: string]: (AttributeBinder | UniformBinder) }; cacheKey: string; - layoutAttributes: Array; _buffers: Array; - constructor(layer: TypedStyleLayer, zoom: number, filterProperties: (_: string) => boolean, layoutAttributes: Array) { + constructor(layer: TypedStyleLayer, zoom: number, filterProperties: (_: string) => boolean) { this.binders = {}; - this.layoutAttributes = layoutAttributes; this._buffers = []; const keys = []; @@ -500,6 +498,36 @@ export default class ProgramConfiguration { return result; } + getBinderAttributes(): Array { + const result = []; + for (const property in this.binders) { + const binder = this.binders[property]; + if (binder instanceof SourceExpressionBinder || binder instanceof CompositeExpressionBinder) { + for (let i = 0; i < binder.paintVertexAttributes.length; i++) { + result.push(binder.paintVertexAttributes[i].name); + } + } else if (binder instanceof CrossFadedCompositeBinder) { + for (let i = 0; i < patternAttributes.members.length; i++) { + result.push(patternAttributes.members[i].name); + } + } + } + return result; + } + + getBinderUniforms(): Array { + const uniforms = []; + for (const property in this.binders) { + const binder = this.binders[property]; + if (binder instanceof ConstantBinder || binder instanceof CrossFadedConstantBinder || binder instanceof CompositeExpressionBinder) { + for (const uniformName of binder.uniformNames) { + uniforms.push(uniformName); + } + } + } + return uniforms; + } + getPaintVertexBuffers(): Array { return this._buffers; } @@ -567,10 +595,10 @@ export class ProgramConfigurationSet { _featureMap: FeaturePositionMap; _bufferOffset: number; - constructor(layoutAttributes: Array, layers: $ReadOnlyArray, zoom: number, filterProperties: (_: string) => boolean = () => true) { + constructor(layers: $ReadOnlyArray, zoom: number, filterProperties: (_: string) => boolean = () => true) { this.programConfigurations = {}; for (const layer of layers) { - this.programConfigurations[layer.id] = new ProgramConfiguration(layer, zoom, filterProperties, layoutAttributes); + this.programConfigurations[layer.id] = new ProgramConfiguration(layer, zoom, filterProperties); } this.needsUpload = false; this._featureMap = new FeaturePositionMap(); diff --git a/src/render/painter.js b/src/render/painter.js index 165a1e6e91c..56507434800 100644 --- a/src/render/painter.js +++ b/src/render/painter.js @@ -599,7 +599,7 @@ class Painter { this.cache = this.cache || {}; const key = `${name}${programConfiguration ? programConfiguration.cacheKey : ''}${this._showOverdrawInspector ? '/overdraw' : ''}`; if (!this.cache[key]) { - this.cache[key] = new Program(this.context, shaders[name], programConfiguration, programUniforms[name], this._showOverdrawInspector); + this.cache[key] = new Program(this.context, name, shaders[name], programConfiguration, programUniforms[name], this._showOverdrawInspector); } return this.cache[key]; } diff --git a/src/render/program.js b/src/render/program.js index 962988a78c5..6345516f619 100644 --- a/src/render/program.js +++ b/src/render/program.js @@ -21,6 +21,16 @@ export type DrawMode = | $PropertyType | $PropertyType; +function getTokenizedAttributesAndUniforms (array: Array): Array { + const result = []; + + for (let i = 0; i < array.length; i++) { + if (array[i] === null) continue; + const token = array[i].split(' '); + result.push(token.pop()); + } + return result; +} class Program { program: WebGLProgram; attributes: {[_: string]: number}; @@ -30,13 +40,27 @@ class Program { failedToCreate: boolean; constructor(context: Context, - source: {fragmentSource: string, vertexSource: string}, - configuration: ?ProgramConfiguration, - fixedUniforms: (Context, UniformLocations) => Us, - showOverdrawInspector: boolean) { + name: string, + source: {fragmentSource: string, vertexSource: string, staticAttributes: Array, staticUniforms: Array}, + configuration: ?ProgramConfiguration, + fixedUniforms: (Context, UniformLocations) => Us, + showOverdrawInspector: boolean) { const gl = context.gl; this.program = gl.createProgram(); + const staticAttrInfo = getTokenizedAttributesAndUniforms(source.staticAttributes); + const dynamicAttrInfo = configuration ? configuration.getBinderAttributes() : []; + const allAttrInfo = staticAttrInfo.concat(dynamicAttrInfo); + + const staticUniformsInfo = source.staticUniforms ? getTokenizedAttributesAndUniforms(source.staticUniforms) : []; + const dynamicUniformsInfo = configuration ? configuration.getBinderUniforms() : []; + // remove duplicate uniforms + const uniformList = staticUniformsInfo.concat(dynamicUniformsInfo); + const allUniformsInfo = []; + for (const uniform of uniformList) { + if (allUniformsInfo.indexOf(uniform) < 0) allUniformsInfo.push(uniform); + } + const defines = configuration ? configuration.defines() : []; if (showOverdrawInspector) { defines.push('#define OVERDRAW_INSPECTOR;'); @@ -64,13 +88,16 @@ class Program { assert(gl.getShaderParameter(vertexShader, gl.COMPILE_STATUS), (gl.getShaderInfoLog(vertexShader): any)); gl.attachShader(this.program, vertexShader); - // Manually bind layout attributes in the order defined by their - // ProgramInterface so that we don't dynamically link an unused - // attribute at position 0, which can cause rendering to fail for an - // entire layer (see #4607, #4728) - const layoutAttributes = configuration ? configuration.layoutAttributes : []; - for (let i = 0; i < layoutAttributes.length; i++) { - gl.bindAttribLocation(this.program, i, layoutAttributes[i].name); + this.attributes = {}; + const uniformLocations = {}; + + this.numAttributes = allAttrInfo.length; + + for (let i = 0; i < this.numAttributes; i++) { + if (allAttrInfo[i]) { + gl.bindAttribLocation(this.program, i, allAttrInfo[i]); + this.attributes[allAttrInfo[i]] = i; + } } gl.linkProgram(this.program); @@ -79,23 +106,14 @@ class Program { gl.deleteShader(vertexShader); gl.deleteShader(fragmentShader); - this.numAttributes = gl.getProgramParameter(this.program, gl.ACTIVE_ATTRIBUTES); - - this.attributes = {}; - const uniformLocations = {}; - - for (let i = 0; i < this.numAttributes; i++) { - const attribute = gl.getActiveAttrib(this.program, i); - if (attribute) { - this.attributes[attribute.name] = gl.getAttribLocation(this.program, attribute.name); - } - } - - const numUniforms = gl.getProgramParameter(this.program, gl.ACTIVE_UNIFORMS); - for (let i = 0; i < numUniforms; i++) { - const uniform = gl.getActiveUniform(this.program, i); - if (uniform) { - uniformLocations[uniform.name] = gl.getUniformLocation(this.program, uniform.name); + const uniformsArray = Array.from(allUniformsInfo); + for (let it = 0; it < uniformsArray.length; it++) { + const uniform = uniformsArray[it]; + if (uniform && !uniformLocations[uniform]) { + const uniformLocation = gl.getUniformLocation(this.program, uniform); + if (uniformLocation) { + uniformLocations[uniform] = uniformLocation; + } } } diff --git a/src/shaders/shaders.js b/src/shaders/shaders.js index 221728dfddd..25f1fabdbcb 100644 --- a/src/shaders/shaders.js +++ b/src/shaders/shaders.js @@ -87,6 +87,11 @@ export const symbolTextAndIcon = compile(symbolTextAndIconFrag, symbolTextAndIco function compile(fragmentSource, vertexSource) { const re = /#pragma mapbox: ([\w]+) ([\w]+) ([\w]+) ([\w]+)/g; + const staticAttributes = vertexSource.match(/attribute ([\w]+) ([\w]+)/g); + const fragmentUniforms = fragmentSource.match(/uniform ([\w]+) ([\w]+)([\s]*)([\w]*)/g); + const vertexUniforms = vertexSource.match(/uniform ([\w]+) ([\w]+)([\s]*)([\w]*)/g); + const staticUniforms = vertexUniforms ? vertexUniforms.concat(fragmentUniforms) : fragmentUniforms; + const fragmentPragmas = {}; fragmentSource = fragmentSource.replace(re, (match, operation, precision, type, name) => { @@ -176,5 +181,5 @@ uniform ${precision} ${type} u_${name}; } }); - return {fragmentSource, vertexSource}; + return {fragmentSource, vertexSource, staticAttributes, staticUniforms}; } From 907fdbf6a0d3d7c15587fe109c88a8d85be8a0b1 Mon Sep 17 00:00:00 2001 From: Ryan Hamley Date: Mon, 4 May 2020 16:05:54 -0700 Subject: [PATCH 30/50] Fix instrumentation for memory metrics (#9654) --- src/ui/map.js | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/ui/map.js b/src/ui/map.js index 613bd549f47..29cd55d40c1 100755 --- a/src/ui/map.js +++ b/src/ui/map.js @@ -2522,13 +2522,14 @@ class Map extends Camera { if (somethingDirty || this._repaint) { this.triggerRepaint(); } else if (!this.isMoving() && this.loaded()) { - if (!this._fullyLoaded) { - this._fullyLoaded = true; - PerformanceUtils.mark(PerformanceMarkers.fullLoad); - } this.fire(new Event('idle')); } + if (this._loaded && !this._fullyLoaded && !somethingDirty) { + this._fullyLoaded = true; + PerformanceUtils.mark(PerformanceMarkers.fullLoad); + } + return this; } From 2ec9611d258d2d6153c992cd25e836f2c9230027 Mon Sep 17 00:00:00 2001 From: Kara Stubbs Date: Wed, 6 May 2020 18:45:36 +0100 Subject: [PATCH 31/50] Fix CONTRIBUTING.md documentation of setup. (#9653) Noticed this bug in the documentation of the setup when getting round to contributing a PR. ## Launch Checklist - [x] briefly describe the changes in this PR - [ ] apply changelog label ('bug', 'feature', 'docs', etc) or use the label 'skip changelog' --- CONTRIBUTING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 9c1dfc3fdc1..d3cbb5d9e14 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -139,7 +139,7 @@ Here is a recommended way to get setup: 1. Fork this project 2. Clone your new fork, `git clone git@github.com:GithubUser/mapbox-gl-js.git` 3. `cd mapbox-gl-js` -4. Add the Mapbox repository as an upstream repository: `git add remote upstream git@github.com:mapbox/mapbox-gl-js.git` +4. Add the Mapbox repository as an upstream repository: `git remote add upstream git@github.com:mapbox/mapbox-gl-js.git` 5. Create a new branch `git checkout -b your-branch` for your contribution 6. Write code, open a PR from your branch when you're ready 7. If you need to rebase your fork's PR branch onto master to resolve conflicts: `git fetch upstream`, `git rebase upstream/master` and force push to Github `git push --force origin your-branch` From 888ca8ab29bfc07d46b0a15f94a2c8b57a4b9a34 Mon Sep 17 00:00:00 2001 From: Ryan Hamley Date: Thu, 7 May 2020 10:51:00 -0700 Subject: [PATCH 32/50] update attributioncontrol (#9665) Co-authored-by: HeyStenson --- src/ui/control/attribution_control.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ui/control/attribution_control.js b/src/ui/control/attribution_control.js index 628148677fe..a1664f7da2b 100644 --- a/src/ui/control/attribution_control.js +++ b/src/ui/control/attribution_control.js @@ -12,11 +12,11 @@ type Options = { }; /** - * An `AttributionControl` control presents the map's [attribution information](https://www.mapbox.com/help/attribution/). + * An `AttributionControl` control presents the map's [attribution information](https://docs.mapbox.com/help/how-mapbox-works/attribution/). * * @implements {IControl} * @param {Object} [options] - * @param {boolean} [options.compact] If `true` force a compact attribution that shows the full attribution on mouse hover, or if `false` force the full attribution control. The default is a responsive attribution that collapses when the map is less than 640 pixels wide. + * @param {boolean} [options.compact] If `true`, force a compact attribution that shows the full attribution on mouse hover. If `false`, force the full attribution control. The default is a responsive attribution that collapses when the map is less than 640 pixels wide. **Attribution should not be collapsed if it can comfortably fit on the map. `compact` should only be used to modify default attribution when map size makes it impossible to fit [default attribution](https://docs.mapbox.com/help/how-mapbox-works/attribution/) and when the automatic compact resizing for default settings are not sufficient.** * @param {string | Array} [options.customAttribution] String or strings to show in addition to any other attributions. * @example * var map = new mapboxgl.Map({attributionControl: false}) From a655fc7d6b214c8e00bdbfd0e9e0eb54b9454075 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Minh=20Nguy=E1=BB=85n?= Date: Thu, 7 May 2020 17:00:14 -0700 Subject: [PATCH 33/50] ios-v5.9.0, macos-v0.16.0 compatibility Updated the compatibility tables for iOS map SDK v5.9.0 and macOS map SDK v0.16.0. --- src/style-spec/reference/v8.json | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/style-spec/reference/v8.json b/src/style-spec/reference/v8.json index 8b12accf811..47ba798a051 100644 --- a/src/style-spec/reference/v8.json +++ b/src/style-spec/reference/v8.json @@ -695,10 +695,16 @@ "doc": "Sorts features in ascending order based on this value. Features with a higher sort key will appear above features with a lower sort key.", "sdk-support": { "basic functionality": { - "js": "1.2.0" + "js": "1.2.0", + "android": "9.2.0", + "ios": "5.9.0", + "macos": "0.16.0" }, "data-driven styling": { - "js": "1.2.0" + "js": "1.2.0", + "android": "9.2.0", + "ios": "5.9.0", + "macos": "0.16.0" } }, "expression": { From 6013c3f8b912617b751fee54c85cea426592a19e Mon Sep 17 00:00:00 2001 From: Chloe Krawczyk Date: Fri, 8 May 2020 16:56:49 -0700 Subject: [PATCH 34/50] [docs] Add distance expression entry; update other expressions; update symbol-z-order (#9655) fix https://github.com/mapbox/mapbox-gl-js/issues/9617 --- src/style-spec/reference/v8.json | 52 +++++++++++++++---------- test/unit/style-spec/expression.test.js | 6 ++- 2 files changed, 37 insertions(+), 21 deletions(-) diff --git a/src/style-spec/reference/v8.json b/src/style-spec/reference/v8.json index 47ba798a051..5101dbdbc61 100644 --- a/src/style-spec/reference/v8.json +++ b/src/style-spec/reference/v8.json @@ -1071,17 +1071,17 @@ "type": "enum", "values": { "auto": { - "doc": "If `symbol-sort-key` is set, sort based on that. Otherwise sort symbols by their y-position relative to the viewport." + "doc": "Sorts symbols by `symbol-sort-key` if set. Otherwise, sorts symbols by their y-position relative to the viewport if `icon-allow-overlap` or `text-allow-overlap` is set to `true` or `icon-ignore-placement` or `text-ignore-placement` is `false`." }, "viewport-y": { - "doc": "Symbols will be sorted by their y-position relative to the viewport." + "doc": "Sorts symbols by their y-position relative to the viewport if `icon-allow-overlap` or `text-allow-overlap` is set to `true` or `icon-ignore-placement` or `text-ignore-placement` is `false`." }, "source": { - "doc": "Symbols will be rendered in the same order as the source data with no sorting applied." + "doc": "Sorts symbols by `symbol-sort-key` if set. Otherwise, no sorting is applied; symbols are rendered in the same order as the source data." } }, "default": "auto", - "doc": "Controls the order in which overlapping symbols in the same layer are rendered", + "doc": "Determines whether overlapping symbols in the same layer are rendered in the order that they appear in the data source or by their y-position relative to the viewport. To control the order and prioritization of symbols otherwise, use `symbol-sort-key`.", "sdk-support": { "basic functionality": { "js": "0.49.0", @@ -2626,8 +2626,17 @@ } } }, - "index-of": { - "doc": "Returns the first index at which a given element can be found in an array, or for a string, the first occurrence of the specified value. If a second argument is provided, then the search is started from that position. Returns -1 if the value is not found.", + "index-of": { + "doc": "Returns the first position at which an item can be found in an array or a substring can be found in a string, or `-1` if the input cannot be found. Accepts an optional index from where to begin the search.", + "group": "Lookup", + "sdk-support": { + "basic functionality": { + "js": "1.10.0" + } + } + }, + "slice": { + "doc": "Returns an item from an array or a substring from a string from a specified start index, or between a start index and an end index if set. The return value is inclusive of the start index but not of the end index.", "group": "Lookup", "sdk-support": { "basic functionality": { @@ -2648,7 +2657,7 @@ } }, "match": { - "doc": "Selects the output whose label value matches the input value, or the fallback value if no match is found. The input can be any expression (e.g. `[\"get\", \"building_type\"]`). Each label must be either:\n * a single literal value; or\n * an array of literal values, whose values must be all strings or all numbers (e.g. `[100, 101]` or `[\"c\", \"b\"]`). The input matches if any of the values in the array matches, similar to the `\"in\"` operator.\n\nEach label must be unique. If the input type does not match the type of the labels, the result will be the fallback value.", + "doc": "Selects the output whose label value matches the input value, or the fallback value if no match is found. The input can be any expression (e.g. `[\"get\", \"building_type\"]`). Each label must be either:\n - a single literal value; or\n - an array of literal values, whose values must be all strings or all numbers (e.g. `[100, 101]` or `[\"c\", \"b\"]`). The input matches if any of the values in the array matches, similar to the `\"in\"` operator.\nEach label must be unique. If the input type does not match the type of the labels, the result will be the fallback value.", "group": "Decision", "sdk-support": { "basic functionality": { @@ -2684,7 +2693,7 @@ } }, "interpolate": { - "doc": "Produces continuous, smooth results by interpolating between pairs of input and output values (\"stops\"). The `input` may be any numeric expression (e.g., `[\"get\", \"population\"]`). Stop inputs must be numeric literals in strictly ascending order. The output type must be `number`, `array`, or `color`.\n\nInterpolation types:\n- `[\"linear\"]`: interpolates linearly between the pair of stops just less than and just greater than the input.\n- `[\"exponential\", base]`: interpolates exponentially between the stops just less than and just greater than the input. `base` controls the rate at which the output increases: higher values make the output increase more towards the high end of the range. With values close to 1 the output increases linearly.\n- `[\"cubic-bezier\", x1, y1, x2, y2]`: interpolates using the cubic bezier curve defined by the given control points.", + "doc": "Produces continuous, smooth results by interpolating between pairs of input and output values (\"stops\"). The `input` may be any numeric expression (e.g., `[\"get\", \"population\"]`). Stop inputs must be numeric literals in strictly ascending order. The output type must be `number`, `array`, or `color`.\n\nInterpolation types:\n- `[\"linear\"]`: Interpolates linearly between the pair of stops just less than and just greater than the input.\n- `[\"exponential\", base]`: Interpolates exponentially between the stops just less than and just greater than the input. `base` controls the rate at which the output increases: higher values make the output increase more towards the high end of the range. With values close to 1 the output increases linearly.\n- `[\"cubic-bezier\", x1, y1, x2, y2]`: Interpolates using the cubic bezier curve defined by the given control points.", "group": "Ramps, scales, curves", "sdk-support": { "basic functionality": { @@ -2822,7 +2831,7 @@ } }, "format": { - "doc": "Returns `formatted` text containing annotations for use in mixed-format `text-field` entries. For a `text-field` entries of a string type, following option object's properties are supported: If set, the `text-font` value overrides the font specified by the root layout properties. If set, the `font-scale` value specifies a scaling factor relative to the `text-size` specified in the root layout properties. If set, the `text-color` value overrides the color specified by the root paint properties for this layer.", + "doc": "Returns a `formatted` string for displaying mixed-format text in the `text-field` property. The input may contain a string literal or expression, including an [`'image'`](#types-image) expression. Strings may be followed by a style override object that supports the following properties:\n- `\"text-font\"`: Overrides the font stack specified by the root layout property.\n- `\"text-color\"`: Overrides the color specified by the root paint property.\n- `\"font-scale\"`: Applies a scaling factor on `text-size` as specified by the root layout property.", "group": "Types", "sdk-support": { "basic functionality": { @@ -2851,6 +2860,7 @@ }, "image": { "js": "1.6.0", + "android": "8.6.0", "ios": "5.7.0", "macos": "0.15.0" } @@ -3019,7 +3029,7 @@ } }, "geometry-type": { - "doc": "Gets the feature's geometry type: Point, MultiPoint, LineString, MultiLineString, Polygon, MultiPolygon.", + "doc": "Gets the feature's geometry type: `Point`, `MultiPoint`, `LineString`, `MultiLineString`, `Polygon`, `MultiPolygon`.", "group": "Feature data", "sdk-support": { "basic functionality": { @@ -3351,6 +3361,17 @@ } } }, + "distance": { + "doc": "Returns the shortest distance in meters between the evaluated feature and the input geometry. The input value can be a valid GeoJSON of type `Point`, `MultiPoint`, `LineString`, `MultiLineString`, `Polygon`, `MultiPolygon`, `Feature`, or `FeatureCollection`. Distance values returned may vary in precision due to loss in precision from encoding geometries, particularly below zoom level 13.", + "group": "Math", + "sdk-support": { + "basic functionality": { + "android": "9.2.0", + "ios": "5.9.0", + "macos": "0.16.0" + } + } + }, "==": { "doc": "Returns `true` if the input values are equal, `false` otherwise. The comparison is strictly typed: values of different runtime types are always considered unequal. Cases where the types are known to be different at parse time are considered invalid and will produce a parse error. Accepts an optional `collator` argument to control locale-dependent string comparisons.", "group": "Decision", @@ -3496,7 +3517,7 @@ } }, "within": { - "doc": "Returns `true` if the feature being evaluated is inside the pre-defined geometry boundary, `false` otherwise. The expression has one argument which must be a valid GeoJSON Polygon/Multi-Polygon object. The expression only evaluates on `Point` or `LineString` feature. For `Point` feature, The expression will return false if any point of the feature is on the boundary or outside the boundary. For `LineString` feature, the expression will return false if the line is fully outside the boundary, or the line is partially intersecting the boundary, which means either part of the line is outside of the boundary, or end point of the line lies on the boundary.", + "doc": "Returns `true` if the evaluated feature is fully contained inside a boundary of the input geometry, `false` otherwise. The input value can be a valid GeoJSON of type `Polygon`, `MultiPolygon`, `Feature`, or `FeatureCollection`. Supported features for evaluation:\n- `Point`: Returns `false` if a point is on the boundary or falls outside the boundary.\n- `LineString`: Returns `false` if any part of a line falls outside the boundary, the line intersects the boundary, or a line's endpoint is on the boundary.", "group": "Decision", "sdk-support": { "basic functionality": { @@ -3564,15 +3585,6 @@ "macos": "0.9.0" } } - }, - "slice": { - "doc": "Returns a portion of a string or an array starting from the provided beginning index. If a second argument is provided, then the return portion will run to, but not include, the end index.", - "group": "String", - "sdk-support": { - "basic functionality": { - "js": "1.10.0" - } - } } } }, diff --git a/test/unit/style-spec/expression.test.js b/test/unit/style-spec/expression.test.js index 7579190a657..0936eef3cae 100644 --- a/test/unit/style-spec/expression.test.js +++ b/test/unit/style-spec/expression.test.js @@ -11,7 +11,11 @@ const definitionList = Object.keys(definitions).filter((expression) => { test('v8.json includes all definitions from style-spec', (t) => { const v8List = Object.keys(v8.expression_name.values); - t.deepEquals(definitionList, v8List.sort()); + const v8SupportedList = v8List.filter((expression) => { + //filter out expressions that are not supported in GL-JS + return !!v8.expression_name.values[expression]["sdk-support"]["basic functionality"]["js"]; + }); + t.deepEquals(definitionList, v8SupportedList.sort()); t.end(); }); From fb6d18d25ce19b2a7c03014fab2f74db25336acc Mon Sep 17 00:00:00 2001 From: Chloe Krawczyk Date: Sat, 9 May 2020 09:21:06 -0700 Subject: [PATCH 35/50] [docs] Add android version support for stretchable icons (#9672) --- src/style-spec/reference/v8.json | 1 + 1 file changed, 1 insertion(+) diff --git a/src/style-spec/reference/v8.json b/src/style-spec/reference/v8.json index 5101dbdbc61..dc09427c641 100644 --- a/src/style-spec/reference/v8.json +++ b/src/style-spec/reference/v8.json @@ -1271,6 +1271,7 @@ }, "stretchable icons": { "js": "1.6.0", + "android": "9.2.0", "ios": "5.8.0", "macos": "0.15.0" } From 498a96bba904808405ca9b8d8aa5e028689b53d4 Mon Sep 17 00:00:00 2001 From: Ryan Hamley Date: Tue, 12 May 2020 17:19:47 -0700 Subject: [PATCH 36/50] Fix image expression (#9668) --- src/source/worker.js | 6 ++++++ src/source/worker_source.js | 1 + src/style/properties.js | 8 ++++---- src/style/style_layer.js | 4 ++-- 4 files changed, 13 insertions(+), 6 deletions(-) diff --git a/src/source/worker.js b/src/source/worker.js index ab6827e3afc..27a10fa3a54 100644 --- a/src/source/worker.js +++ b/src/source/worker.js @@ -77,6 +77,12 @@ export default class Worker { setImages(mapId: string, images: Array, callback: WorkerTileCallback) { this.availableImages[mapId] = images; + for (const workerSource in this.workerSources[mapId]) { + const ws = this.workerSources[mapId][workerSource]; + for (const source in ws) { + ws[source].availableImages = images; + } + } callback(); } diff --git a/src/source/worker_source.js b/src/source/worker_source.js index 7acc8140728..c800049e3b3 100644 --- a/src/source/worker_source.js +++ b/src/source/worker_source.js @@ -71,6 +71,7 @@ export type WorkerDEMTileCallback = (err: ?Error, result: ?DEMData) => void; * @param layerIndex */ export interface WorkerSource { + availableImages: Array, // Disabled due to https://github.com/facebook/flow/issues/5208 // constructor(actor: Actor, layerIndex: StyleLayerIndex): WorkerSource; diff --git a/src/style/properties.js b/src/style/properties.js index 1f6f971ec0f..57829f85bdf 100644 --- a/src/style/properties.js +++ b/src/style/properties.js @@ -317,10 +317,10 @@ export class Transitioning { this._values = (Object.create(properties.defaultTransitioningPropertyValues): any); } - possiblyEvaluate(parameters: EvaluationParameters, availableImages?: Array): PossiblyEvaluated { + possiblyEvaluate(parameters: EvaluationParameters, canonical?: CanonicalTileID, availableImages?: Array): PossiblyEvaluated { const result = new PossiblyEvaluated(this._properties); // eslint-disable-line no-use-before-define for (const property of Object.keys(this._values)) { - result._values[property] = this._values[property].possiblyEvaluate(parameters, availableImages); + result._values[property] = this._values[property].possiblyEvaluate(parameters, canonical, availableImages); } return result; } @@ -385,10 +385,10 @@ export class Layout { return result; } - possiblyEvaluate(parameters: EvaluationParameters, availableImages?: Array): PossiblyEvaluated { + possiblyEvaluate(parameters: EvaluationParameters, canonical?: CanonicalTileID, availableImages?: Array): PossiblyEvaluated { const result = new PossiblyEvaluated(this._properties); // eslint-disable-line no-use-before-define for (const property of Object.keys(this._values)) { - result._values[property] = this._values[property].possiblyEvaluate(parameters, availableImages); + result._values[property] = this._values[property].possiblyEvaluate(parameters, canonical, availableImages); } return result; } diff --git a/src/style/style_layer.js b/src/style/style_layer.js index 82c3f68df4d..ce06dcb9ace 100644 --- a/src/style/style_layer.js +++ b/src/style/style_layer.js @@ -199,10 +199,10 @@ class StyleLayer extends Evented { } if (this._unevaluatedLayout) { - (this: any).layout = this._unevaluatedLayout.possiblyEvaluate(parameters, availableImages); + (this: any).layout = this._unevaluatedLayout.possiblyEvaluate(parameters, undefined, availableImages); } - (this: any).paint = this._transitioningPaint.possiblyEvaluate(parameters, availableImages); + (this: any).paint = this._transitioningPaint.possiblyEvaluate(parameters, undefined, availableImages); } serialize() { From 184b75d98c79df913cf31cdc256aa45e15df4bfe Mon Sep 17 00:00:00 2001 From: Ansis Brammanis Date: Wed, 13 May 2020 14:05:17 -0400 Subject: [PATCH 37/50] fix #9676, fire touchmove event while handlers are active (#9685) --- src/ui/handler/map_event.js | 9 +++--- test/unit/ui/handler/map_event.test.js | 45 +++++++++++++++++++++++++- 2 files changed, 48 insertions(+), 6 deletions(-) diff --git a/src/ui/handler/map_event.js b/src/ui/handler/map_event.js index 891fe414f2b..31b46e5ba23 100644 --- a/src/ui/handler/map_event.js +++ b/src/ui/handler/map_event.js @@ -68,6 +68,10 @@ export class MapEventHandler { return this._firePreventable(new MapTouchEvent(e.type, this._map, e)); } + touchmove(e: TouchEvent) { + this._map.fire(new MapTouchEvent(e.type, this._map, e)); + } + touchend(e: TouchEvent) { this._map.fire(new MapTouchEvent(e.type, this._map, e)); } @@ -114,11 +118,6 @@ export class BlockableMapEventHandler { this._map.fire(new MapMouseEvent(e.type, this._map, e)); } - touchmove(e: TouchEvent) { - // touchmove map events should not be fired when interaction handlers (pan, rotate, etc) are active - this._map.fire(new MapTouchEvent(e.type, this._map, e)); - } - mousedown() { this._delayContextMenu = true; } diff --git a/test/unit/ui/handler/map_event.test.js b/test/unit/ui/handler/map_event.test.js index 5e41314192e..90eb5b3bb46 100644 --- a/test/unit/ui/handler/map_event.test.js +++ b/test/unit/ui/handler/map_event.test.js @@ -29,7 +29,6 @@ test('MapEvent handler fires touch events with correct values', (t) => { t.deepEqual(touchstart.getCall(0).args[0].point, {x: 0, y: 50}); t.equal(touchmove.callCount, 0); t.equal(touchend.callCount, 0); - console.log(touchstart); simulate.touchmove(map.getCanvas(), {touches: touchesMove, targetTouches: touchesMove}); t.equal(touchstart.callCount, 1); @@ -46,3 +45,47 @@ test('MapEvent handler fires touch events with correct values', (t) => { map.remove(); t.end(); }); + +test('MapEvent handler fires touchmove even while drag handler is active', (t) => { + const map = createMap(t); + const target = map.getCanvas(); + map.dragPan.enable(); + + const touchstart = t.spy(); + const touchmove = t.spy(); + const touchend = t.spy(); + const drag = t.spy(); + + map.on('touchstart', touchstart); + map.on('touchmove', touchmove); + map.on('touchend', touchend); + map.on('drag', drag); + + const touchesStart = [{target, identifier: 1, clientX: 0, clientY: 50}]; + const touchesMove = [{target, identifier: 1, clientX: 0, clientY: 60}]; + const touchesEnd = [{target, identifier: 1, clientX: 0, clientY: 60}]; + + simulate.touchstart(map.getCanvas(), {touches: touchesStart, targetTouches: touchesStart}); + t.equal(touchstart.callCount, 1); + t.deepEqual(touchstart.getCall(0).args[0].point, {x: 0, y: 50}); + t.equal(touchmove.callCount, 0); + t.equal(touchend.callCount, 0); + + simulate.touchmove(map.getCanvas(), {touches: touchesMove, targetTouches: touchesMove}); + t.equal(touchstart.callCount, 1); + t.equal(touchmove.callCount, 1); + t.deepEqual(touchmove.getCall(0).args[0].point, {x: 0, y: 60}); + t.equal(touchend.callCount, 0); + + simulate.touchend(map.getCanvas(), {touches: [], targetTouches: [], changedTouches: touchesEnd}); + t.equal(touchstart.callCount, 1); + t.equal(touchmove.callCount, 1); + t.equal(touchend.callCount, 1); + t.deepEqual(touchend.getCall(0).args[0].point, {x: 0, y: 60}); + + map._renderTaskQueue.run(); + t.equal(drag.callCount, 1); + + map.remove(); + t.end(); +}); From 791a42baafc8aa47deca60ccf06baa43a805a543 Mon Sep 17 00:00:00 2001 From: Karim Naaji Date: Thu, 14 May 2020 08:59:14 -0700 Subject: [PATCH 38/50] Pattern attributes 4-bytes alignment (#9681) * Align pattern_attributes to 4-bytes boundaries This was reported as being an issue on failed render tests on AMD graphics cards * Warn on 4-bytes misalignment $ yarn run codegen Warning: The layout StructArrayLayout3ui6 is not aligned to 4-bytes boundaries. Warning: The layout StructArrayLayout3i6 is not aligned to 4-bytes boundaries. Warning: The layout StructArrayLayout3ui6 is not aligned to 4-bytes boundaries. Warning: The layout StructArrayLayout1ui2 is not aligned to 4-bytes boundaries. * Exclude non-deterministic test from render tests harness This test is non-deterministic on AMD, slight offset in the pattern. Excluding until root cause can be identified on this hardware --- build/generate-struct-arrays.js | 2 ++ src/data/array_types.js | 20 +++++++++----------- src/data/bucket/pattern_attributes.js | 4 ++-- test/ignores.json | 3 ++- 4 files changed, 15 insertions(+), 14 deletions(-) diff --git a/build/generate-struct-arrays.js b/build/generate-struct-arrays.js index ccbc1ecd179..f2ce2887159 100644 --- a/build/generate-struct-arrays.js +++ b/build/generate-struct-arrays.js @@ -93,6 +93,8 @@ function createStructArrayLayoutType({members, size, alignment}) { const key = `${members.map(m => `${m.components}${typeAbbreviations[m.type]}`).join('')}${size}`; const className = `StructArrayLayout${key}`; + // Layout alignment to 4 bytes boundaries can be an issue on some set of graphics cards. Particularly AMD. + if (size % 4 !== 0) { console.warn(`Warning: The layout ${className} is not aligned to 4-bytes boundaries.`); } if (!layoutCache[key]) { layoutCache[key] = { className, diff --git a/src/data/array_types.js b/src/data/array_types.js index 30b2c8c5f2f..db28da52273 100644 --- a/src/data/array_types.js +++ b/src/data/array_types.js @@ -149,12 +149,11 @@ register('StructArrayLayout2i4ub8', StructArrayLayout2i4ub8); /** * Implementation of the StructArray layout: - * [0]: Uint16[8] - * [16]: Uint8[2] + * [0]: Uint16[10] * * @private */ -class StructArrayLayout8ui2ub18 extends StructArray { +class StructArrayLayout10ui20 extends StructArray { uint8: Uint8Array; uint16: Uint16Array; @@ -170,8 +169,7 @@ class StructArrayLayout8ui2ub18 extends StructArray { } emplace(i: number, v0: number, v1: number, v2: number, v3: number, v4: number, v5: number, v6: number, v7: number, v8: number, v9: number) { - const o2 = i * 9; - const o1 = i * 18; + const o2 = i * 10; this.uint16[o2 + 0] = v0; this.uint16[o2 + 1] = v1; this.uint16[o2 + 2] = v2; @@ -180,14 +178,14 @@ class StructArrayLayout8ui2ub18 extends StructArray { this.uint16[o2 + 5] = v5; this.uint16[o2 + 6] = v6; this.uint16[o2 + 7] = v7; - this.uint8[o1 + 16] = v8; - this.uint8[o1 + 17] = v9; + this.uint16[o2 + 8] = v8; + this.uint16[o2 + 9] = v9; return i; } } -StructArrayLayout8ui2ub18.prototype.bytesPerElement = 18; -register('StructArrayLayout8ui2ub18', StructArrayLayout8ui2ub18); +StructArrayLayout10ui20.prototype.bytesPerElement = 20; +register('StructArrayLayout10ui20', StructArrayLayout10ui20); /** * Implementation of the StructArray layout: @@ -1097,7 +1095,7 @@ export { StructArrayLayout4i8, StructArrayLayout2i4i12, StructArrayLayout2i4ub8, - StructArrayLayout8ui2ub18, + StructArrayLayout10ui20, StructArrayLayout4i4ui4i24, StructArrayLayout3f12, StructArrayLayout1ul4, @@ -1122,7 +1120,7 @@ export { StructArrayLayout2i4i12 as FillExtrusionLayoutArray, StructArrayLayout2i4 as HeatmapLayoutArray, StructArrayLayout2i4ub8 as LineLayoutArray, - StructArrayLayout8ui2ub18 as PatternLayoutArray, + StructArrayLayout10ui20 as PatternLayoutArray, StructArrayLayout4i4ui4i24 as SymbolLayoutArray, StructArrayLayout3f12 as SymbolDynamicLayoutArray, StructArrayLayout1ul4 as SymbolOpacityArray, diff --git a/src/data/bucket/pattern_attributes.js b/src/data/bucket/pattern_attributes.js index 01b512b8fc9..c4da6049f44 100644 --- a/src/data/bucket/pattern_attributes.js +++ b/src/data/bucket/pattern_attributes.js @@ -5,8 +5,8 @@ const patternAttributes = createLayout([ // [tl.x, tl.y, br.x, br.y] {name: 'a_pattern_from', components: 4, type: 'Uint16'}, {name: 'a_pattern_to', components: 4, type: 'Uint16'}, - {name: 'a_pixel_ratio_from', components: 1, type: 'Uint8'}, - {name: 'a_pixel_ratio_to', components: 1, type: 'Uint8'}, + {name: 'a_pixel_ratio_from', components: 1, type: 'Uint16'}, + {name: 'a_pixel_ratio_to', components: 1, type: 'Uint16'}, ]); export default patternAttributes; diff --git a/test/ignores.json b/test/ignores.json index 40fb7d909fa..a395277c56b 100644 --- a/test/ignores.json +++ b/test/ignores.json @@ -25,5 +25,6 @@ "render-tests/tile-mode/streets-v11": "skip - mapbox-gl-js does not support tile-mode", "render-tests/within/paint-line": "https://github.com/mapbox/mapbox-gl-js/issues/7023", "render-tests/icon-text-fit/text-variable-anchor-tile-map-mode": "skip - mapbox-gl-js does not support tile-mode", - "render-tests/text-variable-anchor/all-anchors-labels-priority-tile-map-mode": "skip - mapbox-gl-js does not support tile-mode" + "render-tests/text-variable-anchor/all-anchors-labels-priority-tile-map-mode": "skip - mapbox-gl-js does not support tile-mode", + "render-tests/fill-extrusion-pattern/1.5x-on-1x-add-image": "skip - non-deterministic on AMD graphics cards" } From fcc33c1f4133d63ddcf6973dec53f802451da749 Mon Sep 17 00:00:00 2001 From: Ansis Brammanis Date: Thu, 14 May 2020 13:21:10 -0400 Subject: [PATCH 39/50] map.isMoving() match move events fix #9647 (#9679) * map.isMoving() match move events fix #9647 Previously isMoving() would return true if any interaction handler was active. Handlers are sometimes active even if they haven't changed the map yet. This resulted in the isMoving() returning true when the map hasn't moved. This makes isMoving aligned with movestart/move/moveend events. Since move events may be fired after several mouse events have been batched, the camera changes a mouseevent will *later* cause won't be reflected in isMoving() when that mouseevent is fired. * lint * fix test --- src/ui/handler_manager.js | 32 ++++++++++++++++++------ src/ui/map.js | 2 +- test/unit/ui/handler/drag_rotate.test.js | 2 ++ test/unit/ui/map/isMoving.test.js | 12 +++++++++ test/unit/ui/map_events.test.js | 29 +++++++++++++++++++++ 5 files changed, 68 insertions(+), 9 deletions(-) diff --git a/src/ui/handler_manager.js b/src/ui/handler_manager.js index 1589e1d0f59..63160fc020b 100644 --- a/src/ui/handler_manager.js +++ b/src/ui/handler_manager.js @@ -259,6 +259,10 @@ class HandlerManager { return !!this._eventsInProgress.rotate; } + isMoving() { + return Boolean(isMoving(this._eventsInProgress)) || this.isZooming(); + } + _blockedByActive(activeHandlers: { [string]: Handler }, allowed: Array, myName: string) { for (const name in activeHandlers) { if (name === myName) continue; @@ -430,17 +434,23 @@ class HandlerManager { const wasMoving = isMoving(this._eventsInProgress); const nowMoving = isMoving(newEventsInProgress); - if (!wasMoving && nowMoving) { - this._fireEvent('movestart', nowMoving.originalEvent); - } + const startEvents = {}; for (const eventName in newEventsInProgress) { const {originalEvent} = newEventsInProgress[eventName]; - const isStart = !this._eventsInProgress[eventName]; - this._eventsInProgress[eventName] = newEventsInProgress[eventName]; - if (isStart) { - this._fireEvent(`${eventName}start`, originalEvent); + if (!this._eventsInProgress[eventName]) { + startEvents[`${eventName}start`] = originalEvent; } + this._eventsInProgress[eventName] = newEventsInProgress[eventName]; + } + + // fire start events only after this._eventsInProgress has been updated + if (!wasMoving && nowMoving) { + this._fireEvent('movestart', nowMoving.originalEvent); + } + + for (const name in startEvents) { + this._fireEvent(name, startEvents[name]); } if (newEventsInProgress.rotate) this._bearingChanged = true; @@ -454,16 +464,22 @@ class HandlerManager { this._fireEvent(eventName, originalEvent); } + const endEvents = {}; + let originalEndEvent; for (const eventName in this._eventsInProgress) { const {handlerName, originalEvent} = this._eventsInProgress[eventName]; if (!this._handlersById[handlerName].isActive()) { delete this._eventsInProgress[eventName]; originalEndEvent = deactivatedHandlers[handlerName] || originalEvent; - this._fireEvent(`${eventName}end`, originalEndEvent); + endEvents[`${eventName}end`] = originalEndEvent; } } + for (const name in endEvents) { + this._fireEvent(name, endEvents[name]); + } + const stillMoving = isMoving(this._eventsInProgress); if ((wasMoving || nowMoving) && !stillMoving) { this._updatingCamera = true; diff --git a/src/ui/map.js b/src/ui/map.js index 29cd55d40c1..8d726c49e2a 100755 --- a/src/ui/map.js +++ b/src/ui/map.js @@ -853,7 +853,7 @@ class Map extends Camera { * var isMoving = map.isMoving(); */ isMoving(): boolean { - return this._moving || this.handlers.isActive(); + return this._moving || this.handlers.isMoving(); } /** diff --git a/test/unit/ui/handler/drag_rotate.test.js b/test/unit/ui/handler/drag_rotate.test.js index d1107828606..25da019b54b 100644 --- a/test/unit/ui/handler/drag_rotate.test.js +++ b/test/unit/ui/handler/drag_rotate.test.js @@ -273,9 +273,11 @@ test('DragRotateHandler ensures that map.isMoving() returns true during drag', ( simulate.mousedown(map.getCanvas(), {buttons: 2, button: 2}); simulate.mousemove(map.getCanvas(), {buttons: 2, clientX: 10, clientY: 10}); + map._renderTaskQueue.run(); t.ok(map.isMoving()); simulate.mouseup(map.getCanvas(), {buttons: 0, button: 2}); + map._renderTaskQueue.run(); t.ok(!map.isMoving()); map.remove(); diff --git a/test/unit/ui/map/isMoving.test.js b/test/unit/ui/map/isMoving.test.js index 0cf88158a63..f215bb2ddc3 100644 --- a/test/unit/ui/map/isMoving.test.js +++ b/test/unit/ui/map/isMoving.test.js @@ -39,12 +39,18 @@ test('Map#isMoving returns true during a camera zoom animation', (t) => { test('Map#isMoving returns true when drag panning', (t) => { const map = createMap(t); + map.on('movestart', () => { + t.equal(map.isMoving(), true); + }); map.on('dragstart', () => { t.equal(map.isMoving(), true); }); map.on('dragend', () => { t.equal(map.isMoving(), false); + }); + map.on('moveend', () => { + t.equal(map.isMoving(), false); map.remove(); t.end(); }); @@ -65,12 +71,18 @@ test('Map#isMoving returns true when drag rotating', (t) => { // Prevent inertial rotation. t.stub(browser, 'now').returns(0); + map.on('movestart', () => { + t.equal(map.isMoving(), true); + }); map.on('rotatestart', () => { t.equal(map.isMoving(), true); }); map.on('rotateend', () => { t.equal(map.isMoving(), false); + }); + map.on('moveend', () => { + t.equal(map.isMoving(), false); map.remove(); t.end(); }); diff --git a/test/unit/ui/map_events.test.js b/test/unit/ui/map_events.test.js index 21e8041fe88..a0cd8d99d78 100644 --- a/test/unit/ui/map_events.test.js +++ b/test/unit/ui/map_events.test.js @@ -601,3 +601,32 @@ test(`Map#on click fires subsequent click event if there is no corresponding mou map.remove(); t.end(); }); + +test("Map#isMoving() returns false in mousedown/mouseup/click with no movement", (t) => { + const map = createMap(t, {interactive: true, clickTolerance: 4}); + let mousedown, mouseup, click; + map.on('mousedown', () => { mousedown = map.isMoving(); }); + map.on('mouseup', () => { mouseup = map.isMoving(); }); + map.on('click', () => { click = map.isMoving(); }); + + const canvas = map.getCanvas(); + const MouseEvent = window(canvas).MouseEvent; + + canvas.dispatchEvent(new MouseEvent('mousedown', {bubbles: true, clientX: 100, clientY: 100})); + t.equal(mousedown, false); + map._renderTaskQueue.run(); + t.equal(mousedown, false); + + canvas.dispatchEvent(new MouseEvent('mouseup', {bubbles: true, clientX: 100, clientY: 100})); + t.equal(mouseup, false); + map._renderTaskQueue.run(); + t.equal(mouseup, false); + + canvas.dispatchEvent(new MouseEvent('click', {bubbles: true, clientX: 100, clientY: 100})); + t.equal(click, false); + map._renderTaskQueue.run(); + t.equal(click, false); + + map.remove(); + t.end(); +}); From 8ff13033d1c106054bbbe0981369a964a7354ae5 Mon Sep 17 00:00:00 2001 From: Ansis Brammanis Date: Thu, 14 May 2020 13:21:45 -0400 Subject: [PATCH 40/50] fix pinching while touching markers (#9683) * fix multi-finger gestures touching markers Previously we were using e.targetTouches as the list of touches relevant to the map. It contains only the touches that first touched the same target. Touches that hit a marker have a different target. This fixes uses e.touches and filters out touches who's target is not a child of the map. fix #9675 * update tests and add regression test --- src/ui/handler/handler_util.js | 2 +- src/ui/handler/tap_drag_zoom.js | 20 +-- src/ui/handler/tap_recognizer.js | 28 ++-- src/ui/handler/tap_zoom.js | 18 +-- src/ui/handler/touch_pan.js | 20 +-- src/ui/handler/touch_zoom_rotate.js | 28 ++-- src/ui/handler_manager.js | 26 +++- test/unit/ui/handler/dblclick_zoom.test.js | 6 +- test/unit/ui/handler/drag_pan.test.js | 20 ++- test/unit/ui/handler/map_event.test.js | 7 +- .../unit/ui/handler/touch_zoom_rotate.test.js | 145 ++++++++++++++++-- test/unit/ui/map.test.js | 2 +- 12 files changed, 227 insertions(+), 95 deletions(-) diff --git a/src/ui/handler/handler_util.js b/src/ui/handler/handler_util.js index e26f8a80d5b..493f93d6264 100644 --- a/src/ui/handler/handler_util.js +++ b/src/ui/handler/handler_util.js @@ -2,7 +2,7 @@ import assert from 'assert'; -export function indexTouches(touches: TouchList, points: Array) { +export function indexTouches(touches: Array, points: Array) { assert(touches.length === points.length); const obj = {}; for (let i = 0; i < touches.length; i++) { diff --git a/src/ui/handler/tap_drag_zoom.js b/src/ui/handler/tap_drag_zoom.js index c1e57bf931f..d8bf435a2c5 100644 --- a/src/ui/handler/tap_drag_zoom.js +++ b/src/ui/handler/tap_drag_zoom.js @@ -30,7 +30,7 @@ export default class TapDragZoomHandler { this._tap.reset(); } - touchstart(e: TouchEvent, points: Array) { + touchstart(e: TouchEvent, points: Array, mapTouches: Array) { if (this._swipePoint) return; if (this._tapTime && e.timeStamp - this._tapTime > MAX_TAP_INTERVAL) { @@ -38,19 +38,19 @@ export default class TapDragZoomHandler { } if (!this._tapTime) { - this._tap.touchstart(e, points); - } else if (e.targetTouches.length > 0) { + this._tap.touchstart(e, points, mapTouches); + } else if (mapTouches.length > 0) { this._swipePoint = points[0]; - this._swipeTouch = e.targetTouches[0].identifier; + this._swipeTouch = mapTouches[0].identifier; } } - touchmove(e: TouchEvent, points: Array) { + touchmove(e: TouchEvent, points: Array, mapTouches: Array) { if (!this._tapTime) { - this._tap.touchmove(e, points); + this._tap.touchmove(e, points, mapTouches); } else if (this._swipePoint) { - if (e.targetTouches[0].identifier !== this._swipeTouch) { + if (mapTouches[0].identifier !== this._swipeTouch) { return; } @@ -67,14 +67,14 @@ export default class TapDragZoomHandler { } } - touchend(e: TouchEvent) { + touchend(e: TouchEvent, points: Array, mapTouches: Array) { if (!this._tapTime) { - const point = this._tap.touchend(e); + const point = this._tap.touchend(e, points, mapTouches); if (point) { this._tapTime = e.timeStamp; } } else if (this._swipePoint) { - if (e.targetTouches.length === 0) { + if (mapTouches.length === 0) { this.reset(); } } diff --git a/src/ui/handler/tap_recognizer.js b/src/ui/handler/tap_recognizer.js index 40328e19764..01e9dee629e 100644 --- a/src/ui/handler/tap_recognizer.js +++ b/src/ui/handler/tap_recognizer.js @@ -35,9 +35,9 @@ export class SingleTapRecognizer { this.aborted = false; } - touchstart(e: TouchEvent, points: Array) { + touchstart(e: TouchEvent, points: Array, mapTouches: Array) { - if (this.centroid || e.targetTouches.length > this.numTouches) { + if (this.centroid || mapTouches.length > this.numTouches) { this.aborted = true; } if (this.aborted) { @@ -48,16 +48,16 @@ export class SingleTapRecognizer { this.startTime = e.timeStamp; } - if (e.targetTouches.length === this.numTouches) { + if (mapTouches.length === this.numTouches) { this.centroid = getCentroid(points); - this.touches = indexTouches(e.targetTouches, points); + this.touches = indexTouches(mapTouches, points); } } - touchmove(e: TouchEvent, points: Array) { + touchmove(e: TouchEvent, points: Array, mapTouches: Array) { if (this.aborted || !this.centroid) return; - const newTouches = indexTouches(e.targetTouches, points); + const newTouches = indexTouches(mapTouches, points); for (const id in this.touches) { const prevPos = this.touches[id]; const pos = newTouches[id]; @@ -67,12 +67,12 @@ export class SingleTapRecognizer { } } - touchend(e: TouchEvent) { + touchend(e: TouchEvent, points: Array, mapTouches: Array) { if (!this.centroid || e.timeStamp - this.startTime > MAX_TOUCH_TIME) { this.aborted = true; } - if (e.targetTouches.length === 0) { + if (mapTouches.length === 0) { const centroid = !this.aborted && this.centroid; this.reset(); if (centroid) return centroid; @@ -102,16 +102,16 @@ export class TapRecognizer { this.singleTap.reset(); } - touchstart(e: TouchEvent, points: Array) { - this.singleTap.touchstart(e, points); + touchstart(e: TouchEvent, points: Array, mapTouches: Array) { + this.singleTap.touchstart(e, points, mapTouches); } - touchmove(e: TouchEvent, points: Array) { - this.singleTap.touchmove(e, points); + touchmove(e: TouchEvent, points: Array, mapTouches: Array) { + this.singleTap.touchmove(e, points, mapTouches); } - touchend(e: TouchEvent) { - const tap = this.singleTap.touchend(e); + touchend(e: TouchEvent, points: Array, mapTouches: Array) { + const tap = this.singleTap.touchend(e, points, mapTouches); if (tap) { const soonEnough = e.timeStamp - this.lastTime < MAX_TAP_INTERVAL; const closeEnough = !this.lastTap || this.lastTap.dist(tap) < MAX_DIST; diff --git a/src/ui/handler/tap_zoom.js b/src/ui/handler/tap_zoom.js index 0249c1069e9..1aee59d1e88 100644 --- a/src/ui/handler/tap_zoom.js +++ b/src/ui/handler/tap_zoom.js @@ -31,19 +31,19 @@ export default class TapZoomHandler { this._zoomOut.reset(); } - touchstart(e: TouchEvent, points: Array) { - this._zoomIn.touchstart(e, points); - this._zoomOut.touchstart(e, points); + touchstart(e: TouchEvent, points: Array, mapTouches: Array) { + this._zoomIn.touchstart(e, points, mapTouches); + this._zoomOut.touchstart(e, points, mapTouches); } - touchmove(e: TouchEvent, points: Array) { - this._zoomIn.touchmove(e, points); - this._zoomOut.touchmove(e, points); + touchmove(e: TouchEvent, points: Array, mapTouches: Array) { + this._zoomIn.touchmove(e, points, mapTouches); + this._zoomOut.touchmove(e, points, mapTouches); } - touchend(e: TouchEvent) { - const zoomInPoint = this._zoomIn.touchend(e); - const zoomOutPoint = this._zoomOut.touchend(e); + touchend(e: TouchEvent, points: Array, mapTouches: Array) { + const zoomInPoint = this._zoomIn.touchend(e, points, mapTouches); + const zoomOutPoint = this._zoomOut.touchend(e, points, mapTouches); if (zoomInPoint) { this._active = true; diff --git a/src/ui/handler/touch_pan.js b/src/ui/handler/touch_pan.js index a04a7f7f1d6..be63664b6bd 100644 --- a/src/ui/handler/touch_pan.js +++ b/src/ui/handler/touch_pan.js @@ -24,20 +24,20 @@ export default class TouchPanHandler { this._sum = new Point(0, 0); } - touchstart(e: TouchEvent, points: Array) { - return this._calculateTransform(e, points); + touchstart(e: TouchEvent, points: Array, mapTouches: Array) { + return this._calculateTransform(e, points, mapTouches); } - touchmove(e: TouchEvent, points: Array) { + touchmove(e: TouchEvent, points: Array, mapTouches: Array) { if (!this._active) return; e.preventDefault(); - return this._calculateTransform(e, points); + return this._calculateTransform(e, points, mapTouches); } - touchend(e: TouchEvent, points: Array) { - this._calculateTransform(e, points); + touchend(e: TouchEvent, points: Array, mapTouches: Array) { + this._calculateTransform(e, points, mapTouches); - if (this._active && e.targetTouches.length < this._minTouches) { + if (this._active && mapTouches.length < this._minTouches) { this.reset(); } } @@ -46,10 +46,10 @@ export default class TouchPanHandler { this.reset(); } - _calculateTransform(e: TouchEvent, points: Array) { - if (e.targetTouches.length > 0) this._active = true; + _calculateTransform(e: TouchEvent, points: Array, mapTouches: Array) { + if (mapTouches.length > 0) this._active = true; - const touches = indexTouches(e.targetTouches, points); + const touches = indexTouches(mapTouches, points); const touchPointSum = new Point(0, 0); const touchDeltaSum = new Point(0, 0); diff --git a/src/ui/handler/touch_zoom_rotate.js b/src/ui/handler/touch_zoom_rotate.js index d7b7743b5b9..8e7b910c205 100644 --- a/src/ui/handler/touch_zoom_rotate.js +++ b/src/ui/handler/touch_zoom_rotate.js @@ -24,26 +24,28 @@ class TwoTouchHandler { _start(points: [Point, Point]) {} //eslint-disable-line _move(points: [Point, Point], pinchAround: Point, e: TouchEvent) { return {}; } //eslint-disable-line - touchstart(e: TouchEvent, points: Array) { - if (this._firstTwoTouches || e.targetTouches.length < 2) return; + touchstart(e: TouchEvent, points: Array, mapTouches: Array) { + //console.log(e.target, e.targetTouches.length ? e.targetTouches[0].target : null); + //log('touchstart', points, e.target.innerHTML, e.targetTouches.length ? e.targetTouches[0].target.innerHTML: undefined); + if (this._firstTwoTouches || mapTouches.length < 2) return; this._firstTwoTouches = [ - e.targetTouches[0].identifier, - e.targetTouches[1].identifier + mapTouches[0].identifier, + mapTouches[1].identifier ]; // implemented by child classes this._start([points[0], points[1]]); } - touchmove(e: TouchEvent, points: Array) { + touchmove(e: TouchEvent, points: Array, mapTouches: Array) { if (!this._firstTwoTouches) return; e.preventDefault(); const [idA, idB] = this._firstTwoTouches; - const a = getTouchById(e, points, idA); - const b = getTouchById(e, points, idB); + const a = getTouchById(mapTouches, points, idA); + const b = getTouchById(mapTouches, points, idB); if (!a || !b) return; const pinchAround = this._aroundCenter ? null : a.add(b).div(2); @@ -52,12 +54,12 @@ class TwoTouchHandler { } - touchend(e: TouchEvent, points: Array) { + touchend(e: TouchEvent, points: Array, mapTouches: Array) { if (!this._firstTwoTouches) return; const [idA, idB] = this._firstTwoTouches; - const a = getTouchById(e, points, idA); - const b = getTouchById(e, points, idB); + const a = getTouchById(mapTouches, points, idA); + const b = getTouchById(mapTouches, points, idB); if (a && b) return; if (this._active) DOM.suppressClick(); @@ -88,9 +90,9 @@ class TwoTouchHandler { } } -function getTouchById(e: TouchEvent, points: Array, identifier: number) { - for (let i = 0; i < e.targetTouches.length; i++) { - if (e.targetTouches[i].identifier === identifier) return points[i]; +function getTouchById(mapTouches: Array, points: Array, identifier: number) { + for (let i = 0; i < mapTouches.length; i++) { + if (mapTouches[i].identifier === identifier) return points[i]; } } diff --git a/src/ui/handler_manager.js b/src/ui/handler_manager.js index 63160fc020b..e244ef1086a 100644 --- a/src/ui/handler_manager.js +++ b/src/ui/handler_manager.js @@ -49,10 +49,10 @@ export interface Handler { // Handlers can optionally implement these methods. // They are called with dom events whenever those dom evens are received. - +touchstart?: (e: TouchEvent, points: Array) => HandlerResult | void; - +touchmove?: (e: TouchEvent, points: Array) => HandlerResult | void; - +touchend?: (e: TouchEvent, points: Array) => HandlerResult | void; - +touchcancel?: (e: TouchEvent, points: Array) => HandlerResult | void; + +touchstart?: (e: TouchEvent, points: Array, mapTouches: Array) => HandlerResult | void; + +touchmove?: (e: TouchEvent, points: Array, mapTouches: Array) => HandlerResult | void; + +touchend?: (e: TouchEvent, points: Array, mapTouches: Array) => HandlerResult | void; + +touchcancel?: (e: TouchEvent, points: Array, mapTouches: Array) => HandlerResult | void; +mousedown?: (e: MouseEvent, point: Point) => HandlerResult | void; +mousemove?: (e: MouseEvent, point: Point) => HandlerResult | void; +mouseup?: (e: MouseEvent, point: Point) => HandlerResult | void; @@ -277,6 +277,17 @@ class HandlerManager { this.handleEvent(e, `${e.type}Window`); } + _getMapTouches(touches: TouchList) { + const mapTouches = []; + for (const t of touches) { + const target = ((t.target: any): Node); + if (this._el.contains(target)) { + mapTouches.push(t); + } + } + return ((mapTouches: any): TouchList); + } + handleEvent(e: InputEvent | RenderFrameEvent, eventName?: string) { if (e.type === 'blur') { @@ -298,9 +309,8 @@ class HandlerManager { const eventsInProgress = {}; const activeHandlers = {}; - const points = e ? (e.targetTouches ? - DOM.touchPos(this._el, ((e: any): TouchEvent).targetTouches) : - DOM.mousePos(this._el, ((e: any): MouseEvent))) : null; + const mapTouches = e.touches ? this._getMapTouches(((e: any): TouchEvent).touches) : undefined; + const points = mapTouches ? DOM.touchPos(this._el, mapTouches) : DOM.mousePos(this._el, ((e: any): MouseEvent)); for (const {handlerName, handler, allowed} of this._handlers) { if (!handler.isEnabled()) continue; @@ -311,7 +321,7 @@ class HandlerManager { } else { if ((handler: any)[eventName || e.type]) { - data = (handler: any)[eventName || e.type](e, points); + data = (handler: any)[eventName || e.type](e, points, mapTouches); this.mergeHandlerResult(mergedHandlerResult, eventsInProgress, data, handlerName, inputEvent); if (data && data.needsRenderFrame) { this._triggerRenderFrame(); diff --git a/test/unit/ui/handler/dblclick_zoom.test.js b/test/unit/ui/handler/dblclick_zoom.test.js index a3adb4a63f4..737c7b86cf6 100644 --- a/test/unit/ui/handler/dblclick_zoom.test.js +++ b/test/unit/ui/handler/dblclick_zoom.test.js @@ -12,10 +12,10 @@ function createMap(t) { function simulateDoubleTap(map, delay = 100) { const canvas = map.getCanvas(); return new Promise(resolve => { - simulate.touchstart(canvas, {targetTouches: [{clientX: 0, clientY: 0}]}); + simulate.touchstart(canvas, {touches: [{target: canvas, clientX: 0, clientY: 0}]}); simulate.touchend(canvas); setTimeout(() => { - simulate.touchstart(canvas, {targetTouches: [{clientX: 0, clientY: 0}]}); + simulate.touchstart(canvas, {touches: [{target: canvas, clientX: 0, clientY: 0}]}); simulate.touchend(canvas); map._renderTaskQueue.run(); resolve(); @@ -122,7 +122,7 @@ test('DoubleClickZoomHandler zooms on the second touchend event of a double tap' map.on('zoomstart', zoom); const canvas = map.getCanvas(); - const touchOptions = {targetTouches: [{clientX: 0.5, clientY: 0.5}]}; + const touchOptions = {touches: [{target: canvas, clientX: 0.5, clientY: 0.5}]}; simulate.touchstart(canvas, touchOptions); simulate.touchend(canvas); diff --git a/test/unit/ui/handler/drag_pan.test.js b/test/unit/ui/handler/drag_pan.test.js index 6f364ae3e8e..785efaf488e 100644 --- a/test/unit/ui/handler/drag_pan.test.js +++ b/test/unit/ui/handler/drag_pan.test.js @@ -84,6 +84,7 @@ test('DragPanHandler captures mousemove events during a mouse-triggered drag (re test('DragPanHandler fires dragstart, drag, and dragend events at appropriate times in response to a touch-triggered drag', (t) => { const map = createMap(t); + const target = map.getCanvas(); const dragstart = t.spy(); const drag = t.spy(); @@ -93,13 +94,13 @@ test('DragPanHandler fires dragstart, drag, and dragend events at appropriate ti map.on('drag', drag); map.on('dragend', dragend); - simulate.touchstart(map.getCanvas(), {targetTouches: [{clientX: 0, clientY: 0}]}); + simulate.touchstart(map.getCanvas(), {touches: [{target, clientX: 0, clientY: 0}]}); map._renderTaskQueue.run(); t.equal(dragstart.callCount, 0); t.equal(drag.callCount, 0); t.equal(dragend.callCount, 0); - simulate.touchmove(map.getCanvas(), {targetTouches: [{clientX: 10, clientY: 10}]}); + simulate.touchmove(map.getCanvas(), {touches: [{target, clientX: 10, clientY: 10}]}); map._renderTaskQueue.run(); t.equal(dragstart.callCount, 1); t.equal(drag.callCount, 1); @@ -157,14 +158,15 @@ test('DragPanHandler ends a mouse-triggered drag if the window blurs', (t) => { test('DragPanHandler ends a touch-triggered drag if the window blurs', (t) => { const map = createMap(t); + const target = map.getCanvas(); const dragend = t.spy(); map.on('dragend', dragend); - simulate.touchstart(map.getCanvas(), {targetTouches: [{clientX: 0, clientY: 0}]}); + simulate.touchstart(map.getCanvas(), {touches: [{target, clientX: 0, clientY: 0}]}); map._renderTaskQueue.run(); - simulate.touchmove(map.getCanvas(), {targetTouches: [{clientX: 10, clientY: 10}]}); + simulate.touchmove(map.getCanvas(), {touches: [{target, clientX: 10, clientY: 10}]}); map._renderTaskQueue.run(); simulate.blur(window); @@ -428,6 +430,7 @@ test('DragPanHandler does not begin a drag if preventDefault is called on the mo test('DragPanHandler does not begin a drag if preventDefault is called on the touchstart event', (t) => { const map = createMap(t); + const target = map.getCanvas(); map.on('touchstart', e => e.preventDefault()); @@ -439,10 +442,10 @@ test('DragPanHandler does not begin a drag if preventDefault is called on the to map.on('drag', drag); map.on('dragend', dragend); - simulate.touchstart(map.getCanvas(), {targetTouches: [{clientX: 0, clientY: 0}]}); + simulate.touchstart(map.getCanvas(), {touches: [{target, clientX: 0, clientY: 0}]}); map._renderTaskQueue.run(); - simulate.touchmove(map.getCanvas(), {targetTouches: [{clientX: 10, clientY: 10}]}); + simulate.touchmove(map.getCanvas(), {touches: [{target, clientX: 10, clientY: 10}]}); map._renderTaskQueue.run(); simulate.touchend(map.getCanvas()); @@ -458,6 +461,7 @@ test('DragPanHandler does not begin a drag if preventDefault is called on the to test('DragPanHandler does not begin a drag if preventDefault is called on the touchstart event (delegated)', (t) => { const map = createMap(t); + const target = map.getCanvas(); t.stub(map, 'getLayer') .callsFake(() => true); @@ -476,10 +480,10 @@ test('DragPanHandler does not begin a drag if preventDefault is called on the to map.on('drag', drag); map.on('dragend', dragend); - simulate.touchstart(map.getCanvas(), {targetTouches: [{clientX: 0, clientY: 0}]}); + simulate.touchstart(map.getCanvas(), {touches: [{target, clientX: 0, clientY: 0}]}); map._renderTaskQueue.run(); - simulate.touchmove(map.getCanvas(), {targetTouches: [{clientX: 10, clientY: 10}]}); + simulate.touchmove(map.getCanvas(), {touches: [{target, clientX: 10, clientY: 10}]}); map._renderTaskQueue.run(); simulate.touchend(map.getCanvas()); diff --git a/test/unit/ui/handler/map_event.test.js b/test/unit/ui/handler/map_event.test.js index 90eb5b3bb46..d99a496358b 100644 --- a/test/unit/ui/handler/map_event.test.js +++ b/test/unit/ui/handler/map_event.test.js @@ -11,6 +11,7 @@ function createMap(t) { test('MapEvent handler fires touch events with correct values', (t) => { const map = createMap(t); + const target = map.getCanvas(); const touchstart = t.spy(); const touchmove = t.spy(); @@ -20,9 +21,9 @@ test('MapEvent handler fires touch events with correct values', (t) => { map.on('touchmove', touchmove); map.on('touchend', touchend); - const touchesStart = [{identifier: 1, clientX: 0, clientY: 50}]; - const touchesMove = [{identifier: 1, clientX: 0, clientY: 60}]; - const touchesEnd = [{identifier: 1, clientX: 0, clientY: 60}]; + const touchesStart = [{target, identifier: 1, clientX: 0, clientY: 50}]; + const touchesMove = [{target, identifier: 1, clientX: 0, clientY: 60}]; + const touchesEnd = [{target, identifier: 1, clientX: 0, clientY: 60}]; simulate.touchstart(map.getCanvas(), {touches: touchesStart, targetTouches: touchesStart}); t.equal(touchstart.callCount, 1); diff --git a/test/unit/ui/handler/touch_zoom_rotate.test.js b/test/unit/ui/handler/touch_zoom_rotate.test.js index a70ce438c4f..5028aed6c0c 100644 --- a/test/unit/ui/handler/touch_zoom_rotate.test.js +++ b/test/unit/ui/handler/touch_zoom_rotate.test.js @@ -1,6 +1,7 @@ import {test} from '../../../util/test'; import window from '../../../../src/util/window'; import Map from '../../../../src/ui/map'; +import Marker from '../../../../src/ui/marker'; import DOM from '../../../../src/util/dom'; import simulate from '../../../util/simulate_interaction'; @@ -11,6 +12,7 @@ function createMap(t) { test('TouchZoomRotateHandler fires zoomstart, zoom, and zoomend events at appropriate times in response to a pinch-zoom gesture', (t) => { const map = createMap(t); + const target = map.getCanvas(); const zoomstart = t.spy(); const zoom = t.spy(); @@ -22,25 +24,25 @@ test('TouchZoomRotateHandler fires zoomstart, zoom, and zoomend events at approp map.on('zoom', zoom); map.on('zoomend', zoomend); - simulate.touchstart(map.getCanvas(), {targetTouches: [{identifier: 1, clientX: 0, clientY: -50}, {identifier: 2, clientX: 0, clientY: 50}]}); + simulate.touchstart(map.getCanvas(), {touches: [{target, identifier: 1, clientX: 0, clientY: -50}, {target, identifier: 2, clientX: 0, clientY: 50}]}); map._renderTaskQueue.run(); t.equal(zoomstart.callCount, 0); t.equal(zoom.callCount, 0); t.equal(zoomend.callCount, 0); - simulate.touchmove(map.getCanvas(), {targetTouches: [{identifier: 1, clientX: 0, clientY: -100}, {identifier: 2, clientX: 0, clientY: 100}]}); + simulate.touchmove(map.getCanvas(), {touches: [{target, identifier: 1, clientX: 0, clientY: -100}, {target, identifier: 2, clientX: 0, clientY: 100}]}); map._renderTaskQueue.run(); t.equal(zoomstart.callCount, 1); t.equal(zoom.callCount, 1); t.equal(zoomend.callCount, 0); - simulate.touchmove(map.getCanvas(), {targetTouches: [{identifier: 1, clientX: 0, clientY: -60}, {identifier: 2, clientX: 0, clientY: 60}]}); + simulate.touchmove(map.getCanvas(), {touches: [{target, identifier: 1, clientX: 0, clientY: -60}, {target, identifier: 2, clientX: 0, clientY: 60}]}); map._renderTaskQueue.run(); t.equal(zoomstart.callCount, 1); t.equal(zoom.callCount, 2); t.equal(zoomend.callCount, 0); - simulate.touchend(map.getCanvas(), {targetTouches: []}); + simulate.touchend(map.getCanvas(), {touches: []}); map._renderTaskQueue.run(); // incremented because inertia starts a second zoom @@ -55,6 +57,7 @@ test('TouchZoomRotateHandler fires zoomstart, zoom, and zoomend events at approp test('TouchZoomRotateHandler fires rotatestart, rotate, and rotateend events at appropriate times in response to a pinch-rotate gesture', (t) => { const map = createMap(t); + const target = map.getCanvas(); const rotatestart = t.spy(); const rotate = t.spy(); @@ -64,25 +67,25 @@ test('TouchZoomRotateHandler fires rotatestart, rotate, and rotateend events at map.on('rotate', rotate); map.on('rotateend', rotateend); - simulate.touchstart(map.getCanvas(), {targetTouches: [{identifier: 0, clientX: 0, clientY: -50}, {identifier: 1, clientX: 0, clientY: 50}]}); + simulate.touchstart(map.getCanvas(), {touches: [{target, identifier: 0, clientX: 0, clientY: -50}, {target, identifier: 1, clientX: 0, clientY: 50}]}); map._renderTaskQueue.run(); t.equal(rotatestart.callCount, 0); t.equal(rotate.callCount, 0); t.equal(rotateend.callCount, 0); - simulate.touchmove(map.getCanvas(), {targetTouches: [{identifier: 0, clientX: -50, clientY: 0}, {identifier: 1, clientX: 50, clientY: 0}]}); + simulate.touchmove(map.getCanvas(), {touches: [{target, identifier: 0, clientX: -50, clientY: 0}, {target, identifier: 1, clientX: 50, clientY: 0}]}); map._renderTaskQueue.run(); t.equal(rotatestart.callCount, 1); t.equal(rotate.callCount, 1); t.equal(rotateend.callCount, 0); - simulate.touchmove(map.getCanvas(), {targetTouches: [{identifier: 0, clientX: 0, clientY: -50}, {identifier: 1, clientX: 0, clientY: 50}]}); + simulate.touchmove(map.getCanvas(), {touches: [{target, identifier: 0, clientX: 0, clientY: -50}, {target, identifier: 1, clientX: 0, clientY: 50}]}); map._renderTaskQueue.run(); t.equal(rotatestart.callCount, 1); t.equal(rotate.callCount, 2); t.equal(rotateend.callCount, 0); - simulate.touchend(map.getCanvas(), {targetTouches: []}); + simulate.touchend(map.getCanvas(), {touches: []}); map._renderTaskQueue.run(); t.equal(rotatestart.callCount, 1); t.equal(rotate.callCount, 2); @@ -94,19 +97,20 @@ test('TouchZoomRotateHandler fires rotatestart, rotate, and rotateend events at test('TouchZoomRotateHandler does not begin a gesture if preventDefault is called on the touchstart event', (t) => { const map = createMap(t); + const target = map.getCanvas(); map.on('touchstart', e => e.preventDefault()); const move = t.spy(); map.on('move', move); - simulate.touchstart(map.getCanvas(), {targetTouches: [{clientX: 0, clientY: 0}, {clientX: 5, clientY: 0}]}); + simulate.touchstart(map.getCanvas(), {touches: [{target, clientX: 0, clientY: 0}, {target, clientX: 5, clientY: 0}]}); map._renderTaskQueue.run(); - simulate.touchmove(map.getCanvas(), {targetTouches: [{clientX: 0, clientY: 0}, {clientX: 0, clientY: 5}]}); + simulate.touchmove(map.getCanvas(), {touches: [{target, clientX: 0, clientY: 0}, {target, clientX: 0, clientY: 5}]}); map._renderTaskQueue.run(); - simulate.touchend(map.getCanvas(), {targetTouches: []}); + simulate.touchend(map.getCanvas(), {touches: []}); map._renderTaskQueue.run(); t.equal(move.callCount, 0); @@ -117,6 +121,7 @@ test('TouchZoomRotateHandler does not begin a gesture if preventDefault is calle test('TouchZoomRotateHandler starts zoom immediately when rotation disabled', (t) => { const map = createMap(t); + const target = map.getCanvas(); map.touchZoomRotate.disableRotation(); map.handlers._handlersById.tapZoom.disable(); @@ -128,25 +133,25 @@ test('TouchZoomRotateHandler starts zoom immediately when rotation disabled', (t map.on('zoom', zoom); map.on('zoomend', zoomend); - simulate.touchstart(map.getCanvas(), {targetTouches: [{identifier: 0, clientX: 0, clientY: -5}, {identifier: 2, clientX: 0, clientY: 5}]}); + simulate.touchstart(map.getCanvas(), {touches: [{target, identifier: 0, clientX: 0, clientY: -5}, {target, identifier: 2, clientX: 0, clientY: 5}]}); map._renderTaskQueue.run(); t.equal(zoomstart.callCount, 0); t.equal(zoom.callCount, 0); t.equal(zoomend.callCount, 0); - simulate.touchmove(map.getCanvas(), {targetTouches: [{identifier: 0, clientX: 0, clientY: -5}, {identifier: 2, clientX: 0, clientY: 6}]}); + simulate.touchmove(map.getCanvas(), {touches: [{target, identifier: 0, clientX: 0, clientY: -5}, {target, identifier: 2, clientX: 0, clientY: 6}]}); map._renderTaskQueue.run(); t.equal(zoomstart.callCount, 1); t.equal(zoom.callCount, 1); t.equal(zoomend.callCount, 0); - simulate.touchmove(map.getCanvas(), {targetTouches: [{identifier: 0, clientX: 0, clientY: -5}, {identifier: 2, clientX: 0, clientY: 4}]}); + simulate.touchmove(map.getCanvas(), {touches: [{target, identifier: 0, clientX: 0, clientY: -5}, {target, identifier: 2, clientX: 0, clientY: 4}]}); map._renderTaskQueue.run(); t.equal(zoomstart.callCount, 1); t.equal(zoom.callCount, 2); t.equal(zoomend.callCount, 0); - simulate.touchend(map.getCanvas(), {targetTouches: []}); + simulate.touchend(map.getCanvas(), {touches: []}); map._renderTaskQueue.run(); // incremented because inertia starts a second zoom t.equal(zoomstart.callCount, 2); @@ -169,3 +174,113 @@ test('TouchZoomRotateHandler adds css class used for disabling default touch beh t.ok(map.getCanvasContainer().classList.contains(className)); t.end(); }); + +test('TouchZoomRotateHandler zooms when touching two markers on the same map', (t) => { + const map = createMap(t); + + const marker1 = new Marker() + .setLngLat([0, 0]) + .addTo(map); + const marker2 = new Marker() + .setLngLat([0, 0]) + .addTo(map); + const target1 = marker1.getElement(); + const target2 = marker2.getElement(); + + const zoomstart = t.spy(); + const zoom = t.spy(); + const zoomend = t.spy(); + + map.handlers._handlersById.tapZoom.disable(); + map.touchPitch.disable(); + map.on('zoomstart', zoomstart); + map.on('zoom', zoom); + map.on('zoomend', zoomend); + + simulate.touchstart(map.getCanvas(), {touches: [{target: target1, identifier: 1, clientX: 0, clientY: -50}]}); + simulate.touchstart(map.getCanvas(), {touches: [{target: target1, identifier: 1, clientX: 0, clientY: -50}, {target: target2, identifier: 2, clientX: 0, clientY: 50}]}); + map._renderTaskQueue.run(); + t.equal(zoomstart.callCount, 0); + t.equal(zoom.callCount, 0); + t.equal(zoomend.callCount, 0); + + simulate.touchmove(map.getCanvas(), {touches: [{target: target1, identifier: 1, clientX: 0, clientY: -100}, {target: target2, identifier: 2, clientX: 0, clientY: 100}]}); + map._renderTaskQueue.run(); + t.equal(zoomstart.callCount, 1); + t.equal(zoom.callCount, 1); + t.equal(zoomend.callCount, 0); + + simulate.touchmove(map.getCanvas(), {touches: [{target: target1, identifier: 1, clientX: 0, clientY: -60}, {target: target2, identifier: 2, clientX: 0, clientY: 60}]}); + map._renderTaskQueue.run(); + t.equal(zoomstart.callCount, 1); + t.equal(zoom.callCount, 2); + t.equal(zoomend.callCount, 0); + + simulate.touchend(map.getCanvas(), {touches: []}); + map._renderTaskQueue.run(); + + // incremented because inertia starts a second zoom + t.equal(zoomstart.callCount, 2); + map._renderTaskQueue.run(); + t.equal(zoom.callCount, 3); + t.equal(zoomend.callCount, 1); + + map.remove(); + t.end(); +}); + +test('TouchZoomRotateHandler does not zoom when touching an element not on the map', (t) => { + const map = createMap(t); + + const marker1 = new Marker() + .setLngLat([0, 0]) + .addTo(map); + const marker2 = new Marker() + .setLngLat([0, 0]); + + const target1 = marker1.getElement(); // on map + const target2 = marker2.getElement(); // not on map + + const zoomstart = t.spy(); + const zoom = t.spy(); + const zoomend = t.spy(); + + map.handlers._handlersById.tapZoom.disable(); + map.touchPitch.disable(); + map.dragPan.disable(); + map.on('zoomstart', zoomstart); + map.on('zoom', zoom); + map.on('zoomend', zoomend); + + simulate.touchstart(map.getCanvas(), {touches: [{target: target1, identifier: 1, clientX: 0, clientY: -50}]}); + simulate.touchstart(map.getCanvas(), {touches: [{target: target1, identifier: 1, clientX: 0, clientY: -50}, {target: target2, identifier: 2, clientX: 0, clientY: 50}]}); + map._renderTaskQueue.run(); + t.equal(zoomstart.callCount, 0); + t.equal(zoom.callCount, 0); + t.equal(zoomend.callCount, 0); + + simulate.touchmove(map.getCanvas(), {touches: [{target: target1, identifier: 1, clientX: 0, clientY: -100}, {target: target2, identifier: 2, clientX: 0, clientY: 100}]}); + map._renderTaskQueue.run(); + t.equal(zoomstart.callCount, 0); + t.equal(zoom.callCount, 0); + t.equal(zoomend.callCount, 0); + + simulate.touchmove(map.getCanvas(), {touches: [{target: target1, identifier: 1, clientX: 0, clientY: -60}, {target: target2, identifier: 2, clientX: 0, clientY: 60}]}); + map._renderTaskQueue.run(); + t.equal(zoomstart.callCount, 0); + t.equal(zoom.callCount, 0); + t.equal(zoomend.callCount, 0); + + simulate.touchend(map.getCanvas(), {touches: []}); + map._renderTaskQueue.run(); + + // incremented because inertia starts a second zoom + t.equal(zoomstart.callCount, 0); + map._renderTaskQueue.run(); + t.equal(zoom.callCount, 0); + t.equal(zoomend.callCount, 0); + + map.remove(); + t.end(); +}); + diff --git a/test/unit/ui/map.test.js b/test/unit/ui/map.test.js index 6e312951bd7..dedddf15f67 100755 --- a/test/unit/ui/map.test.js +++ b/test/unit/ui/map.test.js @@ -1999,7 +1999,7 @@ test('Map', (t) => { const map = createMap(t, {interactive: true}); map.flyTo({center: [200, 0], duration: 100}); - simulate.touchstart(map.getCanvasContainer(), {targetTouches: [{clientX: 0, clientY: 0}]}); + simulate.touchstart(map.getCanvasContainer(), {touches: [{target: map.getCanvas(), clientX: 0, clientY: 0}]}); t.equal(map.isEasing(), false); map.remove(); From 08d5fb2d066d4fa48830132527693c4abbec3050 Mon Sep 17 00:00:00 2001 From: Karim Naaji Date: Thu, 14 May 2020 15:20:00 -0700 Subject: [PATCH 41/50] Review nits: set an interim paint in style_layer (#9688) * set an interim paint in style_layer * add a simple test for undefined paint * Address review nits * Silence inconsistent flow error Co-authored-by: Nick Mann --- src/source/tile.js | 2 +- src/style/style_layer.js | 4 +++- test/unit/style/style_layer.test.js | 7 +++++++ 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/src/source/tile.js b/src/source/tile.js index dcb8120bf2b..25164718dee 100644 --- a/src/source/tile.js +++ b/src/source/tile.js @@ -405,7 +405,7 @@ class Tile { bucket.update(sourceLayerStates, sourceLayer, this.imageAtlas && this.imageAtlas.patternPositions || {}); const layer = painter && painter.style && painter.style.getLayer(id); - if (layer && layer.paint) { + if (layer) { this.queryPadding = Math.max(this.queryPadding, layer.queryRadius(bucket)); } } diff --git a/src/style/style_layer.js b/src/style/style_layer.js index ce06dcb9ace..c2a7d6cb0cb 100644 --- a/src/style/style_layer.js +++ b/src/style/style_layer.js @@ -10,7 +10,7 @@ import { emitValidationErrors } from './validate_style'; import {Evented} from '../util/evented'; -import {Layout, Transitionable, Transitioning, Properties, PossiblyEvaluatedPropertyValue} from './properties'; +import {Layout, Transitionable, Transitioning, Properties, PossiblyEvaluated, PossiblyEvaluatedPropertyValue} from './properties'; import {supportsPropertyExpression} from '../style-spec/util/properties'; import type {FeatureState} from '../style-spec/expression'; @@ -100,6 +100,8 @@ class StyleLayer extends Evented { } this._transitioningPaint = this._transitionablePaint.untransitioned(); + //$FlowFixMe + this.paint = new PossiblyEvaluated(properties.paint); } } diff --git a/test/unit/style/style_layer.test.js b/test/unit/style/style_layer.test.js index 9152dc82f34..8d682f164ad 100644 --- a/test/unit/style/style_layer.test.js +++ b/test/unit/style/style_layer.test.js @@ -403,5 +403,12 @@ test('StyleLayer#serialize', (t) => { t.end(); }); + t.test('layer.paint is never undefined', (t) => { + const layer = createStyleLayer({type: 'fill'}); + // paint is never undefined + t.ok(layer.paint); + t.end(); + }); + t.end(); }); From 0773d67aed7bf51460a2140ccf6fd23c7a9c0542 Mon Sep 17 00:00:00 2001 From: Karim Naaji Date: Fri, 15 May 2020 18:04:17 -0700 Subject: [PATCH 42/50] [master] Changelog v1.10.1 (#9695) (#9697) * [release-danube] Changelog v1.10.1 (#9695) * v1.10.1 Co-authored-by: Arindam Bose * Suggestions from https://github.com/mapbox/mapbox-gl-js/pull/9695 Co-authored-by: Arindam Bose --- CHANGELOG.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1adef5386dd..20bd3dc4f5d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,13 @@ +## 1.10.1 + +### 🐞 Bug fixes +* Fix markers interrupting touch gestures ([#9675](https://github.com/mapbox/mapbox-gl-js/issues/9675), fixed by [#9683](https://github.com/mapbox/mapbox-gl-js/pull/9683)) +* Fix bug where `map.isMoving()` returned true while map was not moving ([#9647](https://github.com/mapbox/mapbox-gl-js/issues/9647), fixed by [#9679](https://github.com/mapbox/mapbox-gl-js/pull/9679)) +* Fix regression that prevented `touchmove` events from firing during gestures ([#9676](https://github.com/mapbox/mapbox-gl-js/issues/9676), fixed by [#9685](https://github.com/mapbox/mapbox-gl-js/pull/9685)) +* Fix `image` expression evaluation which was broken under certain conditions ([#9630](https://github.com/mapbox/mapbox-gl-js/issues/9630), fixed by [#9685](https://github.com/mapbox/mapbox-gl-js/pull/9668)) +* Fix nested `within` expressions in filters not evaluating correctly ([#9605](https://github.com/mapbox/mapbox-gl-js/issues/9605), fixed by [#9611](https://github.com/mapbox/mapbox-gl-js/pull/9611)) +* Fix potential `undefined` paint variable in `StyleLayer` ([#9688](https://github.com/mapbox/mapbox-gl-js/pull/9688)) (h/t [mannnick24](https://github.com/mannnick24)) + ## 1.10.0 ### ✨ Features From 8fffb972f63390f9f75be31f3f040c06ea1a5e08 Mon Sep 17 00:00:00 2001 From: Katy DeCorah Date: Wed, 20 May 2020 12:12:41 -0400 Subject: [PATCH 43/50] fix `zoom` example (#9705) --- src/ui/events.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/ui/events.js b/src/ui/events.js index 66abb6d81fd..c48b42f52ac 100644 --- a/src/ui/events.js +++ b/src/ui/events.js @@ -796,6 +796,7 @@ export type MapEvent = * @memberof Map * @instance * @property {MapMouseEvent | MapTouchEvent} data + * @example * // Initialize the map * var map = new mapboxgl.Map({ // map options }); * // Set an event listener that fires From 9201dee6372bd9c345a10d1ae0eb7efaf15d7e69 Mon Sep 17 00:00:00 2001 From: Eric Fischer Date: Wed, 20 May 2020 09:36:42 -0700 Subject: [PATCH 44/50] Add tests for undocumented but supported ["literal"] expression forms (#9696) --- test/integration/README.md | 6 ++++++ .../literal/literal-false/test.json | 14 ++++++++++++++ .../literal/literal-null/test.json | 14 ++++++++++++++ .../literal/literal-number/test.json | 14 ++++++++++++++ .../literal/literal-string/test.json | 14 ++++++++++++++ .../literal/literal-true/test.json | 14 ++++++++++++++ 6 files changed, 76 insertions(+) create mode 100644 test/integration/expression-tests/literal/literal-false/test.json create mode 100644 test/integration/expression-tests/literal/literal-null/test.json create mode 100644 test/integration/expression-tests/literal/literal-number/test.json create mode 100644 test/integration/expression-tests/literal/literal-string/test.json create mode 100644 test/integration/expression-tests/literal/literal-true/test.json diff --git a/test/integration/README.md b/test/integration/README.md index 77c43e037bf..79e570ef274 100644 --- a/test/integration/README.md +++ b/test/integration/README.md @@ -32,6 +32,12 @@ or yarn run test-query-node ``` +To run only the expression tests: + +``` +yarn run test-expressions +``` + ### Running specific tests To run a subset of tests or an individual test, you can pass a specific subdirectory to the `test-render` script. For example, to run all the tests for a given property, e.g. `circle-radius`: diff --git a/test/integration/expression-tests/literal/literal-false/test.json b/test/integration/expression-tests/literal/literal-false/test.json new file mode 100644 index 00000000000..9a6850c7744 --- /dev/null +++ b/test/integration/expression-tests/literal/literal-false/test.json @@ -0,0 +1,14 @@ +{ + "expression": ["literal", false], + "inputs": [[{}, {}]], + "expected": { + "compiled": { + "result": "success", + "isFeatureConstant": true, + "isZoomConstant": true, + "type": "boolean" + }, + "outputs": [false], + "serialized": false + } +} diff --git a/test/integration/expression-tests/literal/literal-null/test.json b/test/integration/expression-tests/literal/literal-null/test.json new file mode 100644 index 00000000000..2d4ebfa598d --- /dev/null +++ b/test/integration/expression-tests/literal/literal-null/test.json @@ -0,0 +1,14 @@ +{ + "expression": ["literal", null], + "inputs": [[{}, {}]], + "expected": { + "compiled": { + "result": "success", + "isFeatureConstant": true, + "isZoomConstant": true, + "type": "null" + }, + "outputs": [null], + "serialized": null + } +} diff --git a/test/integration/expression-tests/literal/literal-number/test.json b/test/integration/expression-tests/literal/literal-number/test.json new file mode 100644 index 00000000000..01c129ec58a --- /dev/null +++ b/test/integration/expression-tests/literal/literal-number/test.json @@ -0,0 +1,14 @@ +{ + "expression": ["literal", 7.5], + "inputs": [[{}, {}]], + "expected": { + "compiled": { + "result": "success", + "isFeatureConstant": true, + "isZoomConstant": true, + "type": "number" + }, + "outputs": [7.5], + "serialized": 7.5 + } +} diff --git a/test/integration/expression-tests/literal/literal-string/test.json b/test/integration/expression-tests/literal/literal-string/test.json new file mode 100644 index 00000000000..ac6914326cb --- /dev/null +++ b/test/integration/expression-tests/literal/literal-string/test.json @@ -0,0 +1,14 @@ +{ + "expression": ["literal", "hello"], + "inputs": [[{}, {}]], + "expected": { + "compiled": { + "result": "success", + "isFeatureConstant": true, + "isZoomConstant": true, + "type": "string" + }, + "outputs": ["hello"], + "serialized": "hello" + } +} diff --git a/test/integration/expression-tests/literal/literal-true/test.json b/test/integration/expression-tests/literal/literal-true/test.json new file mode 100644 index 00000000000..0e6d30e35ff --- /dev/null +++ b/test/integration/expression-tests/literal/literal-true/test.json @@ -0,0 +1,14 @@ +{ + "expression": ["literal", true], + "inputs": [[{}, {}]], + "expected": { + "compiled": { + "result": "success", + "isFeatureConstant": true, + "isZoomConstant": true, + "type": "boolean" + }, + "outputs": [true], + "serialized": true + } +} From fd7901ed79916f20e7ddb8a6bb51182868299c50 Mon Sep 17 00:00:00 2001 From: Karim Naaji Date: Wed, 20 May 2020 12:01:52 -0700 Subject: [PATCH 45/50] [release-danube] style spec (#9706) (#9710) * Style spec v13.15.0 Co-authored-by: Chloe Krawczyk Co-authored-by: Chloe Krawczyk --- src/style-spec/CHANGELOG.md | 5 +++++ src/style-spec/package.json | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/style-spec/CHANGELOG.md b/src/style-spec/CHANGELOG.md index 90d9917e73c..f18b6812011 100644 --- a/src/style-spec/CHANGELOG.md +++ b/src/style-spec/CHANGELOG.md @@ -1,3 +1,8 @@ +## 13.15.0 + +### ✨ Features and improvements +* Add `distance` expression to `style-spec`. This expression returns the shortest distance between a feature and an input geometry ([#9655](https://github.com/mapbox/mapbox-gl-js/pull/9655)) + ## 13.14.0 ### ✨ Features and improvements diff --git a/src/style-spec/package.json b/src/style-spec/package.json index 0ee3833018a..905ccbee624 100644 --- a/src/style-spec/package.json +++ b/src/style-spec/package.json @@ -1,7 +1,7 @@ { "name": "@mapbox/mapbox-gl-style-spec", "description": "a specification for mapbox gl styles", - "version": "13.15.0-dev", + "version": "13.16.0-dev", "author": "Mapbox", "keywords": [ "mapbox", From aaaaa4eb94944ff5a99d5936deccee2d1ad5bc8d Mon Sep 17 00:00:00 2001 From: Katy DeCorah Date: Thu, 21 May 2020 07:58:35 -0400 Subject: [PATCH 46/50] [docs] Replace relative links with JSDoc links (#9690) * fixes map.event:error link fixes https://github.com/mapbox/mapbox-gl-js-docs/issues/20 * replace markdown links with jsdoc link * fitbounds -> fitBounds --- src/ui/camera.js | 2 +- src/ui/control/geolocate_control.js | 2 +- src/ui/events.js | 20 ++++++++++---------- src/ui/map.js | 10 +++++----- 4 files changed, 17 insertions(+), 17 deletions(-) diff --git a/src/ui/camera.js b/src/ui/camera.js index 40bfad0dc9e..a0f89ef56bf 100644 --- a/src/ui/camera.js +++ b/src/ui/camera.js @@ -645,7 +645,7 @@ class Camera extends Evented { * map.fitScreenCoordinates(p0, p1, map.getBearing(), { * padding: {top: 10, bottom:25, left: 15, right: 5} * }); - * @see [Used by BoxZoomHandler](https://www.mapbox.com/mapbox-gl-js/api/#boxzoomhandler) + * @see Used by {@link BoxZoomHandler} */ fitScreenCoordinates(p0: PointLike, p1: PointLike, bearing: number, options?: AnimationOptions & CameraOptions, eventData?: Object) { return this._fitInternal( diff --git a/src/ui/control/geolocate_control.js b/src/ui/control/geolocate_control.js index 87a39f3848f..2cd3866db03 100644 --- a/src/ui/control/geolocate_control.js +++ b/src/ui/control/geolocate_control.js @@ -80,7 +80,7 @@ let noTimeout = false; * @implements {IControl} * @param {Object} [options] * @param {Object} [options.positionOptions={enableHighAccuracy: false, timeout: 6000}] A Geolocation API [PositionOptions](https://developer.mozilla.org/en-US/docs/Web/API/PositionOptions) object. - * @param {Object} [options.fitBoundsOptions={maxZoom: 15}] A [`fitBounds`](#map#fitbounds) options object to use when the map is panned and zoomed to the user's location. The default is to use a `maxZoom` of 15 to limit how far the map will zoom in for very accurate locations. + * @param {Object} [options.fitBoundsOptions={maxZoom: 15}] A {@link Map#fitBounds} options object to use when the map is panned and zoomed to the user's location. The default is to use a `maxZoom` of 15 to limit how far the map will zoom in for very accurate locations. * @param {Object} [options.trackUserLocation=false] If `true` the Geolocate Control becomes a toggle button and when active the map will receive updates to the user's location as it changes. * @param {Object} [options.showAccuracyCircle=true] By default, if showUserLocation is `true`, a transparent circle will be drawn around the user location indicating the accuracy (95% confidence level) of the user's location. Set to `false` to disable. Always disabled when showUserLocation is `false`. * @param {Object} [options.showUserLocation=true] By default a dot will be shown on the map at the user's location. Set to `false` to disable. diff --git a/src/ui/events.js b/src/ui/events.js index c48b42f52ac..108ae88d553 100644 --- a/src/ui/events.js +++ b/src/ui/events.js @@ -23,16 +23,16 @@ import type LngLat from '../geo/lng_lat'; */ export class MapMouseEvent extends Event { /** - * The event type (one of [`mousedown`](#map.event:mousedown), - * [`mouseup`](#map.event:mouseup), - * [`click`](#map.event:click), - * [`dblclick`](#map.event:dblclick), - * [`mousemove`](#map.event:mousemove), - * [`mouseover`](#map.event:mouseover), - * [`mouseenter`](#map.event:mouseenter), - * [`mouseleave`](#map.event:mouseleave), - * [`mouseout`](#map.event:mouseout), - * [`contextmenu`](#map.event:contextmenu)). + * The event type (one of {@link Map.event:mousedown}, + * {@link Map.event:mouseup}, + * {@link Map.event:click}, + * {@link Map.event:dblclick}, + * {@link Map.event:mousemove}, + * {@link Map.event:mouseover}, + * {@link Map.event:mouseenter}, + * {@link Map.event:mouseleave}, + * {@link Map.event:mouseout}, + * {@link Map.event:contextmenu}). */ type: 'mousedown' | 'mouseup' diff --git a/src/ui/map.js b/src/ui/map.js index 8d726c49e2a..e3192d562ec 100755 --- a/src/ui/map.js +++ b/src/ui/map.js @@ -226,7 +226,7 @@ const defaultOptions = { * @param {number} [options.bearing=0] The initial bearing (rotation) of the map, measured in degrees counter-clockwise from north. If `bearing` is not specified in the constructor options, Mapbox GL JS will look for it in the map's style object. If it is not specified in the style, either, it will default to `0`. * @param {number} [options.pitch=0] The initial pitch (tilt) of the map, measured in degrees away from the plane of the screen (0-60). If `pitch` is not specified in the constructor options, Mapbox GL JS will look for it in the map's style object. If it is not specified in the style, either, it will default to `0`. * @param {LngLatBoundsLike} [options.bounds] The initial bounds of the map. If `bounds` is specified, it overrides `center` and `zoom` constructor options. - * @param {Object} [options.fitBoundsOptions] A [`fitBounds`](#map#fitbounds) options object to use _only_ when fitting the initial `bounds` provided above. + * @param {Object} [options.fitBoundsOptions] A {@link Map#fitBounds} options object to use _only_ when fitting the initial `bounds` provided above. * @param {boolean} [options.renderWorldCopies=true] If `true`, multiple copies of the world will be rendered side by side beyond -180 and 180 degrees longitude. If set to `false`: * - When the map is zoomed out far enough that a single representation of the world does not fill the map's entire * container, there will be blank space beyond 180 and -180 degrees longitude. @@ -1560,7 +1560,7 @@ class Map extends Camera { * [`background-pattern`](https://docs.mapbox.com/mapbox-gl-js/style-spec/#paint-background-background-pattern), * [`fill-pattern`](https://docs.mapbox.com/mapbox-gl-js/style-spec/#paint-fill-fill-pattern), * or [`line-pattern`](https://docs.mapbox.com/mapbox-gl-js/style-spec/#paint-line-line-pattern). - * A {@link Map#error} event will be fired if there is not enough space in the sprite to add this image. + * A {@link Map.event:error} event will be fired if there is not enough space in the sprite to add this image. * * @param id The ID of the image. * @param image The image as an `HTMLImageElement`, `ImageData`, `ImageBitmap` or object with `width`, `height`, and `data` @@ -1759,14 +1759,14 @@ class Map extends Camera { * A layer defines how data from a specified source will be styled. Read more about layer types * and available paint and layout properties in the [Mapbox Style Specification](https://docs.mapbox.com/mapbox-gl-js/style-spec/#layers). * - * @param {Object | CustomLayerInterface} layer The layer to add, conforming to either the Mapbox Style Specification's [layer definition](https://docs.mapbox.com/mapbox-gl-js/style-spec/#layers) or, less commonly, the [`CustomLayerInterface`](https://docs.mapbox.com/mapbox-gl-js/api/#customlayerinterface) specification. + * @param {Object | CustomLayerInterface} layer The layer to add, conforming to either the Mapbox Style Specification's [layer definition](https://docs.mapbox.com/mapbox-gl-js/style-spec/#layers) or, less commonly, the {@link CustomLayerInterface} specification. * The Mapbox Style Specification's layer definition is appropriate for most layers. * * @param {string} layer.id A unique idenfier that you define. * @param {string} layer.type The type of layer (for example `fill` or `symbol`). * A list of layer types is available in the [Mapbox Style Specification](https://docs.mapbox.com/mapbox-gl-js/style-spec/layers/#type). * - * (This can also be `custom`. For more information, see [`CustomLayerInterface`](https://docs.mapbox.com/mapbox-gl-js/api/#customlayerinterface).) + * (This can also be `custom`. For more information, see {@link CustomLayerInterface}.) * @param {string | Object} [layer.source] The data source for the layer. * Reference a source that has _already been defined_ using the source's unique id. * Reference a _new source_ using a source object (as defined in the [Mapbox Style Specification](https://docs.mapbox.com/mapbox-gl-js/style-spec/sources/)) directly. @@ -1796,7 +1796,7 @@ class Map extends Camera { * If no minzoom is provided, the layer will be visible at all zoom levels for which there are tiles available. * @param {Object} [layer.metadata] (optional) Arbitrary properties useful to track with the layer, but do not influence rendering. * @param {string} [layer.renderingMode] This is only applicable for layers with the type `custom`. - * See [`CustomLayerInterface`](https://docs.mapbox.com/mapbox-gl-js/api/#customlayerinterface) for more information. + * See {@link CustomLayerInterface} for more information. * @param {string} [beforeId] The ID of an existing layer to insert the new layer before. * If this argument is not specified, the layer will be appended to the end of the layers array. * From 9e3e4bbfabfdf86ba01150c3e96688e37c69be14 Mon Sep 17 00:00:00 2001 From: Sanaz Golbabaei <32684731+sgolbabaei@users.noreply.github.com> Date: Thu, 28 May 2020 12:46:22 -0700 Subject: [PATCH 47/50] Removing Array.from (#9736) (#9737) --- src/render/program.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/render/program.js b/src/render/program.js index 6345516f619..7e273c0a339 100644 --- a/src/render/program.js +++ b/src/render/program.js @@ -106,9 +106,8 @@ class Program { gl.deleteShader(vertexShader); gl.deleteShader(fragmentShader); - const uniformsArray = Array.from(allUniformsInfo); - for (let it = 0; it < uniformsArray.length; it++) { - const uniform = uniformsArray[it]; + for (let it = 0; it < allUniformsInfo.length; it++) { + const uniform = allUniformsInfo[it]; if (uniform && !uniformLocations[uniform]) { const uniformLocation = gl.getUniformLocation(this.program, uniform); if (uniformLocation) { From 01faae15e35706e9233f6dafe68663aa3f2733d6 Mon Sep 17 00:00:00 2001 From: Sanaz Golbabaei <32684731+sgolbabaei@users.noreply.github.com> Date: Thu, 28 May 2020 14:26:40 -0700 Subject: [PATCH 48/50] Release Erie Changelog (#9730) * first draft of the changelog * PR comments --- CHANGELOG.md | 10 ++++++++++ package.json | 2 +- src/style-spec/package.json | 2 +- 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 20bd3dc4f5d..778b0764aad 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,13 @@ +## 1.11.0-beta.1 + +### ✨ Features and improvments +* Add an option to scale the default `Marker` icon.([#9414](https://github.com/mapbox/mapbox-gl-js/pull/9414)) (h/t [adrianababakanian](https://github.com/adrianababakanian)) +* Improving the shader compilation speed by manually getting the run-time attributes and uniforms.([#9497](https://github.com/mapbox/mapbox-gl-js/pull/9497)) + +### 🐞 Bug fixes +* Fix a bug where map got stuck in a DragRotate interaction if it's mouseup occurred outside of the browser window or iframe.([#9512](https://github.com/mapbox/mapbox-gl-js/pull/9512)) +* Fix potential visual regression for `*-pattern` properties on AMD graphics card vendor.([#9681](https://github.com/mapbox/mapbox-gl-js/pull/9681)) + ## 1.10.1 ### 🐞 Bug fixes diff --git a/package.json b/package.json index c0530b568b7..2e695f588f6 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "mapbox-gl", "description": "A WebGL interactive maps library", - "version": "1.11.0-dev", + "version": "1.11.0-beta.1", "main": "dist/mapbox-gl.js", "style": "dist/mapbox-gl.css", "license": "SEE LICENSE IN LICENSE.txt", diff --git a/src/style-spec/package.json b/src/style-spec/package.json index 905ccbee624..0b9a6e015a0 100644 --- a/src/style-spec/package.json +++ b/src/style-spec/package.json @@ -1,7 +1,7 @@ { "name": "@mapbox/mapbox-gl-style-spec", "description": "a specification for mapbox gl styles", - "version": "13.16.0-dev", + "version": "13.16.0-beta.1", "author": "Mapbox", "keywords": [ "mapbox", From 8940417f369028d6075b662cf6b98344f747f606 Mon Sep 17 00:00:00 2001 From: Sanaz Golbabaei <32684731+sgolbabaei@users.noreply.github.com> Date: Wed, 10 Jun 2020 11:02:06 -0700 Subject: [PATCH 49/50] cherry picks from Master to Release-Erie (#9772) * Cherry pick (https://github.com/mapbox/mapbox-gl-js/pull/9757) to release-erie * Cherry pick (https://github.com/mapbox/mapbox-gl-js/pull/9753) to release-erie * Cherry pick (https://github.com/mapbox/mapbox-gl-js/pull/9749) to release-erie * Cherry pick (https://github.com/mapbox/mapbox-gl-js/pull/9748) to release-erie --- package.json | 2 +- rollup/bundle_prelude.js | 4 +++- src/data/load_geometry.js | 24 +++++++++++------------- src/source/geojson_source.js | 1 + src/style-spec/reference/v8.json | 4 ++++ src/ui/handler_manager.js | 14 ++++++++------ src/util/browser.js | 2 +- src/util/browser/window.js | 3 ++- src/util/dom.js | 2 +- test/build/min.test.js | 5 +++++ test/unit/source/geojson_source.test.js | 2 ++ yarn.lock | 8 ++++---- 12 files changed, 43 insertions(+), 28 deletions(-) diff --git a/package.json b/package.json index 2e695f588f6..fd2fc4517b2 100644 --- a/package.json +++ b/package.json @@ -33,7 +33,7 @@ "potpack": "^1.0.1", "quickselect": "^2.0.0", "rw": "^1.3.3", - "supercluster": "^7.0.0", + "supercluster": "^7.1.0", "tinyqueue": "^2.0.3", "vt-pbf": "^3.1.1" }, diff --git a/rollup/bundle_prelude.js b/rollup/bundle_prelude.js index 50fe63085e3..4787f64f4d0 100644 --- a/rollup/bundle_prelude.js +++ b/rollup/bundle_prelude.js @@ -14,6 +14,8 @@ if (!shared) { var sharedChunk = {}; shared(sharedChunk); mapboxgl = chunk(sharedChunk); - mapboxgl.workerUrl = window.URL.createObjectURL(new Blob([workerBundleString], { type: 'text/javascript' })); + if (typeof window !== 'undefined') { + mapboxgl.workerUrl = window.URL.createObjectURL(new Blob([workerBundleString], { type: 'text/javascript' })); + } } } diff --git a/src/data/load_geometry.js b/src/data/load_geometry.js index 5a094fa1f81..c8885c57bda 100644 --- a/src/data/load_geometry.js +++ b/src/data/load_geometry.js @@ -10,14 +10,9 @@ import type Point from '@mapbox/point-geometry'; // While visible coordinates are within [0, EXTENT], tiles may theoretically // contain cordinates within [-Infinity, Infinity]. Our range is limited by the // number of bits used to represent the coordinate. -function createBounds(bits) { - return { - min: -1 * Math.pow(2, bits - 1), - max: Math.pow(2, bits - 1) - 1 - }; -} - -const bounds = createBounds(15); +const BITS = 15; +const MAX = Math.pow(2, BITS - 1) - 1; +const MIN = -MAX - 1; /** * Loads a geometry from a VectorTileFeature and scales it to the common extent @@ -34,13 +29,16 @@ export default function loadGeometry(feature: VectorTileFeature): Array bounds.max || point.y < bounds.min || point.y > bounds.max) { + if (x < point.x || x > point.x + 1 || y < point.y || y > point.y + 1) { + // warn when exceeding allowed extent except for the 1-px-off case + // https://github.com/mapbox/mapbox-gl-js/issues/8992 warnOnce('Geometry exceeds allowed extent, reduce your vector tile buffer size'); - point.x = clamp(point.x, bounds.min, bounds.max); - point.y = clamp(point.y, bounds.min, bounds.max); } } } diff --git a/src/source/geojson_source.js b/src/source/geojson_source.js index 67cfab6f8a8..6bfc40b0cef 100644 --- a/src/source/geojson_source.js +++ b/src/source/geojson_source.js @@ -138,6 +138,7 @@ class GeoJSONSource extends Evented implements Source { maxZoom: options.clusterMaxZoom !== undefined ? Math.min(options.clusterMaxZoom, this.maxzoom - 1) : (this.maxzoom - 1), + minPoints: Math.max(2, options.clusterMinPoints || 2), extent: EXTENT, radius: (options.clusterRadius || 50) * scale, log: false, diff --git a/src/style-spec/reference/v8.json b/src/style-spec/reference/v8.json index dc09427c641..e713ee924e5 100644 --- a/src/style-spec/reference/v8.json +++ b/src/style-spec/reference/v8.json @@ -377,6 +377,10 @@ "type": "number", "doc": "Max zoom on which to cluster points if clustering is enabled. Defaults to one zoom less than maxzoom (so that last zoom features are not clustered)." }, + "clusterMinPoints": { + "type": "number", + "doc": "Minimum number of points necessary to form a cluster if clustering is enabled. Defaults to `2`." + }, "clusterProperties": { "type": "*", "doc": "An object defining custom properties on the generated clusters if clustering is enabled, aggregating values from clustered points. Has the form `{\"property_name\": [operator, map_expression]}`. `operator` is any expression function that accepts at least 2 operands (e.g. `\"+\"` or `\"max\"`) — it accumulates the property value from clusters/points the cluster contains; `map_expression` produces the value of a single point.\n\nExample: `{\"sum\": [\"+\", [\"get\", \"scalerank\"]]}`.\n\nFor more advanced use cases, in place of `operator`, you can use a custom reduce expression that references a special `[\"accumulated\"]` value, e.g.:\n`{\"sum\": [[\"+\", [\"accumulated\"], [\"get\", \"sum\"]], [\"get\", \"scalerank\"]]}`" diff --git a/src/ui/handler_manager.js b/src/ui/handler_manager.js index e244ef1086a..6ebba919b63 100644 --- a/src/ui/handler_manager.js +++ b/src/ui/handler_manager.js @@ -128,12 +128,14 @@ class HandlerManager { const el = this._el; this._listeners = [ - // Bind touchstart and touchmove with passive: false because, even though - // they only fire a map events and therefore could theoretically be - // passive, binding with passive: true causes iOS not to respect - // e.preventDefault() in _other_ handlers, even if they are non-passive - // (see https://bugs.webkit.org/show_bug.cgi?id=184251) - [el, 'touchstart', {passive: false}], + // This needs to be `passive: true` so that a double tap fires two + // pairs of touchstart/end events in iOS Safari 13. If this is set to + // `passive: false` then the second pair of events is only fired if + // preventDefault() is called on the first touchstart. Calling preventDefault() + // undesirably prevents click events. + [el, 'touchstart', {passive: true}], + // This needs to be `passive: false` so that scrolls and pinches can be + // prevented in browsers that don't support `touch-actions: none`, for example iOS Safari 12. [el, 'touchmove', {passive: false}], [el, 'touchend', undefined], [el, 'touchcancel', undefined], diff --git a/src/util/browser.js b/src/util/browser.js index 710841b0d13..808a6fcc5c4 100755 --- a/src/util/browser.js +++ b/src/util/browser.js @@ -54,7 +54,7 @@ const exported = { return linkEl.href; }, - hardwareConcurrency: window.navigator.hardwareConcurrency || 4, + hardwareConcurrency: window.navigator && window.navigator.hardwareConcurrency || 4, get devicePixelRatio() { return window.devicePixelRatio; }, get prefersReducedMotion(): boolean { diff --git a/src/util/browser/window.js b/src/util/browser/window.js index 7a525659dbb..d2ceea5613a 100644 --- a/src/util/browser/window.js +++ b/src/util/browser/window.js @@ -2,4 +2,5 @@ /* eslint-env browser */ import type {Window} from '../../types/window'; -export default (self: Window); +// shim window for the case of requiring the browser bundle in Node +export default typeof self !== 'undefined' ? (self: Window) : (({}: any): Window); diff --git a/src/util/dom.js b/src/util/dom.js index fd5d3e91498..34e2af64f8b 100644 --- a/src/util/dom.js +++ b/src/util/dom.js @@ -20,7 +20,7 @@ DOM.createNS = function (namespaceURI: string, tagName: string) { return el; }; -const docStyle = window.document.documentElement.style; +const docStyle = window.document && window.document.documentElement.style; function testProp(props) { if (!docStyle) return props[0]; diff --git a/test/build/min.test.js b/test/build/min.test.js index 5cdcda50406..649a893274c 100644 --- a/test/build/min.test.js +++ b/test/build/min.test.js @@ -36,6 +36,11 @@ test('can be browserified', (t) => { }); }); +test('evaluates without errors', (t) => { + t.doesNotThrow(() => require(path.join(__dirname, '../../dist/mapbox-gl.js'))); + t.end(); +}); + test('distributed in plain ES5 code', (t) => { const linter = new Linter(); const messages = linter.verify(minBundle, { diff --git a/test/unit/source/geojson_source.test.js b/test/unit/source/geojson_source.test.js index ee672172ae5..ba8328460a3 100644 --- a/test/unit/source/geojson_source.test.js +++ b/test/unit/source/geojson_source.test.js @@ -176,6 +176,7 @@ test('GeoJSONSource#update', (t) => { t.equal(message, 'geojson.loadData'); t.deepEqual(params.superclusterOptions, { maxZoom: 12, + minPoints: 3, extent: 8192, radius: 1600, log: false, @@ -190,6 +191,7 @@ test('GeoJSONSource#update', (t) => { cluster: true, clusterMaxZoom: 12, clusterRadius: 100, + clusterMinPoints: 3, generateId: true }, mockDispatcher).load(); }); diff --git a/yarn.lock b/yarn.lock index 9d306d9edeb..e15ae38e4c4 100644 --- a/yarn.lock +++ b/yarn.lock @@ -10299,10 +10299,10 @@ sugarss@^2.0.0: dependencies: postcss "^7.0.2" -supercluster@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/supercluster/-/supercluster-7.0.0.tgz#75d474fafb0a055db552ed7bd7bbda583f6ab321" - integrity sha512-8VuHI8ynylYQj7Qf6PBMWy1PdgsnBiIxujOgc9Z83QvJ8ualIYWNx2iMKyKeC4DZI5ntD9tz/CIwwZvIelixsA== +supercluster@^7.1.0: + version "7.1.0" + resolved "https://registry.yarnpkg.com/supercluster/-/supercluster-7.1.0.tgz#f0a457426ec0ab95d69c5f03b51e049774b94479" + integrity sha512-LDasImUAFMhTqhK+cUXfy9C2KTUqJ3gucLjmNLNFmKWOnDUBxLFLH9oKuXOTCLveecmxh8fbk8kgh6Q0gsfe2w== dependencies: kdbush "^3.0.0" From e5e0a9c084885787a7095e70610999df1fe78d8f Mon Sep 17 00:00:00 2001 From: Sanaz Golbabaei <32684731+sgolbabaei@users.noreply.github.com> Date: Wed, 10 Jun 2020 14:39:58 -0700 Subject: [PATCH 50/50] Updaring changelong.md and package.json (#9778) * Updaring changelong.md and package.json * fixing typo * cleanup * Update CHANGELOG.md Co-authored-by: Arindam Bose * Update CHANGELOG.md Co-authored-by: Arindam Bose Co-authored-by: Arindam Bose --- CHANGELOG.md | 8 ++++++-- package.json | 2 +- src/style-spec/package.json | 2 +- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 778b0764aad..9b0be4e6cdf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,12 +1,16 @@ -## 1.11.0-beta.1 +## 1.11.0 -### ✨ Features and improvments +### ✨ Features and improvements * Add an option to scale the default `Marker` icon.([#9414](https://github.com/mapbox/mapbox-gl-js/pull/9414)) (h/t [adrianababakanian](https://github.com/adrianababakanian)) * Improving the shader compilation speed by manually getting the run-time attributes and uniforms.([#9497](https://github.com/mapbox/mapbox-gl-js/pull/9497)) +* Added `clusterMinPoints` option for clustered GeoJSON sources that defines the minimum number of points to form a cluster.([#9748](https://github.com/mapbox/mapbox-gl-js/pull/9748)) ### 🐞 Bug fixes * Fix a bug where map got stuck in a DragRotate interaction if it's mouseup occurred outside of the browser window or iframe.([#9512](https://github.com/mapbox/mapbox-gl-js/pull/9512)) * Fix potential visual regression for `*-pattern` properties on AMD graphics card vendor.([#9681](https://github.com/mapbox/mapbox-gl-js/pull/9681)) +* Fix zooming with a double tap on iOS Safari 13.([#9757](https://github.com/mapbox/mapbox-gl-js/pull/9757)) +* Removed a misleading `geometry exceeds allowed extent` warning when using Mapbox Streets vector tiles.([#9753](https://github.com/mapbox/mapbox-gl-js/pull/9753)) +* Fix reference error when requiring the browser bundle in Node. ([#9749](https://github.com/mapbox/mapbox-gl-js/pull/9749)) ## 1.10.1 diff --git a/package.json b/package.json index fd2fc4517b2..ef105294c2b 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "mapbox-gl", "description": "A WebGL interactive maps library", - "version": "1.11.0-beta.1", + "version": "1.11.0", "main": "dist/mapbox-gl.js", "style": "dist/mapbox-gl.css", "license": "SEE LICENSE IN LICENSE.txt", diff --git a/src/style-spec/package.json b/src/style-spec/package.json index 0b9a6e015a0..53958fff0c9 100644 --- a/src/style-spec/package.json +++ b/src/style-spec/package.json @@ -1,7 +1,7 @@ { "name": "@mapbox/mapbox-gl-style-spec", "description": "a specification for mapbox gl styles", - "version": "13.16.0-beta.1", + "version": "13.16.0", "author": "Mapbox", "keywords": [ "mapbox",