{"version":3,"file":"mapbox-gl.js","sources":["../src/util/browser/window.js","../node_modules/@mapbox/unitbezier/index.js","../node_modules/@mapbox/point-geometry/index.js","../src/util/util.js","../src/util/performance.js","../src/util/browser.js","../src/util/config.js","../src/util/webp_supported.js","../src/util/sku_token.js","../src/util/mapbox.js","../src/util/tile_request_cache.js","../src/util/ajax.js","../src/util/evented.js","../src/style-spec/util/extend.js","../src/style-spec/util/unbundle_jsonlint.js","../src/style-spec/expression/parsing_error.js","../src/style-spec/expression/scope.js","../src/style-spec/expression/types.js","../node_modules/csscolorparser/csscolorparser.js","../src/style-spec/util/color.js","../src/style-spec/expression/types/collator.js","../src/style-spec/expression/types/formatted.js","../src/style-spec/expression/types/resolved_image.js","../src/style-spec/expression/values.js","../src/style-spec/expression/definitions/literal.js","../src/style-spec/expression/runtime_error.js","../src/style-spec/expression/definitions/assertion.js","../src/style-spec/expression/definitions/format.js","../src/style-spec/expression/definitions/image.js","../src/style-spec/expression/definitions/coercion.js","../src/style-spec/expression/evaluation_context.js","../src/style-spec/expression/compound_expression.js","../src/style-spec/expression/definitions/collator.js","../src/style-spec/expression/definitions/within.js","../src/style-spec/expression/is_constant.js","../src/style-spec/expression/definitions/var.js","../src/style-spec/expression/parsing_context.js","../src/style-spec/expression/stops.js","../src/style-spec/expression/definitions/step.js","../src/style-spec/util/interpolate.js","../src/style-spec/util/color_spaces.js","../src/style-spec/expression/definitions/interpolate.js","../src/style-spec/expression/definitions/coalesce.js","../src/style-spec/expression/definitions/let.js","../src/style-spec/expression/definitions/at.js","../src/style-spec/expression/definitions/in.js","../src/style-spec/expression/definitions/index_of.js","../src/style-spec/expression/definitions/match.js","../src/style-spec/expression/definitions/case.js","../src/style-spec/expression/definitions/slice.js","../src/style-spec/expression/definitions/comparison.js","../src/style-spec/expression/definitions/number_format.js","../src/style-spec/expression/definitions/length.js","../src/style-spec/expression/definitions/index.js","../src/style-spec/util/result.js","../src/style-spec/util/properties.js","../src/style-spec/util/get_type.js","../src/style-spec/function/index.js","../src/style-spec/expression/index.js","../src/style-spec/error/validation_error.js","../src/style-spec/validate/validate_object.js","../src/style-spec/validate/validate_array.js","../src/style-spec/validate/validate_number.js","../src/style-spec/validate/validate_function.js","../src/style-spec/validate/validate_expression.js","../src/style-spec/validate/validate_enum.js","../src/style-spec/feature_filter/index.js","../src/style-spec/validate/validate_filter.js","../src/style-spec/validate/validate_property.js","../src/style-spec/validate/validate_paint_property.js","../src/style-spec/validate/validate_layout_property.js","../src/style-spec/validate/validate_layer.js","../src/style-spec/validate/validate_string.js","../src/style-spec/validate/validate_source.js","../src/style-spec/validate/validate_light.js","../src/style-spec/validate/validate_terrain.js","../src/style-spec/validate/validate_fog.js","../src/style-spec/validate/validate.js","../src/style-spec/validate/validate_boolean.js","../src/style-spec/validate/validate_color.js","../src/style-spec/validate/validate_formatted.js","../src/style-spec/validate/validate_image.js","../src/style-spec/validate/validate_projection.js","../src/style-spec/validate/validate_glyphs_url.js","../src/style-spec/validate_style.min.js","../src/style/validate_style.js","../node_modules/grid-index/grid-index.js","../src/util/web_worker_transfer.js","../src/style/zoom_history.js","../src/util/is_char_in_unicode_block.js","../src/util/script_detection.js","../src/source/rtl_text_plugin.js","../src/style/evaluation_parameters.js","../src/style/properties.js","../src/shaders/encode_attribute.js","../src/util/struct_array.js","../src/data/array_types.js","../src/data/bucket/pattern_attributes.js","../src/data/bucket/dash_attributes.js","../node_modules/murmurhash-js/murmurhash3_gc.js","../node_modules/murmurhash-js/murmurhash2_gc.js","../node_modules/murmurhash-js/index.js","../src/data/feature_position_map.js","../src/render/uniform_binding.js","../src/data/program_configuration.js","../src/style/style_layer.js","../src/data/bucket/circle_attributes.js","../src/data/segment.js","../src/data/extent.js","../src/geo/lng_lat_bounds.js","../src/geo/lng_lat.js","../src/geo/mercator_coordinate.js","../src/geo/projection/resample.js","../src/data/load_geometry.js","../src/data/evaluation_feature.js","../src/data/bucket/circle_bucket.js","../src/util/intersection_tests.js","../src/style/query_utils.js","../src/style/style_layer/circle_style_layer_properties.js","../node_modules/gl-matrix/esm/common.js","../node_modules/gl-matrix/esm/mat3.js","../node_modules/gl-matrix/esm/mat4.js","../node_modules/gl-matrix/esm/vec3.js","../node_modules/gl-matrix/esm/vec4.js","../node_modules/gl-matrix/esm/quat.js","../src/util/primitives.js","../src/style/style_layer/circle_style_layer.js","../src/data/bucket/heatmap_bucket.js","../src/util/image.js","../src/style/style_layer/heatmap_style_layer_properties.js","../src/util/color_ramp.js","../src/style/style_layer/hillshade_style_layer_properties.js","../src/data/bucket/fill_attributes.js","../node_modules/earcut/src/earcut.js","../node_modules/quickselect/index.js","../src/util/classify_rings.js","../src/data/bucket/pattern_bucket_features.js","../src/data/bucket/fill_bucket.js","../src/style/style_layer/fill_style_layer_properties.js","../src/data/bucket/fill_extrusion_attributes.js","../node_modules/@mapbox/vector-tile/lib/vectortilefeature.js","../node_modules/@mapbox/vector-tile/lib/vectortilelayer.js","../node_modules/@mapbox/vector-tile/lib/vectortile.js","../node_modules/@mapbox/vector-tile/index.js","../src/util/polygon_clipping.js","../src/data/bucket/fill_extrusion_bucket.js","../src/style/style_layer/fill_extrusion_style_layer_properties.js","../node_modules/@mapbox/whoots-js/index.mjs","../src/source/tile_id.js","../src/style/style_layer/fill_extrusion_style_layer.js","../src/data/bucket/line_attributes.js","../src/data/bucket/line_attributes_ext.js","../src/data/bucket/line_bucket.js","../src/style/style_layer/line_style_layer_properties.js","../src/style/style_layer/line_style_layer.js","../src/data/bucket/symbol_attributes.js","../src/symbol/one_em.js","../src/symbol/symbol_size.js","../src/symbol/transform_text.js","../src/util/verticalize_punctuation.js","../node_modules/ieee754/index.js","../node_modules/pbf/index.js","../src/style/parse_glyph_pbf.js","../node_modules/potpack/index.mjs","../src/render/image_atlas.js","../src/symbol/shaping.js","../src/symbol/anchor.js","../src/symbol/check_max_angle.js","../src/symbol/get_anchors.js","../src/symbol/clip_line.js","../node_modules/@mapbox/tiny-sdf/index.js","../src/render/glyph_manager.js","../src/symbol/quads.js","../src/style/load_glyph_range.js","../node_modules/tinyqueue/index.js","../src/util/find_pole_of_inaccessibility.js","../src/symbol/symbol_layout.js","../src/terrain/globe_attributes.js","../src/data/pos_attributes.js","../src/geo/projection/globe_util.js","../src/geo/projection/far_z.js","../src/geo/projection/tile_transform.js","../src/geo/projection/projection.js","../src/geo/projection/albers.js","../src/geo/projection/equal_earth.js","../src/geo/projection/equirectangular.js","../src/geo/projection/lambert.js","../src/geo/projection/mercator.js","../src/geo/projection/natural_earth.js","../src/geo/projection/winkel_tripel.js","../src/geo/projection/cylindrical_equal_area.js","../src/geo/projection/globe.js","../src/geo/projection/index.js","../src/data/bucket/symbol_bucket.js","../src/symbol/mergelines.js","../src/style/style_layer/symbol_style_layer_properties.js","../src/style/format_section_override.js","../src/style/style_layer/symbol_style_layer.js","../src/util/resolve_tokens.js","../src/style/style_layer/background_style_layer_properties.js","../src/style/style_layer/raster_style_layer_properties.js","../src/style/style_layer/custom_style_layer.js","../src/style/style_layer/sky_style_layer_properties.js","../src/style/style_layer/sky_style_layer.js","../src/style/create_style_layer.js","../src/style/style_layer/heatmap_style_layer.js","../src/style/style_layer/hillshade_style_layer.js","../src/style/style_layer/fill_style_layer.js","../src/style/style_layer/background_style_layer.js","../src/style/style_layer/raster_style_layer.js","../src/render/texture.js","../src/render/line_atlas.js","../src/util/throttled_invoker.js","../src/util/scheduler.js","../src/util/dictionary_coder.js","../src/util/vectortile_to_geojson.js","../src/source/tile_mesh.js","../src/data/bounds_attributes.js","../src/source/tile.js","../src/data/bucket.js","../src/source/source_state.js","../src/data/dem_tree.js","../src/data/dem_data.js","../src/source/tile_cache.js","../src/gl/depth_mode.js","../src/gl/stencil_mode.js","../src/gl/color_mode.js","../src/gl/cull_face_mode.js","../src/source/source_cache.js","../src/terrain/elevation.js","../src/data/feature_index.js","../src/render/glyph_atlas.js","../src/source/worker_tile.js","../src/source/vector_tile_worker_source.js","../src/util/actor.js","../src/style-spec/util/ref_properties.js","../src/style-spec/group_by_layout.js","../src/style/style_layer_index.js","../src/source/raster_dem_tile_worker_source.js","../node_modules/@mapbox/geojson-rewind/index.js","../src/source/geojson_wrapper.js","../node_modules/vt-pbf/lib/geojson_wrapper.js","../node_modules/vt-pbf/index.js","../node_modules/kdbush/src/sort.js","../node_modules/kdbush/src/within.js","../node_modules/kdbush/src/index.js","../node_modules/kdbush/src/range.js","../node_modules/supercluster/index.js","../node_modules/geojson-vt/src/simplify.js","../node_modules/geojson-vt/src/feature.js","../node_modules/geojson-vt/src/convert.js","../node_modules/geojson-vt/src/clip.js","../node_modules/geojson-vt/src/wrap.js","../node_modules/geojson-vt/src/transform.js","../node_modules/geojson-vt/src/tile.js","../node_modules/geojson-vt/src/index.js","../src/source/geojson_worker_source.js","../src/source/worker.js","../src/style-spec/util/deep_equal.js","../node_modules/@mapbox/mapbox-gl-supported/index.js","../src/util/dom.js","../node_modules/gl-matrix/esm/mat2.js","../src/style/style_image.js","../src/render/image_manager.js","../src/style/light.js","../src/style/terrain.js","../src/style/fog_helpers.js","../src/style/fog.js","../src/util/dispatcher.js","../src/source/pixels_to_tile_units.js","../src/style/query_geometry.js","../src/source/load_tilejson.js","../src/source/tile_bounds.js","../src/gl/index_buffer.js","../src/gl/vertex_buffer.js","../src/gl/value.js","../src/gl/framebuffer.js","../src/gl/context.js","../src/source/raster_tile_source.js","../src/util/offscreen_canvas_supported.js","../src/source/image_source.js","../src/source/source.js","../src/source/vector_tile_source.js","../src/source/raster_dem_tile_source.js","../src/source/geojson_source.js","../src/source/video_source.js","../src/source/canvas_source.js","../src/source/custom_source.js","../src/source/query_features.js","../src/util/browser/web_worker.js","../src/util/worker_pool.js","../src/util/global_worker_pool.js","../src/style-spec/deref.js","../src/style-spec/diff.js","../src/symbol/path_interpolator.js","../src/symbol/grid_index.js","../src/symbol/projection.js","../src/symbol/collision_index.js","../src/geo/projection/projection_util.js","../src/symbol/placement.js","../src/style/pauseable_placement.js","../src/symbol/cross_tile_symbol_index.js","../src/style/style.js","../src/style-spec/empty.js","../src/style/load_sprite.js","../src/shaders/shaders.js","../src/render/vertex_array_object.js","../src/render/program/hillshade_program.js","../src/render/draw_hillshade.js","../src/terrain/terrain_raster_program.js","../src/terrain/globe_raster_program.js","../src/terrain/draw_terrain_raster.js","../src/render/program/clipping_mask_program.js","../src/render/raster_fade.js","../src/terrain/terrain.js","../src/render/program.js","../src/render/fog.js","../src/render/program/pattern.js","../src/render/program/fill_extrusion_program.js","../src/render/program/fill_program.js","../src/render/program/circle_program.js","../src/render/program/collision_program.js","../src/render/program/debug_program.js","../src/render/program/heatmap_program.js","../src/render/program/line_program.js","../src/render/program/raster_program.js","../src/render/program/symbol_program.js","../src/render/program/background_program.js","../src/render/program/program_uniforms.js","../src/render/program/skybox_program.js","../src/render/program/skybox_capture_program.js","../src/render/draw_collision_debug.js","../src/render/draw_symbol.js","../src/render/draw_fill.js","../src/render/draw_fill_extrusion.js","../src/render/draw_debug.js","../src/render/skybox_attributes.js","../src/render/skybox_geometry.js","../src/render/draw_sky.js","../src/render/atmosphere_attributes.js","../src/render/atmosphere_buffer.js","../src/render/painter.js","../src/render/draw_circle.js","../src/render/draw_heatmap.js","../src/render/draw_line.js","../src/render/draw_raster.js","../src/render/draw_background.js","../src/render/draw_custom.js","../src/render/draw_atmosphere.js","../src/geo/edge_insets.js","../src/ui/free_camera.js","../src/geo/projection/adjustments.js","../src/geo/transform.js","../src/util/throttle.js","../src/ui/hash.js","../src/ui/handler_inertia.js","../src/ui/events.js","../src/ui/handler/map_event.js","../src/ui/handler/box_zoom.js","../src/ui/handler/handler_util.js","../src/ui/handler/tap_recognizer.js","../src/ui/handler/tap_zoom.js","../src/ui/handler/mouse.js","../src/ui/handler/touch_pan.js","../src/ui/handler/touch_zoom_rotate.js","../src/ui/handler/keyboard.js","../src/ui/handler/scroll_zoom.js","../src/ui/handler/shim/dblclick_zoom.js","../src/ui/handler/click_zoom.js","../src/ui/handler/tap_drag_zoom.js","../src/ui/handler/shim/drag_pan.js","../src/ui/handler/shim/drag_rotate.js","../src/ui/handler/shim/touch_zoom_rotate.js","../src/ui/handler_manager.js","../src/ui/camera.js","../src/ui/control/attribution_control.js","../src/ui/control/logo_control.js","../src/util/task_queue.js","../src/util/smart_wrap.js","../src/ui/anchor.js","../src/ui/marker.js","../src/util/eased_variable.js","../src/ui/default_locale.js","../src/ui/map.js","../src/ui/control/navigation_control.js","../src/ui/control/geolocate_control.js","../src/ui/control/scale_control.js","../src/ui/popup.js","../src/index.js","../src/ui/control/fullscreen_control.js","../rollup/mapboxgl.js"],"sourcesContent":["// @flow\n/* eslint-env browser */\nimport type {Window} from '../../types/window.js';\n\n// shim window for the case of requiring the browser bundle in Node\nexport default ((typeof self !== 'undefined' ? self : ({}: any)): Window);\n","/*\n * Copyright (C) 2008 Apple Inc. All Rights Reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n * 1. Redistributions of source code must retain the above copyright\n * notice, this list of conditions and the following disclaimer.\n * 2. Redistributions in binary form must reproduce the above copyright\n * notice, this list of conditions and the following disclaimer in the\n * documentation and/or other materials provided with the distribution.\n *\n * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n * Ported from Webkit\n * http://svn.webkit.org/repository/webkit/trunk/Source/WebCore/platform/graphics/UnitBezier.h\n */\n\nmodule.exports = UnitBezier;\n\nfunction UnitBezier(p1x, p1y, p2x, p2y) {\n // Calculate the polynomial coefficients, implicit first and last control points are (0,0) and (1,1).\n this.cx = 3.0 * p1x;\n this.bx = 3.0 * (p2x - p1x) - this.cx;\n this.ax = 1.0 - this.cx - this.bx;\n\n this.cy = 3.0 * p1y;\n this.by = 3.0 * (p2y - p1y) - this.cy;\n this.ay = 1.0 - this.cy - this.by;\n\n this.p1x = p1x;\n this.p1y = p2y;\n this.p2x = p2x;\n this.p2y = p2y;\n}\n\nUnitBezier.prototype.sampleCurveX = function(t) {\n // `ax t^3 + bx t^2 + cx t' expanded using Horner's rule.\n return ((this.ax * t + this.bx) * t + this.cx) * t;\n};\n\nUnitBezier.prototype.sampleCurveY = function(t) {\n return ((this.ay * t + this.by) * t + this.cy) * t;\n};\n\nUnitBezier.prototype.sampleCurveDerivativeX = function(t) {\n return (3.0 * this.ax * t + 2.0 * this.bx) * t + this.cx;\n};\n\nUnitBezier.prototype.solveCurveX = function(x, epsilon) {\n if (typeof epsilon === 'undefined') epsilon = 1e-6;\n\n var t0, t1, t2, x2, i;\n\n // First try a few iterations of Newton's method -- normally very fast.\n for (t2 = x, i = 0; i < 8; i++) {\n\n x2 = this.sampleCurveX(t2) - x;\n if (Math.abs(x2) < epsilon) return t2;\n\n var d2 = this.sampleCurveDerivativeX(t2);\n if (Math.abs(d2) < 1e-6) break;\n\n t2 = t2 - x2 / d2;\n }\n\n // Fall back to the bisection method for reliability.\n t0 = 0.0;\n t1 = 1.0;\n t2 = x;\n\n if (t2 < t0) return t0;\n if (t2 > t1) return t1;\n\n while (t0 < t1) {\n\n x2 = this.sampleCurveX(t2);\n if (Math.abs(x2 - x) < epsilon) return t2;\n\n if (x > x2) {\n t0 = t2;\n } else {\n t1 = t2;\n }\n\n t2 = (t1 - t0) * 0.5 + t0;\n }\n\n // Failure.\n return t2;\n};\n\nUnitBezier.prototype.solve = function(x, epsilon) {\n return this.sampleCurveY(this.solveCurveX(x, epsilon));\n};\n","'use strict';\n\nmodule.exports = Point;\n\n/**\n * A standalone point geometry with useful accessor, comparison, and\n * modification methods.\n *\n * @class Point\n * @param {Number} x the x-coordinate. this could be longitude or screen\n * pixels, or any other sort of unit.\n * @param {Number} y the y-coordinate. this could be latitude or screen\n * pixels, or any other sort of unit.\n * @example\n * var point = new Point(-77, 38);\n */\nfunction Point(x, y) {\n this.x = x;\n this.y = y;\n}\n\nPoint.prototype = {\n\n /**\n * Clone this point, returning a new point that can be modified\n * without affecting the old one.\n * @return {Point} the clone\n */\n clone: function() { return new Point(this.x, this.y); },\n\n /**\n * Add this point's x & y coordinates to another point,\n * yielding a new point.\n * @param {Point} p the other point\n * @return {Point} output point\n */\n add: function(p) { return this.clone()._add(p); },\n\n /**\n * Subtract this point's x & y coordinates to from point,\n * yielding a new point.\n * @param {Point} p the other point\n * @return {Point} output point\n */\n sub: function(p) { return this.clone()._sub(p); },\n\n /**\n * Multiply this point's x & y coordinates by point,\n * yielding a new point.\n * @param {Point} p the other point\n * @return {Point} output point\n */\n multByPoint: function(p) { return this.clone()._multByPoint(p); },\n\n /**\n * Divide this point's x & y coordinates by point,\n * yielding a new point.\n * @param {Point} p the other point\n * @return {Point} output point\n */\n divByPoint: function(p) { return this.clone()._divByPoint(p); },\n\n /**\n * Multiply this point's x & y coordinates by a factor,\n * yielding a new point.\n * @param {Point} k factor\n * @return {Point} output point\n */\n mult: function(k) { return this.clone()._mult(k); },\n\n /**\n * Divide this point's x & y coordinates by a factor,\n * yielding a new point.\n * @param {Point} k factor\n * @return {Point} output point\n */\n div: function(k) { return this.clone()._div(k); },\n\n /**\n * Rotate this point around the 0, 0 origin by an angle a,\n * given in radians\n * @param {Number} a angle to rotate around, in radians\n * @return {Point} output point\n */\n rotate: function(a) { return this.clone()._rotate(a); },\n\n /**\n * Rotate this point around p point by an angle a,\n * given in radians\n * @param {Number} a angle to rotate around, in radians\n * @param {Point} p Point to rotate around\n * @return {Point} output point\n */\n rotateAround: function(a,p) { return this.clone()._rotateAround(a,p); },\n\n /**\n * Multiply this point by a 4x1 transformation matrix\n * @param {Array} m transformation matrix\n * @return {Point} output point\n */\n matMult: function(m) { return this.clone()._matMult(m); },\n\n /**\n * Calculate this point but as a unit vector from 0, 0, meaning\n * that the distance from the resulting point to the 0, 0\n * coordinate will be equal to 1 and the angle from the resulting\n * point to the 0, 0 coordinate will be the same as before.\n * @return {Point} unit vector point\n */\n unit: function() { return this.clone()._unit(); },\n\n /**\n * Compute a perpendicular point, where the new y coordinate\n * is the old x coordinate and the new x coordinate is the old y\n * coordinate multiplied by -1\n * @return {Point} perpendicular point\n */\n perp: function() { return this.clone()._perp(); },\n\n /**\n * Return a version of this point with the x & y coordinates\n * rounded to integers.\n * @return {Point} rounded point\n */\n round: function() { return this.clone()._round(); },\n\n /**\n * Return the magitude of this point: this is the Euclidean\n * distance from the 0, 0 coordinate to this point's x and y\n * coordinates.\n * @return {Number} magnitude\n */\n mag: function() {\n return Math.sqrt(this.x * this.x + this.y * this.y);\n },\n\n /**\n * Judge whether this point is equal to another point, returning\n * true or false.\n * @param {Point} other the other point\n * @return {boolean} whether the points are equal\n */\n equals: function(other) {\n return this.x === other.x &&\n this.y === other.y;\n },\n\n /**\n * Calculate the distance from this point to another point\n * @param {Point} p the other point\n * @return {Number} distance\n */\n dist: function(p) {\n return Math.sqrt(this.distSqr(p));\n },\n\n /**\n * Calculate the distance from this point to another point,\n * without the square root step. Useful if you're comparing\n * relative distances.\n * @param {Point} p the other point\n * @return {Number} distance\n */\n distSqr: function(p) {\n var dx = p.x - this.x,\n dy = p.y - this.y;\n return dx * dx + dy * dy;\n },\n\n /**\n * Get the angle from the 0, 0 coordinate to this point, in radians\n * coordinates.\n * @return {Number} angle\n */\n angle: function() {\n return Math.atan2(this.y, this.x);\n },\n\n /**\n * Get the angle from this point to another point, in radians\n * @param {Point} b the other point\n * @return {Number} angle\n */\n angleTo: function(b) {\n return Math.atan2(this.y - b.y, this.x - b.x);\n },\n\n /**\n * Get the angle between this point and another point, in radians\n * @param {Point} b the other point\n * @return {Number} angle\n */\n angleWith: function(b) {\n return this.angleWithSep(b.x, b.y);\n },\n\n /*\n * Find the angle of the two vectors, solving the formula for\n * the cross product a x b = |a||b|sin(θ) for θ.\n * @param {Number} x the x-coordinate\n * @param {Number} y the y-coordinate\n * @return {Number} the angle in radians\n */\n angleWithSep: function(x, y) {\n return Math.atan2(\n this.x * y - this.y * x,\n this.x * x + this.y * y);\n },\n\n _matMult: function(m) {\n var x = m[0] * this.x + m[1] * this.y,\n y = m[2] * this.x + m[3] * this.y;\n this.x = x;\n this.y = y;\n return this;\n },\n\n _add: function(p) {\n this.x += p.x;\n this.y += p.y;\n return this;\n },\n\n _sub: function(p) {\n this.x -= p.x;\n this.y -= p.y;\n return this;\n },\n\n _mult: function(k) {\n this.x *= k;\n this.y *= k;\n return this;\n },\n\n _div: function(k) {\n this.x /= k;\n this.y /= k;\n return this;\n },\n\n _multByPoint: function(p) {\n this.x *= p.x;\n this.y *= p.y;\n return this;\n },\n\n _divByPoint: function(p) {\n this.x /= p.x;\n this.y /= p.y;\n return this;\n },\n\n _unit: function() {\n this._div(this.mag());\n return this;\n },\n\n _perp: function() {\n var y = this.y;\n this.y = this.x;\n this.x = -y;\n return this;\n },\n\n _rotate: function(angle) {\n var cos = Math.cos(angle),\n sin = Math.sin(angle),\n x = cos * this.x - sin * this.y,\n y = sin * this.x + cos * this.y;\n this.x = x;\n this.y = y;\n return this;\n },\n\n _rotateAround: function(angle, p) {\n var cos = Math.cos(angle),\n sin = Math.sin(angle),\n x = p.x + cos * (this.x - p.x) - sin * (this.y - p.y),\n y = p.y + sin * (this.x - p.x) + cos * (this.y - p.y);\n this.x = x;\n this.y = y;\n return this;\n },\n\n _round: function() {\n this.x = Math.round(this.x);\n this.y = Math.round(this.y);\n return this;\n }\n};\n\n/**\n * Construct a point from an array if necessary, otherwise if the input\n * is already a Point, or an unknown type, return it unchanged\n * @param {Array|Point|*} a any kind of input value\n * @return {Point} constructed point, or passed-through value.\n * @example\n * // this\n * var point = Point.convert([0, 1]);\n * // is equivalent to\n * var point = new Point(0, 1);\n */\nPoint.convert = function (a) {\n if (a instanceof Point) {\n return a;\n }\n if (Array.isArray(a)) {\n return new Point(a[0], a[1]);\n }\n return a;\n};\n","// @flow\n\nimport UnitBezier from '@mapbox/unitbezier';\n\nimport Point from '@mapbox/point-geometry';\nimport window from './window.js';\nimport assert from 'assert';\n\nimport type {Callback} from '../types/callback.js';\nimport type {Mat4, Vec4} from 'gl-matrix';\n\nconst DEG_TO_RAD = Math.PI / 180;\nconst RAD_TO_DEG = 180 / Math.PI;\n\n/**\n * Converts an angle in degrees to radians\n * copy all properties from the source objects into the destination.\n * The last source object given overrides properties from previous\n * source objects.\n *\n * @param a angle to convert\n * @returns the angle in radians\n * @private\n */\nexport function degToRad(a: number): number {\n return a * DEG_TO_RAD;\n}\n\n/**\n * Converts an angle in radians to degrees\n * copy all properties from the source objects into the destination.\n * The last source object given overrides properties from previous\n * source objects.\n *\n * @param a angle to convert\n * @returns the angle in degrees\n * @private\n */\nexport function radToDeg(a: number): number {\n return a * RAD_TO_DEG;\n}\n\nconst TILE_CORNERS = [[0, 0], [1, 0], [1, 1], [0, 1]];\n\n/**\n * Given a particular bearing, returns the corner of the tile thats farthest\n * along the bearing.\n *\n * @param {number} bearing angle in degrees (-180, 180]\n * @returns {QuadCorner}\n * @private\n */\nexport function furthestTileCorner(bearing: number): [number, number] {\n const alignedBearing = ((bearing + 45) + 360) % 360;\n const cornerIdx = Math.round(alignedBearing / 90) % 4;\n return TILE_CORNERS[cornerIdx];\n}\n\n/**\n * @module util\n * @private\n */\n\n/**\n * Given a value `t` that varies between 0 and 1, return\n * an interpolation function that eases between 0 and 1 in a pleasing\n * cubic in-out fashion.\n *\n * @private\n */\nexport function easeCubicInOut(t: number): number {\n if (t <= 0) return 0;\n if (t >= 1) return 1;\n const t2 = t * t,\n t3 = t2 * t;\n return 4 * (t < 0.5 ? t3 : 3 * (t - t2) + t3 - 0.75);\n}\n\n/**\n * Computes an AABB for a set of points.\n *\n * @param {Point[]} points\n * @returns {{ min: Point, max: Point}}\n * @private\n */\nexport function getBounds(points: Point[]): { min: Point, max: Point} {\n let minX = Infinity;\n let minY = Infinity;\n let maxX = -Infinity;\n let maxY = -Infinity;\n for (const p of points) {\n minX = Math.min(minX, p.x);\n minY = Math.min(minY, p.y);\n maxX = Math.max(maxX, p.x);\n maxY = Math.max(maxY, p.y);\n }\n\n return {\n min: new Point(minX, minY),\n max: new Point(maxX, maxY),\n };\n}\n\n/**\n * Returns the square of the 2D distance between an AABB defined by min and max and a point.\n * If point is null or undefined, the AABB distance from the origin (0,0) is returned.\n *\n * @param {Point} min The minimum extent of the AABB.\n * @param {Point} max The maximum extent of the AABB.\n * @param {Point} [point] The point to compute the distance from, may be undefined.\n * @returns {number} The square distance from the AABB, 0.0 if the AABB contains the point.\n */\nexport function getAABBPointSquareDist(min: Point, max: Point, point: ?Point): number {\n let sqDist = 0.0;\n\n for (let i = 0; i < 2; ++i) {\n const v = point ? point[i] : 0.0;\n assert(min[i] < max[i], 'Invalid aabb min and max inputs, min[i] must be < max[i].');\n if (min[i] > v) sqDist += (min[i] - v) * (min[i] - v);\n if (max[i] < v) sqDist += (v - max[i]) * (v - max[i]);\n }\n\n return sqDist;\n}\n\n/**\n * Converts a AABB into a polygon with clockwise winding order.\n *\n * @param {Point} min The top left point.\n * @param {Point} max The bottom right point.\n * @param {number} [buffer=0] The buffer width.\n * @param {boolean} [close=true] Whether to close the polygon or not.\n * @returns {Point[]} The polygon.\n */\nexport function polygonizeBounds(min: Point, max: Point, buffer: number = 0, close: boolean = true): Point[] {\n const offset = new Point(buffer, buffer);\n const minBuf = min.sub(offset);\n const maxBuf = max.add(offset);\n const polygon = [minBuf, new Point(maxBuf.x, minBuf.y), maxBuf, new Point(minBuf.x, maxBuf.y)];\n\n if (close) {\n polygon.push(minBuf.clone());\n }\n return polygon;\n}\n\n/**\n * Takes a convex ring and expands it outward by applying a buffer around it.\n * This function assumes that the ring is in clockwise winding order.\n *\n * @param {Point[]} ring The input ring.\n * @param {number} buffer The buffer width.\n * @returns {Point[]} The expanded ring.\n */\nexport function bufferConvexPolygon(ring: Point[], buffer: number): Point[] {\n assert(ring.length > 2, 'bufferConvexPolygon requires the ring to have atleast 3 points');\n const output = [];\n for (let currIdx = 0; currIdx < ring.length; currIdx++) {\n const prevIdx = wrap(currIdx - 1, -1, ring.length - 1);\n const nextIdx = wrap(currIdx + 1, -1, ring.length - 1);\n const prev = ring[prevIdx];\n const curr = ring[currIdx];\n const next = ring[nextIdx];\n const p1 = prev.sub(curr).unit();\n const p2 = next.sub(curr).unit();\n const interiorAngle = p2.angleWithSep(p1.x, p1.y);\n // Calcuate a vector that points in the direction of the angle bisector between two sides.\n // Scale it based on a right angled triangle constructed at that corner.\n const offset = p1.add(p2).unit().mult(-1 * buffer / Math.sin(interiorAngle / 2));\n output.push(curr.add(offset));\n }\n return output;\n}\n\ntype EaseFunction = (t: number) => number;\n\n/**\n * Given given (x, y), (x1, y1) control points for a bezier curve,\n * return a function that interpolates along that curve.\n *\n * @param p1x control point 1 x coordinate\n * @param p1y control point 1 y coordinate\n * @param p2x control point 2 x coordinate\n * @param p2y control point 2 y coordinate\n * @private\n */\nexport function bezier(p1x: number, p1y: number, p2x: number, p2y: number): EaseFunction {\n const bezier = new UnitBezier(p1x, p1y, p2x, p2y);\n return function(t: number) {\n return bezier.solve(t);\n };\n}\n\n/**\n * A default bezier-curve powered easing function with\n * control points (0.25, 0.1) and (0.25, 1)\n *\n * @private\n */\nexport const ease: EaseFunction = bezier(0.25, 0.1, 0.25, 1);\n\n/**\n * constrain n to the given range via min + max\n *\n * @param n value\n * @param min the minimum value to be returned\n * @param max the maximum value to be returned\n * @returns the clamped value\n * @private\n */\nexport function clamp(n: number, min: number, max: number): number {\n return Math.min(max, Math.max(min, n));\n}\n\n/**\n * Equivalent to GLSL smoothstep.\n *\n * @param {number} e0 The lower edge of the sigmoid\n * @param {number} e1 The upper edge of the sigmoid\n * @param {number} x the value to be interpolated\n * @returns {number} in the range [0, 1]\n * @private\n */\nexport function smoothstep(e0: number, e1: number, x: number): number {\n x = clamp((x - e0) / (e1 - e0), 0, 1);\n return x * x * (3 - 2 * x);\n}\n\n/**\n * constrain n to the given range, excluding the minimum, via modular arithmetic\n *\n * @param n value\n * @param min the minimum value to be returned, exclusive\n * @param max the maximum value to be returned, inclusive\n * @returns constrained number\n * @private\n */\nexport function wrap(n: number, min: number, max: number): number {\n const d = max - min;\n const w = ((n - min) % d + d) % d + min;\n return (w === min) ? max : w;\n}\n\n/**\n * Computes shortest angle in range [-180, 180) between two angles.\n *\n * @param {*} a First angle in degrees\n * @param {*} b Second angle in degrees\n * @returns Shortest angle\n * @private\n */\nexport function shortestAngle(a: number, b: number): number {\n const diff = (b - a + 180) % 360 - 180;\n return diff < -180 ? diff + 360 : diff;\n}\n\n/*\n * Call an asynchronous function on an array of arguments,\n * calling `callback` with the completed results of all calls.\n *\n * @param array input to each call of the async function.\n * @param fn an async function with signature (data, callback)\n * @param callback a callback run after all async work is done.\n * called with an array, containing the results of each async call.\n * @private\n */\nexport function asyncAll(\n array: Array,\n fn: (item: Item, fnCallback: Callback) => void,\n callback: Callback>\n): void {\n if (!array.length) { return callback(null, []); }\n let remaining = array.length;\n const results = new Array(array.length);\n let error = null;\n array.forEach((item, i) => {\n fn(item, (err, result) => {\n if (err) error = err;\n results[i] = ((result: any): Result); // https://github.com/facebook/flow/issues/2123\n if (--remaining === 0) callback(error, results);\n });\n });\n}\n\n/*\n * Polyfill for Object.values. Not fully spec compliant, but we don't\n * need it to be.\n *\n * @private\n */\nexport function values(obj: {[key: string]: T}): Array {\n const result = [];\n for (const k in obj) {\n result.push(obj[k]);\n }\n return result;\n}\n\n/*\n * Compute the difference between the keys in one object and the keys\n * in another object.\n *\n * @returns keys difference\n * @private\n */\nexport function keysDifference(obj: {[key: string]: S}, other: {[key: string]: T}): Array {\n const difference = [];\n for (const i in obj) {\n if (!(i in other)) {\n difference.push(i);\n }\n }\n return difference;\n}\n\n/**\n * Given a destination object and optionally many source objects,\n * copy all properties from the source objects into the destination.\n * The last source object given overrides properties from previous\n * source objects.\n *\n * @param dest destination object\n * @param sources sources from which properties are pulled\n * @private\n */\nexport function extend(dest: Object, ...sources: Array): Object {\n for (const src of sources) {\n for (const k in src) {\n dest[k] = src[k];\n }\n }\n return dest;\n}\n\n/**\n * Given an object and a number of properties as strings, return version\n * of that object with only those properties.\n *\n * @param src the object\n * @param properties an array of property names chosen\n * to appear on the resulting object.\n * @returns object with limited properties.\n * @example\n * var foo = { name: 'Charlie', age: 10 };\n * var justName = pick(foo, ['name']);\n * // justName = { name: 'Charlie' }\n * @private\n */\nexport function pick(src: Object, properties: Array): Object {\n const result = {};\n for (let i = 0; i < properties.length; i++) {\n const k = properties[i];\n if (k in src) {\n result[k] = src[k];\n }\n }\n return result;\n}\n\nlet id = 1;\n\n/**\n * Return a unique numeric id, starting at 1 and incrementing with\n * each call.\n *\n * @returns unique numeric id.\n * @private\n */\nexport function uniqueId(): number {\n return id++;\n}\n\n/**\n * Return a random UUID (v4). Taken from: https://gist.github.com/jed/982883\n * @private\n */\nexport function uuid(): string {\n function b(a) {\n return a ? (a ^ Math.random() * (16 >> a / 4)).toString(16) :\n //$FlowFixMe: Flow doesn't like the implied array literal conversion here\n ([1e7] + -[1e3] + -4e3 + -8e3 + -1e11).replace(/[018]/g, b);\n }\n return b();\n}\n\n/**\n * Return whether a given value is a power of two\n * @private\n */\nexport function isPowerOfTwo(value: number): boolean {\n return (Math.log(value) / Math.LN2) % 1 === 0;\n}\n\n/**\n * Return the next power of two, or the input value if already a power of two\n * @private\n */\nexport function nextPowerOfTwo(value: number): number {\n if (value <= 1) return 1;\n return Math.pow(2, Math.ceil(Math.log(value) / Math.LN2));\n}\n\n/**\n * Return the previous power of two, or the input value if already a power of two\n * @private\n */\nexport function prevPowerOfTwo(value: number): number {\n if (value <= 1) return 1;\n return Math.pow(2, Math.floor(Math.log(value) / Math.LN2));\n}\n\n/**\n * Validate a string to match UUID(v4) of the\n * form: xxxxxxxx-xxxx-4xxx-[89ab]xxx-xxxxxxxxxxxx\n * @param str string to validate.\n * @private\n */\nexport function validateUuid(str: ?string): boolean {\n return str ? /^[0-9a-f]{8}-[0-9a-f]{4}-[4][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(str) : false;\n}\n\n/**\n * Given an array of member function names as strings, replace all of them\n * with bound versions that will always refer to `context` as `this`. This\n * is useful for classes where otherwise event bindings would reassign\n * `this` to the evented object or some other value: this lets you ensure\n * the `this` value always.\n *\n * @param fns list of member function names\n * @param context the context value\n * @example\n * function MyClass() {\n * bindAll(['ontimer'], this);\n * this.name = 'Tom';\n * }\n * MyClass.prototype.ontimer = function() {\n * alert(this.name);\n * };\n * var myClass = new MyClass();\n * setTimeout(myClass.ontimer, 100);\n * @private\n */\nexport function bindAll(fns: Array, context: Object): void {\n fns.forEach((fn) => {\n if (!context[fn]) { return; }\n context[fn] = context[fn].bind(context);\n });\n}\n\n/**\n * Determine if a string ends with a particular substring\n *\n * @private\n */\nexport function endsWith(string: string, suffix: string): boolean {\n return string.indexOf(suffix, string.length - suffix.length) !== -1;\n}\n\n/**\n * Create an object by mapping all the values of an existing object while\n * preserving their keys.\n *\n * @private\n */\nexport function mapObject(input: Object, iterator: Function, context?: Object): Object {\n const output = {};\n for (const key in input) {\n output[key] = iterator.call(context || this, input[key], key, input);\n }\n return output;\n}\n\n/**\n * Create an object by filtering out values of an existing object.\n *\n * @private\n */\nexport function filterObject(input: Object, iterator: Function, context?: Object): Object {\n const output = {};\n for (const key in input) {\n if (iterator.call(context || this, input[key], key, input)) {\n output[key] = input[key];\n }\n }\n return output;\n}\n\nimport deepEqual from '../style-spec/util/deep_equal.js';\nexport {deepEqual};\n\n/**\n * Deeply clones two objects.\n *\n * @private\n */\nexport function clone(input: T): T {\n if (Array.isArray(input)) {\n return ((input.map(clone): any): T);\n } else if (typeof input === 'object' && input) {\n return ((mapObject(input, clone): any): T);\n } else {\n return input;\n }\n}\n\n/**\n * Maps a value from a range between [min, max] to the range [outMin, outMax]\n *\n * @private\n */\nexport function mapValue(value: number, min: number, max: number, outMin: number, outMax: number): number {\n return clamp((value - min) / (max - min) * (outMax - outMin) + outMin, outMin, outMax);\n}\n\n/**\n * Check if two arrays have at least one common element.\n *\n * @private\n */\nexport function arraysIntersect(a: Array, b: Array): boolean {\n for (let l = 0; l < a.length; l++) {\n if (b.indexOf(a[l]) >= 0) return true;\n }\n return false;\n}\n\n/**\n * Print a warning message to the console and ensure duplicate warning messages\n * are not printed.\n *\n * @private\n */\nconst warnOnceHistory: {[key: string]: boolean} = {};\n\nexport function warnOnce(message: string): void {\n if (!warnOnceHistory[message]) {\n // console isn't defined in some WebWorkers, see #2558\n if (typeof console !== \"undefined\") console.warn(message);\n warnOnceHistory[message] = true;\n }\n}\n\n/**\n * Indicates if the provided Points are in a counter clockwise (true) or clockwise (false) order\n *\n * @private\n * @returns true for a counter clockwise set of points\n */\n// http://bryceboe.com/2006/10/23/line-segment-intersection-algorithm/\nexport function isCounterClockwise(a: Point, b: Point, c: Point): boolean {\n return (c.y - a.y) * (b.x - a.x) > (b.y - a.y) * (c.x - a.x);\n}\n\n/**\n * Returns the signed area for the polygon ring. Postive areas are exterior rings and\n * have a clockwise winding. Negative areas are interior rings and have a counter clockwise\n * ordering.\n *\n * @private\n * @param ring Exterior or interior ring\n */\nexport function calculateSignedArea(ring: Array): number {\n let sum = 0;\n for (let i = 0, len = ring.length, j = len - 1, p1, p2; i < len; j = i++) {\n p1 = ring[i];\n p2 = ring[j];\n sum += (p2.x - p1.x) * (p1.y + p2.y);\n }\n return sum;\n}\n\n/* global self, WorkerGlobalScope */\n/**\n * Returns true if run in the web-worker context.\n *\n * @private\n * @returns {boolean}\n */\nexport function isWorker(): boolean {\n return typeof WorkerGlobalScope !== 'undefined' && typeof self !== 'undefined' &&\n self instanceof WorkerGlobalScope;\n}\n\n/**\n * Parses data from 'Cache-Control' headers.\n *\n * @private\n * @param cacheControl Value of 'Cache-Control' header\n * @return object containing parsed header info.\n */\n\nexport function parseCacheControl(cacheControl: string): Object {\n // Taken from [Wreck](https://github.com/hapijs/wreck)\n const re = /(?:^|(?:\\s*\\,\\s*))([^\\x00-\\x20\\(\\)<>@\\,;\\:\\\\\"\\/\\[\\]\\?\\=\\{\\}\\x7F]+)(?:\\=(?:([^\\x00-\\x20\\(\\)<>@\\,;\\:\\\\\"\\/\\[\\]\\?\\=\\{\\}\\x7F]+)|(?:\\\"((?:[^\"\\\\]|\\\\.)*)\\\")))?/g;\n\n const header = {};\n cacheControl.replace(re, ($0, $1, $2, $3) => {\n const value = $2 || $3;\n header[$1] = value ? value.toLowerCase() : true;\n return '';\n });\n\n if (header['max-age']) {\n const maxAge = parseInt(header['max-age'], 10);\n if (isNaN(maxAge)) delete header['max-age'];\n else header['max-age'] = maxAge;\n }\n\n return header;\n}\n\nlet _isSafari = null;\n\nexport function _resetSafariCheckForTest() {\n _isSafari = null;\n}\n\n/**\n * Returns true when run in WebKit derived browsers.\n * This is used as a workaround for a memory leak in Safari caused by using Transferable objects to\n * transfer data between WebWorkers and the main thread.\n * https://github.com/mapbox/mapbox-gl-js/issues/8771\n *\n * This should be removed once the underlying Safari issue is fixed.\n *\n * @private\n * @param scope {WindowOrWorkerGlobalScope} Since this function is used both on the main thread and WebWorker context,\n * let the calling scope pass in the global scope object.\n * @returns {boolean}\n */\nexport function isSafari(scope: any): boolean {\n if (_isSafari == null) {\n const userAgent = scope.navigator ? scope.navigator.userAgent : null;\n _isSafari = !!scope.safari ||\n !!(userAgent && (/\\b(iPad|iPhone|iPod)\\b/.test(userAgent) || (!!userAgent.match('Safari') && !userAgent.match('Chrome'))));\n }\n return _isSafari;\n}\n\nexport function isSafariWithAntialiasingBug(scope: any): ?boolean {\n const userAgent = scope.navigator ? scope.navigator.userAgent : null;\n if (!isSafari(scope)) return false;\n // 15.4 is known to be buggy.\n // 15.5 may or may not include the fix. Mark it as buggy to be on the safe side.\n return userAgent && (userAgent.match('Version/15.4') || userAgent.match('Version/15.5') || userAgent.match(/CPU (OS|iPhone OS) (15_4|15_5) like Mac OS X/));\n}\n\nexport function isFullscreen(): boolean {\n return !!window.document.fullscreenElement || !!window.document.webkitFullscreenElement;\n}\n\nexport function storageAvailable(type: string): boolean {\n try {\n const storage = window[type];\n storage.setItem('_mapbox_test_', 1);\n storage.removeItem('_mapbox_test_');\n return true;\n } catch (e) {\n return false;\n }\n}\n\n// The following methods are from https://developer.mozilla.org/en-US/docs/Web/API/WindowBase64/Base64_encoding_and_decoding#The_Unicode_Problem\n//Unicode compliant base64 encoder for strings\nexport function b64EncodeUnicode(str: string): string {\n return window.btoa(\n encodeURIComponent(str).replace(/%([0-9A-F]{2})/g,\n (match, p1) => {\n return String.fromCharCode(Number('0x' + p1)); //eslint-disable-line\n }\n )\n );\n}\n\n// Unicode compliant decoder for base64-encoded strings\nexport function b64DecodeUnicode(str: string): string {\n return decodeURIComponent(window.atob(str).split('').map((c) => {\n return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2); //eslint-disable-line\n }).join(''));\n}\n\nexport function getColumn(matrix: Mat4, col: number): Vec4 {\n return [matrix[col * 4], matrix[col * 4 + 1], matrix[col * 4 + 2], matrix[col * 4 + 3]];\n}\n\nexport function setColumn(matrix: Mat4, col: number, values: Vec4) {\n matrix[col * 4 + 0] = values[0];\n matrix[col * 4 + 1] = values[1];\n matrix[col * 4 + 2] = values[2];\n matrix[col * 4 + 3] = values[3];\n}\n","// @flow\n\nimport window from '../util/window.js';\nconst performance = window.performance;\n\nperformance.mark('library-evaluate');\n\nimport {isWorker} from '../util/util.js';\nimport type {RequestParameters} from '../util/ajax.js';\n\nexport type PerformanceMetrics = {\n loadTime: number,\n fullLoadTime: number,\n percentDroppedFrames: number,\n parseTile: number,\n parseTile1: number,\n parseTile2: number,\n workerTask: number,\n workerInitialization: number,\n workerEvaluateScript: number,\n workerIdle: number,\n workerIdlePercent: number,\n placementTime: number,\n timelines: Array\n};\n\nexport type PerformanceMark = {mark: string, name: string};\n\nexport const PerformanceMarkers = {\n create: 'create',\n load: 'load',\n fullLoad: 'fullLoad'\n};\n\nlet fullLoadFinished = false;\nlet placementTime = 0;\n\nexport const PerformanceUtils = {\n mark(marker: $Keys) {\n performance.mark(marker);\n\n if (marker === PerformanceMarkers.fullLoad) {\n fullLoadFinished = true;\n }\n },\n measure(name: string, begin?: string, end?: string) {\n performance.measure(name, begin, end);\n },\n beginMeasure(name: string): PerformanceMark {\n const mark = name;\n performance.mark(mark);\n return {\n mark,\n name\n };\n },\n endMeasure(m: PerformanceMark) {\n performance.measure(m.name, m.mark);\n },\n recordPlacementTime(time: number) {\n // Ignore placementTimes during loading\n if (!fullLoadFinished) {\n return;\n }\n\n placementTime += time;\n },\n frame(timestamp: number, isRenderFrame: boolean) {\n performance.mark('frame', {\n detail: {\n timestamp,\n isRenderFrame\n }\n });\n },\n clearMetrics() {\n placementTime = 0;\n fullLoadFinished = false;\n\n performance.clearMeasures('loadTime');\n performance.clearMeasures('fullLoadTime');\n\n for (const marker in PerformanceMarkers) {\n performance.clearMarks(PerformanceMarkers[marker]);\n }\n },\n\n getPerformanceMetrics(): PerformanceMetrics {\n const metrics = {};\n\n performance.measure('loadTime', PerformanceMarkers.create, PerformanceMarkers.load);\n performance.measure('fullLoadTime', PerformanceMarkers.create, PerformanceMarkers.fullLoad);\n\n const measures = performance.getEntriesByType('measure');\n for (const measure of measures) {\n metrics[measure.name] = (metrics[measure.name] || 0) + measure.duration;\n }\n\n metrics.placementTime = placementTime;\n\n return metrics;\n },\n\n getWorkerPerformanceMetrics(): { timeOrigin: string, entries: Array, scope: string } {\n const entries = performance.getEntries().map(entry => {\n const result = entry.toJSON();\n if (entry.detail) {\n Object.assign(result, {\n detail: entry.detail\n });\n }\n return result;\n });\n return {\n scope: isWorker() ? 'Worker' : 'Window',\n timeOrigin: performance.timeOrigin,\n entries\n };\n }\n};\n\nexport function getPerformanceMeasurement(request: ?RequestParameters): Array {\n const url = request ? request.url.toString() : undefined;\n return performance.getEntriesByName(url);\n}\n\nexport default performance;\n","// @flow strict\n\nimport window from './window.js';\nimport type {Cancelable} from '../types/cancelable.js';\n\nlet linkEl;\n\nlet reducedMotionQuery: MediaQueryList;\n\nlet stubTime;\n\nlet canvas;\n\n/**\n * @private\n */\nconst exported = {\n /**\n * Returns either performance.now() or a value set by setNow.\n * @returns {number} Time value in milliseconds.\n */\n now(): number {\n if (stubTime !== undefined) {\n return stubTime;\n }\n return window.performance.now();\n },\n setNow(time: number) {\n stubTime = time;\n },\n\n restoreNow() {\n stubTime = undefined;\n },\n\n frame(fn: (paintStartTimestamp: number) => void): Cancelable {\n const frame = window.requestAnimationFrame(fn);\n return {cancel: () => window.cancelAnimationFrame(frame)};\n },\n\n getImageData(img: CanvasImageSource, padding?: number = 0): ImageData {\n const {width, height} = img;\n\n if (!canvas) {\n canvas = window.document.createElement('canvas');\n }\n\n const context = canvas.getContext('2d');\n if (!context) {\n throw new Error('failed to create canvas 2d context');\n }\n\n if (width > canvas.width || height > canvas.height) {\n canvas.width = width;\n canvas.height = height;\n }\n\n context.clearRect(-padding, -padding, width + 2 * padding, height + 2 * padding);\n context.drawImage(img, 0, 0, width, height);\n return context.getImageData(-padding, -padding, width + 2 * padding, height + 2 * padding);\n },\n\n resolveURL(path: string): string {\n if (!linkEl) linkEl = window.document.createElement('a');\n linkEl.href = path;\n return linkEl.href;\n },\n\n get devicePixelRatio(): number { return window.devicePixelRatio; },\n get prefersReducedMotion(): boolean {\n if (!window.matchMedia) return false;\n // Lazily initialize media query.\n if (reducedMotionQuery == null) {\n reducedMotionQuery = window.matchMedia('(prefers-reduced-motion: reduce)');\n }\n return reducedMotionQuery.matches;\n },\n};\n\nexport default exported;\n","// @flow strict\n\ntype Config = {|\n API_URL: string,\n API_URL_REGEX: RegExp,\n EVENTS_URL: ?string,\n SESSION_PATH: string,\n FEEDBACK_URL: string,\n REQUIRE_ACCESS_TOKEN: boolean,\n TILE_URL_VERSION: string,\n RASTER_URL_PREFIX: string,\n ACCESS_TOKEN: ?string,\n MAX_PARALLEL_IMAGE_REQUESTS: number\n|};\n\nlet mapboxHTTPURLRegex;\n\nconst config: Config = {\n API_URL: 'https://api.mapbox.com',\n get API_URL_REGEX () {\n if (mapboxHTTPURLRegex == null) {\n const prodMapboxHTTPURLRegex = /^((https?:)?\\/\\/)?([^\\/]+\\.)?mapbox\\.c(n|om)(\\/|\\?|$)/i;\n try {\n mapboxHTTPURLRegex = (process.env.API_URL_REGEX != null) ? new RegExp(process.env.API_URL_REGEX) : prodMapboxHTTPURLRegex;\n } catch (e) {\n mapboxHTTPURLRegex = prodMapboxHTTPURLRegex;\n }\n }\n\n return mapboxHTTPURLRegex;\n },\n get EVENTS_URL() {\n if (!this.API_URL) { return null; }\n if (this.API_URL.indexOf('https://api.mapbox.cn') === 0) {\n return 'https://events.mapbox.cn/events/v2';\n } else if (this.API_URL.indexOf('https://api.mapbox.com') === 0) {\n return 'https://events.mapbox.com/events/v2';\n } else {\n return null;\n }\n },\n SESSION_PATH: '/map-sessions/v1',\n FEEDBACK_URL: 'https://apps.mapbox.com/feedback',\n TILE_URL_VERSION: 'v4',\n RASTER_URL_PREFIX: 'raster/v1',\n REQUIRE_ACCESS_TOKEN: true,\n ACCESS_TOKEN: null,\n MAX_PARALLEL_IMAGE_REQUESTS: 16\n};\n\nexport default config;\n","// @flow strict\n\nimport window from './window.js';\n\nconst exported = {\n supported: false,\n testSupport\n};\n\nexport default exported;\n\nlet glForTesting;\nlet webpCheckComplete = false;\nlet webpImgTest;\nlet webpImgTestOnloadComplete = false;\n\nif (window.document) {\n webpImgTest = window.document.createElement('img');\n webpImgTest.onload = function() {\n if (glForTesting) testWebpTextureUpload(glForTesting);\n glForTesting = null;\n webpImgTestOnloadComplete = true;\n };\n webpImgTest.onerror = function() {\n webpCheckComplete = true;\n glForTesting = null;\n };\n webpImgTest.src = 'data:image/webp;base64,UklGRh4AAABXRUJQVlA4TBEAAAAvAQAAAAfQ//73v/+BiOh/AAA=';\n}\n\nfunction testSupport(gl: WebGLRenderingContext) {\n if (webpCheckComplete || !webpImgTest) return;\n\n // HTMLImageElement.complete is set when an image is done loading it's source\n // regardless of whether the load was successful or not.\n // It's possible for an error to set HTMLImageElement.complete to true which would trigger\n // testWebpTextureUpload and mistakenly set exported.supported to true in browsers which don't support webp\n // To avoid this, we set a flag in the image's onload handler and only call testWebpTextureUpload\n // after a successful image load event.\n if (webpImgTestOnloadComplete) {\n testWebpTextureUpload(gl);\n } else {\n glForTesting = gl;\n\n }\n}\n\nfunction testWebpTextureUpload(gl: WebGLRenderingContext) {\n // Edge 18 supports WebP but not uploading a WebP image to a gl texture\n // Test support for this before allowing WebP images.\n // https://github.com/mapbox/mapbox-gl-js/issues/7671\n const texture = gl.createTexture();\n gl.bindTexture(gl.TEXTURE_2D, texture);\n\n try {\n gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, webpImgTest);\n\n // The error does not get triggered in Edge if the context is lost\n if (gl.isContextLost()) return;\n\n exported.supported = true;\n } catch (e) {\n // Catch \"Unspecified Error.\" in Edge 18.\n }\n\n gl.deleteTexture(texture);\n\n webpCheckComplete = true;\n}\n","// @flow\n\n/***** START WARNING REMOVAL OR MODIFICATION OF THE\n* FOLLOWING CODE VIOLATES THE MAPBOX TERMS OF SERVICE ******\n* The following code is used to access Mapbox's APIs. Removal or modification\n* of this code can result in higher fees and/or\n* termination of your account with Mapbox.\n*\n* Under the Mapbox Terms of Service, you may not use this code to access Mapbox\n* Mapping APIs other than through Mapbox SDKs.\n*\n* The Mapping APIs documentation is available at https://docs.mapbox.com/api/maps/#maps\n* and the Mapbox Terms of Service are available at https://www.mapbox.com/tos/\n******************************************************************************/\n\ntype SkuTokenObject = {|\n token: string,\n tokenExpiresAt: number\n|};\n\nconst SKU_ID = '01';\n\nfunction createSkuToken(): SkuTokenObject {\n // SKU_ID and TOKEN_VERSION are specified by an internal schema and should not change\n const TOKEN_VERSION = '1';\n const base62chars = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';\n // sessionRandomizer is a randomized 10-digit base-62 number\n let sessionRandomizer = '';\n for (let i = 0; i < 10; i++) {\n sessionRandomizer += base62chars[Math.floor(Math.random() * 62)];\n }\n const expiration = 12 * 60 * 60 * 1000; // 12 hours\n const token = [TOKEN_VERSION, SKU_ID, sessionRandomizer].join('');\n const tokenExpiresAt = Date.now() + expiration;\n\n return {token, tokenExpiresAt};\n}\n\nexport {createSkuToken, SKU_ID};\n\n/***** END WARNING - REMOVAL OR MODIFICATION OF THE\nPRECEDING CODE VIOLATES THE MAPBOX TERMS OF SERVICE ******/\n","// @flow\n\n/***** START WARNING REMOVAL OR MODIFICATION OF THE\n* FOLLOWING CODE VIOLATES THE MAPBOX TERMS OF SERVICE ******\n* The following code is used to access Mapbox's APIs. Removal or modification\n* of this code can result in higher fees and/or\n* termination of your account with Mapbox.\n*\n* Under the Mapbox Terms of Service, you may not use this code to access Mapbox\n* Mapping APIs other than through Mapbox SDKs.\n*\n* The Mapping APIs documentation is available at https://docs.mapbox.com/api/maps/#maps\n* and the Mapbox Terms of Service are available at https://www.mapbox.com/tos/\n******************************************************************************/\n\nimport config from './config.js';\nimport window from './window.js';\nimport webpSupported from './webp_supported.js';\nimport {createSkuToken, SKU_ID} from './sku_token.js';\nimport {version as sdkVersion} from '../../package.json';\nimport {uuid, validateUuid, storageAvailable, b64DecodeUnicode, b64EncodeUnicode, warnOnce, extend} from './util.js';\nimport {postData, ResourceType, getData} from './ajax.js';\n\nimport type {RequestParameters} from './ajax.js';\nimport type {Cancelable} from '../types/cancelable.js';\nimport type {TileJSON} from '../types/tilejson.js';\n\ntype ResourceTypeEnum = $Keys;\nexport type RequestTransformFunction = (url: string, resourceType?: ResourceTypeEnum) => RequestParameters;\n\ntype UrlObject = {|\n protocol: string,\n authority: string,\n path: string,\n params: Array\n|};\n\ntype EventCallback = (err: ?Error) => void;\n\nexport const AUTH_ERR_MSG: string = 'NO_ACCESS_TOKEN';\n\nexport class RequestManager {\n _skuToken: string;\n _skuTokenExpiresAt: number;\n _transformRequestFn: ?RequestTransformFunction;\n _customAccessToken: ?string;\n _silenceAuthErrors: boolean;\n\n constructor(transformRequestFn?: RequestTransformFunction, customAccessToken?: string, silenceAuthErrors: ?boolean) {\n this._transformRequestFn = transformRequestFn;\n this._customAccessToken = customAccessToken;\n this._silenceAuthErrors = !!silenceAuthErrors;\n this._createSkuToken();\n }\n\n _createSkuToken() {\n const skuToken = createSkuToken();\n this._skuToken = skuToken.token;\n this._skuTokenExpiresAt = skuToken.tokenExpiresAt;\n }\n\n _isSkuTokenExpired(): boolean {\n return Date.now() > this._skuTokenExpiresAt;\n }\n\n transformRequest(url: string, type: ResourceTypeEnum): RequestParameters {\n if (this._transformRequestFn) {\n return this._transformRequestFn(url, type) || {url};\n }\n\n return {url};\n }\n\n normalizeStyleURL(url: string, accessToken?: string): string {\n if (!isMapboxURL(url)) return url;\n const urlObject = parseUrl(url);\n urlObject.path = `/styles/v1${urlObject.path}`;\n return this._makeAPIURL(urlObject, this._customAccessToken || accessToken);\n }\n\n normalizeGlyphsURL(url: string, accessToken?: string): string {\n if (!isMapboxURL(url)) return url;\n const urlObject = parseUrl(url);\n urlObject.path = `/fonts/v1${urlObject.path}`;\n return this._makeAPIURL(urlObject, this._customAccessToken || accessToken);\n }\n\n normalizeSourceURL(url: string, accessToken?: ?string, language?: ?string, worldview?: ?string): string {\n if (!isMapboxURL(url)) return url;\n const urlObject = parseUrl(url);\n urlObject.path = `/v4/${urlObject.authority}.json`;\n // TileJSON requests need a secure flag appended to their URLs so\n // that the server knows to send SSL-ified resource references.\n urlObject.params.push('secure');\n if (language) {\n urlObject.params.push(`language=${language}`);\n }\n if (worldview) {\n urlObject.params.push(`worldview=${worldview}`);\n }\n\n return this._makeAPIURL(urlObject, this._customAccessToken || accessToken);\n }\n\n normalizeSpriteURL(url: string, format: string, extension: string, accessToken?: string): string {\n const urlObject = parseUrl(url);\n if (!isMapboxURL(url)) {\n urlObject.path += `${format}${extension}`;\n return formatUrl(urlObject);\n }\n urlObject.path = `/styles/v1${urlObject.path}/sprite${format}${extension}`;\n return this._makeAPIURL(urlObject, this._customAccessToken || accessToken);\n }\n\n normalizeTileURL(tileURL: string, use2x?: boolean, rasterTileSize?: number): string {\n if (this._isSkuTokenExpired()) {\n this._createSkuToken();\n }\n\n if (tileURL && !isMapboxURL(tileURL)) return tileURL;\n\n const urlObject = parseUrl(tileURL);\n const imageExtensionRe = /(\\.(png|jpg)\\d*)(?=$)/;\n const extension = webpSupported.supported ? '.webp' : '$1';\n\n // The v4 mapbox tile API supports 512x512 image tiles but they must be requested as '@2x' tiles.\n const use2xAs512 = rasterTileSize && urlObject.authority !== 'raster' && rasterTileSize === 512;\n\n const suffix = use2x || use2xAs512 ? '@2x' : '';\n urlObject.path = urlObject.path.replace(imageExtensionRe, `${suffix}${extension}`);\n\n if (urlObject.authority === 'raster') {\n urlObject.path = `/${config.RASTER_URL_PREFIX}${urlObject.path}`;\n } else {\n const tileURLAPIPrefixRe = /^.+\\/v4\\//;\n urlObject.path = urlObject.path.replace(tileURLAPIPrefixRe, '/');\n urlObject.path = `/${config.TILE_URL_VERSION}${urlObject.path}`;\n }\n\n const accessToken = this._customAccessToken || getAccessToken(urlObject.params) || config.ACCESS_TOKEN;\n if (config.REQUIRE_ACCESS_TOKEN && accessToken && this._skuToken) {\n urlObject.params.push(`sku=${this._skuToken}`);\n }\n\n return this._makeAPIURL(urlObject, accessToken);\n }\n\n canonicalizeTileURL(url: string, removeAccessToken: boolean): string {\n // matches any file extension specified by a dot and one or more alphanumeric characters\n const extensionRe = /\\.[\\w]+$/;\n\n const urlObject = parseUrl(url);\n // Make sure that we are dealing with a valid Mapbox tile URL.\n // Has to begin with /v4/ or /raster/v1, with a valid filename + extension\n if (!urlObject.path.match(/^(\\/v4\\/|\\/raster\\/v1\\/)/) || !urlObject.path.match(extensionRe)) {\n // Not a proper Mapbox tile URL.\n return url;\n }\n // Reassemble the canonical URL from the parts we've parsed before.\n let result = \"mapbox://\";\n if (urlObject.path.match(/^\\/raster\\/v1\\//)) {\n // If the tile url has /raster/v1/, make the final URL mapbox://raster/....\n const rasterPrefix = `/${config.RASTER_URL_PREFIX}/`;\n result += `raster/${urlObject.path.replace(rasterPrefix, '')}`;\n } else {\n const tilesPrefix = `/${config.TILE_URL_VERSION}/`;\n result += `tiles/${urlObject.path.replace(tilesPrefix, '')}`;\n }\n\n // Append the query string, minus the access token parameter.\n let params = urlObject.params;\n if (removeAccessToken) {\n params = params.filter(p => !p.match(/^access_token=/));\n }\n if (params.length) result += `?${params.join('&')}`;\n return result;\n }\n\n canonicalizeTileset(tileJSON: TileJSON, sourceURL?: string): Array {\n const removeAccessToken = sourceURL ? isMapboxURL(sourceURL) : false;\n const canonical = [];\n for (const url of tileJSON.tiles || []) {\n if (isMapboxHTTPURL(url)) {\n canonical.push(this.canonicalizeTileURL(url, removeAccessToken));\n } else {\n canonical.push(url);\n }\n }\n return canonical;\n }\n\n _makeAPIURL(urlObject: UrlObject, accessToken: string | null | void): string {\n const help = 'See https://www.mapbox.com/api-documentation/#access-tokens-and-token-scopes';\n const apiUrlObject = parseUrl(config.API_URL);\n urlObject.protocol = apiUrlObject.protocol;\n urlObject.authority = apiUrlObject.authority;\n\n if (urlObject.protocol === 'http') {\n const i = urlObject.params.indexOf('secure');\n if (i >= 0) urlObject.params.splice(i, 1);\n }\n\n if (apiUrlObject.path !== '/') {\n urlObject.path = `${apiUrlObject.path}${urlObject.path}`;\n }\n\n if (!config.REQUIRE_ACCESS_TOKEN) return formatUrl(urlObject);\n\n accessToken = accessToken || config.ACCESS_TOKEN;\n if (!this._silenceAuthErrors) {\n if (!accessToken)\n throw new Error(`An API access token is required to use Mapbox GL. ${help}`);\n if (accessToken[0] === 's')\n throw new Error(`Use a public access token (pk.*) with Mapbox GL, not a secret access token (sk.*). ${help}`);\n }\n\n urlObject.params = urlObject.params.filter((d) => d.indexOf('access_token') === -1);\n urlObject.params.push(`access_token=${accessToken || ''}`);\n return formatUrl(urlObject);\n }\n}\n\nexport function isMapboxURL(url: string): boolean {\n return url.indexOf('mapbox:') === 0;\n}\n\nexport function isMapboxHTTPURL(url: string): boolean {\n return config.API_URL_REGEX.test(url);\n}\n\nexport function hasCacheDefeatingSku(url: string): boolean {\n return url.indexOf('sku=') > 0 && isMapboxHTTPURL(url);\n}\n\nfunction getAccessToken(params: Array): string | null {\n for (const param of params) {\n const match = param.match(/^access_token=(.*)$/);\n if (match) {\n return match[1];\n }\n }\n return null;\n}\n\nconst urlRe = /^(\\w+):\\/\\/([^/?]*)(\\/[^?]+)?\\??(.+)?/;\n\nfunction parseUrl(url: string): UrlObject {\n const parts = url.match(urlRe);\n if (!parts) {\n throw new Error('Unable to parse URL object');\n }\n return {\n protocol: parts[1],\n authority: parts[2],\n path: parts[3] || '/',\n params: parts[4] ? parts[4].split('&') : []\n };\n}\n\nfunction formatUrl(obj: UrlObject): string {\n const params = obj.params.length ? `?${obj.params.join('&')}` : '';\n return `${obj.protocol}://${obj.authority}${obj.path}${params}`;\n}\n\nconst telemEventKey = 'mapbox.eventData';\n\nfunction parseAccessToken(accessToken: ?string) {\n if (!accessToken) {\n return null;\n }\n\n const parts = accessToken.split('.');\n if (!parts || parts.length !== 3) {\n return null;\n }\n\n try {\n const jsonData = JSON.parse(b64DecodeUnicode(parts[1]));\n return jsonData;\n } catch (e) {\n return null;\n }\n}\n\ntype TelemetryEventType = 'appUserTurnstile' | 'map.load' | 'map.auth';\n\nclass TelemetryEvent {\n eventData: any;\n anonId: ?string;\n queue: Array;\n type: TelemetryEventType;\n pendingRequest: ?Cancelable;\n _customAccessToken: ?string;\n\n constructor(type: TelemetryEventType) {\n this.type = type;\n this.anonId = null;\n this.eventData = {};\n this.queue = [];\n this.pendingRequest = null;\n }\n\n getStorageKey(domain: ?string): string {\n const tokenData = parseAccessToken(config.ACCESS_TOKEN);\n let u = '';\n if (tokenData && tokenData['u']) {\n u = b64EncodeUnicode(tokenData['u']);\n } else {\n u = config.ACCESS_TOKEN || '';\n }\n return domain ?\n `${telemEventKey}.${domain}:${u}` :\n `${telemEventKey}:${u}`;\n }\n\n fetchEventData() {\n const isLocalStorageAvailable = storageAvailable('localStorage');\n const storageKey = this.getStorageKey();\n const uuidKey = this.getStorageKey('uuid');\n\n if (isLocalStorageAvailable) {\n //Retrieve cached data\n try {\n const data = window.localStorage.getItem(storageKey);\n if (data) {\n this.eventData = JSON.parse(data);\n }\n\n const uuid = window.localStorage.getItem(uuidKey);\n if (uuid) this.anonId = uuid;\n } catch (e) {\n warnOnce('Unable to read from LocalStorage');\n }\n }\n }\n\n saveEventData() {\n const isLocalStorageAvailable = storageAvailable('localStorage');\n const storageKey = this.getStorageKey();\n const uuidKey = this.getStorageKey('uuid');\n if (isLocalStorageAvailable) {\n try {\n window.localStorage.setItem(uuidKey, this.anonId);\n if (Object.keys(this.eventData).length >= 1) {\n window.localStorage.setItem(storageKey, JSON.stringify(this.eventData));\n }\n } catch (e) {\n warnOnce('Unable to write to LocalStorage');\n }\n }\n\n }\n\n processRequests(_: ?string) {}\n\n /*\n * If any event data should be persisted after the POST request, the callback should modify eventData`\n * to the values that should be saved. For this reason, the callback should be invoked prior to the call\n * to TelemetryEvent#saveData\n */\n postEvent(timestamp: number, additionalPayload: {[_: string]: any}, callback: EventCallback, customAccessToken?: ?string) {\n if (!config.EVENTS_URL) return;\n const eventsUrlObject: UrlObject = parseUrl(config.EVENTS_URL);\n eventsUrlObject.params.push(`access_token=${customAccessToken || config.ACCESS_TOKEN || ''}`);\n\n const payload: Object = {\n event: this.type,\n created: new Date(timestamp).toISOString(),\n sdkIdentifier: 'mapbox-gl-js',\n sdkVersion,\n skuId: SKU_ID,\n userId: this.anonId\n };\n\n const finalPayload = additionalPayload ? extend(payload, additionalPayload) : payload;\n const request: RequestParameters = {\n url: formatUrl(eventsUrlObject),\n headers: {\n 'Content-Type': 'text/plain' //Skip the pre-flight OPTIONS request\n },\n body: JSON.stringify([finalPayload])\n };\n\n this.pendingRequest = postData(request, (error) => {\n this.pendingRequest = null;\n callback(error);\n this.saveEventData();\n this.processRequests(customAccessToken);\n });\n }\n\n queueRequest(event: number | {id: number, timestamp: number}, customAccessToken?: ?string) {\n this.queue.push(event);\n this.processRequests(customAccessToken);\n }\n}\n\nexport class MapLoadEvent extends TelemetryEvent {\n +success: {[_: number]: boolean};\n skuToken: string;\n errorCb: EventCallback;\n\n constructor() {\n super('map.load');\n this.success = {};\n this.skuToken = '';\n }\n\n postMapLoadEvent(mapId: number, skuToken: string, customAccessToken: ?string, callback: EventCallback) {\n this.skuToken = skuToken;\n this.errorCb = callback;\n\n if (config.EVENTS_URL) {\n if (customAccessToken || config.ACCESS_TOKEN) {\n this.queueRequest({id: mapId, timestamp: Date.now()}, customAccessToken);\n } else {\n this.errorCb(new Error(AUTH_ERR_MSG));\n }\n }\n }\n\n processRequests(customAccessToken?: ?string) {\n if (this.pendingRequest || this.queue.length === 0) return;\n const {id, timestamp} = this.queue.shift();\n\n // Only one load event should fire per map\n if (id && this.success[id]) return;\n\n if (!this.anonId) {\n this.fetchEventData();\n }\n\n if (!validateUuid(this.anonId)) {\n this.anonId = uuid();\n }\n\n this.postEvent(timestamp, {skuToken: this.skuToken}, (err) => {\n if (err) {\n this.errorCb(err);\n } else {\n if (id) this.success[id] = true;\n }\n\n }, customAccessToken);\n }\n}\n\nexport class MapSessionAPI extends TelemetryEvent {\n +success: {[_: number]: boolean};\n skuToken: string;\n errorCb: EventCallback;\n\n constructor() {\n super('map.auth');\n this.success = {};\n this.skuToken = '';\n }\n\n getSession(timestamp: number, token: string, callback: EventCallback, customAccessToken?: ?string) {\n if (!config.API_URL || !config.SESSION_PATH) return;\n const authUrlObject: UrlObject = parseUrl(config.API_URL + config.SESSION_PATH);\n authUrlObject.params.push(`sku=${token || ''}`);\n authUrlObject.params.push(`access_token=${customAccessToken || config.ACCESS_TOKEN || ''}`);\n\n const request: RequestParameters = {\n url: formatUrl(authUrlObject),\n headers: {\n 'Content-Type': 'text/plain', //Skip the pre-flight OPTIONS request\n }\n };\n\n this.pendingRequest = getData(request, (error) => {\n this.pendingRequest = null;\n callback(error);\n this.saveEventData();\n this.processRequests(customAccessToken);\n });\n }\n\n getSessionAPI(mapId: number, skuToken: string, customAccessToken: ?string, callback: EventCallback) {\n this.skuToken = skuToken;\n this.errorCb = callback;\n\n if (config.SESSION_PATH && config.API_URL) {\n if (customAccessToken || config.ACCESS_TOKEN) {\n this.queueRequest({id: mapId, timestamp: Date.now()}, customAccessToken);\n } else {\n this.errorCb(new Error(AUTH_ERR_MSG));\n }\n }\n }\n\n processRequests(customAccessToken?: ?string) {\n if (this.pendingRequest || this.queue.length === 0) return;\n const {id, timestamp} = this.queue.shift();\n\n // Only one load event should fire per map\n if (id && this.success[id]) return;\n\n this.getSession(timestamp, this.skuToken, (err) => {\n if (err) {\n this.errorCb(err);\n } else {\n if (id) this.success[id] = true;\n }\n }, customAccessToken);\n }\n}\n\nexport class TurnstileEvent extends TelemetryEvent {\n constructor(customAccessToken?: ?string) {\n super('appUserTurnstile');\n this._customAccessToken = customAccessToken;\n }\n\n postTurnstileEvent(tileUrls: Array, customAccessToken?: ?string) {\n //Enabled only when Mapbox Access Token is set and a source uses\n // mapbox tiles.\n if (config.EVENTS_URL &&\n config.ACCESS_TOKEN &&\n Array.isArray(tileUrls) &&\n tileUrls.some(url => isMapboxURL(url) || isMapboxHTTPURL(url))) {\n this.queueRequest(Date.now(), customAccessToken);\n }\n }\n\n processRequests(customAccessToken?: ?string) {\n if (this.pendingRequest || this.queue.length === 0) {\n return;\n }\n\n if (!this.anonId || !this.eventData.lastSuccess || !this.eventData.tokenU) {\n //Retrieve cached data\n this.fetchEventData();\n }\n\n const tokenData = parseAccessToken(config.ACCESS_TOKEN);\n const tokenU = tokenData ? tokenData['u'] : config.ACCESS_TOKEN;\n //Reset event data cache if the access token owner changed.\n let dueForEvent = tokenU !== this.eventData.tokenU;\n\n if (!validateUuid(this.anonId)) {\n this.anonId = uuid();\n dueForEvent = true;\n }\n\n const nextUpdate = this.queue.shift();\n // Record turnstile event once per calendar day.\n if (this.eventData.lastSuccess) {\n const lastUpdate = new Date(this.eventData.lastSuccess);\n const nextDate = new Date(nextUpdate);\n const daysElapsed = (nextUpdate - this.eventData.lastSuccess) / (24 * 60 * 60 * 1000);\n dueForEvent = dueForEvent || daysElapsed >= 1 || daysElapsed < -1 || lastUpdate.getDate() !== nextDate.getDate();\n } else {\n dueForEvent = true;\n }\n\n if (!dueForEvent) {\n this.processRequests();\n return;\n }\n\n this.postEvent(nextUpdate, {\"enabled.telemetry\": false}, (err) => {\n if (!err) {\n this.eventData.lastSuccess = nextUpdate;\n this.eventData.tokenU = tokenU;\n }\n }, customAccessToken);\n }\n}\n\nconst turnstileEvent_ = new TurnstileEvent();\nexport const postTurnstileEvent: (tileUrls: Array, customAccessToken?: ?string) => void = turnstileEvent_.postTurnstileEvent.bind(turnstileEvent_);\n\nconst mapLoadEvent_ = new MapLoadEvent();\nexport const postMapLoadEvent: (number, string, ?string, EventCallback) => void = mapLoadEvent_.postMapLoadEvent.bind(mapLoadEvent_);\n\nconst mapSessionAPI_ = new MapSessionAPI();\nexport const getMapSessionAPI: (number, string, ?string, EventCallback) => void = mapSessionAPI_.getSessionAPI.bind(mapSessionAPI_);\n\nconst authenticatedMaps = new Set();\nexport function storeAuthState(gl: WebGLRenderingContext, state: boolean) {\n if (state) {\n authenticatedMaps.add(gl);\n } else {\n authenticatedMaps.delete(gl);\n }\n}\n\nexport function isMapAuthenticated(gl: WebGLRenderingContext): boolean {\n return authenticatedMaps.has(gl);\n}\n\nexport function removeAuthState(gl: WebGLRenderingContext) {\n authenticatedMaps.delete(gl);\n}\n\n/***** END WARNING - REMOVAL OR MODIFICATION OF THE\nPRECEDING CODE VIOLATES THE MAPBOX TERMS OF SERVICE ******/\n","// @flow\n\nimport {warnOnce, parseCacheControl} from './util.js';\nimport window from './window.js';\n\nimport type Dispatcher from './dispatcher.js';\n\nconst CACHE_NAME = 'mapbox-tiles';\nlet cacheLimit = 500; // 50MB / (100KB/tile) ~= 500 tiles\nlet cacheCheckThreshold = 50;\n\nconst MIN_TIME_UNTIL_EXPIRY = 1000 * 60 * 7; // 7 minutes. Skip caching tiles with a short enough max age.\n\nexport type ResponseOptions = {\n status: number,\n statusText: string,\n headers: Headers\n};\n\n// We're using a global shared cache object. Normally, requesting ad-hoc Cache objects is fine, but\n// Safari has a memory leak in which it fails to release memory when requesting keys() from a Cache\n// object. See https://bugs.webkit.org/show_bug.cgi?id=203991 for more information.\nlet sharedCaches = {};\n\nfunction getCacheName(url: string) {\n const queryParams = getQueryParameters(url);\n let language;\n let worldview;\n\n if (queryParams) {\n queryParams.forEach(param => {\n const entry = param.split('=');\n if (entry[0] === 'language') {\n language = entry[1];\n } else if (entry[0] === 'worldview') {\n worldview = entry[1];\n }\n });\n }\n\n let cacheName = CACHE_NAME;\n if (language) cacheName += `-${language}`;\n if (worldview) cacheName += `-${worldview}`;\n return cacheName;\n}\n\nfunction getCaches() {\n try {\n return window.caches;\n } catch (e) {\n //