diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000000..8e0cc69297 --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +*.json merge=json diff --git a/.github/workflows/deploy_pietervdvn.yml b/.github/workflows/deploy_pietervdvn.yml index 54b89561a8..dfa5644685 100644 --- a/.github/workflows/deploy_pietervdvn.yml +++ b/.github/workflows/deploy_pietervdvn.yml @@ -11,7 +11,7 @@ jobs: - name: Set up Node.js uses: actions/setup-node@v1.2.0 with: - node-version: '15' + node-version: '16' env: ACTIONS_ALLOW_UNSECURE_COMMANDS: 'true' diff --git a/.github/workflows/pull_request_check.yml b/.github/workflows/pull_request_check.yml deleted file mode 100644 index 27165f7761..0000000000 --- a/.github/workflows/pull_request_check.yml +++ /dev/null @@ -1,71 +0,0 @@ -name: Pull request check -on: - pull_request_target: - types: [ opened, edited, synchronize, ready_for_review, review_requested ] - -jobs: - build: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - - - uses: actions/checkout@v2 - - name: Set up Node.js - uses: actions/setup-node@v1.2.0 - env: - ACTIONS_ALLOW_UNSECURE_COMMANDS: 'true' - - - name: install deps - run: npm ci - - - name: create generated dir - run: mkdir ./assets/generated - - - name: create stub themes - run: "echo '{\"layers\": [], \"themes\": []}' > ./assets/generated/known_layers_and_themes.json" - - - name: generate assets - run: npm run generate:images - - - name: generate translations - run: npm run generate:translations - - - name: Compile license info - run: npm run generate:licenses - - - name: Compile and validate themes and layers - run: npm run validate:layeroverview - - - name: Validate license info - run: npm run validate:licenses - - - name: Set failure key - run: | - ls - if [[ -f "layer_report.txt" || -f "missing_licenses.txt" ]]; then - echo "Found a report..." - echo "VALIDATION_FAILED=true" >> $GITHUB_ENV - else - echo "VALIDATION_FAILED=false" >> $GITHUB_ENV - fi - - - name: Test variable - run: echo "${{ env.VALIDATION_FAILED }}" - - - name: Archive reports - uses: actions/upload-artifact@v2 - if: >- - env.VALIDATION_FAILED == 'true' - with: - name: reports - path: | - layer_report.txt - missing_licenses.txt - - - name: Comment PR - uses: allthatjazzleo/actions-pull-request-add-comment@master - if: >- - env.VALIDATION_FAILED == 'true' - with: - message: "cat layer_report.txt missing_licenses.txt" - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/theme_validation_and_deploy.yml b/.github/workflows/theme_validation_and_deploy.yml index 06c0b7fdf6..76ccd81598 100644 --- a/.github/workflows/theme_validation_and_deploy.yml +++ b/.github/workflows/theme_validation_and_deploy.yml @@ -13,7 +13,7 @@ jobs: - name: Set up Node.js uses: actions/setup-node@v1.2.0 with: - node-version: '15' + node-version: '16' env: ACTIONS_ALLOW_UNSECURE_COMMANDS: 'true' @@ -23,9 +23,6 @@ jobs: - name: create generated dir run: mkdir ./assets/generated - - name: create stub themes - run: "echo '{\"layers\": [], \"themes\": []}' > ./assets/generated/known_layers_and_themes.json" - - name: Prepare deploy run: npm run prepare-deploy diff --git a/.tool-versions b/.tool-versions index 8ac21489c2..132d0eed63 100644 --- a/.tool-versions +++ b/.tool-versions @@ -1 +1 @@ -nodejs 14.14.0 \ No newline at end of file +nodejs 16.9.1 \ No newline at end of file diff --git a/Customizations/AllKnownLayouts.ts b/Customizations/AllKnownLayouts.ts index 35cfb010cb..57dbbf0ba9 100644 --- a/Customizations/AllKnownLayouts.ts +++ b/Customizations/AllKnownLayouts.ts @@ -1,6 +1,7 @@ import AllKnownLayers from "./AllKnownLayers"; import * as known_themes from "../assets/generated/known_layers_and_themes.json" import LayoutConfig from "../Models/ThemeConfig/LayoutConfig"; +import LayerConfig from "../Models/ThemeConfig/LayerConfig"; export class AllKnownLayouts { @@ -8,6 +9,26 @@ export class AllKnownLayouts { public static allKnownLayouts: Map = AllKnownLayouts.AllLayouts(); public static layoutsList: LayoutConfig[] = AllKnownLayouts.GenerateOrderedList(AllKnownLayouts.allKnownLayouts); + public static AllPublicLayers(){ + const allLayers : LayerConfig[] = [] + const seendIds = new Set() + const publicLayouts = AllKnownLayouts.layoutsList.filter(l => !l.hideFromOverview) + for (const layout of publicLayouts) { + if(layout.hideFromOverview){ + continue + } + for (const layer of layout.layers) { + if(seendIds.has(layer.id)){ + continue + } + seendIds.add(layer.id) + allLayers.push(layer) + } + + } + return allLayers + } + private static GenerateOrderedList(allKnownLayouts: Map): LayoutConfig[] { const keys = ["personal", "cyclofix", "hailhydrant", "bookcases", "toilets", "aed"] const list = [] diff --git a/Docs/CalculatedTags.md b/Docs/CalculatedTags.md index 13f2d287e5..8d8973b7b7 100644 --- a/Docs/CalculatedTags.md +++ b/Docs/CalculatedTags.md @@ -1,91 +1,115 @@ -Metatags + + Metatags ========== Metatags are extra tags available, in order to display more data or to give better questions. -The are calculated automatically on every feature when the data arrives in the webbrowser. This document gives an -overview of the available metatags. +The are calculated automatically on every feature when the data arrives in the webbrowser. This document gives an overview of the available metatags. -**Hint:** when using metatags, add the [query parameter](URL_Parameters.md) `debug=true` to the URL. This will include a -box in the popup for features which shows all the properties of the object +**Hint:** when using metatags, add the [query parameter](URL_Parameters.md) `debug=true` to the URL. This will include a box in the popup for features which shows all the properties of the object -Metatags calculated by MapComplete + Metatags calculated by MapComplete ------------------------------------ -The following values are always calculated, by default, by MapComplete and are available automatically on all elements -in every theme +The following values are always calculated, by default, by MapComplete and are available automatically on all elements in every theme + + +### _lat, _lon + -### _lat, _lon The latitude and longitude of the point (or centerpoint in the case of a way/area) -### _surface, _surface:ha + +### _surface, _surface:ha + + The surface area of the feature, in square meters and in hectare. Not set on points and ways -### _length, _length:km -The total length of a feature in meters (and in kilometers, rounded to one decimal for '_length:km'). For a surface, the -length of the perimeter +### _length, _length:km -### Theme-defined keys -If 'units' is defined in the layoutConfig, then this metatagger will rewrite the specified keys to have the canonical -form (e.g. `1meter` will be rewritten to `1m`) -### _country +The total length of a feature in meters (and in kilometers, rounded to one decimal for '_length:km'). For a surface, the length of the perimeter + + +### Theme-defined keys + + + +If 'units' is defined in the layoutConfig, then this metatagger will rewrite the specified keys to have the canonical form (e.g. `1meter` will be rewritten to `1m`) + + +### _country + + The country code of the property (with latlon2country) -### _isOpen, _isOpen:description + +### _isOpen, _isOpen:description + + If 'opening_hours' is present, it will add the current state of the feature (being 'yes' or 'no') -### _width:needed, _width:needed:no_pedestrians, _width:difference -Legacy for a specific project calculating the needed width for safe traffic on a road. Only activated if 'width: -carriageway' is present +### _width:needed, _width:needed:no_pedestrians, _width:difference -### _direction:numerical, _direction:leftright -_direction:numerical is a normalized, numerical direction based on 'camera:direction' or on 'direction'; it is only -present if a valid direction is found (e.g. 38.5 or NE). _direction:leftright is either 'left' or 'right', which is -left-looking on the map or 'right-looking' on the map -### _now:date, _now:datetime, _loaded:date, _loaded:_datetime +Legacy for a specific project calculating the needed width for safe traffic on a road. Only activated if 'width:carriageway' is present + + +### _direction:numerical, _direction:leftright + + + +_direction:numerical is a normalized, numerical direction based on 'camera:direction' or on 'direction'; it is only present if a valid direction is found (e.g. 38.5 or NE). _direction:leftright is either 'left' or 'right', which is left-looking on the map or 'right-looking' on the map + + +### _now:date, _now:datetime, _loaded:date, _loaded:_datetime + + + +Adds the time that the data got loaded - pretty much the time of downloading from overpass. The format is YYYY-MM-DD hh:mm, aka 'sortable' aka ISO-8601-but-not-entirely + + +### _last_edit:contributor, _last_edit:contributor:uid, _last_edit:changeset, _last_edit:timestamp, _version_number -Adds the time that the data got loaded - pretty much the time of downloading from overpass. The format is YYYY-MM-DD hh: -mm, aka 'sortable' aka ISO-8601-but-not-entirely -### _last_edit:contributor, _last_edit:contributor:uid, _last_edit:changeset, _last_edit:timestamp, _version_number Information about the last edit of this object. -Calculating tags with Javascript + Calculating tags with Javascript ---------------------------------- -In some cases, it is useful to have some tags calculated based on other properties. Some useful tags are available by -default (e.g. `lat`, `lon`, `_country`), as detailed above. +In some cases, it is useful to have some tags calculated based on other properties. Some useful tags are available by default (e.g. `lat`, `lon`, `_country`), as detailed above. It is also possible to calculate your own tags - but this requires some javascript knowledge. + + Before proceeding, some warnings: -- DO NOT DO THIS AS BEGINNER -- **Only do this if all other techniques fail** This should _not_ be done to create a rendering effect, only to - calculate a specific value -- **THIS MIGHT BE DISABLED WITHOUT ANY NOTICE ON UNOFFICIAL THEMES** As unofficial themes might be loaded from the - internet, this is the equivalent of injecting arbitrary code into the client. It'll be disabled if abuse occurs. -To enable this feature, add a field `calculatedTags` in the layer object, e.g.: + + - DO NOT DO THIS AS BEGINNER + - **Only do this if all other techniques fail** This should _not_ be done to create a rendering effect, only to calculate a specific value + - **THIS MIGHT BE DISABLED WITHOUT ANY NOTICE ON UNOFFICIAL THEMES** As unofficial themes might be loaded from the internet, this is the equivalent of injecting arbitrary code into the client. It'll be disabled if abuse occurs. + + +To enable this feature, add a field `calculatedTags` in the layer object, e.g.: ```` @@ -101,56 +125,58 @@ To enable this feature, add a field `calculatedTags` in the layer object, e.g.: ```` -The above code will be executed for every feature in the layer. The feature is accessible as `feat` and is an amended -geojson object: -- `area` contains the surface area (in square meters) of the object -- `lat` and `lon` contain the latitude and longitude -Some advanced functions are available on **feat** as well: +The above code will be executed for every feature in the layer. The feature is accessible as `feat` and is an amended geojson object: -- distanceTo -- overlapWith -- closest -- memberships -- score -### distanceTo -Calculates the distance between the feature and a specified point in kilometer. The input should either be a pair of -coordinates, a geojson feature or the ID of an object + - `area` contains the surface area (in square meters) of the object + - `lat` and `lon` contain the latitude and longitude -0. longitude -1. latitude -### overlapWith +Some advanced functions are available on **feat** as well: -Gives a list of features from the specified layer which this feature (partly) overlaps with. If the current feature is a -point, all features that embed the point are given. The returned value is `{ feat: GeoJSONFeature, overlap: number}[]` -where `overlap` is the overlapping surface are (in m²) for areas, the overlapping length (in meter) if the current -feature is a line or `undefined` if the current feature is a point + - distanceTo + - overlapWith + - closest + - memberships + - score + +### distanceTo -0. ...layerIds - one or more layer ids of the layer from which every feature is checked for overlap) + Calculates the distance between the feature and a specified point in kilometer. The input should either be a pair of coordinates, a geojson feature or the ID of an object -### closest + 0. longitude + 1. latitude + +### overlapWith -Given either a list of geojson features or a single layer name, gives the single object which is nearest to the feature. -In the case of ways/polygons, only the centerpoint is considered. + Gives a list of features from the specified layer which this feature (partly) overlaps with. If the current feature is a point, all features that embed the point are given. The returned value is `{ feat: GeoJSONFeature, overlap: number}[]` where `overlap` is the overlapping surface are (in m²) for areas, the overlapping length (in meter) if the current feature is a line or `undefined` if the current feature is a point. -0. list of features +For example to get all objects which overlap or embed from a layer, use `_contained_climbing_routes_properties=feat.overlapWith('climbing_route')` -### memberships + 0. ...layerIds - one or more layer ids of the layer from which every feature is checked for overlap) + +### closest -Gives a list of `{role: string, relation: Relation}`-objects, containing all the relations that this feature is part of. + Given either a list of geojson features or a single layer name, gives the single object which is nearest to the feature. In the case of ways/polygons, only the centerpoint is considered. -For example: `_part_of_walking_routes=feat.memberships().map(r => r.relation.tags.name).join(';')` + 0. list of features + +### memberships -### score + Gives a list of `{role: string, relation: Relation}`-objects, containing all the relations that this feature is part of. -Given the path of an aspected routing json file, will calculate the score. This score is wrapped in a UIEventSource, so -for further calculations, use `.map(score => ...)` +For example: `_part_of_walking_routes=feat.memberships().map(r => r.relation.tags.name).join(';')` -For -example: `_comfort_score=feat.score('https://raw.githubusercontent.com/pietervdvn/AspectedRouting/master/Examples/bicycle/aspects/bicycle.comfort.json')` -0. path Generated from SimpleMetaTagger, ExtraFunction \ No newline at end of file + +### score + + Given the path of an aspected routing json file, will calculate the score. This score is wrapped in a UIEventSource, so for further calculations, use `.map(score => ...)` + +For example: `_comfort_score=feat.score('https://raw.githubusercontent.com/pietervdvn/AspectedRouting/master/Examples/bicycle/aspects/bicycle.comfort.json')` + + 0. path + Generated from SimpleMetaTagger, ExtraFunction \ No newline at end of file diff --git a/Docs/Development_deployment.md b/Docs/Development_deployment.md index 9fa81f836f..659ecc5d05 100644 --- a/Docs/Development_deployment.md +++ b/Docs/Development_deployment.md @@ -26,14 +26,17 @@ Devcontainer (see more details later). To develop and build MapComplete, you -0. Make sure you have a recent version of nodejs - at least 12.0, preferably 15 0. Make a fork and clone the repository. -1. Install `npm`. Linux: `sudo apt install npm` (or your favourite package manager), Windows: install +0. Install the nodejs version specified in [.tool-versions](./.tool-versions) + - You can [use asdf to manage your runtime versions](https://asdf-vm.com/). +0. Install `npm`. Linux: `sudo apt install npm` (or your favourite package manager), Windows: install nodeJS: https://nodejs.org/en/download/ -3. Run `npm run init` and generate some additional dependencies and generated files. Note that it'll install the - dependencies too -4. Run `npm run start` to host a local testversion at http://localhost:1234/index.html -5. By default, a landing page with available themes is served. In order to load a single theme, use `layout=themename` +0. On iOS, install `wget` (`brew install wget`) +0. Run `npm run init` which … + - runs `npm install` + - generates some additional dependencies and files +0. Run `npm run start` to host a local testversion at http://localhost:1234/index.html +0. By default, a landing page with available themes is served. In order to load a single theme, use `layout=themename` or `userlayout=true#` as [Query parameter](URL_Parameters.md). Note that the shorter URLs ( e.g. `bookcases.html`, `aed.html`, ...) _don't_ exist on the development version. @@ -99,6 +102,11 @@ Weird errors Try removing `node_modules`, `package-lock.json` and `.cache` +Misc setup +---------- + +The json-git-merger is used to quickly merge translation files, [documentation here](https://github.com/jonatanpedersen/git-json-merge#single-project--directory) + Overview of package.json-scripts -------------------------------- @@ -125,4 +133,3 @@ Overview of package.json-scripts - `deploy:staging`,`deploy:pietervdvn`, `deploy:production`: deploy the latest code on various locations - `lint`: get depressed by the amount of warnings - `clean`: remove some generated files which are annoying in the repo - diff --git a/Docs/Release_Notes.md b/Docs/Release_Notes.md index ea1b1ded29..c6f0ac35dc 100644 --- a/Docs/Release_Notes.md +++ b/Docs/Release_Notes.md @@ -3,6 +3,41 @@ Release Notes Some highlights of new releases. +0.10 +---- + +The 0.10 version contains a lot of refactorings on various core of the application, namely in the rendering stack, the fetching of data and uploading. + +Some highlights are: + +1. The addition of fallback overpass servers +2. Fetching data from OSM directly (especially useful in the personal theme) +3. Splitting all the features per tile (with a maximum amount of features per tile, splitting further if needed), making everything a ton faster +4. If a tile has too much features, the featuers are not shown. Instead, a rectangle with the feature amount is shown. + +Furthermore, it contains a few new themes and theme updates: + +- Restaurants and fast food +- Pubs and cafés +- Charging stations got a major overhaul - thanks for all the input on the available plugs +- Observation towers and binoculars +- The addition of a hackerspace theme (as made on SOTM) + +Other various small improvements: + +- The filter state is now exposed in the URL, so can be shared +- Lots of other fixes, as usual + +0.8 and 0.9 +----------- + +Addition of filters per layer +Addition of a download-as-pdf for select themes +Addition of a download-as-geojson and download-as-csv for select themes + +... + + 0.7.0 ----- diff --git a/Docs/SpecialInputElements.md b/Docs/SpecialInputElements.md index b7628b51b3..957a74db48 100644 --- a/Docs/SpecialInputElements.md +++ b/Docs/SpecialInputElements.md @@ -1,7 +1,6 @@ # Available types for text fields -The listed types here trigger a special input element. Use them in `tagrendering.freeform.type` of your tagrendering to -activate them +The listed types here trigger a special input element. Use them in `tagrendering.freeform.type` of your tagrendering to activate them ## string @@ -21,8 +20,7 @@ A geographical direction, in degrees. 0° is north, 90° is east, ... Will retur ## length -A geographical length in meters (rounded at two points). Will give an extra minimap with a measurement tool. -Arguments: [ zoomlevel, preferredBackgroundMapType (comma separated) ], e.g. `["21", "map,photo"] +A geographical length in meters (rounded at two points). Will give an extra minimap with a measurement tool. Arguments: [ zoomlevel, preferredBackgroundMapType (comma separated) ], e.g. `["21", "map,photo"] ## wikidata diff --git a/Docs/SpecialRenderings.md b/Docs/SpecialRenderings.md index 056765e58d..da4a4bc204 100644 --- a/Docs/SpecialRenderings.md +++ b/Docs/SpecialRenderings.md @@ -1,110 +1,92 @@ -### Special tag renderings -In a tagrendering, some special values are substituted by an advanced UI-element. This allows advanced features and -visualizations to be reused by custom themes or even to query third-party API's. General usage is `{func_name()}` -, `{func_name(arg, someotherarg)}` or `{func_name(args):cssStyle}`. Note that you _do not_fcs need to use quotes around -your arguments, the comma is enough to separate them. This also implies you cannot use a comma in your args +### Special tag renderings -### all_tags + In a tagrendering, some special values are substituted by an advanced UI-element. This allows advanced features and visualizations to be reused by custom themes or even to query third-party API's. General usage is `{func_name()}`, `{func_name(arg, someotherarg)}` or `{func_name(args):cssStyle}`. Note that you _do not_fcs need to use quotes around your arguments, the comma is enough to separate them. This also implies you cannot use a comma in your args +### all_tags -Prints all key-value pairs of the object - used for debugging + Prints all key-value pairs of the object - used for debugging name | default | description ------ | --------- | ------------- -#### Example usage + +#### Example usage -{all_tags()} + `{all_tags()}` +### image_carousel -### image_carousel - -Creates an image carousel for the given sources. An attempt will be made to guess what source is used. Supported: -Wikidata identifiers, Wikipedia pages, Wikimedia categories, IMGUR (with attribution, direct links) + Creates an image carousel for the given sources. An attempt will be made to guess what source is used. Supported: Wikidata identifiers, Wikipedia pages, Wikimedia categories, IMGUR (with attribution, direct links) name | default | description ------ | --------- | ------------- -image key/prefix | image | The keys given to the images, e.g. if image is given, the first picture URL will be added as image, the second as image:0, the third as image:1, etc... +image key/prefix | image | The keys given to the images, e.g. if image is given, the first picture URL will be added as image, the second as image:0, the third as image:1, etc... smart search | true | Also include images given via 'Wikidata', 'wikimedia_commons' and 'mapillary + +#### Example usage -#### Example usage + `{image_carousel(image,true)}` +### image_upload -{image_carousel(image,true)} - -### image_upload - -Creates a button where a user can upload an image to IMGUR + Creates a button where a user can upload an image to IMGUR name | default | description ------ | --------- | ------------- image-key | image | Image tag to add the URL to (or image-tag:0, image-tag:1 when multiple images are added) + +#### Example usage -#### Example usage + `{image_upload(image)}` +### minimap -{image_upload(image)} - -### minimap - -A small map showing the selected feature. Note that no styling is applied, wrap this in a div + A small map showing the selected feature. Note that no styling is applied, wrap this in a div name | default | description ------ | --------- | ------------- zoomlevel | 18 | The (maximum) zoomlevel: the target zoomlevel after fitting the entire feature. The minimap will fit the entire feature, then zoom out to this zoom level. The higher, the more zoomed in with 1 being the entire world and 19 being really close idKey | id | (Matches all resting arguments) This argument should be the key of a property of the feature. The corresponding value is interpreted as either the id or the a list of ID's. The features with these ID's will be shown on this minimap. + +#### Example usage -#### Example usage + `{minimap()}`, `{minimap(17, id, _list_of_embedded_feature_ids_calculated_by_calculated_tag):height:10rem; border: 2px solid black}` +### reviews -`{minimap()}` -, `{minimap(17, id, _list_of_embedded_feature_ids_calculated_by_calculated_tag):height:10rem; border: 2px solid black}` - -### reviews - -Adds an overview of the mangrove-reviews of this object. Mangrove.Reviews needs - in order to identify the reviewed -object - a coordinate and a name. By default, the name of the object is given, but this can be overwritten + Adds an overview of the mangrove-reviews of this object. Mangrove.Reviews needs - in order to identify the reviewed object - a coordinate and a name. By default, the name of the object is given, but this can be overwritten name | default | description ------ | --------- | ------------- subjectKey | name | The key to use to determine the subject. If specified, the subject will be tags[subjectKey] fallback | undefined | The identifier to use, if tags[subjectKey] as specified above is not available. This is effectively a fallback value + +#### Example usage -#### Example usage + `{reviews()}` for a vanilla review, `{reviews(name, play_forest)}` to review a play forest. If a name is known, the name will be used as identifier, otherwise 'play_forest' is used +### opening_hours_table -{reviews()} for a vanilla review, {reviews(name, play_forest)} to review a play forest. If a name is known, -the name will be used as identifier, otherwise 'play_forest' is used - -### opening_hours_table - -Creates an opening-hours table. Usage: {opening_hours_table(opening_hours)} to create a table of the tag ' -opening_hours'. + Creates an opening-hours table. Usage: {opening_hours_table(opening_hours)} to create a table of the tag 'opening_hours'. name | default | description ------ | --------- | ------------- key | opening_hours | The tagkey from which the table is constructed. + +#### Example usage -#### Example usage + `{opening_hours_table(opening_hours)}` +### live -{opening_hours_table(opening_hours)} - -### live - -Downloads a JSON from the given URL, e.g. '{live(example.org/data.json, shorthand:x.y.z, other:a.b.c, shorthand)}' will -download the given file, will create an object {shorthand: json[x][y][z], other: json[a][b][c] out of it and will -return 'other' or 'json[a][b][c]. This is made to use in combination with tags, e.g. {live({url}, {url:format}, -needed_value)} + Downloads a JSON from the given URL, e.g. '{live(example.org/data.json, shorthand:x.y.z, other:a.b.c, shorthand)}' will download the given file, will create an object {shorthand: json[x][y][z], other: json[a][b][c] out of it and will return 'other' or 'json[a][b][c]. This is made to use in combination with tags, e.g. {live({url}, {url:format}, needed_value)} name | default | description ------ | --------- | ------------- Url | undefined | The URL to load Shorthands | undefined | A list of shorthands, of the format 'shorthandname:path.path.path'. separated by ; path | undefined | The path (or shorthand) that should be returned + +#### Example usage -#### Example usage + {live({url},{url:format},hour)} {live(https://data.mobility.brussels/bike/api/counts/?request=live&featureID=CB2105,hour:data.hour_cnt;day:data.day_cnt;year:data.year_cnt,hour)} +### histogram -{live({url},{url:format},hour)} -{live(https://data.mobility.brussels/bike/api/counts/?request=live&featureID=CB2105,hour:data.hour_cnt;day:data.day_cnt;year:data.year_cnt,hour)} - -### histogram - -Create a histogram for a list of given values, read from the properties. + Create a histogram for a list of given values, read from the properties. name | default | description ------ | --------- | ------------- @@ -112,31 +94,50 @@ key | undefined | The key to be read and to generate a histogram from title | | The text to put above the given values column countHeader | | The text to put above the counts colors* | undefined | (Matches all resting arguments - optional) Matches a regex onto a color value, e.g. `3[a-zA-Z+-]*:#33cc33` + +#### Example usage -#### Example usage + `{histogram('some_key')}` with properties being `{some_key: ['a','b','a','c']} to create a histogram +### share_link -`{histogram('some_key')}` with properties being `{some_key: ['a','b','a','c']} to create a histogram - -### share_link - -Creates a link that (attempts to) open the native 'share'-screen + Creates a link that (attempts to) open the native 'share'-screen name | default | description ------ | --------- | ------------- url | undefined | The url to share (default: current URL) + +#### Example usage -#### Example usage + {share_link()} to share the current page, {share_link()} to share the given url +### canonical -{share_link()} to share the current page, {share_link()} to share the given url - -### canonical - -Converts a short, canonical value into the long, translated text + Converts a short, canonical value into the long, translated text name | default | description ------ | --------- | ------------- key | undefined | The key of the tag to give the canonical text for + +#### Example usage -#### Example usage + {canonical(length)} will give 42 metre (in french) +### import_button -{canonical(length)} will give 42 metre (in french) Generated from UI/SpecialVisualisations.ts \ No newline at end of file + This button will copy the data from an external dataset into OpenStreetMap. It is only functional in official themes but can be tested in unofficial themes. + +If you want to import a dataset, make sure that: + +1. The dataset to import has a suitable license +2. The community has been informed of the import +3. The theme will filter out duplicate nodes4. All other requirements of the [import guidelines](https://wiki.openstreetmap.org/wiki/Import/Guidelines) have been followed + +name | default | description +------ | --------- | ------------- +tags | undefined | Tags to copy-specification. This contains one or more pairs (seperated by a `;`), e.g. `amenity=fast_food; addr:housenumber={number}`. This new point will then have the tags `amenity=fast_food` and `addr:housenumber` with the value that was saved in `number` in the original feature. (Hint: prepare these values, e.g. with calculatedTags) +text | Import this data into OpenStreetMap | The text to show on the button +icon | ./assets/svg/addSmall.svg | A nice icon to show in the button + +#### Example usage + + `{import_button(,Import this data into OpenStreetMap,./assets/svg/addSmall.svg)}` + +Generated from UI/SpecialVisualisations.ts \ No newline at end of file diff --git a/Docs/TagInfo/mapcomplete_charging_stations.json b/Docs/TagInfo/mapcomplete_charging_stations.json index 9484441290..22ca1d0a4b 100644 --- a/Docs/TagInfo/mapcomplete_charging_stations.json +++ b/Docs/TagInfo/mapcomplete_charging_stations.json @@ -106,121 +106,464 @@ }, { "key": "socket:schuko", - "description": "Layer 'Charging stations' shows socket:schuko=1 with a fixed text, namely ' Schuko wall plug without ground pin (CEE7/4 type F)' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", + "description": "Layer 'Charging stations' shows socket:schuko=1 with a fixed text, namely ' Schuko wall plug without ground pin (CEE7/4 type F)' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", "value": "1" }, { "key": "socket:schuko", - "description": "Layer 'Charging stations' shows socket:schuko~^..*$&socket:schuko!~^1$ with a fixed text, namely ' Schuko wall plug without ground pin (CEE7/4 type F)' (in the MapComplete.osm.be theme 'Charging stations')" + "description": "Layer 'Charging stations' shows socket:schuko~^..*$&socket:schuko!~^1$ with a fixed text, namely ' Schuko wall plug without ground pin (CEE7/4 type F)' (in the MapComplete.osm.be theme 'Charging stations')" }, { "key": "socket:typee", - "description": "Layer 'Charging stations' shows socket:typee=1 with a fixed text, namely ' European wall plug with ground pin (CEE7/4 type E)' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", + "description": "Layer 'Charging stations' shows socket:typee=1 with a fixed text, namely ' European wall plug with ground pin (CEE7/4 type E)' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", "value": "1" }, { "key": "socket:typee", - "description": "Layer 'Charging stations' shows socket:typee~^..*$&socket:typee!~^1$ with a fixed text, namely ' European wall plug with ground pin (CEE7/4 type E)' (in the MapComplete.osm.be theme 'Charging stations')" + "description": "Layer 'Charging stations' shows socket:typee~^..*$&socket:typee!~^1$ with a fixed text, namely ' European wall plug with ground pin (CEE7/4 type E)' (in the MapComplete.osm.be theme 'Charging stations')" }, { "key": "socket:chademo", - "description": "Layer 'Charging stations' shows socket:chademo=1 with a fixed text, namely ' Chademo' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", + "description": "Layer 'Charging stations' shows socket:chademo=1 with a fixed text, namely ' Chademo' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", "value": "1" }, { "key": "socket:chademo", - "description": "Layer 'Charging stations' shows socket:chademo~^..*$&socket:chademo!~^1$ with a fixed text, namely ' Chademo' (in the MapComplete.osm.be theme 'Charging stations')" + "description": "Layer 'Charging stations' shows socket:chademo~^..*$&socket:chademo!~^1$ with a fixed text, namely ' Chademo' (in the MapComplete.osm.be theme 'Charging stations')" }, { "key": "socket:type1_cable", - "description": "Layer 'Charging stations' shows socket:type1_cable=1 with a fixed text, namely ' Type 1 with cable (J1772)' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", + "description": "Layer 'Charging stations' shows socket:type1_cable=1 with a fixed text, namely ' Type 1 with cable (J1772)' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", "value": "1" }, { "key": "socket:type1_cable", - "description": "Layer 'Charging stations' shows socket:type1_cable~^..*$&socket:type1_cable!~^1$ with a fixed text, namely ' Type 1 with cable (J1772)' (in the MapComplete.osm.be theme 'Charging stations')" + "description": "Layer 'Charging stations' shows socket:type1_cable~^..*$&socket:type1_cable!~^1$ with a fixed text, namely ' Type 1 with cable (J1772)' (in the MapComplete.osm.be theme 'Charging stations')" }, { "key": "socket:type1", - "description": "Layer 'Charging stations' shows socket:type1=1 with a fixed text, namely ' Type 1 without cable (J1772)' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", + "description": "Layer 'Charging stations' shows socket:type1=1 with a fixed text, namely ' Type 1 without cable (J1772)' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", "value": "1" }, { "key": "socket:type1", - "description": "Layer 'Charging stations' shows socket:type1~^..*$&socket:type1!~^1$ with a fixed text, namely ' Type 1 without cable (J1772)' (in the MapComplete.osm.be theme 'Charging stations')" + "description": "Layer 'Charging stations' shows socket:type1~^..*$&socket:type1!~^1$ with a fixed text, namely ' Type 1 without cable (J1772)' (in the MapComplete.osm.be theme 'Charging stations')" }, { "key": "socket:type1_combo", - "description": "Layer 'Charging stations' shows socket:type1_combo=1 with a fixed text, namely ' Type 1 CCS (aka Type 1 Combo)' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", + "description": "Layer 'Charging stations' shows socket:type1_combo=1 with a fixed text, namely ' Type 1 CCS (aka Type 1 Combo)' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", "value": "1" }, { "key": "socket:type1_combo", - "description": "Layer 'Charging stations' shows socket:type1_combo~^..*$&socket:type1_combo!~^1$ with a fixed text, namely ' Type 1 CCS (aka Type 1 Combo)' (in the MapComplete.osm.be theme 'Charging stations')" + "description": "Layer 'Charging stations' shows socket:type1_combo~^..*$&socket:type1_combo!~^1$ with a fixed text, namely ' Type 1 CCS (aka Type 1 Combo)' (in the MapComplete.osm.be theme 'Charging stations')" }, { "key": "socket:tesla_supercharger", - "description": "Layer 'Charging stations' shows socket:tesla_supercharger=1 with a fixed text, namely ' Tesla Supercharger' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", + "description": "Layer 'Charging stations' shows socket:tesla_supercharger=1 with a fixed text, namely ' Tesla Supercharger' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", "value": "1" }, { "key": "socket:tesla_supercharger", - "description": "Layer 'Charging stations' shows socket:tesla_supercharger~^..*$&socket:tesla_supercharger!~^1$ with a fixed text, namely ' Tesla Supercharger' (in the MapComplete.osm.be theme 'Charging stations')" + "description": "Layer 'Charging stations' shows socket:tesla_supercharger~^..*$&socket:tesla_supercharger!~^1$ with a fixed text, namely ' Tesla Supercharger' (in the MapComplete.osm.be theme 'Charging stations')" }, { "key": "socket:type2", - "description": "Layer 'Charging stations' shows socket:type2=1 with a fixed text, namely ' Type 2 (mennekes)' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", + "description": "Layer 'Charging stations' shows socket:type2=1 with a fixed text, namely ' Type 2 (mennekes)' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", "value": "1" }, { "key": "socket:type2", - "description": "Layer 'Charging stations' shows socket:type2~^..*$&socket:type2!~^1$ with a fixed text, namely ' Type 2 (mennekes)' (in the MapComplete.osm.be theme 'Charging stations')" + "description": "Layer 'Charging stations' shows socket:type2~^..*$&socket:type2!~^1$ with a fixed text, namely ' Type 2 (mennekes)' (in the MapComplete.osm.be theme 'Charging stations')" }, { "key": "socket:type2_combo", - "description": "Layer 'Charging stations' shows socket:type2_combo=1 with a fixed text, namely ' Type 2 CCS (mennekes)' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", + "description": "Layer 'Charging stations' shows socket:type2_combo=1 with a fixed text, namely ' Type 2 CCS (mennekes)' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", "value": "1" }, { "key": "socket:type2_combo", - "description": "Layer 'Charging stations' shows socket:type2_combo~^..*$&socket:type2_combo!~^1$ with a fixed text, namely ' Type 2 CCS (mennekes)' (in the MapComplete.osm.be theme 'Charging stations')" + "description": "Layer 'Charging stations' shows socket:type2_combo~^..*$&socket:type2_combo!~^1$ with a fixed text, namely ' Type 2 CCS (mennekes)' (in the MapComplete.osm.be theme 'Charging stations')" }, { "key": "socket:schuko", "description": "Layer 'Charging stations' shows and asks freeform values for key 'socket:schuko' (in the MapComplete.osm.be theme 'Charging stations')" }, + { + "key": "socket:schuko:voltage", + "description": "Layer 'Charging stations' shows and asks freeform values for key 'socket:schuko:voltage' (in the MapComplete.osm.be theme 'Charging stations')" + }, + { + "key": "socket:socket:schuko:voltage", + "description": "Layer 'Charging stations' shows socket:socket:schuko:voltage=230 V with a fixed text, namely 'Schuko wall plug without ground pin (CEE7/4 type F) outputs 230 volt' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", + "value": "230 V" + }, + { + "key": "socket:schuko:current", + "description": "Layer 'Charging stations' shows and asks freeform values for key 'socket:schuko:current' (in the MapComplete.osm.be theme 'Charging stations')" + }, + { + "key": "socket:socket:schuko:current", + "description": "Layer 'Charging stations' shows socket:socket:schuko:current=16 A with a fixed text, namely 'Schuko wall plug without ground pin (CEE7/4 type F) outputs at most 16 A' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", + "value": "16 A" + }, + { + "key": "socket:schuko:output", + "description": "Layer 'Charging stations' shows and asks freeform values for key 'socket:schuko:output' (in the MapComplete.osm.be theme 'Charging stations')" + }, + { + "key": "socket:socket:schuko:output", + "description": "Layer 'Charging stations' shows socket:socket:schuko:output=3.6 kw with a fixed text, namely 'Schuko wall plug without ground pin (CEE7/4 type F) outputs at most 3.6 kw' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", + "value": "3.6 kw" + }, { "key": "socket:typee", "description": "Layer 'Charging stations' shows and asks freeform values for key 'socket:typee' (in the MapComplete.osm.be theme 'Charging stations')" }, + { + "key": "socket:typee:voltage", + "description": "Layer 'Charging stations' shows and asks freeform values for key 'socket:typee:voltage' (in the MapComplete.osm.be theme 'Charging stations')" + }, + { + "key": "socket:socket:typee:voltage", + "description": "Layer 'Charging stations' shows socket:socket:typee:voltage=230 V with a fixed text, namely 'European wall plug with ground pin (CEE7/4 type E) outputs 230 volt' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", + "value": "230 V" + }, + { + "key": "socket:typee:current", + "description": "Layer 'Charging stations' shows and asks freeform values for key 'socket:typee:current' (in the MapComplete.osm.be theme 'Charging stations')" + }, + { + "key": "socket:socket:typee:current", + "description": "Layer 'Charging stations' shows socket:socket:typee:current=16 A with a fixed text, namely 'European wall plug with ground pin (CEE7/4 type E) outputs at most 16 A' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", + "value": "16 A" + }, + { + "key": "socket:typee:output", + "description": "Layer 'Charging stations' shows and asks freeform values for key 'socket:typee:output' (in the MapComplete.osm.be theme 'Charging stations')" + }, + { + "key": "socket:socket:typee:output", + "description": "Layer 'Charging stations' shows socket:socket:typee:output=3 kw with a fixed text, namely 'European wall plug with ground pin (CEE7/4 type E) outputs at most 3 kw' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", + "value": "3 kw" + }, + { + "key": "socket:socket:typee:output", + "description": "Layer 'Charging stations' shows socket:socket:typee:output=22 kw with a fixed text, namely 'European wall plug with ground pin (CEE7/4 type E) outputs at most 22 kw' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", + "value": "22 kw" + }, { "key": "socket:chademo", "description": "Layer 'Charging stations' shows and asks freeform values for key 'socket:chademo' (in the MapComplete.osm.be theme 'Charging stations')" }, + { + "key": "socket:chademo:voltage", + "description": "Layer 'Charging stations' shows and asks freeform values for key 'socket:chademo:voltage' (in the MapComplete.osm.be theme 'Charging stations')" + }, + { + "key": "socket:socket:chademo:voltage", + "description": "Layer 'Charging stations' shows socket:socket:chademo:voltage=500 V with a fixed text, namely 'Chademo outputs 500 volt' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", + "value": "500 V" + }, + { + "key": "socket:chademo:current", + "description": "Layer 'Charging stations' shows and asks freeform values for key 'socket:chademo:current' (in the MapComplete.osm.be theme 'Charging stations')" + }, + { + "key": "socket:socket:chademo:current", + "description": "Layer 'Charging stations' shows socket:socket:chademo:current=120 A with a fixed text, namely 'Chademo outputs at most 120 A' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", + "value": "120 A" + }, + { + "key": "socket:chademo:output", + "description": "Layer 'Charging stations' shows and asks freeform values for key 'socket:chademo:output' (in the MapComplete.osm.be theme 'Charging stations')" + }, + { + "key": "socket:socket:chademo:output", + "description": "Layer 'Charging stations' shows socket:socket:chademo:output=50 kw with a fixed text, namely 'Chademo outputs at most 50 kw' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", + "value": "50 kw" + }, { "key": "socket:type1_cable", "description": "Layer 'Charging stations' shows and asks freeform values for key 'socket:type1_cable' (in the MapComplete.osm.be theme 'Charging stations')" }, + { + "key": "socket:type1_cable:voltage", + "description": "Layer 'Charging stations' shows and asks freeform values for key 'socket:type1_cable:voltage' (in the MapComplete.osm.be theme 'Charging stations')" + }, + { + "key": "socket:socket:type1_cable:voltage", + "description": "Layer 'Charging stations' shows socket:socket:type1_cable:voltage=200 V with a fixed text, namely 'Type 1 with cable (J1772) outputs 200 volt' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", + "value": "200 V" + }, + { + "key": "socket:socket:type1_cable:voltage", + "description": "Layer 'Charging stations' shows socket:socket:type1_cable:voltage=240 V with a fixed text, namely 'Type 1 with cable (J1772) outputs 240 volt' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", + "value": "240 V" + }, + { + "key": "socket:type1_cable:current", + "description": "Layer 'Charging stations' shows and asks freeform values for key 'socket:type1_cable:current' (in the MapComplete.osm.be theme 'Charging stations')" + }, + { + "key": "socket:socket:type1_cable:current", + "description": "Layer 'Charging stations' shows socket:socket:type1_cable:current=32 A with a fixed text, namely 'Type 1 with cable (J1772) outputs at most 32 A' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", + "value": "32 A" + }, + { + "key": "socket:type1_cable:output", + "description": "Layer 'Charging stations' shows and asks freeform values for key 'socket:type1_cable:output' (in the MapComplete.osm.be theme 'Charging stations')" + }, + { + "key": "socket:socket:type1_cable:output", + "description": "Layer 'Charging stations' shows socket:socket:type1_cable:output=3.7 kw with a fixed text, namely 'Type 1 with cable (J1772) outputs at most 3.7 kw' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", + "value": "3.7 kw" + }, + { + "key": "socket:socket:type1_cable:output", + "description": "Layer 'Charging stations' shows socket:socket:type1_cable:output=7 kw with a fixed text, namely 'Type 1 with cable (J1772) outputs at most 7 kw' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", + "value": "7 kw" + }, { "key": "socket:type1", "description": "Layer 'Charging stations' shows and asks freeform values for key 'socket:type1' (in the MapComplete.osm.be theme 'Charging stations')" }, + { + "key": "socket:type1:voltage", + "description": "Layer 'Charging stations' shows and asks freeform values for key 'socket:type1:voltage' (in the MapComplete.osm.be theme 'Charging stations')" + }, + { + "key": "socket:socket:type1:voltage", + "description": "Layer 'Charging stations' shows socket:socket:type1:voltage=200 V with a fixed text, namely 'Type 1 without cable (J1772) outputs 200 volt' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", + "value": "200 V" + }, + { + "key": "socket:socket:type1:voltage", + "description": "Layer 'Charging stations' shows socket:socket:type1:voltage=240 V with a fixed text, namely 'Type 1 without cable (J1772) outputs 240 volt' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", + "value": "240 V" + }, + { + "key": "socket:type1:current", + "description": "Layer 'Charging stations' shows and asks freeform values for key 'socket:type1:current' (in the MapComplete.osm.be theme 'Charging stations')" + }, + { + "key": "socket:socket:type1:current", + "description": "Layer 'Charging stations' shows socket:socket:type1:current=32 A with a fixed text, namely 'Type 1 without cable (J1772) outputs at most 32 A' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", + "value": "32 A" + }, + { + "key": "socket:type1:output", + "description": "Layer 'Charging stations' shows and asks freeform values for key 'socket:type1:output' (in the MapComplete.osm.be theme 'Charging stations')" + }, + { + "key": "socket:socket:type1:output", + "description": "Layer 'Charging stations' shows socket:socket:type1:output=3.7 kw with a fixed text, namely 'Type 1 without cable (J1772) outputs at most 3.7 kw' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", + "value": "3.7 kw" + }, + { + "key": "socket:socket:type1:output", + "description": "Layer 'Charging stations' shows socket:socket:type1:output=6.6 kw with a fixed text, namely 'Type 1 without cable (J1772) outputs at most 6.6 kw' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", + "value": "6.6 kw" + }, + { + "key": "socket:socket:type1:output", + "description": "Layer 'Charging stations' shows socket:socket:type1:output=7 kw with a fixed text, namely 'Type 1 without cable (J1772) outputs at most 7 kw' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", + "value": "7 kw" + }, + { + "key": "socket:socket:type1:output", + "description": "Layer 'Charging stations' shows socket:socket:type1:output=7.2 kw with a fixed text, namely 'Type 1 without cable (J1772) outputs at most 7.2 kw' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", + "value": "7.2 kw" + }, { "key": "socket:type1_combo", "description": "Layer 'Charging stations' shows and asks freeform values for key 'socket:type1_combo' (in the MapComplete.osm.be theme 'Charging stations')" }, + { + "key": "socket:type1_combo:voltage", + "description": "Layer 'Charging stations' shows and asks freeform values for key 'socket:type1_combo:voltage' (in the MapComplete.osm.be theme 'Charging stations')" + }, + { + "key": "socket:socket:type1_combo:voltage", + "description": "Layer 'Charging stations' shows socket:socket:type1_combo:voltage=400 V with a fixed text, namely 'Type 1 CCS (aka Type 1 Combo) outputs 400 volt' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", + "value": "400 V" + }, + { + "key": "socket:socket:type1_combo:voltage", + "description": "Layer 'Charging stations' shows socket:socket:type1_combo:voltage=1000 V with a fixed text, namely 'Type 1 CCS (aka Type 1 Combo) outputs 1000 volt' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", + "value": "1000 V" + }, + { + "key": "socket:type1_combo:current", + "description": "Layer 'Charging stations' shows and asks freeform values for key 'socket:type1_combo:current' (in the MapComplete.osm.be theme 'Charging stations')" + }, + { + "key": "socket:socket:type1_combo:current", + "description": "Layer 'Charging stations' shows socket:socket:type1_combo:current=50 A with a fixed text, namely 'Type 1 CCS (aka Type 1 Combo) outputs at most 50 A' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", + "value": "50 A" + }, + { + "key": "socket:socket:type1_combo:current", + "description": "Layer 'Charging stations' shows socket:socket:type1_combo:current=125 A with a fixed text, namely 'Type 1 CCS (aka Type 1 Combo) outputs at most 125 A' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", + "value": "125 A" + }, + { + "key": "socket:type1_combo:output", + "description": "Layer 'Charging stations' shows and asks freeform values for key 'socket:type1_combo:output' (in the MapComplete.osm.be theme 'Charging stations')" + }, + { + "key": "socket:socket:type1_combo:output", + "description": "Layer 'Charging stations' shows socket:socket:type1_combo:output=50 kw with a fixed text, namely 'Type 1 CCS (aka Type 1 Combo) outputs at most 50 kw' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", + "value": "50 kw" + }, + { + "key": "socket:socket:type1_combo:output", + "description": "Layer 'Charging stations' shows socket:socket:type1_combo:output=62.5 kw with a fixed text, namely 'Type 1 CCS (aka Type 1 Combo) outputs at most 62.5 kw' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", + "value": "62.5 kw" + }, + { + "key": "socket:socket:type1_combo:output", + "description": "Layer 'Charging stations' shows socket:socket:type1_combo:output=150 kw with a fixed text, namely 'Type 1 CCS (aka Type 1 Combo) outputs at most 150 kw' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", + "value": "150 kw" + }, + { + "key": "socket:socket:type1_combo:output", + "description": "Layer 'Charging stations' shows socket:socket:type1_combo:output=350 kw with a fixed text, namely 'Type 1 CCS (aka Type 1 Combo) outputs at most 350 kw' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", + "value": "350 kw" + }, { "key": "socket:tesla_supercharger", "description": "Layer 'Charging stations' shows and asks freeform values for key 'socket:tesla_supercharger' (in the MapComplete.osm.be theme 'Charging stations')" }, + { + "key": "socket:tesla_supercharger:voltage", + "description": "Layer 'Charging stations' shows and asks freeform values for key 'socket:tesla_supercharger:voltage' (in the MapComplete.osm.be theme 'Charging stations')" + }, + { + "key": "socket:socket:tesla_supercharger:voltage", + "description": "Layer 'Charging stations' shows socket:socket:tesla_supercharger:voltage=480 V with a fixed text, namely 'Tesla Supercharger outputs 480 volt' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", + "value": "480 V" + }, + { + "key": "socket:tesla_supercharger:current", + "description": "Layer 'Charging stations' shows and asks freeform values for key 'socket:tesla_supercharger:current' (in the MapComplete.osm.be theme 'Charging stations')" + }, + { + "key": "socket:socket:tesla_supercharger:current", + "description": "Layer 'Charging stations' shows socket:socket:tesla_supercharger:current=125 A with a fixed text, namely 'Tesla Supercharger outputs at most 125 A' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", + "value": "125 A" + }, + { + "key": "socket:socket:tesla_supercharger:current", + "description": "Layer 'Charging stations' shows socket:socket:tesla_supercharger:current=350 A with a fixed text, namely 'Tesla Supercharger outputs at most 350 A' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", + "value": "350 A" + }, + { + "key": "socket:tesla_supercharger:output", + "description": "Layer 'Charging stations' shows and asks freeform values for key 'socket:tesla_supercharger:output' (in the MapComplete.osm.be theme 'Charging stations')" + }, + { + "key": "socket:socket:tesla_supercharger:output", + "description": "Layer 'Charging stations' shows socket:socket:tesla_supercharger:output=120 kw with a fixed text, namely 'Tesla Supercharger outputs at most 120 kw' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", + "value": "120 kw" + }, + { + "key": "socket:socket:tesla_supercharger:output", + "description": "Layer 'Charging stations' shows socket:socket:tesla_supercharger:output=150 kw with a fixed text, namely 'Tesla Supercharger outputs at most 150 kw' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", + "value": "150 kw" + }, + { + "key": "socket:socket:tesla_supercharger:output", + "description": "Layer 'Charging stations' shows socket:socket:tesla_supercharger:output=250 kw with a fixed text, namely 'Tesla Supercharger outputs at most 250 kw' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", + "value": "250 kw" + }, { "key": "socket:type2", "description": "Layer 'Charging stations' shows and asks freeform values for key 'socket:type2' (in the MapComplete.osm.be theme 'Charging stations')" }, + { + "key": "socket:type2:voltage", + "description": "Layer 'Charging stations' shows and asks freeform values for key 'socket:type2:voltage' (in the MapComplete.osm.be theme 'Charging stations')" + }, + { + "key": "socket:socket:type2:voltage", + "description": "Layer 'Charging stations' shows socket:socket:type2:voltage=230 V with a fixed text, namely 'Type 2 (mennekes) outputs 230 volt' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", + "value": "230 V" + }, + { + "key": "socket:socket:type2:voltage", + "description": "Layer 'Charging stations' shows socket:socket:type2:voltage=400 V with a fixed text, namely 'Type 2 (mennekes) outputs 400 volt' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", + "value": "400 V" + }, + { + "key": "socket:type2:current", + "description": "Layer 'Charging stations' shows and asks freeform values for key 'socket:type2:current' (in the MapComplete.osm.be theme 'Charging stations')" + }, + { + "key": "socket:socket:type2:current", + "description": "Layer 'Charging stations' shows socket:socket:type2:current=16 A with a fixed text, namely 'Type 2 (mennekes) outputs at most 16 A' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", + "value": "16 A" + }, + { + "key": "socket:socket:type2:current", + "description": "Layer 'Charging stations' shows socket:socket:type2:current=32 A with a fixed text, namely 'Type 2 (mennekes) outputs at most 32 A' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", + "value": "32 A" + }, + { + "key": "socket:type2:output", + "description": "Layer 'Charging stations' shows and asks freeform values for key 'socket:type2:output' (in the MapComplete.osm.be theme 'Charging stations')" + }, + { + "key": "socket:socket:type2:output", + "description": "Layer 'Charging stations' shows socket:socket:type2:output=11 kw with a fixed text, namely 'Type 2 (mennekes) outputs at most 11 kw' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", + "value": "11 kw" + }, + { + "key": "socket:socket:type2:output", + "description": "Layer 'Charging stations' shows socket:socket:type2:output=22 kw with a fixed text, namely 'Type 2 (mennekes) outputs at most 22 kw' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", + "value": "22 kw" + }, { "key": "socket:type2_combo", "description": "Layer 'Charging stations' shows and asks freeform values for key 'socket:type2_combo' (in the MapComplete.osm.be theme 'Charging stations')" }, + { + "key": "socket:type2_combo:voltage", + "description": "Layer 'Charging stations' shows and asks freeform values for key 'socket:type2_combo:voltage' (in the MapComplete.osm.be theme 'Charging stations')" + }, + { + "key": "socket:socket:type2_combo:voltage", + "description": "Layer 'Charging stations' shows socket:socket:type2_combo:voltage=500 V with a fixed text, namely 'Type 2 CCS (mennekes) outputs 500 volt' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", + "value": "500 V" + }, + { + "key": "socket:socket:type2_combo:voltage", + "description": "Layer 'Charging stations' shows socket:socket:type2_combo:voltage=920 V with a fixed text, namely 'Type 2 CCS (mennekes) outputs 920 volt' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", + "value": "920 V" + }, + { + "key": "socket:type2_combo:current", + "description": "Layer 'Charging stations' shows and asks freeform values for key 'socket:type2_combo:current' (in the MapComplete.osm.be theme 'Charging stations')" + }, + { + "key": "socket:socket:type2_combo:current", + "description": "Layer 'Charging stations' shows socket:socket:type2_combo:current=125 A with a fixed text, namely 'Type 2 CCS (mennekes) outputs at most 125 A' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", + "value": "125 A" + }, + { + "key": "socket:socket:type2_combo:current", + "description": "Layer 'Charging stations' shows socket:socket:type2_combo:current=350 A with a fixed text, namely 'Type 2 CCS (mennekes) outputs at most 350 A' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", + "value": "350 A" + }, + { + "key": "socket:type2_combo:output", + "description": "Layer 'Charging stations' shows and asks freeform values for key 'socket:type2_combo:output' (in the MapComplete.osm.be theme 'Charging stations')" + }, + { + "key": "socket:socket:type2_combo:output", + "description": "Layer 'Charging stations' shows socket:socket:type2_combo:output=50 kw with a fixed text, namely 'Type 2 CCS (mennekes) outputs at most 50 kw' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", + "value": "50 kw" + }, { "key": "authentication:membership_card", "description": "Layer 'Charging stations' shows authentication:membership_card=yes with a fixed text, namely 'Authentication by a membership card' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", @@ -274,6 +617,44 @@ "description": "Layer 'Charging stations' shows opening_hours=24/7 with a fixed text, namely '24/7 opened (including holidays)' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", "value": "24/7" }, + { + "key": "charge", + "description": "Layer 'Charging stations' shows and asks freeform values for key 'charge' (in the MapComplete.osm.be theme 'Charging stations')" + }, + { + "key": "fee", + "description": "Layer 'Charging stations' shows fee=no&charge= with a fixed text, namely 'Free to use' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", + "value": "no" + }, + { + "key": "charge", + "description": "Layer 'Charging stations' shows fee=no&charge= with a fixed text, namely 'Free to use' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations') Picking this answer will delete the key charge.", + "value": "" + }, + { + "key": "payment:cash", + "description": "Layer 'Charging stations' shows payment:cash=yes with a fixed text, namely 'Cash is accepted here' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", + "value": "yes" + }, + { + "key": "payment:cards", + "description": "Layer 'Charging stations' shows payment:cards=yes with a fixed text, namely 'Payment cards are accepted here' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", + "value": "yes" + }, + { + "key": "payment:app", + "description": "Layer 'Charging stations' shows payment:app=yes with a fixed text, namely 'Payment is done using a dedicated app' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", + "value": "yes" + }, + { + "key": "maxstay", + "description": "Layer 'Charging stations' shows and asks freeform values for key 'maxstay' (in the MapComplete.osm.be theme 'Charging stations')" + }, + { + "key": "maxstay", + "description": "Layer 'Charging stations' shows maxstay=unlimited with a fixed text, namely 'No timelimit on leaving your vehicle here' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", + "value": "unlimited" + }, { "key": "network", "description": "Layer 'Charging stations' shows and asks freeform values for key 'network' (in the MapComplete.osm.be theme 'Charging stations')" @@ -412,4 +793,4 @@ "value": "yes" } ] -} \ No newline at end of file +} diff --git a/Docs/TagInfo/mapcomplete_observation_towers.json b/Docs/TagInfo/mapcomplete_observation_towers.json new file mode 100644 index 0000000000..ecf00cd66d --- /dev/null +++ b/Docs/TagInfo/mapcomplete_observation_towers.json @@ -0,0 +1,124 @@ +{ + "data_format": 1, + "project": { + "name": "MapComplete Observation towers", + "description": "Publicly accessible towers to enjoy the view", + "project_url": "https://mapcomplete.osm.be/observation_towers", + "doc_url": "https://github.com/pietervdvn/MapComplete/tree/master/assets/themes/", + "icon_url": "https://mapcomplete.osm.be/assets/layers/observation_tower/Tower_observation.svg", + "contact_name": "Pieter Vander Vennet, ", + "contact_email": "pietervdvn@posteo.net" + }, + "tags": [ + { + "key": "tower:type", + "description": "The MapComplete theme Observation towers has a layer Observation towers showing features with this tag", + "value": "observation" + }, + { + "key": "image", + "description": "The layer 'Observation towers allows to upload images and adds them under the 'image'-tag (and image:0, image:1, ... for multiple images). Furhtermore, this layer shows images based on the keys image, wikidata, wikipedia, wikimedia_commons and mapillary" + }, + { + "key": "mapillary", + "description": "The layer 'Observation towers allows to upload images and adds them under the 'image'-tag (and image:0, image:1, ... for multiple images). Furhtermore, this layer shows images based on the keys image, wikidata, wikipedia, wikimedia_commons and mapillary" + }, + { + "key": "wikidata", + "description": "The layer 'Observation towers allows to upload images and adds them under the 'image'-tag (and image:0, image:1, ... for multiple images). Furhtermore, this layer shows images based on the keys image, wikidata, wikipedia, wikimedia_commons and mapillary" + }, + { + "key": "wikipedia", + "description": "The layer 'Observation towers allows to upload images and adds them under the 'image'-tag (and image:0, image:1, ... for multiple images). Furhtermore, this layer shows images based on the keys image, wikidata, wikipedia, wikimedia_commons and mapillary" + }, + { + "key": "name", + "description": "Layer 'Observation towers' shows and asks freeform values for key 'name' (in the MapComplete.osm.be theme 'Observation towers')" + }, + { + "key": "noname", + "description": "Layer 'Observation towers' shows noname=yes with a fixed text, namely 'This tower doesn't have a specific name' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Observation towers')", + "value": "yes" + }, + { + "key": "height", + "description": "Layer 'Observation towers' shows and asks freeform values for key 'height' (in the MapComplete.osm.be theme 'Observation towers')" + }, + { + "key": "operator", + "description": "Layer 'Observation towers' shows and asks freeform values for key 'operator' (in the MapComplete.osm.be theme 'Observation towers')" + }, + { + "key": "website", + "description": "Layer 'Observation towers' shows and asks freeform values for key 'website' (in the MapComplete.osm.be theme 'Observation towers')" + }, + { + "key": "charge", + "description": "Layer 'Observation towers' shows and asks freeform values for key 'charge' (in the MapComplete.osm.be theme 'Observation towers')" + }, + { + "key": "fee", + "description": "Layer 'Observation towers' shows fee=no&charge= with a fixed text, namely 'Free to visit' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Observation towers')", + "value": "no" + }, + { + "key": "charge", + "description": "Layer 'Observation towers' shows fee=no&charge= with a fixed text, namely 'Free to visit' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Observation towers') Picking this answer will delete the key charge.", + "value": "" + }, + { + "key": "payment:cash", + "description": "Layer 'Observation towers' shows payment:cash=yes with a fixed text, namely 'Cash is accepted here' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Observation towers')", + "value": "yes" + }, + { + "key": "payment:cards", + "description": "Layer 'Observation towers' shows payment:cards=yes with a fixed text, namely 'Payment cards are accepted here' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Observation towers')", + "value": "yes" + }, + { + "key": "payment:app", + "description": "Layer 'Observation towers' shows payment:app=yes with a fixed text, namely 'Payment is done using a dedicated app' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Observation towers')", + "value": "yes" + }, + { + "key": "wheelchair", + "description": "Layer 'Observation towers' shows wheelchair=designated with a fixed text, namely 'This place is specially adapated for wheelchair users' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Observation towers')", + "value": "designated" + }, + { + "key": "wheelchair", + "description": "Layer 'Observation towers' shows wheelchair=yes with a fixed text, namely 'This place is easily reachable with a wheelchair' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Observation towers')", + "value": "yes" + }, + { + "key": "wheelchair", + "description": "Layer 'Observation towers' shows wheelchair=limited with a fixed text, namely 'It is possible to reach this place in a wheelchair, but it is not easy' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Observation towers')", + "value": "limited" + }, + { + "key": "wheelchair", + "description": "Layer 'Observation towers' shows wheelchair=no with a fixed text, namely 'This place is not reachable with a wheelchair' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Observation towers')", + "value": "no" + }, + { + "key": "service:bicycle:cleaning:charge", + "description": "Layer 'Observation towers' shows and asks freeform values for key 'service:bicycle:cleaning:charge' (in the MapComplete.osm.be theme 'Observation towers')" + }, + { + "key": "service:bicycle:cleaning:fee", + "description": "Layer 'Observation towers' shows service:bicycle:cleaning:fee=no&service:bicycle:cleaning:charge= with a fixed text, namely 'The cleaning service is free to use' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Observation towers')", + "value": "no&service:bicycle:cleaning:charge=" + }, + { + "key": "service:bicycle:cleaning:fee", + "description": "Layer 'Observation towers' shows service:bicycle:cleaning:fee=no& with a fixed text, namely 'Free to use' (in the MapComplete.osm.be theme 'Observation towers')", + "value": "no&" + }, + { + "key": "service:bicycle:cleaning:fee", + "description": "Layer 'Observation towers' shows service:bicycle:cleaning:fee=yes with a fixed text, namely 'The cleaning service has a fee' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Observation towers')", + "value": "yes" + } + ] +} \ No newline at end of file diff --git a/Docs/TagInfo/mapcomplete_toilets.json b/Docs/TagInfo/mapcomplete_toilets.json index 161489fb52..713b2b65ba 100644 --- a/Docs/TagInfo/mapcomplete_toilets.json +++ b/Docs/TagInfo/mapcomplete_toilets.json @@ -55,6 +55,11 @@ "description": "Layer 'Toilets' shows access=key with a fixed text, namely 'Accessible, but one has to ask a key to enter' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Open Toilet Map')", "value": "key" }, + { + "key": "access", + "description": "Layer 'Toilets' shows access=public with a fixed text, namely 'Public access' (in the MapComplete.osm.be theme 'Open Toilet Map')", + "value": "public" + }, { "key": "fee", "description": "Layer 'Toilets' shows fee=yes with a fixed text, namely 'These are paid toilets' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Open Toilet Map')", diff --git a/Docs/TagInfo/mapcomplete_uk_addresses.json b/Docs/TagInfo/mapcomplete_uk_addresses.json new file mode 100644 index 0000000000..92774e2f19 --- /dev/null +++ b/Docs/TagInfo/mapcomplete_uk_addresses.json @@ -0,0 +1,39 @@ +{ + "data_format": 1, + "project": { + "name": "MapComplete UK Addresses", + "description": "Help to build an open dataset of UK addresses", + "project_url": "https://mapcomplete.osm.be/uk_addresses", + "doc_url": "https://github.com/pietervdvn/MapComplete/tree/master/assets/themes/", + "icon_url": "https://mapcomplete.osm.be/assets/themes/uk_addresses/housenumber_unknown.svg", + "contact_name": "Pieter Vander Vennet, Pieter Vander Vennet, Rob Nickerson, Russ Garrett", + "contact_email": "pietervdvn@posteo.net" + }, + "tags": [ + { + "key": "inspireid", + "description": "The MapComplete theme UK Addresses has a layer Addresses to check showing features with this tag" + }, + { + "key": "addr:housenumber", + "description": "The MapComplete theme UK Addresses has a layer Known addresses in OSM showing features with this tag" + }, + { + "key": "addr:street", + "description": "The MapComplete theme UK Addresses has a layer Known addresses in OSM showing features with this tag" + }, + { + "key": "addr:housenumber", + "description": "Layer 'Known addresses in OSM' shows and asks freeform values for key 'addr:housenumber' (in the MapComplete.osm.be theme 'UK Addresses')" + }, + { + "key": "nohousenumber", + "description": "Layer 'Known addresses in OSM' shows nohousenumber=yes with a fixed text, namely 'This building has no house number' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'UK Addresses')", + "value": "yes" + }, + { + "key": "addr:street", + "description": "Layer 'Known addresses in OSM' shows and asks freeform values for key 'addr:street' (in the MapComplete.osm.be theme 'UK Addresses')" + } + ] +} \ No newline at end of file diff --git a/Docs/TagInfo/mapcomplete_waste_basket.json b/Docs/TagInfo/mapcomplete_waste_basket.json index a1a4c91494..eeebd2e5b9 100644 --- a/Docs/TagInfo/mapcomplete_waste_basket.json +++ b/Docs/TagInfo/mapcomplete_waste_basket.json @@ -14,6 +14,36 @@ "key": "amenity", "description": "The MapComplete theme Waste Basket has a layer Waste Basket showing features with this tag", "value": "waste_basket" + }, + { + "key": "waste", + "description": "Layer 'Waste Basket' shows waste= with a fixed text, namely 'A waste basket for general waste' (in the MapComplete.osm.be theme 'Waste Basket') Picking this answer will delete the key waste.", + "value": "" + }, + { + "key": "waste", + "description": "Layer 'Waste Basket' shows waste=trash with a fixed text, namely 'A waste basket for general waste' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Waste Basket')", + "value": "trash" + }, + { + "key": "waste", + "description": "Layer 'Waste Basket' shows waste=dog_excrement with a fixed text, namely 'A waste basket for dog excrements' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Waste Basket')", + "value": "dog_excrement" + }, + { + "key": "waste", + "description": "Layer 'Waste Basket' shows waste=cigarettes with a fixed text, namely 'A waste basket for cigarettes' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Waste Basket')", + "value": "cigarettes" + }, + { + "key": "waste", + "description": "Layer 'Waste Basket' shows waste=drugs with a fixed text, namely 'A waste basket for drugs' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Waste Basket')", + "value": "drugs" + }, + { + "key": "waste", + "description": "Layer 'Waste Basket' shows waste=sharps with a fixed text, namely 'A waste basket for needles and other sharp objects' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Waste Basket')", + "value": "sharps" } ] } \ No newline at end of file diff --git a/Docs/Tags_format.md b/Docs/Tags_format.md index 4b42959acd..25db913cd6 100644 --- a/Docs/Tags_format.md +++ b/Docs/Tags_format.md @@ -31,6 +31,8 @@ Strict not equals To check if a key does _not_ equal a certain value, use `key!=value`. This is converted behind the scenes to `key!~^value$` +If `key` is not present or empty, this will match too. + ### If key is present This implies that, to check if a key is present, `key!=` can be used. This will only match if the key is present and not diff --git a/Docs/Tools/GenerateSeries.ts b/Docs/Tools/GenerateSeries.ts index 5cced5a6b9..73e97e9a43 100644 --- a/Docs/Tools/GenerateSeries.ts +++ b/Docs/Tools/GenerateSeries.ts @@ -75,9 +75,7 @@ class StatsDownloader { while (url) { ScriptUtils.erasableLog(`Downloading stats for ${year}-${month}, page ${page} ${url}`) - const result = await ScriptUtils.DownloadJSON(url, { - headers: headers - }) + const result = await ScriptUtils.DownloadJSON(url, headers) page++; allFeatures.push(...result.features) if (result.features === undefined) { diff --git a/Docs/URL_Parameters.md b/Docs/URL_Parameters.md index 6b0afe450f..f353809015 100644 --- a/Docs/URL_Parameters.md +++ b/Docs/URL_Parameters.md @@ -1,3 +1,4 @@ + URL-parameters and URL-hash ============================ @@ -8,8 +9,8 @@ What is a URL parameter? URL-parameters are extra parts of the URL used to set the state. -For example, if the url is `https://mapcomplete.osm.be/cyclofix?lat=51.0&lon=4.3&z=5&test=true#node/1234`, the -URL-parameters are stated in the part between the `?` and the `#`. There are multiple, all separated by `&`, namely: +For example, if the url is `https://mapcomplete.osm.be/cyclofix?lat=51.0&lon=4.3&z=5&test=true#node/1234`, +the URL-parameters are stated in the part between the `?` and the `#`. There are multiple, all separated by `&`, namely: - The url-parameter `lat` is `51.0` in this instance - The url-parameter `lon` is `4.3` in this instance @@ -19,175 +20,169 @@ URL-parameters are stated in the part between the `?` and the `#`. There are mul Finally, the URL-hash is the part after the `#`. It is `node/1234` in this case. -download-control-toggle + download-control-toggle ------------------------- -Whether or not the download panel is shown The default value is _false_ + Whether or not the download panel is shown The default value is _false_ -filter-toggle + filter-toggle --------------- -Whether or not the filter view is shown The default value is _false_ + Whether or not the filter view is shown The default value is _false_ -tab + tab ----- -The tab that is shown in the welcome-message. 0 = the explanation of the theme,1 = OSM-credits, 2 = sharescreen, 3 = -more themes, 4 = about mapcomplete (user must be logged in and have >50 changesets) The default value is _0_ + The tab that is shown in the welcome-message. 0 = the explanation of the theme,1 = OSM-credits, 2 = sharescreen, 3 = more themes, 4 = about mapcomplete (user must be logged in and have >50 changesets) The default value is _0_ -z + z --- -The initial/current zoom level The default value is _0_ + The initial/current zoom level The default value is _0_ -lat + lat ----- -The initial/current latitude The default value is _0_ + The initial/current latitude The default value is _0_ -lon + lon ----- -The initial/current longitude of the app The default value is _0_ + The initial/current longitude of the app The default value is _0_ -fs-userbadge + fs-userbadge -------------- -Disables/Enables the user information pill (userbadge) at the top left. Disabling this disables logging in and thus -disables editing all together, effectively putting MapComplete into read-only mode. The default value is _true_ + Disables/Enables the user information pill (userbadge) at the top left. Disabling this disables logging in and thus disables editing all together, effectively putting MapComplete into read-only mode. The default value is _true_ -fs-search + fs-search ----------- -Disables/Enables the search bar The default value is _true_ + Disables/Enables the search bar The default value is _true_ -fs-background + fs-background --------------- -Disables/Enables the background layer control The default value is _true_ + Disables/Enables the background layer control The default value is _true_ -fs-filter + fs-filter ----------- -Disables/Enables the filter The default value is _true_ + Disables/Enables the filter The default value is _true_ -fs-add-new + fs-add-new ------------ -Disables/Enables the 'add new feature'-popup. (A theme without presets might not have it in the first place) The default -value is _true_ + Disables/Enables the 'add new feature'-popup. (A theme without presets might not have it in the first place) The default value is _true_ -fs-welcome-message + fs-welcome-message -------------------- -Disables/enables the help menu or welcome message The default value is _true_ + Disables/enables the help menu or welcome message The default value is _true_ -fs-iframe + fs-iframe ----------- -Disables/Enables the iframe-popup The default value is _false_ + Disables/Enables the iframe-popup The default value is _false_ -fs-more-quests + fs-more-quests ---------------- -Disables/Enables the 'More Quests'-tab in the welcome message The default value is _true_ + Disables/Enables the 'More Quests'-tab in the welcome message The default value is _true_ -fs-share-screen + fs-share-screen ----------------- -Disables/Enables the 'Share-screen'-tab in the welcome message The default value is _true_ + Disables/Enables the 'Share-screen'-tab in the welcome message The default value is _true_ -fs-geolocation + fs-geolocation ---------------- -Disables/Enables the geolocation button The default value is _true_ + Disables/Enables the geolocation button The default value is _true_ -fs-all-questions + fs-all-questions ------------------ -Always show all questions The default value is _false_ + Always show all questions The default value is _false_ -fs-export + fs-export ----------- -Enable the export as GeoJSON and CSV button The default value is _false_ + Enable the export as GeoJSON and CSV button The default value is _false_ -fs-pdf + fs-pdf -------- -Enable the PDF download button The default value is _false_ + Enable the PDF download button The default value is _false_ -test + test ------ -If true, 'dryrun' mode is activated. The app will behave as normal, except that changes to OSM will be printed onto the -console instead of actually uploaded to osm.org The default value is _false_ + If true, 'dryrun' mode is activated. The app will behave as normal, except that changes to OSM will be printed onto the console instead of actually uploaded to osm.org The default value is _false_ -debug + debug ------- -If true, shows some extra debugging help such as all the available tags on every object The default value is _false_ + If true, shows some extra debugging help such as all the available tags on every object The default value is _false_ -fake-user + fake-user ----------- -If true, 'dryrun' mode is activated and a fake user account is loaded The default value is _false_ + If true, 'dryrun' mode is activated and a fake user account is loaded The default value is _false_ -backend + backend --------- -The OSM backend to use - can be used to redirect mapcomplete to the testing backend when using 'osm-test' The default -value is _osm_ + The OSM backend to use - can be used to redirect mapcomplete to the testing backend when using 'osm-test' The default value is _osm_ -overpassUrl + overpassUrl ------------- -Point mapcomplete to a different overpass-instance. Example: https://overpass-api.de/api/interpreter The default value -is _https://overpass-api.de/api/interpreter_ + Point mapcomplete to a different overpass-instance. Example: https://overpass-api.de/api/interpreter The default value is _https://overpass-api.de/api/interpreter_ -overpassTimeout + overpassTimeout ----------------- -Set a different timeout (in seconds) for queries in overpass The default value is _30_ + Set a different timeout (in seconds) for queries in overpass The default value is _30_ -custom-css + custom-css ------------ -If specified, the custom css from the given link will be loaded additionaly The default value is __ + If specified, the custom css from the given link will be loaded additionaly The default value is __ -background + background ------------ -The id of the background layer to start with The default value is _osm_ + The id of the background layer to start with The default value is _osm_ -layer- + layer- ------------------ -Wether or not the layer with id is shown The default value is _true_ Generated from QueryParameters \ No newline at end of file + Wether or not the layer with id is shown The default value is _true_ Generated from QueryParameters \ No newline at end of file diff --git a/Docs/wikiIndex.txt b/Docs/wikiIndex.txt index f9d8ab6da8..4bd9edfad8 100644 --- a/Docs/wikiIndex.txt +++ b/Docs/wikiIndex.txt @@ -4,8 +4,8 @@ {{service_item |name= [https://mapcomplete.osm.be/personal personal] |region= Worldwide -|lang= {{#language:en|en}}, {{#language:nl|en}}, {{#language:es|en}}, {{#language:ca|en}}, {{#language:gl|en}}, {{#language:de|en}} -|descr= A MapComplete theme: Create a personal theme based on all the available layers of all themes +|lang= {{#language:en|en}}, {{#language:nl|en}}, {{#language:es|en}}, {{#language:ca|en}}, {{#language:gl|en}}, {{#language:fr|en}}, {{#language:de|en}}, {{#language:ja|en}}, {{#language:zh_Hant|en}}, {{#language:ru|en}} +|descr= A MapComplete theme: Create a personal theme based on all the available layers of all themes. In order to show some data, open [[#filter]] |material= {{yes|[https://mapcomplete.osm.be/ Yes]}} |image= MapComplete_Screenshot.png |genre= POI, editor, personal @@ -13,25 +13,29 @@ {{service_item |name= [https://mapcomplete.osm.be/cyclofix cyclofix] |region= Worldwide -|lang= {{#language:en|en}}, {{#language:nl|en}}, {{#language:fr|en}}, {{#language:gl|en}}, {{#language:de|en}} +|lang= {{#language:en|en}}, {{#language:nl|en}}, {{#language:fr|en}}, {{#language:gl|en}}, {{#language:de|en}}, {{#language:ru|en}}, {{#language:ja|en}}, {{#language:zh_Hant|en}}, {{#language:it|en}} |descr= A MapComplete theme: The goal of this map is to present cyclists with an easy-to-use solution to find the appropriate infrastructure for their needs.

You can track your precise location (mobile only) and select layers that are relevant for you in the bottom left corner. You can also use this tool to add or edit pins (points of interest) to the map and provide more data by answering the questions.

All changes you make will automatically be saved in the global database of OpenStreetMap and can be freely re-used by others.

For more information about the cyclofix project, go to [[https://cyclofix.osm.be/]]. |material= {{yes|[https://mapcomplete.osm.be/ Yes]}} |image= MapComplete_Screenshot.png |genre= POI, editor, cyclofix }} {{service_item -|name= [https://mapcomplete.osm.be/aed aed] +|name= [https://mapcomplete.osm.be/hailhydrant hailhydrant] |region= Worldwide -|lang= {{#language:en|en}}, {{#language:ca|en}}, {{#language:es|en}}, {{#language:fr|en}}, {{#language:nl|en}}, {{#language:de|en}} -|descr= A MapComplete theme: On this map, one can find and mark nearby defibrillators -|material= {{yes|[https://mapcomplete.osm.be/ Yes]}} +|lang= {{#language:en|en}}, {{#language:ja|en}}, {{#language:zh_Hant|en}}, {{#language:ru|en}}, {{#language:fr|en}}, {{#language:nb_NO|en}}, {{#language:it|en}}, {{#language:id|en}} +|descr= A MapComplete theme: On this map you can find and update hydrants, fire stations, ambulance stations, and extinguishers in your favorite neighborhoods. + +You can track your precise location (mobile only) and select layers that are relevant for you in the bottom left corner. You can also use this tool to add or edit pins (points of interest) to the map and provide additional details by answering available questions. + +All changes you make will automatically be saved in the global database of OpenStreetMap and can be freely re-used by others. +|material= {{yes|[https://mapcomplete.osm.be/ Yes, by Erwin Olario;]}} |image= MapComplete_Screenshot.png -|genre= POI, editor, aed +|genre= POI, editor, hailhydrant }} {{service_item |name= [https://mapcomplete.osm.be/bookcases bookcases] |region= Worldwide -|lang= {{#language:en|en}}, {{#language:nl|en}}, {{#language:de|en}}, {{#language:fr|en}} +|lang= {{#language:en|en}}, {{#language:nl|en}}, {{#language:de|en}}, {{#language:fr|en}}, {{#language:ru|en}}, {{#language:ja|en}}, {{#language:zh_Hant|en}}, {{#language:it|en}}, {{#language:pt_BR|en}} |descr= A MapComplete theme: A public bookcase is a small streetside cabinet, box, old phone boot or some other objects where books are stored. Everyone can place or take a book. This map aims to collect all these bookcases. You can discover new bookcases nearby and, with a free OpenStreetMap account, quickly add your favourite bookcases. |material= {{yes|[https://mapcomplete.osm.be/ Yes]}} |image= MapComplete_Screenshot.png @@ -40,47 +44,173 @@ {{service_item |name= [https://mapcomplete.osm.be/toilets toilets] |region= Worldwide -|lang= {{#language:en|en}}, {{#language:de|en}}, {{#language:fr|en}} +|lang= {{#language:en|en}}, {{#language:de|en}}, {{#language:fr|en}}, {{#language:nl|en}}, {{#language:ru|en}}, {{#language:ja|en}}, {{#language:zh_Hant|en}}, {{#language:pl|en}} |descr= A MapComplete theme: A map of public toilets |material= {{yes|[https://mapcomplete.osm.be/ Yes]}} |image= MapComplete_Screenshot.png |genre= POI, editor, toilets }} {{service_item -|name= [https://mapcomplete.osm.be/artworks artworks] +|name= [https://mapcomplete.osm.be/aed aed] |region= Worldwide -|lang= {{#language:en|en}}, {{#language:nl|en}}, {{#language:fr|en}}, {{#language:de|en}} -|descr= A MapComplete theme: Welcome to Open Artwork Map, a map of statues, busts, grafittis, ... all over the world +|lang= {{#language:en|en}}, {{#language:ca|en}}, {{#language:es|en}}, {{#language:fr|en}}, {{#language:nl|en}}, {{#language:de|en}}, {{#language:hu|en}}, {{#language:id|en}}, {{#language:it|en}}, {{#language:ru|en}}, {{#language:ja|en}}, {{#language:zh_Hant|en}}, {{#language:nb_NO|en}}, {{#language:sv|en}}, {{#language:pl|en}}, {{#language:pt_BR|en}} +|descr= A MapComplete theme: On this map, one can find and mark nearby defibrillators |material= {{yes|[https://mapcomplete.osm.be/ Yes]}} |image= MapComplete_Screenshot.png -|genre= POI, editor, artworks +|genre= POI, editor, aed +}} +{{service_item +|name= [https://mapcomplete.osm.be/artwork artwork] +|region= Worldwide +|lang= {{#language:en|en}}, {{#language:nl|en}}, {{#language:fr|en}}, {{#language:de|en}}, {{#language:hu|en}}, {{#language:id|en}}, {{#language:it|en}}, {{#language:ru|en}}, {{#language:ja|en}}, {{#language:zh_Hant|en}}, {{#language:sv|en}}, {{#language:pl|en}}, {{#language:es|en}}, {{#language:nb_NO|en}} +|descr= A MapComplete theme: Welcome to Open Artwork Map, a map of statues, busts, grafittis and other artwork all over the world +|material= {{yes|[https://mapcomplete.osm.be/ Yes]}} +|image= MapComplete_Screenshot.png +|genre= POI, editor, artwork +}} +{{service_item +|name= [https://mapcomplete.osm.be/benches benches] +|region= Worldwide +|lang= {{#language:en|en}}, {{#language:de|en}}, {{#language:fr|en}}, {{#language:nl|en}}, {{#language:it|en}}, {{#language:ru|en}}, {{#language:ja|en}}, {{#language:zh_Hant|en}}, {{#language:nb_NO|en}}, {{#language:pt_BR|en}} +|descr= A MapComplete theme: This map shows all benches that are recorded in OpenStreetMap: Individual benches, and benches belonging to public transport stops or shelters. With an OpenStreetMap account, you can map new benches or edit details of existing benches. +|material= {{yes|[https://mapcomplete.osm.be/ Yes, by Florian Edelmann;]}} +|image= MapComplete_Screenshot.png +|genre= POI, editor, benches +}} +{{service_item +|name= [https://mapcomplete.osm.be/bicyclelib bicyclelib] +|region= Worldwide +|lang= {{#language:en|en}}, {{#language:nl|en}}, {{#language:it|en}}, {{#language:ru|en}}, {{#language:ja|en}}, {{#language:fr|en}}, {{#language:zh_Hant|en}}, {{#language:nb_NO|en}}, {{#language:de|en}}, {{#language:pt_BR|en}} +|descr= A MapComplete theme: A bicycle library is a place where bicycles can be lent, often for a small yearly fee. A notable use case are bicycle libraries for kids, which allows them to change for a bigger bike when they've outgrown their current bike +|material= {{yes|[https://mapcomplete.osm.be/ Yes]}} +|image= MapComplete_Screenshot.png +|genre= POI, editor, bicyclelib +}} +{{service_item +|name= [https://mapcomplete.osm.be/binoculars binoculars] +|region= Worldwide +|lang= {{#language:en|en}}, {{#language:nl|en}} +|descr= A MapComplete theme: A map with binoculars fixed in place with a pole. It can typically be found on touristic locations, viewpoints, on top of panoramic towers or occasionally on a nature reserve. +|material= {{yes|[https://mapcomplete.osm.be/ Yes]}} +|image= MapComplete_Screenshot.png +|genre= POI, editor, binoculars +}} +{{service_item +|name= [https://mapcomplete.osm.be/cafes_and_pubs cafes_and_pubs] +|region= Worldwide +|lang= {{#language:nl|en}}, {{#language:fr|en}}, {{#language:en|en}} +|descr= A MapComplete theme: Cafés, kroegen en drinkgelegenheden +|material= {{yes|[https://mapcomplete.osm.be/ Yes]}} +|image= MapComplete_Screenshot.png +|genre= POI, editor, cafes_and_pubs +}} +{{service_item +|name= [https://mapcomplete.osm.be/campersite campersite] +|region= Worldwide +|lang= {{#language:en|en}}, {{#language:nl|en}}, {{#language:it|en}}, {{#language:ru|en}}, {{#language:ja|en}}, {{#language:fr|en}}, {{#language:zh_Hant|en}}, {{#language:pt_BR|en}}, {{#language:id|en}}, {{#language:nb_NO|en}} +|descr= A MapComplete theme: This site collects all official camper stopover places and places where you can dump grey and black water. You can add details about the services provided and the cost. Add pictures and reviews. This is a website and a webapp. The data is stored in OpenStreetMap, so it will be free forever and can be re-used by any app. +|material= {{yes|[https://mapcomplete.osm.be/ Yes, by joost schouppe;]}} +|image= MapComplete_Screenshot.png +|genre= POI, editor, campersite +}} +{{service_item +|name= [https://mapcomplete.osm.be/charging_stations charging_stations] +|region= Worldwide +|lang= {{#language:en|en}}, {{#language:id|en}}, {{#language:it|en}}, {{#language:ja|en}}, {{#language:ru|en}}, {{#language:zh_Hant|en}}, {{#language:it|en}}, {{#language:nl|en}}, {{#language:nb_NO|en}} +|descr= A MapComplete theme: On this open map, one can find and mark information about charging stations +|material= {{yes|[https://mapcomplete.osm.be/ Yes]}} +|image= MapComplete_Screenshot.png +|genre= POI, editor, charging_stations +}} +{{service_item +|name= [https://mapcomplete.osm.be/climbing climbing] +|region= Worldwide +|lang= {{#language:nl|en}}, {{#language:de|en}}, {{#language:en|en}}, {{#language:ru|en}}, {{#language:ja|en}}, {{#language:zh_Hant|en}}, {{#language:nb_NO|en}}, {{#language:it|en}}, {{#language:ca|en}}, {{#language:fr|en}}, {{#language:id|en}} +|descr= A MapComplete theme: On this map you will find various climbing opportunities such as climbing gyms, bouldering halls and rocks in nature. +|material= {{yes|[https://mapcomplete.osm.be/ Yes, by Christian Neumann ;]}} +|image= MapComplete_Screenshot.png +|genre= POI, editor, climbing +}} +{{service_item +|name= [https://mapcomplete.osm.be/cycle_infra cycle_infra] +|region= Worldwide +|lang= {{#language:en|en}}, {{#language:nl|en}} +|descr= A MapComplete theme: A map where you can view and edit things related to the bicycle infrastructure. Made during #osoc21. +|material= {{yes|[https://mapcomplete.osm.be/ Yes]}} +|image= MapComplete_Screenshot.png +|genre= POI, editor, cycle_infra +}} +{{service_item +|name= [https://mapcomplete.osm.be/cyclestreets cyclestreets] +|region= Worldwide +|lang= {{#language:nl|en}}, {{#language:en|en}}, {{#language:ja|en}}, {{#language:zh_Hant|en}}, {{#language:nb_NO|en}}, {{#language:it|en}}, {{#language:ru|en}} +|descr= A MapComplete theme: A cyclestreet is is a street where motorized traffic is not allowed to overtake cyclists. They are signposted by a special traffic sign. Cyclestreets can be found in the Netherlands and Belgium, but also in Germany and France. +|material= {{yes|[https://mapcomplete.osm.be/ Yes]}} +|image= MapComplete_Screenshot.png +|genre= POI, editor, cyclestreets +}} +{{service_item +|name= [https://mapcomplete.osm.be/drinking_water drinking_water] +|region= Worldwide +|lang= {{#language:en|en}}, {{#language:nl|en}}, {{#language:fr|en}}, {{#language:ru|en}}, {{#language:ja|en}}, {{#language:zh_Hant|en}}, {{#language:it|en}} +|descr= A MapComplete theme: On this map, publicly accessible drinking water spots are shown and can be easily added +|material= {{yes|[https://mapcomplete.osm.be/ Yes]}} +|image= MapComplete_Screenshot.png +|genre= POI, editor, drinking_water +}} +{{service_item +|name= [https://mapcomplete.osm.be/facadegardens facadegardens] +|region= Worldwide +|lang= {{#language:nl|en}}, {{#language:en|en}}, {{#language:ja|en}}, {{#language:zh_Hant|en}}, {{#language:it|en}}, {{#language:fr|en}}, {{#language:nb_NO|en}}, {{#language:ru|en}} +|descr= A MapComplete theme: [[https://nl.wikipedia.org/wiki/Geveltuin' target=_blank>Facade gardens, green facades and trees in the city not only bring peace and quiet, but also a more beautiful city, greater biodiversity, a cooling effect and better air quality.
Klimaan VZW and Mechelen Klimaatneutraal want to map existing and new facade gardens as an example for people who want to build their own garden or for city walkers who love nature.
More info about the project at klimaan.be. +|material= {{yes|[https://mapcomplete.osm.be/ Yes, by joost schouppe; stla;]}} +|image= MapComplete_Screenshot.png +|genre= POI, editor, facadegardens +}} +{{service_item +|name= [https://mapcomplete.osm.be/food food] +|region= Worldwide +|lang= {{#language:nl|en}}, {{#language:fr|en}}, {{#language:en|en}} +|descr= A MapComplete theme: Restaurants en fast food +|material= {{yes|[https://mapcomplete.osm.be/ Yes]}} +|image= MapComplete_Screenshot.png +|genre= POI, editor, food +}} +{{service_item +|name= [https://mapcomplete.osm.be/fritures fritures] +|region= Worldwide +|lang= {{#language:nl|en}}, {{#language:fr|en}}, {{#language:en|en}}, {{#language:ja|en}}, {{#language:ca|en}}, {{#language:id|en}}, {{#language:ru|en}}, {{#language:it|en}}, {{#language:nb_NO|en}} +|descr= A MapComplete theme: Op deze kaart vind je je favoriete frituur! +|material= {{yes|[https://mapcomplete.osm.be/ Yes]}} +|image= MapComplete_Screenshot.png +|genre= POI, editor, fritures }} {{service_item |name= [https://mapcomplete.osm.be/ghostbikes ghostbikes] |region= Worldwide -|lang= {{#language:en|en}}, {{#language:nl|en}}, {{#language:de|en}} +|lang= {{#language:en|en}}, {{#language:nl|en}}, {{#language:de|en}}, {{#language:ja|en}}, {{#language:nb_NO|en}}, {{#language:zh_Hant|en}}, {{#language:fr|en}}, {{#language:eo|en}}, {{#language:es|en}}, {{#language:fi|en}}, {{#language:gl|en}}, {{#language:hu|en}}, {{#language:it|en}}, {{#language:pl|en}}, {{#language:pt_BR|en}}, {{#language:ru|en}}, {{#language:sv|en}} |descr= A MapComplete theme: A ghost bike is a memorial for a cyclist who died in a traffic accident, in the form of a white bicycle placed permanently near the accident location.

On this map, one can see all the ghost bikes which are known by OpenStreetMap. Is a ghost bike missing? Everyone can add or update information here - you only need to have a (free) OpenStreetMap account. |material= {{yes|[https://mapcomplete.osm.be/ Yes]}} |image= MapComplete_Screenshot.png |genre= POI, editor, ghostbikes }} {{service_item -|name= [https://mapcomplete.osm.be/shops shops] +|name= [https://mapcomplete.osm.be/hackerspaces hackerspaces] |region= Worldwide -|lang= {{#language:en|en}}, {{#language:fr|en}} -|descr= A MapComplete theme: On this map, one can mark basic information about shops, add opening hours and phone numbers +|lang= {{#language:en|en}} +|descr= A MapComplete theme: On this map you can see hackerspaces, add a new hackerspace or update data directly |material= {{yes|[https://mapcomplete.osm.be/ Yes]}} |image= MapComplete_Screenshot.png -|genre= POI, editor, shops +|genre= POI, editor, hackerspaces }} {{service_item -|name= [https://mapcomplete.osm.be/drinking_water drinking_water] +|name= [https://mapcomplete.osm.be/maps maps] |region= Worldwide -|lang= {{#language:en|en}}, {{#language:nl|en}} -|descr= A MapComplete theme: On this map, publicly accessible drinkging water spots are shown and can be easily added +|lang= {{#language:en|en}}, {{#language:nl|en}}, {{#language:fr|en}}, {{#language:ja|en}}, {{#language:zh_Hant|en}}, {{#language:ru|en}} +|descr= A MapComplete theme: On this map you can find all maps OpenStreetMap knows - typically a big map on an information board showing the area, city or region, e.g. a tourist map on the back of a billboard, a map of a nature reserve, a map of cycling networks in the region, ...)

If a map is missing, you can easily map this map on OpenStreetMap. |material= {{yes|[https://mapcomplete.osm.be/ Yes]}} |image= MapComplete_Screenshot.png -|genre= POI, editor, drinking_water +|genre= POI, editor, maps }} {{service_item |name= [https://mapcomplete.osm.be/nature nature] @@ -92,111 +222,93 @@ |genre= POI, editor, nature }} {{service_item -|name= [https://mapcomplete.osm.be/fietsstraten fietsstraten] -|region= Worldwide -|lang= {{#language:nl|en}} -|descr= A MapComplete theme: Een fietsstraat is een straat waar
  • automobilisten geen fietsers mogen inhalen
  • Er een maximumsnelheid van 30km/u geldt
  • Fietsers gemotoriseerde voortuigen links mogen inhalen
  • Fietsers nog steeds voorrang aan rechts moeten verlenen - ook aan auto's en voetgangers op het zebrapad


Op deze open kaart kan je alle gekende fietsstraten zien en kan je ontbrekende fietsstraten aanduiden. Om de kaart aan te passen, moet je je aanmelden met OpenStreetMap en helemaal inzoomen tot straatniveau. -|material= {{yes|[https://mapcomplete.osm.be/ Yes]}} -|image= MapComplete_Screenshot.png -|genre= POI, editor, fietsstraten -}} -{{service_item -|name= [https://mapcomplete.osm.be/bicyclelib bicyclelib] +|name= [https://mapcomplete.osm.be/observation_towers observation_towers] |region= Worldwide |lang= {{#language:en|en}}, {{#language:nl|en}} -|descr= A MapComplete theme: A bicycle library is a place where bicycles can be lent, often for a small yearly fee. A notable use case are bicycle libraries for kids, which allows them to change for a bigger bike when they've outgrown their current bike +|descr= A MapComplete theme: Publicly accessible towers to enjoy the view |material= {{yes|[https://mapcomplete.osm.be/ Yes]}} |image= MapComplete_Screenshot.png -|genre= POI, editor, bicyclelib +|genre= POI, editor, observation_towers }} {{service_item -|name= [https://mapcomplete.osm.be/maps maps] +|name= [https://mapcomplete.osm.be/openwindpowermap openwindpowermap] |region= Worldwide -|lang= {{#language:en|en}}, {{#language:nl|en}}, {{#language:fr|en}} -|descr= A MapComplete theme: On this map you can find all maps OpenStreetMap knows - typically a big map on an information board showing the area, city or region, e.g. a tourist map on the back of a billboard, a map of a nature reserve, a map of cycling networks in the region, ...)

If a map is missing, you can easily map this map on OpenStreetMap. +|lang= {{#language:en|en}}, {{#language:fr|en}}, {{#language:nl|en}} +|descr= A MapComplete theme: A map for showing and editing wind turbines. +|material= {{yes|[https://mapcomplete.osm.be/ Yes, by Seppe Santens;]}} +|image= MapComplete_Screenshot.png +|genre= POI, editor, openwindpowermap +}} +{{service_item +|name= [https://mapcomplete.osm.be/parkings parkings] +|region= Worldwide +|lang= {{#language:nl|en}}, {{#language:en|en}} +|descr= A MapComplete theme: This map shows different parking spots |material= {{yes|[https://mapcomplete.osm.be/ Yes]}} |image= MapComplete_Screenshot.png -|genre= POI, editor, maps +|genre= POI, editor, parkings }} {{service_item -|name= [https://mapcomplete.osm.be/fritures fritures] +|name= [https://mapcomplete.osm.be/playgrounds playgrounds] |region= Worldwide -|lang= {{#language:nl|en}}, {{#language:fr|en}} -|descr= A MapComplete theme: Op deze kaart vind je je favoriete frituur! +|lang= {{#language:nl|en}}, {{#language:en|en}}, {{#language:fr|en}}, {{#language:ja|en}}, {{#language:zh_Hant|en}}, {{#language:ru|en}} +|descr= A MapComplete theme: On this map, you find playgrounds and can add more information |material= {{yes|[https://mapcomplete.osm.be/ Yes]}} |image= MapComplete_Screenshot.png -|genre= POI, editor, fritures +|genre= POI, editor, playgrounds }} {{service_item -|name= [https://mapcomplete.osm.be/benches benches] +|name= [https://mapcomplete.osm.be/shops shops] |region= Worldwide -|lang= {{#language:en|en}}, {{#language:de|en}}, {{#language:fr|en}} -|descr= A MapComplete theme: This map shows all benches that are recorded in OpenStreetMap: Individual benches, and benches belonging to public transport stops or shelters. With an OpenStreetMap account, you can map new benches or edit details of existing benches. -|material= {{yes|[https://mapcomplete.osm.be/ Yes, by Florian Edelmann;]}} -|image= MapComplete_Screenshot.png -|genre= POI, editor, benches -}} -{{service_item -|name= [https://mapcomplete.osm.be/charging_stations charging_stations] -|region= Worldwide -|lang= {{#language:en|en}} -|descr= A MapComplete theme: On this open map, one can find and mark information about charging stations +|lang= {{#language:en|en}}, {{#language:fr|en}}, {{#language:ja|en}}, {{#language:zh_Hant|en}}, {{#language:ru|en}}, {{#language:nl|en}}, {{#language:ca|en}}, {{#language:id|en}} +|descr= A MapComplete theme: On this map, one can mark basic information about shops, add opening hours and phone numbers |material= {{yes|[https://mapcomplete.osm.be/ Yes]}} |image= MapComplete_Screenshot.png -|genre= POI, editor, charging_stations +|genre= POI, editor, shops +}} +{{service_item +|name= [https://mapcomplete.osm.be/sport_pitches sport_pitches] +|region= Worldwide +|lang= {{#language:nl|en}}, {{#language:fr|en}}, {{#language:en|en}}, {{#language:ja|en}}, {{#language:zh_Hant|en}}, {{#language:ru|en}} +|descr= A MapComplete theme: A sport pitch is an area where sports are played +|material= {{yes|[https://mapcomplete.osm.be/ Yes]}} +|image= MapComplete_Screenshot.png +|genre= POI, editor, sport_pitches }} {{service_item |name= [https://mapcomplete.osm.be/surveillance surveillance] |region= Worldwide -|lang= {{#language:en|en}}, {{#language:nl|en}} +|lang= {{#language:en|en}}, {{#language:nl|en}}, {{#language:ja|en}}, {{#language:zh_Hant|en}}, {{#language:fr|en}}, {{#language:pl|en}} |descr= A MapComplete theme: On this open map, you can find surveillance cameras. |material= {{yes|[https://mapcomplete.osm.be/ Yes]}} |image= MapComplete_Screenshot.png |genre= POI, editor, surveillance }} {{service_item -|name= [https://mapcomplete.osm.be/climbing climbing] -|region= Worldwide -|lang= {{#language:de|en}}, {{#language:en|en}}, {{#language:nl|en}} -|descr= A MapComplete theme: On this map you will find various climbing opportunities such as climbing gyms, bouldering halls and rocks in nature. -|material= {{yes|[https://mapcomplete.osm.be/ Yes, by Christian Neumann ;]}} -|image= MapComplete_Screenshot.png -|genre= POI, editor, climbing -}} -{{service_item -|name= [https://mapcomplete.osm.be/playgrounds playgrounds] -|region= Worldwide -|lang= {{#language:nl|en}} -|descr= A MapComplete theme: Op deze kaart vind je speelplekken zoals speeltuinen, speelbossen en sportterreinen -|material= {{yes|[https://mapcomplete.osm.be/ Yes]}} -|image= MapComplete_Screenshot.png -|genre= POI, editor, playgrounds -}} -{{service_item |name= [https://mapcomplete.osm.be/trees trees] |region= Worldwide -|lang= {{#language:nl|en}}, {{#language:en|en}} +|lang= {{#language:nl|en}}, {{#language:en|en}}, {{#language:fr|en}}, {{#language:it|en}}, {{#language:ru|en}}, {{#language:ja|en}}, {{#language:zh_Hant|en}}, {{#language:pl|en}} |descr= A MapComplete theme: Map all the trees! |material= {{yes|[https://mapcomplete.osm.be/ Yes, by Midgard;]}} |image= MapComplete_Screenshot.png |genre= POI, editor, trees }} {{service_item -|name= [https://mapcomplete.osm.be/campersite campersite] +|name= [https://mapcomplete.osm.be/uk_addresses uk_addresses] |region= Worldwide |lang= {{#language:en|en}} -|descr= A MapComplete theme: This site collects all official camper stopover places and places where you can dump grey and black water. You can add details about the services provided and the cost. Add pictures and reviews. This is a website and a webapp. The data is stored in OpenStreetMap, so it will be free forever and can be re-used by any app. -|material= {{yes|[https://mapcomplete.osm.be/ Yes, by joost schouppe;]}} +|descr= A MapComplete theme: Contribute to OpenStreetMap by filling out address information +|material= {{yes|[https://mapcomplete.osm.be/ Yes, by Pieter Vander Vennet, Rob Nickerson, Russ Garrett;]}} |image= MapComplete_Screenshot.png -|genre= POI, editor, campersite +|genre= POI, editor, uk_addresses }} {{service_item -|name= [https://mapcomplete.osm.be/sport_pitches sport_pitches] +|name= [https://mapcomplete.osm.be/waste_basket waste_basket] |region= Worldwide -|lang= {{#language:nl|en}} -|descr= A MapComplete theme: Een sportveld is een ingerichte plaats met infrastructuur om een sport te beoefenen +|lang= {{#language:en|en}}, {{#language:nl|en}} +|descr= A MapComplete theme: On this map, you'll find waste baskets near you. If a waste basket is missing on this map, you can add it yourself |material= {{yes|[https://mapcomplete.osm.be/ Yes]}} |image= MapComplete_Screenshot.png -|genre= POI, editor, sport_pitches +|genre= POI, editor, waste_basket }} |} \ No newline at end of file diff --git a/InitUiElements.ts b/InitUiElements.ts index 1835c4e071..8fafd7677d 100644 --- a/InitUiElements.ts +++ b/InitUiElements.ts @@ -1,7 +1,6 @@ import {FixedUiElement} from "./UI/Base/FixedUiElement"; import Toggle from "./UI/Input/Toggle"; import State from "./State"; -import LoadFromOverpass from "./Logic/Actors/OverpassFeatureSource"; import {UIEventSource} from "./Logic/UIEventSource"; import {QueryParameters} from "./Logic/Web/QueryParameters"; import StrayClickHandler from "./Logic/Actors/StrayClickHandler"; @@ -16,31 +15,32 @@ import Link from "./UI/Base/Link"; import * as personal from "./assets/themes/personal/personal.json"; import * as L from "leaflet"; import Img from "./UI/Base/Img"; -import UserDetails from "./Logic/Osm/OsmConnection"; import Attribution from "./UI/BigComponents/Attribution"; -import LayerResetter from "./Logic/Actors/LayerResetter"; +import BackgroundLayerResetter from "./Logic/Actors/BackgroundLayerResetter"; import FullWelcomePaneWithTabs from "./UI/BigComponents/FullWelcomePaneWithTabs"; -import ShowDataLayer from "./UI/ShowDataLayer"; +import ShowDataLayer from "./UI/ShowDataLayer/ShowDataLayer"; import Hash from "./Logic/Web/Hash"; import FeaturePipeline from "./Logic/FeatureSource/FeaturePipeline"; import ScrollableFullScreen from "./UI/Base/ScrollableFullScreen"; import Translations from "./UI/i18n/Translations"; import MapControlButton from "./UI/MapControlButton"; -import SelectedFeatureHandler from "./Logic/Actors/SelectedFeatureHandler"; import LZString from "lz-string"; -import FeatureSource from "./Logic/FeatureSource/FeatureSource"; -import AllKnownLayers from "./Customizations/AllKnownLayers"; import AvailableBaseLayers from "./Logic/Actors/AvailableBaseLayers"; -import {TagsFilter} from "./Logic/Tags/TagsFilter"; import LeftControls from "./UI/BigComponents/LeftControls"; import RightControls from "./UI/BigComponents/RightControls"; import {LayoutConfigJson} from "./Models/ThemeConfig/Json/LayoutConfigJson"; import LayoutConfig from "./Models/ThemeConfig/LayoutConfig"; -import LayerConfig from "./Models/ThemeConfig/LayerConfig"; import Minimap from "./UI/Base/Minimap"; -import Constants from "./Models/Constants"; +import SelectedFeatureHandler from "./Logic/Actors/SelectedFeatureHandler"; import Combine from "./UI/Base/Combine"; import {SubtleButton} from "./UI/Base/SubtleButton"; +import ShowTileInfo from "./UI/ShowDataLayer/ShowTileInfo"; +import {Tiles} from "./Models/TileRange"; +import {TileHierarchyAggregator} from "./UI/ShowDataLayer/TileHierarchyAggregator"; +import FilterConfig from "./Models/ThemeConfig/FilterConfig"; +import FilteredLayer from "./Models/FilteredLayer"; +import {BBox} from "./Logic/BBox"; +import {AllKnownLayouts} from "./Customizations/AllKnownLayouts"; export class InitUiElements { static InitAll( @@ -67,10 +67,24 @@ export class InitUiElements { "LayoutFromBase64 is ", layoutFromBase64 ); + + if(layoutToUse.id === personal.id){ + layoutToUse.layers = AllKnownLayouts.AllPublicLayers() + for (const layer of layoutToUse.layers) { + layer.minzoomVisible = Math.max(layer.minzoomVisible, layer.minzoom) + layer.minzoom = Math.max(16, layer.minzoom) + } + } State.state = new State(layoutToUse); - // This 'leaks' the global state via the window object, useful for debugging + if(layoutToUse.id === personal.id) { + // Disable overpass all together + State.state.overpassMaxZoom.setData(0) + + } + + // This 'leaks' the global state via the window object, useful for debugging // @ts-ignore window.mapcomplete_state = State.state; @@ -99,46 +113,6 @@ export class InitUiElements { } } - function updateFavs() { - // This is purely for the personal theme to load the layers there - const favs = State.state.favouriteLayers.data ?? []; - - const neededLayers = new Set(); - - console.log("Favourites are: ", favs); - layoutToUse.layers.splice(0, layoutToUse.layers.length); - let somethingChanged = false; - for (const fav of favs) { - if (AllKnownLayers.sharedLayers.has(fav)) { - const layer = AllKnownLayers.sharedLayers.get(fav); - if (!neededLayers.has(layer)) { - neededLayers.add(layer); - somethingChanged = true; - } - } - - for (const layouts of State.state.installedThemes.data) { - for (const layer of layouts.layout.layers) { - if (typeof layer === "string") { - continue; - } - if (layer.id === fav) { - if (!neededLayers.has(layer)) { - neededLayers.add(layer); - somethingChanged = true; - } - } - } - } - } - if (somethingChanged) { - console.log("layoutToUse.layers:", layoutToUse.layers); - State.state.layoutToUse.data.layers = Array.from(neededLayers); - State.state.layoutToUse.ping(); - State.state.layerUpdater?.ForceRefresh(); - } - } - if (layoutToUse.customCss !== undefined) { Utils.LoadCustomCss(layoutToUse.customCss); } @@ -171,35 +145,43 @@ export class InitUiElements { ).AttachTo("messagesbox"); } - State.state.osmConnection.userDetails - .map((userDetails: UserDetails) => userDetails?.home) - .addCallbackAndRunD((home) => { - const color = getComputedStyle(document.body).getPropertyValue( - "--subtle-detail-color" - ); - const icon = L.icon({ - iconUrl: Img.AsData( - Svg.home_white_bg.replace(/#ffffff/g, color) - ), - iconSize: [30, 30], - iconAnchor: [15, 15], - }); - const marker = L.marker([home.lat, home.lon], {icon: icon}); - marker.addTo(State.state.leafletMap.data); + function addHomeMarker() { + const userDetails = State.state.osmConnection.userDetails.data; + if (userDetails === undefined) { + return false; + } + const home = userDetails.home; + if (home === undefined) { + return userDetails.loggedIn; // If logged in, the home is not set and we unregister. If not logged in, we stay registered if a login still comes + } + const leaflet = State.state.leafletMap.data; + if (leaflet === undefined) { + return false; + } + const color = getComputedStyle(document.body).getPropertyValue( + "--subtle-detail-color" + ); + const icon = L.icon({ + iconUrl: Img.AsData( + Svg.home_white_bg.replace(/#ffffff/g, color) + ), + iconSize: [30, 30], + iconAnchor: [15, 15], }); - - if (layoutToUse.id === personal.id) { - updateFavs(); + const marker = L.marker([home.lat, home.lon], {icon: icon}); + marker.addTo(leaflet); + return true; } + State.state.osmConnection.userDetails + .addCallbackAndRunD(_ => addHomeMarker()); + State.state.leafletMap.addCallbackAndRunD(_ => addHomeMarker()) + + InitUiElements.setupAllLayerElements(); - - if (layoutToUse.id === personal.id) { - State.state.favouriteLayers.addCallback(updateFavs); - State.state.installedThemes.addCallback(updateFavs); - } else { State.state.locationControl.ping(); - } + + new SelectedFeatureHandler(Hash.hash, State.state) // Reset the loading message once things are loaded new CenterMessageBox().AttachTo("centermessage"); @@ -211,7 +193,7 @@ export class InitUiElements { static LoadLayoutFromHash( userLayoutParam: UIEventSource ): [LayoutConfig, string] { - let hash = location.hash.substr(1); + let hash = location.hash.substr(1); try { const layoutFromBase64 = userLayoutParam.data; // layoutFromBase64 contains the name of the theme. This is partly to do tracking with goat counter @@ -251,18 +233,18 @@ export class InitUiElements { userLayoutParam.setData(layoutToUse.id); return [layoutToUse, btoa(Utils.MinifyJSON(JSON.stringify(json)))]; } catch (e) { - - if(hash === undefined || hash.length < 10){ + + if (hash === undefined || hash.length < 10) { e = "Did you effectively add a theme? It seems no data could be found." } - + new Combine([ "Error: could not parse the custom layout:", - new FixedUiElement(""+e).SetClass("alert"), - new SubtleButton("./assets/svg/mapcomplete_logo.svg", - "Go back to the theme overview", - {url: window.location.protocol+"//"+ window.location.hostname+"/index.html", newTab: false}) - + new FixedUiElement("" + e).SetClass("alert"), + new SubtleButton("./assets/svg/mapcomplete_logo.svg", + "Go back to the theme overview", + {url: window.location.protocol + "//" + window.location.hostname + "/index.html", newTab: false}) + ]) .SetClass("flex flex-col") .AttachTo("centermessage"); @@ -334,40 +316,39 @@ export class InitUiElements { (layer) => layer.id ); - new LayerResetter( + new BackgroundLayerResetter( State.state.backgroundLayer, State.state.locationControl, State.state.availableBackgroundLayers, - State.state.layoutToUse.map( - (layout: LayoutConfig) => layout.defaultBackgroundId - ) + State.state.layoutToUse.defaultBackgroundId ); const attr = new Attribution( State.state.locationControl, State.state.osmConnection.userDetails, State.state.layoutToUse, - State.state.leafletMap + State.state.currentBounds ); - new Minimap({ + Minimap.createMiniMap({ background: State.state.backgroundLayer, location: State.state.locationControl, leafletMap: State.state.leafletMap, + bounds: State.state.currentBounds, attribution: attr, lastClickLocation: State.state.LastClickLocation }).SetClass("w-full h-full") .AttachTo("leafletDiv") - const layout = State.state.layoutToUse.data; + const layout = State.state.layoutToUse; if (layout.lockLocation) { if (layout.lockLocation === true) { - const tile = Utils.embedded_tile( + const tile = Tiles.embedded_tile( layout.startLat, layout.startLon, layout.startZoom - 1 ); - const bounds = Utils.tile_bounds(tile.z, tile.x, tile.y); + const bounds = Tiles.tile_bounds(tile.z, tile.x, tile.y); // We use the bounds to get a sense of distance for this zoom level const latDiff = bounds[0][0] - bounds[1][0]; const lonDiff = bounds[0][1] - bounds[1][1]; @@ -385,76 +366,150 @@ export class InitUiElements { } } - private static InitLayers(): FeatureSource { + private static InitLayers(): void { const state = State.state; - state.filteredLayers = state.layoutToUse.map((layoutToUse) => { - const flayers = []; + const empty = [] - for (const layer of layoutToUse.layers) { - const isDisplayed = QueryParameters.GetQueryParameter( + const flayers: FilteredLayer[] = []; + + for (const layer of state.layoutToUse.layers) { + let defaultShown = "true" + if(state.layoutToUse.id === personal.id){ + defaultShown = "false" + } + + let isDisplayed: UIEventSource + if(state.layoutToUse.id === personal.id){ + isDisplayed = State.state.osmConnection.GetPreference("personal-theme-layer-" + layer.id + "-enabled") + .map(value => value === "yes", [], enabled => { + return enabled ? "yes" : ""; + }) + isDisplayed.addCallbackAndRun(d =>console.log("IsDisplayed for layer", layer.id, "is currently", d) ) + }else{ + isDisplayed = QueryParameters.GetQueryParameter( "layer-" + layer.id, - "true", + defaultShown, "Wether or not layer " + layer.id + " is shown" ).map( (str) => str !== "false", [], (b) => b.toString() ); - const flayer = { - isDisplayed: isDisplayed, - layerDef: layer, - appliedFilters: new UIEventSource(undefined), - }; - flayers.push(flayer); } - return flayers; - }); + const flayer = { + isDisplayed: isDisplayed, + layerDef: layer, + appliedFilters: new UIEventSource<{ filter: FilterConfig, selected: number }[]>([]), + }; - const updater = new LoadFromOverpass( - state.locationControl, - state.layoutToUse, - state.leafletMap, - state.overpassUrl, - state.overpassTimeout, - Constants.useOsmApiAt - ); - State.state.layerUpdater = updater; + if (layer.filters.length > 0) { + const filtersPerName = new Map() + layer.filters.forEach(f => filtersPerName.set(f.id, f)) + const qp = QueryParameters.GetQueryParameter("filter-" + layer.id, "","Filtering state for a layer") + flayer.appliedFilters.map(filters => { + filters = filters ?? [] + return filters.map(f => f.filter.id + "." + f.selected).join(",") + }, [], textual => { + if(textual.length === 0){ + return empty + } + return textual.split(",").map(part => { + const [filterId, selected] = part.split("."); + return {filter: filtersPerName.get(filterId), selected: Number(selected)} + }).filter(f => f.filter !== undefined && !isNaN(f.selected)) + }).syncWith(qp, true) + } - const source = new FeaturePipeline( - state.filteredLayers, - State.state.changes, - updater, - state.osmApiFeatureSource, - state.layoutToUse, - state.locationControl, - state.selectedElement - ); + flayers.push(flayer); + } + state.filteredLayers = new UIEventSource(flayers); + + - State.state.featurePipeline = source; - new ShowDataLayer( - source.features, - State.state.leafletMap, - State.state.layoutToUse - ); - const selectedFeatureHandler = new SelectedFeatureHandler( - Hash.hash, - State.state.selectedElement, - source, - State.state.osmApiFeatureSource + const clusterCounter = TileHierarchyAggregator.createHierarchy() + new ShowDataLayer({ + features: clusterCounter.getCountsForZoom(State.state.locationControl, State.state.layoutToUse.clustering.minNeededElements), + leafletMap: State.state.leafletMap, + layerToShow: ShowTileInfo.styling, + enablePopups: false + }) + + State.state.featurePipeline = new FeaturePipeline( + source => { + + clusterCounter.addTile(source) + + const clustering = State.state.layoutToUse.clustering + const doShowFeatures = source.features.map( + f => { + const z = State.state.locationControl.data.zoom + + if(!source.layer.isDisplayed.data){ + return false; + } + + if (z < source.layer.layerDef.minzoom) { + // Layer is always hidden for this zoom level + return false; + } + + if (z >= clustering.maxZoom) { + return true + } + + if (f.length > clustering.minNeededElements) { + // This tile alone already has too much features + return false + } + + let [tileZ, tileX, tileY] = Tiles.tile_from_index(source.tileIndex); + if (tileZ >= z) { + + while (tileZ > z) { + tileZ-- + tileX = Math.floor(tileX / 2) + tileY = Math.floor(tileY / 2) + } + + if (clusterCounter.getTile(Tiles.tile_index(tileZ, tileX, tileY))?.totalValue > clustering.minNeededElements) { + return false + } + } + + + const bounds = State.state.currentBounds.data + if(bounds === undefined){ + // Map is not yet displayed + return false; + } + if (!source.bbox.overlapsWith(bounds)) { + // Not within range + return false + } + + return true + }, [State.state.currentBounds] + ) + + new ShowDataLayer( + { + features: source, + leafletMap: State.state.leafletMap, + layerToShow: source.layer.layerDef, + doShowLayer: doShowFeatures + } + ); + }, state ); - selectedFeatureHandler.zoomToSelectedFeature( - State.state.locationControl - ); - return source; } private static setupAllLayerElements() { // ------------- Setup the layers ------------------------------- - const source = InitUiElements.InitLayers(); + InitUiElements.InitLayers(); - new LeftControls(source).AttachTo("bottom-left"); + new LeftControls(State.state).AttachTo("bottom-left"); new RightControls().AttachTo("bottom-right"); // ------------------ Setup various other UI elements ------------ diff --git a/Logic/Actors/LayerResetter.ts b/Logic/Actors/BackgroundLayerResetter.ts similarity index 84% rename from Logic/Actors/LayerResetter.ts rename to Logic/Actors/BackgroundLayerResetter.ts index 1a2175dfc2..d193d81329 100644 --- a/Logic/Actors/LayerResetter.ts +++ b/Logic/Actors/BackgroundLayerResetter.ts @@ -6,13 +6,13 @@ import Loc from "../../Models/Loc"; /** * Sets the current background layer to a layer that is actually available */ -export default class LayerResetter { +export default class BackgroundLayerResetter { constructor(currentBackgroundLayer: UIEventSource, location: UIEventSource, availableLayers: UIEventSource, - defaultLayerId: UIEventSource = undefined) { - defaultLayerId = defaultLayerId ?? new UIEventSource(AvailableBaseLayers.osmCarto.id); + defaultLayerId: string = undefined) { + defaultLayerId = defaultLayerId ?? AvailableBaseLayers.osmCarto.id; // Change the baselayer back to OSM if we go out of the current range of the layer availableLayers.addCallbackAndRun(availableLayers => { @@ -28,7 +28,7 @@ export default class LayerResetter { if (availableLayer.min_zoom > location.data.zoom) { break; } - if (availableLayer.id === defaultLayerId.data) { + if (availableLayer.id === defaultLayerId) { defaultLayer = availableLayer; } return; // All good - the current layer still works! diff --git a/Logic/Actors/GeoLocationHandler.ts b/Logic/Actors/GeoLocationHandler.ts index e3e2455bf3..43ce441d7d 100644 --- a/Logic/Actors/GeoLocationHandler.ts +++ b/Logic/Actors/GeoLocationHandler.ts @@ -60,12 +60,12 @@ export default class GeoLocationHandler extends VariableUiElement { * @private */ private readonly _previousLocationGrant: UIEventSource; - private readonly _layoutToUse: UIEventSource; + private readonly _layoutToUse: LayoutConfig; constructor( currentGPSLocation: UIEventSource<{ latlng: any; accuracy: number }>, leafletMap: UIEventSource, - layoutToUse: UIEventSource + layoutToUse: LayoutConfig ) { const hasLocation = currentGPSLocation.map( (location) => location !== undefined @@ -207,6 +207,9 @@ export default class GeoLocationHandler extends VariableUiElement { }); const map = self._leafletMap.data; + if(map === undefined){ + return; + } const newMarker = L.marker(location.latlng, {icon: icon}); newMarker.addTo(map); @@ -230,7 +233,7 @@ export default class GeoLocationHandler extends VariableUiElement { navigator?.permissions ?.query({name: "geolocation"}) ?.then(function (status) { - console.log("Geolocation is already", status); + console.log("Geolocation permission is ", status.state); if (status.state === "granted") { self.StartGeolocating(forceZoom); } @@ -264,7 +267,7 @@ export default class GeoLocationHandler extends VariableUiElement { } // We check that the GPS location is not out of bounds - const b = this._layoutToUse.data.lockLocation; + const b = this._layoutToUse.lockLocation; let inRange = true; if (b) { if (b !== true) { @@ -289,7 +292,6 @@ export default class GeoLocationHandler extends VariableUiElement { private StartGeolocating(zoomToGPS = true) { const self = this; - console.log("Starting geolocation"); this._lastUserRequest = zoomToGPS ? new Date() : new Date(0); if (self._permission.data === "denied") { @@ -301,8 +303,6 @@ export default class GeoLocationHandler extends VariableUiElement { this.MoveToCurrentLoction(16); } - console.log("Searching location using GPS"); - if (self._isActive.data) { return; } diff --git a/Logic/Actors/ImageSearcher.ts b/Logic/Actors/ImageSearcher.ts deleted file mode 100644 index c2d089e513..0000000000 --- a/Logic/Actors/ImageSearcher.ts +++ /dev/null @@ -1,173 +0,0 @@ -import {ImagesInCategory, Wikidata, Wikimedia} from "../ImageProviders/Wikimedia"; -import {UIEventSource} from "../UIEventSource"; - -/** - * There are multiple way to fetch images for an object - * 1) There is an image tag - * 2) There is an image tag, the image tag contains multiple ';'-separated URLS - * 3) there are multiple image tags, e.g. 'image', 'image:0', 'image:1', and 'image_0', 'image_1' - however, these are pretty rare so we are gonna ignore them - * 4) There is a wikimedia_commons-tag, which either has a 'File': or a 'category:' containing images - * 5) There is a wikidata-tag, and the wikidata item either has an 'image' attribute or has 'a link to a wikimedia commons category' - * 6) There is a wikipedia article, from which we can deduct the wikidata item - * - * For some images, author and license should be shown - */ -/** - * Class which search for all the possible locations for images and which builds a list of UI-elements for it. - * Note that this list is embedded into an UIEVentSource, ready to put it into a carousel. - * - */ -export class ImageSearcher extends UIEventSource<{ key: string, url: string }[]> { - - private static _cache = new Map(); - private readonly _wdItem = new UIEventSource(""); - private readonly _commons = new UIEventSource(""); - - private constructor(tags: UIEventSource, imagePrefix = "image", loadSpecial = true) { - super([]) - const self = this; - - function AddImages(images: { key: string, url: string }[]) { - const oldUrls = self.data.map(kurl => kurl.url); - let somethingChanged = false; - for (const image of images) { - const url = image.url; - - if (url === undefined || url === null || url === "") { - continue; - } - if (oldUrls.indexOf(url) >= 0) { - // Already exists - continue; - } - - self.data.push(image); - somethingChanged = true; - } - if (somethingChanged) { - self.ping(); - } - } - - function addImage(image: string) { - AddImages([{url: image, key: undefined}]); - } - - - // By wrapping this in a UIEventSource, we prevent multiple queries of loadWikiData - this._wdItem.addCallback(wdItemContents => { - ImageSearcher.loadWikidata(wdItemContents, addImage); - }); - this._commons.addCallback(commonsData => { - ImageSearcher.LoadCommons(commonsData, addImage) - }); - tags.addCallbackAndRun(tags => { - AddImages(ImageSearcher.LoadImages(tags, imagePrefix)); - }); - - if (loadSpecial) { - tags.addCallbackAndRunD(tags => { - - const wdItem = tags.wikidata; - if (wdItem !== undefined) { - self._wdItem.setData(wdItem); - } - const commons = tags.wikimedia_commons; - if (commons !== undefined) { - self._commons.setData(commons); - } - - if (tags.mapillary) { - let mapillary = tags.mapillary; - const prefix = "https://www.mapillary.com/map/im/"; - - let regex = /https?:\/\/www.mapillary.com\/app\/.*pKey=([^&]*).*/ - let match = mapillary.match(regex); - if (match) { - mapillary = match[1]; - } - - if (mapillary.indexOf(prefix) < 0) { - mapillary = prefix + mapillary; - } - - - AddImages([{url: mapillary, key: undefined}]); - } - }) - } - } - - public static construct(tags: UIEventSource, imagePrefix = "image", loadSpecial = true): ImageSearcher { - const key = tags.data["id"] + " " + imagePrefix + loadSpecial; - if (tags.data["id"] !== undefined && ImageSearcher._cache.has(key)) { - return ImageSearcher._cache.get(key) - } - - const searcher = new ImageSearcher(tags, imagePrefix, loadSpecial); - ImageSearcher._cache.set(key, searcher) - return searcher; - } - - private static loadWikidata(wikidataItem, addImage: ((url: string) => void)): void { - // Load the wikidata item, then detect usage on 'commons' - let allWikidataId = wikidataItem.split(";"); - for (let wikidataId of allWikidataId) { - // @ts-ignore - if (wikidataId.startsWith("Q")) { - wikidataId = wikidataId.substr(1); - } - Wikimedia.GetWikiData(parseInt(wikidataId), (wd: Wikidata) => { - addImage(wd.image); - Wikimedia.GetCategoryFiles(wd.commonsWiki, (images: ImagesInCategory) => { - for (const image of images.images) { - if (image.startsWith("File:")) { - addImage(image); - } - } - }) - }) - } - } - - private static LoadCommons(commonsData: string, addImage: ((url: string) => void)): void { - const allCommons: string[] = commonsData.split(";"); - for (const commons of allCommons) { - if (commons.startsWith("Category:")) { - Wikimedia.GetCategoryFiles(commons, (images: ImagesInCategory) => { - for (const image of images.images) { - if (image.startsWith("File:")) { - addImage(image) - } - } - }) - } else { - if (commons.startsWith("File:")) { - addImage(commons) - } - } - } - } - - private static LoadImages(tags: any, imagePrefix: string): { key: string, url: string }[] { - const imageTag = tags[imagePrefix]; - const images: { key: string, url: string }[] = []; - if (imageTag !== undefined) { - const bareImages = imageTag.split(";"); - for (const bareImage of bareImages) { - images.push({key: imagePrefix, url: bareImage}) - } - } - - for (const key in tags) { - if (key.startsWith(imagePrefix + ":")) { - const url = tags[key] - images.push({key: key, url: url}) - } - } - - - return images; - } - -} \ No newline at end of file diff --git a/Logic/Actors/OverpassFeatureSource.ts b/Logic/Actors/OverpassFeatureSource.ts index b650d819ba..4f4f7a77f8 100644 --- a/Logic/Actors/OverpassFeatureSource.ts +++ b/Logic/Actors/OverpassFeatureSource.ts @@ -1,13 +1,15 @@ import {UIEventSource} from "../UIEventSource"; -import Loc from "../../Models/Loc"; import {Or} from "../Tags/Or"; import {Overpass} from "../Osm/Overpass"; -import Bounds from "../../Models/Bounds"; import FeatureSource from "../FeatureSource/FeatureSource"; import {Utils} from "../../Utils"; import {TagsFilter} from "../Tags/TagsFilter"; import SimpleMetaTagger from "../SimpleMetaTagger"; import LayoutConfig from "../../Models/ThemeConfig/LayoutConfig"; +import RelationsTracker from "../Osm/RelationsTracker"; +import {BBox} from "../BBox"; +import Loc from "../../Models/Loc"; +import LayerConfig from "../../Models/ThemeConfig/LayerConfig"; export default class OverpassFeatureSource implements FeatureSource { @@ -20,116 +22,54 @@ export default class OverpassFeatureSource implements FeatureSource { public readonly features: UIEventSource<{ feature: any, freshness: Date }[]> = new UIEventSource(undefined); - public readonly sufficientlyZoomed: UIEventSource; public readonly runningQuery: UIEventSource = new UIEventSource(false); public readonly timeout: UIEventSource = new UIEventSource(0); - + + public readonly relationsTracker: RelationsTracker; + + private readonly retries: UIEventSource = new UIEventSource(0); - /** - * The previous bounds for which the query has been run at the given zoom level - * - * Note that some layers only activate on a certain zoom level. - * If the map location changes, we check for each layer if it is loaded: - * we start checking the bounds at the first zoom level the layer might operate. If in bounds - no reload needed, otherwise we continue walking down - */ - private readonly _previousBounds: Map = new Map(); - private readonly _location: UIEventSource; - private readonly _layoutToUse: UIEventSource; - private readonly _leafletMap: UIEventSource; - private readonly _interpreterUrl: UIEventSource; - private readonly _timeout: UIEventSource; + + private readonly state: { + readonly locationControl: UIEventSource, + readonly layoutToUse: LayoutConfig, + readonly overpassUrl: UIEventSource; + readonly overpassTimeout: UIEventSource; + readonly currentBounds: UIEventSource + } + private readonly _isActive: UIEventSource; + private readonly onBboxLoaded: (bbox: BBox, date: Date, layers: LayerConfig[]) => void; - /** - * The most important layer should go first, as that one gets first pick for the questions - */ constructor( - location: UIEventSource, - layoutToUse: UIEventSource, - leafletMap: UIEventSource, - interpreterUrl: UIEventSource, - timeout: UIEventSource, - maxZoom = undefined) { - this._location = location; - this._layoutToUse = layoutToUse; - this._leafletMap = leafletMap; - this._interpreterUrl = interpreterUrl; - this._timeout = timeout; + state: { + readonly locationControl: UIEventSource, + readonly layoutToUse: LayoutConfig, + readonly overpassUrl: UIEventSource; + readonly overpassTimeout: UIEventSource; + readonly overpassMaxZoom: UIEventSource, + readonly currentBounds: UIEventSource + }, + options?: { + isActive?: UIEventSource, + relationTracker: RelationsTracker, + onBboxLoaded?: (bbox: BBox, date: Date, layers: LayerConfig[]) => void + }) { + + this.state = state + this._isActive = options.isActive; + this.onBboxLoaded = options.onBboxLoaded + this.relationsTracker = options.relationTracker const self = this; - - this.sufficientlyZoomed = location.map(location => { - if (location?.zoom === undefined) { - return false; - } - let minzoom = Math.min(...layoutToUse.data.layers.map(layer => layer.minzoom ?? 18)); - if(location.zoom < minzoom){ - return false; - } - if(maxZoom !== undefined && location.zoom > maxZoom){ - return false; - } - - return true; - }, [layoutToUse] - ); - for (let i = 0; i < 25; i++) { - // This update removes all data on all layers -> erase the map on lower levels too - this._previousBounds.set(i, []); - } - - layoutToUse.addCallback(() => { + state.currentBounds.addCallback(_ => { self.update() - }); - location.addCallback(() => { - self.update() - }); - leafletMap.addCallbackAndRunD(_ => { - self.update(); }) + } - public ForceRefresh() { - for (let i = 0; i < 25; i++) { - this._previousBounds.set(i, []); - } - this.update(); - } - - private GetFilter(): Overpass { + private GetFilter(interpreterUrl: string, layersToDownload: LayerConfig[]): Overpass { let filters: TagsFilter[] = []; let extraScripts: string[] = []; - for (const layer of this._layoutToUse.data.layers) { - if (typeof (layer) === "string") { - throw "A layer was not expanded!" - } - if (this._location.data.zoom < layer.minzoom) { - continue; - } - if (layer.doNotDownload) { - continue; - } - if (layer.source.geojsonSource !== undefined) { - // Not our responsibility to download this layer! - continue; - } - - - // Check if data for this layer has already been loaded - let previouslyLoaded = false; - for (let z = layer.minzoom; z < 25 && !previouslyLoaded; z++) { - const previousLoadedBounds = this._previousBounds.get(z); - if (previousLoadedBounds === undefined) { - continue; - } - for (const previousLoadedBound of previousLoadedBounds) { - previouslyLoaded = previouslyLoaded || this.IsInBounds(previousLoadedBound); - if (previouslyLoaded) { - break; - } - } - } - if (previouslyLoaded) { - continue; - } + for (const layer of layersToDownload) { if (layer.source.overpassScript !== undefined) { extraScripts.push(layer.source.overpassScript) } else { @@ -141,95 +81,113 @@ export default class OverpassFeatureSource implements FeatureSource { if (filters.length + extraScripts.length === 0) { return undefined; } - return new Overpass(new Or(filters), extraScripts, this._interpreterUrl, this._timeout); + return new Overpass(new Or(filters), extraScripts, interpreterUrl, this.state.overpassTimeout, this.relationsTracker); } - private update(): void { + private update() { + if (!this._isActive.data) { + return; + } + const self = this; + this.updateAsync().then(bboxDate => { + if(bboxDate === undefined || self.onBboxLoaded === undefined){ + return; + } + const [bbox, date, layers] = bboxDate + self.onBboxLoaded(bbox, date, layers) + }) + } + + private async updateAsync(): Promise<[BBox, Date, LayerConfig[]]> { if (this.runningQuery.data) { console.log("Still running a query, not updating"); - return; + return undefined; } if (this.timeout.data > 0) { console.log("Still in timeout - not updating") - return; + return undefined; } - const bounds = this._leafletMap.data?.getBounds(); + const bounds = this.state.currentBounds.data?.pad(this.state.layoutToUse.widenFactor)?.expandToTileBounds(14); + if (bounds === undefined) { - return; + return undefined; } - - const diff = this._layoutToUse.data.widenFactor; - - const n = Math.min(90, bounds.getNorth() + diff); - const e = Math.min(180, bounds.getEast() + diff); - const s = Math.max(-90, bounds.getSouth() - diff); - const w = Math.max(-180, bounds.getWest() - diff); - const queryBounds = {north: n, east: e, south: s, west: w}; - - const z = Math.floor(this._location.data.zoom ?? 0); - const self = this; - const overpass = this.GetFilter(); - if (overpass === undefined) { - return; + + + const layersToDownload = [] + for (const layer of this.state.layoutToUse.layers) { + + if (typeof (layer) === "string") { + throw "A layer was not expanded!" + } + if(this.state.locationControl.data.zoom < layer.minzoom){ + continue; + } + if (layer.doNotDownload) { + continue; + } + if (layer.source.geojsonSource !== undefined) { + // Not our responsibility to download this layer! + continue; + } + layersToDownload.push(layer) } - this.runningQuery.setData(true); - overpass.queryGeoJson(queryBounds, - function (data, date) { - self._previousBounds.get(z).push(queryBounds); - self.retries.setData(0); - const features = data.features.map(f => ({feature: f, freshness: date})); - SimpleMetaTagger.objectMetaInfo.addMetaTags(features) - self.features.setData(features); - self.runningQuery.setData(false); - }, - function (reason) { - self.retries.data++; - self.ForceRefresh(); - self.timeout.setData(self.retries.data * 5); - console.log(`QUERY FAILED (retrying in ${5 * self.retries.data} sec) due to ${reason}`); - self.retries.ping(); - self.runningQuery.setData(false); + let data: any = undefined + let date: Date = undefined + const overpassUrls = self.state.overpassUrl.data + let lastUsed = 0; - function countDown() { - window?.setTimeout( - function () { - if (self.timeout.data > 1) { - self.timeout.setData(self.timeout.data - 1); - window.setTimeout( - countDown, - 1000 - ) - } else { - self.timeout.setData(0); - self.update() - } - }, 1000 - ) + do { + try { + + const overpass = this.GetFilter(overpassUrls[lastUsed], layersToDownload); + + if (overpass === undefined) { + return undefined; } + this.runningQuery.setData(true); - countDown(); + [data, date] = await overpass.queryGeoJson(bounds) + console.log("Querying overpass is done", data) + } catch (e) { + self.retries.data++; + self.retries.ping(); + console.error(`QUERY FAILED due to`, e); + await Utils.waitFor(1000) + + if (lastUsed + 1 < overpassUrls.length) { + lastUsed++ + console.log("Trying next time with", overpassUrls[lastUsed]) + } else { + lastUsed = 0 + self.timeout.setData(self.retries.data * 5); + + while (self.timeout.data > 0) { + await Utils.waitFor(1000) + console.log(self.timeout.data) + self.timeout.data-- + self.timeout.ping(); + } + } } - ); + } while (data === undefined); - - } - - private IsInBounds(bounds: Bounds): boolean { - if (this._previousBounds === undefined) { - return false; + self.retries.setData(0); + try { + data.features.forEach(feature => SimpleMetaTagger.objectMetaInfo.applyMetaTagsOnFeature(feature, date)); + self.features.setData(data.features.map(f => ({feature: f, freshness: date}))); + return [bounds, date, layersToDownload]; + } catch (e) { + console.error("Got the overpass response, but could not process it: ", e, e.stack) + } finally { + self.runningQuery.setData(false); } - const b = this._leafletMap.data.getBounds(); - return b.getSouth() >= bounds.south && - b.getNorth() <= bounds.north && - b.getEast() <= bounds.east && - b.getWest() >= bounds.west; + } - - } \ No newline at end of file diff --git a/Logic/Actors/SelectedFeatureHandler.ts b/Logic/Actors/SelectedFeatureHandler.ts index 5a63d5cb28..73e69d72cd 100644 --- a/Logic/Actors/SelectedFeatureHandler.ts +++ b/Logic/Actors/SelectedFeatureHandler.ts @@ -1,50 +1,62 @@ import {UIEventSource} from "../UIEventSource"; -import FeatureSource from "../FeatureSource/FeatureSource"; import {OsmObject} from "../Osm/OsmObject"; import Loc from "../../Models/Loc"; +import {ElementStorage} from "../ElementStorage"; import FeaturePipeline from "../FeatureSource/FeaturePipeline"; -import OsmApiFeatureSource from "../FeatureSource/OsmApiFeatureSource"; /** * Makes sure the hash shows the selected element and vice-versa. */ export default class SelectedFeatureHandler { - private static readonly _no_trigger_on = ["welcome", "copyright", "layers", "new"] - private readonly _featureSource: FeatureSource; - private readonly _hash: UIEventSource; - private readonly _selectedFeature: UIEventSource; - private readonly _osmApiSource: OsmApiFeatureSource; + private static readonly _no_trigger_on = new Set(["welcome", "copyright", "layers", "new", "", undefined]) + hash: UIEventSource; + private readonly state: { + selectedElement: UIEventSource + } - constructor(hash: UIEventSource, - selectedFeature: UIEventSource, - featureSource: FeaturePipeline, - osmApiSource: OsmApiFeatureSource) { - this._hash = hash; - this._selectedFeature = selectedFeature; - this._featureSource = featureSource; - this._osmApiSource = osmApiSource; - const self = this; - hash.addCallback(h => { + constructor( + hash: UIEventSource, + state: { + selectedElement: UIEventSource, + allElements: ElementStorage, + featurePipeline: FeaturePipeline + } + ) { + this.hash = hash; + this.state = state + + + // If the hash changes, set the selected element correctly + function setSelectedElementFromHash(h){ if (h === undefined || h === "") { - selectedFeature.setData(undefined); - } else { - self.selectFeature(); + // Hash has been cleared - we clear the selected element + state.selectedElement.setData(undefined); + }else{ + // we search the element to select + const feature = state.allElements.ContainingFeatures.get(h) + if(feature === undefined){ + return; + } + const currentlySeleced = state.selectedElement.data + if(currentlySeleced === undefined){ + state.selectedElement.setData(feature) + return; + } + if(currentlySeleced.properties?.id === feature.properties.id){ + // We already have the right feature + return; + } + state.selectedElement.setData(feature) } - }) + } - hash.addCallbackAndRunD(h => { - try { - self.downloadFeature(h) - } catch (e) { - console.error("Could not download feature, probably a weird hash") - } - }) + hash.addCallback(setSelectedElementFromHash) - featureSource.features.addCallback(_ => self.selectFeature()); - selectedFeature.addCallback(feature => { + // IF the selected element changes, set the hash correctly + state.selectedElement.addCallback(feature => { if (feature === undefined) { - if (SelectedFeatureHandler._no_trigger_on.indexOf(hash.data) < 0) { + if (!SelectedFeatureHandler._no_trigger_on.has(hash.data)) { hash.setData("") } } @@ -54,15 +66,26 @@ export default class SelectedFeatureHandler { hash.setData(h) } }) - - this.selectFeature(); + + state.featurePipeline.newDataLoadedSignal.addCallbackAndRunD(_ => { + // New data was loaded. In initial startup, the hash might be set (via the URL) but might not be selected yet + if(hash.data === undefined || SelectedFeatureHandler._no_trigger_on.has(hash.data)){ + // This is an invalid hash anyway + return; + } + if(state.selectedElement.data !== undefined){ + // We already have something selected + return; + } + setSelectedElementFromHash(hash.data) + }) } // If a feature is selected via the hash, zoom there public zoomToSelectedFeature(location: UIEventSource) { - const hash = this._hash.data; - if (hash === undefined || SelectedFeatureHandler._no_trigger_on.indexOf(hash) >= 0) { + const hash = this.hash.data; + if (hash === undefined || SelectedFeatureHandler._no_trigger_on.has(hash)) { return; // No valid feature selected } // We should have a valid osm-ID and zoom to it... But we wrap it in try-catch to be sure @@ -80,42 +103,4 @@ export default class SelectedFeatureHandler { } } - private downloadFeature(hash: string) { - if (hash === undefined || hash === "") { - return; - } - if (SelectedFeatureHandler._no_trigger_on.indexOf(hash) >= 0) { - return; - } - try { - - this._osmApiSource.load(hash) - } catch (e) { - console.log("Could not download feature, probably a weird hash:", hash) - } - } - - private selectFeature() { - const features = this._featureSource?.features?.data; - if (features === undefined) { - return; - } - if (this._selectedFeature.data?.properties?.id === this._hash.data) { - // Feature already selected - return; - } - - const hash = this._hash.data; - if (hash === undefined || hash === "" || hash === "#") { - return; - } - for (const feature of features) { - const id = feature.feature?.properties?.id; - if (id === hash) { - this._selectedFeature.setData(feature.feature); - break; - } - } - } - } \ No newline at end of file diff --git a/Logic/Actors/TitleHandler.ts b/Logic/Actors/TitleHandler.ts index 520be0ed8d..91ec89b0c8 100644 --- a/Logic/Actors/TitleHandler.ts +++ b/Logic/Actors/TitleHandler.ts @@ -2,65 +2,42 @@ import {UIEventSource} from "../UIEventSource"; import Translations from "../../UI/i18n/Translations"; import Locale from "../../UI/i18n/Locale"; import TagRenderingAnswer from "../../UI/Popup/TagRenderingAnswer"; -import {ElementStorage} from "../ElementStorage"; import Combine from "../../UI/Base/Combine"; import LayoutConfig from "../../Models/ThemeConfig/LayoutConfig"; +import {ElementStorage} from "../ElementStorage"; -class TitleElement extends UIEventSource { - - private readonly _layoutToUse: UIEventSource; - private readonly _selectedFeature: UIEventSource; - private readonly _allElementsStorage: ElementStorage; - - constructor(layoutToUse: UIEventSource, - selectedFeature: UIEventSource, - allElementsStorage: ElementStorage) { - super("MapComplete"); - - this._layoutToUse = layoutToUse; - this._selectedFeature = selectedFeature; - this._allElementsStorage = allElementsStorage; - - this.syncWith( - this._selectedFeature.map( - selected => { - const defaultTitle = Translations.WT(this._layoutToUse.data?.title)?.txt ?? "MapComplete" - - if (selected === undefined) { - return defaultTitle - } - - const layout = layoutToUse.data; - const tags = selected.properties; - - - for (const layer of layout.layers) { - if (layer.title === undefined) { - continue; - } - if (layer.source.osmTags.matchesProperties(tags)) { - const tagsSource = allElementsStorage.getEventSourceById(tags.id) - const title = new TagRenderingAnswer(tagsSource, layer.title) - return new Combine([defaultTitle, " | ", title]).ConstructElement().innerText; - } - } +export default class TitleHandler { + constructor(state : { + selectedElement: UIEventSource, + layoutToUse: LayoutConfig, + allElements: ElementStorage + }) { + const currentTitle: UIEventSource = state.selectedElement.map( + selected => { + const layout = state.layoutToUse + const defaultTitle = Translations.WT(layout?.title)?.txt ?? "MapComplete" + if (selected === undefined) { return defaultTitle } - , [Locale.language, layoutToUse] - ) + + const tags = selected.properties; + for (const layer of layout.layers) { + if (layer.title === undefined) { + continue; + } + if (layer.source.osmTags.matchesProperties(tags)) { + const tagsSource = state.allElements.getEventSourceById(tags.id) + const title = new TagRenderingAnswer(tagsSource, layer.title) + return new Combine([defaultTitle, " | ", title]).ConstructElement()?.innerText ?? defaultTitle; + } + } + return defaultTitle + }, [Locale.language] ) - } - -} - -export default class TitleHandler { - constructor(layoutToUse: UIEventSource, - selectedFeature: UIEventSource, - allElementsStorage: ElementStorage) { - new TitleElement(layoutToUse, selectedFeature, allElementsStorage).addCallbackAndRunD(title => { + currentTitle.addCallbackAndRunD(title => { document.title = title }) } diff --git a/Logic/BBox.ts b/Logic/BBox.ts new file mode 100644 index 0000000000..a6f350cf83 --- /dev/null +++ b/Logic/BBox.ts @@ -0,0 +1,165 @@ +import * as turf from "@turf/turf"; +import {TileRange, Tiles} from "../Models/TileRange"; + +export class BBox { + + readonly maxLat: number; + readonly maxLon: number; + readonly minLat: number; + readonly minLon: number; + static global: BBox = new BBox([[-180, -90], [180, 90]]); + + constructor(coordinates) { + this.maxLat = -90; + this.maxLon = -180; + this.minLat = 90; + this.minLon = 180; + + + for (const coordinate of coordinates) { + this.maxLon = Math.max(this.maxLon, coordinate[0]); + this.maxLat = Math.max(this.maxLat, coordinate[1]); + this.minLon = Math.min(this.minLon, coordinate[0]); + this.minLat = Math.min(this.minLat, coordinate[1]); + } + + this.maxLon = Math.min(this.maxLon, 180) + this.maxLat = Math.min(this.maxLat, 90) + this.minLon = Math.max(this.minLon, -180) + this.minLat = Math.max(this.minLat, -90) + + + this.check(); + } + + static fromLeafletBounds(bounds) { + return new BBox([[bounds.getWest(), bounds.getNorth()], [bounds.getEast(), bounds.getSouth()]]) + } + + static get(feature): BBox { + if (feature.bbox?.overlapsWith === undefined) { + const turfBbox: number[] = turf.bbox(feature) + feature.bbox = new BBox([[turfBbox[0], turfBbox[1]], [turfBbox[2], turfBbox[3]]]); + } + return feature.bbox; + } + + /** + * Constructs a tilerange which fully contains this bbox (thus might be a bit larger) + * @param zoomlevel + */ + public containingTileRange(zoomlevel): TileRange { + return Tiles.TileRangeBetween(zoomlevel, this.minLat, this.minLon, this.maxLat, this.maxLon) + } + + public overlapsWith(other: BBox) { + if (this.maxLon < other.minLon) { + return false; + } + if (this.maxLat < other.minLat) { + return false; + } + if (this.minLon > other.maxLon) { + return false; + } + return this.minLat <= other.maxLat; + + } + + public isContainedIn(other: BBox) { + if (this.maxLon > other.maxLon) { + return false; + } + if (this.maxLat > other.maxLat) { + return false; + } + if (this.minLon < other.minLon) { + return false; + } + if (this.minLat < other.minLat) { + return false + } + return true; + } + + private check() { + if (isNaN(this.maxLon) || isNaN(this.maxLat) || isNaN(this.minLon) || isNaN(this.minLat)) { + console.log(this); + throw "BBOX has NAN"; + } + } + + static fromTile(z: number, x: number, y: number): BBox { + return new BBox(Tiles.tile_bounds_lon_lat(z, x, y)) + } + + static fromTileIndex(i: number): BBox { + if (i === 0) { + return BBox.global + } + return BBox.fromTile(...Tiles.tile_from_index(i)) + } + + getEast() { + return this.maxLon + } + + getNorth() { + return this.maxLat + } + + getWest() { + return this.minLon + } + + getSouth() { + return this.minLat + } + + pad(factor: number): BBox { + const latDiff = this.maxLat - this.minLat + const lat = (this.maxLat + this.minLat) / 2 + const lonDiff = this.maxLon - this.minLon + const lon = (this.maxLon + this.minLon) / 2 + return new BBox([[ + lon - lonDiff * factor, + lat - latDiff * factor + ], [lon + lonDiff * factor, + lat + latDiff * factor]]) + } + + toLeaflet() { + return [[this.minLat, this.minLon], [this.maxLat, this.maxLon]] + } + + asGeoJson(properties: any): any { + return { + type: "Feature", + properties: properties, + geometry: { + type: "Polygon", + coordinates: [[ + + [this.minLon, this.minLat], + [this.maxLon, this.minLat], + [this.maxLon, this.maxLat], + [this.minLon, this.maxLat], + [this.minLon, this.minLat], + + ]] + } + } + } + + /** + * Expands the BBOx so that it contains complete tiles for the given zoomlevel + * @param zoomlevel + */ + expandToTileBounds(zoomlevel: number): BBox { + const ul = Tiles.embedded_tile(this.minLat, this.minLon, zoomlevel) + const lr = Tiles.embedded_tile(this.maxLat, this.maxLon, zoomlevel) + const boundsul = Tiles.tile_bounds_lon_lat(ul.z, ul.x, ul.y) + const boundslr = Tiles.tile_bounds_lon_lat(lr.z, lr.x, lr.y) + return new BBox([].concat(boundsul, boundslr)) + } +} \ No newline at end of file diff --git a/Logic/ContributorCount.ts b/Logic/ContributorCount.ts index 9c954bdfb4..f39d1106f0 100644 --- a/Logic/ContributorCount.ts +++ b/Logic/ContributorCount.ts @@ -1,21 +1,47 @@ /// Given a feature source, calculates a list of OSM-contributors who mapped the latest versions -import FeatureSource from "./FeatureSource/FeatureSource"; import {UIEventSource} from "./UIEventSource"; +import FeaturePipeline from "./FeatureSource/FeaturePipeline"; +import Loc from "../Models/Loc"; +import {BBox} from "./BBox"; export default class ContributorCount { - public readonly Contributors: UIEventSource>; + public readonly Contributors: UIEventSource> = new UIEventSource>(new Map()); + private readonly state: { featurePipeline: FeaturePipeline, currentBounds: UIEventSource, locationControl: UIEventSource }; - constructor(featureSource: FeatureSource) { - this.Contributors = featureSource.features.map(features => { - const hist = new Map(); - for (const feature of features) { - const contributor = feature.feature.properties["_last_edit:contributor"] + constructor(state: { featurePipeline: FeaturePipeline, currentBounds: UIEventSource, locationControl: UIEventSource }) { + this.state = state; + const self = this; + state.currentBounds.map(bbox => { + self.update(bbox) + }) + state.featurePipeline.runningQuery.addCallbackAndRun( + _ => self.update(state.currentBounds.data) + ) + + } + + private lastUpdate: Date = undefined; + + private update(bbox: BBox) { + if(bbox === undefined){ + return; + } + const now = new Date(); + if (this.lastUpdate !== undefined && ((now.getTime() - this.lastUpdate.getTime()) < 1000 * 60)) { + return; + } + this.lastUpdate = now; + const featuresList = this.state.featurePipeline.GetAllFeaturesWithin(bbox) + const hist = new Map(); + for (const list of featuresList) { + for (const feature of list) { + const contributor = feature.properties["_last_edit:contributor"] const count = hist.get(contributor) ?? 0; hist.set(contributor, count + 1) } - return hist; - }) + } + this.Contributors.setData(hist) } } \ No newline at end of file diff --git a/Logic/ElementStorage.ts b/Logic/ElementStorage.ts index 91193ea6d1..ae51094aea 100644 --- a/Logic/ElementStorage.ts +++ b/Logic/ElementStorage.ts @@ -39,11 +39,10 @@ export class ElementStorage { } getEventSourceById(elementId): UIEventSource { - if (this._elements.has(elementId)) { - return this._elements.get(elementId); + if(elementId === undefined){ + return undefined; } - console.error("Can not find eventsource with id ", elementId); - return undefined; + return this._elements.get(elementId); } has(id) { diff --git a/Logic/ExtraFunction.ts b/Logic/ExtraFunction.ts index b3b10ac2ab..76c28c5a9b 100644 --- a/Logic/ExtraFunction.ts +++ b/Logic/ExtraFunction.ts @@ -1,13 +1,24 @@ import {GeoOperations} from "./GeoOperations"; import Combine from "../UI/Base/Combine"; -import {Relation} from "./Osm/ExtractRelations"; +import RelationsTracker from "./Osm/RelationsTracker"; import State from "../State"; -import {Utils} from "../Utils"; import BaseUIElement from "../UI/BaseUIElement"; import List from "../UI/Base/List"; import Title from "../UI/Base/Title"; import {UIEventSourceTools} from "./UIEventSource"; import AspectedRouting from "./Osm/aspectedRouting"; +import {BBox} from "./BBox"; + +export interface ExtraFuncParams { + /** + * Gets all the features from the given layer within the given BBOX. + * Note that more features then requested can be given back. + * Format: [ [ geojson, geojson, geojson, ... ], [geojson, ...], ...] + */ + getFeaturesWithin: (layerId: string, bbox: BBox) => any[][], + memberships: RelationsTracker +} + export class ExtraFunction { @@ -45,21 +56,30 @@ export class ExtraFunction { private static readonly OverlapFunc = new ExtraFunction( { name: "overlapWith", - doc: "Gives a list of features from the specified layer which this feature (partly) overlaps with. If the current feature is a point, all features that embed the point are given. The returned value is `{ feat: GeoJSONFeature, overlap: number}[]` where `overlap` is the overlapping surface are (in m²) for areas, the overlapping length (in meter) if the current feature is a line or `undefined` if the current feature is a point", + doc: "Gives a list of features from the specified layer which this feature (partly) overlaps with. " + + "If the current feature is a point, all features that embed the point are given. " + + "The returned value is `{ feat: GeoJSONFeature, overlap: number}[]` where `overlap` is the overlapping surface are (in m²) for areas, the overlapping length (in meter) if the current feature is a line or `undefined` if the current feature is a point.\n" + + "\n" + + "For example to get all objects which overlap or embed from a layer, use `_contained_climbing_routes_properties=feat.overlapWith('climbing_route')`", args: ["...layerIds - one or more layer ids of the layer from which every feature is checked for overlap)"] }, (params, feat) => { return (...layerIds: string[]) => { const result = [] + + const bbox = BBox.get(feat) + for (const layerId of layerIds) { - const otherLayer = params.featuresPerLayer.get(layerId); - if (otherLayer === undefined) { + const otherLayers = params.getFeaturesWithin(layerId, bbox) + if (otherLayers === undefined) { continue; } - if (otherLayer.length === 0) { + if (otherLayers.length === 0) { continue; } - result.push(...GeoOperations.calculateOverlap(feat, otherLayer)); + for (const otherLayer of otherLayers) { + result.push(...GeoOperations.calculateOverlap(feat, otherLayer)); + } } return result; } @@ -69,10 +89,13 @@ export class ExtraFunction { { name: "distanceTo", doc: "Calculates the distance between the feature and a specified point in kilometer. The input should either be a pair of coordinates, a geojson feature or the ID of an object", - args: ["longitude", "latitude"] + args: ["feature OR featureID OR longitude", "undefined OR latitude"] }, (featuresPerLayer, feature) => { return (arg0, lat) => { + if (arg0 === undefined) { + return undefined; + } if (typeof arg0 === "number") { // Feature._lon and ._lat is conveniently place by one of the other metatags return GeoOperations.distanceBetween([arg0, lat], [feature._lon, feature._lat]); @@ -92,59 +115,40 @@ export class ExtraFunction { } } ) - private static readonly ClosestObjectFunc = new ExtraFunction( { name: "closest", - doc: "Given either a list of geojson features or a single layer name, gives the single object which is nearest to the feature. In the case of ways/polygons, only the centerpoint is considered.", + doc: "Given either a list of geojson features or a single layer name, gives the single object which is nearest to the feature. In the case of ways/polygons, only the centerpoint is considered. Returns a single geojson feature or undefined if nothing is found (or not yet laoded)", args: ["list of features"] }, (params, feature) => { - return (features) => { - if (typeof features === "string") { - const name = features - features = params.featuresPerLayer.get(features) - if (features === undefined) { - var keys = Utils.NoNull(Array.from(params.featuresPerLayer.keys())); - if (keys.length > 0) { - throw `No features defined for ${name}. Defined layers are ${keys.join(", ")}`; - } else { - // This is the first pass over an external dataset - // Other data probably still has to load! - return undefined; - } - - } - } - - let closestFeature = undefined; - let closestDistance = undefined; - for (const otherFeature of features) { - if (otherFeature == feature || otherFeature.id == feature.id) { - continue; // We ignore self - } - let distance = undefined; - if (otherFeature._lon !== undefined && otherFeature._lat !== undefined) { - distance = GeoOperations.distanceBetween([otherFeature._lon, otherFeature._lat], [feature._lon, feature._lat]); - } else { - distance = GeoOperations.distanceBetween( - GeoOperations.centerpointCoordinates(otherFeature), - [feature._lon, feature._lat] - ) - } - if (distance === undefined) { - throw "Undefined distance!" - } - if (closestFeature === undefined || distance < closestDistance) { - closestFeature = otherFeature - closestDistance = distance; - } - } - return closestFeature; - } + return (features) => ExtraFunction.GetClosestNFeatures(params, feature, features)?.[0]?.feat } ) + private static readonly ClosestNObjectFunc = new ExtraFunction( + { + name: "closestn", + doc: "Given either a list of geojson features or a single layer name, gives the n closest objects which are nearest to the feature (excluding the feature itself). In the case of ways/polygons, only the centerpoint is considered. " + + "Returns a list of `{feat: geojson, distance:number}` the empty list if nothing is found (or not yet loaded)\n\n" + + "If a 'unique tag key' is given, the tag with this key will only appear once (e.g. if 'name' is given, all features will have a different name)", + args: ["list of features or layer name", "amount of features", "unique tag key (optional)", "maxDistanceInMeters (optional)"] + }, + (params, feature) => { + + return (features, amount, uniqueTag, maxDistanceInMeters) => { + let distance : number = Number(maxDistanceInMeters) + if(isNaN(distance)){ + distance = undefined + } + return ExtraFunction.GetClosestNFeatures(params, feature, features, { + maxFeatures: Number(amount), + uniqueTag: uniqueTag, + maxDistance: distance + }); + } + } + ) private static readonly Memberships = new ExtraFunction( { @@ -154,11 +158,12 @@ export class ExtraFunction { "For example: `_part_of_walking_routes=feat.memberships().map(r => r.relation.tags.name).join(';')`", args: [] }, - (params, _) => { - return () => params.relations ?? []; + (params, feat) => { + return () => + params.memberships.knownRelations.data.get(feat.properties.id) ?? [] + } ) - private static readonly AspectedRouting = new ExtraFunction( { name: "score", @@ -178,30 +183,30 @@ export class ExtraFunction { } } ) - private static readonly allFuncs: ExtraFunction[] = [ ExtraFunction.DistanceToFunc, ExtraFunction.OverlapFunc, ExtraFunction.ClosestObjectFunc, + ExtraFunction.ClosestNObjectFunc, ExtraFunction.Memberships, ExtraFunction.AspectedRouting ]; private readonly _name: string; private readonly _args: string[]; private readonly _doc: string; - private readonly _f: (params: { featuresPerLayer: Map, relations: { role: string, relation: Relation }[] }, feat: any) => any; + private readonly _f: (params: ExtraFuncParams, feat: any) => any; constructor(options: { name: string, doc: string, args: string[] }, - f: ((params: { featuresPerLayer: Map, relations: { role: string, relation: Relation }[] }, feat: any) => any)) { + f: ((params: ExtraFuncParams, feat: any) => any)) { this._name = options.name; this._doc = options.doc; this._args = options.args; this._f = f; } - public static FullPatchFeature(featuresPerLayer: Map, relations: { role: string, relation: Relation }[], feature) { + public static FullPatchFeature(params: ExtraFuncParams, feature) { for (const func of ExtraFunction.allFuncs) { - func.PatchFeature(featuresPerLayer, relations, feature); + func.PatchFeature(params, feature); } } @@ -221,7 +226,134 @@ export class ExtraFunction { ]); } - public PatchFeature(featuresPerLayer: Map, relations: { role: string, relation: Relation }[], feature: any) { - feature[this._name] = this._f({featuresPerLayer: featuresPerLayer, relations: relations}, feature) + /** + * Gets the closes N features, sorted by ascending distance. + * + * @param params: The link to mapcomplete state + * @param feature: The central feature under consideration + * @param features: The other features + * @param options: maxFeatures: The maximum amount of features to be returned. Default: 1; uniqueTag: returned features are not allowed to have the same value for this key; maxDistance: stop searching if it is too far away (in meter). Default: 500m + * @constructor + * @private + */ + private static GetClosestNFeatures(params: ExtraFuncParams, + feature: any, + features: string | any[], + options?: { maxFeatures?: number, uniqueTag?: string | undefined, maxDistance?: number }): { feat: any, distance: number }[] { + const maxFeatures = options?.maxFeatures ?? 1 + const maxDistance = options?.maxDistance ?? 500 + const uniqueTag: string | undefined = options?.uniqueTag + if (typeof features === "string") { + const name = features + const bbox = GeoOperations.bbox(GeoOperations.buffer(GeoOperations.bbox(feature), maxDistance)) + features = params.getFeaturesWithin(name, new BBox(bbox.geometry.coordinates)) + }else{ + features = [features] + } + if (features === undefined) { + return; + } + + let closestFeatures: { feat: any, distance: number }[] = []; + for(const featureList of features) { + for (const otherFeature of featureList) { + if (otherFeature === feature || otherFeature.id === feature.id) { + continue; // We ignore self + } + let distance = undefined; + if (otherFeature._lon !== undefined && otherFeature._lat !== undefined) { + distance = GeoOperations.distanceBetween([otherFeature._lon, otherFeature._lat], [feature._lon, feature._lat]); + } else { + distance = GeoOperations.distanceBetween( + GeoOperations.centerpointCoordinates(otherFeature), + [feature._lon, feature._lat] + ) + } + if (distance === undefined || distance === null) { + console.error("Could not calculate the distance between", feature, "and", otherFeature) + throw "Undefined distance!" + } + if (distance > maxDistance) { + continue + } + + if (closestFeatures.length === 0) { + closestFeatures.push({ + feat: otherFeature, + distance: distance + }) + continue; + } + + if (closestFeatures.length >= maxFeatures && closestFeatures[maxFeatures - 1].distance < distance) { + // The last feature of the list (and thus the furthest away is still closer + // No use for checking, as we already have plenty of features! + continue + } + + let targetIndex = closestFeatures.length + for (let i = 0; i < closestFeatures.length; i++) { + const closestFeature = closestFeatures[i]; + + if (uniqueTag !== undefined) { + const uniqueTagsMatch = otherFeature.properties[uniqueTag] !== undefined && + closestFeature.feat.properties[uniqueTag] === otherFeature.properties[uniqueTag] + if (uniqueTagsMatch) { + targetIndex = -1 + if (closestFeature.distance > distance) { + // This is a very special situation: + // We want to see the tag `uniquetag=some_value` only once in the entire list (e.g. to prevent road segements of identical names to fill up the list of 'names of nearby roads') + // AT this point, we have found a closer segment with the same, identical tag + // so we replace directly + closestFeatures[i] = {feat: otherFeature, distance: distance} + } + break; + } + } + + if (closestFeature.distance > distance) { + targetIndex = i + + if (uniqueTag !== undefined) { + const uniqueValue = otherFeature.properties[uniqueTag] + // We might still have some other values later one with the same uniquetag that have to be cleaned + for (let j = i; j < closestFeatures.length; j++) { + if (closestFeatures[j].feat.properties[uniqueTag] === uniqueValue) { + closestFeatures.splice(j, 1) + } + } + } + break; + } + } + + if (targetIndex == -1) { + continue; // value is already swapped by the unique tag + } + + if (targetIndex < maxFeatures) { + // insert and drop one + closestFeatures.splice(targetIndex, 0, { + feat: otherFeature, + distance: distance + }) + if (closestFeatures.length >= maxFeatures) { + closestFeatures.splice(maxFeatures, 1) + } + } else { + // Overwrite the last element + closestFeatures[targetIndex] = { + feat: otherFeature, + distance: distance + } + + } + } + } + return closestFeatures; + } + + public PatchFeature(params: ExtraFuncParams, feature: any) { + feature[this._name] = this._f(params, feature) } } diff --git a/Logic/FeatureSource/RegisteringFeatureSource.ts b/Logic/FeatureSource/Actors/RegisteringAllFromFeatureSourceActor.ts similarity index 69% rename from Logic/FeatureSource/RegisteringFeatureSource.ts rename to Logic/FeatureSource/Actors/RegisteringAllFromFeatureSourceActor.ts index e464a60b8e..2408464700 100644 --- a/Logic/FeatureSource/RegisteringFeatureSource.ts +++ b/Logic/FeatureSource/Actors/RegisteringAllFromFeatureSourceActor.ts @@ -1,8 +1,8 @@ -import FeatureSource from "./FeatureSource"; -import {UIEventSource} from "../UIEventSource"; -import State from "../../State"; +import FeatureSource from "../FeatureSource"; +import {UIEventSource} from "../../UIEventSource"; +import State from "../../../State"; -export default class RegisteringFeatureSource implements FeatureSource { +export default class RegisteringAllFromFeatureSourceActor { public readonly features: UIEventSource<{ feature: any; freshness: Date }[]>; public readonly name; diff --git a/Logic/FeatureSource/Actors/SaveTileToLocalStorageActor.ts b/Logic/FeatureSource/Actors/SaveTileToLocalStorageActor.ts new file mode 100644 index 0000000000..2894d56b82 --- /dev/null +++ b/Logic/FeatureSource/Actors/SaveTileToLocalStorageActor.ts @@ -0,0 +1,36 @@ +/*** + * Saves all the features that are passed in to localstorage, so they can be retrieved on the next run + * + * Technically, more an Actor then a featuresource, but it fits more neatly this ay + */ +import {FeatureSourceForLayer} from "../FeatureSource"; + +export default class SaveTileToLocalStorageActor { + public static readonly storageKey: string = "cached-features"; + public static readonly formatVersion: string = "1" + + constructor(source: FeatureSourceForLayer, tileIndex: number) { + source.features.addCallbackAndRunD(features => { + const key = `${SaveTileToLocalStorageActor.storageKey}-${source.layer.layerDef.id}-${tileIndex}` + const now = new Date() + + try { + if (features.length > 0) { + localStorage.setItem(key, JSON.stringify(features)); + } + // We _still_ write the time to know that this tile is empty! + SaveTileToLocalStorageActor.MarkVisited(source.layer.layerDef.id, tileIndex, now) + } catch (e) { + console.warn("Could not save the features to local storage:", e) + } + }) + } + + + public static MarkVisited(layerId: string, tileId: number, freshness: Date){ + const key = `${SaveTileToLocalStorageActor.storageKey}-${layerId}-${tileId}` + localStorage.setItem(key + "-time", JSON.stringify(freshness.getTime())) + localStorage.setItem(key + "-format", SaveTileToLocalStorageActor.formatVersion) + + } +} \ No newline at end of file diff --git a/Logic/FeatureSource/ChangeApplicator.ts b/Logic/FeatureSource/ChangeApplicator.ts deleted file mode 100644 index 9b4f2271dc..0000000000 --- a/Logic/FeatureSource/ChangeApplicator.ts +++ /dev/null @@ -1,162 +0,0 @@ -import FeatureSource from "./FeatureSource"; -import {UIEventSource} from "../UIEventSource"; -import {Changes} from "../Osm/Changes"; -import {ChangeDescription} from "../Osm/Actions/ChangeDescription"; -import {Utils} from "../../Utils"; -import {OsmNode, OsmRelation, OsmWay} from "../Osm/OsmObject"; - - -/** - * Applies changes from 'Changes' onto a featureSource - */ -export default class ChangeApplicator implements FeatureSource { - public readonly features: UIEventSource<{ feature: any; freshness: Date }[]>; - public readonly name: string; - - constructor(source: FeatureSource, changes: Changes, mode?: { - generateNewGeometries: boolean - }) { - - this.name = "ChangesApplied(" + source.name + ")" - this.features = source.features - const seenChanges = new Set(); - const self = this; - let runningUpdate = false; - source.features.addCallbackAndRunD(features => { - if (runningUpdate) { - return; // No need to ping again - } - ChangeApplicator.ApplyChanges(features, changes.pendingChanges.data, mode) - seenChanges.clear() - }) - - changes.pendingChanges.addCallbackAndRunD(changes => { - runningUpdate = true; - changes = changes.filter(ch => !seenChanges.has(ch)) - changes.forEach(c => seenChanges.add(c)) - ChangeApplicator.ApplyChanges(self.features.data, changes, mode) - source.features.ping() - runningUpdate = false; - }) - - - } - - - /** - * Returns true if the geometry is changed and the source should be pinged - */ - private static ApplyChanges(features: { feature: any; freshness: Date }[], cs: ChangeDescription[], mode: { generateNewGeometries: boolean }): boolean { - if (cs.length === 0 || features === undefined) { - return; - } - - console.log("Applying changes ", this.name, cs) - let geometryChanged = false; - const changesPerId: Map = new Map() - for (const c of cs) { - const id = c.type + "/" + c.id - if (!changesPerId.has(id)) { - changesPerId.set(id, []) - } - changesPerId.get(id).push(c) - } - - - const now = new Date() - - function add(feature) { - feature.id = feature.properties.id - features.push({ - feature: feature, - freshness: now - }) - console.log("Added a new feature: ", feature) - geometryChanged = true; - } - - // First, create the new features - they have a negative ID - // We don't set the properties yet though - if (mode?.generateNewGeometries) { - changesPerId.forEach(cs => { - cs - .forEach(change => { - if (change.id >= 0) { - return; // Nothing to do here, already created - } - - if (change.changes === undefined) { - // An update to the object - not the actual created - return; - } - - try { - - switch (change.type) { - case "node": - const n = new OsmNode(change.id) - n.lat = change.changes["lat"] - n.lon = change.changes["lon"] - const geojson = n.asGeoJson() - add(geojson) - break; - case "way": - const w = new OsmWay(change.id) - w.nodes = change.changes["nodes"] - add(w.asGeoJson()) - break; - case "relation": - const r = new OsmRelation(change.id) - r.members = change.changes["members"] - add(r.asGeoJson()) - break; - } - - } catch (e) { - console.error(e) - } - }) - }) - } - - for (const feature of features) { - const f = feature.feature; - const id = f.properties.id; - if (!changesPerId.has(id)) { - continue; - } - - - const changed = {} - // Copy all the properties - Utils.Merge(f, changed) - // play the changes onto the copied object - - for (const change of changesPerId.get(id)) { - for (const kv of change.tags ?? []) { - // Apply tag changes and ping the consumers - f.properties[kv.k] = kv.v; - } - - // Apply other changes to the object - if (change.changes !== undefined) { - geometryChanged = true; - switch (change.type) { - case "node": - // @ts-ignore - const coor: { lat, lon } = change.changes; - f.geometry.coordinates = [coor.lon, coor.lat] - break; - case "way": - f.geometry.coordinates = change.changes["locations"] - break; - case "relation": - console.error("Changes to relations are not yet supported") - break; - } - } - } - } - return geometryChanged - } -} \ No newline at end of file diff --git a/Logic/FeatureSource/FeatureDuplicatorPerLayer.ts b/Logic/FeatureSource/FeatureDuplicatorPerLayer.ts deleted file mode 100644 index 87d91bb0ce..0000000000 --- a/Logic/FeatureSource/FeatureDuplicatorPerLayer.ts +++ /dev/null @@ -1,64 +0,0 @@ -import FeatureSource from "./FeatureSource"; -import {UIEventSource} from "../UIEventSource"; -import FilteredLayer from "../../Models/FilteredLayer"; - - -/** - * In some rare cases, some elements are shown on multiple layers (when 'passthrough' is enabled) - * If this is the case, multiple objects with a different _matching_layer_id are generated. - * In any case, this featureSource marks the objects with _matching_layer_id - */ -export default class FeatureDuplicatorPerLayer implements FeatureSource { - public readonly features: UIEventSource<{ feature: any; freshness: Date }[]>; - - public readonly name; - - constructor(layers: UIEventSource, upstream: FeatureSource) { - this.name = "FeatureDuplicator of " + upstream.name; - this.features = upstream.features.map(features => { - const newFeatures: { feature: any, freshness: Date }[] = []; - if (features === undefined) { - return newFeatures; - } - - for (const f of features) { - if (f.feature._matching_layer_id) { - // Already matched previously - // We simply add it - newFeatures.push(f); - continue; - } - - - let foundALayer = false; - for (const layer of layers.data) { - if (layer.layerDef.source.osmTags.matchesProperties(f.feature.properties)) { - foundALayer = true; - if (layer.layerDef.passAllFeatures) { - - // We copy the feature; the "properties" field is kept identical though! - // Keeping "properties" identical is needed, as it might break the 'allElementStorage' otherwise - const newFeature = { - geometry: f.feature.geometry, - id: f.feature.id, - type: f.feature.type, - properties: f.feature.properties, - _matching_layer_id: layer.layerDef.id - } - newFeatures.push({feature: newFeature, freshness: f.freshness}); - } else { - // If not 'passAllFeatures', we are done - f.feature._matching_layer_id = layer.layerDef.id; - newFeatures.push(f); - break; - } - } - } - } - return newFeatures; - - }) - - } - -} \ No newline at end of file diff --git a/Logic/FeatureSource/FeaturePipeline.ts b/Logic/FeatureSource/FeaturePipeline.ts index 5e91b4567f..3aa0af3562 100644 --- a/Logic/FeatureSource/FeaturePipeline.ts +++ b/Logic/FeatureSource/FeaturePipeline.ts @@ -1,95 +1,415 @@ -import FilteringFeatureSource from "../FeatureSource/FilteringFeatureSource"; -import FeatureSourceMerger from "../FeatureSource/FeatureSourceMerger"; -import RememberingSource from "../FeatureSource/RememberingSource"; -import WayHandlingApplyingFeatureSource from "../FeatureSource/WayHandlingApplyingFeatureSource"; -import FeatureDuplicatorPerLayer from "../FeatureSource/FeatureDuplicatorPerLayer"; -import FeatureSource from "../FeatureSource/FeatureSource"; -import {UIEventSource} from "../UIEventSource"; -import LocalStorageSaver from "./LocalStorageSaver"; -import LocalStorageSource from "./LocalStorageSource"; -import Loc from "../../Models/Loc"; -import GeoJsonSource from "./GeoJsonSource"; -import MetaTaggingFeatureSource from "./MetaTaggingFeatureSource"; -import RegisteringFeatureSource from "./RegisteringFeatureSource"; -import FilteredLayer from "../../Models/FilteredLayer"; -import {Changes} from "../Osm/Changes"; -import ChangeApplicator from "./ChangeApplicator"; import LayoutConfig from "../../Models/ThemeConfig/LayoutConfig"; +import FilteringFeatureSource from "./Sources/FilteringFeatureSource"; +import PerLayerFeatureSourceSplitter from "./PerLayerFeatureSourceSplitter"; +import FeatureSource, {FeatureSourceForLayer, IndexedFeatureSource, Tiled} from "./FeatureSource"; +import TiledFeatureSource from "./TiledFeatureSource/TiledFeatureSource"; +import {UIEventSource} from "../UIEventSource"; +import {TileHierarchyTools} from "./TiledFeatureSource/TileHierarchy"; +import FilteredLayer from "../../Models/FilteredLayer"; +import MetaTagging from "../MetaTagging"; +import RememberingSource from "./Sources/RememberingSource"; +import OverpassFeatureSource from "../Actors/OverpassFeatureSource"; +import {Changes} from "../Osm/Changes"; +import GeoJsonSource from "./Sources/GeoJsonSource"; +import Loc from "../../Models/Loc"; +import WayHandlingApplyingFeatureSource from "./Sources/WayHandlingApplyingFeatureSource"; +import RegisteringAllFromFeatureSourceActor from "./Actors/RegisteringAllFromFeatureSourceActor"; +import TiledFromLocalStorageSource from "./TiledFeatureSource/TiledFromLocalStorageSource"; +import SaveTileToLocalStorageActor from "./Actors/SaveTileToLocalStorageActor"; +import DynamicGeoJsonTileSource from "./TiledFeatureSource/DynamicGeoJsonTileSource"; +import {TileHierarchyMerger} from "./TiledFeatureSource/TileHierarchyMerger"; +import RelationsTracker from "../Osm/RelationsTracker"; +import {NewGeometryFromChangesFeatureSource} from "./Sources/NewGeometryFromChangesFeatureSource"; +import ChangeGeometryApplicator from "./Sources/ChangeGeometryApplicator"; +import {BBox} from "../BBox"; +import OsmFeatureSource from "./TiledFeatureSource/OsmFeatureSource"; +import {OsmConnection} from "../Osm/OsmConnection"; +import {Tiles} from "../../Models/TileRange"; +import TileFreshnessCalculator from "./TileFreshnessCalculator"; -export default class FeaturePipeline implements FeatureSource { - public features: UIEventSource<{ feature: any; freshness: Date }[]>; +export default class FeaturePipeline { - public readonly name = "FeaturePipeline" + public readonly sufficientlyZoomed: UIEventSource; - constructor(flayers: UIEventSource, - changes: Changes, - updater: FeatureSource, - fromOsmApi: FeatureSource, - layout: UIEventSource, - locationControl: UIEventSource, - selectedElement: UIEventSource) { + public readonly runningQuery: UIEventSource; + public readonly timeout: UIEventSource; - const allLoadedFeatures = new UIEventSource<{ feature: any; freshness: Date }[]>([]) + public readonly somethingLoaded: UIEventSource = new UIEventSource(false) + public readonly newDataLoadedSignal: UIEventSource = new UIEventSource(undefined) - // first we metatag, then we save to get the metatags into storage too - // Note that we need to register before we do metatagging (as it expects the event sources) + private readonly overpassUpdater: OverpassFeatureSource + private state: { + readonly filteredLayers: UIEventSource, + readonly locationControl: UIEventSource, + readonly selectedElement: UIEventSource, + readonly changes: Changes, + readonly layoutToUse: LayoutConfig, + readonly leafletMap: any, + readonly overpassUrl: UIEventSource; + readonly overpassTimeout: UIEventSource; + readonly overpassMaxZoom: UIEventSource; + readonly osmConnection: OsmConnection + readonly currentBounds: UIEventSource + }; + private readonly relationTracker: RelationsTracker + private readonly perLayerHierarchy: Map; - // AT last, the metaTagging also needs to be run _after_ the duplicatorPerLayer - const amendedOverpassSource = - new RememberingSource( - new LocalStorageSaver( - new MetaTaggingFeatureSource(allLoadedFeatures, - new FeatureDuplicatorPerLayer(flayers, - new RegisteringFeatureSource( - new ChangeApplicator( - updater, changes - )) - )), layout)); + private readonly freshnesses = new Map(); - const geojsonSources: FeatureSource [] = GeoJsonSource - .ConstructMultiSource(flayers.data, locationControl) - .map(geojsonSource => { - let source = new RegisteringFeatureSource( - new FeatureDuplicatorPerLayer(flayers, - new ChangeApplicator(geojsonSource, changes))); - if (!geojsonSource.isOsmCache) { - source = new MetaTaggingFeatureSource(allLoadedFeatures, source, updater.features); + private readonly oldestAllowedDate: Date = new Date(new Date().getTime() - 60 * 60 * 24 * 30 * 1000); + private readonly osmSourceZoomLevel = 14 + + constructor( + handleFeatureSource: (source: FeatureSourceForLayer & Tiled) => void, + state: { + readonly filteredLayers: UIEventSource, + readonly locationControl: UIEventSource, + readonly selectedElement: UIEventSource, + readonly changes: Changes, + readonly layoutToUse: LayoutConfig, + readonly leafletMap: any, + readonly overpassUrl: UIEventSource; + readonly overpassTimeout: UIEventSource; + readonly overpassMaxZoom: UIEventSource; + readonly osmConnection: OsmConnection + readonly currentBounds: UIEventSource + }) { + this.state = state; + + const self = this + // milliseconds + const useOsmApi = state.locationControl.map(l => l.zoom > (state.overpassMaxZoom.data ?? 12)) + this.relationTracker = new RelationsTracker() + + + this.sufficientlyZoomed = state.locationControl.map(location => { + if (location?.zoom === undefined) { + return false; } - return source + let minzoom = Math.min(...state.layoutToUse.layers.map(layer => layer.minzoom ?? 18)); + return location.zoom >= minzoom; + } + ); + + const neededTilesFromOsm = this.getNeededTilesFromOsm(this.sufficientlyZoomed) + + const perLayerHierarchy = new Map() + this.perLayerHierarchy = perLayerHierarchy + + const patchedHandleFeatureSource = function (src: FeatureSourceForLayer & IndexedFeatureSource & Tiled) { + // This will already contain the merged features for this tile. In other words, this will only be triggered once for every tile + const srcFiltered = + new FilteringFeatureSource(state, src.tileIndex, + new WayHandlingApplyingFeatureSource( + new ChangeGeometryApplicator(src, state.changes) + ) + ) + + handleFeatureSource(srcFiltered) + self.somethingLoaded.setData(true) + self.freshnesses.get(src.layer.layerDef.id).addTileLoad(src.tileIndex, new Date()) + }; + + + for (const filteredLayer of state.filteredLayers.data) { + const id = filteredLayer.layerDef.id + const source = filteredLayer.layerDef.source + + const hierarchy = new TileHierarchyMerger(filteredLayer, (tile, _) => patchedHandleFeatureSource(tile)) + perLayerHierarchy.set(id, hierarchy) + + this.freshnesses.set(id, new TileFreshnessCalculator()) + + if (source.geojsonSource === undefined) { + // This is an OSM layer + // We load the cached values and register them + // Getting data from upstream happens a bit lower + new TiledFromLocalStorageSource(filteredLayer, + (src) => { + new RegisteringAllFromFeatureSourceActor(src) + hierarchy.registerTile(src); + src.features.addCallbackAndRunD(_ => self.newDataLoadedSignal.setData(src)) + }, state) + + TiledFromLocalStorageSource.GetFreshnesses(id).forEach((value, key) => { + self.freshnesses.get(id).addTileLoad(key, value) + }) + + continue + } + + if (source.geojsonZoomLevel === undefined) { + // This is a 'load everything at once' geojson layer + const src = new GeoJsonSource(filteredLayer) + + if (source.isOsmCacheLayer) { + // We split them up into tiles anyway as it is an OSM source + TiledFeatureSource.createHierarchy(src, { + layer: src.layer, + minZoomLevel: 14, + dontEnforceMinZoom: true, + registerTile: (tile) => { + new RegisteringAllFromFeatureSourceActor(tile) + perLayerHierarchy.get(id).registerTile(tile) + tile.features.addCallbackAndRunD(_ => self.newDataLoadedSignal.setData(tile)) + } + }) + }else{ + new RegisteringAllFromFeatureSourceActor(src) + perLayerHierarchy.get(id).registerTile(src) + src.features.addCallbackAndRunD(_ => self.newDataLoadedSignal.setData(src)) + } + } else { + new DynamicGeoJsonTileSource( + filteredLayer, + tile => { + new RegisteringAllFromFeatureSourceActor(tile) + perLayerHierarchy.get(id).registerTile(tile) + tile.features.addCallbackAndRunD(_ => self.newDataLoadedSignal.setData(tile)) + }, + state + ) + } + } + + + const osmFeatureSource = new OsmFeatureSource({ + isActive: useOsmApi, + neededTiles: neededTilesFromOsm, + handleTile: tile => { + new RegisteringAllFromFeatureSourceActor(tile) + new SaveTileToLocalStorageActor(tile, tile.tileIndex) + perLayerHierarchy.get(tile.layer.layerDef.id).registerTile(tile) + tile.features.addCallbackAndRunD(_ => self.newDataLoadedSignal.setData(tile)) + + }, + state: state, + markTileVisited: (tileId) => + state.filteredLayers.data.forEach(flayer => { + SaveTileToLocalStorageActor.MarkVisited(flayer.layerDef.id, tileId, new Date()) + }) + }) + + + const updater = this.initOverpassUpdater(state, useOsmApi) + this.overpassUpdater = updater; + this.timeout = updater.timeout + + // Actually load data from the overpass source + new PerLayerFeatureSourceSplitter(state.filteredLayers, + (source) => TiledFeatureSource.createHierarchy(source, { + layer: source.layer, + minZoomLevel: 14, + dontEnforceMinZoom: true, + maxFeatureCount: state.layoutToUse.clustering.minNeededElements, + maxZoomLevel: state.layoutToUse.clustering.maxZoom, + registerTile: (tile) => { + // We save the tile data for the given layer to local storage + new SaveTileToLocalStorageActor(tile, tile.tileIndex) + perLayerHierarchy.get(source.layer.layerDef.id).registerTile(new RememberingSource(tile)) + tile.features.addCallbackAndRunD(_ => self.newDataLoadedSignal.setData(tile)) + + } + }), + updater) + + + // Also load points/lines that are newly added. + const newGeometry = new NewGeometryFromChangesFeatureSource(state.changes) + new RegisteringAllFromFeatureSourceActor(newGeometry) + // A NewGeometryFromChangesFeatureSource does not split per layer, so we do this next + new PerLayerFeatureSourceSplitter(state.filteredLayers, + (perLayer) => { + // We don't bother to split them over tiles as it'll contain little features by default, so we simply add them like this + perLayerHierarchy.get(perLayer.layer.layerDef.id).registerTile(perLayer) + // AT last, we always apply the metatags whenever possible + perLayer.features.addCallbackAndRunD(_ => self.applyMetaTags(perLayer)) + perLayer.features.addCallbackAndRunD(_ => self.newDataLoadedSignal.setData(perLayer)) + + }, + newGeometry + ) + + + // Whenever fresh data comes in, we need to update the metatagging + self.newDataLoadedSignal.stabilized(1000).addCallback(src => { + self.updateAllMetaTagging() + }) + + + this.runningQuery = updater.runningQuery.map( + overpass => overpass || osmFeatureSource.isRunning.data, [osmFeatureSource.isRunning] + ) + + + } + + private freshnessForVisibleLayers(z: number, x: number, y: number): Date { + let oldestDate = undefined; + for (const flayer of this.state.filteredLayers.data) { + if (!flayer.isDisplayed.data) { + continue + } + if (this.state.locationControl.data.zoom < flayer.layerDef.minzoom) { + continue; + } + const freshness = this.freshnesses.get(flayer.layerDef.id).freshnessFor(z, x, y) + if (freshness === undefined) { + // SOmething is undefined --> we return undefined as we have to download + return undefined + } + if (oldestDate === undefined || oldestDate > freshness) { + oldestDate = freshness + } + } + return oldestDate + } + + private getNeededTilesFromOsm(isSufficientlyZoomed: UIEventSource): UIEventSource { + const self = this + return this.state.currentBounds.map(bbox => { + if (bbox === undefined) { + return + } + if (!isSufficientlyZoomed.data) { + return; + } + const osmSourceZoomLevel = self.osmSourceZoomLevel + const range = bbox.containingTileRange(osmSourceZoomLevel) + const tileIndexes = [] + if (range.total > 100) { + // Too much tiles! + return [] + } + Tiles.MapRange(range, (x, y) => { + const i = Tiles.tile_index(osmSourceZoomLevel, x, y); + const oldestDate = self.freshnessForVisibleLayers(osmSourceZoomLevel, x, y) + if (oldestDate !== undefined && oldestDate > this.oldestAllowedDate) { + console.debug("Skipping tile", osmSourceZoomLevel, x, y, "as a decently fresh one is available") + // The cached tiles contain decently fresh data + return; + } + tileIndexes.push(i) + }) + return tileIndexes + }) + } + + private initOverpassUpdater(state: { + layoutToUse: LayoutConfig, + currentBounds: UIEventSource, + locationControl: UIEventSource, + readonly overpassUrl: UIEventSource; + readonly overpassTimeout: UIEventSource; + readonly overpassMaxZoom: UIEventSource, + }, useOsmApi: UIEventSource): OverpassFeatureSource { + const minzoom = Math.min(...state.layoutToUse.layers.map(layer => layer.minzoom)) + const overpassIsActive = state.currentBounds.map(bbox => { + if (bbox === undefined) { + return false + } + let zoom = state.locationControl.data.zoom + if (zoom < minzoom) { + return false; + } + if (zoom > 16) { + zoom = 16 + } + if (zoom < 8) { + zoom = zoom + 2 + } + + const range = bbox.containingTileRange(zoom) + if (range.total > 100) { + return false + } + const self = this; + const allFreshnesses = Tiles.MapRange(range, (x, y) => self.freshnessForVisibleLayers(zoom, x, y)) + return allFreshnesses.some(freshness => freshness === undefined || freshness < this.oldestAllowedDate) + + }, [state.locationControl]) + + const self = this; + const updater = new OverpassFeatureSource(state, + { + relationTracker: this.relationTracker, + isActive: useOsmApi.map(b => !b && overpassIsActive.data, [overpassIsActive]), + onBboxLoaded: ((bbox, date, downloadedLayers) => { + Tiles.MapRange(bbox.containingTileRange(self.osmSourceZoomLevel), (x, y) => { + downloadedLayers.forEach(layer => { + SaveTileToLocalStorageActor.MarkVisited(layer.id, Tiles.tile_index(this.osmSourceZoomLevel, x, y), date) + }) + }) + + }) }); - const amendedLocalStorageSource = - new RememberingSource(new RegisteringFeatureSource(new FeatureDuplicatorPerLayer(flayers, new ChangeApplicator(new LocalStorageSource(layout), changes)) - )); - const amendedOsmApiSource = new RememberingSource( - new MetaTaggingFeatureSource(allLoadedFeatures, - new FeatureDuplicatorPerLayer(flayers, - new RegisteringFeatureSource(new ChangeApplicator(fromOsmApi, changes, - { - // We lump in the new points here - generateNewGeometries: true - } - ))))); + // Register everything in the state' 'AllElements' + new RegisteringAllFromFeatureSourceActor(updater) + return updater; + } - const merged = - new FeatureSourceMerger([ - amendedOverpassSource, - amendedOsmApiSource, - amendedLocalStorageSource, - ...geojsonSources - ]); + private applyMetaTags(src: FeatureSourceForLayer) { + const self = this + window.setTimeout( + () => { + console.debug("Applying metatagging onto ", src.name) + const layerDef = src.layer.layerDef; + MetaTagging.addMetatags( + src.features.data, + { + memberships: this.relationTracker, + getFeaturesWithin: (layerId, bbox: BBox) => self.GetFeaturesWithin(layerId, bbox) + }, + layerDef, + { + includeDates: true, + // We assume that the non-dated metatags are already set by the cache generator + includeNonDates: layerDef.source.geojsonSource === undefined || !layerDef.source.isOsmCacheLayer + } + ) + }, + 15 + ) - merged.features.syncWith(allLoadedFeatures) + } - this.features = new WayHandlingApplyingFeatureSource(flayers, - new FilteringFeatureSource( - flayers, - locationControl, - selectedElement, - merged - )).features; + private updateAllMetaTagging() { + const self = this; + console.log("Reupdating all metatagging") + this.perLayerHierarchy.forEach(hierarchy => { + hierarchy.loadedTiles.forEach(src => { + self.applyMetaTags(src) + }) + }) + + } + + public GetAllFeaturesWithin(bbox: BBox): any[][] { + const self = this + const tiles = [] + Array.from(this.perLayerHierarchy.keys()) + .forEach(key => tiles.push(...self.GetFeaturesWithin(key, bbox))) + return tiles; + } + + public GetFeaturesWithin(layerId: string, bbox: BBox): any[][] { + const requestedHierarchy = this.perLayerHierarchy.get(layerId) + if (requestedHierarchy === undefined) { + console.warn("Layer ", layerId, "is not defined. Try one of ", Array.from(this.perLayerHierarchy.keys())) + return undefined; + } + return TileHierarchyTools.getTiles(requestedHierarchy, bbox) + .filter(featureSource => featureSource.features?.data !== undefined) + .map(featureSource => featureSource.features.data.map(fs => fs.feature)) + } + + public GetTilesPerLayerWithin(bbox: BBox, handleTile: (tile: FeatureSourceForLayer & Tiled) => void) { + Array.from(this.perLayerHierarchy.values()).forEach(hierarchy => { + TileHierarchyTools.getTiles(hierarchy, bbox).forEach(handleTile) + }) } } \ No newline at end of file diff --git a/Logic/FeatureSource/FeatureSource.ts b/Logic/FeatureSource/FeatureSource.ts index 624658e798..7d603d1f6b 100644 --- a/Logic/FeatureSource/FeatureSource.ts +++ b/Logic/FeatureSource/FeatureSource.ts @@ -1,5 +1,7 @@ import {UIEventSource} from "../UIEventSource"; import {Utils} from "../../Utils"; +import FilteredLayer from "../../Models/FilteredLayer"; +import {BBox} from "../BBox"; export default interface FeatureSource { features: UIEventSource<{ feature: any, freshness: Date }[]>; @@ -9,38 +11,30 @@ export default interface FeatureSource { name: string; } -export class FeatureSourceUtils { +export interface Tiled { + tileIndex: number, + bbox: BBox +} - /** - * Exports given featurePipeline as a geojson FeatureLists (downloads as a json) - * @param featurePipeline The FeaturePipeline you want to export - * @param options The options object - * @param options.metadata True if you want to include the MapComplete metadata, false otherwise - */ - public static extractGeoJson(featurePipeline: FeatureSource, options: { metadata?: boolean } = {}) { - let defaults = { - metadata: false, - } - options = Utils.setDefaults(options, defaults); +/** + * A feature source which only contains features for the defined layer + */ +export interface FeatureSourceForLayer extends FeatureSource{ + readonly layer: FilteredLayer +} - // Select all features, ignore the freshness and other data - let featureList: any[] = featurePipeline.features.data.map((feature) => - JSON.parse(JSON.stringify((feature.feature)))); // Make a deep copy! +/** + * A feature source which is aware of the indexes it contains + */ +export interface IndexedFeatureSource extends FeatureSource { + readonly containedIds: UIEventSource> +} - if (!options.metadata) { - for (let i = 0; i < featureList.length; i++) { - let feature = featureList[i]; - for (let property in feature.properties) { - if (property[0] == "_" && property !== "_lat" && property !== "_lon") { - delete featureList[i]["properties"][property]; - } - } - } - } - return {type: "FeatureCollection", features: featureList} - - - } - - -} \ No newline at end of file +/** + * A feature source which has some extra data about it's state + */ +export interface FeatureSourceState { + readonly sufficientlyZoomed: UIEventSource; + readonly runningQuery: UIEventSource; + readonly timeout: UIEventSource; +} diff --git a/Logic/FeatureSource/FeatureSourceMerger.ts b/Logic/FeatureSource/FeatureSourceMerger.ts deleted file mode 100644 index 3d1b794c6d..0000000000 --- a/Logic/FeatureSource/FeatureSourceMerger.ts +++ /dev/null @@ -1,74 +0,0 @@ -import FeatureSource from "./FeatureSource"; -import {UIEventSource} from "../UIEventSource"; - -/** - * Merges features from different featureSources - * Uses the freshest feature available in the case multiple sources offer data with the same identifier - */ -export default class FeatureSourceMerger implements FeatureSource { - - public features: UIEventSource<{ feature: any; freshness: Date }[]> = new UIEventSource<{ feature: any; freshness: Date }[]>([]); - public readonly name; - private readonly _sources: FeatureSource[]; - - constructor(sources: FeatureSource[]) { - this._sources = sources; - this.name = "SourceMerger of (" + sources.map(s => s.name).join(", ") + ")" - const self = this; - for (let i = 0; i < sources.length; i++) { - let source = sources[i]; - source.features.addCallback(() => { - self.Update(); - }); - } - this.Update(); - } - - private Update() { - - let somethingChanged = false; - const all: Map = new Map(); - // We seed the dictionary with the previously loaded features - const oldValues = this.features.data ?? []; - for (const oldValue of oldValues) { - all.set(oldValue.feature.id + oldValue.feature._matching_layer_id, oldValue) - } - - for (const source of this._sources) { - if (source?.features?.data === undefined) { - continue; - } - for (const f of source.features.data) { - const id = f.feature.properties.id + f.feature._matching_layer_id; - if (!all.has(id)) { - // This is a new feature - somethingChanged = true; - all.set(id, f); - continue; - } - - // This value has been seen already, either in a previous run or by a previous datasource - // Let's figure out if something changed - const oldV = all.get(id); - if (oldV.freshness < f.freshness) { - // Jup, this feature is fresher - all.set(id, f); - somethingChanged = true; - } - } - } - - if (!somethingChanged) { - // We don't bother triggering an update - return; - } - - const newList = []; - all.forEach((value, key) => { - newList.push(value) - }) - this.features.setData(newList); - } - - -} \ No newline at end of file diff --git a/Logic/FeatureSource/FilteringFeatureSource.ts b/Logic/FeatureSource/FilteringFeatureSource.ts deleted file mode 100644 index 718f711b12..0000000000 --- a/Logic/FeatureSource/FilteringFeatureSource.ts +++ /dev/null @@ -1,162 +0,0 @@ -import FeatureSource from "./FeatureSource"; -import {UIEventSource} from "../UIEventSource"; -import Loc from "../../Models/Loc"; -import Hash from "../Web/Hash"; -import {TagsFilter} from "../Tags/TagsFilter"; -import LayerConfig from "../../Models/ThemeConfig/LayerConfig"; - -export default class FilteringFeatureSource implements FeatureSource { - public features: UIEventSource<{ feature: any; freshness: Date }[]> = - new UIEventSource<{ feature: any; freshness: Date }[]>([]); - public readonly name = "FilteringFeatureSource"; - - constructor( - layers: UIEventSource<{ - isDisplayed: UIEventSource; - layerDef: LayerConfig; - appliedFilters: UIEventSource; - }[]>, - location: UIEventSource, - selectedElement: UIEventSource, - upstream: FeatureSource - ) { - const self = this; - - function update() { - const layerDict = {}; - if (layers.data.length == 0) { - console.warn("No layers defined!"); - return; - } - for (const layer of layers.data) { - const prev = layerDict[layer.layerDef.id] - if (prev !== undefined) { - // We have seen this layer before! - // We prefer the one which has a name - if (layer.layerDef.name === undefined) { - // This one is hidden, so we skip it - console.log("Ignoring layer selection from ", layer) - continue; - } - } - layerDict[layer.layerDef.id] = layer; - } - - const features: { feature: any; freshness: Date }[] = - upstream.features.data; - - const missingLayers = new Set(); - - const newFeatures = features.filter((f) => { - const layerId = f.feature._matching_layer_id; - - if ( - selectedElement.data?.id === f.feature.id || - f.feature.id === Hash.hash.data) { - // This is the selected object - it gets a free pass even if zoom is not sufficient or it is filtered away - return true; - } - - if (layerId === undefined) { - return false; - } - const layer: { - isDisplayed: UIEventSource; - layerDef: LayerConfig; - appliedFilters: UIEventSource; - } = layerDict[layerId]; - if (layer === undefined) { - missingLayers.add(layerId); - return false; - } - - const isShown = layer.layerDef.isShown; - const tags = f.feature.properties; - if (isShown.IsKnown(tags)) { - const result = layer.layerDef.isShown.GetRenderValue( - f.feature.properties - ).txt; - if (result !== "yes") { - return false; - } - } - - const tagsFilter = layer.appliedFilters.data; - if (tagsFilter) { - if (!tagsFilter.matchesProperties(f.feature.properties)) { - // Hidden by the filter on the layer itself - we want to hide it no matter wat - return false; - } - } - if (!FilteringFeatureSource.showLayer(layer, location)) { - // The layer itself is either disabled or hidden due to zoom constraints - // We should return true, but it might still match some other layer - return false; - } - - return true; - }); - - self.features.setData(newFeatures); - if (missingLayers.size > 0) { - console.error( - "Some layers were not found: ", - Array.from(missingLayers) - ); - } - } - - upstream.features.addCallback(() => { - update(); - }); - location - .map((l) => { - // We want something that is stable for the shown layers - const displayedLayerIndexes = []; - for (let i = 0; i < layers.data.length; i++) { - const layer = layers.data[i]; - if (l.zoom < layer.layerDef.minzoom) { - continue; - } - - if (!layer.isDisplayed.data) { - continue; - } - displayedLayerIndexes.push(i); - } - return displayedLayerIndexes.join(","); - }) - .addCallback(() => { - update(); - }); - - layers.addCallback(update); - - const registered = new Set>(); - layers.addCallbackAndRun((layers) => { - for (const layer of layers) { - if (registered.has(layer.isDisplayed)) { - continue; - } - registered.add(layer.isDisplayed); - layer.isDisplayed.addCallback(() => update()); - layer.appliedFilters.addCallback(() => update()); - } - }); - - update(); - } - - private static showLayer( - layer: { - isDisplayed: UIEventSource; - layerDef: LayerConfig; - }, - location: UIEventSource - ) { - return ( - layer.isDisplayed.data && - layer.layerDef.minzoomVisible <= location.data.zoom - ); - } -} diff --git a/Logic/FeatureSource/GeoJsonSource.ts b/Logic/FeatureSource/GeoJsonSource.ts deleted file mode 100644 index 84a89327e2..0000000000 --- a/Logic/FeatureSource/GeoJsonSource.ts +++ /dev/null @@ -1,200 +0,0 @@ -import FeatureSource from "./FeatureSource"; -import {UIEventSource} from "../UIEventSource"; -import Loc from "../../Models/Loc"; -import State from "../../State"; -import {Utils} from "../../Utils"; -import LayerConfig from "../../Models/ThemeConfig/LayerConfig"; - - -/** - * Fetches a geojson file somewhere and passes it along - */ -export default class GeoJsonSource implements FeatureSource { - - public readonly features: UIEventSource<{ feature: any; freshness: Date }[]>; - public readonly name; - public readonly isOsmCache: boolean - private onFail: ((errorMsg: any, url: string) => void) = undefined; - private readonly layerId: string; - private readonly seenids: Set = new Set() - - private constructor(locationControl: UIEventSource, - flayer: { isDisplayed: UIEventSource, layerDef: LayerConfig }, - onFail?: ((errorMsg: any) => void)) { - this.layerId = flayer.layerDef.id; - let url = flayer.layerDef.source.geojsonSource.replace("{layer}", flayer.layerDef.id); - this.name = "GeoJsonSource of " + url; - const zoomLevel = flayer.layerDef.source.geojsonZoomLevel; - - this.isOsmCache = flayer.layerDef.source.isOsmCacheLayer; - - this.features = new UIEventSource<{ feature: any; freshness: Date }[]>([]) - - if (zoomLevel === undefined) { - // This is a classic, static geojson layer - if (onFail === undefined) { - onFail = _ => { - } - } - this.onFail = onFail; - - this.LoadJSONFrom(url) - } else { - this.ConfigureDynamicLayer(url, zoomLevel, locationControl, flayer) - } - } - - /** - * Merges together the layers which have the same source - * @param flayers - * @param locationControl - * @constructor - */ - public static ConstructMultiSource(flayers: { isDisplayed: UIEventSource, layerDef: LayerConfig }[], locationControl: UIEventSource): GeoJsonSource[] { - - const flayersPerSource = new Map, layerDef: LayerConfig }[]>(); - for (const flayer of flayers) { - const url = flayer.layerDef.source.geojsonSource?.replace(/{layer}/g, flayer.layerDef.id) - if (url === undefined) { - continue; - } - - if (!flayersPerSource.has(url)) { - flayersPerSource.set(url, []) - } - flayersPerSource.get(url).push(flayer) - } - - const sources: GeoJsonSource[] = [] - - flayersPerSource.forEach((flayers, key) => { - if (flayers.length == 1) { - sources.push(new GeoJsonSource(locationControl, flayers[0])); - return; - } - - const zoomlevels = Utils.Dedup(flayers.map(flayer => "" + (flayer.layerDef.source.geojsonZoomLevel ?? ""))) - if (zoomlevels.length > 1) { - throw "Multiple zoomlevels defined for same geojson source " + key - } - - let isShown = new UIEventSource(true, "IsShown for multiple layers: or of multiple values"); - for (const flayer of flayers) { - flayer.isDisplayed.addCallbackAndRun(() => { - let value = false; - for (const flayer of flayers) { - value = flayer.isDisplayed.data || value; - } - isShown.setData(value); - }); - - } - - const source = new GeoJsonSource(locationControl, { - isDisplayed: isShown, - layerDef: flayers[0].layerDef // We only care about the source info here - }) - sources.push(source) - - }) - return sources; - - } - - private ConfigureDynamicLayer(url: string, zoomLevel: number, locationControl: UIEventSource, flayer: { isDisplayed: UIEventSource, layerDef: LayerConfig }) { - // This is a dynamic template with a fixed zoom level - url = url.replace("{z}", "" + zoomLevel) - const loadedTiles = new Set(); - const self = this; - this.onFail = (msg, url) => { - console.warn(`Could not load geojson layer from`, url, "due to", msg) - loadedTiles.add(url); // We add the url to the 'loadedTiles' in order to not reload it in the future - } - - const neededTiles = locationControl.map( - location => { - if (!flayer.isDisplayed.data) { - // No need to download! - the layer is disabled - return undefined; - } - - if (location.zoom < flayer.layerDef.minzoom) { - // No need to download! - the layer is disabled - return undefined; - } - - // Yup, this is cheating to just get the bounds here - const bounds = State.state.leafletMap.data?.getBounds() - if(bounds === undefined){ - // We'll retry later - return undefined - } - const tileRange = Utils.TileRangeBetween(zoomLevel, bounds.getNorth(), bounds.getEast(), bounds.getSouth(), bounds.getWest()) - const needed = Utils.MapRange(tileRange, (x, y) => { - return url.replace("{x}", "" + x).replace("{y}", "" + y); - }) - return new Set(needed); - } - , [flayer.isDisplayed, State.state.leafletMap]); - neededTiles.stabilized(250).addCallback((needed: Set) => { - if (needed === undefined) { - return; - } - - needed.forEach(neededTile => { - if (loadedTiles.has(neededTile)) { - return; - } - - loadedTiles.add(neededTile) - self.LoadJSONFrom(neededTile) - - }) - }) - - } - - private LoadJSONFrom(url: string) { - const eventSource = this.features; - const self = this; - Utils.downloadJson(url) - .then(json => { - if (json.elements === [] && json.remarks.indexOf("runtime error") > 0) { - self.onFail("Runtime error (timeout)", url) - return; - } - const time = new Date(); - const newFeatures: { feature: any, freshness: Date } [] = [] - let i = 0; - let skipped = 0; - for (const feature of json.features) { - if (feature.properties.id === undefined) { - feature.properties.id = url + "/" + i; - feature.id = url + "/" + i; - i++; - } - if (self.seenids.has(feature.properties.id)) { - skipped++; - continue; - } - self.seenids.add(feature.properties.id) - - let freshness: Date = time; - if (feature.properties["_last_edit:timestamp"] !== undefined) { - freshness = new Date(feature.properties["_last_edit:timestamp"]) - } - - newFeatures.push({feature: feature, freshness: freshness}) - } - console.debug("Downloaded " + newFeatures.length + " new features and " + skipped + " already seen features from " + url); - - if (newFeatures.length == 0) { - return; - } - - eventSource.setData(eventSource.data.concat(newFeatures)) - - }).catch(msg => self.onFail(msg, url)) - } - -} diff --git a/Logic/FeatureSource/LocalStorageSaver.ts b/Logic/FeatureSource/LocalStorageSaver.ts deleted file mode 100644 index 354adb75ab..0000000000 --- a/Logic/FeatureSource/LocalStorageSaver.ts +++ /dev/null @@ -1,41 +0,0 @@ -/*** - * Saves all the features that are passed in to localstorage, so they can be retrieved on the next run - * - * Technically, more an Actor then a featuresource, but it fits more neatly this ay - */ -import FeatureSource from "./FeatureSource"; -import {UIEventSource} from "../UIEventSource"; -import LayoutConfig from "../../Models/ThemeConfig/LayoutConfig"; - -export default class LocalStorageSaver implements FeatureSource { - public static readonly storageKey: string = "cached-features"; - public readonly features: UIEventSource<{ feature: any; freshness: Date }[]>; - - public readonly name = "LocalStorageSaver"; - - constructor(source: FeatureSource, layout: UIEventSource) { - this.features = source.features; - - this.features.addCallbackAndRunD(features => { - const now = new Date().getTime() - features = features.filter(f => layout.data.cacheTimeout > Math.abs(now - f.freshness.getTime()) / 1000) - - - if (features.length == 0) { - return; - } - - try { - const key = LocalStorageSaver.storageKey + layout.data.id - localStorage.setItem(key, JSON.stringify(features)); - console.log("Saved ", features.length, "elements to", key) - } catch (e) { - console.warn("Could not save the features to local storage:", e) - } - }) - - - } - - -} \ No newline at end of file diff --git a/Logic/FeatureSource/LocalStorageSource.ts b/Logic/FeatureSource/LocalStorageSource.ts deleted file mode 100644 index e072b948ea..0000000000 --- a/Logic/FeatureSource/LocalStorageSource.ts +++ /dev/null @@ -1,35 +0,0 @@ -import FeatureSource from "./FeatureSource"; -import {UIEventSource} from "../UIEventSource"; -import LocalStorageSaver from "./LocalStorageSaver"; -import LayoutConfig from "../../Models/ThemeConfig/LayoutConfig"; - -export default class LocalStorageSource implements FeatureSource { - public features: UIEventSource<{ feature: any; freshness: Date }[]>; - public readonly name = "LocalStorageSource"; - - constructor(layout: UIEventSource) { - this.features = new UIEventSource<{ feature: any; freshness: Date }[]>([]) - const key = LocalStorageSaver.storageKey + layout.data.id - layout.addCallbackAndRun(_ => { - try { - const fromStorage = localStorage.getItem(key); - if (fromStorage == null) { - return; - } - const loaded: { feature: any; freshness: Date | string }[] = - JSON.parse(fromStorage); - - const parsed: { feature: any; freshness: Date }[] = loaded.map(ff => ({ - feature: ff.feature, - freshness: typeof ff.freshness == "string" ? new Date(ff.freshness) : ff.freshness - })) - - this.features.setData(parsed); - console.log("Loaded ", loaded.length, " features from localstorage as cache") - } catch (e) { - console.log("Could not load features from localStorage:", e) - localStorage.removeItem(key) - } - }) - } -} \ No newline at end of file diff --git a/Logic/FeatureSource/MetaTaggingFeatureSource.ts b/Logic/FeatureSource/MetaTaggingFeatureSource.ts deleted file mode 100644 index 3ae5c90152..0000000000 --- a/Logic/FeatureSource/MetaTaggingFeatureSource.ts +++ /dev/null @@ -1,52 +0,0 @@ -import FeatureSource from "./FeatureSource"; -import {UIEventSource} from "../UIEventSource"; -import State from "../../State"; -import Hash from "../Web/Hash"; -import MetaTagging from "../MetaTagging"; - -export default class MetaTaggingFeatureSource implements FeatureSource { - public readonly features: UIEventSource<{ feature: any; freshness: Date }[]> = new UIEventSource<{ feature: any; freshness: Date }[]>(undefined); - - public readonly name; - - /*** - * Constructs a new metatagger which'll calculate various tags - * @param allFeaturesSource: A source where all the currently known features can be found - used to calculate overlaps etc - * @param source: the source of features that should get their metatag and which should be exported again - * @param updateTrigger - */ - constructor(allFeaturesSource: UIEventSource<{ feature: any; freshness: Date }[]>, source: FeatureSource, updateTrigger?: UIEventSource) { - const self = this; - this.name = "MetaTagging of " + source.name - - if (allFeaturesSource === undefined) { - throw ("UIEVentSource is undefined") - } - - function update() { - const featuresFreshness = source.features.data - if (featuresFreshness === undefined) { - return; - } - featuresFreshness.forEach(featureFresh => { - const feature = featureFresh.feature; - - if (Hash.hash.data === feature.properties.id) { - State.state.selectedElement.setData(feature); - } - }) - - MetaTagging.addMetatags(featuresFreshness, - allFeaturesSource, - State.state.knownRelations.data, State.state.layoutToUse.data.layers); - self.features.setData(featuresFreshness); - } - - source.features.addCallbackAndRun(_ => update()); - updateTrigger?.addCallback(_ => { - console.debug("Updating because of external call") - update(); - }) - } - -} \ No newline at end of file diff --git a/Logic/FeatureSource/OsmApiFeatureSource.ts b/Logic/FeatureSource/OsmApiFeatureSource.ts deleted file mode 100644 index d48cc5f4bd..0000000000 --- a/Logic/FeatureSource/OsmApiFeatureSource.ts +++ /dev/null @@ -1,111 +0,0 @@ -import FeatureSource from "./FeatureSource"; -import {UIEventSource} from "../UIEventSource"; -import {OsmObject} from "../Osm/OsmObject"; -import {Utils} from "../../Utils"; -import Loc from "../../Models/Loc"; -import FilteredLayer from "../../Models/FilteredLayer"; -import Constants from "../../Models/Constants"; - - -export default class OsmApiFeatureSource implements FeatureSource { - public readonly features: UIEventSource<{ feature: any; freshness: Date }[]> = new UIEventSource<{ feature: any; freshness: Date }[]>([]); - public readonly name: string = "OsmApiFeatureSource"; - private readonly loadedTiles: Set = new Set(); - private readonly _state: { - leafletMap: UIEventSource; - locationControl: UIEventSource, filteredLayers: UIEventSource}; - - constructor(minZoom = undefined, state: {locationControl: UIEventSource, filteredLayers: UIEventSource, leafletMap: UIEventSource}) { - this._state = state; - if(minZoom !== undefined){ - if(minZoom < 14){ - throw "MinZoom should be at least 14 or higher, OSM-api won't work otherwise" - } - const self = this; - state.locationControl.addCallbackAndRunD(location => { - if(location.zoom > minZoom){ - return; - } - self.loadArea() - }) - } - } - - - public load(id: string) { - if (id.indexOf("-") >= 0) { - // Newly added point - not yet in OSM - return; - } - console.debug("Downloading", id, "from the OSM-API") - OsmObject.DownloadObject(id).addCallbackAndRunD(element => { - try { - const geojson = element.asGeoJson(); - geojson.id = geojson.properties.id; - this.features.setData([{feature: geojson, freshness: element.timestamp}]) - } catch (e) { - console.error(e) - } - }) - } - - /** - * Loads the current inview-area - */ - public loadArea(z: number = 14): boolean { - const layers = this._state.filteredLayers.data; - - const disabledLayers = layers.filter(layer => layer.layerDef.source.overpassScript !== undefined || layer.layerDef.source.geojsonSource !== undefined) - if (disabledLayers.length > 0) { - return false; - } - const loc = this._state.locationControl.data; - if (loc.zoom < Constants.useOsmApiAt) { - return false; - } - if (this._state.leafletMap.data === undefined) { - return false; // Not yet inited - } - const bounds = this._state.leafletMap.data.getBounds() - const tileRange = Utils.TileRangeBetween(z, bounds.getNorth(), bounds.getEast(), bounds.getSouth(), bounds.getWest()) - const self = this; - Utils.MapRange(tileRange, (x, y) => { - const key = x + "/" + y; - if (self.loadedTiles.has(key)) { - return; - } - - self.loadedTiles.add(key); - - const bounds = Utils.tile_bounds(z, x, y); - console.log("Loading OSM data tile", z, x, y, " with bounds", bounds) - OsmObject.LoadArea(bounds, objects => { - const keptGeoJson: { feature: any, freshness: Date }[] = [] - // Which layer does the object match? - for (const object of objects) { - - for (const flayer of layers) { - const layer = flayer.layerDef; - const tags = object.tags - const doesMatch = layer.source.osmTags.matchesProperties(tags); - if (doesMatch) { - const geoJson = object.asGeoJson(); - geoJson._matching_layer_id = layer.id - keptGeoJson.push({feature: geoJson, freshness: object.timestamp}) - break; - } - - } - - } - - self.features.setData(keptGeoJson) - }); - - }); - - return true; - - } - -} \ No newline at end of file diff --git a/Logic/FeatureSource/PerLayerFeatureSourceSplitter.ts b/Logic/FeatureSource/PerLayerFeatureSourceSplitter.ts new file mode 100644 index 0000000000..f9a0cbe1a1 --- /dev/null +++ b/Logic/FeatureSource/PerLayerFeatureSourceSplitter.ts @@ -0,0 +1,93 @@ +import FeatureSource, {FeatureSourceForLayer, Tiled} from "./FeatureSource"; +import {UIEventSource} from "../UIEventSource"; +import FilteredLayer from "../../Models/FilteredLayer"; +import SimpleFeatureSource from "./Sources/SimpleFeatureSource"; + + +/** + * In some rare cases, some elements are shown on multiple layers (when 'passthrough' is enabled) + * If this is the case, multiple objects with a different _matching_layer_id are generated. + * In any case, this featureSource marks the objects with _matching_layer_id + */ +export default class PerLayerFeatureSourceSplitter { + + constructor(layers: UIEventSource, + handleLayerData: (source: FeatureSourceForLayer & Tiled) => void, + upstream: FeatureSource, + options?:{ + tileIndex?: number, + handleLeftovers?: (featuresWithoutLayer: any[]) => void + }) { + + const knownLayers = new Map() + + function update() { + const features = upstream.features.data; + if (features === undefined) { + return; + } + if (layers.data === undefined) { + return; + } + + // We try to figure out (for each feature) in which feature store it should be saved. + // Note that this splitter is only run when it is invoked by the overpass feature source, so we can't be sure in which layer it should go + + const featuresPerLayer = new Map(); + const noLayerFound = [] + function addTo(layer: FilteredLayer, feature: { feature, freshness }) { + const id = layer.layerDef.id + const list = featuresPerLayer.get(id) + if (list !== undefined) { + list.push(feature) + } else { + featuresPerLayer.set(id, [feature]) + } + } + + for (const f of features) { + for (const layer of layers.data) { + if (layer.layerDef.source.osmTags.matchesProperties(f.feature.properties)) { + // We have found our matching layer! + addTo(layer, f) + if (!layer.layerDef.passAllFeatures) { + // If not 'passAllFeatures', we are done for this feature + break; + } + } + noLayerFound.push(f) + } + } + + // At this point, we have our features per layer as a list + // We assign them to the correct featureSources + for (const layer of layers.data) { + const id = layer.layerDef.id; + const features = featuresPerLayer.get(id) + if (features === undefined) { + // No such features for this layer + continue; + } + + let featureSource = knownLayers.get(id) + if (featureSource === undefined) { + // Not yet initialized - now is a good time + featureSource = new SimpleFeatureSource(layer, options?.tileIndex) + featureSource.features.setData(features) + knownLayers.set(id, featureSource) + handleLayerData(featureSource) + } else { + featureSource.features.setData(features) + } + } + + // AT last, the leftovers are handled + if(options?.handleLeftovers !== undefined && noLayerFound.length > 0){ + options.handleLeftovers(noLayerFound) + } + } + + layers.addCallback(_ => update()) + upstream.features.addCallbackAndRunD(_ => update()) + } +} \ No newline at end of file diff --git a/Logic/FeatureSource/Sources/ChangeGeometryApplicator.ts b/Logic/FeatureSource/Sources/ChangeGeometryApplicator.ts new file mode 100644 index 0000000000..a35879d960 --- /dev/null +++ b/Logic/FeatureSource/Sources/ChangeGeometryApplicator.ts @@ -0,0 +1,84 @@ +/** + * Applies geometry changes from 'Changes' onto every feature of a featureSource + */ +import {Changes} from "../../Osm/Changes"; +import {UIEventSource} from "../../UIEventSource"; +import {FeatureSourceForLayer, IndexedFeatureSource} from "../FeatureSource"; +import FilteredLayer from "../../../Models/FilteredLayer"; +import {ChangeDescription, ChangeDescriptionTools} from "../../Osm/Actions/ChangeDescription"; + + +export default class ChangeGeometryApplicator implements FeatureSourceForLayer { + public readonly features: UIEventSource<{ feature: any; freshness: Date }[]> = new UIEventSource<{ feature: any; freshness: Date }[]>([]); + public readonly name: string; + private readonly source: IndexedFeatureSource; + private readonly changes: Changes; + public readonly layer: FilteredLayer + + constructor(source: (IndexedFeatureSource & FeatureSourceForLayer), changes: Changes) { + this.source = source; + this.changes = changes; + this.layer = source.layer + + this.name = "ChangesApplied(" + source.name + ")" + this.features = new UIEventSource<{ feature: any; freshness: Date }[]>(undefined) + + const self = this; + source.features.addCallbackAndRunD(_ => self.update()) + + changes.allChanges.addCallbackAndRunD(_ => self.update()) + + } + + private update() { + const upstreamFeatures = this.source.features.data + const upstreamIds = this.source.containedIds.data + const changesToApply = this.changes.allChanges.data + ?.filter(ch => + // Does upsteram have this element? If not, we skip + upstreamIds.has(ch.type + "/" + ch.id) && + // Are any (geometry) changes defined? + ch.changes !== undefined && + // Ignore new elements, they are handled by the NewGeometryFromChangesFeatureSource + ch.id > 0) + + if (changesToApply === undefined || changesToApply.length === 0) { + // No changes to apply! + // Pass the original feature and lets continue our day + this.features.setData(upstreamFeatures); + return; + } + + const changesPerId = new Map() + for (const ch of changesToApply) { + const key = ch.type + "/" + ch.id + if(changesPerId.has(key)){ + changesPerId.get(key).push(ch) + }else{ + changesPerId.set(key, [ch]) + } + } + const newFeatures: { feature: any, freshness: Date }[] = [] + for (const feature of upstreamFeatures) { + const changesForFeature = changesPerId.get(feature.feature.properties.id) + if (changesForFeature === undefined) { + // No changes for this element + newFeatures.push(feature) + continue; + } + + // Allright! We have a feature to rewrite! + const copy = { + ...feature + } + // We only apply the last change as that one'll have the latest geometry + const change = changesForFeature[changesForFeature.length - 1] + copy.feature.geometry = ChangeDescriptionTools.getGeojsonGeometry(change) + console.log("Applying a geometry change onto ", feature, change, copy) + newFeatures.push(copy) + } + this.features.setData(newFeatures) + + } + +} \ No newline at end of file diff --git a/Logic/FeatureSource/Sources/FeatureSourceMerger.ts b/Logic/FeatureSource/Sources/FeatureSourceMerger.ts new file mode 100644 index 0000000000..b1797d0aec --- /dev/null +++ b/Logic/FeatureSource/Sources/FeatureSourceMerger.ts @@ -0,0 +1,100 @@ +/** + * Merges features from different featureSources for a single layer + * Uses the freshest feature available in the case multiple sources offer data with the same identifier + */ +import {UIEventSource} from "../../UIEventSource"; +import FeatureSource, {FeatureSourceForLayer, IndexedFeatureSource, Tiled} from "../FeatureSource"; +import FilteredLayer from "../../../Models/FilteredLayer"; +import {Utils} from "../../../Utils"; +import {Tiles} from "../../../Models/TileRange"; +import {BBox} from "../../BBox"; + +export default class FeatureSourceMerger implements FeatureSourceForLayer, Tiled, IndexedFeatureSource { + + public features: UIEventSource<{ feature: any; freshness: Date }[]> = new UIEventSource<{ feature: any; freshness: Date }[]>([]); + public readonly name; + public readonly layer: FilteredLayer + private readonly _sources: UIEventSource; + public readonly tileIndex: number; + public readonly bbox: BBox; + public readonly containedIds: UIEventSource> = new UIEventSource>(new Set()) + + constructor(layer: FilteredLayer, tileIndex: number, bbox: BBox, sources: UIEventSource) { + this.tileIndex = tileIndex; + this.bbox = bbox; + this._sources = sources; + this.layer = layer; + this.name = "FeatureSourceMerger("+layer.layerDef.id+", "+Tiles.tile_from_index(tileIndex).join(",")+")" + const self = this; + + const handledSources = new Set(); + + sources.addCallbackAndRunD(sources => { + let newSourceRegistered = false; + for (let i = 0; i < sources.length; i++) { + let source = sources[i]; + if (handledSources.has(source)) { + continue + } + handledSources.add(source) + newSourceRegistered = true + source.features.addCallback(() => { + self.Update(); + }); + if (newSourceRegistered) { + self.Update(); + } + } + }) + + } + + private Update() { + + let somethingChanged = false; + const all: Map = new Map(); + // We seed the dictionary with the previously loaded features + const oldValues = this.features.data ?? []; + for (const oldValue of oldValues) { + all.set(oldValue.feature.id, oldValue) + } + + for (const source of this._sources.data) { + if (source?.features?.data === undefined) { + continue; + } + for (const f of source.features.data) { + const id = f.feature.properties.id; + if (!all.has(id)) { + // This is a new feature + somethingChanged = true; + all.set(id, f); + continue; + } + + // This value has been seen already, either in a previous run or by a previous datasource + // Let's figure out if something changed + const oldV = all.get(id); + if (oldV.freshness < f.freshness) { + // Jup, this feature is fresher + all.set(id, f); + somethingChanged = true; + } + } + } + + if (!somethingChanged) { + // We don't bother triggering an update + return; + } + + const newList = []; + all.forEach((value, _) => { + newList.push(value) + }) + this.containedIds.setData(new Set(all.keys())) + this.features.setData(newList); + } + + +} \ No newline at end of file diff --git a/Logic/FeatureSource/Sources/FilteringFeatureSource.ts b/Logic/FeatureSource/Sources/FilteringFeatureSource.ts new file mode 100644 index 0000000000..0c2c9d92ae --- /dev/null +++ b/Logic/FeatureSource/Sources/FilteringFeatureSource.ts @@ -0,0 +1,90 @@ +import {UIEventSource} from "../../UIEventSource"; +import LayerConfig from "../../../Models/ThemeConfig/LayerConfig"; +import FilteredLayer from "../../../Models/FilteredLayer"; +import {FeatureSourceForLayer, Tiled} from "../FeatureSource"; +import Hash from "../../Web/Hash"; +import {BBox} from "../../BBox"; + +export default class FilteringFeatureSource implements FeatureSourceForLayer, Tiled { + public features: UIEventSource<{ feature: any; freshness: Date }[]> = + new UIEventSource<{ feature: any; freshness: Date }[]>([]); + public readonly name; + public readonly layer: FilteredLayer; + public readonly tileIndex: number + public readonly bbox: BBox + + constructor( + state: { + locationControl: UIEventSource<{ zoom: number }>, + selectedElement: UIEventSource, + }, + tileIndex, + upstream: FeatureSourceForLayer + ) { + const self = this; + this.name = "FilteringFeatureSource(" + upstream.name + ")" + this.tileIndex = tileIndex + this.bbox = BBox.fromTileIndex(tileIndex) + + this.layer = upstream.layer; + const layer = upstream.layer; + + function update() { + + const features: { feature: any; freshness: Date }[] = upstream.features.data; + const newFeatures = features.filter((f) => { + if ( + state.selectedElement.data?.id === f.feature.id || + f.feature.id === Hash.hash.data) { + // This is the selected object - it gets a free pass even if zoom is not sufficient or it is filtered away + return true; + } + + const isShown = layer.layerDef.isShown; + const tags = f.feature.properties; + if (isShown.IsKnown(tags)) { + const result = layer.layerDef.isShown.GetRenderValue( + f.feature.properties + ).txt; + if (result !== "yes") { + return false; + } + } + + const tagsFilter = layer.appliedFilters.data; + for (const filter of tagsFilter ?? []) { + const neededTags = filter.filter.options[filter.selected].osmTags + if (!neededTags.matchesProperties(f.feature.properties)) { + // Hidden by the filter on the layer itself - we want to hide it no matter wat + return false; + } + } + + + return true; + }); + + self.features.setData(newFeatures); + } + + upstream.features.addCallback(() => { + update(); + }); + + + layer.appliedFilters.addCallback(_ => { + update() + }) + + update(); + } + + private static showLayer( + layer: { + isDisplayed: UIEventSource; + layerDef: LayerConfig; + }) { + return layer.isDisplayed.data; + + } +} diff --git a/Logic/FeatureSource/Sources/GeoJsonSource.ts b/Logic/FeatureSource/Sources/GeoJsonSource.ts new file mode 100644 index 0000000000..b341a0dd3b --- /dev/null +++ b/Logic/FeatureSource/Sources/GeoJsonSource.ts @@ -0,0 +1,120 @@ +/** + * Fetches a geojson file somewhere and passes it along + */ +import {UIEventSource} from "../../UIEventSource"; +import FilteredLayer from "../../../Models/FilteredLayer"; +import {Utils} from "../../../Utils"; +import {FeatureSourceForLayer, Tiled} from "../FeatureSource"; +import {Tiles} from "../../../Models/TileRange"; +import {BBox} from "../../BBox"; + + +export default class GeoJsonSource implements FeatureSourceForLayer, Tiled { + + public readonly features: UIEventSource<{ feature: any; freshness: Date }[]>; + public readonly name; + public readonly isOsmCache: boolean + private onFail: ((errorMsg: any, url: string) => void) = undefined; + private readonly seenids: Set = new Set() + public readonly layer: FilteredLayer; + + public readonly tileIndex + public readonly bbox; + + /** + * Only used if the actual source is a tiled geojson. + * A big feature might be contained in multiple tiles. + * However, we only want to load them once. The blacklist thus contains all ids of all features previously seen + * @private + */ + private readonly featureIdBlacklist?: UIEventSource> + + public constructor(flayer: FilteredLayer, + zxy?: [number, number, number], + options?: { + featureIdBlacklist?: UIEventSource> + }) { + + if (flayer.layerDef.source.geojsonZoomLevel !== undefined && zxy === undefined) { + throw "Dynamic layers are not supported. Use 'DynamicGeoJsonTileSource instead" + } + + this.layer = flayer; + this.featureIdBlacklist = options?.featureIdBlacklist + let url = flayer.layerDef.source.geojsonSource.replace("{layer}", flayer.layerDef.id); + if (zxy !== undefined) { + const [z, x, y] = zxy; + url = url + .replace('{z}', "" + z) + .replace('{x}', "" + x) + .replace('{y}', "" + y) + this.tileIndex = Tiles.tile_index(z, x, y) + this.bbox = BBox.fromTile(z, x, y) + } else { + this.tileIndex = Tiles.tile_index(0, 0, 0) + this.bbox = BBox.global; + } + + this.name = "GeoJsonSource of " + url; + + this.isOsmCache = flayer.layerDef.source.isOsmCacheLayer; + this.features = new UIEventSource<{ feature: any; freshness: Date }[]>([]) + this.LoadJSONFrom(url) + } + + + private LoadJSONFrom(url: string) { + const eventSource = this.features; + const self = this; + Utils.downloadJson(url) + .then(json => { + if (json.elements === [] && json.remarks.indexOf("runtime error") > 0) { + self.onFail("Runtime error (timeout)", url) + return; + } + const time = new Date(); + const newFeatures: { feature: any, freshness: Date } [] = [] + let i = 0; + let skipped = 0; + for (const feature of json.features) { + const props = feature.properties + for (const key in props) { + if (typeof props[key] !== "string") { + // Make sure all the values are string, it crashes stuff otherwise + props[key] = "" + props[key] + } + } + + if (props.id === undefined) { + props.id = url + "/" + i; + feature.id = url + "/" + i; + i++; + } + if (self.seenids.has(props.id)) { + skipped++; + continue; + } + self.seenids.add(props.id) + + if(self.featureIdBlacklist?.data?.has(props.id)){ + continue; + } + + let freshness: Date = time; + if (feature.properties["_last_edit:timestamp"] !== undefined) { + freshness = new Date(props["_last_edit:timestamp"]) + } + + newFeatures.push({feature: feature, freshness: freshness}) + } + + if (newFeatures.length == 0) { + return; + } + + eventSource.setData(eventSource.data.concat(newFeatures)) + + }).catch(msg => console.error("Could not load geojon layer", url, "due to", msg)) + } + +} diff --git a/Logic/FeatureSource/Sources/NewGeometryFromChangesFeatureSource.ts b/Logic/FeatureSource/Sources/NewGeometryFromChangesFeatureSource.ts new file mode 100644 index 0000000000..0a1c67f411 --- /dev/null +++ b/Logic/FeatureSource/Sources/NewGeometryFromChangesFeatureSource.ts @@ -0,0 +1,94 @@ +import {Changes} from "../../Osm/Changes"; +import {OsmNode, OsmRelation, OsmWay} from "../../Osm/OsmObject"; +import FeatureSource from "../FeatureSource"; +import {UIEventSource} from "../../UIEventSource"; +import {ChangeDescription} from "../../Osm/Actions/ChangeDescription"; +import State from "../../../State"; + +export class NewGeometryFromChangesFeatureSource implements FeatureSource { + // This class name truly puts the 'Java' into 'Javascript' + + /** + * A feature source containing exclusively new elements + */ + public readonly features: UIEventSource<{ feature: any; freshness: Date }[]> = new UIEventSource<{ feature: any; freshness: Date }[]>([]); + public readonly name: string = "newFeatures"; + + constructor(changes: Changes) { + + const seenChanges = new Set(); + const features = this.features.data; + const self = this; + + changes.pendingChanges + .map(changes => changes.filter(ch => + // only new objects allowed + ch.id < 0 && + // The change is an update to the object (e.g. tags or geometry) - not the actual create + ch.changes !== undefined && + // If tags is undefined, this is probably a new point that is part of a split road + ch.tags !== undefined && + // Already handled + !seenChanges.has(ch))) + .addCallbackAndRunD(changes => { + + if (changes.length === 0) { + return; + } + + const now = new Date(); + + function add(feature) { + feature.id = feature.properties.id + features.push({ + feature: feature, + freshness: now + }) + console.warn("Added a new feature: ", JSON.stringify(feature)) + } + + for (const change of changes) { + seenChanges.add(change) + try { + const tags = {} + for (const kv of change.tags) { + tags[kv.k] = kv.v + } + tags["id"] = change.type+"/"+change.id + + tags["_backend"] = State.state.osmConnection._oauth_config.url + + switch (change.type) { + case "node": + const n = new OsmNode(change.id) + n.tags = tags + n.lat = change.changes["lat"] + n.lon = change.changes["lon"] + const geojson = n.asGeoJson() + add(geojson) + break; + case "way": + const w = new OsmWay(change.id) + w.tags = tags + w.nodes = change.changes["nodes"] + w.coordinates = change.changes["coordinates"].map(coor => coor.reverse()) + add(w.asGeoJson()) + break; + case "relation": + const r = new OsmRelation(change.id) + r.tags = tags + r.members = change.changes["members"] + add(r.asGeoJson()) + break; + } + } catch (e) { + console.error("Could not generate a new geometry to render on screen for:", e) + } + + } + + self.features.ping() + }) + } + +} \ No newline at end of file diff --git a/Logic/FeatureSource/RememberingSource.ts b/Logic/FeatureSource/Sources/RememberingSource.ts similarity index 60% rename from Logic/FeatureSource/RememberingSource.ts rename to Logic/FeatureSource/Sources/RememberingSource.ts index 458b278d0f..6835767363 100644 --- a/Logic/FeatureSource/RememberingSource.ts +++ b/Logic/FeatureSource/Sources/RememberingSource.ts @@ -1,17 +1,24 @@ /** - * Every previously added point is remembered, but new points are added + * Every previously added point is remembered, but new points are added. + * Data coming from upstream will always overwrite a previous value */ -import FeatureSource from "./FeatureSource"; -import {UIEventSource} from "../UIEventSource"; +import FeatureSource, {Tiled} from "../FeatureSource"; +import {UIEventSource} from "../../UIEventSource"; +import {BBox} from "../../BBox"; + +export default class RememberingSource implements FeatureSource , Tiled{ -export default class RememberingSource implements FeatureSource { public readonly features: UIEventSource<{ feature: any, freshness: Date }[]>; - public readonly name; - - constructor(source: FeatureSource) { + public readonly tileIndex : number + public readonly bbox : BBox + + constructor(source: FeatureSource & Tiled) { const self = this; this.name = "RememberingSource of " + source.name; + this.tileIndex= source.tileIndex + this.bbox = source.bbox; + const empty = []; this.features = source.features.map(features => { const oldFeatures = self.features?.data ?? empty; @@ -20,9 +27,9 @@ export default class RememberingSource implements FeatureSource { } // Then new ids - const ids = new Set(features.map(f => f.feature.properties.id + f.feature.geometry.type + f.feature._matching_layer_id)); + const ids = new Set(features.map(f => f.feature.properties.id + f.feature.geometry.type)); // the old data - const oldData = oldFeatures.filter(old => !ids.has(old.feature.properties.id + old.feature.geometry.type + old.feature._matching_layer_id)) + const oldData = oldFeatures.filter(old => !ids.has(old.feature.properties.id + old.feature.geometry.type)) return [...features, ...oldData]; }) } diff --git a/Logic/FeatureSource/Sources/SimpleFeatureSource.ts b/Logic/FeatureSource/Sources/SimpleFeatureSource.ts new file mode 100644 index 0000000000..b06aae6d27 --- /dev/null +++ b/Logic/FeatureSource/Sources/SimpleFeatureSource.ts @@ -0,0 +1,22 @@ +import {UIEventSource} from "../../UIEventSource"; +import FilteredLayer from "../../../Models/FilteredLayer"; +import {FeatureSourceForLayer, Tiled} from "../FeatureSource"; +import {Utils} from "../../../Utils"; +import {Tiles} from "../../../Models/TileRange"; +import {BBox} from "../../BBox"; + +export default class SimpleFeatureSource implements FeatureSourceForLayer, Tiled { + public readonly features: UIEventSource<{ feature: any; freshness: Date }[]> = new UIEventSource<{ feature: any; freshness: Date }[]>([]); + public readonly name: string = "SimpleFeatureSource"; + public readonly layer: FilteredLayer; + public readonly bbox: BBox = BBox.global; + public readonly tileIndex: number; + + constructor(layer: FilteredLayer, tileIndex: number) { + this.name = "SimpleFeatureSource(" + layer.layerDef.id + ")" + this.layer = layer + this.tileIndex = tileIndex ?? 0; + this.bbox = BBox.fromTileIndex(this.tileIndex) + } + +} \ No newline at end of file diff --git a/Logic/FeatureSource/Sources/StaticFeatureSource.ts b/Logic/FeatureSource/Sources/StaticFeatureSource.ts new file mode 100644 index 0000000000..d5795a1d51 --- /dev/null +++ b/Logic/FeatureSource/Sources/StaticFeatureSource.ts @@ -0,0 +1,28 @@ +import FeatureSource from "../FeatureSource"; +import {UIEventSource} from "../../UIEventSource"; + +/** + * A simple dummy implementation for whenever it is needed + */ +export default class StaticFeatureSource implements FeatureSource { + public readonly features: UIEventSource<{ feature: any; freshness: Date }[]>; + public readonly name: string = "StaticFeatureSource" + + constructor(features: any[] | UIEventSource>, useFeaturesDirectly) { + const now = new Date(); + if (useFeaturesDirectly) { + // @ts-ignore + this.features = features + } else if (features instanceof UIEventSource) { + // @ts-ignore + this.features = features.map(features => features.map(f => ({feature: f, freshness: now}))) + } else { + this.features = new UIEventSource(features.map(f => ({ + feature: f, + freshness: now + }))) + } + } + + +} \ No newline at end of file diff --git a/Logic/FeatureSource/WayHandlingApplyingFeatureSource.ts b/Logic/FeatureSource/Sources/WayHandlingApplyingFeatureSource.ts similarity index 53% rename from Logic/FeatureSource/WayHandlingApplyingFeatureSource.ts rename to Logic/FeatureSource/Sources/WayHandlingApplyingFeatureSource.ts index b2e5fba149..cb36c4b21a 100644 --- a/Logic/FeatureSource/WayHandlingApplyingFeatureSource.ts +++ b/Logic/FeatureSource/Sources/WayHandlingApplyingFeatureSource.ts @@ -1,44 +1,37 @@ -import FeatureSource from "./FeatureSource"; -import {UIEventSource} from "../UIEventSource"; -import {GeoOperations} from "../GeoOperations"; -import LayerConfig from "../../Models/ThemeConfig/LayerConfig"; - /** * This is the part of the pipeline which introduces extra points at the center of an area (but only if this is demanded by the wayhandling) */ -export default class WayHandlingApplyingFeatureSource implements FeatureSource { +import {UIEventSource} from "../../UIEventSource"; +import LayerConfig from "../../../Models/ThemeConfig/LayerConfig"; +import {GeoOperations} from "../../GeoOperations"; +import {FeatureSourceForLayer} from "../FeatureSource"; + +export default class WayHandlingApplyingFeatureSource implements FeatureSourceForLayer { public readonly features: UIEventSource<{ feature: any; freshness: Date }[]>; public readonly name; + public readonly layer; + + constructor(upstream: FeatureSourceForLayer) { + + this.name = "Wayhandling(" + upstream.name + ")"; + this.layer = upstream.layer + const layer = upstream.layer.layerDef; + + if (layer.wayHandling === LayerConfig.WAYHANDLING_DEFAULT) { + // We don't have to do anything fancy + // lets just wire up the upstream + this.features = upstream.features; + return; + } - constructor(layers: UIEventSource<{ - layerDef: LayerConfig - }[]>, - upstream: FeatureSource) { - this.name = "Wayhandling of " + upstream.name; this.features = upstream.features.map( features => { if (features === undefined) { return; } - - const layerDict = {}; - let allDefaultWayHandling = true; - for (const layer of layers.data) { - layerDict[layer.layerDef.id] = layer; - if (layer.layerDef.wayHandling !== LayerConfig.WAYHANDLING_DEFAULT) { - allDefaultWayHandling = false; - } - } - const newFeatures: { feature: any, freshness: Date }[] = []; for (const f of features) { const feat = f.feature; - const layerId = feat._matching_layer_id; - const layer: LayerConfig = layerDict[layerId].layerDef; - if (layer === undefined) { - console.error("No layer found with id " + layerId); - continue; - } if (layer.wayHandling === LayerConfig.WAYHANDLING_DEFAULT) { newFeatures.push(f); @@ -47,19 +40,17 @@ export default class WayHandlingApplyingFeatureSource implements FeatureSource { if (feat.geometry.type === "Point") { newFeatures.push(f); - // it is a point, nothing to do here + // feature is a point, nothing to do here continue; } // Create the copy const centerPoint = GeoOperations.centerpoint(feat); - centerPoint["_matching_layer_id"] = feat._matching_layer_id; newFeatures.push({feature: centerPoint, freshness: f.freshness}); if (layer.wayHandling === LayerConfig.WAYHANDLING_CENTER_AND_WAY) { newFeatures.push(f); } - } return newFeatures; } diff --git a/Logic/FeatureSource/TileFreshnessCalculator.ts b/Logic/FeatureSource/TileFreshnessCalculator.ts new file mode 100644 index 0000000000..6b397f5408 --- /dev/null +++ b/Logic/FeatureSource/TileFreshnessCalculator.ts @@ -0,0 +1,72 @@ +import {Tiles} from "../../Models/TileRange"; + +export default class TileFreshnessCalculator { + + /** + * All the freshnesses per tile index + * @private + */ + private readonly freshnesses = new Map(); + + /** + * Marks that some data got loaded for this layer + * @param tileId + * @param freshness + */ + public addTileLoad(tileId: number, freshness: Date){ + const existingFreshness = this.freshnessFor(...Tiles.tile_from_index(tileId)) + if(existingFreshness >= freshness){ + return; + } + this.freshnesses.set(tileId, freshness) + + + // Do we have freshness for the neighbouring tiles? If so, we can mark the tile above as loaded too! + let [z, x, y] = Tiles.tile_from_index(tileId) + if(z === 0){ + return; + } + x = x - (x % 2) // Make the tiles always even + y = y - (y % 2) + + const ul = this.freshnessFor(z, x, y)?.getTime() + if(ul === undefined){ + return + } + const ur = this.freshnessFor(z, x + 1, y)?.getTime() + if(ur === undefined){ + return + } + const ll = this.freshnessFor(z, x, y + 1)?.getTime() + if(ll === undefined){ + return + } + const lr = this.freshnessFor(z, x + 1, y + 1)?.getTime() + if(lr === undefined){ + return + } + + const leastFresh = Math.min(ul, ur, ll, lr) + const date = new Date() + date.setTime(leastFresh) + this.addTileLoad( + Tiles.tile_index(z - 1, Math.floor(x / 2), Math.floor(y / 2)), + date + ) + + } + + public freshnessFor(z: number, x: number, y:number): Date { + if(z < 0){ + return undefined + } + const tileId = Tiles.tile_index(z, x, y) + if(this.freshnesses.has(tileId)) { + return this.freshnesses.get(tileId) + } + // recurse up + return this.freshnessFor(z - 1, Math.floor(x /2), Math.floor(y / 2)) + + } + +} \ No newline at end of file diff --git a/Logic/FeatureSource/TiledFeatureSource/DynamicGeoJsonTileSource.ts b/Logic/FeatureSource/TiledFeatureSource/DynamicGeoJsonTileSource.ts new file mode 100644 index 0000000000..fcfdbea5a4 --- /dev/null +++ b/Logic/FeatureSource/TiledFeatureSource/DynamicGeoJsonTileSource.ts @@ -0,0 +1,72 @@ +import FilteredLayer from "../../../Models/FilteredLayer"; +import {FeatureSourceForLayer, Tiled} from "../FeatureSource"; +import {UIEventSource} from "../../UIEventSource"; +import Loc from "../../../Models/Loc"; +import DynamicTileSource from "./DynamicTileSource"; +import {Utils} from "../../../Utils"; +import GeoJsonSource from "../Sources/GeoJsonSource"; + +export default class DynamicGeoJsonTileSource extends DynamicTileSource { + constructor(layer: FilteredLayer, + registerLayer: (layer: FeatureSourceForLayer & Tiled) => void, + state: { + locationControl: UIEventSource + leafletMap: any + }) { + const source = layer.layerDef.source + if (source.geojsonZoomLevel === undefined) { + throw "Invalid layer: geojsonZoomLevel expected" + } + if (source.geojsonSource === undefined) { + throw "Invalid layer: geojsonSource expected" + } + + const whitelistUrl = source.geojsonSource.replace("{z}_{x}_{y}.geojson", "overview.json") + .replace("{layer}",layer.layerDef.id) + + let whitelist = undefined + Utils.downloadJson(whitelistUrl).then( + json => { + const data = new Map>(); + for (const x in json) { + data.set(Number(x), new Set(json[x])) + } + whitelist = data + } + ).catch(err => { + console.warn("No whitelist found for ", layer.layerDef.id, err) + }) + + const seenIds = new Set(); + const blackList = new UIEventSource(seenIds) + super( + layer, + source.geojsonZoomLevel, + (zxy) => { + if(whitelist !== undefined){ + const isWhiteListed = whitelist.get(zxy[1])?.has(zxy[2]) + if(!isWhiteListed){ + return undefined; + } + } + + const src = new GeoJsonSource( + layer, + zxy, + { + featureIdBlacklist: blackList + } + ) + src.features.addCallbackAndRunD(feats => { + feats.forEach(feat => seenIds.add(feat.feature.properties.id)) + blackList.ping(); + }) + registerLayer(src) + return src + }, + state + ); + + } + +} \ No newline at end of file diff --git a/Logic/FeatureSource/TiledFeatureSource/DynamicTileSource.ts b/Logic/FeatureSource/TiledFeatureSource/DynamicTileSource.ts new file mode 100644 index 0000000000..a6ef04458c --- /dev/null +++ b/Logic/FeatureSource/TiledFeatureSource/DynamicTileSource.ts @@ -0,0 +1,79 @@ + +import State from "../../../State"; +import FilteredLayer from "../../../Models/FilteredLayer"; +import {FeatureSourceForLayer, Tiled} from "../FeatureSource"; +import {Utils} from "../../../Utils"; +import {UIEventSource} from "../../UIEventSource"; +import Loc from "../../../Models/Loc"; +import TileHierarchy from "./TileHierarchy"; +import {Tiles} from "../../../Models/TileRange"; + +/*** + * A tiled source which dynamically loads the required tiles at a fixed zoom level + */ +export default class DynamicTileSource implements TileHierarchy { + private readonly _loadedTiles = new Set(); + + public readonly loadedTiles: Map; + + constructor( + layer: FilteredLayer, + zoomlevel: number, + constructTile: (zxy: [number, number, number]) => (FeatureSourceForLayer & Tiled), + state: { + locationControl: UIEventSource + leafletMap: any + } + ) { + state = State.state + const self = this; + + this.loadedTiles = new Map() + const neededTiles = state.locationControl.map( + location => { + if (!layer.isDisplayed.data) { + // No need to download! - the layer is disabled + return undefined; + } + + if (location.zoom < layer.layerDef.minzoom) { + // No need to download! - the layer is disabled + return undefined; + } + + // Yup, this is cheating to just get the bounds here + const bounds = state.leafletMap.data?.getBounds() + if (bounds === undefined) { + // We'll retry later + return undefined + } + const tileRange = Tiles.TileRangeBetween(zoomlevel, bounds.getNorth(), bounds.getEast(), bounds.getSouth(), bounds.getWest()) + + const needed = Tiles.MapRange(tileRange, (x, y) => Tiles.tile_index(zoomlevel, x, y)).filter(i => !self._loadedTiles.has(i)) + if (needed.length === 0) { + return undefined + } + return needed + } + , [layer.isDisplayed, state.leafletMap]).stabilized(250); + + neededTiles.addCallbackAndRunD(neededIndexes => { + console.log("Tiled geojson source ",layer.layerDef.id," needs", neededIndexes) + if (neededIndexes === undefined) { + return; + } + for (const neededIndex of neededIndexes) { + self._loadedTiles.add(neededIndex) + const src = constructTile(Tiles.tile_from_index(neededIndex)) + if(src !== undefined){ + self.loadedTiles.set(neededIndex, src) + } + } + }) + + + } + + +} + diff --git a/Logic/FeatureSource/TiledFeatureSource/OsmFeatureSource.ts b/Logic/FeatureSource/TiledFeatureSource/OsmFeatureSource.ts new file mode 100644 index 0000000000..5641d5b980 --- /dev/null +++ b/Logic/FeatureSource/TiledFeatureSource/OsmFeatureSource.ts @@ -0,0 +1,118 @@ +import {Utils} from "../../../Utils"; +import * as OsmToGeoJson from "osmtogeojson"; +import StaticFeatureSource from "../Sources/StaticFeatureSource"; +import PerLayerFeatureSourceSplitter from "../PerLayerFeatureSourceSplitter"; +import {UIEventSource} from "../../UIEventSource"; +import FilteredLayer from "../../../Models/FilteredLayer"; +import {FeatureSourceForLayer, Tiled} from "../FeatureSource"; +import {Tiles} from "../../../Models/TileRange"; +import {BBox} from "../../BBox"; +import {OsmConnection} from "../../Osm/OsmConnection"; + +export default class OsmFeatureSource { + private readonly _backend: string; + + public readonly isRunning: UIEventSource = new UIEventSource(false) + private readonly filteredLayers: UIEventSource; + private readonly handleTile: (fs: (FeatureSourceForLayer & Tiled)) => void; + private isActive: UIEventSource; + private options: { + handleTile: (tile: FeatureSourceForLayer & Tiled) => void; + isActive: UIEventSource, + neededTiles: UIEventSource, + state: { + readonly osmConnection: OsmConnection; + }, + markTileVisited?: (tileId: number) => void + }; + private readonly downloadedTiles = new Set() + + constructor(options: { + handleTile: (tile: FeatureSourceForLayer & Tiled) => void; + isActive: UIEventSource, + neededTiles: UIEventSource, + state: { + readonly filteredLayers: UIEventSource; + readonly osmConnection: OsmConnection; + }, + markTileVisited?: (tileId: number) => void + }) { + this.options = options; + this._backend = options.state.osmConnection._oauth_config.url; + this.filteredLayers = options.state.filteredLayers.map(layers => layers.filter(layer => layer.layerDef.source.geojsonSource === undefined)) + this.handleTile = options.handleTile + this.isActive = options.isActive + const self = this + options.neededTiles.addCallbackAndRunD(neededTiles => { + if (options.isActive?.data === false) { + return; + } + + self.isRunning.setData(true) + try { + + for (const neededTile of neededTiles) { + if (self.downloadedTiles.has(neededTile)) { + return; + } + self.downloadedTiles.add(neededTile) + Promise.resolve(self.LoadTile(...Tiles.tile_from_index(neededTile)).then(_ => { + })) + } + } catch (e) { + console.error(e) + } + self.isRunning.setData(false) + }) + } + + private async LoadTile(z, x, y): Promise { + if (z > 20) { + throw "This is an absurd high zoom level" + } + + const bbox = BBox.fromTile(z, x, y) + const url = `${this._backend}/api/0.6/map?bbox=${bbox.minLon},${bbox.minLat},${bbox.maxLon},${bbox.maxLat}` + try { + + console.log("Attempting to get tile", z, x, y, "from the osm api") + const osmXml = await Utils.download(url, {"accept": "application/xml"}) + try { + const parsed = new DOMParser().parseFromString(osmXml, "text/xml"); + console.log("Got tile", z, x, y, "from the osm api") + const geojson = OsmToGeoJson.default(parsed, + // @ts-ignore + { + flatProperties: true + }); + console.log("Tile geojson:", z, x, y, "is", geojson) + const index = Tiles.tile_index(z, x, y); + new PerLayerFeatureSourceSplitter(this.filteredLayers, + this.handleTile, + new StaticFeatureSource(geojson.features, false), + { + tileIndex:index + } + ); + if(this.options.markTileVisited){ + this.options.markTileVisited(index) + } + } catch (e) { + console.error("Weird error: ", e) + } + } catch (e) { + console.error("Could not download tile", z, x, y, "due to", e, "; retrying with smaller bounds") + if (e === "rate limited") { + return; + } + await this.LoadTile(z + 1, x * 2, y * 2) + await this.LoadTile(z + 1, 1 + x * 2, y * 2) + await this.LoadTile(z + 1, x * 2, 1 + y * 2) + await this.LoadTile(z + 1, 1 + x * 2, 1 + y * 2) + return; + } + + + } + +} \ No newline at end of file diff --git a/Logic/FeatureSource/TiledFeatureSource/README.md b/Logic/FeatureSource/TiledFeatureSource/README.md new file mode 100644 index 0000000000..93b51c6ad6 --- /dev/null +++ b/Logic/FeatureSource/TiledFeatureSource/README.md @@ -0,0 +1,27 @@ +Data in MapComplete can come from multiple sources. + +Currently, they are: + +- The Overpass-API +- The OSM-API +- One or more GeoJSON files. This can be a single file or a set of tiled geojson files +- LocalStorage, containing features from a previous visit +- Changes made by the user introducing new features + +When the data enters from Overpass or from the OSM-API, they are first distributed per layer: + +OVERPASS | ---PerLayerFeatureSource---> FeatureSourceForLayer[] +OSM | + +The GeoJSon files (not tiled) are then added to this list + +A single FeatureSourcePerLayer is then further handled by splitting it into a tile hierarchy. + + + +In order to keep thins snappy, they are distributed over a tiled database per layer. + + +## Notes + +`cached-featuresbookcases` is the old key used `cahced-features{themeid}` and should be cleaned up \ No newline at end of file diff --git a/Logic/FeatureSource/TiledFeatureSource/TileHierarchy.ts b/Logic/FeatureSource/TiledFeatureSource/TileHierarchy.ts new file mode 100644 index 0000000000..f2e43069d1 --- /dev/null +++ b/Logic/FeatureSource/TiledFeatureSource/TileHierarchy.ts @@ -0,0 +1,25 @@ +import FeatureSource, {Tiled} from "../FeatureSource"; +import {BBox} from "../../BBox"; + +export default interface TileHierarchy { + + /** + * A mapping from 'tile_index' to the actual tile featrues + */ + loadedTiles: Map + +} + +export class TileHierarchyTools { + + public static getTiles(hierarchy: TileHierarchy, bbox: BBox): T[] { + const result = [] + hierarchy.loadedTiles.forEach((tile) => { + if (tile.bbox.overlapsWith(bbox)) { + result.push(tile) + } + }) + return result; + } + +} \ No newline at end of file diff --git a/Logic/FeatureSource/TiledFeatureSource/TileHierarchyMerger.ts b/Logic/FeatureSource/TiledFeatureSource/TileHierarchyMerger.ts new file mode 100644 index 0000000000..6fd3dae65c --- /dev/null +++ b/Logic/FeatureSource/TiledFeatureSource/TileHierarchyMerger.ts @@ -0,0 +1,46 @@ +import TileHierarchy from "./TileHierarchy"; +import {UIEventSource} from "../../UIEventSource"; +import FeatureSource, {FeatureSourceForLayer, IndexedFeatureSource, Tiled} from "../FeatureSource"; +import FilteredLayer from "../../../Models/FilteredLayer"; +import FeatureSourceMerger from "../Sources/FeatureSourceMerger"; +import {Tiles} from "../../../Models/TileRange"; +import {BBox} from "../../BBox"; + +export class TileHierarchyMerger implements TileHierarchy { + public readonly loadedTiles: Map = new Map(); + private readonly sources: Map> = new Map>(); + + public readonly layer: FilteredLayer; + private _handleTile: (src: FeatureSourceForLayer & IndexedFeatureSource, index: number) => void; + + constructor(layer: FilteredLayer, handleTile: (src: FeatureSourceForLayer & IndexedFeatureSource & Tiled, index: number) => void) { + this.layer = layer; + this._handleTile = handleTile; + } + + /** + * Add another feature source for the given tile. + * Entries for this tile will be merged + * @param src + * @param index + */ + public registerTile(src: FeatureSource & Tiled) { + + const index = src.tileIndex + if (this.sources.has(index)) { + const sources = this.sources.get(index) + sources.data.push(src) + sources.ping() + return; + } + + // We have to setup + const sources = new UIEventSource([src]) + this.sources.set(index, sources) + const merger = new FeatureSourceMerger(this.layer, index, BBox.fromTile(...Tiles.tile_from_index(index)), sources) + this.loadedTiles.set(index, merger) + this._handleTile(merger, index) + } + + +} \ No newline at end of file diff --git a/Logic/FeatureSource/TiledFeatureSource/TiledFeatureSource.ts b/Logic/FeatureSource/TiledFeatureSource/TiledFeatureSource.ts new file mode 100644 index 0000000000..4950ea328f --- /dev/null +++ b/Logic/FeatureSource/TiledFeatureSource/TiledFeatureSource.ts @@ -0,0 +1,206 @@ +import FeatureSource, {FeatureSourceForLayer, IndexedFeatureSource, Tiled} from "../FeatureSource"; +import {UIEventSource} from "../../UIEventSource"; +import {Utils} from "../../../Utils"; +import FilteredLayer from "../../../Models/FilteredLayer"; +import TileHierarchy from "./TileHierarchy"; +import {Tiles} from "../../../Models/TileRange"; +import {BBox} from "../../BBox"; + +/** + * Contains all features in a tiled fashion. + * The data will be automatically broken down into subtiles when there are too much features in a single tile or if the zoomlevel is too high + */ +export default class TiledFeatureSource implements Tiled, IndexedFeatureSource, FeatureSourceForLayer, TileHierarchy { + public readonly z: number; + public readonly x: number; + public readonly y: number; + public readonly parent: TiledFeatureSource; + public readonly root: TiledFeatureSource + public readonly layer: FilteredLayer; + /* An index of all known tiles. allTiles[z][x][y].get('layerid') will yield the corresponding tile. + * Only defined on the root element! + */ + public readonly loadedTiles: Map = undefined; + + public readonly maxFeatureCount: number; + public readonly name; + public readonly features: UIEventSource<{ feature: any, freshness: Date }[]> + public readonly containedIds: UIEventSource> + + public readonly bbox: BBox; + private upper_left: TiledFeatureSource + private upper_right: TiledFeatureSource + private lower_left: TiledFeatureSource + private lower_right: TiledFeatureSource + private readonly maxzoom: number; + private readonly options: TiledFeatureSourceOptions + public readonly tileIndex: number; + + private constructor(z: number, x: number, y: number, parent: TiledFeatureSource, options?: TiledFeatureSourceOptions) { + this.z = z; + this.x = x; + this.y = y; + this.bbox = BBox.fromTile(z, x, y) + this.tileIndex = Tiles.tile_index(z, x, y) + this.name = `TiledFeatureSource(${z},${x},${y})` + this.parent = parent; + this.layer = options.layer + options = options ?? {} + this.maxFeatureCount = options?.maxFeatureCount ?? 250; + this.maxzoom = options.maxZoomLevel ?? 18 + this.options = options; + if (parent === undefined) { + throw "Parent is not allowed to be undefined. Use null instead" + } + if (parent === null && z !== 0 && x !== 0 && y !== 0) { + throw "Invalid root tile: z, x and y should all be null" + } + if (parent === null) { + this.root = this; + this.loadedTiles = new Map() + } else { + this.root = this.parent.root; + this.loadedTiles = this.root.loadedTiles; + const i = Tiles.tile_index(z, x, y) + this.root.loadedTiles.set(i, this) + } + this.features = new UIEventSource([]) + this.containedIds = this.features.map(features => { + if (features === undefined) { + return undefined; + } + return new Set(features.map(f => f.feature.properties.id)) + }) + + // We register this tile, but only when there is some data in it + if (this.options.registerTile !== undefined) { + this.features.addCallbackAndRunD(features => { + if (features.length === 0) { + return; + } + this.options.registerTile(this) + return true; + }) + } + + + } + + public static createHierarchy(features: FeatureSource, options?: TiledFeatureSourceOptions): TiledFeatureSource { + const root = new TiledFeatureSource(0, 0, 0, null, options) + features.features?.addCallbackAndRunD(feats => root.addFeatures(feats)) + return root; + } + + private isSplitNeeded(featureCount: number){ + if(this.upper_left !== undefined){ + // This tile has been split previously, so we keep on splitting + return true; + } + if(this.z >= this.maxzoom){ + // We are not allowed to split any further + return false + } + if(this.options.minZoomLevel !== undefined && this.z < this.options.minZoomLevel){ + // We must have at least this zoom level before we are allowed to start splitting + return true + } + + // To much features - we split + return featureCount > this.maxFeatureCount + + } + + /*** + * Adds the list of features to this hierarchy. + * If there are too much features, the list will be broken down and distributed over the subtiles (only retaining features that don't fit a subtile on this level) + * @param features + * @private + */ + private addFeatures(features: { feature: any, freshness: Date }[]) { + if (features === undefined || features.length === 0) { + return; + } + + if (!this.isSplitNeeded(features.length)) { + this.features.setData(features) + return; + } + + if (this.upper_left === undefined) { + this.upper_left = new TiledFeatureSource(this.z + 1, this.x * 2, this.y * 2, this, this.options) + this.upper_right = new TiledFeatureSource(this.z + 1, this.x * 2 + 1, this.y * 2, this, this.options) + this.lower_left = new TiledFeatureSource(this.z + 1, this.x * 2, this.y * 2 + 1, this, this.options) + this.lower_right = new TiledFeatureSource(this.z + 1, this.x * 2 + 1, this.y * 2 + 1, this, this.options) + } + + const ulf = [] + const urf = [] + const llf = [] + const lrf = [] + const overlapsboundary = [] + + for (const feature of features) { + const bbox = BBox.get(feature.feature) + + if (this.options.dontEnforceMinZoom) { + if (bbox.overlapsWith(this.upper_left.bbox)) { + ulf.push(feature) + } else if (bbox.overlapsWith(this.upper_right.bbox)) { + urf.push(feature) + } else if (bbox.overlapsWith(this.lower_left.bbox)) { + llf.push(feature) + } else if (bbox.overlapsWith(this.lower_right.bbox)) { + lrf.push(feature) + } else { + overlapsboundary.push(feature) + } + }else if (this.options.minZoomLevel === undefined) { + if (bbox.isContainedIn(this.upper_left.bbox)) { + ulf.push(feature) + } else if (bbox.isContainedIn(this.upper_right.bbox)) { + urf.push(feature) + } else if (bbox.isContainedIn(this.lower_left.bbox)) { + llf.push(feature) + } else if (bbox.isContainedIn(this.lower_right.bbox)) { + lrf.push(feature) + } else { + overlapsboundary.push(feature) + } + } else { + // We duplicate a feature on a boundary into every tile as we need to get to the minZoomLevel + if (bbox.overlapsWith(this.upper_left.bbox)) { + ulf.push(feature) + } + if (bbox.overlapsWith(this.upper_right.bbox)) { + urf.push(feature) + } + if (bbox.overlapsWith(this.lower_left.bbox)) { + llf.push(feature) + } + if (bbox.overlapsWith(this.lower_right.bbox)) { + lrf.push(feature) + } + } + } + this.upper_left.addFeatures(ulf) + this.upper_right.addFeatures(urf) + this.lower_left.addFeatures(llf) + this.lower_right.addFeatures(lrf) + this.features.setData(overlapsboundary) + + } +} + +export interface TiledFeatureSourceOptions { + readonly maxFeatureCount?: number, + readonly maxZoomLevel?: number, + readonly minZoomLevel?: number, + /** + * IF minZoomLevel is set, and if a feature runs through a tile boundary, it would normally be duplicated. + * Setting 'dontEnforceMinZoomLevel' will still allow bigger zoom levels for those features + */ + readonly dontEnforceMinZoom?: boolean, + readonly registerTile?: (tile: TiledFeatureSource & Tiled) => void, + readonly layer?: FilteredLayer +} \ No newline at end of file diff --git a/Logic/FeatureSource/TiledFeatureSource/TiledFromLocalStorageSource.ts b/Logic/FeatureSource/TiledFeatureSource/TiledFromLocalStorageSource.ts new file mode 100644 index 0000000000..6f815f48d5 --- /dev/null +++ b/Logic/FeatureSource/TiledFeatureSource/TiledFromLocalStorageSource.ts @@ -0,0 +1,132 @@ +import FilteredLayer from "../../../Models/FilteredLayer"; +import {FeatureSourceForLayer, Tiled} from "../FeatureSource"; +import {UIEventSource} from "../../UIEventSource"; +import Loc from "../../../Models/Loc"; +import TileHierarchy from "./TileHierarchy"; +import SaveTileToLocalStorageActor from "../Actors/SaveTileToLocalStorageActor"; +import {Tiles} from "../../../Models/TileRange"; +import {BBox} from "../../BBox"; + +export default class TiledFromLocalStorageSource implements TileHierarchy { + public loadedTiles: Map = new Map(); + + public static GetFreshnesses(layerId: string): Map { + const prefix = SaveTileToLocalStorageActor.storageKey + "-" + layerId + "-" + const freshnesses = new Map() + for (const key of Object.keys(localStorage)) { + if(!(key.startsWith(prefix) && key.endsWith("-time"))){ + continue + } + const index = Number(key.substring(prefix.length, key.length - "-time".length)) + const time = Number(localStorage.getItem(key)) + const freshness = new Date() + freshness.setTime(time) + freshnesses.set(index, freshness) + } + return freshnesses + } + + constructor(layer: FilteredLayer, + handleFeatureSource: (src: FeatureSourceForLayer & Tiled, index: number) => void, + state: { + locationControl: UIEventSource + leafletMap: any + }) { + + const undefinedTiles = new Set() + const prefix = SaveTileToLocalStorageActor.storageKey + "-" + layer.layerDef.id + "-" + // @ts-ignore + const indexes: number[] = Object.keys(localStorage) + .filter(key => { + return key.startsWith(prefix) && !key.endsWith("-time") && !key.endsWith("-format"); + }) + .map(key => { + return Number(key.substring(prefix.length)); + }) + .filter(i => !isNaN(i)) + + console.debug("Layer", layer.layerDef.id, "has following tiles in available in localstorage", indexes.map(i => Tiles.tile_from_index(i).join("/")).join(", ")) + for (const index of indexes) { + + const prefix = SaveTileToLocalStorageActor.storageKey + "-" + layer.layerDef.id + "-" + index; + const version = localStorage.getItem(prefix + "-format") + if (version === undefined || version !== SaveTileToLocalStorageActor.formatVersion) { + // Invalid version! Remove this tile from local storage + localStorage.removeItem(prefix) + localStorage.removeItem(prefix+"-time") + localStorage.removeItem(prefix+"-format") + undefinedTiles.add(index) + console.log("Dropped old format tile", prefix) + } + } + + const zLevels = indexes.map(i => i % 100) + const indexesSet = new Set(indexes) + const maxZoom = Math.max(...zLevels) + const minZoom = Math.min(...zLevels) + const self = this; + + const neededTiles = state.locationControl.map( + location => { + if (!layer.isDisplayed.data) { + // No need to download! - the layer is disabled + return undefined; + } + + if (location.zoom < layer.layerDef.minzoom) { + // No need to download! - the layer is disabled + return undefined; + } + + // Yup, this is cheating to just get the bounds here + const bounds = state.leafletMap.data?.getBounds() + if (bounds === undefined) { + // We'll retry later + return undefined + } + + const needed = [] + for (let z = minZoom; z <= maxZoom; z++) { + + const tileRange = Tiles.TileRangeBetween(z, bounds.getNorth(), bounds.getEast(), bounds.getSouth(), bounds.getWest()) + const neededZ = Tiles.MapRange(tileRange, (x, y) => Tiles.tile_index(z, x, y)) + .filter(i => !self.loadedTiles.has(i) && !undefinedTiles.has(i) && indexesSet.has(i)) + needed.push(...neededZ) + } + + if (needed.length === 0) { + return undefined + } + return needed + } + , [layer.isDisplayed, state.leafletMap]).stabilized(50); + + neededTiles.addCallbackAndRunD(neededIndexes => { + for (const neededIndex of neededIndexes) { + // We load the features from localStorage + try { + const key = SaveTileToLocalStorageActor.storageKey + "-" + layer.layerDef.id + "-" + neededIndex + const data = localStorage.getItem(key) + const features = JSON.parse(data) + const src = { + layer: layer, + features: new UIEventSource<{ feature: any; freshness: Date }[]>(features), + name: "FromLocalStorage(" + key + ")", + tileIndex: neededIndex, + bbox: BBox.fromTileIndex(neededIndex) + } + handleFeatureSource(src, neededIndex) + self.loadedTiles.set(neededIndex, src) + } catch (e) { + console.error("Could not load data tile from local storage due to", e) + undefinedTiles.add(neededIndex) + } + } + + + }) + + } + + +} \ No newline at end of file diff --git a/Logic/GeoOperations.ts b/Logic/GeoOperations.ts index 81cc3d066e..3fa10f7a23 100644 --- a/Logic/GeoOperations.ts +++ b/Logic/GeoOperations.ts @@ -1,4 +1,5 @@ import * as turf from '@turf/turf' +import {BBox} from "./BBox"; export class GeoOperations { @@ -7,7 +8,7 @@ export class GeoOperations { } /** - * Converts a GeoJSon feature to a point feature + * Converts a GeoJson feature to a point GeoJson feature * @param feature */ static centerpoint(feature: any) { @@ -185,8 +186,51 @@ export class GeoOperations { return turf.length(feature) * 1000 } + static buffer(feature: any, bufferSizeInMeter: number) { + return turf.buffer(feature, bufferSizeInMeter / 1000, { + units: 'kilometers' + }) + } + + static bbox(feature: any) { + const [lon, lat, lon0, lat0] = turf.bbox(feature) + return { + "type": "Feature", + "geometry": { + "type": "LineString", + "coordinates": [ + [ + lon, + lat + ], + [ + lon0, + lat + ], + [ + lon0, + lat0 + ], + [ + lon, + lat0 + ], + [ + lon, + lat + ], + ] + } + } + } + /** * Generates the closest point on a way from a given point + * + * The properties object will contain three values: + // - `index`: closest point was found on nth line part, + // - `dist`: distance between pt and the closest point (in kilometer), + // `location`: distance along the line between start and the closest point. * @param way The road on which you want to find a point * @param point Point defined as [lon, lat] */ @@ -334,77 +378,3 @@ export class GeoOperations { } -export class BBox { - - readonly maxLat: number; - readonly maxLon: number; - readonly minLat: number; - readonly minLon: number; - - constructor(coordinates) { - this.maxLat = Number.MIN_VALUE; - this.maxLon = Number.MIN_VALUE; - this.minLat = Number.MAX_VALUE; - this.minLon = Number.MAX_VALUE; - - - for (const coordinate of coordinates) { - this.maxLon = Math.max(this.maxLon, coordinate[0]); - this.maxLat = Math.max(this.maxLat, coordinate[1]); - this.minLon = Math.min(this.minLon, coordinate[0]); - this.minLat = Math.min(this.minLat, coordinate[1]); - } - this.check(); - } - - static fromLeafletBounds(bounds) { - return new BBox([[bounds.getWest(), bounds.getNorth()], [bounds.getEast(), bounds.getSouth()]]) - } - - static get(feature) { - if (feature.bbox?.overlapsWith === undefined) { - const turfBbox: number[] = turf.bbox(feature) - feature.bbox = new BBox([[turfBbox[0], turfBbox[1]], [turfBbox[2], turfBbox[3]]]); - } - - return feature.bbox; - } - - public overlapsWith(other: BBox) { - if (this.maxLon < other.minLon) { - return false; - } - if (this.maxLat < other.minLat) { - return false; - } - if (this.minLon > other.maxLon) { - return false; - } - return this.minLat <= other.maxLat; - - } - - public isContainedIn(other: BBox) { - if (this.maxLon > other.maxLon) { - return false; - } - if (this.maxLat > other.maxLat) { - return false; - } - if (this.minLon < other.minLon) { - return false; - } - if (this.minLat < other.minLat) { - return false - } - return true; - } - - private check() { - if (isNaN(this.maxLon) || isNaN(this.maxLat) || isNaN(this.minLon) || isNaN(this.minLat)) { - console.log(this); - throw "BBOX has NAN"; - } - } - -} \ No newline at end of file diff --git a/Logic/ImageProviders/AllImageProviders.ts b/Logic/ImageProviders/AllImageProviders.ts index e616e59e7e..92818379f2 100644 --- a/Logic/ImageProviders/AllImageProviders.ts +++ b/Logic/ImageProviders/AllImageProviders.ts @@ -1,9 +1,71 @@ import {Mapillary} from "./Mapillary"; -import {Wikimedia} from "./Wikimedia"; +import {WikimediaImageProvider} from "./WikimediaImageProvider"; import {Imgur} from "./Imgur"; +import GenericImageProvider from "./GenericImageProvider"; +import {UIEventSource} from "../UIEventSource"; +import ImageProvider, {ProvidedImage} from "./ImageProvider"; +import {WikidataImageProvider} from "./WikidataImageProvider"; +/** + * A generic 'from the interwebz' image picker, without attribution + */ export default class AllImageProviders { - public static ImageAttributionSource = [Imgur.singleton, Mapillary.singleton, Wikimedia.singleton] + public static ImageAttributionSource: ImageProvider[] = [ + Imgur.singleton, + Mapillary.singleton, + WikidataImageProvider.singleton, + WikimediaImageProvider.singleton, + new GenericImageProvider([].concat(...Imgur.defaultValuePrefix, WikimediaImageProvider.commonsPrefix, ...Mapillary.valuePrefixes))] + + + private static _cache: Map> = new Map>() + + public static LoadImagesFor(tags: UIEventSource, imagePrefix?: string): UIEventSource { + const id = tags.data.id + if (id === undefined) { + return undefined; + } + + const cached = this._cache.get(tags.data.id) + if (cached !== undefined) { + return cached + } + + + const source = new UIEventSource([]) + this._cache.set(id, source) + const allSources = [] + for (const imageProvider of AllImageProviders.ImageAttributionSource) { + + let prefixes = imageProvider.defaultKeyPrefixes + if(imagePrefix !== undefined){ + prefixes = [...prefixes] + if(prefixes.indexOf("image") >= 0){ + prefixes.splice(prefixes.indexOf("image"), 1) + } + prefixes.push(imagePrefix) + } + + const singleSource = imageProvider.GetRelevantUrls(tags, { + prefixes: prefixes + }) + allSources.push(singleSource) + singleSource.addCallbackAndRunD(_ => { + const all : ProvidedImage[] = [].concat(...allSources.map(source => source.data)) + const uniq = [] + const seen = new Set() + for (const img of all) { + if(seen.has(img.url)){ + continue + } + seen.add(img.url) + uniq.push(img) + } + source.setData(uniq) + }) + } + return source; + } } \ No newline at end of file diff --git a/Logic/ImageProviders/GenericImageProvider.ts b/Logic/ImageProviders/GenericImageProvider.ts new file mode 100644 index 0000000000..170829fe07 --- /dev/null +++ b/Logic/ImageProviders/GenericImageProvider.ts @@ -0,0 +1,36 @@ +import ImageProvider, {ProvidedImage} from "./ImageProvider"; + +export default class GenericImageProvider extends ImageProvider { + public defaultKeyPrefixes: string[] = ["image"]; + + private readonly _valuePrefixBlacklist: string[]; + + public constructor(valuePrefixBlacklist: string[]) { + super(); + this._valuePrefixBlacklist = valuePrefixBlacklist; + } + + + protected DownloadAttribution(url: string) { + return undefined + } + + async ExtractUrls(key: string, value: string): Promise[]> { + + if (this._valuePrefixBlacklist.some(prefix => value.startsWith(prefix))) { + return [] + } + + return [Promise.resolve({ + key: key, + url: value, + provider: this + })] + } + + SourceIcon(backlinkSource?: string) { + return undefined; + } + + +} \ No newline at end of file diff --git a/Logic/ImageProviders/ImageAttributionSource.ts b/Logic/ImageProviders/ImageAttributionSource.ts deleted file mode 100644 index 7841c899b1..0000000000 --- a/Logic/ImageProviders/ImageAttributionSource.ts +++ /dev/null @@ -1,30 +0,0 @@ -import {UIEventSource} from "../UIEventSource"; -import {LicenseInfo} from "./Wikimedia"; -import BaseUIElement from "../../UI/BaseUIElement"; - - -export default abstract class ImageAttributionSource { - - private _cache = new Map>() - - GetAttributionFor(url: string): UIEventSource { - const cached = this._cache.get(url); - if (cached !== undefined) { - return cached; - } - const src = this.DownloadAttribution(url) - this._cache.set(url, src) - return src; - } - - - public abstract SourceIcon(backlinkSource?: string): BaseUIElement; - - /*Converts a value to a URL. Can return null if not applicable*/ - public PrepareUrl(value: string): string | UIEventSource{ - return value; - } - - protected abstract DownloadAttribution(url: string): UIEventSource; - -} \ No newline at end of file diff --git a/Logic/ImageProviders/ImageProvider.ts b/Logic/ImageProviders/ImageProvider.ts new file mode 100644 index 0000000000..efce6aa924 --- /dev/null +++ b/Logic/ImageProviders/ImageProvider.ts @@ -0,0 +1,72 @@ +import {UIEventSource} from "../UIEventSource"; +import BaseUIElement from "../../UI/BaseUIElement"; +import {LicenseInfo} from "./LicenseInfo"; + +export interface ProvidedImage { + url: string, key: string, provider: ImageProvider +} + +export default abstract class ImageProvider { + + public abstract readonly defaultKeyPrefixes : string[] = ["mapillary", "image"] + + private _cache = new Map>() + + GetAttributionFor(url: string): UIEventSource { + const cached = this._cache.get(url); + if (cached !== undefined) { + return cached; + } + const src =UIEventSource.FromPromise(this.DownloadAttribution(url)) + this._cache.set(url, src) + return src; + } + + public abstract SourceIcon(backlinkSource?: string): BaseUIElement; + + protected abstract DownloadAttribution(url: string): Promise; + + /** + * Given a properies object, maps it onto _all_ the available pictures for this imageProvider + */ + public GetRelevantUrls(allTags: UIEventSource, options?: { + prefixes?: string[] + }):UIEventSource { + const prefixes = options?.prefixes ?? this.defaultKeyPrefixes + if(prefixes === undefined){ + throw "The image provider"+this.constructor.name+" doesn't define `defaultKeyPrefixes`" + } + const relevantUrls = new UIEventSource<{ url: string; key: string; provider: ImageProvider }[]>([]) + const seenValues = new Set() + allTags.addCallbackAndRunD(tags => { + for (const key in tags) { + if(!prefixes.some(prefix => key.startsWith(prefix))){ + continue + } + const value = tags[key] + if(seenValues.has(value)){ + continue + } + seenValues.add(value) + this.ExtractUrls(key, value).then(promises => { + for (const promise of promises ?? []) { + if(promise === undefined){ + continue + } + promise.then(providedImage => { + if(providedImage === undefined){ + return + } + relevantUrls.data.push(providedImage) + relevantUrls.ping() + }) + } + }) + } + }) + return relevantUrls + } + + public abstract ExtractUrls(key: string, value: string) : Promise[]>; + +} \ No newline at end of file diff --git a/Logic/ImageProviders/Imgur.ts b/Logic/ImageProviders/Imgur.ts index 4325d3e37f..289dad1f8a 100644 --- a/Logic/ImageProviders/Imgur.ts +++ b/Logic/ImageProviders/Imgur.ts @@ -1,11 +1,14 @@ -// @ts-ignore import $ from "jquery" -import {LicenseInfo} from "./Wikimedia"; -import ImageAttributionSource from "./ImageAttributionSource"; -import {UIEventSource} from "../UIEventSource"; +import ImageProvider, {ProvidedImage} from "./ImageProvider"; import BaseUIElement from "../../UI/BaseUIElement"; +import {Utils} from "../../Utils"; +import Constants from "../../Models/Constants"; +import {LicenseInfo} from "./LicenseInfo"; -export class Imgur extends ImageAttributionSource { +export class Imgur extends ImageProvider { + + public static readonly defaultValuePrefix = ["https://i.imgur.com"] + public readonly defaultKeyPrefixes: string[] = ["image"]; public static readonly singleton = new Imgur(); @@ -86,51 +89,39 @@ export class Imgur extends ImageAttributionSource { return undefined; } - protected DownloadAttribution(url: string): UIEventSource { - const src = new UIEventSource(undefined) - - + protected DownloadAttribution: (url: string) => Promise = async (url: string) => { const hash = url.substr("https://i.imgur.com/".length).split(".jpg")[0]; const apiUrl = 'https://api.imgur.com/3/image/' + hash; - const apiKey = '7070e7167f0a25a'; + const response = await Utils.downloadJson(apiUrl, {Authorization: 'Client-ID ' + Constants.ImgurApiKey}) - const settings = { - async: true, - crossDomain: true, - processData: false, - contentType: false, - type: 'GET', - url: apiUrl, - headers: { - Authorization: 'Client-ID ' + apiKey, - Accept: 'application/json', - }, - }; - // @ts-ignore - $.ajax(settings).done(function (response) { - const descr: string = response.data.description ?? ""; - const data: any = {}; - for (const tag of descr.split("\n")) { - const kv = tag.split(":"); - const k = kv[0]; - data[k] = kv[1].replace("\r", ""); - } + const descr: string = response.data.description ?? ""; + const data: any = {}; + for (const tag of descr.split("\n")) { + const kv = tag.split(":"); + const k = kv[0]; + data[k] = kv[1]?.replace("\r", ""); + } - const licenseInfo = new LicenseInfo(); + const licenseInfo = new LicenseInfo(); - licenseInfo.licenseShortName = data.license; - licenseInfo.artist = data.author; + licenseInfo.licenseShortName = data.license; + licenseInfo.artist = data.author; - src.setData(licenseInfo) - - }).fail((reason) => { - console.log("Getting metadata from to IMGUR failed", reason) - }); - - return src; + return licenseInfo } + public async ExtractUrls(key: string, value: string): Promise[]> { + if (Imgur.defaultValuePrefix.some(prefix => value.startsWith(prefix))) { + return [Promise.resolve({ + url: value, + key: key, + provider: this + })] + } + return [] + } + } \ No newline at end of file diff --git a/Logic/ImageProviders/LicenseInfo.ts b/Logic/ImageProviders/LicenseInfo.ts new file mode 100644 index 0000000000..b5954693c7 --- /dev/null +++ b/Logic/ImageProviders/LicenseInfo.ts @@ -0,0 +1,10 @@ +export class LicenseInfo { + artist: string = ""; + license: string = ""; + licenseShortName: string = ""; + usageTerms: string = ""; + attributionRequired: boolean = false; + copyrighted: boolean = false; + credit: string = ""; + description: string = ""; +} \ No newline at end of file diff --git a/Logic/ImageProviders/Mapillary.ts b/Logic/ImageProviders/Mapillary.ts index 72a593d299..3fa25a534d 100644 --- a/Logic/ImageProviders/Mapillary.ts +++ b/Logic/ImageProviders/Mapillary.ts @@ -1,30 +1,31 @@ -import {LicenseInfo} from "./Wikimedia"; -import ImageAttributionSource from "./ImageAttributionSource"; +import ImageProvider, {ProvidedImage} from "./ImageProvider"; import BaseUIElement from "../../UI/BaseUIElement"; import {UIEventSource} from "../UIEventSource"; import Svg from "../../Svg"; import {Utils} from "../../Utils"; +import {LicenseInfo} from "./LicenseInfo"; +import Constants from "../../Models/Constants"; +import {fail} from "assert"; -export class Mapillary extends ImageAttributionSource { +export class Mapillary extends ImageProvider { - public static readonly singleton = new Mapillary(); + defaultKeyPrefixes = ["mapillary","image"] - private static readonly v4_cached_urls = new Map>(); - - private static readonly client_token_v3 = 'TXhLaWthQ1d4RUg0czVxaTVoRjFJZzowNDczNjUzNmIyNTQyYzI2' - private static readonly client_token_v4 = "MLY|4441509239301885|b40ad2d3ea105435bd40c7e76993ae85" + public static readonly singleton = new Mapillary(); + private static readonly valuePrefix = "https://a.mapillary.com" + public static readonly valuePrefixes = [Mapillary.valuePrefix, "http://mapillary.com","https://mapillary.com"] private constructor() { super(); } - private static ExtractKeyFromURL(value: string): { + private static ExtractKeyFromURL(value: string, failIfNoMath = false): { key: string, isApiv4?: boolean } { - if (value.startsWith("https://a.mapillary.com")) { - const key = value.substring(0, value.lastIndexOf("?")).substring(value.lastIndexOf("/") + 1) - return {key:key, isApiv4: !isNaN(Number(key))}; + if (value.startsWith(Mapillary.valuePrefix)) { + const key = value.substring(0, value.lastIndexOf("?")).substring(value.lastIndexOf("/") + 1) + return {key: key, isApiv4: !isNaN(Number(key))}; } const newApiFormat = value.match(/https?:\/\/www.mapillary.com\/app\/\?pKey=([0-9]*)/) if (newApiFormat !== null) { @@ -32,12 +33,11 @@ export class Mapillary extends ImageAttributionSource { } const mapview = value.match(/https?:\/\/www.mapillary.com\/map\/im\/(.*)/) - console.log("Mapview matched ", value, mapview) - if(mapview !== null){ + if (mapview !== null) { const key = mapview[1] - return {key:key, isApiv4: !isNaN(Number(key))}; + return {key: key, isApiv4: !isNaN(Number(key))}; } - + if (value.toLowerCase().startsWith("https://www.mapillary.com/map/im/")) { // Extract the key of the image @@ -48,8 +48,11 @@ export class Mapillary extends ImageAttributionSource { if (matchApi !== null) { return {key: matchApi[1]}; } - - + + if(failIfNoMath){ + return undefined; + } + return {key: value, isApiv4: !isNaN(Number(value))}; } @@ -57,54 +60,59 @@ export class Mapillary extends ImageAttributionSource { return Svg.mapillary_svg(); } - PrepareUrl(value: string): string | UIEventSource { - const keyV = Mapillary.ExtractKeyFromURL(value) - if (!keyV.isApiv4) { - return `https://images.mapillary.com/${keyV.key}/thumb-640.jpg?client_id=${Mapillary.client_token_v3}` - } else { - const key = keyV.key; - if(Mapillary.v4_cached_urls.has(key)){ - return Mapillary.v4_cached_urls.get(key) - } - - const metadataUrl ='https://graph.mapillary.com/' + key + '?fields=thumb_1024_url&&access_token=' + Mapillary.client_token_v4; - const source = new UIEventSource(undefined) - Mapillary.v4_cached_urls.set(key, source) - Utils.downloadJson(metadataUrl).then( - json => { - console.warn("Got response on mapillary image", json, json["thumb_1024_url"]) - return source.setData(json["thumb_1024_url"]); - } - ) - return source - } + async ExtractUrls(key: string, value: string): Promise[]> { + return [this.PrepareUrlAsync(key, value)] } - protected DownloadAttribution(url: string): UIEventSource { + private async PrepareUrlAsync(key: string, value: string): Promise { + const failIfNoMatch = key.indexOf("mapillary") < 0 + const keyV = Mapillary.ExtractKeyFromURL(value, failIfNoMatch) + if(keyV === undefined){ + return undefined; + } + + if (!keyV.isApiv4) { + const url = `https://images.mapillary.com/${keyV.key}/thumb-640.jpg?client_id=${Constants.mapillary_client_token_v3}` + return { + url: url, + provider: this, + key: key + } + } else { + const mapillaryId = keyV.key; + const metadataUrl = 'https://graph.mapillary.com/' + mapillaryId + '?fields=thumb_1024_url&&access_token=' + Constants.mapillary_client_token_v4; + const response = await Utils.downloadJson(metadataUrl) + const url = response["thumb_1024_url"]; + return { + url: url, + provider: this, + key: key + } + } + } + + protected async DownloadAttribution(url: string): Promise { const keyV = Mapillary.ExtractKeyFromURL(url) - if(keyV.isApiv4){ + if (keyV.isApiv4) { const license = new LicenseInfo() license.artist = "Contributor name unavailable"; license.license = "CC BY-SA 4.0"; // license.license = "Creative Commons Attribution-ShareAlike 4.0 International License"; license.attributionRequired = true; - return new UIEventSource(license) - + return license + } const key = keyV.key - - const metadataURL = `https://a.mapillary.com/v3/images/${key}?client_id=TXhLaWthQ1d4RUg0czVxaTVoRjFJZzowNDczNjUzNmIyNTQyYzI2` - const source = new UIEventSource(undefined) - Utils.downloadJson(metadataURL).then(data => { - const license = new LicenseInfo(); - license.artist = data.properties?.username; - license.licenseShortName = "CC BY-SA 4.0"; - license.license = "Creative Commons Attribution-ShareAlike 4.0 International License"; - license.attributionRequired = true; - source.setData(license); - }) - return source + const metadataURL = `https://a.mapillary.com/v3/images/${key}?client_id=TXhLaWthQ1d4RUg0czVxaTVoRjFJZzowNDczNjUzNmIyNTQyYzI2` + const data = await Utils.downloadJson(metadataURL) + const license = new LicenseInfo(); + license.artist = data.properties?.username; + license.licenseShortName = "CC BY-SA 4.0"; + license.license = "Creative Commons Attribution-ShareAlike 4.0 International License"; + license.attributionRequired = true; + + return license } } \ No newline at end of file diff --git a/Logic/ImageProviders/WikidataImageProvider.ts b/Logic/ImageProviders/WikidataImageProvider.ts new file mode 100644 index 0000000000..f24f58acae --- /dev/null +++ b/Logic/ImageProviders/WikidataImageProvider.ts @@ -0,0 +1,46 @@ +import {Utils} from "../../Utils"; +import ImageProvider, {ProvidedImage} from "./ImageProvider"; +import BaseUIElement from "../../UI/BaseUIElement"; +import Svg from "../../Svg"; +import {WikimediaImageProvider} from "./WikimediaImageProvider"; +import Wikidata from "../Web/Wikidata"; + +export class WikidataImageProvider extends ImageProvider { + + public SourceIcon(backlinkSource?: string): BaseUIElement { + throw Svg.wikidata_svg(); + } + + public static readonly singleton = new WikidataImageProvider() + public readonly defaultKeyPrefixes = ["wikidata"] + + private constructor() { + super() + } + + protected DownloadAttribution(url: string): Promise { + throw new Error("Method not implemented; shouldn't be needed!"); + } + + public async ExtractUrls(key: string, value: string): Promise[]> { + const entity = await Wikidata.LoadWikidataEntryAsync(value) + if(entity === undefined){ + return [] + } + + const allImages : Promise[] = [] + // P18 is the claim 'depicted in this image' + for (const img of Array.from(entity.claims.get("P18") ?? [])) { + const promises = await WikimediaImageProvider.singleton.ExtractUrls(undefined, img) + allImages.push(...promises) + } + + const commons = entity.commons + if (commons !== undefined) { + const promises = await WikimediaImageProvider.singleton.ExtractUrls(undefined , commons) + allImages.push(...promises) + } + return allImages + } + +} \ No newline at end of file diff --git a/Logic/ImageProviders/Wikimedia.ts b/Logic/ImageProviders/Wikimedia.ts deleted file mode 100644 index 32c7625235..0000000000 --- a/Logic/ImageProviders/Wikimedia.ts +++ /dev/null @@ -1,195 +0,0 @@ -import ImageAttributionSource from "./ImageAttributionSource"; -import BaseUIElement from "../../UI/BaseUIElement"; -import Svg from "../../Svg"; -import {UIEventSource} from "../UIEventSource"; -import Link from "../../UI/Base/Link"; -import {Utils} from "../../Utils"; - -/** - * This module provides endpoints for wikipedia/wikimedia and others - */ -export class Wikimedia extends ImageAttributionSource { - - - public static readonly singleton = new Wikimedia(); - - private constructor() { - super(); - } - - - static ImageNameToUrl(filename: string, width: number = 500, height: number = 200): string { - filename = encodeURIComponent(filename); - return "https://commons.wikimedia.org/wiki/Special:FilePath/" + filename + "?width=" + width + "&height=" + height; - } - - static GetCategoryFiles(categoryName: string, handleCategory: ((ImagesInCategory: ImagesInCategory) => void), - alreadyLoaded = 0, - continueParameter: { k: string, param: string } = undefined) { - if (categoryName === undefined || categoryName === null || categoryName === "") { - return; - } - // @ts-ignore - if (!categoryName.startsWith("Category:")) { - categoryName = "Category:" + categoryName; - } - let url = "https://commons.wikimedia.org/w/api.php?" + - "action=query&list=categorymembers&format=json&" + - "&origin=*" + - "&cmtitle=" + encodeURIComponent(categoryName); - if (continueParameter !== undefined) { - url = url + "&" + continueParameter.k + "=" + continueParameter.param; - } - const self = this; - console.log("Loading a wikimedia category: ", url) - Utils.downloadJson(url).then((response) => { - let imageOverview = new ImagesInCategory(); - let members = response.query?.categorymembers; - if (members === undefined) { - members = []; - } - - for (const member of members) { - imageOverview.images.push(member.title); - } - console.log("Got images! ", imageOverview) - if (response.continue === undefined) { - handleCategory(imageOverview); - return; - } - - if (alreadyLoaded > 10) { - console.log(`Recursive wikimedia category load stopped for ${categoryName} - got already enough images now (${alreadyLoaded})`) - handleCategory(imageOverview) - return; - } - - self.GetCategoryFiles(categoryName, - (recursiveImages) => { - recursiveImages.images.push(...imageOverview.images); - handleCategory(recursiveImages); - }, - alreadyLoaded + 10, - {k: "cmcontinue", param: response.continue.cmcontinue}) - - }); - } - - static GetWikiData(id: number, handleWikidata: ((Wikidata) => void)) { - const url = "https://www.wikidata.org/wiki/Special:EntityData/Q" + id + ".json"; - Utils.downloadJson(url).then(response => { - const entity = response.entities["Q" + id]; - const commons = entity.sitelinks.commonswiki; - const wd = new Wikidata(); - wd.commonsWiki = commons?.title; - - // P18 is the claim 'depicted in this image' - const image = entity.claims.P18?.[0]?.mainsnak?.datavalue?.value; - if (image) { - wd.image = "File:" + image; - } - handleWikidata(wd); - }); - } - - private static ExtractFileName(url: string) { - if (!url.startsWith("http")) { - return url; - } - const path = new URL(url).pathname - return path.substring(path.lastIndexOf("/") + 1); - - } - - SourceIcon(backlink: string): BaseUIElement { - const img = Svg.wikimedia_commons_white_svg() - .SetStyle("width:2em;height: 2em"); - if (backlink === undefined) { - return img - } - - - return new Link(Svg.wikimedia_commons_white_img, - `https://commons.wikimedia.org/wiki/${backlink}`, true) - - - } - - PrepareUrl(value: string): string { - - if (value.toLowerCase().startsWith("https://commons.wikimedia.org/wiki/")) { - return value; - } - return Wikimedia.ImageNameToUrl(value, 500, 400) - .replace(/'/g, '%27'); - } - - protected DownloadAttribution(filename: string): UIEventSource { - - const source = new UIEventSource(undefined); - - filename = Wikimedia.ExtractFileName(filename) - - if (filename === "") { - return source; - } - - const url = "https://en.wikipedia.org/w/" + - "api.php?action=query&prop=imageinfo&iiprop=extmetadata&" + - "titles=" + filename + - "&format=json&origin=*"; - Utils.downloadJson(url).then( - data => { - const licenseInfo = new LicenseInfo(); - const license = (data.query.pages[-1].imageinfo ?? [])[0]?.extmetadata; - if (license === undefined) { - console.error("This file has no usable metedata or license attached... Please fix the license info file yourself!") - source.setData(null) - return; - } - - licenseInfo.artist = license.Artist?.value; - licenseInfo.license = license.License?.value; - licenseInfo.copyrighted = license.Copyrighted?.value; - licenseInfo.attributionRequired = license.AttributionRequired?.value; - licenseInfo.usageTerms = license.UsageTerms?.value; - licenseInfo.licenseShortName = license.LicenseShortName?.value; - licenseInfo.credit = license.Credit?.value; - licenseInfo.description = license.ImageDescription?.value; - source.setData(licenseInfo); - } - ) - - return source; - - } - - -} - -export class Wikidata { - - commonsWiki: string; - image: string; - -} - -export class ImagesInCategory { - // Filenames of relevant images - images: string[] = []; -} - -export class LicenseInfo { - - - artist: string = ""; - license: string = ""; - licenseShortName: string = ""; - usageTerms: string = ""; - attributionRequired: boolean = false; - copyrighted: boolean = false; - credit: string = ""; - description: string = ""; - - -} \ No newline at end of file diff --git a/Logic/ImageProviders/WikimediaImageProvider.ts b/Logic/ImageProviders/WikimediaImageProvider.ts new file mode 100644 index 0000000000..0d2f6c1d0f --- /dev/null +++ b/Logic/ImageProviders/WikimediaImageProvider.ts @@ -0,0 +1,168 @@ +import ImageProvider, {ProvidedImage} from "./ImageProvider"; +import BaseUIElement from "../../UI/BaseUIElement"; +import Svg from "../../Svg"; +import Link from "../../UI/Base/Link"; +import {Utils} from "../../Utils"; +import {LicenseInfo} from "./LicenseInfo"; + +/** + * This module provides endpoints for wikimedia and others + */ +export class WikimediaImageProvider extends ImageProvider { + + + private readonly commons_key = "wikimedia_commons" + public readonly defaultKeyPrefixes = [this.commons_key,"image"] + public static readonly singleton = new WikimediaImageProvider(); + public static readonly commonsPrefix = "https://commons.wikimedia.org/wiki/" + + private constructor() { + super(); + } + + /** + * Recursively walks a wikimedia commons category in order to search for (image) files + * Returns (a promise of) a list of URLS + * @param categoryName The name of the wikimedia category + * @param maxLoad: the maximum amount of images to return + * @param continueParameter: if the page indicates that more pages should be loaded, this uses a token to continue. Provided by wikimedia + */ + private static async GetImagesInCategory(categoryName: string, + maxLoad = 10, + continueParameter: string = undefined): Promise { + if (categoryName === undefined || categoryName === null || categoryName === "") { + return []; + } + if (!categoryName.startsWith("Category:")) { + categoryName = "Category:" + categoryName; + } + + let url = "https://commons.wikimedia.org/w/api.php?" + + "action=query&list=categorymembers&format=json&" + + "&origin=*" + + "&cmtitle=" + encodeURIComponent(categoryName); + if (continueParameter !== undefined) { + url = `${url}&cmcontinue=${continueParameter}`; + } + const response = await Utils.downloadJson(url) + const members = response.query?.categorymembers ?? []; + const imageOverview: string[] = members.map(member => member.title); + + if (response.continue === undefined) { + // We are done crawling through the category - no continuation in sight + return imageOverview; + } + + if (maxLoad - imageOverview.length <= 0) { + console.debug(`Recursive wikimedia category load stopped for ${categoryName}`) + return imageOverview; + } + + // We do have a continue token - let's load the next page + const recursive = await this.GetImagesInCategory(categoryName, maxLoad - imageOverview.length, response.continue.cmcontinue) + imageOverview.push(...recursive) + return imageOverview + } + + private static ExtractFileName(url: string) { + if (!url.startsWith("http")) { + return url; + } + const path = new URL(url).pathname + return path.substring(path.lastIndexOf("/") + 1); + + } + + SourceIcon(backlink: string): BaseUIElement { + const img = Svg.wikimedia_commons_white_svg() + .SetStyle("width:2em;height: 2em"); + if (backlink === undefined) { + return img + } + + + return new Link(Svg.wikimedia_commons_white_img, + `https://commons.wikimedia.org/wiki/${backlink}`, true) + + + } + + private PrepareUrl(value: string): string { + + if (value.toLowerCase().startsWith("https://commons.wikimedia.org/wiki/")) { + return value; + } + return (`https://commons.wikimedia.org/wiki/Special:FilePath/${encodeURIComponent(value)}?width=500&height=400`) + } + + protected async DownloadAttribution(filename: string): Promise { + filename = WikimediaImageProvider.ExtractFileName(filename) + + if (filename === "") { + return undefined; + } + + const url = "https://en.wikipedia.org/w/" + + "api.php?action=query&prop=imageinfo&iiprop=extmetadata&" + + "titles=" + filename + + "&format=json&origin=*"; + const data = await Utils.downloadJson(url) + const licenseInfo = new LicenseInfo(); + const license = (data.query.pages[-1].imageinfo ?? [])[0]?.extmetadata; + if (license === undefined) { + console.error("This file has no usable metedata or license attached... Please fix the license info file yourself!") + return undefined; + } + + licenseInfo.artist = license.Artist?.value; + licenseInfo.license = license.License?.value; + licenseInfo.copyrighted = license.Copyrighted?.value; + licenseInfo.attributionRequired = license.AttributionRequired?.value; + licenseInfo.usageTerms = license.UsageTerms?.value; + licenseInfo.licenseShortName = license.LicenseShortName?.value; + licenseInfo.credit = license.Credit?.value; + licenseInfo.description = license.ImageDescription?.value; + return licenseInfo; + + } + + private async UrlForImage(image: string): Promise { + if (!image.startsWith("File:")) { + image = "File:" + image + } + return {url: this.PrepareUrl(image), key: undefined, provider: this} + } + + public async ExtractUrls(key: string, value: string): Promise[]> { + if(key !== undefined && key !== this.commons_key && !value.startsWith(WikimediaImageProvider.commonsPrefix)){ + return [] + } + + if (value.startsWith(WikimediaImageProvider.commonsPrefix)) { + value = value.substring(WikimediaImageProvider.commonsPrefix.length) + } else if (value.startsWith("https://upload.wikimedia.org")) { + const result: ProvidedImage = { + key: undefined, + url: value, + provider: this + } + return [Promise.resolve(result)] + } + if (value.startsWith("Category:")) { + const urls = await WikimediaImageProvider.GetImagesInCategory(value) + return urls.map(image => this.UrlForImage(image)) + } + if (value.startsWith("File:")) { + return [this.UrlForImage(value)] + } + if (value.startsWith("http")) { + // PRobably an error + return [] + } + // We do a last effort and assume this is a file + return [this.UrlForImage("File:" + value)] + } + + +} + diff --git a/Logic/MetaTagging.ts b/Logic/MetaTagging.ts index 5d55518ba9..9f96e77309 100644 --- a/Logic/MetaTagging.ts +++ b/Logic/MetaTagging.ts @@ -1,15 +1,10 @@ import SimpleMetaTagger from "./SimpleMetaTagger"; -import {ExtraFunction} from "./ExtraFunction"; -import {Relation} from "./Osm/ExtractRelations"; +import {ExtraFuncParams, ExtraFunction} from "./ExtraFunction"; import {UIEventSource} from "./UIEventSource"; import LayerConfig from "../Models/ThemeConfig/LayerConfig"; +import State from "../State"; -interface Params { - featuresPerLayer: Map, - memberships: Map -} - /** * Metatagging adds various tags to the elements, e.g. lat, lon, surface area, ... * @@ -22,92 +17,98 @@ export default class MetaTagging { private static readonly stopErrorOutputAt = 10; /** - * An actor which adds metatags on every feature in the given object - * The features are a list of geojson-features, with a "properties"-field and geometry + * This method (re)calculates all metatags and calculated tags on every given object. + * The given features should be part of the given layer */ - static addMetatags(features: { feature: any; freshness: Date }[], - allKnownFeatures: UIEventSource<{ feature: any; freshness: Date }[]>, - relations: Map, - layers: LayerConfig[], - includeDates = true) { + public static addMetatags(features: { feature: any; freshness: Date }[], + params: ExtraFuncParams, + layer: LayerConfig, + options?: { + includeDates?: true | boolean, + includeNonDates?: true | boolean + }) { if (features === undefined || features.length === 0) { return; } - for (const metatag of SimpleMetaTagger.metatags) { - if (metatag.includesDates && !includeDates) { - // We do not add dated entries - continue; - } - try { - metatag.addMetaTags(features); - } catch (e) { - console.error("Could not calculate metatag for ", metatag.keys.join(","), ":", e) - } - } - - // The functions - per layer - which add the new keys - const layerFuncs = new Map void)>(); - for (const layer of layers) { - layerFuncs.set(layer.id, this.createRetaggingFunc(layer)); - } - - allKnownFeatures.addCallbackAndRunD(newFeatures => { - - const featuresPerLayer = new Map(); - const allFeatures = Array.from(new Set(features.concat(newFeatures))) - for (const feature of allFeatures) { - - const key = feature.feature._matching_layer_id; - if (!featuresPerLayer.has(key)) { - featuresPerLayer.set(key, []) + const metatagsToApply: SimpleMetaTagger [] = [] + for (const metatag of SimpleMetaTagger.metatags) { + if (metatag.includesDates) { + if (options.includeDates ?? true) { + metatagsToApply.push(metatag) + } + } else { + if (options.includeNonDates ?? true) { + metatagsToApply.push(metatag) + } } - featuresPerLayer.get(key).push(feature.feature) } - for (const feature of features) { - // @ts-ignore - const key = feature.feature._matching_layer_id; - const f = layerFuncs.get(key); - if (f === undefined) { - continue; - } + // The calculated functions - per layer - which add the new keys + const layerFuncs = this.createRetaggingFunc(layer) - try { - f({featuresPerLayer: featuresPerLayer, memberships: relations}, feature.feature) - } catch (e) { - console.error(e) - } + for (let i = 0; i < features.length; i++) { + const ff = features[i]; + const feature = ff.feature + const freshness = ff.freshness + let somethingChanged = false + for (const metatag of metatagsToApply) { + try { + if(!metatag.keys.some(key => feature.properties[key] === undefined)){ + // All keys are already defined, we probably already ran this one + continue + } + const newValueAdded = metatag.applyMetaTagsOnFeature(feature, freshness) + /* Note that the expression: + * `somethingChanged = newValueAdded || metatag.applyMetaTagsOnFeature(feature, freshness)` + * Is WRONG + * + * IF something changed is `true` due to an earlier run, it will short-circuit and _not_ evaluate the right hand of the OR, + * thus not running an update! + */ + somethingChanged = newValueAdded || somethingChanged + } catch (e) { + console.error("Could not calculate metatag for ", metatag.keys.join(","), ":", e, e.stack) + } + } + + if(layerFuncs !== undefined){ + try { + layerFuncs(params, feature) + } catch (e) { + console.error(e) + } + somethingChanged = true + } + + if(somethingChanged){ + State.state?.allElements?.getEventSourceById(feature.properties.id)?.ping() + } } - - - }) - - } + private static createRetaggingFunc(layer: LayerConfig): - ((params: Params, feature: any) => void) { + ((params: ExtraFuncParams, feature: any) => void) { const calculatedTags: [string, string][] = layer.calculatedTags; if (calculatedTags === undefined) { return undefined; } - const functions: ((params: Params, feature: any) => void)[] = []; + const functions: ((params: ExtraFuncParams, feature: any) => void)[] = []; for (const entry of calculatedTags) { const key = entry[0] const code = entry[1]; if (code === undefined) { continue; } + const func = new Function("feat", "return " + code + ";"); try { - - const f = (featuresPerLayer, feature: any) => { try { let result = func(feature); @@ -132,7 +133,7 @@ export default class MetaTagging { feature.properties[key] = result; } catch (e) { if (MetaTagging.errorPrintCount < MetaTagging.stopErrorOutputAt) { - console.warn("Could not calculate a calculated tag defined by " + code + " due to " + e + ". This is code defined in the theme. Are you the theme creator? Doublecheck your code. Note that the metatags might not be stable on new features", e) + console.warn("Could not calculate a calculated tag defined by " + code + " due to " + e + ". This is code defined in the theme. Are you the theme creator? Doublecheck your code. Note that the metatags might not be stable on new features", e,e.stack) MetaTagging.errorPrintCount++; if (MetaTagging.errorPrintCount == MetaTagging.stopErrorOutputAt) { console.error("Got ", MetaTagging.stopErrorOutputAt, " errors calculating this metatagging - stopping output now") @@ -146,18 +147,18 @@ export default class MetaTagging { console.error("Could not create a dynamic function: ", e) } } - return (params: Params, feature) => { + return (params: ExtraFuncParams, feature) => { const tags = feature.properties if (tags === undefined) { return; } - const relations = params.memberships?.get(feature.properties.id) ?? [] - ExtraFunction.FullPatchFeature(params.featuresPerLayer, relations, feature); + ExtraFunction.FullPatchFeature(params, feature); try { for (const f of functions) { f(params, feature); } + State.state?.allElements?.getEventSourceById(feature.properties.id)?.ping(); } catch (e) { console.error("While calculating a tag value: ", e) } diff --git a/Logic/Osm/Actions/ChangeDescription.ts b/Logic/Osm/Actions/ChangeDescription.ts index f91d794fa0..1787d1b1bb 100644 --- a/Logic/Osm/Actions/ChangeDescription.ts +++ b/Logic/Osm/Actions/ChangeDescription.ts @@ -1,21 +1,38 @@ +import {OsmNode, OsmRelation, OsmWay} from "../OsmObject"; + +/** + * Represents a single change to an object + */ export interface ChangeDescription { + /** + * Identifier of the object + */ type: "node" | "way" | "relation", /** - * Negative for a new objects + * Identifier of the object + * Negative for new objects */ id: number, - /* - v = "" or v = undefined to erase this tag - */ + + /** + * All changes to tags + * v = "" or v = undefined to erase this tag + */ tags?: { k: string, v: string }[], + /** + * A change to the geometry: + * 1) Change of node location + * 2) Change of way geometry + * 3) Change of relation members (untested) + */ changes?: { lat: number, lon: number } | { - // Coordinates are only used for rendering. They should be lon, lat - locations: [number, number][] + // Coordinates are only used for rendering. They should be LAT, LON + coordinates: [number, number][] nodes: number[], } | { members: { type: "node" | "way" | "relation", ref: number, role: string }[] @@ -25,6 +42,26 @@ export interface ChangeDescription { Set to delete the object */ doDelete?: boolean +} - +export class ChangeDescriptionTools{ + + public static getGeojsonGeometry(change: ChangeDescription): any{ + switch (change.type) { + case "node": + const n = new OsmNode(change.id) + n.lat = change.changes["lat"] + n.lon = change.changes["lon"] + return n.asGeoJson().geometry + case "way": + const w = new OsmWay(change.id) + w.nodes = change.changes["nodes"] + w.coordinates = change.changes["coordinates"].map(coor => coor.reverse()) + return w.asGeoJson().geometry + case "relation": + const r = new OsmRelation(change.id) + r.members = change.changes["members"] + return r.asGeoJson().geometry + } + } } \ No newline at end of file diff --git a/Logic/Osm/Actions/ChangeTagAction.ts b/Logic/Osm/Actions/ChangeTagAction.ts index 36bafcbeea..784f4d4dc6 100644 --- a/Logic/Osm/Actions/ChangeTagAction.ts +++ b/Logic/Osm/Actions/ChangeTagAction.ts @@ -37,7 +37,7 @@ export default class ChangeTagAction extends OsmChangeAction { return {k: key.trim(), v: value.trim()}; } - CreateChangeDescriptions(changes: Changes): ChangeDescription [] { + async CreateChangeDescriptions(changes: Changes): Promise { const changedTags: { k: string, v: string }[] = this._tagsFilter.asChange(this._currentTags).map(ChangeTagAction.checkChange) const typeId = this._elementId.split("/") const type = typeId[0] diff --git a/Logic/Osm/Actions/CreateNewNodeAction.ts b/Logic/Osm/Actions/CreateNewNodeAction.ts index 4b127a7dac..4841180bdd 100644 --- a/Logic/Osm/Actions/CreateNewNodeAction.ts +++ b/Logic/Osm/Actions/CreateNewNodeAction.ts @@ -24,10 +24,10 @@ export default class CreateNewNodeAction extends OsmChangeAction { throw "Lat or lon are undefined!" } this._snapOnto = options?.snapOnto; - this._reusePointDistance = options.reusePointWithinMeters ?? 1 + this._reusePointDistance = options?.reusePointWithinMeters ?? 1 } - CreateChangeDescriptions(changes: Changes): ChangeDescription[] { + async CreateChangeDescriptions(changes: Changes): Promise { const id = changes.getNewID() const properties = { id: "node/" + id @@ -97,7 +97,7 @@ export default class CreateNewNodeAction extends OsmChangeAction { type: "way", id: this._snapOnto.id, changes: { - locations: locations, + coordinates: locations, nodes: ids } } diff --git a/Logic/Osm/Actions/DeleteAction.ts b/Logic/Osm/Actions/DeleteAction.ts index 178a269278..68919bd126 100644 --- a/Logic/Osm/Actions/DeleteAction.ts +++ b/Logic/Osm/Actions/DeleteAction.ts @@ -62,7 +62,7 @@ export default class DeleteAction { } State.state.osmConnection.changesetHandler.DeleteElement( obj, - State.state.layoutToUse.data, + State.state.layoutToUse, reason, State.state.allElements, () => { @@ -159,7 +159,7 @@ export default class DeleteAction { canBeDeleted: false, reason: t.notEnoughExperience }) - return; + return true; // unregister this caller! } if (!useTheInternet) { @@ -167,13 +167,14 @@ export default class DeleteAction { } // All right! We have arrived at a point that we should query OSM again to check that the point isn't a part of ways or relations - OsmObject.DownloadReferencingRelations(id).addCallbackAndRunD(rels => { + OsmObject.DownloadReferencingRelations(id).then(rels => { hasRelations.setData(rels.length > 0) }) - OsmObject.DownloadReferencingWays(id).addCallbackAndRunD(ways => { + OsmObject.DownloadReferencingWays(id).then(ways => { hasWays.setData(ways.length > 0) }) + return true; // unregister to only run once }) diff --git a/Logic/Osm/Actions/OsmChangeAction.ts b/Logic/Osm/Actions/OsmChangeAction.ts index 0308ca8f61..784b192ba1 100644 --- a/Logic/Osm/Actions/OsmChangeAction.ts +++ b/Logic/Osm/Actions/OsmChangeAction.ts @@ -17,7 +17,7 @@ export default abstract class OsmChangeAction { return this.CreateChangeDescriptions(changes) } - protected abstract CreateChangeDescriptions(changes: Changes): ChangeDescription[] + protected abstract CreateChangeDescriptions(changes: Changes): Promise } \ No newline at end of file diff --git a/Logic/Osm/Actions/RelationSplitHandler.ts b/Logic/Osm/Actions/RelationSplitHandler.ts new file mode 100644 index 0000000000..42562c035d --- /dev/null +++ b/Logic/Osm/Actions/RelationSplitHandler.ts @@ -0,0 +1,144 @@ +import OsmChangeAction from "./OsmChangeAction"; +import {Changes} from "../Changes"; +import {ChangeDescription} from "./ChangeDescription"; +import {OsmObject, OsmRelation, OsmWay} from "../OsmObject"; + +export interface RelationSplitInput { + relation: OsmRelation, + originalWayId: number, + allWayIdsInOrder: number[], + originalNodes: number[], + allWaysNodesInOrder: number[][] +} + +/** + * When a way is split and this way is part of a relation, the relation should be updated too to have the new segment if relevant. + */ +export default class RelationSplitHandler extends OsmChangeAction { + private readonly _input: RelationSplitInput; + + constructor(input: RelationSplitInput) { + super() + this._input = input; + } + + async CreateChangeDescriptions(changes: Changes): Promise { + return new InPlaceReplacedmentRTSH(this._input).CreateChangeDescriptions(changes) + } + + +} + + +/** + * A simple strategy to split relations: + * -> Download the way members just before and just after the original way + * -> Make sure they are still aligned + * + * Note that the feature might appear multiple times. + */ +export class InPlaceReplacedmentRTSH extends OsmChangeAction { + private readonly _input: RelationSplitInput; + + constructor(input: RelationSplitInput) { + super(); + this._input = input; + } + + /** + * Returns which node should border the member at the given index + */ + private async targetNodeAt(i: number, first: boolean) { + const member = this._input.relation.members[i] + if (member === undefined) { + return undefined + } + if (member.type === "node") { + return member.ref + } + if (member.type === "way") { + const osmWay = await OsmObject.DownloadObjectAsync("way/" + member.ref) + const nodes = osmWay.nodes + if (first) { + return nodes[0] + } else { + return nodes[nodes.length - 1] + } + } + if (member.type === "relation") { + return undefined + } + return undefined; + } + + async CreateChangeDescriptions(changes: Changes): Promise { + + const wayId = this._input.originalWayId + const relation = this._input.relation + const members = relation.members + const originalNodes = this._input.originalNodes; + const firstNode = originalNodes[0] + const lastNode = originalNodes[originalNodes.length - 1] + const newMembers: { type: "node" | "way" | "relation", ref: number, role: string }[] = [] + + for (let i = 0; i < members.length; i++) { + const member = members[i]; + if (member.type !== "way" || member.ref !== wayId) { + newMembers.push(member) + continue; + } + + const nodeIdBefore = await this.targetNodeAt(i - 1, false) + const nodeIdAfter = await this.targetNodeAt(i + 1, true) + + const firstNodeMatches = nodeIdBefore === undefined || nodeIdBefore === firstNode + const lastNodeMatches =nodeIdAfter === undefined || nodeIdAfter === lastNode + + if (firstNodeMatches && lastNodeMatches) { + // We have a classic situation, forward situation + for (const wId of this._input.allWayIdsInOrder) { + newMembers.push({ + ref: wId, + type: "way", + role: member.role + }) + } + continue; + } + + const firstNodeMatchesRev = nodeIdBefore === undefined || nodeIdBefore === lastNode + const lastNodeMatchesRev =nodeIdAfter === undefined || nodeIdAfter === firstNode + if (firstNodeMatchesRev || lastNodeMatchesRev) { + // We (probably) have a reversed situation, backward situation + for (let i1 = this._input.allWayIdsInOrder.length - 1; i1 >= 0; i1--){ + // Iterate BACKWARDS + const wId = this._input.allWayIdsInOrder[i1]; + newMembers.push({ + ref: wId, + type: "way", + role: member.role + }) + } + continue; + } + + // Euhm, allright... Something weird is going on, but let's not care too much + // Lets pretend this is forward going + for (const wId of this._input.allWayIdsInOrder) { + newMembers.push({ + ref: wId, + type: "way", + role: member.role + }) + } + + } + + return [{ + id: relation.id, + type: "relation", + changes: {members: newMembers} + }]; + } + +} \ No newline at end of file diff --git a/Logic/Osm/Actions/RelationSplitlHandler.ts b/Logic/Osm/Actions/RelationSplitlHandler.ts deleted file mode 100644 index 591051fca2..0000000000 --- a/Logic/Osm/Actions/RelationSplitlHandler.ts +++ /dev/null @@ -1,20 +0,0 @@ -/** - * The logic to handle relations after a way within - */ -import OsmChangeAction from "./OsmChangeAction"; -import {Changes} from "../Changes"; -import {ChangeDescription} from "./ChangeDescription"; -import {OsmRelation} from "../OsmObject"; - -export default class RelationSplitlHandler extends OsmChangeAction { - - constructor(partOf: OsmRelation[], newWayIds: number[], originalNodes: number[]) { - super() - } - - CreateChangeDescriptions(changes: Changes): ChangeDescription[] { - return []; - } - - -} \ No newline at end of file diff --git a/Logic/Osm/Actions/SplitAction.ts b/Logic/Osm/Actions/SplitAction.ts index 8ee1731e43..085aab4130 100644 --- a/Logic/Osm/Actions/SplitAction.ts +++ b/Logic/Osm/Actions/SplitAction.ts @@ -1,9 +1,9 @@ -import {OsmRelation, OsmWay} from "../OsmObject"; +import {OsmObject, OsmWay} from "../OsmObject"; import {Changes} from "../Changes"; import {GeoOperations} from "../../GeoOperations"; import OsmChangeAction from "./OsmChangeAction"; import {ChangeDescription} from "./ChangeDescription"; -import RelationSplitlHandler from "./RelationSplitlHandler"; +import RelationSplitHandler from "./RelationSplitHandler"; interface SplitInfo { originalIndex?: number, // or negative for new elements @@ -12,17 +12,18 @@ interface SplitInfo { } export default class SplitAction extends OsmChangeAction { - private readonly roadObject: any; - private readonly osmWay: OsmWay; - private _partOf: OsmRelation[]; - private readonly _splitPoints: any[]; + private readonly wayId: string; + private readonly _splitPointsCoordinates: [number, number] []// lon, lat - constructor(osmWay: OsmWay, wayGeoJson: any, partOf: OsmRelation[], splitPoints: any[]) { + /** + * + * @param wayId + * @param splitPointCoordinates: lon, lat + */ + constructor(wayId: string, splitPointCoordinates: [number, number][]) { super() - this.osmWay = osmWay; - this.roadObject = wayGeoJson; - this._partOf = partOf; - this._splitPoints = splitPoints; + this.wayId = wayId; + this._splitPointsCoordinates = splitPointCoordinates } private static SegmentSplitInfo(splitInfo: SplitInfo[]): SplitInfo[][] { @@ -42,26 +43,16 @@ export default class SplitAction extends OsmChangeAction { return wayParts.filter(wp => wp.length > 0) } - CreateChangeDescriptions(changes: Changes): ChangeDescription[] { - const splitPoints = this._splitPoints - // We mark the new split points with a new id - console.log(splitPoints) - for (const splitPoint of splitPoints) { - splitPoint.properties["_is_split_point"] = true - } - - - const self = this; - const partOf = this._partOf - const originalElement = this.osmWay - const originalNodes = this.osmWay.nodes; + async CreateChangeDescriptions(changes: Changes): Promise { + const originalElement = await OsmObject.DownloadObjectAsync(this.wayId) + const originalNodes = originalElement.nodes; // First, calculate splitpoints and remove points close to one another - const splitInfo = self.CalculateSplitCoordinates(splitPoints) + const splitInfo = this.CalculateSplitCoordinates(originalElement) // Now we have a list with e.g. // [ { originalIndex: 0}, {originalIndex: 1, doSplit: true}, {originalIndex: 2}, {originalIndex: undefined, doSplit: true}, {originalIndex: 3}] - // Lets change 'originalIndex' to the actual node id first: + // Lets change 'originalIndex' to the actual node id first (or assign a new id if needed): for (const element of splitInfo) { if (element.originalIndex >= 0) { element.originalIndex = originalElement.nodes[element.originalIndex] @@ -102,25 +93,30 @@ export default class SplitAction extends OsmChangeAction { }) } - const newWayIds: number[] = [] + // The ids of all the ways (including the original) + const allWayIdsInOrder: number[] = [] + + const allWaysNodesInOrder: number[][] = [] // Lets create OsmWays based on them for (const wayPart of wayParts) { let isOriginal = wayPart === longest if (isOriginal) { // We change the actual element! + const nodeIds = wayPart.map(p => p.originalIndex) changeDescription.push({ type: "way", id: originalElement.id, changes: { - locations: wayPart.map(p => p.lngLat), - nodes: wayPart.map(p => p.originalIndex) + coordinates: wayPart.map(p => p.lngLat), + nodes: nodeIds } }) + allWayIdsInOrder.push(originalElement.id) + allWaysNodesInOrder.push(nodeIds) } else { let id = changes.getNewID(); - newWayIds.push(id) - + // Copy the tags from the original object onto the new const kv = [] for (const k in originalElement.tags) { if (!originalElement.tags.hasOwnProperty(k)) { @@ -131,22 +127,35 @@ export default class SplitAction extends OsmChangeAction { } kv.push({k: k, v: originalElement.tags[k]}) } + const nodeIds = wayPart.map(p => p.originalIndex) changeDescription.push({ type: "way", id: id, tags: kv, changes: { - locations: wayPart.map(p => p.lngLat), - nodes: wayPart.map(p => p.originalIndex) + coordinates: wayPart.map(p => p.lngLat), + nodes: nodeIds } }) - } + allWayIdsInOrder.push(id) + allWaysNodesInOrder.push(nodeIds) + } } // At last, we still have to check that we aren't part of a relation... // At least, the order of the ways is identical, so we can keep the same roles - changeDescription.push(...new RelationSplitlHandler(partOf, newWayIds, originalNodes).CreateChangeDescriptions(changes)) + const relations = await OsmObject.DownloadReferencingRelations(this.wayId) + for (const relation of relations) { + const changDescrs = await new RelationSplitHandler({ + relation: relation, + allWayIdsInOrder: allWayIdsInOrder, + originalNodes: originalNodes, + allWaysNodesInOrder: allWaysNodesInOrder, + originalWayId: originalElement.id + }).CreateChangeDescriptions(changes) + changeDescription.push(...changDescrs) + } // And we have our objects! // Time to upload @@ -158,75 +167,102 @@ export default class SplitAction extends OsmChangeAction { * Calculates the actual points to split * If another point is closer then ~5m, we reuse that point */ - private CalculateSplitCoordinates( - splitPoints: any[], - toleranceInM = 5): SplitInfo[] { + private CalculateSplitCoordinates(osmWay: OsmWay, toleranceInM = 5): SplitInfo[] { + const wayGeoJson = osmWay.asGeoJson() + // Should be [lon, lat][] + const originalPoints : [number, number][] = osmWay.coordinates.map(c => [c[1], c[0]]) + const allPoints: { + // lon, lat + coordinates: [number, number], + isSplitPoint: boolean, + originalIndex?: number, // Original index + dist: number, // Distance from the nearest point on the original line + location: number // Distance from the start of the way + }[] = this._splitPointsCoordinates.map(c => { + // From the turf.js docs: + // The properties object will contain three values: + // - `index`: closest point was found on nth line part, + // - `dist`: distance between pt and the closest point, + // `location`: distance along the line between start and the closest point. + let projected = GeoOperations.nearestPoint(wayGeoJson, c) + // c is lon lat + return ({ + coordinates: c, + isSplitPoint: true, + dist: projected.properties.dist, + location: projected.properties.location + }); + }) - const allPoints = [...splitPoints]; - // We have a bunch of coordinates here: [ [lat, lon], [lat, lon], ...] ... - const originalPoints: [number, number][] = this.roadObject.geometry.coordinates - // We project them onto the line (which should yield pretty much the same point + // We have a bunch of coordinates here: [ [lon, lon], [lat, lon], ...] ... + // We project them onto the line (which should yield pretty much the same point and add them to allPoints for (let i = 0; i < originalPoints.length; i++) { let originalPoint = originalPoints[i]; - let projected = GeoOperations.nearestPoint(this.roadObject, originalPoint) - projected.properties["_is_split_point"] = false - projected.properties["_original_index"] = i - allPoints.push(projected) + let projected = GeoOperations.nearestPoint(wayGeoJson, originalPoint) + allPoints.push({ + coordinates: originalPoint, + isSplitPoint: false, + location: projected.properties.location, + originalIndex: i, + dist: projected.properties.dist + }) } // At this point, we have a list of both the split point and the old points, with some properties to discriminate between them // We sort this list so that the new points are at the same location - allPoints.sort((a, b) => a.properties.location - b.properties.location) + allPoints.sort((a, b) => a.location - b.location) - // When this is done, we check that no now point is too close to an already existing point and no very small segments get created - /* for (let i = allPoints.length - 1; i > 0; i--) { - - const point = allPoints[i]; - if (point.properties._original_index !== undefined) { - // This point is already in OSM - we have to keep it! - continue; - } - - if (i != allPoints.length - 1) { - const prevPoint = allPoints[i + 1] - const diff = Math.abs(point.properties.location - prevPoint.properties.location) * 1000 - if (diff <= toleranceInM) { - // To close to the previous point! We delete this point... - allPoints.splice(i, 1) - // ... and mark the previous point as a split point - prevPoint.properties._is_split_point = true - continue; - } - } - - if (i > 0) { - const nextPoint = allPoints[i - 1] - const diff = Math.abs(point.properties.location - nextPoint.properties.location) * 1000 - if (diff <= toleranceInM) { - // To close to the next point! We delete this point... - allPoints.splice(i, 1) - // ... and mark the next point as a split point - nextPoint.properties._is_split_point = true - // noinspection UnnecessaryContinueJS - continue; - } - } - // We don't have to remove this point... - }*/ + for (let i = allPoints.length - 2; i >= 1; i--) { + // We 'merge' points with already existing nodes if they are close enough to avoid closeby elements + + // Note the loop bounds: we skip the first two and last two elements: + // The first and last element are always part of the original way and should be kept + // Furthermore, we run in reverse order as we'll delete elements on the go + + const point = allPoints[i] + if (point.originalIndex !== undefined) { + // We keep the original points + continue + } + if (point.dist * 1000 >= toleranceInM) { + // No need to remove this one + continue + } + + // At this point, 'dist' told us the point is pretty close to an already existing point. + // Lets see which (already existing) point is closer and mark it as splitpoint + const nextPoint = allPoints[i + 1] + const prevPoint = allPoints[i - 1] + const distToNext = nextPoint.location - point.location + const distToPrev = prevPoint.location - point.location + let closest = nextPoint + if (distToNext > distToPrev) { + closest = prevPoint + } + // Ok, we have a closest point! + + if(closest.originalIndex === 0 || closest.originalIndex === originalPoints.length){ + // We can not split on the first or last points... + continue + } + closest.isSplitPoint = true; + allPoints.splice(i, 1) + + } const splitInfo: SplitInfo[] = [] - let nextId = -1 + let nextId = -1 // Note: these IDs are overwritten later on, no need to use a global counter here for (const p of allPoints) { - let index = p.properties._original_index + let index = p.originalIndex if (index === undefined) { index = nextId; nextId--; } const splitInfoElement = { originalIndex: index, - lngLat: p.geometry.coordinates, - doSplit: p.properties._is_split_point + lngLat: p.coordinates, + doSplit: p.isSplitPoint } splitInfo.push(splitInfoElement) } diff --git a/Logic/Osm/Changes.ts b/Logic/Osm/Changes.ts index f36ffa4e0e..866c78c7a2 100644 --- a/Logic/Osm/Changes.ts +++ b/Logic/Osm/Changes.ts @@ -14,19 +14,27 @@ import {LocalStorageSource} from "../Web/LocalStorageSource"; export class Changes { - private static _nextId = -1; // Newly assigned ID's are negative + private _nextId: number = -1; // Newly assigned ID's are negative public readonly name = "Newly added features" /** * All the newly created features as featureSource + all the modified features */ public features = new UIEventSource<{ feature: any, freshness: Date }[]>([]); - public readonly pendingChanges = LocalStorageSource.GetParsed("pending-changes", []) + public readonly pendingChanges: UIEventSource = LocalStorageSource.GetParsed("pending-changes", []) + public readonly allChanges = new UIEventSource(undefined) private readonly isUploading = new UIEventSource(false); private readonly previouslyCreated: OsmObject[] = [] constructor() { + // We keep track of all changes just as well + this.allChanges.setData([...this.pendingChanges.data]) + // If a pending change contains a negative ID, we save that + this._nextId = Math.min(-1, ...this.pendingChanges.data?.map(pch => pch.id) ?? []) + + // Note: a changeset might be reused which was opened just before and might have already used some ids + // This doesn't matter however, as the '-1' is per piecewise upload, not global per changeset } private static createChangesetFor(csId: string, @@ -74,7 +82,7 @@ export class Changes { * Returns a new ID and updates the value for the next ID */ public getNewID() { - return Changes._nextId--; + return this._nextId--; } /** @@ -85,71 +93,69 @@ export class Changes { if (this.pendingChanges.data.length === 0) { return; } - if (this.isUploading.data) { console.log("Is already uploading... Abort") return; } - - this.isUploading.setData(true) - console.log("Beginning upload... " + flushreason ?? ""); - // At last, we build the changeset and upload + this.flushChangesAsync(flushreason) + .then(_ => { + this.isUploading.setData(false) + console.log("Changes flushed!"); + }) + .catch(e => { + this.isUploading.setData(false) + console.error("Flushing changes failed due to", e); + }) + } + + private async flushChangesAsync(flushreason: string = undefined): Promise { const self = this; - const pending = self.pendingChanges.data; - const neededIds = Changes.GetNeededIds(pending) - console.log("Needed ids", neededIds) - OsmObject.DownloadAll(neededIds, true).addCallbackAndRunD(osmObjects => { + try { + console.log("Beginning upload... " + flushreason ?? ""); + // At last, we build the changeset and upload + const pending = self.pendingChanges.data; + const neededIds = Changes.GetNeededIds(pending) + const osmObjects = await Promise.all(neededIds.map(id => OsmObject.DownloadObjectAsync(id))); console.log("Got the fresh objects!", osmObjects, "pending: ", pending) - try { - - - const changes: { - newObjects: OsmObject[], - modifiedObjects: OsmObject[] - deletedObjects: OsmObject[] - - } = self.CreateChangesetObjects(pending, osmObjects) - if (changes.newObjects.length + changes.deletedObjects.length + changes.modifiedObjects.length === 0) { - console.log("No changes to be made") - self.pendingChanges.setData([]) - self.isUploading.setData(false) - return true; // Unregister the callback - } - - - State.state.osmConnection.UploadChangeset( - State.state.layoutToUse.data, - State.state.allElements, - (csId) => Changes.createChangesetFor(csId, changes), - () => { - console.log("Upload successfull!") - self.pendingChanges.setData([]); - self.isUploading.setData(false) - }, - () => { - console.log("Upload failed - trying again later") - return self.isUploading.setData(false); - } // Failed - mark to try again - ) - } catch (e) { - console.error("Could not handle changes - probably an old, pending changeset in localstorage with an invalid format; erasing those", e) + const changes: { + newObjects: OsmObject[], + modifiedObjects: OsmObject[] + deletedObjects: OsmObject[] + } = self.CreateChangesetObjects(pending, osmObjects) + if (changes.newObjects.length + changes.deletedObjects.length + changes.modifiedObjects.length === 0) { + console.log("No changes to be made") self.pendingChanges.setData([]) self.isUploading.setData(false) } - return true; - }); + await State.state.osmConnection.UploadChangeset( + State.state.layoutToUse, + State.state.allElements, + (csId) => Changes.createChangesetFor(csId, changes), + ) + + console.log("Upload successfull!") + this.pendingChanges.setData([]); + this.isUploading.setData(false) + + } catch (e) { + console.error("Could not handle changes - probably an old, pending changeset in localstorage with an invalid format; erasing those", e) + self.pendingChanges.setData([]) + self.isUploading.setData(false) + } } - public applyAction(action: OsmChangeAction) { - const changes = action.Perform(this) + public async applyAction(action: OsmChangeAction): Promise { + const changes = await action.Perform(this) console.log("Received changes:", changes) this.pendingChanges.data.push(...changes); this.pendingChanges.ping(); + this.allChanges.data.push(...changes) + this.allChanges.ping() } private CreateChangesetObjects(changes: ChangeDescription[], downloadedOsmObjects: OsmObject[]): { @@ -303,4 +309,8 @@ export class Changes { return result } + + public registerIdRewrites(mappings: Map): void { + + } } \ No newline at end of file diff --git a/Logic/Osm/ChangesetHandler.ts b/Logic/Osm/ChangesetHandler.ts index dcf0d135aa..aae51d4eee 100644 --- a/Logic/Osm/ChangesetHandler.ts +++ b/Logic/Osm/ChangesetHandler.ts @@ -8,15 +8,23 @@ import Locale from "../../UI/i18n/Locale"; import Constants from "../../Models/Constants"; import {OsmObject} from "./OsmObject"; import LayoutConfig from "../../Models/ThemeConfig/LayoutConfig"; +import {Changes} from "./Changes"; export class ChangesetHandler { public readonly currentChangeset: UIEventSource; + private readonly allElements: ElementStorage; + private readonly changes: Changes; private readonly _dryRun: boolean; private readonly userDetails: UIEventSource; private readonly auth: any; - constructor(layoutName: string, dryRun: boolean, osmConnection: OsmConnection, auth) { + constructor(layoutName: string, dryRun: boolean, osmConnection: OsmConnection, + allElements: ElementStorage, + changes: Changes, + auth) { + this.allElements = allElements; + this.changes = changes; this._dryRun = dryRun; this.userDetails = osmConnection.userDetails; this.auth = auth; @@ -27,35 +35,55 @@ export class ChangesetHandler { } } - private static parseUploadChangesetResponse(response: XMLDocument, allElements: ElementStorage): void { + private handleIdRewrite(node: any, type: string): [string, string] { + const oldId = parseInt(node.attributes.old_id.value); + if (node.attributes.new_id === undefined) { + // We just removed this point! + const element =this. allElements.getEventSourceById("node/" + oldId); + element.data._deleted = "yes" + element.ping(); + return; + } + + const newId = parseInt(node.attributes.new_id.value); + const result: [string, string] = [type + "/" + oldId, type + "/" + newId] + if (!(oldId !== undefined && newId !== undefined && + !isNaN(oldId) && !isNaN(newId))) { + return undefined; + } + if (oldId == newId) { + return undefined; + } + console.log("Rewriting id: ", type + "/" + oldId, "-->", type + "/" + newId); + const element = this.allElements.getEventSourceById("node/" + oldId); + element.data.id = type + "/" + newId; + this.allElements.addElementById(type + "/" + newId, element); + this.allElements.ContainingFeatures.set(type + "/" + newId, this.allElements.ContainingFeatures.get(type + "/" + oldId)) + element.ping(); + return result; + } + + private parseUploadChangesetResponse(response: XMLDocument): void { const nodes = response.getElementsByTagName("node"); + const mappings = new Map() // @ts-ignore for (const node of nodes) { - const oldId = parseInt(node.attributes.old_id.value); - if (node.attributes.new_id === undefined) { - // We just removed this point! - const element = allElements.getEventSourceById("node/" + oldId); - element.data._deleted = "yes" - element.ping(); - continue; + const mapping = this.handleIdRewrite(node, "node") + if (mapping !== undefined) { + mappings.set(mapping[0], mapping[1]) } - - const newId = parseInt(node.attributes.new_id.value); - if (oldId !== undefined && newId !== undefined && - !isNaN(oldId) && !isNaN(newId)) { - if (oldId == newId) { - continue; - } - console.log("Rewriting id: ", oldId, "-->", newId); - const element = allElements.getEventSourceById("node/" + oldId); - element.data.id = "node/" + newId; - allElements.addElementById("node/" + newId, element); - element.ping(); - - } - - } + + const ways = response.getElementsByTagName("way"); + // @ts-ignore + for (const way of ways) { + const mapping = this.handleIdRewrite(way, "way") + if (mapping !== undefined) { + mappings.set(mapping[0], mapping[1]) + } + } + this.changes.registerIdRewrites(mappings) + } /** @@ -68,13 +96,9 @@ export class ChangesetHandler { * If 'dryrun' is specified, the changeset XML will be printed to console instead of being uploaded * */ - public UploadChangeset( + public async UploadChangeset( layout: LayoutConfig, - allElements: ElementStorage, - generateChangeXML: (csid: string) => string, - whenDone: (csId: string) => void, - onFail: () => void) { - + generateChangeXML: (csid: string) => string): Promise { if (this.userDetails.data.csCount == 0) { // The user became a contributor! this.userDetails.data.csCount = 1; @@ -84,46 +108,36 @@ export class ChangesetHandler { if (this._dryRun) { const changesetXML = generateChangeXML("123456"); console.log(changesetXML); - whenDone("123456") return; } - const self = this; - if (this.currentChangeset.data === undefined || this.currentChangeset.data === "") { // We have to open a new changeset - this.OpenChangeset(layout, (csId) => { + try { + const csId = await this.OpenChangeset(layout) this.currentChangeset.setData(csId); const changeset = generateChangeXML(csId); - console.log(changeset); - self.AddChange(csId, changeset, - allElements, - whenDone, - (e) => { - console.error("UPLOADING FAILED!", e) - onFail() - } - ) - }, { - onFail: onFail - }) + console.log("Current changeset is:", changeset); + await this.AddChange(csId, changeset) + } catch (e) { + console.error("Could not open/upload changeset due to ", e) + this.currentChangeset.setData("") + } } else { // There still exists an open changeset (or at least we hope so) const csId = this.currentChangeset.data; - self.AddChange( - csId, - generateChangeXML(csId), - allElements, - whenDone, - (e) => { - console.warn("Could not upload, changeset is probably closed: ", e); - // Mark the CS as closed... - this.currentChangeset.setData(""); - // ... and try again. As the cs is closed, no recursive loop can exist - self.UploadChangeset(layout, allElements, generateChangeXML, whenDone, onFail); - } - ) + try { + await this.AddChange( + csId, + generateChangeXML(csId)) + } catch (e) { + console.warn("Could not upload, changeset is probably closed: ", e); + // Mark the CS as closed... + this.currentChangeset.setData(""); + // ... and try again. As the cs is closed, no recursive loop can exist + await this.UploadChangeset(layout, generateChangeXML) + } } } @@ -143,6 +157,13 @@ export class ChangesetHandler { reason: string, allElements: ElementStorage, continuation: () => void) { + return this.DeleteElementAsync(object, layout, reason, allElements).then(continuation) + } + + public async DeleteElementAsync(object: OsmObject, + layout: LayoutConfig, + reason: string, + allElements: ElementStorage): Promise { function generateChangeXML(csId: string) { let [lat, lon] = object.centerpoint(); @@ -151,9 +172,7 @@ export class ChangesetHandler { changes += `<${object.type} id="${object.id}" version="${object.version}" changeset="${csId}" lat="${lat}" lon="${lon}" />`; changes += ""; - continuation() return changes; - } @@ -163,143 +182,122 @@ export class ChangesetHandler { return; } - const self = this; - this.OpenChangeset(layout, (csId: string) => { - - // The cs is open - let us actually upload! - const changes = generateChangeXML(csId) - - self.AddChange(csId, changes, allElements, (csId) => { - console.log("Successfully deleted ", object.id) - self.CloseChangeset(csId, continuation) - }, (csId) => { - alert("Deletion failed... Should not happend") - // FAILED - self.CloseChangeset(csId, continuation) - }) - }, { - isDeletionCS: true, - deletionReason: reason - } - ) + const csId = await this.OpenChangeset(layout, { + isDeletionCS: true, + deletionReason: reason + }) + // The cs is open - let us actually upload! + const changes = generateChangeXML(csId) + await this.AddChange(csId, changes) + await this.CloseChangeset(csId) } - private CloseChangeset(changesetId: string = undefined, continuation: (() => void) = () => { - }) { - if (changesetId === undefined) { - changesetId = this.currentChangeset.data; - } - if (changesetId === undefined) { - return; - } - console.log("closing changeset", changesetId); - this.currentChangeset.setData(""); - this.auth.xhr({ - method: 'PUT', - path: '/api/0.6/changeset/' + changesetId + '/close', - }, function (err, response) { - if (response == null) { - - console.log("err", err); + private async CloseChangeset(changesetId: string = undefined): Promise { + const self = this + return new Promise(function (resolve, reject) { + if (changesetId === undefined) { + changesetId = self.currentChangeset.data; } - console.log("Closed changeset ", changesetId) - - if (continuation !== undefined) { - continuation(); + if (changesetId === undefined) { + return; } - }); + console.log("closing changeset", changesetId); + self.currentChangeset.setData(""); + self.auth.xhr({ + method: 'PUT', + path: '/api/0.6/changeset/' + changesetId + '/close', + }, function (err, response) { + if (response == null) { + + console.log("err", err); + } + console.log("Closed changeset ", changesetId) + resolve() + }); + }) } private OpenChangeset( layout: LayoutConfig, - continuation: (changesetId: string) => void, options?: { isDeletionCS?: boolean, deletionReason?: string, - onFail?: () => void } - ) { - options = options ?? {} - options.isDeletionCS = options.isDeletionCS ?? false - const commentExtra = layout.changesetmessage !== undefined ? " - " + layout.changesetmessage : ""; - let comment = `Adding data with #MapComplete for theme #${layout.id}${commentExtra}` - if (options.isDeletionCS) { - comment = `Deleting a point with #MapComplete for theme #${layout.id}${commentExtra}` - if (options.deletionReason) { - comment += ": " + options.deletionReason; - } - } - - let path = window.location.pathname; - path = path.substr(1, path.lastIndexOf("/")); - const metadata = [ - ["created_by", `MapComplete ${Constants.vNumber}`], - ["comment", comment], - ["deletion", options.isDeletionCS ? "yes" : undefined], - ["theme", layout.id], - ["language", Locale.language.data], - ["host", window.location.host], - ["path", path], - ["source", State.state.currentGPSLocation.data !== undefined ? "survey" : undefined], - ["imagery", State.state.backgroundLayer.data.id], - ["theme-creator", layout.maintainer] - ] - .filter(kv => (kv[1] ?? "") !== "") - .map(kv => ``) - .join("\n") - - this.auth.xhr({ - method: 'PUT', - path: '/api/0.6/changeset/create', - options: {header: {'Content-Type': 'text/xml'}}, - content: [``, - metadata, - ``].join("") - }, function (err, response) { - if (response === undefined) { - console.log("err", err); - if (options.onFail) { - options.onFail() + ): Promise { + const self = this; + return new Promise(function (resolve, reject) { + options = options ?? {} + options.isDeletionCS = options.isDeletionCS ?? false + const commentExtra = layout.changesetmessage !== undefined ? " - " + layout.changesetmessage : ""; + let comment = `Adding data with #MapComplete for theme #${layout.id}${commentExtra}` + if (options.isDeletionCS) { + comment = `Deleting a point with #MapComplete for theme #${layout.id}${commentExtra}` + if (options.deletionReason) { + comment += ": " + options.deletionReason; } - return; - } else { - continuation(response); } - }); + + let path = window.location.pathname; + path = path.substr(1, path.lastIndexOf("/")); + const metadata = [ + ["created_by", `MapComplete ${Constants.vNumber}`], + ["comment", comment], + ["deletion", options.isDeletionCS ? "yes" : undefined], + ["theme", layout.id], + ["language", Locale.language.data], + ["host", window.location.host], + ["path", path], + ["source", State.state.currentGPSLocation.data !== undefined ? "survey" : undefined], + ["imagery", State.state.backgroundLayer.data.id], + ["theme-creator", layout.maintainer] + ] + .filter(kv => (kv[1] ?? "") !== "") + .map(kv => ``) + .join("\n") + + + self.auth.xhr({ + method: 'PUT', + path: '/api/0.6/changeset/create', + options: {header: {'Content-Type': 'text/xml'}}, + content: [``, + metadata, + ``].join("") + }, function (err, response) { + if (response === undefined) { + console.log("err", err); + reject(err) + } else { + resolve(response); + } + }); + }) + } /** * Upload a changesetXML - * @param changesetId - * @param changesetXML - * @param allElements - * @param continuation - * @param onFail - * @constructor - * @private */ private AddChange(changesetId: string, - changesetXML: string, - allElements: ElementStorage, - continuation: ((changesetId: string) => void), - onFail: ((changesetId: string, reason: string) => void) = undefined) { - this.auth.xhr({ - method: 'POST', - options: {header: {'Content-Type': 'text/xml'}}, - path: '/api/0.6/changeset/' + changesetId + '/upload', - content: changesetXML - }, function (err, response) { - if (response == null) { - console.log("err", err); - if (onFail) { - onFail(changesetId, err); + changesetXML: string): Promise { + const self = this; + return new Promise(function (resolve, reject) { + self.auth.xhr({ + method: 'POST', + options: {header: {'Content-Type': 'text/xml'}}, + path: '/api/0.6/changeset/' + changesetId + '/upload', + content: changesetXML + }, function (err, response) { + if (response == null) { + console.log("err", err); + reject(err); } - return; - } - ChangesetHandler.parseUploadChangesetResponse(response, allElements); - console.log("Uploaded changeset ", changesetId); - continuation(changesetId); - }); + self.parseUploadChangesetResponse(response); + console.log("Uploaded changeset ", changesetId); + resolve(changesetId); + }); + }) + } diff --git a/Logic/Osm/Geocoding.ts b/Logic/Osm/Geocoding.ts index 44915d4e12..55dd996817 100644 --- a/Logic/Osm/Geocoding.ts +++ b/Logic/Osm/Geocoding.ts @@ -11,7 +11,7 @@ export class Geocoding { osm_type: string, osm_id: string }[]) => void), onFail: (() => void)) { - const b = State.state.leafletMap.data.getBounds(); + const b = State.state.currentBounds.data; const url = Geocoding.host + "format=json&limit=1&viewbox=" + `${b.getEast()},${b.getNorth()},${b.getWest()},${b.getSouth()}` + "&accept-language=nl&q=" + query; diff --git a/Logic/Osm/OsmConnection.ts b/Logic/Osm/OsmConnection.ts index 01d18f8cd8..4e1a9297a3 100644 --- a/Logic/Osm/OsmConnection.ts +++ b/Logic/Osm/OsmConnection.ts @@ -9,6 +9,7 @@ import Img from "../../UI/Base/Img"; import {Utils} from "../../Utils"; import {OsmObject} from "./OsmObject"; import LayoutConfig from "../../Models/ThemeConfig/LayoutConfig"; +import {Changes} from "./Changes"; export default class UserDetails { @@ -54,31 +55,33 @@ export class OsmConnection { private _onLoggedIn: ((userDetails: UserDetails) => void)[] = []; private readonly _iframeMode: Boolean | boolean; private readonly _singlePage: boolean; - private readonly _oauth_config: { + public readonly _oauth_config: { oauth_consumer_key: string, oauth_secret: string, url: string }; private isChecking = false; - constructor(dryRun: boolean, - fakeUser: boolean, - oauth_token: UIEventSource, + constructor(options:{dryRun?: false | boolean, + fakeUser?: false | boolean, + allElements: ElementStorage, + changes: Changes, + oauth_token?: UIEventSource, // Used to keep multiple changesets open and to write to the correct changeset layoutName: string, - singlePage: boolean = true, - osmConfiguration: "osm" | "osm-test" = 'osm' + singlePage?: boolean, + osmConfiguration?: "osm" | "osm-test" } ) { - this.fakeUser = fakeUser; - this._singlePage = singlePage; - this._oauth_config = OsmConnection.oauth_configs[osmConfiguration] ?? OsmConnection.oauth_configs.osm; + this.fakeUser = options.fakeUser ?? false; + this._singlePage = options.singlePage ?? true; + this._oauth_config = OsmConnection.oauth_configs[options.osmConfiguration ?? 'osm'] ?? OsmConnection.oauth_configs.osm; console.debug("Using backend", this._oauth_config.url) OsmObject.SetBackendUrl(this._oauth_config.url + "/") this._iframeMode = Utils.runningFromConsole ? false : window !== window.top; this.userDetails = new UIEventSource(new UserDetails(this._oauth_config.url), "userDetails"); - this.userDetails.data.dryRun = dryRun || fakeUser; - if (fakeUser) { + this.userDetails.data.dryRun = (options.dryRun ?? false) || (options.fakeUser ?? false) ; + if (options.fakeUser) { const ud = this.userDetails.data; ud.csCount = 5678 ud.loggedIn = true; @@ -94,23 +97,24 @@ export class OsmConnection { self.AttemptLogin() } }); - this._dryRun = dryRun; + this.isLoggedIn.addCallbackAndRunD(li => console.log("User is logged in!", li)) + this._dryRun = options.dryRun; this.updateAuthObject(); this.preferencesHandler = new OsmPreferences(this.auth, this); - this.changesetHandler = new ChangesetHandler(layoutName, dryRun, this, this.auth); - if (oauth_token.data !== undefined) { - console.log(oauth_token.data) + this.changesetHandler = new ChangesetHandler(options.layoutName, options.dryRun, this, options.allElements, options.changes, this.auth); + if (options.oauth_token?.data !== undefined) { + console.log(options.oauth_token.data) const self = this; - this.auth.bootstrapToken(oauth_token.data, + this.auth.bootstrapToken(options.oauth_token.data, (x) => { console.log("Called back: ", x) self.AttemptLogin(); }, this.auth); - oauth_token.setData(undefined); + options. oauth_token.setData(undefined); } if (this.auth.authenticated()) { @@ -123,10 +127,8 @@ export class OsmConnection { public UploadChangeset( layout: LayoutConfig, allElements: ElementStorage, - generateChangeXML: (csid: string) => string, - whenDone: (csId: string) => void, - onFail: () => {}) { - this.changesetHandler.UploadChangeset(layout, allElements, generateChangeXML, whenDone, onFail); + generateChangeXML: (csid: string) => string): Promise { + return this.changesetHandler.UploadChangeset(layout, generateChangeXML); } public GetPreference(key: string, prefix: string = "mapcomplete-"): UIEventSource { diff --git a/Logic/Osm/OsmObject.ts b/Logic/Osm/OsmObject.ts index a09c1b25ce..9c1efbae98 100644 --- a/Logic/Osm/OsmObject.ts +++ b/Logic/Osm/OsmObject.ts @@ -1,6 +1,7 @@ import {Utils} from "../../Utils"; import * as polygon_features from "../../assets/polygon-features.json"; import {UIEventSource} from "../UIEventSource"; +import {BBox} from "../BBox"; export abstract class OsmObject { @@ -9,11 +10,12 @@ export abstract class OsmObject { protected static backendURL = OsmObject.defaultBackend; private static polygonFeatures = OsmObject.constructPolygonFeatures() private static objectCache = new Map>(); - private static referencingWaysCache = new Map>(); - private static referencingRelationsCache = new Map>(); private static historyCache = new Map>(); type: string; id: number; + /** + * The OSM tags as simple object + */ tags: {} = {}; version: number; public changed: boolean = false; @@ -37,7 +39,7 @@ export abstract class OsmObject { this.backendURL = url; } - static DownloadObject(id: string, forceRefresh: boolean = false): UIEventSource { + public static DownloadObject(id: string, forceRefresh: boolean = false): UIEventSource { let src: UIEventSource; if (OsmObject.objectCache.has(id)) { src = OsmObject.objectCache.get(id) @@ -47,80 +49,71 @@ export abstract class OsmObject { return src; } } else { - src = new UIEventSource(undefined) + src = UIEventSource.FromPromise(OsmObject.DownloadObjectAsync(id)) } + + OsmObject.objectCache.set(id, src); + return src; + } + + static async DownloadObjectAsync(id: string): Promise { const splitted = id.split("/"); const type = splitted[0]; const idN = Number(splitted[1]); if (idN < 0) { - return; + return undefined; } - OsmObject.objectCache.set(id, src); - const newContinuation = (element: OsmObject) => { - src.setData(element) + const full = !id.startsWith("way") ? "" : "/full"; + const url = `${OsmObject.backendURL}api/0.6/${id}${full}`; + const rawData = await Utils.downloadJson(url) + // A full query might contain more then just the requested object (e.g. nodes that are part of a way, where we only want the way) + const parsed = OsmObject.ParseObjects(rawData.elements); + // Lets fetch the object we need + for (const osmObject of parsed) { + if(osmObject.type !== type){ + continue; + } + if(osmObject.id !== idN){ + continue + } + // Found the one! + return osmObject } - - switch (type) { - case("node"): - new OsmNode(idN).Download(newContinuation); - break; - case("way"): - new OsmWay(idN).Download(newContinuation); - break; - case("relation"): - new OsmRelation(idN).Download(newContinuation); - break; - default: - throw "Invalid object type:" + type + id; - - } - return src; + throw "PANIC: requested object is not part of the response" + + } + /** * Downloads the ways that are using this node. * Beware: their geometry will be incomplete! */ - public static DownloadReferencingWays(id: string): UIEventSource { - if (OsmObject.referencingWaysCache.has(id)) { - return OsmObject.referencingWaysCache.get(id); - } - const waysSrc = new UIEventSource([]) - OsmObject.referencingWaysCache.set(id, waysSrc); - Utils.downloadJson(`${OsmObject.backendURL}api/0.6/${id}/ways`) - .then(data => { - const ways = data.elements.map(wayInfo => { + public static DownloadReferencingWays(id: string): Promise { + return Utils.downloadJson(`${OsmObject.backendURL}api/0.6/${id}/ways`).then( + data => { + return data.elements.map(wayInfo => { const way = new OsmWay(wayInfo.id) way.LoadData(wayInfo) return way }) - waysSrc.setData(ways) - }) - return waysSrc; + } + ) } /** * Downloads the relations that are using this feature. * Beware: their geometry will be incomplete! */ - public static DownloadReferencingRelations(id: string): UIEventSource { - if (OsmObject.referencingRelationsCache.has(id)) { - return OsmObject.referencingRelationsCache.get(id); - } - const relsSrc = new UIEventSource(undefined) - OsmObject.referencingRelationsCache.set(id, relsSrc); - Utils.downloadJson(`${OsmObject.backendURL}api/0.6/${id}/relations`) - .then(data => { - const rels = data.elements.map(wayInfo => { - const rel = new OsmRelation(wayInfo.id) - rel.LoadData(wayInfo) - rel.SaveExtraData(wayInfo) - return rel - }) - relsSrc.setData(rels) - }) - return relsSrc; + public static async DownloadReferencingRelations(id: string): Promise { + const data = await Utils.downloadJson(`${OsmObject.backendURL}api/0.6/${id}/relations`) + return data.elements.map(wayInfo => { + const rel = new OsmRelation(wayInfo.id) + rel.LoadData(wayInfo) + rel.SaveExtraData(wayInfo) + return rel + }) } public static DownloadHistory(id: string): UIEventSource { @@ -158,36 +151,12 @@ export abstract class OsmObject { } // bounds should be: [[maxlat, minlon], [minlat, maxlon]] (same as Utils.tile_bounds) - public static LoadArea(bounds: [[number, number], [number, number]], callback: (objects: OsmObject[]) => void) { - const minlon = bounds[0][1] - const maxlon = bounds[1][1] - const minlat = bounds[1][0] - const maxlat = bounds[0][0]; - const url = `${OsmObject.backendURL}api/0.6/map.json?bbox=${minlon},${minlat},${maxlon},${maxlat}` - Utils.downloadJson(url).then(data => { - const elements: any[] = data.elements; - const objects = OsmObject.ParseObjects(elements) - callback(objects); - - }) + public static async LoadArea(bbox: BBox): Promise { + const url = `${OsmObject.backendURL}api/0.6/map.json?bbox=${bbox.minLon},${bbox.minLat},${bbox.maxLon},${bbox.maxLat}` + const data = await Utils.downloadJson(url) + const elements: any[] = data.elements; + return OsmObject.ParseObjects(elements); } - - public static DownloadAll(neededIds, forceRefresh = true): UIEventSource { - // local function which downloads all the objects one by one - // this is one big loop, running one download, then rerunning the entire function - - const allSources: UIEventSource [] = neededIds.map(id => OsmObject.DownloadObject(id, forceRefresh)) - const allCompleted = new UIEventSource(undefined).map(_ => { - return !allSources.some(uiEventSource => uiEventSource.data === undefined) - }, allSources) - return allCompleted.map(completed => { - if (completed) { - return allSources.map(src => src.data) - } - return undefined - }); - } - protected static isPolygon(tags: any): boolean { for (const tagsKey in tags) { if (!tags.hasOwnProperty(tagsKey)) { @@ -208,7 +177,6 @@ export abstract class OsmObject { private static constructPolygonFeatures(): Map, blacklist: boolean }> { const result = new Map, blacklist: boolean }>(); - for (const polygonFeature of polygon_features) { const key = polygonFeature.key; @@ -228,6 +196,7 @@ export abstract class OsmObject { private static ParseObjects(elements: any[]): OsmObject[] { const objects: OsmObject[] = []; const allNodes: Map = new Map() + for (const element of elements) { const type = element.type; const idN = element.id; @@ -249,6 +218,11 @@ export abstract class OsmObject { osmObject.SaveExtraData(element, []) break; } + + if (osmObject !== undefined && OsmObject.backendURL !== OsmObject.defaultBackend) { + osmObject.tags["_backend"] = OsmObject.backendURL + } + osmObject?.LoadData(element) objects.push(osmObject) } @@ -260,7 +234,7 @@ export abstract class OsmObject { public abstract asGeoJson(): any; - abstract SaveExtraData(element: any, allElements: any[]); + abstract SaveExtraData(element: any, allElements: OsmObject[]); /** * Generates the changeset-XML for tags @@ -283,42 +257,6 @@ export abstract class OsmObject { return tags; } - Download(continuation: ((element: OsmObject, meta: OsmObjectMeta) => void)) { - const self = this; - const full = this.type !== "way" ? "" : "/full"; - const url = `${OsmObject.backendURL}api/0.6/${this.type}/${this.id}${full}`; - Utils.downloadJson(url).then(data => { - - const element = data.elements.pop(); - - let nodes = [] - if (self.type === "way" && data.elements.length >= 0) { - nodes = OsmObject.ParseObjects(data.elements) - } - - self.LoadData(element) - self.SaveExtraData(element, nodes); - - const meta = { - "_last_edit:contributor": element.user, - "_last_edit:contributor:uid": element.uid, - "_last_edit:changeset": element.changeset, - "_last_edit:timestamp": new Date(element.timestamp), - "_version_number": element.version - } - - if (OsmObject.backendURL !== OsmObject.defaultBackend) { - self.tags["_backend"] = OsmObject.backendURL - meta["_backend"] = OsmObject.backendURL; - } - - continuation(self, meta); - } - ); - return this; - } - - abstract ChangesetXML(changesetId: string): string; protected VersionXML() { @@ -389,18 +327,10 @@ export class OsmNode extends OsmObject { } } -export interface OsmObjectMeta { - "_last_edit:contributor": string, - "_last_edit:contributor:uid": number, - "_last_edit:changeset": number, - "_last_edit:timestamp": Date, - "_version_number": number - -} - export class OsmWay extends OsmObject { - nodes: number[]; + nodes: number[] = []; + // The coordinates of the way, [lat, lon][] coordinates: [number, number][] = [] lat: number; lon: number; @@ -436,6 +366,10 @@ export class OsmWay extends OsmObject { nodeDict.set(node.id, node) } + if (element.nodes === undefined) { + console.log("PANIC") + } + for (const nodeId of element.nodes) { const node = nodeDict.get(nodeId) if (node === undefined) { @@ -455,12 +389,16 @@ export class OsmWay extends OsmObject { } public asGeoJson() { + let coordinates: ([number, number][] | [number, number][][]) = this.coordinates.map(c => [c[1], c[0]]); + if (this.isPolygon()) { + coordinates = [coordinates] + } return { "type": "Feature", "properties": this.tags, "geometry": { "type": this.isPolygon() ? "Polygon" : "LineString", - "coordinates": this.coordinates.map(c => [c[1], c[0]]) + "coordinates": coordinates } } } @@ -511,7 +449,7 @@ ${members}${tags} this.members = element.members; } - asGeoJson() { + asGeoJson(): any { throw "Not Implemented" } } \ No newline at end of file diff --git a/Logic/Osm/OsmPreferences.ts b/Logic/Osm/OsmPreferences.ts index 291b49a4fc..fadd9c1059 100644 --- a/Logic/Osm/OsmPreferences.ts +++ b/Logic/Osm/OsmPreferences.ts @@ -145,14 +145,14 @@ export class OsmPreferences { private SetPreference(k: string, v: string) { if (!this.userDetails.data.loggedIn) { - console.log(`Not saving preference ${k}: user not logged in`); + console.debug(`Not saving preference ${k}: user not logged in`); return; } if (this.preferences.data[k] === v) { return; } - console.log("Updating preference", k, " to ", Utils.EllipsesAfter(v, 15)); + console.debug("Updating preference", k, " to ", Utils.EllipsesAfter(v, 15)); if (v === undefined || v === "") { this.auth.xhr({ @@ -161,10 +161,10 @@ export class OsmPreferences { options: {header: {'Content-Type': 'text/plain'}}, }, function (error) { if (error) { - console.log("Could not remove preference", error); + console.warn("Could not remove preference", error); return; } - console.log("Preference ", k, "removed!"); + console.debug("Preference ", k, "removed!"); }); return; diff --git a/Logic/Osm/Overpass.ts b/Logic/Osm/Overpass.ts index f6ef4c83e9..257133307f 100644 --- a/Logic/Osm/Overpass.ts +++ b/Logic/Osm/Overpass.ts @@ -1,9 +1,9 @@ import * as OsmToGeoJson from "osmtogeojson"; -import Bounds from "../../Models/Bounds"; import {TagsFilter} from "../Tags/TagsFilter"; -import ExtractRelations from "./ExtractRelations"; +import RelationsTracker from "./RelationsTracker"; import {Utils} from "../../Utils"; import {UIEventSource} from "../UIEventSource"; +import {BBox} from "../BBox"; /** * Interfaces overpass to get all the latest data @@ -11,46 +11,51 @@ import {UIEventSource} from "../UIEventSource"; export class Overpass { public static testUrl: string = null private _filter: TagsFilter - private readonly _interpreterUrl: UIEventSource; + private readonly _interpreterUrl: string; private readonly _timeout: UIEventSource; private readonly _extraScripts: string[]; private _includeMeta: boolean; + private _relationTracker: RelationsTracker; - constructor(filter: TagsFilter, extraScripts: string[], - interpreterUrl: UIEventSource, + + constructor(filter: TagsFilter, + extraScripts: string[], + interpreterUrl: string, timeout: UIEventSource, + relationTracker: RelationsTracker, includeMeta = true) { this._timeout = timeout; this._interpreterUrl = interpreterUrl; this._filter = filter this._extraScripts = extraScripts; this._includeMeta = includeMeta; + this._relationTracker = relationTracker } - queryGeoJson(bounds: Bounds, continuation: ((any, date: Date) => void), onFail: ((reason) => void)): void { + public async queryGeoJson(bounds: BBox): Promise<[any, Date]> { - let query = this.buildQuery("[bbox:" + bounds.south + "," + bounds.west + "," + bounds.north + "," + bounds.east + "]") + let query = this.buildQuery("[bbox:" + bounds.getSouth() + "," + bounds.getWest() + "," + bounds.getNorth() + "," + bounds.getEast() + "]") if (Overpass.testUrl !== null) { console.log("Using testing URL") query = Overpass.testUrl; } - Utils.downloadJson(query) - .then(json => { - if (json.elements === [] && ((json.remarks ?? json.remark).indexOf("runtime error") >= 0)) { - console.log("Timeout or other runtime error"); - onFail("Runtime error (timeout)") - return; - } + const self = this; + const json = await Utils.downloadJson(query) + + if (json.elements.length === 0 && json.remark !== undefined) { + console.warn("Timeout or other runtime error while querying overpass", json.remark); + throw `Runtime error (timeout or similar)${json.remark}` + } + if(json.elements.length === 0){ + console.warn("No features for" ,json) + } - - ExtractRelations.RegisterRelations(json) - // @ts-ignore - const geojson = OsmToGeoJson.default(json); - const osmTime = new Date(json.osm3s.timestamp_osm_base); - - continuation(geojson, osmTime); - }).catch(onFail) + self._relationTracker.RegisterRelations(json) + // @ts-ignore + const geojson = OsmToGeoJson.default(json); + const osmTime = new Date(json.osm3s.timestamp_osm_base); + return [geojson, osmTime]; } buildQuery(bbox: string): string { @@ -64,6 +69,6 @@ export class Overpass { } const query = `[out:json][timeout:${this._timeout.data}]${bbox};(${filter});out body;${this._includeMeta ? 'out meta;' : ''}>;out skel qt;` - return `${this._interpreterUrl.data}?data=${encodeURIComponent(query)}` + return `${this._interpreterUrl}?data=${encodeURIComponent(query)}` } } diff --git a/Logic/Osm/ExtractRelations.ts b/Logic/Osm/RelationsTracker.ts similarity index 54% rename from Logic/Osm/ExtractRelations.ts rename to Logic/Osm/RelationsTracker.ts index 735b495126..f0528e77dc 100644 --- a/Logic/Osm/ExtractRelations.ts +++ b/Logic/Osm/RelationsTracker.ts @@ -1,4 +1,5 @@ import State from "../../State"; +import {UIEventSource} from "../UIEventSource"; export interface Relation { id: number, @@ -13,11 +14,15 @@ export interface Relation { properties: any } -export default class ExtractRelations { +export default class RelationsTracker { - public static RegisterRelations(overpassJson: any): void { - const memberships = ExtractRelations.BuildMembershipTable(ExtractRelations.GetRelationElements(overpassJson)) - State.state.knownRelations.setData(memberships) + public knownRelations = new UIEventSource>(new Map(), "Relation memberships"); + + constructor() { + } + + public RegisterRelations(overpassJson: any): void { + this.UpdateMembershipTable(RelationsTracker.GetRelationElements(overpassJson)) } /** @@ -25,7 +30,7 @@ export default class ExtractRelations { * @param overpassJson * @constructor */ - public static GetRelationElements(overpassJson: any): Relation[] { + private static GetRelationElements(overpassJson: any): Relation[] { const relations = overpassJson.elements .filter(element => element.type === "relation" && element.tags.type !== "multipolygon") for (const relation of relations) { @@ -39,12 +44,11 @@ export default class ExtractRelations { * @param relations * @constructor */ - public static BuildMembershipTable(relations: Relation[]): Map { - const memberships = new Map() - + private UpdateMembershipTable(relations: Relation[]): void { + const memberships = this.knownRelations.data + let changed = false; for (const relation of relations) { for (const member of relation.members) { - const role = { role: member.role, relation: relation @@ -53,11 +57,21 @@ export default class ExtractRelations { if (!memberships.has(key)) { memberships.set(key, []) } - memberships.get(key).push(role) + const knownRelations = memberships.get(key) + + const alreadyExists = knownRelations.some(knownRole => { + return knownRole.role === role.role && knownRole.relation === role.relation + }) + if (!alreadyExists) { + knownRelations.push(role) + changed = true; + } } } + if (changed) { + this.knownRelations.ping() + } - return memberships } } \ No newline at end of file diff --git a/Logic/SimpleMetaTagger.ts b/Logic/SimpleMetaTagger.ts index 0f8170a4e6..2845223f8d 100644 --- a/Logic/SimpleMetaTagger.ts +++ b/Logic/SimpleMetaTagger.ts @@ -1,8 +1,5 @@ import {GeoOperations} from "./GeoOperations"; import State from "../State"; -import {And} from "./Tags/And"; -import {Tag} from "./Tags/Tag"; -import {Or} from "./Tags/Or"; import {Utils} from "../Utils"; import opening_hours from "opening_hours"; import Combine from "../UI/Base/Combine"; @@ -20,7 +17,7 @@ const cardinalDirections = { export default class SimpleMetaTagger { - static coder: any; + public static coder: any; public static readonly objectMetaInfo = new SimpleMetaTagger( { keys: ["_last_edit:contributor", @@ -30,7 +27,7 @@ export default class SimpleMetaTagger { "_version_number"], doc: "Information about the last edit of this object." }, - (feature) => {/*Note: also handled by 'UpdateTagsFromOsmAPI'*/ + (feature) => {/*Note: also called by 'UpdateTagsFromOsmAPI'*/ const tgs = feature.properties; @@ -47,6 +44,7 @@ export default class SimpleMetaTagger { move("changeset", "_last_edit:changeset") move("timestamp", "_last_edit:timestamp") move("version", "_version_number") + return true; } ) private static latlon = new SimpleMetaTagger({ @@ -61,6 +59,7 @@ export default class SimpleMetaTagger { feature.properties["_lon"] = "" + lon; feature._lon = lon; // This is dirty, I know feature._lat = lat; + return true; }) ); private static surfaceArea = new SimpleMetaTagger( @@ -73,6 +72,7 @@ export default class SimpleMetaTagger { feature.properties["_surface"] = "" + sqMeters; feature.properties["_surface:ha"] = "" + Math.floor(sqMeters / 1000) / 10; feature.area = sqMeters; + return true; }) ); @@ -83,18 +83,33 @@ export default class SimpleMetaTagger { }, (feature => { - const units = Utils.NoNull([].concat(State.state?.layoutToUse?.data?.layers?.map(layer => layer.units ?? []))); + const units = Utils.NoNull([].concat(...State.state?.layoutToUse?.layers?.map(layer => layer.units ?? []))); + if (units.length == 0) { + return; + } let rewritten = false; for (const key in feature.properties) { if (!feature.properties.hasOwnProperty(key)) { continue; } for (const unit of units) { + if (unit === undefined) { + continue + } + if (unit.appliesToKeys === undefined) { + console.error("The unit ", unit, "has no appliesToKey defined") + continue + } if (!unit.appliesToKeys.has(key)) { continue; } const value = feature.properties[key] - const [, denomination] = unit.findDenomination(value) + const denom = unit.findDenomination(value) + if (denom === undefined) { + // no valid value found + break; + } + const [, denomination] = denom; let canonical = denomination?.canonicalValue(value) ?? undefined; if (canonical === value) { break; @@ -110,9 +125,7 @@ export default class SimpleMetaTagger { } } - if (rewritten) { - State.state.allElements.getEventSourceById(feature.id).ping(); - } + return rewritten }) ) @@ -127,21 +140,21 @@ export default class SimpleMetaTagger { const km = Math.floor(l / 1000) const kmRest = Math.round((l - km * 1000) / 100) feature.properties["_length:km"] = "" + km + "." + kmRest + return true; }) ) private static country = new SimpleMetaTagger( { keys: ["_country"], - doc: "The country code of the property (with latlon2country)" + doc: "The country code of the property (with latlon2country)", + includesDates: false }, - feature => { - - + ((feature, _) => { let centerPoint: any = GeoOperations.centerpoint(feature); const lat = centerPoint.geometry.coordinates[1]; const lon = centerPoint.geometry.coordinates[0]; - - SimpleMetaTagger.GetCountryCodeFor(lon, lat, (countries) => { + + SimpleMetaTagger.coder?.GetCountryCodeFor(lon, lat, (countries: string[]) => { try { const oldCountry = feature.properties["_country"]; feature.properties["_country"] = countries[0].trim().toLowerCase(); @@ -149,12 +162,12 @@ export default class SimpleMetaTagger { const tagsSource = State.state.allElements.getEventSourceById(feature.properties.id); tagsSource.ping(); } - } catch (e) { console.warn(e) } - }); - } + }) + return false; + }) ) private static isOpen = new SimpleMetaTagger( { @@ -166,7 +179,7 @@ export default class SimpleMetaTagger { if (Utils.runningFromConsole) { // We are running from console, thus probably creating a cache // isOpen is irrelevant - return + return false } const tagsSource = State.state.allElements.getEventSourceById(feature.properties.id); @@ -191,7 +204,7 @@ export default class SimpleMetaTagger { if (oldNextChange > (new Date()).getTime() && tags["_isOpen:oldvalue"] === tags["opening_hours"]) { // Already calculated and should not yet be triggered - return; + return false; } tags["_isOpen"] = oh.getState() ? "yes" : "no"; @@ -219,6 +232,7 @@ export default class SimpleMetaTagger { } } updateTags(); + return true; } catch (e) { console.warn("Error while parsing opening hours of ", tags.id, e); tags["_isOpen"] = "parse_error"; @@ -236,11 +250,11 @@ export default class SimpleMetaTagger { const tags = feature.properties; const direction = tags["camera:direction"] ?? tags["direction"]; if (direction === undefined) { - return; + return false; } const n = cardinalDirections[direction] ?? Number(direction); if (isNaN(n)) { - return; + return false; } // The % operator has range (-360, 360). We apply a trick to get [0, 360). @@ -248,16 +262,17 @@ export default class SimpleMetaTagger { tags["_direction:numerical"] = normalized; tags["_direction:leftright"] = normalized <= 180 ? "right" : "left"; - + return true; }) ) + private static currentTime = new SimpleMetaTagger( { keys: ["_now:date", "_now:datetime", "_loaded:date", "_loaded:_datetime"], doc: "Adds the time that the data got loaded - pretty much the time of downloading from overpass. The format is YYYY-MM-DD hh:mm, aka 'sortable' aka ISO-8601-but-not-entirely", includesDates: true }, - (feature, _, freshness) => { + (feature, freshness) => { const now = new Date(); if (typeof freshness === "string") { @@ -276,7 +291,7 @@ export default class SimpleMetaTagger { feature.properties["_now:datetime"] = datetime(now); feature.properties["_loaded:date"] = date(freshness); feature.properties["_loaded:datetime"] = datetime(freshness); - + return true; } ) public static metatags = [ @@ -294,12 +309,18 @@ export default class SimpleMetaTagger { public readonly keys: string[]; public readonly doc: string; public readonly includesDates: boolean - private readonly _f: (feature: any, index: number, freshness: Date) => void; + public readonly applyMetaTagsOnFeature: (feature: any, freshness: Date) => boolean; - constructor(docs: { keys: string[], doc: string, includesDates?: boolean }, f: ((feature: any, index: number, freshness: Date) => void)) { + /*** + * A function that adds some extra data to a feature + * @param docs: what does this extra data do? + * @param f: apply the changes. Returns true if something changed + */ + constructor(docs: { keys: string[], doc: string, includesDates?: boolean }, + f: ((feature: any, freshness: Date) => boolean)) { this.keys = docs.keys; this.doc = docs.doc; - this._f = f; + this.applyMetaTagsOnFeature = f; this.includesDates = docs.includesDates ?? false; for (const key of docs.keys) { if (!key.startsWith('_') && key.toLowerCase().indexOf("theme") < 0) { @@ -308,11 +329,7 @@ export default class SimpleMetaTagger { } } - static GetCountryCodeFor(lon: number, lat: number, callback: (country: string) => void) { - SimpleMetaTagger.coder?.GetCountryCodeFor(lon, lat, callback) - } - - static HelpText(): BaseUIElement { + public static HelpText(): BaseUIElement { const subElements: (string | BaseUIElement)[] = [ new Combine([ new Title("Metatags", 1), @@ -335,12 +352,4 @@ export default class SimpleMetaTagger { return new Combine(subElements).SetClass("flex-col") } - addMetaTags(features: { feature: any, freshness: Date }[]) { - for (let i = 0; i < features.length; i++) { - let feature = features[i]; - this._f(feature.feature, i, feature.freshness); - } - } - - } diff --git a/Logic/Tags/RegexTag.ts b/Logic/Tags/RegexTag.ts index 20fbbbcbf9..fae2fd70ba 100644 --- a/Logic/Tags/RegexTag.ts +++ b/Logic/Tags/RegexTag.ts @@ -19,6 +19,9 @@ export class RegexTag extends TagsFilter { if (fromTag === undefined) { return; } + if(typeof fromTag === "number"){ + fromTag = "" + fromTag; + } if (typeof possibleRegex === "string") { return fromTag === possibleRegex; } diff --git a/Logic/UIEventSource.ts b/Logic/UIEventSource.ts index d1c85f2266..55770bb713 100644 --- a/Logic/UIEventSource.ts +++ b/Logic/UIEventSource.ts @@ -1,4 +1,5 @@ import {Utils} from "../Utils"; +import * as Events from "events"; export class UIEventSource { @@ -32,14 +33,14 @@ export class UIEventSource { return []; } - public static flatten(source: UIEventSource>, possibleSources: UIEventSource[]): UIEventSource { + public static flatten(source: UIEventSource>, possibleSources?: UIEventSource[]): UIEventSource { const sink = new UIEventSource(source.data?.data); source.addCallback((latestData) => { sink.setData(latestData?.data); }); - for (const possibleSource of possibleSources) { + for (const possibleSource of possibleSources ?? []) { possibleSource?.addCallback(() => { sink.setData(source.data?.data); }) @@ -60,7 +61,76 @@ export class UIEventSource { run(); return source; + } + /** + * Converts a promise into a UIVentsource, sets the UIEVentSource when the result is calculated. + * If the promise fails, the value will stay undefined + * @param promise + * @constructor + */ + public static FromPromise(promise: Promise): UIEventSource { + const src = new UIEventSource(undefined) + promise?.then(d => src.setData(d)) + promise?.catch(err => console.warn("Promise failed:", err)) + return src + } + + /** + * Converts a promise into a UIVentsource, sets the UIEVentSource when the result is calculated. + * If the promise fails, the value will stay undefined + * @param promise + * @constructor + */ + public static FromPromiseWithErr(promise: Promise): UIEventSource<{ success: T } | { error: any }> { + const src = new UIEventSource<{ success: T } | { error: any }>(undefined) + promise?.then(d => src.setData({success: d})) + promise?.catch(err => src.setData({error: err})) + return src + } + + /** + * Given a UIEVentSource with a list, returns a new UIEventSource which is only updated if the _contents_ of the list are different. + * E.g. + * const src = new UIEventSource([1,2,3]) + * const stable = UIEventSource.ListStabilized(src) + * src.addCallback(_ => console.log("src pinged")) + * stable.addCallback(_ => console.log("stable pinged)) + * src.setDate([...src.data]) + * + * This will only trigger 'src pinged' + * + * @param src + * @constructor + */ + public static ListStabilized(src: UIEventSource): UIEventSource { + + const stable = new UIEventSource(src.data) + src.addCallback(list => { + if (list === undefined) { + stable.setData(undefined) + return; + } + const oldList = stable.data + if (oldList === list) { + return; + } + if (oldList.length !== list.length) { + stable.setData(list); + return; + } + + for (let i = 0; i < list.length; i++) { + if (oldList[i] !== list[i]) { + stable.setData(list); + return; + } + } + + // No actual changes, so we don't do anything + return; + }) + return stable } /** @@ -81,9 +151,12 @@ export class UIEventSource { return this; } - public addCallbackAndRun(callback: ((latestData: T) => void)): UIEventSource { - callback(this.data); - return this.addCallback(callback); + public addCallbackAndRun(callback: ((latestData: T) => (boolean | void | any))): UIEventSource { + const doDeleteCallback = callback(this.data); + if (doDeleteCallback !== true) { + this.addCallback(callback); + } + return this; } public setData(t: T): UIEventSource { @@ -114,6 +187,30 @@ export class UIEventSource { } } + /** + * Monadic bind function + */ + public bind(f: ((t: T) => UIEventSource)): UIEventSource { + const mapped = this.map(f) + const sink = new UIEventSource(undefined) + const seenEventSources = new Set>(); + mapped.addCallbackAndRun(newEventSource => { + + if (newEventSource === undefined) { + sink.setData(undefined) + } else if (!seenEventSources.has(newEventSource)) { + seenEventSources.add(newEventSource) + newEventSource.addCallbackAndRun(resultData => { + if (mapped.data === newEventSource) { + sink.setData(resultData); + } + }) + } + }) + + return sink; + } + /** * Monoidal map: * Given a function 'f', will construct a new UIEventSource where the contents will always be "f(this.data)' @@ -188,6 +285,14 @@ export class UIEventSource { } }) } + + addCallbackD(callback: (data: T) => void) { + this.addCallback(data => { + if (data !== undefined && data !== null) { + return callback(data) + } + }) + } } export class UIEventSourceTools { diff --git a/Logic/Web/Hash.ts b/Logic/Web/Hash.ts index ff1df3616b..0c463c689e 100644 --- a/Logic/Web/Hash.ts +++ b/Logic/Web/Hash.ts @@ -9,7 +9,7 @@ export default class Hash { public static hash: UIEventSource = Hash.Get(); /** - * Gets the current string, including the pound sign + * Gets the current string, including the pound sign if there is any * @constructor */ public static Current(): string { diff --git a/Logic/Web/QueryParameters.ts b/Logic/Web/QueryParameters.ts index 065766e47a..36ecc76803 100644 --- a/Logic/Web/QueryParameters.ts +++ b/Logic/Web/QueryParameters.ts @@ -127,7 +127,6 @@ export class QueryParameters { parts.push(encodeURIComponent(key) + "=" + encodeURIComponent(QueryParameters.knownSources[key].data)) } // Don't pollute the history every time a parameter changes - history.replaceState(null, "", "?" + parts.join("&") + Hash.Current()); } diff --git a/Logic/Web/Wikidata.ts b/Logic/Web/Wikidata.ts new file mode 100644 index 0000000000..ae981ee822 --- /dev/null +++ b/Logic/Web/Wikidata.ts @@ -0,0 +1,116 @@ +import {Utils} from "../../Utils"; +import {UIEventSource} from "../UIEventSource"; + + +export interface WikidataResponse { + + id: string, + labels: Map, + descriptions: Map, + claims: Map>, + wikisites: Map + commons: string +} + +/** + * Utility functions around wikidata + */ +export default class Wikidata { + + private static ParseResponse(entity: any): WikidataResponse { + const labels = new Map() + for (const labelName in entity.labels) { + // The labelname is the language code + labels.set(labelName, entity.labels[labelName].value) + } + + const descr = new Map() + for (const labelName in entity.descriptions) { + // The labelname is the language code + descr.set(labelName, entity.descriptions[labelName].value) + } + + const sitelinks = new Map(); + for (const labelName in entity.sitelinks) { + // labelName is `${language}wiki` + const language = labelName.substring(0, labelName.length - 4) + const title = entity.sitelinks[labelName].title + sitelinks.set(language, title) + } + + const commons = sitelinks.get("commons") + sitelinks.delete("commons") + + const claims = new Map>(); + for (const claimId of entity.claims) { + + const claimsList: any[] = entity.claims[claimId] + const values = new Set() + for (const claim of claimsList) { + const value = claim.mainsnak.datavalue.value; + values.add(value) + } + claims.set(claimId, values); + } + + return { + claims: claims, + descriptions: descr, + id: entity.id, + labels: labels, + wikisites: sitelinks, + commons: commons + } + } + + private static readonly _cache = new Map>() + public static LoadWikidataEntry(value: string | number): UIEventSource<{success: WikidataResponse} | {error: any}> { + const key = this.ExtractKey(value) + const cached = Wikidata._cache.get(key) + if(cached !== undefined){ + return cached + } + const src = UIEventSource.FromPromiseWithErr(Wikidata.LoadWikidataEntryAsync(key)) + Wikidata._cache.set(key, src) + return src; + } + + private static ExtractKey(value: string | number) : number{ + if (typeof value === "number") { + return value + } + const wikidataUrl = "https://www.wikidata.org/wiki/" + if (value.startsWith(wikidataUrl)) { + value = value.substring(wikidataUrl.length) + } + if (value.startsWith("http")) { + // Probably some random link in the image field - we skip it + return undefined + } + if (value.startsWith("Q")) { + value = value.substring(1) + } + const n = Number(value) + if(isNaN(n)){ + return undefined + } + return n; + } + + /** + * Loads a wikidata page + * @returns the entity of the given value + */ + public static async LoadWikidataEntryAsync(value: string | number): Promise { + const id = Wikidata.ExtractKey(value) + if(id === undefined){ + console.warn("Could not extract a wikidata entry from", value) + return undefined; + } + console.log("Requesting wikidata with id", id) + const url = "https://www.wikidata.org/wiki/Special:EntityData/Q" + id + ".json"; + const response = await Utils.downloadJson(url) + return Wikidata.ParseResponse(response.entities["Q" + id]) + } + +} \ No newline at end of file diff --git a/Logic/Web/Wikipedia.ts b/Logic/Web/Wikipedia.ts new file mode 100644 index 0000000000..dedfa144d9 --- /dev/null +++ b/Logic/Web/Wikipedia.ts @@ -0,0 +1,75 @@ +/** + * Some usefull utility functions around the wikipedia API + */ +import {Utils} from "../../Utils"; +import {UIEventSource} from "../UIEventSource"; +import Wikidata from "./Wikidata"; + +export default class Wikipedia { + + /** + * When getting a wikipedia page data result, some elements (e.g. navigation, infoboxes, ...) should be removed if 'removeInfoBoxes' is set. + * We do this based on the classes. This set contains a blacklist of the classes to remove + * @private + */ + private static readonly classesToRemove = [ + "shortdescription", + "sidebar", + "infobox","infobox_v2", + "noprint", + "ambox", + "mw-editsection", + "mw-selflink", + "hatnote" // Often redirects + ] + + private static readonly _cache = new Map>() + + public static GetArticle(options: { + pageName: string, + language?: "en" | string}): UIEventSource<{ success: string } | { error: any }>{ + const key = (options.language ?? "en")+":"+options.pageName + const cached = Wikipedia._cache.get(key) + if(cached !== undefined){ + return cached + } + const v = UIEventSource.FromPromiseWithErr(Wikipedia.GetArticleAsync(options)) + Wikipedia._cache.set(key, v) + return v; + } + + public static async GetArticleAsync(options: { + pageName: string, + language?: "en" | string + }): Promise { + + const language = options.language ?? "en" + const url = `https://${language}.wikipedia.org/w/api.php?action=parse&format=json&origin=*&prop=text&page=` + options.pageName + const response = await Utils.downloadJson(url) + const html = response["parse"]["text"]["*"]; + + const div = document.createElement("div") + div.innerHTML = html + const content = Array.from(div.children)[0] + + for (const forbiddenClass of Wikipedia.classesToRemove) { + const toRemove = content.getElementsByClassName(forbiddenClass) + for (const toRemoveElement of Array.from(toRemove)) { + toRemoveElement.parentElement?.removeChild(toRemoveElement) + } + } + + const links = Array.from(content.getElementsByTagName("a")) + + // Rewrite relative links to absolute links + open them in a new tab + links.filter(link => link.getAttribute("href")?.startsWith("/") ?? false). + forEach(link => { + link.target = '_blank' + // note: link.getAttribute("href") gets the textual value, link.href is the rewritten version which'll contain the host for relative paths + link.href = `https://${language}.wikipedia.org${link.getAttribute("href")}`; + }) + + return content.innerHTML + } + +} \ No newline at end of file diff --git a/Models/Bounds.ts b/Models/Bounds.ts deleted file mode 100644 index 3a993c8e06..0000000000 --- a/Models/Bounds.ts +++ /dev/null @@ -1,6 +0,0 @@ -export default interface Bounds { - north: number, - east: number, - south: number, - west: number -} \ No newline at end of file diff --git a/Models/Constants.ts b/Models/Constants.ts index 7c8dacb489..9a9250eae9 100644 --- a/Models/Constants.ts +++ b/Models/Constants.ts @@ -2,7 +2,22 @@ import {Utils} from "../Utils"; export default class Constants { - public static vNumber = "0.9.14"; + public static vNumber = "0.10.1-rc5"; + public static ImgurApiKey = '7070e7167f0a25a' + public static readonly mapillary_client_token_v3 = 'TXhLaWthQ1d4RUg0czVxaTVoRjFJZzowNDczNjUzNmIyNTQyYzI2' + public static readonly mapillary_client_token_v4 = "MLY|4441509239301885|b40ad2d3ea105435bd40c7e76993ae85" + + public static defaultOverpassUrls = [ + // The official instance, 10000 queries per day per project allowed + "https://overpass-api.de/api/interpreter", + // 'Fair usage' + "https://overpass.kumi.systems/api/interpreter", + // Offline: "https://overpass.nchc.org.tw/api/interpreter", + "https://overpass.openstreetmap.ru/cgi/interpreter", + // Doesn't support nwr "https://overpass.openstreetmap.fr/api/interpreter" + ] + + // The user journey states thresholds when a new feature gets unlocked public static userJourney = { @@ -26,12 +41,6 @@ export default class Constants { */ static updateTimeoutSec: number = 30; - /** - * If zoom >= useOsmApiAt, then the OSM api will be used directly. - * If undefined, use overpass exclusively - */ - static useOsmApiAt = undefined; - private static isRetina(): boolean { if (Utils.runningFromConsole) { return; diff --git a/Models/FilteredLayer.ts b/Models/FilteredLayer.ts index 6c387270fc..68ffb44832 100644 --- a/Models/FilteredLayer.ts +++ b/Models/FilteredLayer.ts @@ -1,9 +1,10 @@ import {UIEventSource} from "../Logic/UIEventSource"; import LayerConfig from "./ThemeConfig/LayerConfig"; import {And} from "../Logic/Tags/And"; +import FilterConfig from "./ThemeConfig/FilterConfig"; export default interface FilteredLayer { readonly isDisplayed: UIEventSource; - readonly appliedFilters: UIEventSource; + readonly appliedFilters: UIEventSource<{filter: FilterConfig, selected: number}[]>; readonly layerDef: LayerConfig; } \ No newline at end of file diff --git a/Models/ThemeConfig/FilterConfig.ts b/Models/ThemeConfig/FilterConfig.ts index 464919d764..7a032c6de3 100644 --- a/Models/ThemeConfig/FilterConfig.ts +++ b/Models/ThemeConfig/FilterConfig.ts @@ -5,7 +5,8 @@ import Translations from "../../UI/i18n/Translations"; import {TagUtils} from "../../Logic/Tags/TagUtils"; export default class FilterConfig { - readonly options: { + public readonly id: string + public readonly options: { question: Translation; osmTags: TagsFilter; }[]; @@ -14,11 +15,18 @@ export default class FilterConfig { if (json.options === undefined) { throw `A filter without options was given at ${context}` } + if (json.id === undefined) { + throw `A filter without id was found at ${context}` + } + if(json.id.match(/^[a-zA-Z0-9_-]*$/) === null){ + throw `A filter with invalid id was found at ${context}. Ids should only contain letters, numbers or - _` + + } if (json.options.map === undefined) { throw `A filter was given where the options aren't a list at ${context}` } - + this.id = json.id; this.options = json.options.map((option, i) => { const question = Translations.T( option.question, @@ -34,5 +42,9 @@ export default class FilterConfig { return {question: question, osmTags: osmTags}; }); + + if(this.options.length > 1 && this.options[0].osmTags["and"]?.length !== 0){ + throw "Error in "+context+"."+this.id+": the first option of a multi-filter should always be the 'reset' option and not have any filters" + } } } \ No newline at end of file diff --git a/Models/ThemeConfig/Json/FilterConfigJson.ts b/Models/ThemeConfig/Json/FilterConfigJson.ts index c49f9f3eba..7151e38542 100644 --- a/Models/ThemeConfig/Json/FilterConfigJson.ts +++ b/Models/ThemeConfig/Json/FilterConfigJson.ts @@ -1,6 +1,10 @@ import {AndOrTagConfigJson} from "./TagConfigJson"; export default interface FilterConfigJson { + /** + * An id/name for this filter, used to set the URL parameters + */ + id: string, /** * The options for a filter * If there are multiple options these will be a list of radio buttons diff --git a/Models/ThemeConfig/Json/LayerConfigJson.ts b/Models/ThemeConfig/Json/LayerConfigJson.ts index f03166d07c..f337b1a198 100644 --- a/Models/ThemeConfig/Json/LayerConfigJson.ts +++ b/Models/ThemeConfig/Json/LayerConfigJson.ts @@ -59,10 +59,9 @@ export interface LayerConfigJson { * NOTE: the previous format was 'overpassTags: AndOrTagConfigJson | string', which is interpreted as a shorthand for source: {osmTags: "key=value"} * While still supported, this is considered deprecated */ - source: { osmTags: AndOrTagConfigJson | string } | - { osmTags: AndOrTagConfigJson | string, geoJson: string, geoJsonZoomLevel?: number, isOsmCache?: boolean } | - { osmTags: AndOrTagConfigJson | string, overpassScript: string } - + source: { osmTags: AndOrTagConfigJson | string, overpassScript?: string } | + { osmTags: AndOrTagConfigJson | string, geoJson: string, geoJsonZoomLevel?: number, isOsmCache?: boolean } + /** * * A list of extra tags to calculate, specified as "keyToAssignTo=javascript-expression". @@ -84,7 +83,9 @@ export interface LayerConfigJson { /** * This tag rendering should either be 'yes' or 'no'. If 'no' is returned, then the feature will be hidden from view. - * This is useful to hide certain features from view. Important: hiding features does not work dynamically, but is only calculated when the data is first renders. + * This is useful to hide certain features from view. + * + * Important: hiding features does not work dynamically, but is only calculated when the data is first renders. * This implies that it is not possible to hide a feature after a tagging change * * The default value is 'yes' diff --git a/Models/ThemeConfig/Json/LayoutConfigJson.ts b/Models/ThemeConfig/Json/LayoutConfigJson.ts index 3c312ce2e3..89439f529b 100644 --- a/Models/ThemeConfig/Json/LayoutConfigJson.ts +++ b/Models/ThemeConfig/Json/LayoutConfigJson.ts @@ -52,6 +52,11 @@ export interface LayoutConfigJson { */ language: string | string[]; + /** + * Only used in 'generateLayerOverview': if present, every translation will be checked to make sure it is fully translated + */ + mustHaveLanguage?: string[] + /** * The title, as shown in the welcome message and the more-screen */ @@ -100,7 +105,7 @@ export interface LayoutConfigJson { * However, users tend to pan and zoom a lot. It is pretty annoying if every single pan means a reloading of the data. * For this, the bounds are widened in order to make a small pan still within bounds of the loaded data. * - * IF widenfactor is 0, this feature is disabled. A recommended value is between 0.5 and 0.01 (the latter for very dense queries) + * IF widenfactor is 1, this feature is disabled. A recommended value is between 1 and 3 */ widenFactor?: number; @@ -228,8 +233,8 @@ export interface LayoutConfigJson { */ maxZoom?: number, /** - * The number of elements that should be showed (in total) before clustering starts to happen. - * If clustering is defined, defaults to 0 + * The number of elements per tile needed to start clustering + * If clustering is defined, defaults to 25 */ minNeededElements?: number }, @@ -263,9 +268,9 @@ export interface LayoutConfigJson { enablePdfDownload?: boolean; /** - * Set a different overpass URL. Default: https://overpass-api.de/api/interpreter + * Set one or more overpass URLs to use for this theme.. */ - overpassUrl?: string; + overpassUrl?: string | string[]; /** * Set a different timeout for overpass queries - in seconds. Default: 30s */ diff --git a/Models/ThemeConfig/Json/TagRenderingConfigJson.ts b/Models/ThemeConfig/Json/TagRenderingConfigJson.ts index f700548ae8..0965a24b6a 100644 --- a/Models/ThemeConfig/Json/TagRenderingConfigJson.ts +++ b/Models/ThemeConfig/Json/TagRenderingConfigJson.ts @@ -86,6 +86,10 @@ export interface TagRenderingConfigJson { /** * If this condition is met, then the text under `then` will be shown. * If no value matches, and the user selects this mapping as an option, then these tags will be uploaded to OSM. + * + * For example: {'if': 'diet:vegetarion=yes', 'then':'A vegetarian option is offered here'} + * + * This can be an substituting-tag as well, e.g. {'if': 'addr:street:={_calculated_nearby_streetname}', 'then': '{_calculated_nearby_streetname}'} */ if: AndOrTagConfigJson | string, /** diff --git a/Models/ThemeConfig/LayerConfig.ts b/Models/ThemeConfig/LayerConfig.ts index 371cd1b805..a2e55d71c9 100644 --- a/Models/ThemeConfig/LayerConfig.ts +++ b/Models/ThemeConfig/LayerConfig.ts @@ -18,6 +18,7 @@ import FilterConfig from "./FilterConfig"; import {Unit} from "../Unit"; import DeleteConfig from "./DeleteConfig"; import Svg from "../../Svg"; +import Img from "../../UI/Base/Img"; export default class LayerConfig { static WAYHANDLING_DEFAULT = 0; @@ -104,6 +105,10 @@ export default class LayerConfig { throw context + "Use 'geoJson' instead of 'geoJsonSource'"; } + if (json.source["geojson"] !== undefined) { + throw context + "Use 'geoJson' instead of 'geojson' (the J is a capital letter)"; + } + this.source = new SourceConfig( { osmTags: osmTags, @@ -133,6 +138,14 @@ export default class LayerConfig { const key = kv.substring(0, index); const code = kv.substring(index + 1); + try{ + + new Function("feat", "return " + code + ";"); + }catch(e){ + throw `Invalid function definition: code ${code} is invalid:${e} (at ${context})` + } + + this.calculatedTags.push([key, code]); } } @@ -142,6 +155,9 @@ export default class LayerConfig { this.minzoom = json.minzoom ?? 0; this.minzoomVisible = json.minzoomVisible ?? this.minzoom; this.wayHandling = json.wayHandling ?? 0; + if(json.presets !== undefined && json.presets?.map === undefined){ + throw "Presets should be a list of items (at "+context+")" + } this.presets = (json.presets ?? []).map((pr, i) => { let preciseInput = undefined; @@ -286,6 +302,10 @@ export default class LayerConfig { this.filters = (json.filter ?? []).map((option, i) => { return new FilterConfig(option, `${context}.filter-[${i}]`) }); + + if(json["filters"] !== undefined){ + throw "Error in "+context+": use 'filter' instead of 'filters'" + } const titleIcons = []; const defaultIcons = [ @@ -494,12 +514,13 @@ export default class LayerConfig { ); const match = sourcePart.match(/([a-zA-Z0-9_]*):([^;]*)/); if (match !== null && Svg.All[match[1] + ".svg"] !== undefined) { - html = new Combine([ + html = new Img( (Svg.All[match[1] + ".svg"] as string).replace( /#000000/g, match[2] ), - ]).SetStyle(style); + true + ).SetStyle(style); } return html; } diff --git a/Models/ThemeConfig/LayoutConfig.ts b/Models/ThemeConfig/LayoutConfig.ts index e34a83cb39..8b10f6c54a 100644 --- a/Models/ThemeConfig/LayoutConfig.ts +++ b/Models/ThemeConfig/LayoutConfig.ts @@ -5,8 +5,8 @@ import SharedTagRenderings from "../../Customizations/SharedTagRenderings"; import AllKnownLayers from "../../Customizations/AllKnownLayers"; import {Utils} from "../../Utils"; import LayerConfig from "./LayerConfig"; -import {Unit} from "../Unit"; import {LayerConfigJson} from "./Json/LayerConfigJson"; +import Constants from "../Constants"; export default class LayoutConfig { public readonly id: string; @@ -51,12 +51,12 @@ export default class LayoutConfig { How long is the cache valid, in seconds? */ public readonly cacheTimeout?: number; - public readonly overpassUrl: string; + public readonly overpassUrl: string[]; public readonly overpassTimeout: number; - private readonly _official: boolean; + public readonly official: boolean; constructor(json: LayoutConfigJson, official = true, context?: string) { - this._official = official; + this.official = official; this.id = json.id; context = (context ?? "") + "." + this.id; this.maintainer = json.maintainer; @@ -81,13 +81,16 @@ export default class LayoutConfig { this.title = new Translation(json.title, context + ".title"); this.description = new Translation(json.description, context + ".description"); this.shortDescription = json.shortDescription === undefined ? this.description.FirstSentence() : new Translation(json.shortDescription, context + ".shortdescription"); - this.descriptionTail = json.descriptionTail === undefined ? new Translation({"*": ""}, context + ".descriptionTail") : new Translation(json.descriptionTail, context + ".descriptionTail"); + this.descriptionTail = json.descriptionTail === undefined ? undefined : new Translation(json.descriptionTail, context + ".descriptionTail"); this.icon = json.icon; this.socialImage = json.socialImage; this.startZoom = json.startZoom; this.startLat = json.startLat; this.startLon = json.startLon; - this.widenFactor = json.widenFactor ?? 0.05; + if(json.widenFactor < 1){ + throw "Widenfactor too small" + } + this.widenFactor = json.widenFactor ?? 1.5; this.roamingRenderings = (json.roamingRenderings ?? []).map((tr, i) => { if (typeof tr === "string") { if (SharedTagRenderings.SharedTagRendering.get(tr) !== undefined) { @@ -127,17 +130,12 @@ export default class LayoutConfig { this.clustering = { maxZoom: 16, - minNeededElements: 500 + minNeededElements: 25 }; if (json.clustering) { this.clustering = { maxZoom: json.clustering.maxZoom ?? 18, - minNeededElements: json.clustering.minNeededElements ?? 1 - } - for (const layer of this.layers) { - if (layer.wayHandling !== LayerConfig.WAYHANDLING_CENTER_ONLY) { - console.debug("WARNING: In order to allow clustering, every layer must be set to CENTER_ONLY. Layer", layer.id, "does not respect this for layout", this.id); - } + minNeededElements: json.clustering.minNeededElements ?? 25 } } @@ -160,7 +158,14 @@ export default class LayoutConfig { this.enablePdfDownload = json.enablePdfDownload ?? false; this.customCss = json.customCss; this.cacheTimeout = json.cacheTimout ?? (60 * 24 * 60 * 60) - this.overpassUrl = json.overpassUrl ?? "https://overpass-api.de/api/interpreter" + this.overpassUrl = Constants.defaultOverpassUrls + if(json.overpassUrl !== undefined){ + if(typeof json.overpassUrl === "string"){ + this.overpassUrl = [json.overpassUrl] + }else{ + this.overpassUrl = json.overpassUrl + } + } this.overpassTimeout = json.overpassTimeout ?? 30 } @@ -221,7 +226,7 @@ export default class LayoutConfig { } public CustomCodeSnippets(): string[] { - if (this._official) { + if (this.official) { return []; } const msg = "
This layout uses custom javascript, loaded for the wide internet. The code is printed below, please report suspicious code on the issue tracker of MapComplete:
" @@ -246,14 +251,6 @@ export default class LayoutConfig { return icons } - public LayerIndex(): Map { - const index = new Map(); - for (const layer of this.layers) { - index.set(layer.id, layer) - } - return index; - } - /** * Replaces all the relative image-urls with a fixed image url * This is to fix loading from external sources diff --git a/Models/ThemeConfig/SourceConfig.ts b/Models/ThemeConfig/SourceConfig.ts index db9d21f4fb..1d223bd2bd 100644 --- a/Models/ThemeConfig/SourceConfig.ts +++ b/Models/ThemeConfig/SourceConfig.ts @@ -2,18 +2,18 @@ import {TagsFilter} from "../../Logic/Tags/TagsFilter"; export default class SourceConfig { - osmTags?: TagsFilter; - overpassScript?: string; - geojsonSource?: string; - geojsonZoomLevel?: number; - isOsmCacheLayer: boolean; + public readonly osmTags?: TagsFilter; + public readonly overpassScript?: string; + public readonly geojsonSource?: string; + public readonly geojsonZoomLevel?: number; + public readonly isOsmCacheLayer: boolean; constructor(params: { osmTags?: TagsFilter, overpassScript?: string, geojsonSource?: string, isOsmCache?: boolean, - geojsonSourceLevel?: number + geojsonSourceLevel?: number, }, context?: string) { let defined = 0; diff --git a/Models/TileRange.ts b/Models/TileRange.ts index e1dba5532f..7a542a2135 100644 --- a/Models/TileRange.ts +++ b/Models/TileRange.ts @@ -1,3 +1,6 @@ +import {control} from "leaflet"; +import zoom = control.zoom; + export interface TileRange { xstart: number, ystart: number, @@ -5,4 +8,111 @@ export interface TileRange { yend: number, total: number, zoomlevel: number +} + +export class Tiles { + + public static MapRange(tileRange: TileRange, f: (x: number, y: number) => T): T[] { + const result: T[] = [] + const total = tileRange.total + if(total > 5000){ + throw "Tilerange too big" + } + for (let x = tileRange.xstart; x <= tileRange.xend; x++) { + for (let y = tileRange.ystart; y <= tileRange.yend; y++) { + const t = f(x, y); + result.push(t) + } + } + return result; + } + + + private static tile2long(x, z) { + return (x / Math.pow(2, z) * 360 - 180); + } + + private static tile2lat(y, z) { + const n = Math.PI - 2 * Math.PI * y / Math.pow(2, z); + return (180 / Math.PI * Math.atan(0.5 * (Math.exp(n) - Math.exp(-n)))); + } + + private static lon2tile(lon, zoom) { + return (Math.floor((lon + 180) / 360 * Math.pow(2, zoom))); + } + + private static lat2tile(lat, zoom) { + return (Math.floor((1 - Math.log(Math.tan(lat * Math.PI / 180) + 1 / Math.cos(lat * Math.PI / 180)) / Math.PI) / 2 * Math.pow(2, zoom))); + } + + /** + * Calculates the tile bounds of the + * @param z + * @param x + * @param y + * @returns [[maxlat, minlon], [minlat, maxlon]] + */ + static tile_bounds(z: number, x: number, y: number): [[number, number], [number, number]] { + return [[Tiles.tile2lat(y, z), Tiles.tile2long(x, z)], [Tiles.tile2lat(y + 1, z), Tiles.tile2long(x + 1, z)]] + } + + + static tile_bounds_lon_lat(z: number, x: number, y: number): [[number, number], [number, number]] { + return [[Tiles.tile2long(x, z), Tiles.tile2lat(y, z)], [Tiles.tile2long(x + 1, z), Tiles.tile2lat(y + 1, z)]] + } + + /** + * Returns the centerpoint [lon, lat] of the specified tile + * @param z + * @param x + * @param y + */ + static centerPointOf(z: number, x: number, y: number): [number, number]{ + return [(Tiles.tile2long(x, z) + Tiles.tile2long(x+1, z)) / 2, (Tiles.tile2lat(y, z) + Tiles.tile2lat(y+1, z)) / 2] + } + + static tile_index(z: number, x: number, y: number): number { + return ((x * (2 << z)) + y) * 100 + z + } + /** + * Given a tile index number, returns [z, x, y] + * @param index + * @returns 'zxy' + */ + static tile_from_index(index: number): [number, number, number] { + const z = index % 100; + const factor = 2 << z + index = Math.floor(index / 100) + const x = Math.floor(index / factor) + return [z, x, index % factor] + } + + /** + * Return x, y of the tile containing (lat, lon) on the given zoom level + */ + static embedded_tile(lat: number, lon: number, z: number): { x: number, y: number, z: number } { + return {x: Tiles.lon2tile(lon, z), y: Tiles.lat2tile(lat, z), z: z} + } + + static TileRangeBetween(zoomlevel: number, lat0: number, lon0: number, lat1: number, lon1: number): TileRange { + const t0 = Tiles.embedded_tile(lat0, lon0, zoomlevel) + const t1 = Tiles.embedded_tile(lat1, lon1, zoomlevel) + + const xstart = Math.min(t0.x, t1.x) + const xend = Math.max(t0.x, t1.x) + const ystart = Math.min(t0.y, t1.y) + const yend = Math.max(t0.y, t1.y) + const total = (1 + xend - xstart) * (1 + yend - ystart) + + return { + xstart: xstart, + xend: xend, + ystart: ystart, + yend: yend, + total: total, + zoomlevel: zoomlevel + } + } + + } \ No newline at end of file diff --git a/README.md b/README.md index 1373391a51..566ab402a8 100644 --- a/README.md +++ b/README.md @@ -2,28 +2,30 @@ > Let a thousand flowers bloom -MapComplete is an OpenStreetMap viewer and editor. It shows map features on a certain topic, and allows to see, edit and +**MapComplete is an OpenStreetMap viewer and editor.** It shows map features on a certain topic, and allows to see, edit and add new features to the map. It can be seen as a webversion [crossover of StreetComplete and MapContrib](Docs/MapComplete_vs_other_editors.md). It tries to be just as easy to use as StreetComplete, but it allows to focus on one single theme per instance (e.g. nature, bicycle infrastructure, ...) -The design goals of MapComplete are to be: +**The design goals** of MapComplete are to be: - Easy to use, both on web and on mobile - Easy to deploy (by not having a backend) - Easy to set up a custom theme - Easy to fall down the rabbit hole of OSM -The basic functionality is to download some map features from Overpass and then ask certain questions. An answer is sent +**The basic functionality is** to download some map features from Overpass and then ask certain questions. An answer is sent back to directly to OpenStreetMap. Furthermore, it shows images present in the `image` tag or, if a `wikidata` or `wikimedia_commons`-tag is present, it follows those to get these images too. -An explicit non-goal of MapComplete is to modify geometries of ways. Although adding a point to a way or splitting a way +**An explicit non-goal** of MapComplete is to modify geometries of ways. Although adding a point to a way or splitting a way in two parts might be added one day. +**More about MapComplete:** [Watch Pieter's talk on the 2021 State Of The Map Conference](https://media.ccc.de/v/sotm2021-9448-introduction-and-review-of-mapcomplete) ([YouTube](https://www.youtube.com/watch?v=zTtMn6fNbYY)) about the history, vision and future of MapComplete. + # Creating your own theme It is possible to quickly make and distribute your own theme diff --git a/State.ts b/State.ts index 27e9a251f1..7279136b30 100644 --- a/State.ts +++ b/State.ts @@ -11,16 +11,13 @@ import InstalledThemes from "./Logic/Actors/InstalledThemes"; import BaseLayer from "./Models/BaseLayer"; import Loc from "./Models/Loc"; import Constants from "./Models/Constants"; - -import OverpassFeatureSource from "./Logic/Actors/OverpassFeatureSource"; import TitleHandler from "./Logic/Actors/TitleHandler"; import PendingChangesUploader from "./Logic/Actors/PendingChangesUploader"; -import {Relation} from "./Logic/Osm/ExtractRelations"; -import OsmApiFeatureSource from "./Logic/FeatureSource/OsmApiFeatureSource"; import FeaturePipeline from "./Logic/FeatureSource/FeaturePipeline"; import FilteredLayer from "./Models/FilteredLayer"; import ChangeToElementsActor from "./Logic/Actors/ChangeToElementsActor"; import LayoutConfig from "./Models/ThemeConfig/LayoutConfig"; +import {BBox} from "./Logic/BBox"; /** * Contains the global state: a bunch of UI-event sources @@ -30,16 +27,16 @@ export default class State { // The singleton of the global state public static state: State; - public readonly layoutToUse = new UIEventSource(undefined, "layoutToUse"); + public readonly layoutToUse : LayoutConfig; /** The mapping from id -> UIEventSource */ - public allElements: ElementStorage; + public allElements: ElementStorage = new ElementStorage(); /** THe change handler */ - public changes: Changes; + public changes: Changes = new Changes(); /** The leaflet instance of the big basemap */ @@ -57,10 +54,6 @@ export default class State { public favouriteLayers: UIEventSource; - public layerUpdater: OverpassFeatureSource; - - public osmApiFeatureSource: OsmApiFeatureSource; - public filteredLayers: UIEventSource = new UIEventSource([], "filteredLayers"); /** @@ -71,12 +64,6 @@ export default class State { "Selected element" ); - /** - * Keeps track of relations: which way is part of which other way? - * Set by the overpass-updater; used in the metatagging - */ - public readonly knownRelations = new UIEventSource>(undefined, "Relation memberships"); - public readonly featureSwitchUserbadge: UIEventSource; public readonly featureSwitchSearch: UIEventSource; public readonly featureSwitchBackgroundSlection: UIEventSource; @@ -94,8 +81,11 @@ export default class State { public readonly featureSwitchEnableExport: UIEventSource; public readonly featureSwitchFakeUser: UIEventSource; public readonly featureSwitchExportAsPdf: UIEventSource; - public readonly overpassUrl: UIEventSource; + public readonly overpassUrl: UIEventSource; public readonly overpassTimeout: UIEventSource; + + + public readonly overpassMaxZoom: UIEventSource = new UIEventSource(17, "overpass-max-zoom: point to switch between OSM-api and overpass"); public featurePipeline: FeaturePipeline; @@ -104,6 +94,12 @@ export default class State { * The map location: currently centered lat, lon and zoom */ public readonly locationControl = new UIEventSource(undefined, "locationControl"); + + /** + * The current visible extent of the screen + */ + public readonly currentBounds = new UIEventSource(undefined) + public backgroundLayer; public readonly backgroundLayerId: UIEventSource; @@ -161,8 +157,7 @@ export default class State { constructor(layoutToUse: LayoutConfig) { const self = this; - - this.layoutToUse.setData(layoutToUse); + this.layoutToUse = layoutToUse; // -- Location control initialization { @@ -199,14 +194,7 @@ export default class State { lat.setData(latlonz.lat); lon.setData(latlonz.lon); }); - - this.layoutToUse.addCallback((layoutToUse) => { - const lcd = self.locationControl.data; - lcd.zoom = lcd.zoom ?? layoutToUse?.startZoom; - lcd.lat = lcd.lat ?? layoutToUse?.startLat; - lcd.lon = lcd.lon ?? layoutToUse?.startLon; - self.locationControl.ping(); - }); + } // Helper function to initialize feature switches @@ -215,28 +203,19 @@ export default class State { deflt: (layout: LayoutConfig) => boolean, documentation: string ): UIEventSource { - const queryParameterSource = QueryParameters.GetQueryParameter( + + const defaultValue = deflt(self.layoutToUse); + const queryParam = QueryParameters.GetQueryParameter( key, - undefined, + "" + defaultValue, documentation ); - // I'm so sorry about someone trying to decipher this // It takes the current layout, extracts the default value for this query parameter. A query parameter event source is then retrieved and flattened - return UIEventSource.flatten( - self.layoutToUse.map((layout) => { - const defaultValue = deflt(layout); - const queryParam = QueryParameters.GetQueryParameter( - key, - "" + defaultValue, - documentation - ); - return queryParam.map((str) => - str === undefined ? defaultValue : str !== "false" - ); - }), - [queryParameterSource] - ); + return queryParam.map((str) => + str === undefined ? defaultValue : str !== "false" + ) + } // Feature switch initialization - not as a function as the UIEventSources are readonly @@ -342,9 +321,9 @@ export default class State { ); this.overpassUrl = QueryParameters.GetQueryParameter("overpassUrl", - layoutToUse?.overpassUrl, + (layoutToUse?.overpassUrl ?? Constants.defaultOverpassUrls).join(",") , "Point mapcomplete to a different overpass-instance. Example: https://overpass-api.de/api/interpreter" - ) + ).map(param => param.split(","), [], urls => urls.join(",")) this.overpassTimeout = QueryParameters.GetQueryParameter("overpassTimeout", "" + layoutToUse?.overpassTimeout, @@ -379,27 +358,23 @@ export default class State { return; } - this.osmConnection = new OsmConnection( - this.featureSwitchIsTesting.data, - this.featureSwitchFakeUser.data, - QueryParameters.GetQueryParameter( + this.osmConnection = new OsmConnection({ + changes: this.changes, + dryRun: this.featureSwitchIsTesting.data, + fakeUser: this.featureSwitchFakeUser.data, + allElements: this.allElements, + oauth_token: QueryParameters.GetQueryParameter( "oauth_token", undefined, "Used to complete the login" ), - layoutToUse?.id, - true, - // @ts-ignore - this.featureSwitchApiURL.data - ); + layoutName: layoutToUse?.id, + osmConfiguration: <'osm' | 'osm-test'>this.featureSwitchApiURL.data + }) - this.allElements = new ElementStorage(); - this.changes = new Changes(); new ChangeToElementsActor(this.changes, this.allElements) - this.osmApiFeatureSource = new OsmApiFeatureSource(Constants.useOsmApiAt, this) - new PendingChangesUploader(this.changes, this.selectedElement); this.mangroveIdentity = new MangroveIdentity( @@ -423,11 +398,11 @@ export default class State { Locale.language .addCallback((currentLanguage) => { - const layoutToUse = self.layoutToUse.data; + const layoutToUse = self.layoutToUse; if (layoutToUse === undefined) { return; } - if (this.layoutToUse.data.language.indexOf(currentLanguage) < 0) { + if (this.layoutToUse.language.indexOf(currentLanguage) < 0) { console.log( "Resetting language to", layoutToUse.language[0], @@ -441,7 +416,7 @@ export default class State { }) .ping(); - new TitleHandler(this.layoutToUse, this.selectedElement, this.allElements); + new TitleHandler(this); } private static asFloat(source: UIEventSource): UIEventSource { diff --git a/UI/Base/Img.ts b/UI/Base/Img.ts index 947536b05e..876265b861 100644 --- a/UI/Base/Img.ts +++ b/UI/Base/Img.ts @@ -10,6 +10,9 @@ export default class Img extends BaseUIElement { fallbackImage?: string }) { super(); + if(src === undefined || src === "undefined"){ + throw "Undefined src for image" + } this._src = src; this._rawSvg = rawSvg; this._options = options; diff --git a/UI/Base/Lazy.ts b/UI/Base/Lazy.ts new file mode 100644 index 0000000000..e2b846cd8b --- /dev/null +++ b/UI/Base/Lazy.ts @@ -0,0 +1,16 @@ +import BaseUIElement from "../BaseUIElement"; + +export default class Lazy extends BaseUIElement{ + private readonly _f: () => BaseUIElement; + + constructor(f: () => BaseUIElement) { + super(); + this._f = f; + } + + protected InnerConstructElement(): HTMLElement { + // The caching of the BaseUIElement will guarantee that _f will only be called once + return this._f().ConstructElement(); + } + +} \ No newline at end of file diff --git a/UI/Base/Loading.ts b/UI/Base/Loading.ts index 8711dec0d5..c2636c1f33 100644 --- a/UI/Base/Loading.ts +++ b/UI/Base/Loading.ts @@ -1,7 +1,17 @@ import {FixedUiElement} from "./FixedUiElement"; +import {Translation} from "../i18n/Translation"; +import Combine from "./Combine"; +import Svg from "../../Svg"; +import Translations from "../i18n/Translations"; -export default class Loading extends FixedUiElement { - constructor() { - super("Loading..."); // TODO to be improved +export default class Loading extends Combine { + constructor(msg?: Translation | string) { + const t = Translations.T(msg ) ?? Translations.t.general.loading.Clone(); + t.SetClass("pl-2") + super([ + Svg.loading_svg().SetClass("animate-spin").SetStyle("width: 1.5rem; height: 1.5rem;"), + t + ]) + this.SetClass("flex p-1") } } \ No newline at end of file diff --git a/UI/Base/Minimap.ts b/UI/Base/Minimap.ts index 0c8cf36008..32cdadbd63 100644 --- a/UI/Base/Minimap.ts +++ b/UI/Base/Minimap.ts @@ -1,208 +1,37 @@ import BaseUIElement from "../BaseUIElement"; -import * as L from "leaflet"; -import {Map} from "leaflet"; -import {UIEventSource} from "../../Logic/UIEventSource"; import Loc from "../../Models/Loc"; import BaseLayer from "../../Models/BaseLayer"; -import AvailableBaseLayers from "../../Logic/Actors/AvailableBaseLayers"; -import {Utils} from "../../Utils"; +import {UIEventSource} from "../../Logic/UIEventSource"; +import {BBox} from "../../Logic/BBox"; -export default class Minimap extends BaseUIElement { +export interface MinimapOptions { + background?: UIEventSource, + location?: UIEventSource, + bounds?: UIEventSource, + allowMoving?: boolean, + leafletOptions?: any, + attribution?: BaseUIElement | boolean, + onFullyLoaded?: (leaflet: L.Map) => void, + leafletMap?: UIEventSource, + lastClickLocation?: UIEventSource<{ lat: number, lon: number }> +} - private static _nextId = 0; - public readonly leafletMap: UIEventSource - private readonly _id: string; - private readonly _background: UIEventSource; - private readonly _location: UIEventSource; - private _isInited = false; - private _allowMoving: boolean; - private readonly _leafletoptions: any; - private readonly _onFullyLoaded: (leaflet: L.Map) => void - private readonly _attribution: BaseUIElement | boolean; - private readonly _lastClickLocation: UIEventSource<{ lat: number; lon: number }>; +export interface MinimapObj { + readonly leafletMap: UIEventSource, + installBounds(factor: number | BBox, showRange?: boolean) : void +} - constructor(options?: { - background?: UIEventSource, - location?: UIEventSource, - allowMoving?: boolean, - leafletOptions?: any, - attribution?: BaseUIElement | boolean, - onFullyLoaded?: (leaflet: L.Map) => void, - leafletMap?: UIEventSource, - lastClickLocation?: UIEventSource<{ lat: number, lon: number }> - } - ) { - super() - options = options ?? {} - this.leafletMap = options.leafletMap ?? new UIEventSource(undefined) - this._background = options?.background ?? new UIEventSource(AvailableBaseLayers.osmCarto) - this._location = options?.location ?? new UIEventSource({lat: 0, lon: 0, zoom: 1}) - this._id = "minimap" + Minimap._nextId; - this._allowMoving = options.allowMoving ?? true; - this._leafletoptions = options.leafletOptions ?? {} - this._onFullyLoaded = options.onFullyLoaded - this._attribution = options.attribution - this._lastClickLocation = options.lastClickLocation; - Minimap._nextId++ +export default class Minimap { + /** + * A stub implementation. The actual implementation is injected later on, but only in the browser. + * importing leaflet crashes node-ts, which is pretty annoying considering the fact that a lot of scripts use it + */ + /** + * Construct a minimap + */ + public static createMiniMap: (options: MinimapOptions) => (BaseUIElement & MinimapObj) = (_) => { + throw "CreateMinimap hasn't been initialized yet. Please call MinimapImplementation.initialize()" } - protected InnerConstructElement(): HTMLElement { - const div = document.createElement("div") - div.id = this._id; - div.style.height = "100%" - div.style.width = "100%" - div.style.minWidth = "40px" - div.style.minHeight = "40px" - div.style.position = "relative" - const wrapper = document.createElement("div") - wrapper.appendChild(div) - const self = this; - // @ts-ignore - const resizeObserver = new ResizeObserver(_ => { - self.InitMap(); - self.leafletMap?.data?.invalidateSize() - }); - - resizeObserver.observe(div); - return wrapper; - - } - - private InitMap() { - if (this._constructedHtmlElement === undefined) { - // This element isn't initialized yet - return; - } - - if (document.getElementById(this._id) === null) { - // not yet attached, we probably got some other event - return; - } - - if (this._isInited) { - return; - } - this._isInited = true; - const location = this._location; - const self = this; - let currentLayer = this._background.data.layer() - const options = { - center: <[number, number]>[location.data?.lat ?? 0, location.data?.lon ?? 0], - zoom: location.data?.zoom ?? 2, - layers: [currentLayer], - zoomControl: false, - attributionControl: this._attribution !== undefined, - dragging: this._allowMoving, - scrollWheelZoom: this._allowMoving, - doubleClickZoom: this._allowMoving, - keyboard: this._allowMoving, - touchZoom: this._allowMoving, - // Disabling this breaks the geojson layer - don't ask me why! zoomAnimation: this._allowMoving, - fadeAnimation: this._allowMoving, - } - - Utils.Merge(this._leafletoptions, options) - - const map = L.map(this._id, options); - if (self._onFullyLoaded !== undefined) { - - currentLayer.on("load", () => { - console.log("Fully loaded all tiles!") - self._onFullyLoaded(map) - }) - } - - // Users are not allowed to zoom to the 'copies' on the left and the right, stuff goes wrong then - // We give a bit of leeway for people on the edges - // Also see: https://www.reddit.com/r/openstreetmap/comments/ih4zzc/mapcomplete_a_new_easytouse_editor/g31ubyv/ - - map.setMaxBounds( - [[-100, -200], [100, 200]] - ); - - if (this._attribution !== undefined) { - if (this._attribution === true) { - map.attributionControl.setPrefix(false) - } else { - map.attributionControl.setPrefix( - ""); - } - } - - this._background.addCallbackAndRun(layer => { - const newLayer = layer.layer() - if (currentLayer !== undefined) { - map.removeLayer(currentLayer); - } - currentLayer = newLayer; - if (self._onFullyLoaded !== undefined) { - - currentLayer.on("load", () => { - console.log("Fully loaded all tiles!") - self._onFullyLoaded(map) - }) - } - map.addLayer(newLayer); - map.setMaxZoom(layer.max_zoom ?? map.getMaxZoom()) - if (self._attribution !== true && self._attribution !== false) { - self._attribution?.AttachTo('leaflet-attribution') - } - - }) - - - let isRecursing = false; - map.on("moveend", function () { - if (isRecursing) { - return - } - if (map.getZoom() === location.data.zoom && - map.getCenter().lat === location.data.lat && - map.getCenter().lng === location.data.lon) { - return; - } - location.data.zoom = map.getZoom(); - location.data.lat = map.getCenter().lat; - location.data.lon = map.getCenter().lng; - isRecursing = true; - location.ping(); - isRecursing = false; // This is ugly, I know - }) - - - location.addCallback(loc => { - const mapLoc = map.getCenter() - const dlat = Math.abs(loc.lat - mapLoc[0]) - const dlon = Math.abs(loc.lon - mapLoc[1]) - - if (dlat < 0.000001 && dlon < 0.000001 && map.getZoom() === loc.zoom) { - return; - } - map.setView([loc.lat, loc.lon], loc.zoom) - }) - - location.map(loc => loc.zoom) - .addCallback(zoom => { - if (Math.abs(map.getZoom() - zoom) > 0.1) { - map.setZoom(zoom, {}); - } - }) - - - if (this._lastClickLocation) { - const lastClickLocation = this._lastClickLocation - map.on("click", function (e) { - // @ts-ignore - lastClickLocation?.setData({lat: e.latlng.lat, lon: e.latlng.lng}) - }); - - map.on("contextmenu", function (e) { - // @ts-ignore - lastClickLocation?.setData({lat: e.latlng.lat, lon: e.latlng.lng}); - }); - } - - this.leafletMap.setData(map) - } } \ No newline at end of file diff --git a/UI/Base/MinimapImplementation.ts b/UI/Base/MinimapImplementation.ts new file mode 100644 index 0000000000..9ec90bd313 --- /dev/null +++ b/UI/Base/MinimapImplementation.ts @@ -0,0 +1,280 @@ +import {Utils} from "../../Utils"; +import BaseUIElement from "../BaseUIElement"; +import {UIEventSource} from "../../Logic/UIEventSource"; +import Loc from "../../Models/Loc"; +import BaseLayer from "../../Models/BaseLayer"; +import AvailableBaseLayers from "../../Logic/Actors/AvailableBaseLayers"; +import * as L from "leaflet"; +import {Map} from "leaflet"; +import Minimap, {MinimapObj, MinimapOptions} from "./Minimap"; +import {BBox} from "../../Logic/BBox"; + +export default class MinimapImplementation extends BaseUIElement implements MinimapObj { + private static _nextId = 0; + public readonly leafletMap: UIEventSource + private readonly _id: string; + private readonly _background: UIEventSource; + private readonly _location: UIEventSource; + private _isInited = false; + private _allowMoving: boolean; + private readonly _leafletoptions: any; + private readonly _onFullyLoaded: (leaflet: L.Map) => void + private readonly _attribution: BaseUIElement | boolean; + private readonly _lastClickLocation: UIEventSource<{ lat: number; lon: number }>; + private readonly _bounds: UIEventSource | undefined; + + private constructor(options: MinimapOptions) { + super() + options = options ?? {} + this.leafletMap = options.leafletMap ?? new UIEventSource(undefined) + this._background = options?.background ?? new UIEventSource(AvailableBaseLayers.osmCarto) + this._location = options?.location ?? new UIEventSource({lat: 0, lon: 0, zoom: 1}) + this._bounds = options?.bounds; + this._id = "minimap" + MinimapImplementation._nextId; + this._allowMoving = options.allowMoving ?? true; + this._leafletoptions = options.leafletOptions ?? {} + this._onFullyLoaded = options.onFullyLoaded + this._attribution = options.attribution + this._lastClickLocation = options.lastClickLocation; + MinimapImplementation._nextId++ + + } + + public static initialize() { + Minimap.createMiniMap = options => new MinimapImplementation(options) + } + + public installBounds(factor: number | BBox, showRange?: boolean) { + this.leafletMap.addCallbackD(leaflet => { + let bounds; + if (typeof factor === "number") { + bounds = leaflet.getBounds() + leaflet.setMaxBounds(bounds.pad(factor)) + } else { + // @ts-ignore + leaflet.setMaxBounds(factor.toLeaflet()) + bounds = leaflet.getBounds() + } + + if (showRange) { + const data = { + type: "FeatureCollection", + features: [{ + "type": "Feature", + "geometry": { + "type": "LineString", + "coordinates": [ + [ + bounds.getEast(), + bounds.getNorth() + ], + [ + bounds.getWest(), + bounds.getNorth() + ], + [ + bounds.getWest(), + bounds.getSouth() + ], + + [ + bounds.getEast(), + bounds.getSouth() + ], + [ + bounds.getEast(), + bounds.getNorth() + ] + ] + } + }] + } + // @ts-ignore + L.geoJSON(data, { + style: { + color: "#f00", + weight: 2, + opacity: 0.4 + } + }).addTo(leaflet) + } + }) + } + + protected InnerConstructElement(): HTMLElement { + const div = document.createElement("div") + div.id = this._id; + div.style.height = "100%" + div.style.width = "100%" + div.style.minWidth = "40px" + div.style.minHeight = "40px" + div.style.position = "relative" + const wrapper = document.createElement("div") + wrapper.appendChild(div) + const self = this; + // @ts-ignore + const resizeObserver = new ResizeObserver(_ => { + try { + self.InitMap(); + self.leafletMap?.data?.invalidateSize() + } catch (e) { + console.warn("Could not construct a minimap:", e) + } + }); + + resizeObserver.observe(div); + return wrapper; + + } + + private InitMap() { + if (this._constructedHtmlElement === undefined) { + // This element isn't initialized yet + return; + } + + if (document.getElementById(this._id) === null) { + // not yet attached, we probably got some other event + return; + } + + if (this._isInited) { + return; + } + this._isInited = true; + const location = this._location; + const self = this; + let currentLayer = this._background.data.layer() + let latLon = <[number, number]>[location.data?.lat ?? 0, location.data?.lon ?? 0] + if(isNaN(latLon[0]) || isNaN(latLon[1])){ + latLon = [0,0] + } + const options = { + center: latLon, + zoom: location.data?.zoom ?? 2, + layers: [currentLayer], + zoomControl: false, + attributionControl: this._attribution !== undefined, + dragging: this._allowMoving, + scrollWheelZoom: this._allowMoving, + doubleClickZoom: this._allowMoving, + keyboard: this._allowMoving, + touchZoom: this._allowMoving, + // Disabling this breaks the geojson layer - don't ask me why! zoomAnimation: this._allowMoving, + fadeAnimation: this._allowMoving, + } + + Utils.Merge(this._leafletoptions, options) + + const map = L.map(this._id, options); + if (self._onFullyLoaded !== undefined) { + + currentLayer.on("load", () => { + console.log("Fully loaded all tiles!") + self._onFullyLoaded(map) + }) + } + + // Users are not allowed to zoom to the 'copies' on the left and the right, stuff goes wrong then + // We give a bit of leeway for people on the edges + // Also see: https://www.reddit.com/r/openstreetmap/comments/ih4zzc/mapcomplete_a_new_easytouse_editor/g31ubyv/ + + map.setMaxBounds( + [[-100, -200], [100, 200]] + ); + + if (this._attribution !== undefined) { + if (this._attribution === true) { + map.attributionControl.setPrefix(false) + } else { + map.attributionControl.setPrefix( + ""); + } + } + + this._background.addCallbackAndRun(layer => { + const newLayer = layer.layer() + if (currentLayer !== undefined) { + map.removeLayer(currentLayer); + } + currentLayer = newLayer; + if (self._onFullyLoaded !== undefined) { + + currentLayer.on("load", () => { + console.log("Fully loaded all tiles!") + self._onFullyLoaded(map) + }) + } + map.addLayer(newLayer); + map.setMaxZoom(layer.max_zoom ?? map.getMaxZoom()) + if (self._attribution !== true && self._attribution !== false) { + self._attribution?.AttachTo('leaflet-attribution') + } + + }) + + + let isRecursing = false; + map.on("moveend", function () { + if (isRecursing) { + return + } + if (map.getZoom() === location.data.zoom && + map.getCenter().lat === location.data.lat && + map.getCenter().lng === location.data.lon) { + return; + } + location.data.zoom = map.getZoom(); + location.data.lat = map.getCenter().lat; + location.data.lon = map.getCenter().lng; + isRecursing = true; + location.ping(); + + if (self._bounds !== undefined) { + self._bounds.setData(BBox.fromLeafletBounds(map.getBounds())) + } + + + isRecursing = false; // This is ugly, I know + }) + + + location.addCallback(loc => { + const mapLoc = map.getCenter() + const dlat = Math.abs(loc.lat - mapLoc[0]) + const dlon = Math.abs(loc.lon - mapLoc[1]) + + if (dlat < 0.000001 && dlon < 0.000001 && map.getZoom() === loc.zoom) { + return; + } + map.setView([loc.lat, loc.lon], loc.zoom) + }) + + location.map(loc => loc.zoom) + .addCallback(zoom => { + if (Math.abs(map.getZoom() - zoom) > 0.1) { + map.setZoom(zoom, {}); + } + }) + + if (self._bounds !== undefined) { + self._bounds.setData(BBox.fromLeafletBounds(map.getBounds())) + } + + + if (this._lastClickLocation) { + const lastClickLocation = this._lastClickLocation + map.on("click", function (e) { + // @ts-ignore + lastClickLocation?.setData({lat: e.latlng.lat, lon: e.latlng.lng}) + }); + + map.on("contextmenu", function (e) { + // @ts-ignore + lastClickLocation?.setData({lat: e.latlng.lat, lon: e.latlng.lng}); + }); + } + + this.leafletMap.setData(map) + } +} \ No newline at end of file diff --git a/UI/Base/ScrollableFullScreen.ts b/UI/Base/ScrollableFullScreen.ts index 4a158ec0a5..da8114f8ca 100644 --- a/UI/Base/ScrollableFullScreen.ts +++ b/UI/Base/ScrollableFullScreen.ts @@ -36,6 +36,8 @@ export default class ScrollableFullScreen extends UIElement { this._component = this.BuildComponent(title("desktop"), content("desktop"), isShown) .SetClass("hidden md:block"); this._fullscreencomponent = this.BuildComponent(title("mobile"), content("mobile"), isShown); + + const self = this; isShown.addCallback(isShown => { if (isShown) { diff --git a/UI/Base/TabbedComponent.ts b/UI/Base/TabbedComponent.ts index b5a33871a5..8f71a462a1 100644 --- a/UI/Base/TabbedComponent.ts +++ b/UI/Base/TabbedComponent.ts @@ -31,7 +31,7 @@ export class TabbedComponent extends Combine { tabs.push(tab) } - const header = new Combine(tabs).SetClass("block tabs-header-bar") + const header = new Combine(tabs).SetClass("tabs-header-bar") const actualContent = new VariableUiElement( openedTabSrc.map(i => contentElements[i]) ) diff --git a/UI/Base/Table.ts b/UI/Base/Table.ts index 11e2fd8715..a26f4a025a 100644 --- a/UI/Base/Table.ts +++ b/UI/Base/Table.ts @@ -12,7 +12,7 @@ export default class Table extends BaseUIElement { contents: (BaseUIElement | string)[][], contentStyle?: string[][]) { super(); - this._contentStyle = contentStyle ?? []; + this._contentStyle = contentStyle ?? [["min-width: 9rem"]]; this._header = header?.map(Translations.W); this._contents = contents.map(row => row.map(Translations.W)); } diff --git a/UI/Base/VariableUIElement.ts b/UI/Base/VariableUIElement.ts index 144459a751..6ee720627e 100644 --- a/UI/Base/VariableUIElement.ts +++ b/UI/Base/VariableUIElement.ts @@ -2,16 +2,16 @@ import {UIEventSource} from "../../Logic/UIEventSource"; import BaseUIElement from "../BaseUIElement"; export class VariableUiElement extends BaseUIElement { - private _element: HTMLElement; + private readonly _contents: UIEventSource; - constructor( - contents: UIEventSource - ) { + constructor(contents: UIEventSource) { super(); + this._contents = contents; + } - this._element = document.createElement("span"); - const el = this._element; - contents.addCallbackAndRun((contents) => { + protected InnerConstructElement(): HTMLElement { + const el = document.createElement("span"); + this._contents.addCallbackAndRun((contents) => { while (el.firstChild) { el.removeChild(el.lastChild); } @@ -35,9 +35,6 @@ export class VariableUiElement extends BaseUIElement { } } }); - } - - protected InnerConstructElement(): HTMLElement { - return this._element; + return el; } } diff --git a/UI/BaseUIElement.ts b/UI/BaseUIElement.ts index 99462acbdb..a18b259724 100644 --- a/UI/BaseUIElement.ts +++ b/UI/BaseUIElement.ts @@ -99,9 +99,9 @@ export default abstract class BaseUIElement { if (this.InnerConstructElement === undefined) { throw "ERROR! This is not a correct baseUIElement: " + this.constructor.name } + + try { - - const el = this.InnerConstructElement(); if (el === undefined) { @@ -132,8 +132,7 @@ export default abstract class BaseUIElement { // @ts-ignore e.consumed = true; } - el.style.pointerEvents = "all"; - el.style.cursor = "pointer"; + el.classList.add("pointer-events-none", "cursor-pointer"); } if (this._onHover !== undefined) { @@ -163,4 +162,4 @@ export default abstract class BaseUIElement { } protected abstract InnerConstructElement(): HTMLElement; -} \ No newline at end of file +} diff --git a/UI/BigComponents/AllDownloads.ts b/UI/BigComponents/AllDownloads.ts index 353a4c38b6..3df26f53de 100644 --- a/UI/BigComponents/AllDownloads.ts +++ b/UI/BigComponents/AllDownloads.ts @@ -32,20 +32,20 @@ export default class AllDownloads extends ScrollableFullScreen { freeDivId: "belowmap", background: State.state.backgroundLayer, location: State.state.locationControl, - features: State.state.featurePipeline.features, + features: State.state.featurePipeline, layout: State.state.layoutToUse, }).isRunning.addCallbackAndRun(isRunning => isExporting.setData(isRunning)) } const loading = Svg.loading_svg().SetClass("animate-rotate"); + const dloadTrans = Translations.t.general.download const icon = new Toggle(loading, Svg.floppy_ui(), isExporting); const text = new Toggle( - new FixedUiElement("Exporting..."), - + dloadTrans.exporting.Clone(), new Combine([ - Translations.t.general.download.downloadAsPdf.Clone().SetClass("font-bold"), - Translations.t.general.download.downloadAsPdfHelper.Clone()] + dloadTrans.downloadAsPdf.Clone().SetClass("font-bold"), + dloadTrans.downloadAsPdfHelper.Clone()] ).SetClass("flex flex-col") .onClick(() => { generatePdf() diff --git a/UI/BigComponents/Attribution.ts b/UI/BigComponents/Attribution.ts index 2d322a9612..4715567982 100644 --- a/UI/BigComponents/Attribution.ts +++ b/UI/BigComponents/Attribution.ts @@ -5,24 +5,24 @@ import {UIEventSource} from "../../Logic/UIEventSource"; import UserDetails from "../../Logic/Osm/OsmConnection"; import Constants from "../../Models/Constants"; import Loc from "../../Models/Loc"; -import * as L from "leaflet" import {VariableUiElement} from "../Base/VariableUIElement"; import LayoutConfig from "../../Models/ThemeConfig/LayoutConfig"; +import {BBox} from "../../Logic/BBox"; /** * The bottom right attribution panel in the leaflet map */ export default class Attribution extends Combine { - constructor(location: UIEventSource, + constructor(location: UIEventSource, userDetails: UIEventSource, - layoutToUse: UIEventSource, - leafletMap: UIEventSource) { + layoutToUse: LayoutConfig, + currentBounds: UIEventSource) { const mapComplete = new Link(`Mapcomplete ${Constants.vNumber}`, 'https://github.com/pietervdvn/MapComplete', true); const reportBug = new Link(Svg.bug_ui().SetClass("small-image"), "https://github.com/pietervdvn/MapComplete/issues", true); - const layoutId = layoutToUse?.data?.id; + const layoutId = layoutToUse?.id; const now = new Date() // Note: getMonth is zero-index, we want 1-index but with one substracted, so it checks out! const startDate = now.getFullYear() + "-" + now.getMonth() + "-" + now.getDate() @@ -43,7 +43,7 @@ export default class Attribution extends Combine { if (userDetails.csCount < Constants.userJourney.tagsVisibleAndWikiLinked) { return undefined; } - const bounds: any = leafletMap?.data?.getBounds(); + const bounds: any = currentBounds.data; if (bounds === undefined) { return undefined } @@ -55,7 +55,7 @@ export default class Attribution extends Combine { const josmLink = `http://127.0.0.1:8111/load_and_zoom?left=${left}&right=${right}&top=${top}&bottom=${bottom}` return new Link(Svg.josm_logo_ui().SetClass("small-image"), josmLink, true); }, - [location, leafletMap] + [location, currentBounds] ) ) super([mapComplete, reportBug, stats, editHere, editWithJosm, mapillary]); diff --git a/UI/BigComponents/AttributionPanel.ts b/UI/BigComponents/AttributionPanel.ts index 7a0bcef815..c18c8d2875 100644 --- a/UI/BigComponents/AttributionPanel.ts +++ b/UI/BigComponents/AttributionPanel.ts @@ -20,16 +20,19 @@ export default class AttributionPanel extends Combine { private static LicenseObject = AttributionPanel.GenerateLicenses(); - constructor(layoutToUse: UIEventSource, contributions: UIEventSource>) { + constructor(layoutToUse: LayoutConfig, contributions: UIEventSource>) { super([ Translations.t.general.attribution.attributionContent, - ((layoutToUse.data.maintainer ?? "") == "") ? "" : Translations.t.general.attribution.themeBy.Subs({author: layoutToUse.data.maintainer}), - layoutToUse.data.credits, + ((layoutToUse.maintainer ?? "") == "") ? "" : Translations.t.general.attribution.themeBy.Subs({author: layoutToUse.maintainer}), + layoutToUse.credits, "
", - new Attribution(State.state.locationControl, State.state.osmConnection.userDetails, State.state.layoutToUse, State.state.leafletMap), + new Attribution(State.state.locationControl, State.state.osmConnection.userDetails, State.state.layoutToUse, State.state.currentBounds), "
", new VariableUiElement(contributions.map(contributions => { + if(contributions === undefined){ + return "" + } const sorted = Array.from(contributions, ([name, value]) => ({ name, value @@ -62,7 +65,7 @@ export default class AttributionPanel extends Combine { "
", AttributionPanel.CodeContributors(), "

", Translations.t.general.attribution.iconAttribution.title.Clone().SetClass("pt-6 pb-3"), "

", - ...Utils.NoNull(Array.from(layoutToUse.data.ExtractImages())) + ...Utils.NoNull(Array.from(layoutToUse.ExtractImages())) .map(AttributionPanel.IconAttribution) ]); this.SetClass("flex flex-col link-underline overflow-hidden") diff --git a/UI/BigComponents/BackgroundSelector.ts b/UI/BigComponents/BackgroundSelector.ts index d13b4e9c0e..f5d1a14150 100644 --- a/UI/BigComponents/BackgroundSelector.ts +++ b/UI/BigComponents/BackgroundSelector.ts @@ -25,7 +25,9 @@ export default class BackgroundSelector extends VariableUiElement { if (baseLayers.length <= 1) { return undefined; } - return new DropDown(Translations.t.general.backgroundMap.Clone(), baseLayers, State.state.backgroundLayer) + return new DropDown(Translations.t.general.backgroundMap.Clone(), baseLayers, State.state.backgroundLayer, { + select_class: 'bg-indigo-100 p-1 rounded hover:bg-indigo-200 w-full' + }) } ) ) diff --git a/UI/BigComponents/DownloadPanel.ts b/UI/BigComponents/DownloadPanel.ts index e291bf6b78..552f6f3576 100644 --- a/UI/BigComponents/DownloadPanel.ts +++ b/UI/BigComponents/DownloadPanel.ts @@ -2,54 +2,114 @@ import {SubtleButton} from "../Base/SubtleButton"; import Svg from "../../Svg"; import Translations from "../i18n/Translations"; import State from "../../State"; -import {FeatureSourceUtils} from "../../Logic/FeatureSource/FeatureSource"; import {Utils} from "../../Utils"; import Combine from "../Base/Combine"; import CheckBoxes from "../Input/Checkboxes"; import {GeoOperations} from "../../Logic/GeoOperations"; import Toggle from "../Input/Toggle"; import Title from "../Base/Title"; +import FeaturePipeline from "../../Logic/FeatureSource/FeaturePipeline"; +import {UIEventSource} from "../../Logic/UIEventSource"; +import SimpleMetaTagger from "../../Logic/SimpleMetaTagger"; +import LayoutConfig from "../../Models/ThemeConfig/LayoutConfig"; +import {meta} from "@turf/turf"; +import {BBox} from "../../Logic/BBox"; export class DownloadPanel extends Toggle { + constructor() { + const state: { + featurePipeline: FeaturePipeline, + layoutToUse: LayoutConfig, + currentBounds: UIEventSource + } = State.state + + const t = Translations.t.general.download - const somethingLoaded = State.state.featurePipeline.features.map(features => features.length > 0); + const name = State.state.layoutToUse.id; + const includeMetaToggle = new CheckBoxes([t.includeMetaData.Clone()]) const metaisIncluded = includeMetaToggle.GetValue().map(selected => selected.length > 0) + + const buttonGeoJson = new SubtleButton(Svg.floppy_ui(), new Combine([t.downloadGeojson.Clone().SetClass("font-bold"), t.downloadGeoJsonHelper.Clone()]).SetClass("flex flex-col")) .onClick(() => { - const geojson = FeatureSourceUtils.extractGeoJson(State.state.featurePipeline, {metadata: metaisIncluded.data}) - const name = State.state.layoutToUse.data.id; - Utils.offerContentsAsDownloadableFile(JSON.stringify(geojson), + const geojson = DownloadPanel.getCleanGeoJson(state, metaisIncluded.data) + Utils.offerContentsAsDownloadableFile(JSON.stringify(geojson, null, " "), `MapComplete_${name}_export_${new Date().toISOString().substr(0, 19)}.geojson`, { mimetype: "application/vnd.geo+json" }); }) + const buttonCSV = new SubtleButton(Svg.floppy_ui(), new Combine( [t.downloadCSV.Clone().SetClass("font-bold"), t.downloadCSVHelper.Clone()]).SetClass("flex flex-col")) .onClick(() => { - const geojson = FeatureSourceUtils.extractGeoJson(State.state.featurePipeline, {metadata: metaisIncluded.data}) + const geojson = DownloadPanel.getCleanGeoJson(state, metaisIncluded.data) const csv = GeoOperations.toCSV(geojson.features) - const name = State.state.layoutToUse.data.id; Utils.offerContentsAsDownloadableFile(csv, `MapComplete_${name}_export_${new Date().toISOString().substr(0, 19)}.csv`, { mimetype: "text/csv" }); - - }) + const downloadButtons = new Combine( - [new Title(t.title), buttonGeoJson, buttonCSV, includeMetaToggle, t.licenseInfo.Clone().SetClass("link-underline")]) + [new Title(t.title), + buttonGeoJson, + buttonCSV, + includeMetaToggle, + t.licenseInfo.Clone().SetClass("link-underline")]) .SetClass("w-full flex flex-col border-4 border-gray-300 rounded-3xl p-4") super( downloadButtons, t.noDataLoaded.Clone(), - somethingLoaded) + state.featurePipeline.somethingLoaded) + } + + private static getCleanGeoJson(state: { + featurePipeline: FeaturePipeline, + currentBounds: UIEventSource + }, includeMetaData: boolean) { + + const resultFeatures = [] + const featureList = state.featurePipeline.GetAllFeaturesWithin(state.currentBounds.data); + for (const tile of featureList) { + for (const feature of tile) { + const cleaned = { + type: feature.type, + geometry: feature.geometry, + properties: {...feature.properties} + } + + if (!includeMetaData) { + for (const key in cleaned.properties) { + if (key === "_lon" || key === "_lat") { + continue; + } + if (key.startsWith("_")) { + delete feature.properties[key] + } + } + } + + const datedKeys = [].concat(SimpleMetaTagger.metatags.filter(tagging => tagging.includesDates).map(tagging => tagging.keys)) + for (const key of datedKeys) { + delete feature.properties[key] + } + + resultFeatures.push(feature) + } + } + + return { + type:"FeatureCollection", + features: resultFeatures + } + } } \ No newline at end of file diff --git a/UI/BigComponents/FilterView.ts b/UI/BigComponents/FilterView.ts index 6e3fae5f72..f5e48161fd 100644 --- a/UI/BigComponents/FilterView.ts +++ b/UI/BigComponents/FilterView.ts @@ -7,8 +7,6 @@ import Combine from "../Base/Combine"; import Translations from "../i18n/Translations"; import {Translation} from "../i18n/Translation"; import Svg from "../../Svg"; -import {TagsFilter} from "../../Logic/Tags/TagsFilter"; -import {And} from "../../Logic/Tags/And"; import {UIEventSource} from "../../Logic/UIEventSource"; import BaseUIElement from "../BaseUIElement"; import State from "../../State"; @@ -16,11 +14,6 @@ import FilteredLayer from "../../Models/FilteredLayer"; import BackgroundSelector from "./BackgroundSelector"; import FilterConfig from "../../Models/ThemeConfig/FilterConfig"; - -/** - * Shows the filter - */ - export default class FilterView extends VariableUiElement { constructor(filteredLayer: UIEventSource) { const backgroundSelector = new Toggle( @@ -40,7 +33,7 @@ export default class FilterView extends VariableUiElement { // Name is not defined: we hide this one return undefined; } - const iconStyle = "width:1.5rem;height:1.5rem;margin-left:1.25rem"; + const iconStyle = "width:1.5rem;height:1.5rem;margin-left:1.25rem;flex-shrink: 0;"; const icon = new Combine([Svg.checkbox_filled]).SetStyle(iconStyle); const iconUnselected = new Combine([Svg.checkbox_empty]).SetStyle( @@ -101,26 +94,51 @@ export default class FilterView extends VariableUiElement { return undefined; } - let listFilterElements: [BaseUIElement, UIEventSource][] = layer.filters.map( + const filterIndexes = new Map() + layer.filters.forEach((f, i) => filterIndexes.set(f.id, i)) + + let listFilterElements: [BaseUIElement, UIEventSource<{ filter: FilterConfig, selected: number }>][] = layer.filters.map( FilterView.createFilter ); - const update = () => { - let listTagsFilters = Utils.NoNull( - listFilterElements.map((input) => input[1].data) - ); - flayer.appliedFilters.setData(new And(listTagsFilters)); - }; + listFilterElements.forEach((inputElement, i) => + inputElement[1].addCallback((changed) => { + const oldValue = flayer.appliedFilters.data + + if(changed === undefined){ + // Lets figure out which filter should be removed + // We know this inputElement corresponds with layer.filters[i] + // SO, if there is a value in 'oldValue' with this filter, we have to recalculated + if(!oldValue.some(f => f.filter === layer.filters[i])){ + // The filter to remove is already gone, we can stop + return; + } + }else if(oldValue.some(f => f.filter === changed.filter && f.selected === changed.selected)){ + // The changed value is already there + return; + } + const listTagsFilters = Utils.NoNull( + listFilterElements.map((input) => input[1].data) + ); - listFilterElements.forEach((inputElement) => - inputElement[1].addCallback((_) => update()) + flayer.appliedFilters.setData(listTagsFilters); + }) ); flayer.appliedFilters.addCallbackAndRun(appliedFilters => { - if (appliedFilters === undefined || appliedFilters.and.length === 0) { - listFilterElements.forEach(filter => filter[1].setData(undefined)) - return + for (let i = 0; i < layer.filters.length; i++){ + const filter = layer.filters[i]; + let foundMatch = undefined + for (const appliedFilter of appliedFilters) { + if(appliedFilter.filter === filter){ + foundMatch = appliedFilter + break; + } + } + + listFilterElements[i][1].setData(foundMatch) } + }) return new Combine(listFilterElements.map(input => input[0].SetClass("mt-3"))) @@ -128,7 +146,7 @@ export default class FilterView extends VariableUiElement { } - private static createFilter(filterConfig: FilterConfig): [BaseUIElement, UIEventSource] { + private static createFilter(filterConfig: FilterConfig): [BaseUIElement, UIEventSource<{ filter: FilterConfig, selected: number }>] { if (filterConfig.options.length === 1) { let option = filterConfig.options[0]; @@ -136,26 +154,42 @@ export default class FilterView extends VariableUiElement { const iconUnselected = Svg.checkbox_empty_svg().SetClass("block mr-2"); const toggle = new Toggle( - new Combine([icon, option.question.Clone()]).SetClass("flex"), - new Combine([iconUnselected, option.question.Clone()]).SetClass("flex") + new Combine([icon, option.question.Clone().SetClass("block")]).SetClass("flex"), + new Combine([iconUnselected, option.question.Clone().SetClass("block")]).SetClass("flex") ) .ToggleOnClick() .SetClass("block m-1") - return [toggle, toggle.isEnabled.map(enabled => enabled ? option.osmTags : undefined, [], tags => tags !== undefined)] + const selected = { + filter: filterConfig, + selected: 0 + } + return [toggle, toggle.isEnabled.map(enabled => enabled ? selected : undefined, [], + f => f?.filter === filterConfig && f?.selected === 0) + ] } let options = filterConfig.options; + const values = options.map((f, i) => ({ + filter: filterConfig, selected: i + })) const radio = new RadioButton( options.map( - (option) => - new FixedInputElement(option.question.Clone(), option.osmTags) + (option, i) => + new FixedInputElement(option.question.Clone().SetClass("block"), i) ), { dontStyle: true } ); - return [radio, radio.GetValue()] + return [radio, + radio.GetValue().map( + i => values[i], + [], + selected => { + return selected?.selected + } + )] } } diff --git a/UI/BigComponents/FullWelcomePaneWithTabs.ts b/UI/BigComponents/FullWelcomePaneWithTabs.ts index a73ac1ce57..8ee36ecec6 100644 --- a/UI/BigComponents/FullWelcomePaneWithTabs.ts +++ b/UI/BigComponents/FullWelcomePaneWithTabs.ts @@ -1,7 +1,5 @@ import State from "../../State"; import ThemeIntroductionPanel from "./ThemeIntroductionPanel"; -import * as personal from "../../assets/themes/personal/personal.json"; -import PersonalLayersPanel from "./PersonalLayersPanel"; import Svg from "../../Svg"; import Translations from "../i18n/Translations"; import ShareScreen from "./ShareScreen"; @@ -15,12 +13,13 @@ import ScrollableFullScreen from "../Base/ScrollableFullScreen"; import BaseUIElement from "../BaseUIElement"; import Toggle from "../Input/Toggle"; import LayoutConfig from "../../Models/ThemeConfig/LayoutConfig"; +import {Utils} from "../../Utils"; export default class FullWelcomePaneWithTabs extends ScrollableFullScreen { constructor(isShown: UIEventSource) { - const layoutToUse = State.state.layoutToUse.data; + const layoutToUse = State.state.layoutToUse; super( () => layoutToUse.title.Clone(), () => FullWelcomePaneWithTabs.GenerateContents(layoutToUse, State.state.osmConnection.userDetails, isShown), @@ -31,9 +30,7 @@ export default class FullWelcomePaneWithTabs extends ScrollableFullScreen { private static ConstructBaseTabs(layoutToUse: LayoutConfig, isShown: UIEventSource): { header: string | BaseUIElement; content: BaseUIElement }[] { let welcome: BaseUIElement = new ThemeIntroductionPanel(isShown); - if (layoutToUse.id === personal.id) { - welcome = new PersonalLayersPanel(); - } + const tabs: { header: string | BaseUIElement, content: BaseUIElement }[] = [ {header: ``, content: welcome}, { @@ -62,9 +59,16 @@ export default class FullWelcomePaneWithTabs extends ScrollableFullScreen { const tabs = FullWelcomePaneWithTabs.ConstructBaseTabs(layoutToUse, isShown) const tabsWithAboutMc = [...FullWelcomePaneWithTabs.ConstructBaseTabs(layoutToUse, isShown)] + + const now = new Date() + const lastWeek = new Date(now.getDate() - 7 * 24 * 60 * 60 * 1000) + const date = lastWeek.getFullYear()+"-"+Utils.TwoDigits(lastWeek.getMonth()+1)+"-"+Utils.TwoDigits(lastWeek.getDate()) + const osmcha_link = `https://osmcha.org/?filters=%7B%22date__gte%22%3A%5B%7B%22label%22%3A%22${date}%22%2C%22value%22%3A%222021-01-01%22%7D%5D%2C%22editor%22%3A%5B%7B%22label%22%3A%22mapcomplete%22%2C%22value%22%3A%22mapcomplete%22%7D%5D%7D` + tabsWithAboutMc.push({ header: Svg.help, - content: new Combine([Translations.t.general.aboutMapcomplete.Clone(), "
Version " + Constants.vNumber]) + content: new Combine([Translations.t.general.aboutMapcomplete.Clone() + .Subs({"osmcha_link": osmcha_link}), "
Version " + Constants.vNumber]) .SetClass("link-underline") } ); diff --git a/UI/BigComponents/ImportButton.ts b/UI/BigComponents/ImportButton.ts new file mode 100644 index 0000000000..1604052647 --- /dev/null +++ b/UI/BigComponents/ImportButton.ts @@ -0,0 +1,59 @@ +import BaseUIElement from "../BaseUIElement"; +import {SubtleButton} from "../Base/SubtleButton"; +import {UIEventSource} from "../../Logic/UIEventSource"; +import Combine from "../Base/Combine"; +import {VariableUiElement} from "../Base/VariableUIElement"; +import Translations from "../i18n/Translations"; +import State from "../../State"; +import Constants from "../../Models/Constants"; +import Toggle from "../Input/Toggle"; +import CreateNewNodeAction from "../../Logic/Osm/Actions/CreateNewNodeAction"; +import {Tag} from "../../Logic/Tags/Tag"; + +export default class ImportButton extends Toggle { + constructor(imageUrl: string | BaseUIElement, message: string | BaseUIElement, + originalTags: UIEventSource, + newTags: UIEventSource, lat: number, lon: number) { + const t = Translations.t.general.add; + const isImported = originalTags.map(tags => tags._imported === "yes") + const appliedTags = new Toggle( + new VariableUiElement( + newTags.map(tgs => { + const parts = [] + for (const tag of tgs) { + parts.push(tag.key + "=" + tag.value) + } + const txt = parts.join(" & ") + return t.presetInfo.Subs({tags: txt}).SetClass("subtle") + })), undefined, + State.state.osmConnection.userDetails.map(ud => ud.csCount >= Constants.userJourney.tagsVisibleAt) + ) + const button = new SubtleButton(imageUrl, message) + + + button.onClick(async () => { + if (isImported.data) { + return + } + originalTags.data["_imported"] = "yes" + originalTags.ping() // will set isImported as per its definition + const newElementAction = new CreateNewNodeAction(newTags.data, lat, lon) + await State.state.changes.applyAction(newElementAction) + State.state.selectedElement.setData(State.state.allElements.ContainingFeatures.get( + newElementAction.newElementId + )) + console.log("Did set selected element to", State.state.allElements.ContainingFeatures.get( + newElementAction.newElementId + )) + + + }) + + const withLoadingCheck = new Toggle( + t.stillLoading, + new Combine([button, appliedTags]).SetClass("flex flex-col"), + State.state.featurePipeline.runningQuery + ) + super(t.hasBeenImported, withLoadingCheck, isImported) + } +} \ No newline at end of file diff --git a/UI/BigComponents/LeftControls.ts b/UI/BigComponents/LeftControls.ts index db58abac68..7ee2a74e64 100644 --- a/UI/BigComponents/LeftControls.ts +++ b/UI/BigComponents/LeftControls.ts @@ -9,18 +9,21 @@ import MapControlButton from "../MapControlButton"; import Svg from "../../Svg"; import AllDownloads from "./AllDownloads"; import FilterView from "./FilterView"; -import FeatureSource from "../../Logic/FeatureSource/FeatureSource"; +import {UIEventSource} from "../../Logic/UIEventSource"; +import FeaturePipeline from "../../Logic/FeatureSource/FeaturePipeline"; +import Loc from "../../Models/Loc"; +import {BBox} from "../../Logic/BBox"; export default class LeftControls extends Combine { - constructor(featureSource: FeatureSource) { + constructor(state: {featurePipeline: FeaturePipeline, currentBounds: UIEventSource, locationControl: UIEventSource}) { const toggledCopyright = new ScrollableFullScreen( () => Translations.t.general.attribution.attributionTitle.Clone(), () => new AttributionPanel( State.state.layoutToUse, - new ContributorCount(featureSource).Contributors + new ContributorCount(state).Contributors ), undefined ); diff --git a/UI/BigComponents/MoreScreen.ts b/UI/BigComponents/MoreScreen.ts index 9d8fa44f93..01e4b9f95b 100644 --- a/UI/BigComponents/MoreScreen.ts +++ b/UI/BigComponents/MoreScreen.ts @@ -126,7 +126,7 @@ export default class MoreScreen extends Combine { if (layout.hideFromOverview) { return undefined; } - if (layout.id === State.state.layoutToUse.data?.id) { + if (layout.id === State.state.layoutToUse?.id) { return undefined; } diff --git a/UI/BigComponents/PersonalLayersPanel.ts b/UI/BigComponents/PersonalLayersPanel.ts deleted file mode 100644 index e5a41b733b..0000000000 --- a/UI/BigComponents/PersonalLayersPanel.ts +++ /dev/null @@ -1,120 +0,0 @@ -import {AllKnownLayouts} from "../../Customizations/AllKnownLayouts"; -import Svg from "../../Svg"; -import State from "../../State"; -import Combine from "../Base/Combine"; -import Toggle from "../Input/Toggle"; -import {SubtleButton} from "../Base/SubtleButton"; -import Translations from "../i18n/Translations"; -import BaseUIElement from "../BaseUIElement"; -import {VariableUiElement} from "../Base/VariableUIElement"; -import {UIEventSource} from "../../Logic/UIEventSource"; -import LayerConfig from "../../Models/ThemeConfig/LayerConfig"; - -export default class PersonalLayersPanel extends VariableUiElement { - - constructor() { - super( - State.state.installedThemes.map(installedThemes => { - const t = Translations.t.favourite; - - // Lets get all the layers - const allThemes = AllKnownLayouts.layoutsList.concat(installedThemes.map(layout => layout.layout)) - .filter(theme => !theme.hideFromOverview) - - const allLayers = [] - { - const seenLayers = new Set() - for (const layers of allThemes.map(theme => theme.layers)) { - for (const layer of layers) { - if (seenLayers.has(layer.id)) { - continue - } - seenLayers.add(layer.id) - allLayers.push(layer) - } - } - } - - // Time to create a panel based on them! - const panel: BaseUIElement = new Combine(allLayers.map(PersonalLayersPanel.CreateLayerToggle)); - - - return new Toggle( - new Combine([ - t.panelIntro.Clone(), - panel - ]).SetClass("flex flex-col"), - new SubtleButton( - Svg.osm_logo_ui(), - t.loginNeeded.Clone().SetClass("text-center") - ).onClick(() => State.state.osmConnection.AttemptLogin()), - State.state.osmConnection.isLoggedIn - ) - }) - ) - } - - /*** - * Creates a toggle for the given layer, which'll update State.state.favouriteLayers right away - * @param layer - * @constructor - * @private - */ - private static CreateLayerToggle(layer: LayerConfig): Toggle { - let icon: BaseUIElement = new Combine([layer.GenerateLeafletStyle( - new UIEventSource({id: "node/-1"}), - false - ).icon.html]).SetClass("relative") - let iconUnset = new Combine([layer.GenerateLeafletStyle( - new UIEventSource({id: "node/-1"}), - false - ).icon.html]).SetClass("relative") - - iconUnset.SetStyle("opacity:0.1") - - let name = layer.name; - if (name === undefined) { - return undefined; - } - const content = new Combine([ - Translations.WT(name).Clone().SetClass("font-bold"), - Translations.WT(layer.description)?.Clone() - ]).SetClass("flex flex-col") - - const contentUnselected = new Combine([ - Translations.WT(name).Clone().SetClass("font-bold"), - Translations.WT(layer.description)?.Clone() - ]).SetClass("flex flex-col line-through") - - return new Toggle( - new SubtleButton( - icon, - content), - new SubtleButton( - iconUnset, - contentUnselected - ), - State.state.favouriteLayers.map(favLayers => { - return favLayers.indexOf(layer.id) >= 0 - }, [], (selected, current) => { - if (!selected && current.indexOf(layer.id) <= 0) { - // Not selected and not contained: nothing to change: we return current as is - return current; - } - if (selected && current.indexOf(layer.id) >= 0) { - // Selected and contained: this is fine! - return current; - } - const clone = [...current] - if (selected) { - clone.push(layer.id) - } else { - clone.splice(clone.indexOf(layer.id), 1) - } - return clone - }) - ).ToggleOnClick(); - } - - -} \ No newline at end of file diff --git a/UI/BigComponents/ShareScreen.ts b/UI/BigComponents/ShareScreen.ts index 9b97ef4417..32e1b0ce08 100644 --- a/UI/BigComponents/ShareScreen.ts +++ b/UI/BigComponents/ShareScreen.ts @@ -17,7 +17,7 @@ import LayerConfig from "../../Models/ThemeConfig/LayerConfig"; export default class ShareScreen extends Combine { constructor(layout: LayoutConfig = undefined, layoutDefinition: string = undefined) { - layout = layout ?? State.state?.layoutToUse?.data; + layout = layout ?? State.state?.layoutToUse; layoutDefinition = layoutDefinition ?? State.state?.layoutDefinition; const tr = Translations.t.general.sharescreen; diff --git a/UI/BigComponents/SimpleAddUI.ts b/UI/BigComponents/SimpleAddUI.ts index 6a3729bc56..ad6a73ec52 100644 --- a/UI/BigComponents/SimpleAddUI.ts +++ b/UI/BigComponents/SimpleAddUI.ts @@ -19,7 +19,7 @@ import CreateNewNodeAction from "../../Logic/Osm/Actions/CreateNewNodeAction"; import {OsmObject, OsmWay} from "../../Logic/Osm/OsmObject"; import PresetConfig from "../../Models/ThemeConfig/PresetConfig"; import FilteredLayer from "../../Models/FilteredLayer"; -import {And} from "../../Logic/Tags/And"; +import {BBox} from "../../Logic/BBox"; /* * The SimpleAddUI is a single panel, which can have multiple states: @@ -39,8 +39,6 @@ interface PresetInfo extends PresetConfig { export default class SimpleAddUI extends Toggle { constructor(isShown: UIEventSource) { - - const loginButton = new SubtleButton(Svg.osm_logo_ui(), Translations.t.general.add.pleaseLogin.Clone()) .onClick(() => State.state.osmConnection.AttemptLogin()); const readYourMessages = new Combine([ @@ -52,23 +50,19 @@ export default class SimpleAddUI extends Toggle { const selectedPreset = new UIEventSource(undefined); isShown.addCallback(_ => selectedPreset.setData(undefined)) // Clear preset selection when the UI is closed/opened - + State.state.LastClickLocation.addCallback( _ => selectedPreset.setData(undefined)) + const presetsOverview = SimpleAddUI.CreateAllPresetsPanel(selectedPreset) - function createNewPoint(tags: any[], location: { lat: number, lon: number }, snapOntoWay?: OsmWay) { - console.trace("Creating a new point") + async function createNewPoint(tags: any[], location: { lat: number, lon: number }, snapOntoWay?: OsmWay) { const newElementAction = new CreateNewNodeAction(tags, location.lat, location.lon, {snapOnto: snapOntoWay}) - State.state.changes.applyAction(newElementAction) + await State.state.changes.applyAction(newElementAction) selectedPreset.setData(undefined) isShown.setData(false) State.state.selectedElement.setData(State.state.allElements.ContainingFeatures.get( newElementAction.newElementId )) - console.log("Did set selected element to", State.state.allElements.ContainingFeatures.get( - newElementAction.newElementId - )) - } const addUi = new VariableUiElement( @@ -86,11 +80,7 @@ export default class SimpleAddUI extends Toggle { return true; }) } - - }, - - () => { selectedPreset.setData(undefined) }) @@ -102,9 +92,9 @@ export default class SimpleAddUI extends Toggle { new Toggle( new Toggle( new Toggle( - Translations.t.general.add.stillLoading.Clone().SetClass("alert"), addUi, - State.state.layerUpdater.runningQuery + Translations.t.general.add.stillLoading.Clone().SetClass("alert"), + State.state.featurePipeline.somethingLoaded ), Translations.t.general.add.zoomInFurther.Clone().SetClass("alert"), State.state.locationControl.map(loc => loc.zoom >= Constants.userJourney.minZoomLevelToAddNewPoints) @@ -130,6 +120,7 @@ export default class SimpleAddUI extends Toggle { let location = State.state.LastClickLocation; let preciseInput: LocationInput = undefined if (preset.preciseInput !== undefined) { + // We uncouple the event source const locationSrc = new UIEventSource({ lat: location.data.lat, lon: location.data.lon, @@ -141,25 +132,48 @@ export default class SimpleAddUI extends Toggle { backgroundLayer = AvailableBaseLayers.SelectBestLayerAccordingTo(locationSrc, new UIEventSource(preset.preciseInput.preferredBackground)) } - let features: UIEventSource<{ feature: any }[]> = undefined + let snapToFeatures: UIEventSource<{ feature: any }[]> = undefined + let mapBounds: UIEventSource = undefined if (preset.preciseInput.snapToLayers) { - // We have to snap to certain layers. - // Lets fetch tehm - const asSet = new Set(preset.preciseInput.snapToLayers) - features = State.state.featurePipeline.features.map(f => f.filter(feat => asSet.has(feat.feature._matching_layer_id))) + snapToFeatures = new UIEventSource<{ feature: any }[]>([]) + mapBounds = new UIEventSource(undefined) } + + const tags = TagUtils.KVtoProperties(preset.tags ?? []); - console.log("Opening precise input ", preset.preciseInput, "with tags", tags) preciseInput = new LocationInput({ mapBackground: backgroundLayer, centerLocation: locationSrc, - snapTo: features, + snapTo: snapToFeatures, snappedPointTags: tags, - maxSnapDistance: preset.preciseInput.maxSnapDistance - + maxSnapDistance: preset.preciseInput.maxSnapDistance, + bounds: mapBounds }) preciseInput.SetClass("h-32 rounded-xl overflow-hidden border border-gray").SetStyle("height: 12rem;") + + + if (preset.preciseInput.snapToLayers) { + // We have to snap to certain layers. + // Lets fetch them + + let loadedBbox : BBox= undefined + mapBounds?.addCallbackAndRunD(bbox => { + if(loadedBbox !== undefined && bbox.isContainedIn(loadedBbox)){ + // All is already there + // return; + } + + bbox = bbox.pad(2); + loadedBbox = bbox; + const allFeatures: {feature: any}[] = [] + preset.preciseInput.snapToLayers.forEach(layerId => { + State.state.featurePipeline.GetFeaturesWithin(layerId, bbox).forEach(feats => allFeatures.push(...feats.map(f => ({feature :f})))) + }) + snapToFeatures.setData(allFeatures) + }) + } + } @@ -208,7 +222,7 @@ export default class SimpleAddUI extends Toggle { ] ).SetClass("flex flex-col") ).onClick(() => { - preset.layerToAddTo.appliedFilters.setData(new And([])) + preset.layerToAddTo.appliedFilters.setData([]) cancel() }) @@ -216,8 +230,23 @@ export default class SimpleAddUI extends Toggle { openLayerOrConfirm, disableFilter, preset.layerToAddTo.appliedFilters.map(filters => { - console.log("Current filters are ", filters) - return filters === undefined || filters.normalize().and.length === 0; + if(filters === undefined || filters.length === 0){ + return true; + } + for (const filter of filters) { + if(filter.selected === 0 && filter.filter.options.length === 1){ + return false; + } + if(filter.selected !== undefined){ + const tags = filter.filter.options[filter.selected].osmTags + if(tags !== undefined && tags["and"]?.length !== 0){ + // This actually doesn't filter anything at all + return false; + } + } + } + return true + }) ) diff --git a/UI/BigComponents/ThemeIntroductionPanel.ts b/UI/BigComponents/ThemeIntroductionPanel.ts index 8c4add4d97..fec58bb29f 100644 --- a/UI/BigComponents/ThemeIntroductionPanel.ts +++ b/UI/BigComponents/ThemeIntroductionPanel.ts @@ -2,21 +2,17 @@ import State from "../../State"; import Combine from "../Base/Combine"; import LanguagePicker from "../LanguagePicker"; import Translations from "../i18n/Translations"; -import {VariableUiElement} from "../Base/VariableUIElement"; import Toggle from "../Input/Toggle"; import {SubtleButton} from "../Base/SubtleButton"; import Svg from "../../Svg"; import {UIEventSource} from "../../Logic/UIEventSource"; -export default class ThemeIntroductionPanel extends VariableUiElement { +export default class ThemeIntroductionPanel extends Combine { constructor(isShown: UIEventSource) { + const layout = State.state.layoutToUse - const languagePicker = - new VariableUiElement( - State.state.layoutToUse.map(layout => LanguagePicker.CreateLanguagePicker(layout.language, Translations.t.general.pickLanguage.Clone())) - ) - ; + const languagePicker = LanguagePicker.CreateLanguagePicker(layout.language, Translations.t.general.pickLanguage.Clone()) const toTheMap = new SubtleButton( undefined, @@ -53,17 +49,16 @@ export default class ThemeIntroductionPanel extends VariableUiElement { State.state.featureSwitchUserbadge ) - - super(State.state.layoutToUse.map(layout => new Combine([ + super([ layout.description.Clone(), "

", toTheMap, loginStatus, - layout.descriptionTail.Clone(), + layout.descriptionTail?.Clone(), "
", languagePicker, ...layout.CustomCodeSnippets() - ]))) + ]) this.SetClass("link-underline") } diff --git a/UI/BigComponents/UserBadge.ts b/UI/BigComponents/UserBadge.ts index 62f74e0ae2..f16d1c5f6e 100644 --- a/UI/BigComponents/UserBadge.ts +++ b/UI/BigComponents/UserBadge.ts @@ -18,7 +18,7 @@ export default class UserBadge extends Toggle { const loginButton = Translations.t.general.loginWithOpenStreetMap .Clone() - .SetClass("userbadge-login pt-3 w-full h-full") + .SetClass("userbadge-login inline-flex justify-center items-center w-full h-full text-lg font-bold min-w-[20em]") .onClick(() => State.state.osmConnection.AttemptLogin()); @@ -47,7 +47,7 @@ export default class UserBadge extends Toggle { }); const linkStyle = "flex items-baseline" - const languagePicker = (LanguagePicker.CreateLanguagePicker(State.state.layoutToUse.data.language) ?? new FixedUiElement("")) + const languagePicker = (LanguagePicker.CreateLanguagePicker(State.state.layoutToUse.language) ?? new FixedUiElement("")) .SetStyle("width:min-content;"); let messageSpan = @@ -133,9 +133,9 @@ export default class UserBadge extends Toggle { ) - this.SetClass("shadow rounded-full h-min overflow-hidden block w-max") + this.SetClass("shadow rounded-full h-min overflow-hidden block w-full md:w-max") } -} \ No newline at end of file +} diff --git a/UI/CenterMessageBox.ts b/UI/CenterMessageBox.ts index 62efc2eadd..fe1ef5c655 100644 --- a/UI/CenterMessageBox.ts +++ b/UI/CenterMessageBox.ts @@ -6,7 +6,7 @@ export default class CenterMessageBox extends VariableUiElement { constructor() { const state = State.state; - const updater = State.state.layerUpdater; + const updater = State.state.featurePipeline; const t = Translations.t.centerMessage; const message = updater.runningQuery.map( isRunning => { diff --git a/UI/ExportPDF.ts b/UI/ExportPDF.ts index aea7561b06..1bb57fb1c2 100644 --- a/UI/ExportPDF.ts +++ b/UI/ExportPDF.ts @@ -1,3 +1,19 @@ + + +import jsPDF from "jspdf"; +import {SimpleMapScreenshoter} from "leaflet-simple-map-screenshoter"; +import {UIEventSource} from "../Logic/UIEventSource"; +import Minimap from "./Base/Minimap"; +import Loc from "../Models/Loc"; +import BaseLayer from "../Models/BaseLayer"; +import {FixedUiElement} from "./Base/FixedUiElement"; +import Translations from "./i18n/Translations"; +import State from "../State"; +import Constants from "../Models/Constants"; +import LayoutConfig from "../Models/ThemeConfig/LayoutConfig"; +import FeaturePipeline from "../Logic/FeatureSource/FeaturePipeline"; +import ShowDataLayer from "./ShowDataLayer/ShowDataLayer"; +import {BBox} from "../Logic/BBox"; /** * Creates screenshoter to take png screenshot * Creates jspdf and downloads it @@ -8,21 +24,6 @@ * - add new layout in "PDFLayout" * -> in there are more instructions */ - -import jsPDF from "jspdf"; -import {SimpleMapScreenshoter} from "leaflet-simple-map-screenshoter"; -import {UIEventSource} from "../Logic/UIEventSource"; -import Minimap from "./Base/Minimap"; -import Loc from "../Models/Loc"; -import {BBox} from "../Logic/GeoOperations"; -import ShowDataLayer from "./ShowDataLayer"; -import BaseLayer from "../Models/BaseLayer"; -import {FixedUiElement} from "./Base/FixedUiElement"; -import Translations from "./i18n/Translations"; -import State from "../State"; -import Constants from "../Models/Constants"; -import LayoutConfig from "../Models/ThemeConfig/LayoutConfig"; - export default class ExportPDF { // dimensions of the map in milimeter public isRunning = new UIEventSource(true) @@ -31,7 +32,7 @@ export default class ExportPDF { private readonly mapH = 210; private readonly scaling = 2 private readonly freeDivId: string; - private readonly _layout: UIEventSource; + private readonly _layout: LayoutConfig; private _screenhotTaken = false; constructor( @@ -39,8 +40,8 @@ export default class ExportPDF { freeDivId: string, location: UIEventSource, background?: UIEventSource - features: UIEventSource<{ feature: any }[]>, - layout: UIEventSource + features: FeaturePipeline, + layout: LayoutConfig } ) { @@ -57,7 +58,7 @@ export default class ExportPDF { zoom: l.zoom + 1 } - const minimap = new Minimap({ + const minimap = Minimap.createMiniMap({ location: new UIEventSource(loc), // We remove the link between the old and the new UI-event source as moving the map while the export is running fucks up the screenshot background: options.background, allowMoving: false, @@ -83,24 +84,20 @@ export default class ExportPDF { minimap.AttachTo(options.freeDivId) // Next: we prepare the features. Only fully contained features are shown - const bounded = options.features.map(feats => { - - const leaflet = minimap.leafletMap.data; - if (leaflet === undefined) { - return feats - } + minimap.leafletMap .addCallbackAndRunD(leaflet => { const bounds = BBox.fromLeafletBounds(leaflet.getBounds().pad(0.2)) - return feats.filter(f => BBox.get(f.feature).isContainedIn(bounds)) - - }, [minimap.leafletMap]) - - // Add the features to the minimap - new ShowDataLayer( - bounded, - minimap.leafletMap, - options.layout, - false - ) + options.features.GetTilesPerLayerWithin(bounds, tile => { + new ShowDataLayer( + { + features: tile, + leafletMap: minimap.leafletMap, + layerToShow: tile.layer.layerDef, + enablePopups: false + } + ) + }) + + }) } @@ -110,13 +107,13 @@ export default class ExportPDF { } private async CreatePdf(leaflet: L.Map) { + console.log("PDF creation started") const t = Translations.t.general.pdf; - const layout = this._layout.data + const layout = this._layout const screenshotter = new SimpleMapScreenshoter(); //minimap op index.html -> hidden daar alles op doen en dan weg //minimap - leaflet map ophalen - boundaries ophalen - State.state.featurePipeline screenshotter.addTo(leaflet); - console.log("Taking screenshot") let doc = new jsPDF('landscape'); @@ -166,7 +163,6 @@ export default class ExportPDF { const imgSource = layout.icon const imgType = imgSource.substr(imgSource.lastIndexOf(".") + 1); img.src = imgSource - console.log(imgType) if (imgType.toLowerCase() === "svg") { new FixedUiElement("").AttachTo(this.freeDivId) diff --git a/UI/Image/AttributedImage.ts b/UI/Image/AttributedImage.ts index 69835e105d..b0bb60d804 100644 --- a/UI/Image/AttributedImage.ts +++ b/UI/Image/AttributedImage.ts @@ -1,24 +1,22 @@ import Combine from "../Base/Combine"; import Attribution from "./Attribution"; import Img from "../Base/Img"; -import ImageAttributionSource from "../../Logic/ImageProviders/ImageAttributionSource"; +import {ProvidedImage} from "../../Logic/ImageProviders/ImageProvider"; import BaseUIElement from "../BaseUIElement"; -import {VariableUiElement} from "../Base/VariableUIElement"; +import {Mapillary} from "../../Logic/ImageProviders/Mapillary"; export class AttributedImage extends Combine { - constructor(urlSource: string, imgSource: ImageAttributionSource) { - const preparedUrl = imgSource.PrepareUrl(urlSource) + constructor(imageInfo: ProvidedImage) { let img: BaseUIElement; let attr: BaseUIElement - if (typeof preparedUrl === "string") { - img = new Img(urlSource); - attr = new Attribution(imgSource.GetAttributionFor(urlSource), imgSource.SourceIcon()) - } else { - img = new VariableUiElement(preparedUrl.map(url => new Img(url, false, {fallbackImage: './assets/svg/blocked.svg'}))) - attr = new VariableUiElement(preparedUrl.map(url => new Attribution(imgSource.GetAttributionFor(urlSource), imgSource.SourceIcon()))) - } + img = new Img(imageInfo.url, false, { + fallbackImage: imageInfo.provider === Mapillary.singleton ? "./assets/svg/blocked.svg" : undefined + }); + attr = new Attribution(imageInfo.provider.GetAttributionFor(imageInfo.url), + imageInfo.provider.SourceIcon(), + ) super([img, attr]); diff --git a/UI/Image/Attribution.ts b/UI/Image/Attribution.ts index 30040482fe..f57911e93e 100644 --- a/UI/Image/Attribution.ts +++ b/UI/Image/Attribution.ts @@ -3,7 +3,7 @@ import Translations from "../i18n/Translations"; import BaseUIElement from "../BaseUIElement"; import {VariableUiElement} from "../Base/VariableUIElement"; import {UIEventSource} from "../../Logic/UIEventSource"; -import {LicenseInfo} from "../../Logic/ImageProviders/Wikimedia"; +import {LicenseInfo} from "../../Logic/ImageProviders/LicenseInfo"; export default class Attribution extends VariableUiElement { @@ -13,17 +13,16 @@ export default class Attribution extends VariableUiElement { } super( license.map((license: LicenseInfo) => { - - if (license?.artist === undefined) { - return undefined; + if(license === undefined){ + return undefined } - + return new Combine([ icon?.SetClass("block left").SetStyle("height: 2em; width: 2em; padding-right: 0.5em;"), new Combine([ - Translations.W(license.artist).SetClass("block font-bold"), - Translations.W((license.license ?? "") === "" ? "CC0" : (license.license ?? "")) + Translations.W(license?.artist ?? ".").SetClass("block font-bold"), + Translations.W((license?.license ?? "") === "" ? "CC0" : (license?.license ?? "")) ]).SetClass("flex flex-col") ]).SetClass("flex flex-row bg-black text-white text-sm absolute bottom-0 left-0 p-0.5 pl-5 pr-3 rounded-lg") diff --git a/UI/Image/DeleteImage.ts b/UI/Image/DeleteImage.ts index a5ee19266c..800eec53cb 100644 --- a/UI/Image/DeleteImage.ts +++ b/UI/Image/DeleteImage.ts @@ -15,15 +15,15 @@ export default class DeleteImage extends Toggle { const isDeletedBadge = Translations.t.image.isDeleted.Clone() .SetClass("rounded-full p-1") .SetStyle("color:white;background:#ff8c8c") - .onClick(() => { - State.state?.changes?.applyAction(new ChangeTagAction(tags.data.id, new Tag(key, oldValue), tags.data)) + .onClick(async() => { + await State.state?.changes?.applyAction(new ChangeTagAction(tags.data.id, new Tag(key, oldValue), tags.data)) }); const deleteButton = Translations.t.image.doDelete.Clone() .SetClass("block w-full pl-4 pr-4") .SetStyle("color:white;background:#ff8c8c; border-top-left-radius:30rem; border-top-right-radius: 30rem;") - .onClick(() => { - State.state?.changes?.applyAction( + .onClick( async() => { + await State.state?.changes?.applyAction( new ChangeTagAction(tags.data.id, new Tag(key, ""), tags.data) ) }); @@ -48,7 +48,7 @@ export default class DeleteImage extends Toggle { tags.map(tags => (tags[key] ?? "") !== "") ), undefined /*Login (and thus editing) is disabled*/, - State.state?.featureSwitchUserbadge ?? new UIEventSource(true) + State.state.osmConnection.isLoggedIn ) this.SetClass("cursor-pointer") } diff --git a/UI/Image/ImageCarousel.ts b/UI/Image/ImageCarousel.ts index 4e7d032d28..9932da6164 100644 --- a/UI/Image/ImageCarousel.ts +++ b/UI/Image/ImageCarousel.ts @@ -4,29 +4,33 @@ import Combine from "../Base/Combine"; import DeleteImage from "./DeleteImage"; import {AttributedImage} from "./AttributedImage"; import BaseUIElement from "../BaseUIElement"; -import Img from "../Base/Img"; import Toggle from "../Input/Toggle"; -import {Wikimedia} from "../../Logic/ImageProviders/Wikimedia"; -import {Imgur} from "../../Logic/ImageProviders/Imgur"; -import {Mapillary} from "../../Logic/ImageProviders/Mapillary"; +import ImageProvider from "../../Logic/ImageProviders/ImageProvider"; export class ImageCarousel extends Toggle { - constructor(images: UIEventSource<{ key: string, url: string }[]>, tags: UIEventSource) { - const uiElements = images.map((imageURLS: { key: string, url: string }[]) => { + constructor(images: UIEventSource<{ key: string, url: string, provider: ImageProvider }[]>, tags: UIEventSource) { + const uiElements = images.map((imageURLS: { key: string, url: string, provider: ImageProvider }[]) => { const uiElements: BaseUIElement[] = []; for (const url of imageURLS) { - let image = ImageCarousel.CreateImageElement(url.url) - if (url.key !== undefined) { - image = new Combine([ - image, - new DeleteImage(url.key, tags).SetClass("delete-image-marker absolute top-0 left-0 pl-3") - ]).SetClass("relative"); + try { + let image = new AttributedImage(url) + + if (url.key !== undefined) { + image = new Combine([ + image, + new DeleteImage(url.key, tags).SetClass("delete-image-marker absolute top-0 left-0 pl-3") + ]).SetClass("relative"); + } + image + .SetClass("w-full block") + .SetStyle("min-width: 50px; background: grey;") + uiElements.push(image); + } catch (e) { + console.error("Could not generate image element for", url.url, "due to", e) } - image - .SetClass("w-full block") - .SetStyle("min-width: 50px; background: grey;") - uiElements.push(image); + + } return uiElements; }); @@ -38,33 +42,4 @@ export class ImageCarousel extends Toggle { ) this.SetClass("block w-full"); } - - /*** - * Creates either a 'simpleimage' or a 'wikimediaimage' based on the string - * @param url - * @constructor - */ - private static CreateImageElement(url: string): BaseUIElement { - // @ts-ignore - let attrSource: ImageAttributionSource = undefined; - if (url.startsWith("File:")) { - attrSource = Wikimedia.singleton - } else if (url.toLowerCase().startsWith("https://commons.wikimedia.org/wiki/")) { - attrSource = Wikimedia.singleton; - } else if (url.toLowerCase().startsWith("https://i.imgur.com/")) { - attrSource = Imgur.singleton - } else if (url.toLowerCase().startsWith("https://www.mapillary.com/map/im/")) { - attrSource = Mapillary.singleton - } else { - return new Img(url); - } - - try { - return new AttributedImage(url, attrSource) - } catch (e) { - console.error("Could not create an image: ", e) - return undefined; - } - - } } \ No newline at end of file diff --git a/UI/Image/ImageUploadFlow.ts b/UI/Image/ImageUploadFlow.ts index 2a80535236..4eec0b85c5 100644 --- a/UI/Image/ImageUploadFlow.ts +++ b/UI/Image/ImageUploadFlow.ts @@ -29,10 +29,10 @@ export class ImageUploadFlow extends Toggle { key = imagePrefix + ":" + freeIndex; } console.log("Adding image:" + key, url); - State.state.changes + Promise.resolve(State.state.changes .applyAction(new ChangeTagAction( tags.id, new Tag(key, url), tagsSource.data - )) + ))) }) @@ -55,7 +55,7 @@ export class ImageUploadFlow extends Toggle { const tags = tagsSource.data; - const layout = State.state?.layoutToUse?.data + const layout = State.state?.layoutToUse let matchingLayer: LayerConfig = undefined for (const layer of layout?.layers ?? []) { if (layer.source.osmTags.matchesProperties(tags)) { diff --git a/UI/Input/Checkboxes.ts b/UI/Input/Checkboxes.ts index 15aa758322..affad21920 100644 --- a/UI/Input/Checkboxes.ts +++ b/UI/Input/Checkboxes.ts @@ -31,7 +31,7 @@ export default class CheckBoxes extends InputElement { } protected InnerConstructElement(): HTMLElement { - const el = document.createElement("form"); + const formTag = document.createElement("form"); const value = this.value; const elements = this._elements; @@ -57,18 +57,18 @@ export default class CheckBoxes extends InputElement { "bg-red" ); - const wrapper = document.createElement("span"); + const wrapper = document.createElement("div"); wrapper.classList.add( "wrapper", "flex", "w-full", "border", "border-gray-400", - "m-1" + "mb-1" ); wrapper.appendChild(input); wrapper.appendChild(label); - el.appendChild(wrapper); + formTag.appendChild(wrapper); value.addCallbackAndRunD((selectedValues) => { if (selectedValues.indexOf(i) >= 0) { @@ -97,6 +97,6 @@ export default class CheckBoxes extends InputElement { }; } - return el; + return formTag; } } diff --git a/UI/Input/DirectionInput.ts b/UI/Input/DirectionInput.ts index 6c6b15948e..108065b68b 100644 --- a/UI/Input/DirectionInput.ts +++ b/UI/Input/DirectionInput.ts @@ -6,13 +6,13 @@ import BaseUIElement from "../BaseUIElement"; import {FixedUiElement} from "../Base/FixedUiElement"; import {Utils} from "../../Utils"; import Loc from "../../Models/Loc"; +import Minimap from "../Base/Minimap"; /** * Selects a direction in degrees */ export default class DirectionInput extends InputElement { - public static constructMinimap: ((any) => BaseUIElement); public readonly IsSelected: UIEventSource = new UIEventSource(false); private readonly _location: UIEventSource; private readonly value: UIEventSource; @@ -40,7 +40,7 @@ export default class DirectionInput extends InputElement { let map: BaseUIElement = new FixedUiElement("") if (!Utils.runningFromConsole) { - map = DirectionInput.constructMinimap({ + map = Minimap.createMiniMap({ background: this.background, allowMoving: false, location: this._location diff --git a/UI/Input/FixedInputElement.ts b/UI/Input/FixedInputElement.ts index d82f795d43..479aba16cb 100644 --- a/UI/Input/FixedInputElement.ts +++ b/UI/Input/FixedInputElement.ts @@ -11,11 +11,15 @@ export class FixedInputElement extends InputElement { private readonly _el: HTMLElement; constructor(rendering: BaseUIElement | string, - value: T, + value: T | UIEventSource, comparator: ((t0: T, t1: T) => boolean) = undefined) { super(); this._comparator = comparator ?? ((t0, t1) => t0 == t1); - this.value = new UIEventSource(value); + if(value instanceof UIEventSource){ + this.value = value + }else{ + this.value = new UIEventSource(value); + } const selected = this.IsSelected; this._el = document.createElement("span") @@ -41,6 +45,4 @@ export class FixedInputElement extends InputElement { protected InnerConstructElement(): HTMLElement { return this._el; } - - -} \ No newline at end of file +} diff --git a/UI/Input/InputElement.ts b/UI/Input/InputElement.ts index aadc6d9ee1..f9920b1d6a 100644 --- a/UI/Input/InputElement.ts +++ b/UI/Input/InputElement.ts @@ -10,4 +10,3 @@ export abstract class InputElement extends BaseUIElement { abstract IsValid(t: T): boolean; } - diff --git a/UI/Input/LengthInput.ts b/UI/Input/LengthInput.ts index 3162f5a875..4eb39d7e95 100644 --- a/UI/Input/LengthInput.ts +++ b/UI/Input/LengthInput.ts @@ -5,7 +5,7 @@ import Svg from "../../Svg"; import {Utils} from "../../Utils"; import Loc from "../../Models/Loc"; import {GeoOperations} from "../../Logic/GeoOperations"; -import DirectionInput from "./DirectionInput"; +import Minimap from "../Base/Minimap"; /** @@ -41,7 +41,7 @@ export default class LengthInput extends InputElement { // @ts-ignore let map = undefined if (!Utils.runningFromConsole) { - map = DirectionInput.constructMinimap({ + map = Minimap.createMiniMap({ background: this.background, allowMoving: false, location: this._location, diff --git a/UI/Input/LocationInput.ts b/UI/Input/LocationInput.ts index 875fce9cbe..db96cf7be7 100644 --- a/UI/Input/LocationInput.ts +++ b/UI/Input/LocationInput.ts @@ -8,40 +8,39 @@ import Svg from "../../Svg"; import State from "../../State"; import AvailableBaseLayers from "../../Logic/Actors/AvailableBaseLayers"; import {GeoOperations} from "../../Logic/GeoOperations"; -import ShowDataLayer from "../ShowDataLayer"; -import LayoutConfig from "../../Models/ThemeConfig/LayoutConfig"; -import * as L from "leaflet"; +import ShowDataMultiLayer from "../ShowDataLayer/ShowDataMultiLayer"; +import StaticFeatureSource from "../../Logic/FeatureSource/Sources/StaticFeatureSource"; +import LayerConfig from "../../Models/ThemeConfig/LayerConfig"; +import {BBox} from "../../Logic/BBox"; +import {FixedUiElement} from "../Base/FixedUiElement"; +import ShowDataLayer from "../ShowDataLayer/ShowDataLayer"; export default class LocationInput extends InputElement { - private static readonly matchLayout = new UIEventSource(new LayoutConfig({ - description: "Matchpoint style", - icon: "./assets/svg/crosshair-empty.svg", - id: "matchpoint", - language: ["en"], - layers: [{ + private static readonly matchLayer = new LayerConfig( + { id: "matchpoint", source: { osmTags: {and: []} }, icon: "./assets/svg/crosshair-empty.svg" - }], - maintainer: "MapComplete", - startLat: 0, - startLon: 0, - startZoom: 0, - title: "Location input", - version: "0" - - })); + }, "matchpoint icon", true + ) + IsSelected: UIEventSource = new UIEventSource(false); public readonly snappedOnto: UIEventSource = new UIEventSource(undefined) private _centerLocation: UIEventSource; private readonly mapBackground: UIEventSource; + /** + * The features to which the input should be snapped + * @private + */ private readonly _snapTo: UIEventSource<{ feature: any }[]> private readonly _value: UIEventSource private readonly _snappedPoint: UIEventSource private readonly _maxSnapDistance: number private readonly _snappedPointTags: any; + private readonly _bounds: UIEventSource; + public readonly _matching_layer: LayerConfig; constructor(options: { mapBackground?: UIEventSource, @@ -50,32 +49,32 @@ export default class LocationInput extends InputElement { snappedPointTags?: any, requiresSnapping?: boolean, centerLocation: UIEventSource, + bounds?: UIEventSource }) { super(); this._snapTo = options.snapTo?.map(features => features?.filter(feat => feat.feature.geometry.type !== "Point")) this._maxSnapDistance = options.maxSnapDistance this._centerLocation = options.centerLocation; this._snappedPointTags = options.snappedPointTags + this._bounds = options.bounds; if (this._snapTo === undefined) { this._value = this._centerLocation; } else { const self = this; - let matching_layer: UIEventSource if (self._snappedPointTags !== undefined) { - matching_layer = State.state.layoutToUse.map(layout => { + const layout = State.state.layoutToUse - for (const layer of layout.layers) { - if (layer.source.osmTags.matchesProperties(self._snappedPointTags)) { - return layer.id - } + let matchingLayer = LocationInput.matchLayer + for (const layer of layout.layers) { + if (layer.source.osmTags.matchesProperties(self._snappedPointTags)) { + matchingLayer = layer } - console.error("No matching layer found for tags ", self._snappedPointTags) - return "matchpoint" - }) + } + this._matching_layer = matchingLayer; } else { - matching_layer = new UIEventSource("matchpoint") + this._matching_layer = LocationInput.matchLayer } this._snappedPoint = options.centerLocation.map(loc => { @@ -87,7 +86,7 @@ export default class LocationInput extends InputElement { let min = undefined; let matchedWay = undefined; - for (const feature of self._snapTo.data) { + for (const feature of self._snapTo.data ?? []) { const nearestPointOnLine = GeoOperations.nearestPoint(feature.feature, [loc.lon, loc.lat]) if (min === undefined) { min = nearestPointOnLine @@ -102,19 +101,17 @@ export default class LocationInput extends InputElement { } } - if (min.properties.dist * 1000 > self._maxSnapDistance) { + if (min === undefined || min.properties.dist * 1000 > self._maxSnapDistance) { if (options.requiresSnapping) { return undefined } else { return { "type": "Feature", - "_matching_layer_id": matching_layer.data, "properties": options.snappedPointTags ?? min.properties, "geometry": {"type": "Point", "coordinates": [loc.lon, loc.lat]} } } } - min._matching_layer_id = matching_layer?.data ?? "matchpoint" min.properties = options.snappedPointTags ?? min.properties self.snappedOnto.setData(matchedWay) return min @@ -128,7 +125,7 @@ export default class LocationInput extends InputElement { }) } - this.mapBackground = options.mapBackground ?? State.state.backgroundLayer ?? new UIEventSource(AvailableBaseLayers.osmCarto) + this.mapBackground = options.mapBackground ?? State.state?.backgroundLayer ?? new UIEventSource(AvailableBaseLayers.osmCarto) this.SetClass("block h-full") } @@ -143,83 +140,46 @@ export default class LocationInput extends InputElement { protected InnerConstructElement(): HTMLElement { try { const clickLocation = new UIEventSource(undefined); - const map = new Minimap( + const map = Minimap.createMiniMap( { location: this._centerLocation, background: this.mapBackground, - attribution: this.mapBackground !== State.state.backgroundLayer, - lastClickLocation: clickLocation + attribution: this.mapBackground !== State.state?.backgroundLayer, + lastClickLocation: clickLocation, + bounds: this._bounds } ) clickLocation.addCallbackAndRunD(location => this._centerLocation.setData(location)) - map.leafletMap.addCallbackAndRunD(leaflet => { - const bounds = leaflet.getBounds() - leaflet.setMaxBounds(bounds.pad(0.15)) - const data = { - type: "FeatureCollection", - features: [{ - "type": "Feature", - "geometry": { - "type": "LineString", - "coordinates": [ - [ - bounds.getEast(), - bounds.getNorth() - ], - [ - bounds.getWest(), - bounds.getNorth() - ], - [ - bounds.getWest(), - bounds.getSouth() - ], - [ - bounds.getEast(), - bounds.getSouth() - ], - [ - bounds.getEast(), - bounds.getNorth() - ] - ] - } - }] - } - // @ts-ignore - L.geoJSON(data, { - style: { - color: "#f00", - weight: 2, - opacity: 0.4 - } - }).addTo(leaflet) - }) + map.installBounds(0.15, true); if (this._snapTo !== undefined) { - new ShowDataLayer(this._snapTo, map.leafletMap, State.state.layoutToUse, false, false) - + + // Show the lines to snap to + new ShowDataMultiLayer({ + features: new StaticFeatureSource(this._snapTo, true), + enablePopups: false, + zoomToFeatures: false, + leafletMap: map.leafletMap, + layers: State.state.filteredLayers + } + ) + // Show the central point const matchPoint = this._snappedPoint.map(loc => { if (loc === undefined) { return [] } return [{feature: loc}]; }) - if (this._snapTo) { - let layout = LocationInput.matchLayout - if (this._snappedPointTags !== undefined) { - layout = State.state.layoutToUse - } - new ShowDataLayer( - matchPoint, - map.leafletMap, - layout, - false, false - ) - } + new ShowDataLayer({ + features: new StaticFeatureSource(matchPoint, true), + enablePopups: false, + zoomToFeatures: false, + leafletMap: map.leafletMap, + layerToShow: this._matching_layer + }) + } - this.mapBackground.map(layer => { const leaflet = map.leafletMap.data if (leaflet === undefined || layer === undefined) { @@ -231,20 +191,31 @@ export default class LocationInput extends InputElement { leaflet.setZoom(layer.max_zoom - 1) }, [map.leafletMap]) + + const animatedHand = Svg.hand_ui() + .SetStyle("width: 2rem; height: unset;") + .SetClass("hand-drag-animation block pointer-events-none") + return new Combine([ new Combine([ Svg.move_arrows_ui() .SetClass("block relative pointer-events-none") .SetStyle("left: -2.5rem; top: -2.5rem; width: 5rem; height: 5rem") - ]).SetClass("block w-0 h-0 z-10 relative") - .SetStyle("background: rgba(255, 128, 128, 0.21); left: 50%; top: 50%"), + ]).SetClass("block w-0 h-0 z-10 relative") + .SetStyle("background: rgba(255, 128, 128, 0.21); left: 50%; top: 50%; opacity: 0.5"), + + new Combine([ + animatedHand]) + .SetClass("block w-0 h-0 z-10 relative") + .SetStyle("left: calc(50% + 3rem); top: calc(50% + 2rem); opacity: 0.7"), + map .SetClass("z-0 relative block w-full h-full bg-gray-100") ]).ConstructElement(); } catch (e) { console.error("Could not generate LocationInputElement:", e) - return undefined; + return new FixedUiElement("Constructing a locationInput failed due to" + e).SetClass("alert").ConstructElement(); } } diff --git a/UI/Input/RadioButton.ts b/UI/Input/RadioButton.ts index 7653cc62f4..73310559b3 100644 --- a/UI/Input/RadioButton.ts +++ b/UI/Input/RadioButton.ts @@ -179,26 +179,4 @@ export class RadioButton extends InputElement { return form; } - /* - public ShowValue(t: T): boolean { - if (t === undefined) { - return false; - } - if (!this.IsValid(t)) { - return false; - } - // We check that what is selected matches the previous rendering - for (let i = 0; i < this._elements.length; i++) { - const e = this._elements[i]; - if (e.IsValid(t)) { - this._selectedElementIndex.setData(i); - e.GetValue().setData(t); - const radio = document.getElementById(this.IdFor(i)); - // @ts-ignore - radio?.checked = true; - return; - } - - } - }*/ } diff --git a/UI/Input/VariableInputElement.ts b/UI/Input/VariableInputElement.ts new file mode 100644 index 0000000000..1918dfe9a3 --- /dev/null +++ b/UI/Input/VariableInputElement.ts @@ -0,0 +1,35 @@ +import {InputElement} from "./InputElement"; +import {UIEventSource} from "../../Logic/UIEventSource"; +import BaseUIElement from "../BaseUIElement"; +import {VariableUiElement} from "../Base/VariableUIElement"; + +export default class VariableInputElement extends InputElement { + + private readonly value: UIEventSource; + private readonly element: BaseUIElement + public readonly IsSelected: UIEventSource; + private readonly upstream: UIEventSource>; + + constructor(upstream: UIEventSource>) { + + super() + this.upstream = upstream; + this.value = upstream.bind(v => v.GetValue()) + this.element = new VariableUiElement(upstream) + this.IsSelected = upstream.bind(v => v.IsSelected) + } + + GetValue(): UIEventSource { + return this.value; + } + + protected InnerConstructElement(): HTMLElement { + return this.element.ConstructElement(); + } + + + IsValid(t: T): boolean { + return this.upstream.data.IsValid(t); + } + +} \ No newline at end of file diff --git a/UI/OpeningHours/OpeningHoursRange.ts b/UI/OpeningHours/OpeningHoursRange.ts index 439aac490e..6142ea9a20 100644 --- a/UI/OpeningHours/OpeningHoursRange.ts +++ b/UI/OpeningHours/OpeningHoursRange.ts @@ -37,7 +37,7 @@ export default class OpeningHoursRange extends BaseUIElement { let content: BaseUIElement; if (height > 2) { - content = new Combine([startTime, deleteRange, endTime]).SetClass("flex flex-col h-full").SetStyle("justify-content: space-between;"); + content = new Combine([startTime, deleteRange, endTime]).SetClass("flex flex-col h-full justify-between"); } else { content = new Combine([deleteRange]).SetClass("flex flex-col h-full").SetStyle("flex-content: center; overflow-x: unset;") } @@ -61,4 +61,4 @@ export default class OpeningHoursRange extends BaseUIElement { } -} \ No newline at end of file +} diff --git a/UI/Popup/DeleteWizard.ts b/UI/Popup/DeleteWizard.ts index b81e82f47e..6025ff890d 100644 --- a/UI/Popup/DeleteWizard.ts +++ b/UI/Popup/DeleteWizard.ts @@ -51,14 +51,14 @@ export default class DeleteWizard extends Toggle { const confirm = new UIEventSource(false) - function softDelete(reason: string, tagsToApply: { k: string, v: string }[]) { + async function softDelete(reason: string, tagsToApply: { k: string, v: string }[]) { if (reason !== undefined) { tagsToApply.splice(0, 0, { k: "fixme", v: `A mapcomplete user marked this feature to be deleted (${reason})` }) } - (State.state?.changes ?? new Changes()) + await (State.state?.changes ?? new Changes()) .applyAction(new ChangeTagAction( id, new And(tagsToApply.map(kv => new Tag(kv.k, kv.v))), tagsSource.data )) diff --git a/UI/Popup/EditableTagRendering.ts b/UI/Popup/EditableTagRendering.ts index 4e5c313f5b..2be62b01be 100644 --- a/UI/Popup/EditableTagRendering.ts +++ b/UI/Popup/EditableTagRendering.ts @@ -9,6 +9,7 @@ import Toggle from "../Input/Toggle"; import BaseUIElement from "../BaseUIElement"; import TagRenderingConfig from "../../Models/ThemeConfig/TagRenderingConfig"; import {Unit} from "../../Models/Unit"; +import Lazy from "../Base/Lazy"; export default class EditableTagRendering extends Toggle { @@ -17,39 +18,51 @@ export default class EditableTagRendering extends Toggle { units: Unit [], editMode = new UIEventSource(false) ) { + + // The tagrendering is hidden if: + // The answer is unknown. The questionbox will then show the question + // There is a condition hiding the answer + const renderingIsShown = tags.map(tags => + configuration.IsKnown(tags) && + (configuration?.condition?.matchesProperties(tags) ?? true)) + + super( + new Lazy(() => EditableTagRendering.CreateRendering(tags, configuration, units, editMode)), + undefined, + renderingIsShown + ) + } + + private static CreateRendering(tags: UIEventSource, configuration: TagRenderingConfig, units: Unit[], editMode: UIEventSource) : BaseUIElement{ const answer: BaseUIElement = new TagRenderingAnswer(tags, configuration) answer.SetClass("w-full") let rendering = answer; if (configuration.question !== undefined && State.state?.featureSwitchUserbadge?.data) { // We have a question and editing is enabled - const editButton = - new Combine([Svg.pencil_ui()]).SetClass("block relative h-10 w-10 p-2 float-right").SetStyle("border: 1px solid black; border-radius: 0.7em") - .onClick(() => { - editMode.setData(true); - }); - - const answerWithEditButton = new Combine([answer, - new Toggle(editButton, undefined, State.state.osmConnection.isLoggedIn)]) - .SetClass("flex justify-between w-full") + new Toggle(new Combine([Svg.pencil_ui()]).SetClass("block relative h-10 w-10 p-2 float-right").SetStyle("border: 1px solid black; border-radius: 0.7em") + .onClick(() => { + editMode.setData(true); + }), + undefined, + State.state.osmConnection.isLoggedIn) + ]).SetClass("flex justify-between w-full") - const cancelbutton = - Translations.t.general.cancel.Clone() - .SetClass("btn btn-secondary mr-3") - .onClick(() => { - editMode.setData(false) - }); - - const question = new TagRenderingQuestion(tags, configuration, - { - units: units, - cancelButton: cancelbutton, - afterSave: () => { - editMode.setData(false) - } - }) + const question = new Lazy(() => + new TagRenderingQuestion(tags, configuration, + { + units: units, + cancelButton: Translations.t.general.cancel.Clone() + .SetClass("btn btn-secondary mr-3") + .onClick(() => { + editMode.setData(false) + }), + afterSave: () => { + editMode.setData(false) + } + })) rendering = new Toggle( @@ -59,17 +72,7 @@ export default class EditableTagRendering extends Toggle { ) } rendering.SetClass("block w-full break-word text-default m-1 p-1 border-b border-gray-200 mb-2 pb-2") - // The tagrendering is hidden if: - // The answer is unknown. The questionbox will then show the question - // There is a condition hiding the answer - const renderingIsShown = tags.map(tags => - configuration.IsKnown(tags) && - (configuration?.condition?.matchesProperties(tags) ?? true)) - super( - rendering, - undefined, - renderingIsShown - ) + return rendering; } } \ No newline at end of file diff --git a/UI/Popup/FeatureInfoBox.ts b/UI/Popup/FeatureInfoBox.ts index 694ef48cb2..b7e33546ba 100644 --- a/UI/Popup/FeatureInfoBox.ts +++ b/UI/Popup/FeatureInfoBox.ts @@ -130,7 +130,7 @@ export default class FeatureInfoBox extends ScrollableFullScreen { if (!userbadge) { return undefined } - return new Combine(editElements) + return new Combine(editElements).SetClass("flex flex-col") } )) renderings.push(editors) diff --git a/UI/Popup/QuestionBox.ts b/UI/Popup/QuestionBox.ts index 2b7fdfd7a1..1192ff8ee6 100644 --- a/UI/Popup/QuestionBox.ts +++ b/UI/Popup/QuestionBox.ts @@ -7,6 +7,7 @@ import BaseUIElement from "../BaseUIElement"; import {VariableUiElement} from "../Base/VariableUIElement"; import TagRenderingConfig from "../../Models/ThemeConfig/TagRenderingConfig"; import {Unit} from "../../Models/Unit"; +import Lazy from "../Base/Lazy"; /** @@ -27,7 +28,8 @@ export default class QuestionBox extends VariableUiElement { } const tagRenderingQuestions = tagRenderings - .map((tagRendering, i) => new TagRenderingQuestion(tagsSource, tagRendering, + .map((tagRendering, i) => + new Lazy(() => new TagRenderingQuestion(tagsSource, tagRendering, { units: units, afterSave: () => { @@ -41,7 +43,7 @@ export default class QuestionBox extends VariableUiElement { skippedQuestions.ping(); }) } - )); + ))); const skippedQuestionsButton = Translations.t.general.skippedQuestions.Clone() .onClick(() => { diff --git a/UI/Popup/SplitRoadWizard.ts b/UI/Popup/SplitRoadWizard.ts index 9185cf24ca..17e1bd013b 100644 --- a/UI/Popup/SplitRoadWizard.ts +++ b/UI/Popup/SplitRoadWizard.ts @@ -4,19 +4,28 @@ import {UIEventSource} from "../../Logic/UIEventSource"; import {SubtleButton} from "../Base/SubtleButton"; import Minimap from "../Base/Minimap"; import State from "../../State"; -import ShowDataLayer from "../ShowDataLayer"; +import ShowDataLayer from "../ShowDataLayer/ShowDataLayer"; import {GeoOperations} from "../../Logic/GeoOperations"; import {LeafletMouseEvent} from "leaflet"; import Combine from "../Base/Combine"; import {Button} from "../Base/Button"; import Translations from "../i18n/Translations"; import SplitAction from "../../Logic/Osm/Actions/SplitAction"; -import {OsmObject, OsmWay} from "../../Logic/Osm/OsmObject"; import Title from "../Base/Title"; -import LayoutConfig from "../../Models/ThemeConfig/LayoutConfig"; +import StaticFeatureSource from "../../Logic/FeatureSource/Sources/StaticFeatureSource"; +import ShowDataMultiLayer from "../ShowDataLayer/ShowDataMultiLayer"; +import LayerConfig from "../../Models/ThemeConfig/LayerConfig"; +import {BBox} from "../../Logic/BBox"; export default class SplitRoadWizard extends Toggle { - private static splitLayout = new UIEventSource(SplitRoadWizard.GetSplitLayout()) + private static splitLayerStyling = new LayerConfig({ + id: "splitpositions", + source: {osmTags: "_cutposition=yes"}, + icon: {render: "circle:white;./assets/svg/scissors.svg"}, + iconSize: {render: "30,30,center"}, + }, "(BUILTIN) SplitRoadWizard.ts", true) + + public dialogIsOpened: UIEventSource /** * A UI Element used for splitting roads @@ -34,19 +43,41 @@ export default class SplitRoadWizard extends Toggle { // Toggle variable between show split button and map const splitClicked = new UIEventSource(false); + // Load the road with given id on the minimap + const roadElement = State.state.allElements.ContainingFeatures.get(id) // Minimap on which you can select the points to be splitted - const miniMap = new Minimap({background: State.state.backgroundLayer, allowMoving: false}); - miniMap.SetStyle("width: 100%; height: 24rem;"); + const miniMap = Minimap.createMiniMap( + { + background: State.state.backgroundLayer, + allowMoving: true, + leafletOptions: { + minZoom: 14 + } + }); + miniMap.SetStyle("width: 100%; height: 24rem") + .SetClass("rounded-xl overflow-hidden"); + + miniMap.installBounds(BBox.get(roadElement)) // Define how a cut is displayed on the map - // Load the road with given id on the minimap - const roadElement = State.state.allElements.ContainingFeatures.get(id) - const roadEventSource = new UIEventSource([{feature: roadElement, freshness: new Date()}]); // Datalayer displaying the road and the cut points (if any) - new ShowDataLayer(roadEventSource, miniMap.leafletMap, State.state.layoutToUse, false, true); - new ShowDataLayer(splitPoints, miniMap.leafletMap, SplitRoadWizard.splitLayout, false, false) + new ShowDataLayer({ + features: new StaticFeatureSource(splitPoints, true), + leafletMap: miniMap.leafletMap, + zoomToFeatures: false, + enablePopups: false, + layerToShow: SplitRoadWizard.splitLayerStyling + }) + + new ShowDataMultiLayer({ + features: new StaticFeatureSource([roadElement], false), + layers: State.state.filteredLayers, + leafletMap: miniMap.leafletMap, + enablePopups: false, + zoomToFeatures: true + }) /** * Handles a click on the overleaf map. @@ -54,12 +85,25 @@ export default class SplitRoadWizard extends Toggle { * @param coordinates Clicked location, [lon, lat] */ function onMapClick(coordinates) { + // First, we check if there is another, already existing point nearby + const points = splitPoints.data.map((f, i) => [f.feature, i]) + .filter(p => GeoOperations.distanceBetween(p[0].geometry.coordinates, coordinates) * 1000 < 5) + .map(p => p[1]) + .sort() + .reverse() + if (points.length > 0) { + for (const point of points) { + splitPoints.data.splice(point, 1) + } + splitPoints.ping() + return; + } + // Get nearest point on the road const pointOnRoad = GeoOperations.nearestPoint(roadElement, coordinates); // pointOnRoad is a geojson // Update point properties to let it match the layer pointOnRoad.properties._cutposition = "yes"; - pointOnRoad["_matching_layer_id"] = "splitpositions"; // let the state remember the point, to be able to retrieve it later by id State.state.allElements.addOrGetElement(pointOnRoad); @@ -76,7 +120,7 @@ export default class SplitRoadWizard extends Toggle { })) // Toggle between splitmap - const splitButton = new SubtleButton(Svg.scissors_ui(), t.inviteToSplit.Clone()); + const splitButton = new SubtleButton(Svg.scissors_ui(), t.inviteToSplit.Clone().SetClass("text-lg font-bold")); splitButton.onClick( () => { splitClicked.setData(true) @@ -92,27 +136,9 @@ export default class SplitRoadWizard extends Toggle { // Save button const saveButton = new Button(t.split.Clone(), () => { hasBeenSplit.setData(true) - const way = OsmObject.DownloadObject(id) - const partOfSrc = OsmObject.DownloadReferencingRelations(id); - let hasRun = false - way.map(way => { - const partOf = partOfSrc.data - if (way === undefined || partOf === undefined) { - return; - } - if (hasRun) { - return - } - hasRun = true - const splitAction = new SplitAction( - way, way.asGeoJson(), partOf, splitPoints.data.map(ff => ff.feature) - ) - State.state.changes.applyAction(splitAction) + State.state.changes.applyAction(new SplitAction(id, splitPoints.data.map(ff => ff.feature.geometry.coordinates))) + }) - }, [partOfSrc]) - - - }); saveButton.SetClass("btn btn-primary mr-3"); const disabledSaveButton = new Button("Split", undefined); disabledSaveButton.SetClass("btn btn-disabled mr-3"); @@ -134,22 +160,6 @@ export default class SplitRoadWizard extends Toggle { mapView.SetClass("question") const confirm = new Toggle(mapView, splitToggle, splitClicked); super(t.hasBeenSplit.Clone(), confirm, hasBeenSplit) - } - - private static GetSplitLayout(): LayoutConfig { - return new LayoutConfig({ - maintainer: "mapcomplete", - language: ["en"], - startLon: 0, - startLat: 0, - description: "Split points visualisations - built in at SplitRoadWizard.ts", - icon: "", startZoom: 0, - title: "Split locations", - version: "", - - id: "splitpositions", - layers: [{id: "splitpositions", source: {osmTags: "_cutposition=yes"}, icon: "./assets/svg/plus.svg"}] - }, true, "(BUILTIN) SplitRoadWizard.ts") - + this.dialogIsOpened = splitClicked } } \ No newline at end of file diff --git a/UI/Popup/TagRenderingQuestion.ts b/UI/Popup/TagRenderingQuestion.ts index 5f0881d293..a4eb83b0fb 100644 --- a/UI/Popup/TagRenderingQuestion.ts +++ b/UI/Popup/TagRenderingQuestion.ts @@ -26,6 +26,7 @@ import InputElementWrapper from "../Input/InputElementWrapper"; import ChangeTagAction from "../../Logic/Osm/Actions/ChangeTagAction"; import TagRenderingConfig from "../../Models/ThemeConfig/TagRenderingConfig"; import {Unit} from "../../Models/Unit"; +import VariableInputElement from "../Input/VariableInputElement"; /** * Shows the question element. @@ -43,6 +44,28 @@ export default class TagRenderingQuestion extends Combine { bottomText?: (src: UIEventSource) => BaseUIElement } ) { + + + const applicableMappingsSrc = + UIEventSource.ListStabilized(tags.map(tags => { + const applicableMappings: { if: TagsFilter, then: any, ifnot?: TagsFilter }[] = [] + for (const mapping of configuration.mappings ?? []) { + if (mapping.hideInAnswer === true) { + continue + } + if (mapping.hideInAnswer === false || mapping.hideInAnswer === undefined) { + applicableMappings.push(mapping) + continue + } + const condition = mapping.hideInAnswer; + const isShown = !condition.matchesProperties(tags) + if (isShown) { + applicableMappings.push(mapping) + } + } + return applicableMappings + })); + if (configuration === undefined) { throw "A question is needed for a question visualization" } @@ -52,23 +75,24 @@ export default class TagRenderingQuestion extends Combine { .SetClass("question-text"); - const inputElement: InputElement = TagRenderingQuestion.GenerateInputElement(configuration, applicableUnit, tags) + const inputElement: InputElement = + new VariableInputElement(applicableMappingsSrc.map(applicableMappings => + TagRenderingQuestion.GenerateInputElement(configuration, applicableMappings, applicableUnit, tags) + )) + - if (inputElement === undefined) { - console.error("MultiAnswer failed - probably not a single option was possible", configuration) - throw "MultiAnswer failed - probably not a single option was possible" - } const save = () => { const selection = inputElement.GetValue().data; if (selection) { (State.state?.changes ?? new Changes()) .applyAction(new ChangeTagAction( tags.data.id, selection, tags.data - )) - } - - if (options.afterSave) { - options.afterSave(); + )).then(_ => { + console.log("Tagchanges applied") + }) + if (options.afterSave) { + options.afterSave(); + } } } @@ -108,45 +132,59 @@ export default class TagRenderingQuestion extends Combine { inputElement, options.cancelButton, saveButton, - bottomTags] - ) + bottomTags]) this.SetClass("question") - } - private static GenerateInputElement(configuration: TagRenderingConfig, applicableUnit: Unit, tagsSource: UIEventSource): InputElement { + + private static GenerateInputElement(configuration: TagRenderingConfig, + applicableMappings: { if: TagsFilter, then: any, ifnot?: TagsFilter }[], + applicableUnit: Unit, + tagsSource: UIEventSource) + : InputElement { + + // FreeForm input will be undefined if not present; will already contain a special input element if applicable + const ff = TagRenderingQuestion.GenerateFreeform(configuration, applicableUnit, tagsSource); + + + const hasImages = applicableMappings.filter(mapping => mapping.then.ExtractImages().length > 0).length > 0 let inputEls: InputElement[]; - const mappings = (configuration.mappings ?? []) - .filter(mapping => { - if (mapping.hideInAnswer === true) { - return false; - } - return !(typeof (mapping.hideInAnswer) !== "boolean" && mapping.hideInAnswer.matchesProperties(tagsSource.data)); - - }) + const ifNotsPresent = applicableMappings.some(mapping => mapping.ifnot !== undefined) function allIfNotsExcept(excludeIndex: number): TagsFilter[] { - if (configuration.mappings === undefined) { + if (configuration.mappings === undefined || configuration.mappings.length === 0) { + return undefined + } + if (!ifNotsPresent) { return [] } if (configuration.multiAnswer) { // The multianswer will do the ifnot configuration themself return [] } - return Utils.NoNull(configuration.mappings?.map((m, i) => excludeIndex === i ? undefined : m.ifnot)) + + const negativeMappings = [] + + for (let i = 0; i < applicableMappings.length; i++) { + const mapping = applicableMappings[i]; + if (i === excludeIndex || mapping.ifnot === undefined) { + continue + } + negativeMappings.push(mapping.ifnot) + } + return Utils.NoNull(negativeMappings) + } - const ff = TagRenderingQuestion.GenerateFreeform(configuration, applicableUnit, tagsSource); - const hasImages = mappings.filter(mapping => mapping.then.ExtractImages().length > 0).length > 0 - if (mappings.length < 8 || configuration.multiAnswer || hasImages) { - inputEls = (mappings ?? []).map((mapping, i) => TagRenderingQuestion.GenerateMappingElement(tagsSource, mapping, allIfNotsExcept(i))); + if (applicableMappings.length < 8 || configuration.multiAnswer || hasImages || ifNotsPresent) { + inputEls = (applicableMappings ?? []).map((mapping, i) => TagRenderingQuestion.GenerateMappingElement(tagsSource, mapping, allIfNotsExcept(i))); inputEls = Utils.NoNull(inputEls); } else { const dropdown: InputElement = new DropDown("", - mappings.map((mapping, i) => { + applicableMappings.map((mapping, i) => { return { value: new And([mapping.if, ...allIfNotsExcept(i)]), shown: Translations.WT(mapping.then).Clone() @@ -163,6 +201,9 @@ export default class TagRenderingQuestion extends Combine { if (inputEls.length == 0) { + if(ff === undefined){ + throw "Error: could not generate a question: freeform and all mappings are undefined" + } return ff; } @@ -171,13 +212,14 @@ export default class TagRenderingQuestion extends Combine { } if (configuration.multiAnswer) { - return TagRenderingQuestion.GenerateMultiAnswer(configuration, inputEls, ff, configuration.mappings.map(mp => mp.ifnot)) + return TagRenderingQuestion.GenerateMultiAnswer(configuration, inputEls, ff, applicableMappings.map(mp => mp.ifnot)) } else { return new RadioButton(inputEls, {selectFirstAsDefault: false}) } } + private static GenerateMultiAnswer( configuration: TagRenderingConfig, elements: InputElement[], freeformField: InputElement, ifNotSelected: TagsFilter[]): InputElement { @@ -278,17 +320,23 @@ export default class TagRenderingQuestion extends Combine { return inputEl; } + + /** + * Generates a (Fixed) input element for this mapping. + * Note that the mapping might hide itself if the condition is not met anymore. + * + * Returns: [the element itself, the value to select if not selected. The contents of this UIEventSource might swap to undefined if the conditions to show the answer are unmet] + */ private static GenerateMappingElement( tagsSource: UIEventSource, mapping: { if: TagsFilter, then: Translation, - hideInAnswer: boolean | TagsFilter }, ifNot?: TagsFilter[]): InputElement { - let tagging = mapping.if; - if (ifNot.length > 0) { - tagging = new And([tagging, ...ifNot]) + let tagging: TagsFilter = mapping.if; + if (ifNot !== undefined) { + tagging = new And([mapping.if, ...ifNot]) } return new FixedInputElement( diff --git a/UI/ShowDataLayer.ts b/UI/ShowDataLayer.ts deleted file mode 100644 index ced30b29f8..0000000000 --- a/UI/ShowDataLayer.ts +++ /dev/null @@ -1,219 +0,0 @@ -/** - * The data layer shows all the given geojson elements with the appropriate icon etc - */ -import {UIEventSource} from "../Logic/UIEventSource"; -import * as L from "leaflet" -import "leaflet.markercluster" -import State from "../State"; -import FeatureInfoBox from "./Popup/FeatureInfoBox"; -import LayoutConfig from "../Models/ThemeConfig/LayoutConfig"; -import LayerConfig from "../Models/ThemeConfig/LayerConfig"; - - -export default class ShowDataLayer { - - private _layerDict; - private readonly _leafletMap: UIEventSource; - private _cleanCount = 0; - private readonly _enablePopups: boolean; - private readonly _features: UIEventSource<{ feature: any }[]> - - constructor(features: UIEventSource<{ feature: any }[]>, - leafletMap: UIEventSource, - layoutToUse: UIEventSource, - enablePopups = true, - zoomToFeatures = false) { - this._leafletMap = leafletMap; - this._enablePopups = enablePopups; - this._features = features; - const self = this; - self._layerDict = {}; - - layoutToUse.addCallbackAndRun(layoutToUse => { - for (const layer of layoutToUse.layers) { - if (self._layerDict[layer.id] === undefined) { - self._layerDict[layer.id] = layer; - } - } - }); - - let geoLayer = undefined; - let cluster = undefined; - - function update() { - if (features.data === undefined) { - return; - } - const mp = leafletMap.data; - - if (mp === undefined) { - return; - } - - self._cleanCount++ - // clean all the old stuff away, if any - if (geoLayer !== undefined) { - mp.removeLayer(geoLayer); - } - if (cluster !== undefined) { - mp.removeLayer(cluster); - } - - const allFeats = features.data.map(ff => ff.feature); - geoLayer = self.CreateGeojsonLayer(); - for (const feat of allFeats) { - if (feat === undefined) { - continue - } - // @ts-ignore - geoLayer.addData(feat); - } - if (layoutToUse.data.clustering.minNeededElements <= allFeats.length) { - // Activate clustering if it wasn't already activated - const cl = window["L"]; // This is a dirty workaround, the clustering plugin binds to the L of the window, not of the namespace or something - cluster = cl.markerClusterGroup({disableClusteringAtZoom: layoutToUse.data.clustering.maxZoom}); - cluster.addLayer(geoLayer); - mp.addLayer(cluster); - } else { - mp.addLayer(geoLayer) - } - - if (zoomToFeatures) { - try { - mp.fitBounds(geoLayer.getBounds(), {animate: false}) - } catch (e) { - console.error(e) - } - } - if (self._enablePopups) { - State.state.selectedElement.ping() - } - } - - features.addCallback(() => update()); - leafletMap.addCallback(() => update()); - update(); - } - - - private createStyleFor(feature) { - const tagsSource = State.state.allElements.addOrGetElement(feature); - // Every object is tied to exactly one layer - const layer = this._layerDict[feature._matching_layer_id]; - return layer?.GenerateLeafletStyle(tagsSource, layer._showOnPopup !== undefined); - } - - private pointToLayer(feature, latLng): L.Layer { - // Leaflet cannot handle geojson points natively - // We have to convert them to the appropriate icon - // Click handling is done in the next step - - const layer: LayerConfig = this._layerDict[feature._matching_layer_id]; - if (layer === undefined) { - return; - } - - const tagSource = feature.properties.id === undefined ? new UIEventSource(feature.properties) : State.state.allElements.getEventSourceById(feature.properties.id) - const style = layer.GenerateLeafletStyle(tagSource, !(layer.title === undefined && (layer.tagRenderings ?? []).length === 0)); - const baseElement = style.icon.html; - if (!this._enablePopups) { - baseElement.SetStyle("cursor: initial !important") - } - return L.marker(latLng, { - icon: L.divIcon({ - html: baseElement.ConstructElement(), - className: style.icon.className, - iconAnchor: style.icon.iconAnchor, - iconUrl: style.icon.iconUrl, - popupAnchor: style.icon.popupAnchor, - iconSize: style.icon.iconSize - }) - }); - } - - private postProcessFeature(feature, leafletLayer: L.Layer) { - const layer: LayerConfig = this._layerDict[feature._matching_layer_id]; - if (layer === undefined) { - console.warn("No layer found for object (probably a now disabled layer)", feature, this._layerDict) - return; - } - if (layer.title === undefined || !this._enablePopups) { - // No popup action defined -> Don't do anything - // or probably a map in the popup - no popups needed! - return; - } - - const popup = L.popup({ - autoPan: true, - closeOnEscapeKey: true, - closeButton: false, - autoPanPaddingTopLeft: [15, 15], - - }, leafletLayer); - - leafletLayer.bindPopup(popup); - - let infobox: FeatureInfoBox = undefined; - - const id = `popup-${feature.properties.id}-${this._cleanCount}` - popup.setContent(`
Rendering
`) - - leafletLayer.on("popupopen", () => { - State.state.selectedElement.setData(feature) - - if (infobox === undefined) { - const tags = State.state.allElements.getEventSourceById(feature.properties.id); - infobox = new FeatureInfoBox(tags, layer); - - infobox.isShown.addCallback(isShown => { - if (!isShown) { - State.state.selectedElement.setData(undefined); - leafletLayer.closePopup() - } - }); - } - - - infobox.AttachTo(id) - infobox.Activate(); - }); - const self = this; - State.state.selectedElement.addCallbackAndRunD(selected => { - if (self._leafletMap.data === undefined) { - return; - } - if (leafletLayer.getPopup().isOpen()) { - return; - } - if (selected.properties.id === feature.properties.id) { - // A small sanity check to prevent infinite loops: - if (selected.geometry.type === feature.geometry.type // If a feature is rendered both as way and as point, opening one popup might trigger the other to open, which might trigger the one to open again - && feature.id === feature.properties.id // the feature might have as id 'node/-1' and as 'feature.properties.id' = 'the newly assigned id'. That is no good too - ) { - leafletLayer.openPopup() - } - if (feature.id !== feature.properties.id) { - console.trace("Not opening the popup for", feature) - } - - } - }) - - } - - private CreateGeojsonLayer(): L.Layer { - const self = this; - const data = { - type: "FeatureCollection", - features: [] - } - // @ts-ignore - return L.geoJSON(data, { - style: feature => self.createStyleFor(feature), - pointToLayer: (feature, latLng) => self.pointToLayer(feature, latLng), - onEachFeature: (feature, leafletLayer) => self.postProcessFeature(feature, leafletLayer) - }); - - } - -} \ No newline at end of file diff --git a/UI/ShowDataLayer/ShowDataLayer.ts b/UI/ShowDataLayer/ShowDataLayer.ts new file mode 100644 index 0000000000..a0244a33fa --- /dev/null +++ b/UI/ShowDataLayer/ShowDataLayer.ts @@ -0,0 +1,256 @@ +/** + * The data layer shows all the given geojson elements with the appropriate icon etc + */ +import {UIEventSource} from "../../Logic/UIEventSource"; +import LayerConfig from "../../Models/ThemeConfig/LayerConfig"; +import FeatureInfoBox from "../Popup/FeatureInfoBox"; +import State from "../../State"; +import {ShowDataLayerOptions} from "./ShowDataLayerOptions"; + +export default class ShowDataLayer { + + private readonly _leafletMap: UIEventSource; + private readonly _enablePopups: boolean; + private readonly _features: UIEventSource<{ feature: any }[]> + private readonly _layerToShow: LayerConfig; + + // Used to generate a fresh ID when needed + private _cleanCount = 0; + private geoLayer = undefined; + private isDirty = false; + + /** + * If the selected element triggers, this is used to lookup the correct layer and to open the popup + * Used to avoid a lot of callbacks on the selected element + * + * Note: the key of this dictionary is 'feature.properties.id+features.geometry.type' as one feature might have multiple presentations + * @private + */ + private readonly leafletLayersPerId = new Map() + + + constructor(options: ShowDataLayerOptions & { layerToShow: LayerConfig }) { + this._leafletMap = options.leafletMap; + this._enablePopups = options.enablePopups ?? true; + if (options.features === undefined) { + throw "Invalid ShowDataLayer invocation" + } + const features = options.features.features.map(featFreshes => featFreshes.map(ff => ff.feature)); + this._features = features; + this._layerToShow = options.layerToShow; + const self = this; + + options.leafletMap.addCallbackAndRunD(_ => { + self.update(options) + } + ); + + features.addCallback(_ => self.update(options)); + options.doShowLayer?.addCallbackAndRun(doShow => { + const mp = options.leafletMap.data; + if (mp == undefined) { + return; + } + if (doShow) { + if (self.isDirty) { + self.update(options) + } else { + mp.addLayer(this.geoLayer) + } + } else { + if (this.geoLayer !== undefined) { + mp.removeLayer(this.geoLayer) + } + } + + }) + + + State.state.selectedElement.addCallbackAndRunD(selected => { + if (self._leafletMap.data === undefined) { + return; + } + const v = self.leafletLayersPerId.get(selected.properties.id + selected.geometry.type) + if (v === undefined) { + return; + } + const leafletLayer = v.leafletlayer + const feature = v.feature + if (leafletLayer.getPopup().isOpen()) { + return; + } + if (selected.properties.id !== feature.properties.id) { + return; + } + + if (feature.id !== feature.properties.id) { + // Probably a feature which has renamed + // the feature might have as id 'node/-1' and as 'feature.properties.id' = 'the newly assigned id'. That is no good too + console.log("Not opening the popup for", feature, "as probably renamed") + return; + } + if (selected.geometry.type === feature.geometry.type // If a feature is rendered both as way and as point, opening one popup might trigger the other to open, which might trigger the one to open again + ) { + console.log("Opening popup of feature", feature) + leafletLayer.openPopup() + } + }) + + } + + private update(options: ShowDataLayerOptions) { + if (this._features.data === undefined) { + return; + } + this.isDirty = true; + if (options?.doShowLayer?.data === false) { + return; + } + const mp = options.leafletMap.data; + + if (mp === undefined) { + return; + } + this._cleanCount++ + // clean all the old stuff away, if any + if (this.geoLayer !== undefined) { + mp.removeLayer(this.geoLayer); + } + + const self = this; + const data = { + type: "FeatureCollection", + features: [] + } + // @ts-ignore + this.geoLayer = L.geoJSON(data, { + style: feature => self.createStyleFor(feature), + pointToLayer: (feature, latLng) => self.pointToLayer(feature, latLng), + onEachFeature: (feature, leafletLayer) => self.postProcessFeature(feature, leafletLayer) + }); + + const allFeats = this._features.data; + for (const feat of allFeats) { + if (feat === undefined) { + continue + } + try { + this.geoLayer.addData(feat); + } catch (e) { + console.error("Could not add ", feat, "to the geojson layer in leaflet") + } + } + + if (options.zoomToFeatures ?? false) { + try { + mp.fitBounds(this.geoLayer.getBounds(), {animate: false}) + } catch (e) { + console.error(e) + } + } + + if (options.doShowLayer?.data ?? true) { + mp.addLayer(this.geoLayer) + } + this.isDirty = false; + } + + + private createStyleFor(feature) { + const tagsSource = State.state.allElements.addOrGetElement(feature); + // Every object is tied to exactly one layer + const layer = this._layerToShow + return layer?.GenerateLeafletStyle(tagsSource, true); + } + + private pointToLayer(feature, latLng): L.Layer { + // Leaflet cannot handle geojson points natively + // We have to convert them to the appropriate icon + // Click handling is done in the next step + + const layer: LayerConfig = this._layerToShow + if (layer === undefined) { + return; + } + + let tagSource = State.state.allElements.getEventSourceById(feature.properties.id) + if (tagSource === undefined) { + tagSource = new UIEventSource(feature.properties) + } + const clickable = !(layer.title === undefined && (layer.tagRenderings ?? []).length === 0) + const style = layer.GenerateLeafletStyle(tagSource, clickable); + const baseElement = style.icon.html; + if (!this._enablePopups) { + baseElement.SetStyle("cursor: initial !important") + } + return L.marker(latLng, { + icon: L.divIcon({ + html: baseElement.ConstructElement(), + className: style.icon.className, + iconAnchor: style.icon.iconAnchor, + iconUrl: style.icon.iconUrl ?? "./assets/svg/bug.svg", + popupAnchor: style.icon.popupAnchor, + iconSize: style.icon.iconSize + }) + }); + } + + /** + * POst processing - basically adding the popup + * @param feature + * @param leafletLayer + * @private + */ + private postProcessFeature(feature, leafletLayer: L.Layer) { + const layer: LayerConfig = this._layerToShow + if (layer.title === undefined || !this._enablePopups) { + // No popup action defined -> Don't do anything + // or probably a map in the popup - no popups needed! + return; + } + + const popup = L.popup({ + autoPan: true, + closeOnEscapeKey: true, + closeButton: false, + autoPanPaddingTopLeft: [15, 15], + + }, leafletLayer); + + leafletLayer.bindPopup(popup); + + let infobox: FeatureInfoBox = undefined; + + const id = `popup-${feature.properties.id}-${feature.geometry.type}-${this._cleanCount}` + popup.setContent(`
Popup for ${feature.properties.id} ${feature.geometry.type}
`) + + leafletLayer.on("popupopen", () => { + if (infobox === undefined) { + const tags = State.state.allElements.getEventSourceById(feature.properties.id); + infobox = new FeatureInfoBox(tags, layer); + + infobox.isShown.addCallback(isShown => { + if (!isShown) { + State.state.selectedElement.setData(undefined); + leafletLayer.closePopup() + } + }); + } + infobox.AttachTo(id) + infobox.Activate(); + if (State.state?.selectedElement?.data?.properties?.id !== feature.properties.id) { + State.state.selectedElement.setData(feature) + } + + }); + + + // Add the feature to the index to open the popup when needed + this.leafletLayersPerId.set(feature.properties.id + feature.geometry.type, { + feature: feature, + leafletlayer: leafletLayer + }) + + } + +} \ No newline at end of file diff --git a/UI/ShowDataLayer/ShowDataLayerOptions.ts b/UI/ShowDataLayer/ShowDataLayerOptions.ts new file mode 100644 index 0000000000..2323ce0671 --- /dev/null +++ b/UI/ShowDataLayer/ShowDataLayerOptions.ts @@ -0,0 +1,10 @@ +import FeatureSource from "../../Logic/FeatureSource/FeatureSource"; +import {UIEventSource} from "../../Logic/UIEventSource"; + +export interface ShowDataLayerOptions { + features: FeatureSource, + leafletMap: UIEventSource, + enablePopups?: true | boolean, + zoomToFeatures?: false | boolean, + doShowLayer?: UIEventSource +} \ No newline at end of file diff --git a/UI/ShowDataLayer/ShowDataMultiLayer.ts b/UI/ShowDataLayer/ShowDataMultiLayer.ts new file mode 100644 index 0000000000..924274586f --- /dev/null +++ b/UI/ShowDataLayer/ShowDataMultiLayer.ts @@ -0,0 +1,23 @@ +/** + * SHows geojson on the given leaflet map, but attempts to figure out the correct layer first + */ +import {UIEventSource} from "../../Logic/UIEventSource"; +import ShowDataLayer from "./ShowDataLayer"; +import PerLayerFeatureSourceSplitter from "../../Logic/FeatureSource/PerLayerFeatureSourceSplitter"; +import FilteredLayer from "../../Models/FilteredLayer"; +import {ShowDataLayerOptions} from "./ShowDataLayerOptions"; + +export default class ShowDataMultiLayer { + constructor(options: ShowDataLayerOptions & { layers: UIEventSource }) { + + new PerLayerFeatureSourceSplitter(options.layers, (perLayer => { + const newOptions = { + layerToShow: perLayer.layer.layerDef, + ...options + } + new ShowDataLayer(newOptions) + }), + options.features) + + } +} \ No newline at end of file diff --git a/UI/ShowDataLayer/ShowTileInfo.ts b/UI/ShowDataLayer/ShowTileInfo.ts new file mode 100644 index 0000000000..1bb6367277 --- /dev/null +++ b/UI/ShowDataLayer/ShowTileInfo.ts @@ -0,0 +1,61 @@ +import FeatureSource, {Tiled} from "../../Logic/FeatureSource/FeatureSource"; +import {UIEventSource} from "../../Logic/UIEventSource"; +import LayerConfig from "../../Models/ThemeConfig/LayerConfig"; +import ShowDataLayer from "./ShowDataLayer"; +import StaticFeatureSource from "../../Logic/FeatureSource/Sources/StaticFeatureSource"; +import {GeoOperations} from "../../Logic/GeoOperations"; +import {Tiles} from "../../Models/TileRange"; +import * as clusterstyle from "../../assets/layers/cluster_style/cluster_style.json" +export default class ShowTileInfo { + public static readonly styling = new LayerConfig( + clusterstyle, "tileinfo", true) + + constructor(options: { + source: FeatureSource & Tiled, leafletMap: UIEventSource, layer?: LayerConfig, + doShowLayer?: UIEventSource + }) { + + + const source = options.source + const metaFeature: UIEventSource = + source.features.map(features => { + const bbox = source.bbox + const [z, x, y] = Tiles.tile_from_index(source.tileIndex) + const box = { + "type": "Feature", + "properties": { + "z": z, + "x": x, + "y": y, + "tileIndex": source.tileIndex, + "source": source.name, + "count": features.length, + tileId: source.name + "/" + source.tileIndex + }, + "geometry": { + "type": "Polygon", + "coordinates": [ + [ + [bbox.minLon, bbox.minLat], + [bbox.minLon, bbox.maxLat], + [bbox.maxLon, bbox.maxLat], + [bbox.maxLon, bbox.minLat], + [bbox.minLon, bbox.minLat] + ] + ] + } + } + const center = GeoOperations.centerpoint(box) + return [box, center] + }) + + new ShowDataLayer({ + layerToShow: ShowTileInfo.styling, + features: new StaticFeatureSource(metaFeature, false), + leafletMap: options.leafletMap, + doShowLayer: options.doShowLayer + }) + + } + +} \ No newline at end of file diff --git a/UI/ShowDataLayer/TileHierarchyAggregator.ts b/UI/ShowDataLayer/TileHierarchyAggregator.ts new file mode 100644 index 0000000000..4b1685981d --- /dev/null +++ b/UI/ShowDataLayer/TileHierarchyAggregator.ts @@ -0,0 +1,217 @@ +import FeatureSource, {FeatureSourceForLayer, Tiled} from "../../Logic/FeatureSource/FeatureSource"; +import LayerConfig from "../../Models/ThemeConfig/LayerConfig"; +import {UIEventSource} from "../../Logic/UIEventSource"; +import {Tiles} from "../../Models/TileRange"; +import StaticFeatureSource from "../../Logic/FeatureSource/Sources/StaticFeatureSource"; +import {BBox} from "../../Logic/BBox"; + +export class TileHierarchyAggregator implements FeatureSource { + private _parent: TileHierarchyAggregator; + private _root: TileHierarchyAggregator; + private _z: number; + private _x: number; + private _y: number; + private _tileIndex: number + private _counter: SingleTileCounter + + private _subtiles: [TileHierarchyAggregator, TileHierarchyAggregator, TileHierarchyAggregator, TileHierarchyAggregator] = [undefined, undefined, undefined, undefined] + public totalValue: number = 0 + + private static readonly empty = [] + public readonly features = new UIEventSource<{ feature: any, freshness: Date }[]>(TileHierarchyAggregator.empty) + public readonly name; + + private readonly featuresStatic = [] + private readonly featureProperties: { count: string, tileId: string, id: string }; + + private constructor(parent: TileHierarchyAggregator, z: number, x: number, y: number) { + this._parent = parent; + this._root = parent?._root ?? this + this._z = z; + this._x = x; + this._y = y; + this._tileIndex = Tiles.tile_index(z, x, y) + this.name = "Count(" + this._tileIndex + ")" + + const totals = { + id: ""+this._tileIndex, + tileId: ""+this._tileIndex, + count: ""+0 + } + this.featureProperties = totals + + const now = new Date() + const feature = { + "type": "Feature", + "properties": totals, + "geometry": { + "type": "Point", + "coordinates": Tiles.centerPointOf(z, x, y) + } + } + this.featuresStatic.push({feature: feature, freshness: now}) + + const bbox = BBox.fromTile(z, x, y) + const box = { + "type": "Feature", + "properties": totals, + "geometry": { + "type": "Polygon", + "coordinates": [ + [ + [bbox.minLon, bbox.minLat], + [bbox.minLon, bbox.maxLat], + [bbox.maxLon, bbox.maxLat], + [bbox.maxLon, bbox.minLat], + [bbox.minLon, bbox.minLat] + ] + ] + } + } + this.featuresStatic.push({feature: box, freshness: now}) + } + + public getTile(tileIndex): TileHierarchyAggregator { + if (tileIndex === this._tileIndex) { + return this; + } + let [tileZ, tileX, tileY] = Tiles.tile_from_index(tileIndex) + while (tileZ - 1 > this._z) { + tileX = Math.floor(tileX / 2) + tileY = Math.floor(tileY / 2) + tileZ-- + } + const xDiff = tileX - (2 * this._x) + const yDiff = tileY - (2 * this._y) + const subtileIndex = yDiff * 2 + xDiff; + return this._subtiles[subtileIndex]?.getTile(tileIndex) + } + + private update() { + const newMap = new Map() + let total = 0 + this?._counter?.countsPerLayer?.data?.forEach((count, layerId) => { + newMap.set(layerId, count) + total += count + }) + + for (const tile of this._subtiles) { + if (tile === undefined) { + continue; + } + total += tile.totalValue + } + this.totalValue = total + this._parent?.update() + + if (total === 0) { + this.features.setData(TileHierarchyAggregator.empty) + } else { + this.featureProperties.count = "" + total; + this.features.data = this.featuresStatic + this.features.ping() + } + } + + public addTile(source: FeatureSourceForLayer & Tiled) { + const self = this; + if (source.tileIndex === this._tileIndex) { + if (this._counter === undefined) { + this._counter = new SingleTileCounter(this._tileIndex) + this._counter.countsPerLayer.addCallbackAndRun(_ => self.update()) + } + this._counter.addTileCount(source) + } else { + + // We have to give it to one of the subtiles + let [tileZ, tileX, tileY] = Tiles.tile_from_index(source.tileIndex) + while (tileZ - 1 > this._z) { + tileX = Math.floor(tileX / 2) + tileY = Math.floor(tileY / 2) + tileZ-- + } + const xDiff = tileX - (2 * this._x) + const yDiff = tileY - (2 * this._y) + + const subtileIndex = yDiff * 2 + xDiff; + if (this._subtiles[subtileIndex] === undefined) { + this._subtiles[subtileIndex] = new TileHierarchyAggregator(this, tileZ, tileX, tileY) + } + this._subtiles[subtileIndex].addTile(source) + } + + } + + public static createHierarchy() { + return new TileHierarchyAggregator(undefined, 0, 0, 0) + } + + private visitSubTiles(f : (aggr: TileHierarchyAggregator) => boolean){ + const visitFurther = f(this) + if(visitFurther){ + this._subtiles.forEach(tile => tile?.visitSubTiles(f)) + } + } + + getCountsForZoom(locationControl: UIEventSource<{ zoom : number }>, cutoff: number = 0) : FeatureSource{ + const self = this + + return new StaticFeatureSource( + locationControl.map(loc => { + const features = [] + const targetZoom = loc.zoom + self.visitSubTiles(aggr => { + if(aggr.totalValue < cutoff) { + return false + } + if(aggr._z === targetZoom){ + features.push(...aggr.features.data) + return false + } + return aggr._z <= targetZoom; + + }) + + return features + }) + , true); + } +} + +/** + * Keeps track of a single tile + */ +class SingleTileCounter implements Tiled { + public readonly bbox: BBox; + public readonly tileIndex: number; + public readonly countsPerLayer: UIEventSource> = new UIEventSource>(new Map()) + private readonly registeredLayers: Map = new Map(); + public readonly z: number + public readonly x: number + public readonly y: number + + + constructor(tileIndex: number) { + this.tileIndex = tileIndex + this.bbox = BBox.fromTileIndex(tileIndex) + const [z, x, y] = Tiles.tile_from_index(tileIndex) + this.z = z; + this.x = x; + this.y = y + } + + public addTileCount(source: FeatureSourceForLayer) { + const layer = source.layer.layerDef + this.registeredLayers.set(layer.id, layer) + const self = this + + source.features.map(f => { + const isDisplayed = source.layer.isDisplayed.data + self.countsPerLayer.data.set(layer.id, isDisplayed ? f.length : 0) + self.countsPerLayer.ping() + }, [source.layer.isDisplayed]) + + + } + +} \ No newline at end of file diff --git a/UI/SpecialVisualizations.ts b/UI/SpecialVisualizations.ts index ef6ae16987..de45911db0 100644 --- a/UI/SpecialVisualizations.ts +++ b/UI/SpecialVisualizations.ts @@ -5,7 +5,6 @@ import {ImageCarousel} from "./Image/ImageCarousel"; import Combine from "./Base/Combine"; import {FixedUiElement} from "./Base/FixedUiElement"; import {ImageUploadFlow} from "./Image/ImageUploadFlow"; - import ShareButton from "./BigComponents/ShareButton"; import Svg from "../Svg"; import ReviewElement from "./Reviews/ReviewElement"; @@ -13,17 +12,21 @@ import MangroveReviews from "../Logic/Web/MangroveReviews"; import Translations from "./i18n/Translations"; import ReviewForm from "./Reviews/ReviewForm"; import OpeningHoursVisualization from "./OpeningHours/OpeningHoursVisualization"; - import State from "../State"; -import {ImageSearcher} from "../Logic/Actors/ImageSearcher"; import BaseUIElement from "./BaseUIElement"; import Title from "./Base/Title"; import Table from "./Base/Table"; import Histogram from "./BigComponents/Histogram"; import Loc from "../Models/Loc"; import {Utils} from "../Utils"; -import BaseLayer from "../Models/BaseLayer"; import LayerConfig from "../Models/ThemeConfig/LayerConfig"; +import ImportButton from "./BigComponents/ImportButton"; +import {Tag} from "../Logic/Tags/Tag"; +import StaticFeatureSource from "../Logic/FeatureSource/Sources/StaticFeatureSource"; +import ShowDataMultiLayer from "./ShowDataLayer/ShowDataMultiLayer"; +import Minimap from "./Base/Minimap"; +import AllImageProviders from "../Logic/ImageProviders/AllImageProviders"; +import WikipediaBox from "./WikipediaBox"; export interface SpecialVisualization { funcName: string, @@ -35,14 +38,6 @@ export interface SpecialVisualization { export default class SpecialVisualizations { - - static constructMiniMap: (options?: { - background?: UIEventSource, - location?: UIEventSource, - allowMoving?: boolean, - leafletOptions?: any - }) => BaseUIElement; - static constructShowDataLayer: (features: UIEventSource<{ feature: any; freshness: Date }[]>, leafletMap: UIEventSource, layoutToUse: UIEventSource, enablePopups?: boolean, zoomToFeatures?: boolean) => any; public static specialVisualizations: SpecialVisualization[] = [ { @@ -65,7 +60,6 @@ export default class SpecialVisualizations { })).SetStyle("border: 1px solid black; border-radius: 1em;padding:1em;display:block;") }) }, - { funcName: "image_carousel", docs: "Creates an image carousel for the given sources. An attempt will be made to guess what source is used. Supported: Wikidata identifiers, Wikipedia pages, Wikimedia categories, IMGUR (with attribution, direct links)", @@ -73,21 +67,12 @@ export default class SpecialVisualizations { name: "image key/prefix", defaultValue: "image", doc: "The keys given to the images, e.g. if image is given, the first picture URL will be added as image, the second as image:0, the third as image:1, etc... " - }, - { - name: "smart search", - defaultValue: "true", - doc: "Also include images given via 'Wikidata', 'wikimedia_commons' and 'mapillary" - }], + }], constr: (state: State, tags, args) => { const imagePrefix = args[0]; - const loadSpecial = args[1].toLowerCase() === "true"; - const searcher: UIEventSource<{ key: string, url: string }[]> = ImageSearcher.construct(tags, imagePrefix, loadSpecial); - - return new ImageCarousel(searcher, tags); + return new ImageCarousel(AllImageProviders.LoadImagesFor(tags, imagePrefix), tags); } }, - { funcName: "image_upload", docs: "Creates a button where a user can upload an image to IMGUR", @@ -100,6 +85,20 @@ export default class SpecialVisualizations { return new ImageUploadFlow(tags, args[0]) } }, + { + funcName: "wikipedia", + docs: "A box showing the corresponding wikipedia article - based on the wikidata tag", + args: [ + { + name: "keyToShowWikipediaFor", + doc: "Use the wikidata entry from this key to show the wikipedia article for", + defaultValue: "wikidata" + } + ], + example: "`{wikipedia()}` is a basic example, `{wikipedia(name:etymology:wikidata)}` to show the wikipedia page of whom the feature was named after. Also remember that these can be styled, e.g. `{wikipedia():max-height: 10rem}` to limit the height", + constr: (_, tagsSource, args) => + new WikipediaBox( tagsSource.map(tags => tags[args[0]])) + }, { funcName: "minimap", docs: "A small map showing the selected feature. Note that no styling is applied, wrap this in a div", @@ -153,7 +152,7 @@ export default class SpecialVisualizations { lon: Number(properties._lon), zoom: zoom }) - const minimap = SpecialVisualizations.constructMiniMap( + const minimap = Minimap.createMiniMap( { background: state.backgroundLayer, location: locationSource, @@ -169,12 +168,14 @@ export default class SpecialVisualizations { } }) - SpecialVisualizations.constructShowDataLayer( - featuresToShow, - minimap["leafletMap"], - State.state.layoutToUse, - false, - true + new ShowDataMultiLayer( + { + leafletMap: minimap["leafletMap"], + enablePopups: false, + zoomToFeatures: true, + layers: State.state.filteredLayers, + features: new StaticFeatureSource(featuresToShow, true) + } ) @@ -185,7 +186,7 @@ export default class SpecialVisualizations { { funcName: "reviews", docs: "Adds an overview of the mangrove-reviews of this object. Mangrove.Reviews needs - in order to identify the reviewed object - a coordinate and a name. By default, the name of the object is given, but this can be overwritten", - example: "{reviews()} for a vanilla review, {reviews(name, play_forest)} to review a play forest. If a name is known, the name will be used as identifier, otherwise 'play_forest' is used", + example: "`{reviews()}` for a vanilla review, `{reviews(name, play_forest)}` to review a play forest. If a name is known, the name will be used as identifier, otherwise 'play_forest' is used", args: [{ name: "subjectKey", defaultValue: "name", @@ -222,7 +223,6 @@ export default class SpecialVisualizations { return new OpeningHoursVisualization(tagSource, args[0]) } }, - { funcName: "live", docs: "Downloads a JSON from the given URL, e.g. '{live(example.org/data.json, shorthand:x.y.z, other:a.b.c, shorthand)}' will download the given file, will create an object {shorthand: json[x][y][z], other: json[a][b][c] out of it and will return 'other' or 'json[a][b][c]. This is made to use in combination with tags, e.g. {live({url}, {url:format}, needed_value)}", @@ -243,7 +243,6 @@ export default class SpecialVisualizations { return new VariableUiElement(source.map(data => data[neededValue] ?? "Loading...")); } }, - { funcName: "histogram", docs: "Create a histogram for a list of given values, read from the properties.", @@ -323,10 +322,10 @@ export default class SpecialVisualizations { const generateShareData = () => { - const title = state?.layoutToUse?.data?.title?.txt ?? "MapComplete"; + const title = state?.layoutToUse?.title?.txt ?? "MapComplete"; let matchingLayer: LayerConfig = undefined; - for (const layer of (state?.layoutToUse?.data?.layers ?? [])) { + for (const layer of (state?.layoutToUse?.layers ?? [])) { if (layer.source.osmTags.matchesProperties(tagSource?.data)) { matchingLayer = layer } @@ -344,7 +343,7 @@ export default class SpecialVisualizations { return { title: name, url: url, - text: state?.layoutToUse?.data?.shortDescription?.txt ?? "MapComplete" + text: state?.layoutToUse?.shortDescription?.txt ?? "MapComplete" } } @@ -367,18 +366,86 @@ export default class SpecialVisualizations { const key = args [0] return new VariableUiElement( tagSource.map(tags => tags[key]).map(value => { - if (value === undefined) { - return undefined - } - const allUnits = [].concat(...state.layoutToUse.data.layers.map(lyr => lyr.units)) - const unit = allUnits.filter(unit => unit.isApplicableToKey(key))[0] - if (unit === undefined) { - return value; - } - return unit.asHumanLongValue(value); + if (value === undefined) { + return undefined + } + const allUnits = [].concat(...state.layoutToUse.layers.map(lyr => lyr.units)) + const unit = allUnits.filter(unit => unit.isApplicableToKey(key))[0] + if (unit === undefined) { + return value; + } + return unit.asHumanLongValue(value); - }, - [state.layoutToUse]) + }) + ) + } + }, + { + funcName: "import_button", + args: [ + { + name: "tags", + doc: "Tags to copy-specification. This contains one or more pairs (seperated by a `;`), e.g. `amenity=fast_food; addr:housenumber=$number`. This new point will then have the tags `amenity=fast_food` and `addr:housenumber` with the value that was saved in `number` in the original feature. (Hint: prepare these values, e.g. with calculatedTags)" + }, + { + name: "text", + doc: "The text to show on the button", + defaultValue: "Import this data into OpenStreetMap" + }, + { + name: "icon", + doc: "A nice icon to show in the button", + defaultValue: "./assets/svg/addSmall.svg" + }], + docs: `This button will copy the data from an external dataset into OpenStreetMap. It is only functional in official themes but can be tested in unofficial themes. + +If you want to import a dataset, make sure that: + +1. The dataset to import has a suitable license +2. The community has been informed of the import +3. All other requirements of the [import guidelines](https://wiki.openstreetmap.org/wiki/Import/Guidelines) have been followed + +There are also some technicalities in your theme to keep in mind: + +1. The new point will be added and will flow through the program as any other new point as if it came from OSM. + This means that there should be a layer which will match the new tags and which will display it. +2. The original point from your geojson layer will gain the tag '_imported=yes'. + This should be used to change the appearance or even to hide it (eg by changing the icon size to zero) +3. There should be a way for the theme to detect previously imported points, even after reloading. + A reference number to the original dataset is an excellen way to do this +`, + constr: (state, tagSource, args) => { + if (!state.layoutToUse.official && !state.featureSwitchIsTesting.data) { + return new Combine([new FixedUiElement("The import button is disabled for unofficial themes to prevent accidents.").SetClass("alert"), + new FixedUiElement("To test, add 'test=true' to the URL. The changeset will be printed in the console. Please open a PR to officialize this theme to actually enable the import button.")]) + } + const tgsSpec = args[0].split(",").map(spec => { + const kv = spec.split("=").map(s => s.trim()); + if (kv.length != 2) { + throw "Invalid key spec: multiple '=' found in " + spec + } + return kv + }) + const rewrittenTags: UIEventSource = tagSource.map(tags => { + const newTags: Tag [] = [] + for (const [key, value] of tgsSpec) { + if (value.startsWith('$')) { + const origKey = value.substring(1) + newTags.push(new Tag(key, tags[origKey])) + } else { + newTags.push(new Tag(key, value)) + } + } + return newTags + }) + const id = tagSource.data.id; + const feature = State.state.allElements.ContainingFeatures.get(id) + if (feature.geometry.type !== "Point") { + return new FixedUiElement("Error: can only import point objects").SetClass("alert") + } + const [lon, lat] = feature.geometry.coordinates; + return new ImportButton( + args[2], args[1], tagSource, rewrittenTags, lat, lon ) } } @@ -394,12 +461,12 @@ export default class SpecialVisualizations { [ new Title(viz.funcName, 3), viz.docs, - new Table(["name", "default", "description"], + viz.args.length > 0 ? new Table(["name", "default", "description"], viz.args.map(arg => [arg.name, arg.defaultValue ?? "undefined", arg.doc]) - ), + ) : undefined, new Title("Example usage", 4), new FixedUiElement( - viz.example ?? "{" + viz.funcName + "(" + viz.args.map(arg => arg.defaultValue).join(",") + ")}" + viz.example ?? "`{" + viz.funcName + "(" + viz.args.map(arg => arg.defaultValue).join(",") + ")}`" ).SetClass("literal-code"), ] diff --git a/UI/WikipediaBox.ts b/UI/WikipediaBox.ts new file mode 100644 index 0000000000..7f0d392a60 --- /dev/null +++ b/UI/WikipediaBox.ts @@ -0,0 +1,109 @@ +import {UIEventSource} from "../Logic/UIEventSource"; +import {VariableUiElement} from "./Base/VariableUIElement"; +import Wikipedia from "../Logic/Web/Wikipedia"; +import Loading from "./Base/Loading"; +import {FixedUiElement} from "./Base/FixedUiElement"; +import Combine from "./Base/Combine"; +import BaseUIElement from "./BaseUIElement"; +import Title from "./Base/Title"; +import Translations from "./i18n/Translations"; +import Svg from "../Svg"; +import Wikidata, {WikidataResponse} from "../Logic/Web/Wikidata"; +import Locale from "./i18n/Locale"; +import Toggle from "./Input/Toggle"; + +export default class WikipediaBox extends Toggle { + + + constructor(wikidataId: string | UIEventSource) { + const wp = Translations.t.general.wikipedia; + if (typeof wikidataId === "string") { + wikidataId = new UIEventSource(wikidataId) + } + + + const wikibox = wikidataId + .bind(id => { + console.log("Wikidata is", id) + if(id === undefined){ + return undefined + } + console.log("Initing load WIkidataentry with id", id) + return Wikidata.LoadWikidataEntry(id); + }) + .map(maybewikidata => { + if (maybewikidata === undefined) { + return new Loading(wp.loading.Clone()) + } + if (maybewikidata["error"] !== undefined) { + return wp.failed.Clone().SetClass("alert p-4") + } + const wikidata = maybewikidata["success"] + console.log("Got wikidata response", wikidata) + if (wikidata.wikisites.size === 0) { + return wp.noWikipediaPage.Clone() + } + + const preferredLanguage = [Locale.language.data, "en", Array.from(wikidata.wikisites.keys())[0]] + let language + let pagetitle; + let i = 0 + do { + language = preferredLanguage[i] + pagetitle = wikidata.wikisites.get(language) + i++; + } while (pagetitle === undefined) + return WikipediaBox.createContents(pagetitle, language) + }, [Locale.language]) + + + const contents = new VariableUiElement( + wikibox + ).SetClass("overflow-auto normal-background rounded-lg") + + + const mainContent = new Combine([ + new Combine([Svg.wikipedia_ui().SetStyle("width: 1.5rem").SetClass("mr-3"), + new Title(Translations.t.general.wikipedia.wikipediaboxTitle.Clone(), 2)]).SetClass("flex"), + contents]).SetClass("block rounded-xl subtle-background m-1 p-2 flex flex-col") + .SetStyle("max-height: inherit") + super( + mainContent, + undefined, + wikidataId.map(id => id !== undefined) + ) + } + + /** + * Returns the actual content in a scrollable way + * @param pagename + * @param language + * @private + */ + private static createContents(pagename: string, language: string): BaseUIElement { + const htmlContent = Wikipedia.GetArticle({ + pageName: pagename, + language: language + }) + const wp = Translations.t.general.wikipedia + const contents: UIEventSource = htmlContent.map(htmlContent => { + if (htmlContent === undefined) { + // Still loading + return new Loading(wp.loading.Clone()) + } + if (htmlContent["success"] !== undefined) { + return new FixedUiElement(htmlContent["success"]).SetClass("wikipedia-article") + } + if (htmlContent["error"]) { + console.warn("Loading wikipage failed due to", htmlContent["error"]) + return wp.failed.Clone().SetClass("alert p-4") + } + + return undefined + }) + + return new Combine([new VariableUiElement(contents).SetClass("block pl-6 pt-2")]) + .SetClass("block") + } + +} \ No newline at end of file diff --git a/UI/i18n/Translation.ts b/UI/i18n/Translation.ts index 796c52e8db..15bff437af 100644 --- a/UI/i18n/Translation.ts +++ b/UI/i18n/Translation.ts @@ -196,4 +196,22 @@ export class Translation extends BaseUIElement { return allIcons.filter(icon => icon != undefined) } + static ExtractAllTranslationsFrom(object: any, context = ""): { context: string, tr: Translation }[] { + const allTranslations: { context: string, tr: Translation }[] = [] + for (const key in object) { + const v = object[key] + if (v === undefined || v === null) { + continue + } + if (v instanceof Translation) { + allTranslations.push({context: context +"." + key, tr: v}) + continue + } + if (typeof v === "object") { + allTranslations.push(...Translation.ExtractAllTranslationsFrom(v, context + "." + key)) + continue + } + } + return allTranslations + } } \ No newline at end of file diff --git a/Utils.ts b/Utils.ts index 4cfc525209..5c04c95472 100644 --- a/Utils.ts +++ b/Utils.ts @@ -1,5 +1,4 @@ import * as colors from "./assets/colors.json" -import {TileRange} from "./Models/TileRange"; export class Utils { @@ -8,9 +7,9 @@ export class Utils { * However, ts-node crashes when it sees 'document'. When running from console, we flag this and disable all code where document is needed. * This is a workaround and yet another hack */ - public static runningFromConsole = false; + public static runningFromConsole = typeof window === "undefined"; public static readonly assets_path = "./assets/svg/"; - public static externalDownloadFunction: (url: string) => Promise; + public static externalDownloadFunction: (url: string, headers?: any) => Promise; private static knownKeys = ["addExtraTags", "and", "calculatedTags", "changesetmessage", "clustering", "color", "condition", "customCss", "dashArray", "defaultBackgroundId", "description", "descriptionTail", "doNotDownload", "enableAddNewPoints", "enableBackgroundLayerSelection", "enableGeolocation", "enableLayers", "enableMoreQuests", "enableSearch", "enableShareScreen", "enableUserBadge", "freeform", "hideFromOverview", "hideInAnswer", "icon", "iconOverlays", "iconSize", "id", "if", "ifnot", "isShown", "key", "language", "layers", "lockLocation", "maintainer", "mappings", "maxzoom", "maxZoom", "minNeededElements", "minzoom", "multiAnswer", "name", "or", "osmTags", "passAllFeatures", "presets", "question", "render", "roaming", "roamingRenderings", "rotation", "shortDescription", "socialImage", "source", "startLat", "startLon", "startZoom", "tagRenderings", "tags", "then", "title", "titleIcons", "type", "version", "wayHandling", "widenFactor", "width"] private static extraKeys = ["nl", "en", "fr", "de", "pt", "es", "name", "phone", "email", "amenity", "leisure", "highway", "building", "yes", "no", "true", "false"] @@ -192,11 +191,12 @@ export class Utils { } /** - * Copies all key-value pairs of the source into the target. + * Copies all key-value pairs of the source into the target. This will change the target * If the key starts with a '+', the values of the list will be appended to the target instead of overwritten * @param source * @param target * @constructor + * @return the second parameter as is */ static Merge(source: any, target: any) { for (const key in source) { @@ -237,7 +237,7 @@ export class Utils { } return target; } - + static getOrSetDefault(dict: Map, k: K, v: () => V) { let found = dict.get(k); if (found !== undefined) { @@ -245,45 +245,6 @@ export class Utils { } dict.set(k, v()); return dict.get(k); - - } - - /** - * Calculates the tile bounds of the - * @param z - * @param x - * @param y - * @returns [[maxlat, minlon], [minlat, maxlon]] - */ - static tile_bounds(z: number, x: number, y: number): [[number, number], [number, number]] { - return [[Utils.tile2lat(y, z), Utils.tile2long(x, z)], [Utils.tile2lat(y + 1, z), Utils.tile2long(x + 1, z)]] - } - - /** - * Return x, y of the tile containing (lat, lon) on the given zoom level - */ - static embedded_tile(lat: number, lon: number, z: number): { x: number, y: number, z: number } { - return {x: Utils.lon2tile(lon, z), y: Utils.lat2tile(lat, z), z: z} - } - - static TileRangeBetween(zoomlevel: number, lat0: number, lon0: number, lat1: number, lon1: number): TileRange { - const t0 = Utils.embedded_tile(lat0, lon0, zoomlevel) - const t1 = Utils.embedded_tile(lat1, lon1, zoomlevel) - - const xstart = Math.min(t0.x, t1.x) - const xend = Math.max(t0.x, t1.x) - const ystart = Math.min(t0.y, t1.y) - const yend = Math.max(t0.y, t1.y) - const total = (1 + xend - xstart) * (1 + yend - ystart) - - return { - xstart: xstart, - xend: xend, - ystart: ystart, - yend: yend, - total: total, - zoomlevel: zoomlevel - } } public static MinifyJSON(stringified: string): string { @@ -326,46 +287,56 @@ export class Utils { return result; } - public static MapRange(tileRange: TileRange, f: (x: number, y: number) => T): T[] { - const result: T[] = [] - for (let x = tileRange.xstart; x <= tileRange.xend; x++) { - for (let y = tileRange.ystart; y <= tileRange.yend; y++) { - const t = f(x, y); - result.push(t) - } - } - return result; + + private static injectedDownloads = {} + + public static injectJsonDownloadForTests(url: string, data) { + Utils.injectedDownloads[url] = data } - public static downloadJson(url: string): Promise { + public static download(url: string, headers?: any): Promise { if (this.externalDownloadFunction !== undefined) { - return this.externalDownloadFunction(url) + return this.externalDownloadFunction(url, headers) } - return new Promise( - (resolve, reject) => { - try { - const xhr = new XMLHttpRequest(); - xhr.onload = () => { - if (xhr.status == 200) { - try { - resolve(JSON.parse(xhr.response)) - } catch (e) { - reject("Not a valid json: " + xhr.response) - } - } else { - reject(xhr.statusText) - } - }; - xhr.open('GET', url); - xhr.setRequestHeader("accept", "application/json") - xhr.send(); - } catch (e) { - reject(e) + return new Promise((resolve, reject) => { + const xhr = new XMLHttpRequest(); + xhr.onload = () => { + if (xhr.status == 200) { + resolve(xhr.response) + } else if (xhr.status === 509 || xhr.status === 429) { + reject("rate limited") + } else { + reject(xhr.statusText) + } + }; + xhr.open('GET', url); + if (headers !== undefined) { + + for (const key in headers) { + xhr.setRequestHeader(key, headers[key]) + } } + + xhr.send(); + xhr.onerror = reject } ) + } + public static async downloadJson(url: string, headers?: any): Promise { + const injected = Utils.injectedDownloads[url] + if (injected !== undefined) { + console.log("Using injected resource for test for URL", url) + return new Promise((resolve, _) => resolve(injected)) + } + const data = await Utils.download(url, Utils.Merge({"accept": "application/json"}, headers ?? {})) + try { + return JSON.parse(data) + } catch (e) { + console.error("Could not parse ", data, "due to", e, "\n", e.stack) + throw e; + } } /** @@ -422,29 +393,6 @@ export class Utils { return bestColor ?? hex; } - public static setDefaults(options, defaults) { - for (let key in defaults) { - if (!(key in options)) options[key] = defaults[key]; - } - return options; - } - - private static tile2long(x, z) { - return (x / Math.pow(2, z) * 360 - 180); - } - - private static tile2lat(y, z) { - const n = Math.PI - 2 * Math.PI * y / Math.pow(2, z); - return (180 / Math.PI * Math.atan(0.5 * (Math.exp(n) - Math.exp(-n)))); - } - - private static lon2tile(lon, zoom) { - return (Math.floor((lon + 180) / 360 * Math.pow(2, zoom))); - } - - private static lat2tile(lat, zoom) { - return (Math.floor((1 - Math.log(Math.tan(lat * Math.PI / 180) + 1 / Math.cos(lat * Math.PI / 180)) / Math.PI) / 2 * Math.pow(2, zoom))); - } private static colorDiff(c0: { r: number, g: number, b: number }, c1: { r: number, g: number, b: number }) { return Math.abs(c0.r - c1.r) + Math.abs(c0.g - c1.g) + Math.abs(c0.b - c1.b); @@ -474,17 +422,23 @@ export class Utils { } static sortKeys(o: any) { - const copy = {} + const copy = {} let keys = Object.keys(o) keys = keys.sort() for (const key of keys) { let v = o[key] - if(typeof v === "object"){ + if (typeof v === "object") { v = Utils.sortKeys(v) } copy[key] = v } return copy } + + public static async waitFor(timeMillis: number): Promise { + return new Promise((resolve) => { + window.setTimeout(resolve, timeMillis); + }) + } } diff --git a/assets/layers/bench/bench.json b/assets/layers/bench/bench.json index 5a94c40ad6..af5bbd10bc 100644 --- a/assets/layers/bench/bench.json +++ b/assets/layers/bench/bench.json @@ -581,37 +581,21 @@ "amenity=bench" ], "title": { - "en": "Bench", - "de": "Sitzbank", - "fr": "Banc", - "nl": "Zitbank", - "es": "Banco", - "it": "Panchina", + "en": "bench", + "de": "sitzbank", + "fr": "banc", + "nl": "zitbank", + "es": "banco", + "it": "panchina", "ru": "Скамейка", - "id": "Bangku", + "id": "bangku", "zh_Hans": "长椅", - "nb_NO": "Benk", + "nb_NO": "benk", "zh_Hant": "長椅", - "pt_BR": "Banco", - "fi": "Penkki", + "pt_BR": "banco", + "fi": "penkki", "pl": "Ławka" }, - "description": { - "en": "Add a new bench", - "de": "Neue Sitzbank eintragen", - "fr": "Ajouter un nouveau banc", - "nl": "Voeg een nieuwe zitbank toe", - "es": "Añadir un nuevo banco", - "hu": "Pad hozzáadása", - "it": "Aggiungi una nuova panchina", - "ru": "Добавить новую скамейку", - "zh_Hans": "增加一个新的长椅", - "nb_NO": "Legg til en ny benk", - "zh_Hant": "新增長椅", - "pt_BR": "Adicionar um novo banco", - "fi": "Lisää uusi penkki", - "pl": "Dodaj nową ławkę" - }, "presiceInput": { "preferredBackground": "photo" } diff --git a/assets/layers/bike_shop/bike_shop.json b/assets/layers/bike_shop/bike_shop.json index 571ff46e22..d7930f8547 100644 --- a/assets/layers/bike_shop/bike_shop.json +++ b/assets/layers/bike_shop/bike_shop.json @@ -24,7 +24,7 @@ ] }, { - "#": "if sport is defined and is not bicycle, it is retrackted; if bicycle retail/repair is marked as 'no', it is retracted too.", + "#": "if sport is defined and is not bicycle, it is not matched; if bicycle retail/repair is marked as 'no', it is not shown to too.", "##": "There will be a few false-positives with this. They will get filtered out by people marking both 'not selling bikes' and 'not repairing bikes'. Furthermore, the OSMers will add a sports-subcategory on it", "and": [ "shop=sports", @@ -38,13 +38,6 @@ ] } ] - }, - { - "#": "Any shop with any bicycle service", - "and": [ - "shop~*", - "service:bicycle:.*~~.*" - ] } ] } diff --git a/assets/layers/birdhide/birdhide.json b/assets/layers/birdhide/birdhide.json index 01e2853f9c..38f8e19703 100644 --- a/assets/layers/birdhide/birdhide.json +++ b/assets/layers/birdhide/birdhide.json @@ -235,7 +235,7 @@ "amenity=shelter" ], "title": { - "nl": "Vogelkijkhut" + "nl": "vogelkijkhut" }, "description": { "nl": "Een overdekte hut waarbinnen er warm en droog naar vogels gekeken kan worden" @@ -248,7 +248,7 @@ "shelter=no" ], "title": { - "nl": "Vogelkijkwand" + "nl": "vogelkijkwand" }, "description": { "nl": "Een vogelkijkwand waarachter men kan staan om vogels te kijken" @@ -258,6 +258,7 @@ "wayHandling": 1, "filter": [ { + "id": "wheelchair", "options": [ { "question": { @@ -275,6 +276,7 @@ ] }, { + "id": "shelter", "options": [ { "question": { diff --git a/assets/layers/cafe_pub/cafe_pub.json b/assets/layers/cafe_pub/cafe_pub.json index 3c03eb21a8..e5977e8bbc 100644 --- a/assets/layers/cafe_pub/cafe_pub.json +++ b/assets/layers/cafe_pub/cafe_pub.json @@ -165,10 +165,12 @@ "email", "phone", "payment-options", - "wheelchair-access" + "wheelchair-access", + "dog-access" ], "filter": [ { + "id": "opened-now", "options": [ { "question": { diff --git a/assets/layers/charging_station/README.md b/assets/layers/charging_station/README.md new file mode 100644 index 0000000000..bc88af2d72 --- /dev/null +++ b/assets/layers/charging_station/README.md @@ -0,0 +1,35 @@ +The charging station theme +========================== + +As you might have noticed, the charging station theme is complicated and large. + +There are a ton of repititive questions. Luckily, we can generate those. + +If you want to add a missing socket type, then: + +- Add all the properties in 'types.csv' +- Add an icon. (Note: icons are way better as pictures as they are perceived more abstractly) +- Update license_info.json with the copyright info of the new icon. Note that we strive to have Creative-commons icons only (though there are exceptions) + +AT this point, most of the work should be done; feel free to send a PR. If you would like to test it locally first (which is recommended) and have a working dev environment, then run: + +- Run 'ts-node csvToJson.ts' which will generate a new charging_station.json based on the protojson +- Run`npm run query:licenses` to get an interactive program to add the license of your artwork, followed by `npm run generate:licenses` +- Run `npm run generate:layeroverview` to generate the layer files +- Run `npm run start` to run the instance + +The CSV File +------------ + +The columns in the CSV file are: + +- key: the key as described on the wiki, starts with `socket:` +- image: The associated image (a .svg) +- description:en A description in english +- description:nl A description in english +- countryWhiteList: Only show this plug type in these countries +- countryBlackList: Don't show this plug type in these countries. NOt compatibel with the whiteList +- commonVoltages, commonCurrents, commonOutputs: common values for these tags +- associatedVehicleTypes and neverAssociatedWith: these work in tandem to hide options. + If every associated vehicle type is `no`, then the option is hidden + If at least one `neverAssociatedVehicleType` is `yes` and none of the associated types is yes, then the option is hidden too diff --git a/assets/layers/charging_station/bosch-3pin.svg b/assets/layers/charging_station/bosch-3pin.svg new file mode 100644 index 0000000000..4295a1a751 --- /dev/null +++ b/assets/layers/charging_station/bosch-3pin.svg @@ -0,0 +1,119 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + diff --git a/assets/layers/charging_station/charging_station.json b/assets/layers/charging_station/charging_station.json index ae3a12b1dc..49636eba5c 100644 --- a/assets/layers/charging_station/charging_station.json +++ b/assets/layers/charging_station/charging_station.json @@ -1,2947 +1,3519 @@ { - "id": "charging_station", - "name": { - "en": "Charging stations", - "it": "Stazioni di ricarica", - "ja": "充電ステーション", - "nb_NO": "Ladestasjoner", - "ru": "Зарядные станции", - "zh_Hant": "充電站" - }, - "minzoom": 10, - "source": { - "osmTags": { - "or": [ - "amenity=charging_station", - "disused:amenity=charging_station", - "planned:amenity=charging_station", - "construction:amenity=charging_station" + "id": "charging_station", + "name": { + "en": "Charging stations", + "it": "Stazioni di ricarica", + "ja": "充電ステーション", + "nb_NO": "Ladestasjoner", + "ru": "Зарядные станции", + "zh_Hant": "充電站" + }, + "minzoom": 10, + "source": { + "osmTags": { + "or": [ + "amenity=charging_station", + "disused:amenity=charging_station", + "planned:amenity=charging_station", + "construction:amenity=charging_station" + ] + } + }, + "title": { + "render": { + "en": "Charging station", + "it": "Stazione di ricarica", + "ja": "充電ステーション", + "nb_NO": "Ladestasjon", + "ru": "Зарядная станция", + "zh_Hant": "充電站" + } + }, + "description": { + "en": "A charging station", + "it": "Una stazione di ricarica", + "ja": "充電ステーション", + "nb_NO": "En ladestasjon", + "ru": "Зарядная станция", + "zh_Hant": "充電站" + }, + "calculatedTags": [ + "motorcar=feat.properties.motorcar ?? feat.properties.car" + ], + "tagRenderings": [ + "images", + { + "id": "Type", + "question": { + "en": "Which vehicles are allowed to charge here?" + }, + "multiAnswer": true, + "mappings": [ + { + "if": "bicycle=yes", + "ifnot": "bicycle=no", + "then": { + "en": "bicycles can be charged here" + } + }, + { + "if": "motorcar=yes", + "extraTags": "car=", + "ifnot": { + "and": [ + "car=", + "motorcar=no" ] + }, + "then": { + "en": "Cars can be charged here" + } + }, + { + "if": "scooter=yes", + "ifnot": "scooter=no", + "then": { + "en": "Scooters can be charged here" + } + }, + { + "if": "hgv=yes", + "ifnot": "hgv=no", + "then": { + "en": "Heavy good vehicles (such as trucks) can be charged here" + } + }, + { + "if": "bus=yes", + "ifnot": "bus=no", + "then": { + "en": "Buses can be charged here" + } } + ] }, - "title": { - "render": { - "en": "Charging station", - "it": "Stazione di ricarica", - "ja": "充電ステーション", - "nb_NO": "Ladestasjon", - "ru": "Зарядная станция", - "zh_Hant": "充電站" - } - }, - "description": { - "en": "A charging station", - "it": "Una stazione di ricarica", - "ja": "充電ステーション", - "nb_NO": "En ladestasjon", - "ru": "Зарядная станция", - "zh_Hant": "充電站" - }, - "calculatedTags": [ - "motorcar=feat.properties.motorcar ?? feat.properties.car" - ], - "tagRenderings": [ - "images", - { - "id": "Type", - "question": { - "en": "Which vehicles are allowed to charge here?" - }, - "multiAnswer": true, - "mappings": [ - { - "if": "bicycle=yes", - "ifnot": "bicycle=no", - "then": { - "en": "bicycles can be charged here" - } - }, - { - "if": "motorcar=yes", - "extraTags": "car=", - "ifnot": { - "and": [ - "car=", - "motorcar=no" - ] - }, - "then": { - "en": "Cars can be charged here" - } - }, - { - "if": "scooter=yes", - "ifnot": "scooter=no", - "then": { - "en": "Scooters can be charged here" - } - }, - { - "if": "hgv=yes", - "ifnot": "hgv=no", - "then": { - "en": "Heavy good vehicles (such as trucks) can be charged here" - } - }, - { - "if": "bus=yes", - "ifnot": "bus=no", - "then": { - "en": "Buses can be charged here" - } - } - ] - }, - { - "id": "access", - "question": { - "en": "Who is allowed to use this charging station?" - }, - "render": { - "en": "Access is {access}" - }, - "freeform": { - "key": "access", - "addExtraTags": [ - "fixme=Freeform field used for access - doublecheck the value" - ] - }, - "mappings": [ - { - "if": "access=yes", - "then": "Anyone can use this charging station (payment might be needed)" - }, - { - "if": { - "or": [ - "access=permissive", - "access=public" - ] - }, - "then": "Anyone can use this charging station (payment might be needed)", - "hideInAnswer": true - }, - { - "if": "access=customers", - "then": "Only customers of the place this station belongs to can use this charging station
E.g. a charging station operated by hotel which is only usable by their guests " - }, - { - "if": "access=private", - "then": "Not accessible to the general public (e.g. only accessible to the owners, employees, ...)" - } - ] - }, - { - "id": "capacity", - "render": { - "en": "{capacity} vehicles can be charged here at the same time", - "nl": "{capacity} voertuigen kunnen hier op hetzelfde moment opgeladen worden" - }, - "question": { - "en": "How much vehicles can be charged here at the same time?", - "nl": "Hoeveel voertuigen kunnen hier opgeladen worden?" - }, - "freeform": { - "key": "capacity", - "type": "pnat" - } - }, - { - "id": "Available_charging_stations (generated)", - "question": { - "en": "Which charging stations are available here?" - }, - "multiAnswer": true, - "mappings": [ - { - "if": "socket:schuko=1", - "ifnot": "socket:schuko=", - "then": { - "en": " Schuko wall plug without ground pin (CEE7/4 type F)", - "nl": " Schuko stekker zonder aardingspin (CEE7/4 type F)" - }, - "hideInAnswer": { - "or": [ - "_country!=be", - "_country!=fr", - "_country!=ma", - "_country!=tn", - "_country!=pl", - "_country!=cs", - "_country!=sk", - "_country!=mo" - ] - } - }, - { - "if": { - "and": [ - "socket:schuko~*", - "socket:schuko!=1" - ] - }, - "then": { - "en": " Schuko wall plug without ground pin (CEE7/4 type F)", - "nl": " Schuko stekker zonder aardingspin (CEE7/4 type F)" - }, - "hideInAnswer": true - }, - { - "if": "socket:typee=1", - "ifnot": "socket:typee=", - "then": { - "en": " European wall plug with ground pin (CEE7/4 type E)", - "nl": " Europese stekker met aardingspin (CEE7/4 type E)" - } - }, - { - "if": { - "and": [ - "socket:typee~*", - "socket:typee!=1" - ] - }, - "then": { - "en": " European wall plug with ground pin (CEE7/4 type E)", - "nl": " Europese stekker met aardingspin (CEE7/4 type E)" - }, - "hideInAnswer": true - }, - { - "if": "socket:chademo=1", - "ifnot": "socket:chademo=", - "then": { - "en": " Chademo", - "nl": " " - } - }, - { - "if": { - "and": [ - "socket:chademo~*", - "socket:chademo!=1" - ] - }, - "then": { - "en": " Chademo", - "nl": " " - }, - "hideInAnswer": true - }, - { - "if": "socket:type1_cable=1", - "ifnot": "socket:type1_cable=", - "then": { - "en": " Type 1 with cable (J1772)", - "nl": " Type 1 met kabel (J1772)" - } - }, - { - "if": { - "and": [ - "socket:type1_cable~*", - "socket:type1_cable!=1" - ] - }, - "then": { - "en": " Type 1 with cable (J1772)", - "nl": " Type 1 met kabel (J1772)" - }, - "hideInAnswer": true - }, - { - "if": "socket:type1=1", - "ifnot": "socket:type1=", - "then": { - "en": " Type 1 without cable (J1772)", - "nl": " Type 1 zonder kabel (J1772)" - } - }, - { - "if": { - "and": [ - "socket:type1~*", - "socket:type1!=1" - ] - }, - "then": { - "en": " Type 1 without cable (J1772)", - "nl": " Type 1 zonder kabel (J1772)" - }, - "hideInAnswer": true - }, - { - "if": "socket:type1_combo=1", - "ifnot": "socket:type1_combo=", - "then": { - "en": " Type 1 CCS (aka Type 1 Combo)", - "nl": " " - } - }, - { - "if": { - "and": [ - "socket:type1_combo~*", - "socket:type1_combo!=1" - ] - }, - "then": { - "en": " Type 1 CCS (aka Type 1 Combo)", - "nl": " " - }, - "hideInAnswer": true - }, - { - "if": "socket:tesla_supercharger=1", - "ifnot": "socket:tesla_supercharger=", - "then": { - "en": " Tesla Supercharger", - "nl": " " - } - }, - { - "if": { - "and": [ - "socket:tesla_supercharger~*", - "socket:tesla_supercharger!=1" - ] - }, - "then": { - "en": " Tesla Supercharger", - "nl": " " - }, - "hideInAnswer": true - }, - { - "if": "socket:type2=1", - "ifnot": "socket:type2=", - "then": { - "en": " Type 2 (mennekes)", - "nl": " " - } - }, - { - "if": { - "and": [ - "socket:type2~*", - "socket:type2!=1" - ] - }, - "then": { - "en": " Type 2 (mennekes)", - "nl": " " - }, - "hideInAnswer": true - }, - { - "if": "socket:type2_combo=1", - "ifnot": "socket:type2_combo=", - "then": { - "en": " Type 2 CCS (mennekes)", - "nl": " " - } - }, - { - "if": { - "and": [ - "socket:type2_combo~*", - "socket:type2_combo!=1" - ] - }, - "then": { - "en": " Type 2 CCS (mennekes)", - "nl": " " - }, - "hideInAnswer": true - }, - { - "if": "socket:type2_cable=1", - "ifnot": "socket:type2_cable=", - "then": { - "en": " Type 2 with cable (mennekes)", - "nl": " Type 2 met kabel (J1772)" - } - }, - { - "if": { - "and": [ - "socket:type2_cable~*", - "socket:type2_cable!=1" - ] - }, - "then": { - "en": " Type 2 with cable (mennekes)", - "nl": " Type 2 met kabel (J1772)" - }, - "hideInAnswer": true - }, - { - "if": "socket:tesla_supercharger_ccs=1", - "ifnot": "socket:tesla_supercharger_ccs=", - "then": { - "en": " Tesla Supercharger CCS (a branded type2_css)", - "nl": " " - } - }, - { - "if": { - "and": [ - "socket:tesla_supercharger_ccs~*", - "socket:tesla_supercharger_ccs!=1" - ] - }, - "then": { - "en": " Tesla Supercharger CCS (a branded type2_css)", - "nl": " " - }, - "hideInAnswer": true - }, - { - "if": "socket:tesla_destination=1", - "ifnot": "socket:tesla_destination=", - "then": { - "en": " Tesla Supercharger (destination)", - "nl": " " - }, - "hideInAnswer": { - "or": [ - "_country!=us" - ] - } - }, - { - "if": { - "and": [ - "socket:tesla_destination~*", - "socket:tesla_destination!=1" - ] - }, - "then": { - "en": " Tesla Supercharger (destination)", - "nl": " " - }, - "hideInAnswer": true - }, - { - "if": "socket:tesla_destination=1", - "ifnot": "socket:tesla_destination=", - "then": { - "en": " Tesla supercharger (destination (A Type 2 with cable branded as tesla)", - "nl": " " - }, - "hideInAnswer": { - "or": [ - "_country=us" - ] - } - }, - { - "if": { - "and": [ - "socket:tesla_destination~*", - "socket:tesla_destination!=1" - ] - }, - "then": { - "en": " Tesla supercharger (destination (A Type 2 with cable branded as tesla)", - "nl": " " - }, - "hideInAnswer": true - } - ] - }, - { - "id": "plugs-0", - "question": { - "en": "How much plugs of type Schuko wall plug without ground pin (CEE7/4 type F) are available here?", - "nl": "Hoeveel stekkers van type Schuko stekker zonder aardingspin (CEE7/4 type F) heeft dit oplaadpunt?" - }, - "render": { - "en": "There are Schuko wall plug without ground pin (CEE7/4 type F) plugs of type Schuko wall plug without ground pin (CEE7/4 type F) available here", - "nl": "Hier zijn Schuko stekker zonder aardingspin (CEE7/4 type F) stekkers van het type Schuko stekker zonder aardingspin (CEE7/4 type F)" - }, - "freeform": { - "key": "socket:schuko", - "type": "pnat" - }, - "condition": { - "and": [ - "socket:schuko~*", - "socket:schuko!=0" - ] - } - }, - { - "id": "voltage-0", - "question": { - "en": "What voltage do the plugs with Schuko wall plug without ground pin (CEE7/4 type F) offer?", - "nl": "Welke spanning levert de stekker van type Schuko stekker zonder aardingspin (CEE7/4 type F) " - }, - "render": { - "en": "Schuko wall plug without ground pin (CEE7/4 type F) outputs {socket:schuko:voltage} volt", - "nl": "Schuko stekker zonder aardingspin (CEE7/4 type F) heeft een spanning van {socket:schuko:voltage} volt" - }, - "freeform": { - "key": "socket:schuko:voltage", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:schuko:voltage=230 V", - "then": { - "en": "Schuko wall plug without ground pin (CEE7/4 type F) outputs 230 volt", - "nl": "Schuko stekker zonder aardingspin (CEE7/4 type F) heeft een spanning van 230 volt" - } - } - ], - "condition": { - "and": [ - "socket:schuko~*", - "socket:schuko!=0" - ] - } - }, - { - "id": "current-0", - "question": { - "en": "What current do the plugs with Schuko wall plug without ground pin (CEE7/4 type F) offer?", - "nl": "Welke stroom levert de stekker van type Schuko stekker zonder aardingspin (CEE7/4 type F) ?" - }, - "render": { - "en": "Schuko wall plug without ground pin (CEE7/4 type F) outputs at most {socket:schuko:current}A", - "nl": "Schuko stekker zonder aardingspin (CEE7/4 type F) levert een stroom van maximaal {socket:schuko:current}A" - }, - "freeform": { - "key": "socket:schuko:current", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:schuko:current=16 A", - "then": { - "en": "Schuko wall plug without ground pin (CEE7/4 type F) outputs at most 16 A", - "nl": "Schuko stekker zonder aardingspin (CEE7/4 type F) levert een stroom van maximaal 16 A" - } - } - ], - "condition": { - "and": [ - "socket:schuko~*", - "socket:schuko!=0" - ] - } - }, - { - "id": "power-output-0", - "question": { - "en": "What power output does a single plug of type Schuko wall plug without ground pin (CEE7/4 type F) offer?", - "nl": "Welk vermogen levert een enkele stekker van type Schuko stekker zonder aardingspin (CEE7/4 type F) ?" - }, - "render": { - "en": "Schuko wall plug without ground pin (CEE7/4 type F) outputs at most {socket:schuko:output}", - "nl": "Schuko stekker zonder aardingspin (CEE7/4 type F) levert een vermogen van maximaal {socket:schuko:output}" - }, - "freeform": { - "key": "socket:schuko:output", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:schuko:output=3.6 kw", - "then": { - "en": "Schuko wall plug without ground pin (CEE7/4 type F) outputs at most 3.6 kw", - "nl": "Schuko stekker zonder aardingspin (CEE7/4 type F) levert een vermogen van maximaal 3.6 kw" - } - } - ], - "condition": { - "and": [ - "socket:schuko~*", - "socket:schuko!=0" - ] - } - }, - { - "id": "plugs-1", - "question": { - "en": "How much plugs of type European wall plug with ground pin (CEE7/4 type E) are available here?", - "nl": "Hoeveel stekkers van type Europese stekker met aardingspin (CEE7/4 type E) heeft dit oplaadpunt?" - }, - "render": { - "en": "There are European wall plug with ground pin (CEE7/4 type E) plugs of type European wall plug with ground pin (CEE7/4 type E) available here", - "nl": "Hier zijn Europese stekker met aardingspin (CEE7/4 type E) stekkers van het type Europese stekker met aardingspin (CEE7/4 type E)" - }, - "freeform": { - "key": "socket:typee", - "type": "pnat" - }, - "condition": { - "and": [ - "socket:typee~*", - "socket:typee!=0" - ] - } - }, - { - "id": "voltage-1", - "question": { - "en": "What voltage do the plugs with European wall plug with ground pin (CEE7/4 type E) offer?", - "nl": "Welke spanning levert de stekker van type Europese stekker met aardingspin (CEE7/4 type E) " - }, - "render": { - "en": "European wall plug with ground pin (CEE7/4 type E) outputs {socket:typee:voltage} volt", - "nl": "Europese stekker met aardingspin (CEE7/4 type E) heeft een spanning van {socket:typee:voltage} volt" - }, - "freeform": { - "key": "socket:typee:voltage", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:typee:voltage=230 V", - "then": { - "en": "European wall plug with ground pin (CEE7/4 type E) outputs 230 volt", - "nl": "Europese stekker met aardingspin (CEE7/4 type E) heeft een spanning van 230 volt" - } - } - ], - "condition": { - "and": [ - "socket:typee~*", - "socket:typee!=0" - ] - } - }, - { - "id": "current-1", - "question": { - "en": "What current do the plugs with European wall plug with ground pin (CEE7/4 type E) offer?", - "nl": "Welke stroom levert de stekker van type Europese stekker met aardingspin (CEE7/4 type E) ?" - }, - "render": { - "en": "European wall plug with ground pin (CEE7/4 type E) outputs at most {socket:typee:current}A", - "nl": "Europese stekker met aardingspin (CEE7/4 type E) levert een stroom van maximaal {socket:typee:current}A" - }, - "freeform": { - "key": "socket:typee:current", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:typee:current=16 A", - "then": { - "en": "European wall plug with ground pin (CEE7/4 type E) outputs at most 16 A", - "nl": "Europese stekker met aardingspin (CEE7/4 type E) levert een stroom van maximaal 16 A" - } - } - ], - "condition": { - "and": [ - "socket:typee~*", - "socket:typee!=0" - ] - } - }, - { - "id": "power-output-1", - "question": { - "en": "What power output does a single plug of type European wall plug with ground pin (CEE7/4 type E) offer?", - "nl": "Welk vermogen levert een enkele stekker van type Europese stekker met aardingspin (CEE7/4 type E) ?" - }, - "render": { - "en": "European wall plug with ground pin (CEE7/4 type E) outputs at most {socket:typee:output}", - "nl": "Europese stekker met aardingspin (CEE7/4 type E) levert een vermogen van maximaal {socket:typee:output}" - }, - "freeform": { - "key": "socket:typee:output", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:typee:output=3 kw", - "then": { - "en": "European wall plug with ground pin (CEE7/4 type E) outputs at most 3 kw", - "nl": "Europese stekker met aardingspin (CEE7/4 type E) levert een vermogen van maximaal 3 kw" - } - }, - { - "if": "socket:socket:typee:output=22 kw", - "then": { - "en": "European wall plug with ground pin (CEE7/4 type E) outputs at most 22 kw", - "nl": "Europese stekker met aardingspin (CEE7/4 type E) levert een vermogen van maximaal 22 kw" - } - } - ], - "condition": { - "and": [ - "socket:typee~*", - "socket:typee!=0" - ] - } - }, - { - "id": "plugs-2", - "question": { - "en": "How much plugs of type Chademo are available here?", - "nl": "Hoeveel stekkers van type heeft dit oplaadpunt?" - }, - "render": { - "en": "There are Chademo plugs of type Chademo available here", - "nl": "Hier zijn stekkers van het type " - }, - "freeform": { - "key": "socket:chademo", - "type": "pnat" - }, - "condition": { - "and": [ - "socket:chademo~*", - "socket:chademo!=0" - ] - } - }, - { - "id": "voltage-2", - "question": { - "en": "What voltage do the plugs with Chademo offer?", - "nl": "Welke spanning levert de stekker van type " - }, - "render": { - "en": "Chademo outputs {socket:chademo:voltage} volt", - "nl": " heeft een spanning van {socket:chademo:voltage} volt" - }, - "freeform": { - "key": "socket:chademo:voltage", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:chademo:voltage=500 V", - "then": { - "en": "Chademo outputs 500 volt", - "nl": " heeft een spanning van 500 volt" - } - } - ], - "condition": { - "and": [ - "socket:chademo~*", - "socket:chademo!=0" - ] - } - }, - { - "id": "current-2", - "question": { - "en": "What current do the plugs with Chademo offer?", - "nl": "Welke stroom levert de stekker van type ?" - }, - "render": { - "en": "Chademo outputs at most {socket:chademo:current}A", - "nl": " levert een stroom van maximaal {socket:chademo:current}A" - }, - "freeform": { - "key": "socket:chademo:current", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:chademo:current=120 A", - "then": { - "en": "Chademo outputs at most 120 A", - "nl": " levert een stroom van maximaal 120 A" - } - } - ], - "condition": { - "and": [ - "socket:chademo~*", - "socket:chademo!=0" - ] - } - }, - { - "id": "power-output-2", - "question": { - "en": "What power output does a single plug of type Chademo offer?", - "nl": "Welk vermogen levert een enkele stekker van type ?" - }, - "render": { - "en": "Chademo outputs at most {socket:chademo:output}", - "nl": " levert een vermogen van maximaal {socket:chademo:output}" - }, - "freeform": { - "key": "socket:chademo:output", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:chademo:output=50 kw", - "then": { - "en": "Chademo outputs at most 50 kw", - "nl": " levert een vermogen van maximaal 50 kw" - } - } - ], - "condition": { - "and": [ - "socket:chademo~*", - "socket:chademo!=0" - ] - } - }, - { - "id": "plugs-3", - "question": { - "en": "How much plugs of type Type 1 with cable (J1772) are available here?", - "nl": "Hoeveel stekkers van type Type 1 met kabel (J1772) heeft dit oplaadpunt?" - }, - "render": { - "en": "There are Type 1 with cable (J1772) plugs of type Type 1 with cable (J1772) available here", - "nl": "Hier zijn Type 1 met kabel (J1772) stekkers van het type Type 1 met kabel (J1772)" - }, - "freeform": { - "key": "socket:type1_cable", - "type": "pnat" - }, - "condition": { - "and": [ - "socket:type1_cable~*", - "socket:type1_cable!=0" - ] - } - }, - { - "id": "voltage-3", - "question": { - "en": "What voltage do the plugs with Type 1 with cable (J1772) offer?", - "nl": "Welke spanning levert de stekker van type Type 1 met kabel (J1772) " - }, - "render": { - "en": "Type 1 with cable (J1772) outputs {socket:type1_cable:voltage} volt", - "nl": "Type 1 met kabel (J1772) heeft een spanning van {socket:type1_cable:voltage} volt" - }, - "freeform": { - "key": "socket:type1_cable:voltage", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:type1_cable:voltage=200 V", - "then": { - "en": "Type 1 with cable (J1772) outputs 200 volt", - "nl": "Type 1 met kabel (J1772) heeft een spanning van 200 volt" - } - }, - { - "if": "socket:socket:type1_cable:voltage=240 V", - "then": { - "en": "Type 1 with cable (J1772) outputs 240 volt", - "nl": "Type 1 met kabel (J1772) heeft een spanning van 240 volt" - } - } - ], - "condition": { - "and": [ - "socket:type1_cable~*", - "socket:type1_cable!=0" - ] - } - }, - { - "id": "current-3", - "question": { - "en": "What current do the plugs with Type 1 with cable (J1772) offer?", - "nl": "Welke stroom levert de stekker van type Type 1 met kabel (J1772) ?" - }, - "render": { - "en": "Type 1 with cable (J1772) outputs at most {socket:type1_cable:current}A", - "nl": "Type 1 met kabel (J1772) levert een stroom van maximaal {socket:type1_cable:current}A" - }, - "freeform": { - "key": "socket:type1_cable:current", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:type1_cable:current=32 A", - "then": { - "en": "Type 1 with cable (J1772) outputs at most 32 A", - "nl": "Type 1 met kabel (J1772) levert een stroom van maximaal 32 A" - } - } - ], - "condition": { - "and": [ - "socket:type1_cable~*", - "socket:type1_cable!=0" - ] - } - }, - { - "id": "power-output-3", - "question": { - "en": "What power output does a single plug of type Type 1 with cable (J1772) offer?", - "nl": "Welk vermogen levert een enkele stekker van type Type 1 met kabel (J1772) ?" - }, - "render": { - "en": "Type 1 with cable (J1772) outputs at most {socket:type1_cable:output}", - "nl": "Type 1 met kabel (J1772) levert een vermogen van maximaal {socket:type1_cable:output}" - }, - "freeform": { - "key": "socket:type1_cable:output", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:type1_cable:output=3.7 kw", - "then": { - "en": "Type 1 with cable (J1772) outputs at most 3.7 kw", - "nl": "Type 1 met kabel (J1772) levert een vermogen van maximaal 3.7 kw" - } - }, - { - "if": "socket:socket:type1_cable:output=7 kw", - "then": { - "en": "Type 1 with cable (J1772) outputs at most 7 kw", - "nl": "Type 1 met kabel (J1772) levert een vermogen van maximaal 7 kw" - } - } - ], - "condition": { - "and": [ - "socket:type1_cable~*", - "socket:type1_cable!=0" - ] - } - }, - { - "id": "plugs-4", - "question": { - "en": "How much plugs of type Type 1 without cable (J1772) are available here?", - "nl": "Hoeveel stekkers van type Type 1 zonder kabel (J1772) heeft dit oplaadpunt?" - }, - "render": { - "en": "There are Type 1 without cable (J1772) plugs of type Type 1 without cable (J1772) available here", - "nl": "Hier zijn Type 1 zonder kabel (J1772) stekkers van het type Type 1 zonder kabel (J1772)" - }, - "freeform": { - "key": "socket:type1", - "type": "pnat" - }, - "condition": { - "and": [ - "socket:type1~*", - "socket:type1!=0" - ] - } - }, - { - "id": "voltage-4", - "question": { - "en": "What voltage do the plugs with Type 1 without cable (J1772) offer?", - "nl": "Welke spanning levert de stekker van type Type 1 zonder kabel (J1772) " - }, - "render": { - "en": "Type 1 without cable (J1772) outputs {socket:type1:voltage} volt", - "nl": "Type 1 zonder kabel (J1772) heeft een spanning van {socket:type1:voltage} volt" - }, - "freeform": { - "key": "socket:type1:voltage", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:type1:voltage=200 V", - "then": { - "en": "Type 1 without cable (J1772) outputs 200 volt", - "nl": "Type 1 zonder kabel (J1772) heeft een spanning van 200 volt" - } - }, - { - "if": "socket:socket:type1:voltage=240 V", - "then": { - "en": "Type 1 without cable (J1772) outputs 240 volt", - "nl": "Type 1 zonder kabel (J1772) heeft een spanning van 240 volt" - } - } - ], - "condition": { - "and": [ - "socket:type1~*", - "socket:type1!=0" - ] - } - }, - { - "id": "current-4", - "question": { - "en": "What current do the plugs with Type 1 without cable (J1772) offer?", - "nl": "Welke stroom levert de stekker van type Type 1 zonder kabel (J1772) ?" - }, - "render": { - "en": "Type 1 without cable (J1772) outputs at most {socket:type1:current}A", - "nl": "Type 1 zonder kabel (J1772) levert een stroom van maximaal {socket:type1:current}A" - }, - "freeform": { - "key": "socket:type1:current", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:type1:current=32 A", - "then": { - "en": "Type 1 without cable (J1772) outputs at most 32 A", - "nl": "Type 1 zonder kabel (J1772) levert een stroom van maximaal 32 A" - } - } - ], - "condition": { - "and": [ - "socket:type1~*", - "socket:type1!=0" - ] - } - }, - { - "id": "power-output-4", - "question": { - "en": "What power output does a single plug of type Type 1 without cable (J1772) offer?", - "nl": "Welk vermogen levert een enkele stekker van type Type 1 zonder kabel (J1772) ?" - }, - "render": { - "en": "Type 1 without cable (J1772) outputs at most {socket:type1:output}", - "nl": "Type 1 zonder kabel (J1772) levert een vermogen van maximaal {socket:type1:output}" - }, - "freeform": { - "key": "socket:type1:output", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:type1:output=3.7 kw", - "then": { - "en": "Type 1 without cable (J1772) outputs at most 3.7 kw", - "nl": "Type 1 zonder kabel (J1772) levert een vermogen van maximaal 3.7 kw" - } - }, - { - "if": "socket:socket:type1:output=6.6 kw", - "then": { - "en": "Type 1 without cable (J1772) outputs at most 6.6 kw", - "nl": "Type 1 zonder kabel (J1772) levert een vermogen van maximaal 6.6 kw" - } - }, - { - "if": "socket:socket:type1:output=7 kw", - "then": { - "en": "Type 1 without cable (J1772) outputs at most 7 kw", - "nl": "Type 1 zonder kabel (J1772) levert een vermogen van maximaal 7 kw" - } - }, - { - "if": "socket:socket:type1:output=7.2 kw", - "then": { - "en": "Type 1 without cable (J1772) outputs at most 7.2 kw", - "nl": "Type 1 zonder kabel (J1772) levert een vermogen van maximaal 7.2 kw" - } - } - ], - "condition": { - "and": [ - "socket:type1~*", - "socket:type1!=0" - ] - } - }, - { - "id": "plugs-5", - "question": { - "en": "How much plugs of type Type 1 CCS (aka Type 1 Combo) are available here?", - "nl": "Hoeveel stekkers van type heeft dit oplaadpunt?" - }, - "render": { - "en": "There are Type 1 CCS (aka Type 1 Combo) plugs of type Type 1 CCS (aka Type 1 Combo) available here", - "nl": "Hier zijn stekkers van het type " - }, - "freeform": { - "key": "socket:type1_combo", - "type": "pnat" - }, - "condition": { - "and": [ - "socket:type1_combo~*", - "socket:type1_combo!=0" - ] - } - }, - { - "id": "voltage-5", - "question": { - "en": "What voltage do the plugs with Type 1 CCS (aka Type 1 Combo) offer?", - "nl": "Welke spanning levert de stekker van type " - }, - "render": { - "en": "Type 1 CCS (aka Type 1 Combo) outputs {socket:type1_combo:voltage} volt", - "nl": " heeft een spanning van {socket:type1_combo:voltage} volt" - }, - "freeform": { - "key": "socket:type1_combo:voltage", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:type1_combo:voltage=400 V", - "then": { - "en": "Type 1 CCS (aka Type 1 Combo) outputs 400 volt", - "nl": " heeft een spanning van 400 volt" - } - }, - { - "if": "socket:socket:type1_combo:voltage=1000 V", - "then": { - "en": "Type 1 CCS (aka Type 1 Combo) outputs 1000 volt", - "nl": " heeft een spanning van 1000 volt" - } - } - ], - "condition": { - "and": [ - "socket:type1_combo~*", - "socket:type1_combo!=0" - ] - } - }, - { - "id": "current-5", - "question": { - "en": "What current do the plugs with Type 1 CCS (aka Type 1 Combo) offer?", - "nl": "Welke stroom levert de stekker van type ?" - }, - "render": { - "en": "Type 1 CCS (aka Type 1 Combo) outputs at most {socket:type1_combo:current}A", - "nl": " levert een stroom van maximaal {socket:type1_combo:current}A" - }, - "freeform": { - "key": "socket:type1_combo:current", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:type1_combo:current=50 A", - "then": { - "en": "Type 1 CCS (aka Type 1 Combo) outputs at most 50 A", - "nl": " levert een stroom van maximaal 50 A" - } - }, - { - "if": "socket:socket:type1_combo:current=125 A", - "then": { - "en": "Type 1 CCS (aka Type 1 Combo) outputs at most 125 A", - "nl": " levert een stroom van maximaal 125 A" - } - } - ], - "condition": { - "and": [ - "socket:type1_combo~*", - "socket:type1_combo!=0" - ] - } - }, - { - "id": "power-output-5", - "question": { - "en": "What power output does a single plug of type Type 1 CCS (aka Type 1 Combo) offer?", - "nl": "Welk vermogen levert een enkele stekker van type ?" - }, - "render": { - "en": "Type 1 CCS (aka Type 1 Combo) outputs at most {socket:type1_combo:output}", - "nl": " levert een vermogen van maximaal {socket:type1_combo:output}" - }, - "freeform": { - "key": "socket:type1_combo:output", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:type1_combo:output=50 kw", - "then": { - "en": "Type 1 CCS (aka Type 1 Combo) outputs at most 50 kw", - "nl": " levert een vermogen van maximaal 50 kw" - } - }, - { - "if": "socket:socket:type1_combo:output=62.5 kw", - "then": { - "en": "Type 1 CCS (aka Type 1 Combo) outputs at most 62.5 kw", - "nl": " levert een vermogen van maximaal 62.5 kw" - } - }, - { - "if": "socket:socket:type1_combo:output=150 kw", - "then": { - "en": "Type 1 CCS (aka Type 1 Combo) outputs at most 150 kw", - "nl": " levert een vermogen van maximaal 150 kw" - } - }, - { - "if": "socket:socket:type1_combo:output=350 kw", - "then": { - "en": "Type 1 CCS (aka Type 1 Combo) outputs at most 350 kw", - "nl": " levert een vermogen van maximaal 350 kw" - } - } - ], - "condition": { - "and": [ - "socket:type1_combo~*", - "socket:type1_combo!=0" - ] - } - }, - { - "id": "plugs-6", - "question": { - "en": "How much plugs of type Tesla Supercharger are available here?", - "nl": "Hoeveel stekkers van type heeft dit oplaadpunt?" - }, - "render": { - "en": "There are Tesla Supercharger plugs of type Tesla Supercharger available here", - "nl": "Hier zijn stekkers van het type " - }, - "freeform": { - "key": "socket:tesla_supercharger", - "type": "pnat" - }, - "condition": { - "and": [ - "socket:tesla_supercharger~*", - "socket:tesla_supercharger!=0" - ] - } - }, - { - "id": "voltage-6", - "question": { - "en": "What voltage do the plugs with Tesla Supercharger offer?", - "nl": "Welke spanning levert de stekker van type " - }, - "render": { - "en": "Tesla Supercharger outputs {socket:tesla_supercharger:voltage} volt", - "nl": " heeft een spanning van {socket:tesla_supercharger:voltage} volt" - }, - "freeform": { - "key": "socket:tesla_supercharger:voltage", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:tesla_supercharger:voltage=480 V", - "then": { - "en": "Tesla Supercharger outputs 480 volt", - "nl": " heeft een spanning van 480 volt" - } - } - ], - "condition": { - "and": [ - "socket:tesla_supercharger~*", - "socket:tesla_supercharger!=0" - ] - } - }, - { - "id": "current-6", - "question": { - "en": "What current do the plugs with Tesla Supercharger offer?", - "nl": "Welke stroom levert de stekker van type ?" - }, - "render": { - "en": "Tesla Supercharger outputs at most {socket:tesla_supercharger:current}A", - "nl": " levert een stroom van maximaal {socket:tesla_supercharger:current}A" - }, - "freeform": { - "key": "socket:tesla_supercharger:current", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:tesla_supercharger:current=125 A", - "then": { - "en": "Tesla Supercharger outputs at most 125 A", - "nl": " levert een stroom van maximaal 125 A" - } - }, - { - "if": "socket:socket:tesla_supercharger:current=350 A", - "then": { - "en": "Tesla Supercharger outputs at most 350 A", - "nl": " levert een stroom van maximaal 350 A" - } - } - ], - "condition": { - "and": [ - "socket:tesla_supercharger~*", - "socket:tesla_supercharger!=0" - ] - } - }, - { - "id": "power-output-6", - "question": { - "en": "What power output does a single plug of type Tesla Supercharger offer?", - "nl": "Welk vermogen levert een enkele stekker van type ?" - }, - "render": { - "en": "Tesla Supercharger outputs at most {socket:tesla_supercharger:output}", - "nl": " levert een vermogen van maximaal {socket:tesla_supercharger:output}" - }, - "freeform": { - "key": "socket:tesla_supercharger:output", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:tesla_supercharger:output=120 kw", - "then": { - "en": "Tesla Supercharger outputs at most 120 kw", - "nl": " levert een vermogen van maximaal 120 kw" - } - }, - { - "if": "socket:socket:tesla_supercharger:output=150 kw", - "then": { - "en": "Tesla Supercharger outputs at most 150 kw", - "nl": " levert een vermogen van maximaal 150 kw" - } - }, - { - "if": "socket:socket:tesla_supercharger:output=250 kw", - "then": { - "en": "Tesla Supercharger outputs at most 250 kw", - "nl": " levert een vermogen van maximaal 250 kw" - } - } - ], - "condition": { - "and": [ - "socket:tesla_supercharger~*", - "socket:tesla_supercharger!=0" - ] - } - }, - { - "id": "plugs-7", - "question": { - "en": "How much plugs of type Type 2 (mennekes) are available here?", - "nl": "Hoeveel stekkers van type heeft dit oplaadpunt?" - }, - "render": { - "en": "There are Type 2 (mennekes) plugs of type Type 2 (mennekes) available here", - "nl": "Hier zijn stekkers van het type " - }, - "freeform": { - "key": "socket:type2", - "type": "pnat" - }, - "condition": { - "and": [ - "socket:type2~*", - "socket:type2!=0" - ] - } - }, - { - "id": "voltage-7", - "question": { - "en": "What voltage do the plugs with Type 2 (mennekes) offer?", - "nl": "Welke spanning levert de stekker van type " - }, - "render": { - "en": "Type 2 (mennekes) outputs {socket:type2:voltage} volt", - "nl": " heeft een spanning van {socket:type2:voltage} volt" - }, - "freeform": { - "key": "socket:type2:voltage", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:type2:voltage=230 V", - "then": { - "en": "Type 2 (mennekes) outputs 230 volt", - "nl": " heeft een spanning van 230 volt" - } - }, - { - "if": "socket:socket:type2:voltage=400 V", - "then": { - "en": "Type 2 (mennekes) outputs 400 volt", - "nl": " heeft een spanning van 400 volt" - } - } - ], - "condition": { - "and": [ - "socket:type2~*", - "socket:type2!=0" - ] - } - }, - { - "id": "current-7", - "question": { - "en": "What current do the plugs with Type 2 (mennekes) offer?", - "nl": "Welke stroom levert de stekker van type ?" - }, - "render": { - "en": "Type 2 (mennekes) outputs at most {socket:type2:current}A", - "nl": " levert een stroom van maximaal {socket:type2:current}A" - }, - "freeform": { - "key": "socket:type2:current", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:type2:current=16 A", - "then": { - "en": "Type 2 (mennekes) outputs at most 16 A", - "nl": " levert een stroom van maximaal 16 A" - } - }, - { - "if": "socket:socket:type2:current=32 A", - "then": { - "en": "Type 2 (mennekes) outputs at most 32 A", - "nl": " levert een stroom van maximaal 32 A" - } - } - ], - "condition": { - "and": [ - "socket:type2~*", - "socket:type2!=0" - ] - } - }, - { - "id": "power-output-7", - "question": { - "en": "What power output does a single plug of type Type 2 (mennekes) offer?", - "nl": "Welk vermogen levert een enkele stekker van type ?" - }, - "render": { - "en": "Type 2 (mennekes) outputs at most {socket:type2:output}", - "nl": " levert een vermogen van maximaal {socket:type2:output}" - }, - "freeform": { - "key": "socket:type2:output", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:type2:output=11 kw", - "then": { - "en": "Type 2 (mennekes) outputs at most 11 kw", - "nl": " levert een vermogen van maximaal 11 kw" - } - }, - { - "if": "socket:socket:type2:output=22 kw", - "then": { - "en": "Type 2 (mennekes) outputs at most 22 kw", - "nl": " levert een vermogen van maximaal 22 kw" - } - } - ], - "condition": { - "and": [ - "socket:type2~*", - "socket:type2!=0" - ] - } - }, - { - "id": "plugs-8", - "question": { - "en": "How much plugs of type Type 2 CCS (mennekes) are available here?", - "nl": "Hoeveel stekkers van type heeft dit oplaadpunt?" - }, - "render": { - "en": "There are Type 2 CCS (mennekes) plugs of type Type 2 CCS (mennekes) available here", - "nl": "Hier zijn stekkers van het type " - }, - "freeform": { - "key": "socket:type2_combo", - "type": "pnat" - }, - "condition": { - "and": [ - "socket:type2_combo~*", - "socket:type2_combo!=0" - ] - } - }, - { - "id": "voltage-8", - "question": { - "en": "What voltage do the plugs with Type 2 CCS (mennekes) offer?", - "nl": "Welke spanning levert de stekker van type " - }, - "render": { - "en": "Type 2 CCS (mennekes) outputs {socket:type2_combo:voltage} volt", - "nl": " heeft een spanning van {socket:type2_combo:voltage} volt" - }, - "freeform": { - "key": "socket:type2_combo:voltage", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:type2_combo:voltage=500 V", - "then": { - "en": "Type 2 CCS (mennekes) outputs 500 volt", - "nl": " heeft een spanning van 500 volt" - } - }, - { - "if": "socket:socket:type2_combo:voltage=920 V", - "then": { - "en": "Type 2 CCS (mennekes) outputs 920 volt", - "nl": " heeft een spanning van 920 volt" - } - } - ], - "condition": { - "and": [ - "socket:type2_combo~*", - "socket:type2_combo!=0" - ] - } - }, - { - "id": "current-8", - "question": { - "en": "What current do the plugs with Type 2 CCS (mennekes) offer?", - "nl": "Welke stroom levert de stekker van type ?" - }, - "render": { - "en": "Type 2 CCS (mennekes) outputs at most {socket:type2_combo:current}A", - "nl": " levert een stroom van maximaal {socket:type2_combo:current}A" - }, - "freeform": { - "key": "socket:type2_combo:current", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:type2_combo:current=125 A", - "then": { - "en": "Type 2 CCS (mennekes) outputs at most 125 A", - "nl": " levert een stroom van maximaal 125 A" - } - }, - { - "if": "socket:socket:type2_combo:current=350 A", - "then": { - "en": "Type 2 CCS (mennekes) outputs at most 350 A", - "nl": " levert een stroom van maximaal 350 A" - } - } - ], - "condition": { - "and": [ - "socket:type2_combo~*", - "socket:type2_combo!=0" - ] - } - }, - { - "id": "power-output-8", - "question": { - "en": "What power output does a single plug of type Type 2 CCS (mennekes) offer?", - "nl": "Welk vermogen levert een enkele stekker van type ?" - }, - "render": { - "en": "Type 2 CCS (mennekes) outputs at most {socket:type2_combo:output}", - "nl": " levert een vermogen van maximaal {socket:type2_combo:output}" - }, - "freeform": { - "key": "socket:type2_combo:output", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:type2_combo:output=50 kw", - "then": { - "en": "Type 2 CCS (mennekes) outputs at most 50 kw", - "nl": " levert een vermogen van maximaal 50 kw" - } - } - ], - "condition": { - "and": [ - "socket:type2_combo~*", - "socket:type2_combo!=0" - ] - } - }, - { - "id": "plugs-9", - "question": { - "en": "How much plugs of type Type 2 with cable (mennekes) are available here?", - "nl": "Hoeveel stekkers van type Type 2 met kabel (J1772) heeft dit oplaadpunt?" - }, - "render": { - "en": "There are Type 2 with cable (mennekes) plugs of type Type 2 with cable (mennekes) available here", - "nl": "Hier zijn Type 2 met kabel (J1772) stekkers van het type Type 2 met kabel (J1772)" - }, - "freeform": { - "key": "socket:type2_cable", - "type": "pnat" - }, - "condition": { - "and": [ - "socket:type2_cable~*", - "socket:type2_cable!=0" - ] - } - }, - { - "id": "voltage-9", - "question": { - "en": "What voltage do the plugs with Type 2 with cable (mennekes) offer?", - "nl": "Welke spanning levert de stekker van type Type 2 met kabel (J1772) " - }, - "render": { - "en": "Type 2 with cable (mennekes) outputs {socket:type2_cable:voltage} volt", - "nl": "Type 2 met kabel (J1772) heeft een spanning van {socket:type2_cable:voltage} volt" - }, - "freeform": { - "key": "socket:type2_cable:voltage", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:type2_cable:voltage=230 V", - "then": { - "en": "Type 2 with cable (mennekes) outputs 230 volt", - "nl": "Type 2 met kabel (J1772) heeft een spanning van 230 volt" - } - }, - { - "if": "socket:socket:type2_cable:voltage=400 V", - "then": { - "en": "Type 2 with cable (mennekes) outputs 400 volt", - "nl": "Type 2 met kabel (J1772) heeft een spanning van 400 volt" - } - } - ], - "condition": { - "and": [ - "socket:type2_cable~*", - "socket:type2_cable!=0" - ] - } - }, - { - "id": "current-9", - "question": { - "en": "What current do the plugs with Type 2 with cable (mennekes) offer?", - "nl": "Welke stroom levert de stekker van type Type 2 met kabel (J1772) ?" - }, - "render": { - "en": "Type 2 with cable (mennekes) outputs at most {socket:type2_cable:current}A", - "nl": "Type 2 met kabel (J1772) levert een stroom van maximaal {socket:type2_cable:current}A" - }, - "freeform": { - "key": "socket:type2_cable:current", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:type2_cable:current=16 A", - "then": { - "en": "Type 2 with cable (mennekes) outputs at most 16 A", - "nl": "Type 2 met kabel (J1772) levert een stroom van maximaal 16 A" - } - }, - { - "if": "socket:socket:type2_cable:current=32 A", - "then": { - "en": "Type 2 with cable (mennekes) outputs at most 32 A", - "nl": "Type 2 met kabel (J1772) levert een stroom van maximaal 32 A" - } - } - ], - "condition": { - "and": [ - "socket:type2_cable~*", - "socket:type2_cable!=0" - ] - } - }, - { - "id": "power-output-9", - "question": { - "en": "What power output does a single plug of type Type 2 with cable (mennekes) offer?", - "nl": "Welk vermogen levert een enkele stekker van type Type 2 met kabel (J1772) ?" - }, - "render": { - "en": "Type 2 with cable (mennekes) outputs at most {socket:type2_cable:output}", - "nl": "Type 2 met kabel (J1772) levert een vermogen van maximaal {socket:type2_cable:output}" - }, - "freeform": { - "key": "socket:type2_cable:output", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:type2_cable:output=11 kw", - "then": { - "en": "Type 2 with cable (mennekes) outputs at most 11 kw", - "nl": "Type 2 met kabel (J1772) levert een vermogen van maximaal 11 kw" - } - }, - { - "if": "socket:socket:type2_cable:output=22 kw", - "then": { - "en": "Type 2 with cable (mennekes) outputs at most 22 kw", - "nl": "Type 2 met kabel (J1772) levert een vermogen van maximaal 22 kw" - } - } - ], - "condition": { - "and": [ - "socket:type2_cable~*", - "socket:type2_cable!=0" - ] - } - }, - { - "id": "plugs-10", - "question": { - "en": "How much plugs of type Tesla Supercharger CCS (a branded type2_css) are available here?", - "nl": "Hoeveel stekkers van type heeft dit oplaadpunt?" - }, - "render": { - "en": "There are Tesla Supercharger CCS (a branded type2_css) plugs of type Tesla Supercharger CCS (a branded type2_css) available here", - "nl": "Hier zijn stekkers van het type " - }, - "freeform": { - "key": "socket:tesla_supercharger_ccs", - "type": "pnat" - }, - "condition": { - "and": [ - "socket:tesla_supercharger_ccs~*", - "socket:tesla_supercharger_ccs!=0" - ] - } - }, - { - "id": "voltage-10", - "question": { - "en": "What voltage do the plugs with Tesla Supercharger CCS (a branded type2_css) offer?", - "nl": "Welke spanning levert de stekker van type " - }, - "render": { - "en": "Tesla Supercharger CCS (a branded type2_css) outputs {socket:tesla_supercharger_ccs:voltage} volt", - "nl": " heeft een spanning van {socket:tesla_supercharger_ccs:voltage} volt" - }, - "freeform": { - "key": "socket:tesla_supercharger_ccs:voltage", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:tesla_supercharger_ccs:voltage=500 V", - "then": { - "en": "Tesla Supercharger CCS (a branded type2_css) outputs 500 volt", - "nl": " heeft een spanning van 500 volt" - } - }, - { - "if": "socket:socket:tesla_supercharger_ccs:voltage=920 V", - "then": { - "en": "Tesla Supercharger CCS (a branded type2_css) outputs 920 volt", - "nl": " heeft een spanning van 920 volt" - } - } - ], - "condition": { - "and": [ - "socket:tesla_supercharger_ccs~*", - "socket:tesla_supercharger_ccs!=0" - ] - } - }, - { - "id": "current-10", - "question": { - "en": "What current do the plugs with Tesla Supercharger CCS (a branded type2_css) offer?", - "nl": "Welke stroom levert de stekker van type ?" - }, - "render": { - "en": "Tesla Supercharger CCS (a branded type2_css) outputs at most {socket:tesla_supercharger_ccs:current}A", - "nl": " levert een stroom van maximaal {socket:tesla_supercharger_ccs:current}A" - }, - "freeform": { - "key": "socket:tesla_supercharger_ccs:current", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:tesla_supercharger_ccs:current=125 A", - "then": { - "en": "Tesla Supercharger CCS (a branded type2_css) outputs at most 125 A", - "nl": " levert een stroom van maximaal 125 A" - } - }, - { - "if": "socket:socket:tesla_supercharger_ccs:current=350 A", - "then": { - "en": "Tesla Supercharger CCS (a branded type2_css) outputs at most 350 A", - "nl": " levert een stroom van maximaal 350 A" - } - } - ], - "condition": { - "and": [ - "socket:tesla_supercharger_ccs~*", - "socket:tesla_supercharger_ccs!=0" - ] - } - }, - { - "id": "power-output-10", - "question": { - "en": "What power output does a single plug of type Tesla Supercharger CCS (a branded type2_css) offer?", - "nl": "Welk vermogen levert een enkele stekker van type ?" - }, - "render": { - "en": "Tesla Supercharger CCS (a branded type2_css) outputs at most {socket:tesla_supercharger_ccs:output}", - "nl": " levert een vermogen van maximaal {socket:tesla_supercharger_ccs:output}" - }, - "freeform": { - "key": "socket:tesla_supercharger_ccs:output", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:tesla_supercharger_ccs:output=50 kw", - "then": { - "en": "Tesla Supercharger CCS (a branded type2_css) outputs at most 50 kw", - "nl": " levert een vermogen van maximaal 50 kw" - } - } - ], - "condition": { - "and": [ - "socket:tesla_supercharger_ccs~*", - "socket:tesla_supercharger_ccs!=0" - ] - } - }, - { - "id": "plugs-11", - "question": { - "en": "How much plugs of type Tesla Supercharger (destination) are available here?", - "nl": "Hoeveel stekkers van type heeft dit oplaadpunt?" - }, - "render": { - "en": "There are Tesla Supercharger (destination) plugs of type Tesla Supercharger (destination) available here", - "nl": "Hier zijn stekkers van het type " - }, - "freeform": { - "key": "socket:tesla_destination", - "type": "pnat" - }, - "condition": { - "and": [ - "socket:tesla_destination~*", - "socket:tesla_destination!=0" - ] - } - }, - { - "id": "voltage-11", - "question": { - "en": "What voltage do the plugs with Tesla Supercharger (destination) offer?", - "nl": "Welke spanning levert de stekker van type " - }, - "render": { - "en": "Tesla Supercharger (destination) outputs {socket:tesla_destination:voltage} volt", - "nl": " heeft een spanning van {socket:tesla_destination:voltage} volt" - }, - "freeform": { - "key": "socket:tesla_destination:voltage", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:tesla_destination:voltage=480 V", - "then": { - "en": "Tesla Supercharger (destination) outputs 480 volt", - "nl": " heeft een spanning van 480 volt" - } - } - ], - "condition": { - "and": [ - "socket:tesla_destination~*", - "socket:tesla_destination!=0" - ] - } - }, - { - "id": "current-11", - "question": { - "en": "What current do the plugs with Tesla Supercharger (destination) offer?", - "nl": "Welke stroom levert de stekker van type ?" - }, - "render": { - "en": "Tesla Supercharger (destination) outputs at most {socket:tesla_destination:current}A", - "nl": " levert een stroom van maximaal {socket:tesla_destination:current}A" - }, - "freeform": { - "key": "socket:tesla_destination:current", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:tesla_destination:current=125 A", - "then": { - "en": "Tesla Supercharger (destination) outputs at most 125 A", - "nl": " levert een stroom van maximaal 125 A" - } - }, - { - "if": "socket:socket:tesla_destination:current=350 A", - "then": { - "en": "Tesla Supercharger (destination) outputs at most 350 A", - "nl": " levert een stroom van maximaal 350 A" - } - } - ], - "condition": { - "and": [ - "socket:tesla_destination~*", - "socket:tesla_destination!=0" - ] - } - }, - { - "id": "power-output-11", - "question": { - "en": "What power output does a single plug of type Tesla Supercharger (destination) offer?", - "nl": "Welk vermogen levert een enkele stekker van type ?" - }, - "render": { - "en": "Tesla Supercharger (destination) outputs at most {socket:tesla_destination:output}", - "nl": " levert een vermogen van maximaal {socket:tesla_destination:output}" - }, - "freeform": { - "key": "socket:tesla_destination:output", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:tesla_destination:output=120 kw", - "then": { - "en": "Tesla Supercharger (destination) outputs at most 120 kw", - "nl": " levert een vermogen van maximaal 120 kw" - } - }, - { - "if": "socket:socket:tesla_destination:output=150 kw", - "then": { - "en": "Tesla Supercharger (destination) outputs at most 150 kw", - "nl": " levert een vermogen van maximaal 150 kw" - } - }, - { - "if": "socket:socket:tesla_destination:output=250 kw", - "then": { - "en": "Tesla Supercharger (destination) outputs at most 250 kw", - "nl": " levert een vermogen van maximaal 250 kw" - } - } - ], - "condition": { - "and": [ - "socket:tesla_destination~*", - "socket:tesla_destination!=0" - ] - } - }, - { - "id": "plugs-12", - "question": { - "en": "How much plugs of type Tesla supercharger (destination (A Type 2 with cable branded as tesla) are available here?", - "nl": "Hoeveel stekkers van type heeft dit oplaadpunt?" - }, - "render": { - "en": "There are Tesla supercharger (destination (A Type 2 with cable branded as tesla) plugs of type Tesla supercharger (destination (A Type 2 with cable branded as tesla) available here", - "nl": "Hier zijn stekkers van het type " - }, - "freeform": { - "key": "socket:tesla_destination", - "type": "pnat" - }, - "condition": { - "and": [ - "socket:tesla_destination~*", - "socket:tesla_destination!=0" - ] - } - }, - { - "id": "voltage-12", - "question": { - "en": "What voltage do the plugs with Tesla supercharger (destination (A Type 2 with cable branded as tesla) offer?", - "nl": "Welke spanning levert de stekker van type " - }, - "render": { - "en": "Tesla supercharger (destination (A Type 2 with cable branded as tesla) outputs {socket:tesla_destination:voltage} volt", - "nl": " heeft een spanning van {socket:tesla_destination:voltage} volt" - }, - "freeform": { - "key": "socket:tesla_destination:voltage", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:tesla_destination:voltage=230 V", - "then": { - "en": "Tesla supercharger (destination (A Type 2 with cable branded as tesla) outputs 230 volt", - "nl": " heeft een spanning van 230 volt" - } - }, - { - "if": "socket:socket:tesla_destination:voltage=400 V", - "then": { - "en": "Tesla supercharger (destination (A Type 2 with cable branded as tesla) outputs 400 volt", - "nl": " heeft een spanning van 400 volt" - } - } - ], - "condition": { - "and": [ - "socket:tesla_destination~*", - "socket:tesla_destination!=0" - ] - } - }, - { - "id": "current-12", - "question": { - "en": "What current do the plugs with Tesla supercharger (destination (A Type 2 with cable branded as tesla) offer?", - "nl": "Welke stroom levert de stekker van type ?" - }, - "render": { - "en": "Tesla supercharger (destination (A Type 2 with cable branded as tesla) outputs at most {socket:tesla_destination:current}A", - "nl": " levert een stroom van maximaal {socket:tesla_destination:current}A" - }, - "freeform": { - "key": "socket:tesla_destination:current", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:tesla_destination:current=16 A", - "then": { - "en": "Tesla supercharger (destination (A Type 2 with cable branded as tesla) outputs at most 16 A", - "nl": " levert een stroom van maximaal 16 A" - } - }, - { - "if": "socket:socket:tesla_destination:current=32 A", - "then": { - "en": "Tesla supercharger (destination (A Type 2 with cable branded as tesla) outputs at most 32 A", - "nl": " levert een stroom van maximaal 32 A" - } - } - ], - "condition": { - "and": [ - "socket:tesla_destination~*", - "socket:tesla_destination!=0" - ] - } - }, - { - "id": "power-output-12", - "question": { - "en": "What power output does a single plug of type Tesla supercharger (destination (A Type 2 with cable branded as tesla) offer?", - "nl": "Welk vermogen levert een enkele stekker van type ?" - }, - "render": { - "en": "Tesla supercharger (destination (A Type 2 with cable branded as tesla) outputs at most {socket:tesla_destination:output}", - "nl": " levert een vermogen van maximaal {socket:tesla_destination:output}" - }, - "freeform": { - "key": "socket:tesla_destination:output", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:tesla_destination:output=11 kw", - "then": { - "en": "Tesla supercharger (destination (A Type 2 with cable branded as tesla) outputs at most 11 kw", - "nl": " levert een vermogen van maximaal 11 kw" - } - }, - { - "if": "socket:socket:tesla_destination:output=22 kw", - "then": { - "en": "Tesla supercharger (destination (A Type 2 with cable branded as tesla) outputs at most 22 kw", - "nl": " levert een vermogen van maximaal 22 kw" - } - } - ], - "condition": { - "and": [ - "socket:tesla_destination~*", - "socket:tesla_destination!=0" - ] - } - }, - { - "id": "Authentication", - "question": { - "en": "What kind of authentication is available at the charging station?", - "it": "Quali sono gli orari di apertura di questa stazione di ricarica?", - "ja": "この充電ステーションはいつオープンしますか?", - "nb_NO": "Når åpnet denne ladestasjonen?", - "ru": "В какое время работает эта зарядная станция?", - "zh_Hant": "何時是充電站開放使用的時間?" - }, - "multiAnswer": true, - "mappings": [ - { - "if": "authentication:membership_card=yes", - "ifnot": "authentication:membership_card=no", - "then": { - "en": "Authentication by a membership card" - } - }, - { - "if": "authentication:app=yes", - "ifnot": "authentication:app=no", - "then": { - "en": "Authentication by an app" - } - }, - { - "if": "authentication:phone_call=yes", - "ifnot": "authentication:phone_call=no", - "then": { - "en": "Authentication via phone call is available" - } - }, - { - "if": "authentication:short_message=yes", - "ifnot": "authentication:short_message=no", - "then": { - "en": "Authentication via phone call is available" - } - }, - { - "if": "authentication:nfc=yes", - "ifnot": "authentication:nfc=no", - "then": { - "en": "Authentication via NFC is available" - } - }, - { - "if": "authentication:money_card=yes", - "ifnot": "authentication:money_card=no", - "then": { - "en": "Authentication via Money Card is available" - } - }, - { - "if": "authentication:debit_card=yes", - "ifnot": "authentication:debit_card=no", - "then": { - "en": "Authentication via debit card is available" - } - }, - { - "if": "authentication:none=yes", - "ifnot": "authentication:none=no", - "then": { - "en": "No authentication is needed" - } - } - ] - }, - { - "id": "Auth phone", - "render": { - "en": "Authenticate by calling or SMS'ing to {authentication:phone_call:number}", - "it": "{network}", - "ja": "{network}", - "nb_NO": "{network}", - "ru": "{network}", - "zh_Hant": "{network}" - }, - "question": { - "en": "What's the phone number for authentication call or SMS?", - "it": "A quale rete appartiene questa stazione di ricarica?", - "ja": "この充電ステーションの運営チェーンはどこですか?", - "ru": "К какой сети относится эта станция?", - "zh_Hant": "充電站所屬的網路是?" - }, - "freeform": { - "key": "authentication:phone_call:number", - "type": "phone" - }, - "condition": { - "or": [ - "authentication:phone_call=yes", - "authentication:short_message=yes" - ] - }, - "it": { - "0": { - "then": "Non appartiene a una rete" - } - }, - "ja": { - "0": { - "then": "大規模な運営チェーンの一部ではない" - } - }, - "ru": { - "0": { - "then": "Не является частью более крупной сети" - } - }, - "zh_Hant": { - "0": { - "then": "不屬於大型網路" - } - } - }, - { - "id": "OH", - "render": "{opening_hours_table(opening_hours)}", - "freeform": { - "key": "opening_hours", - "type": "opening_hours" - }, - "question": { - "en": "When is this charging station opened?" - }, - "mappings": [ - { - "if": "opening_hours=24/7", - "then": { - "en": "24/7 opened (including holidays)" - } - } - ] - }, - { - "id": "fee/charge", - "question": { - "en": "How much does one have to pay to use this charging station?", - "nl": "Hoeveel kost het gebruik van dit oplaadpunt?" - }, - "freeform": { - "key": "charge", - "addExtraTags": [ - "fee=yes" - ] - }, - "render": { - "en": "Using this charging station costs {charge}", - "nl": "Dit oplaadpunt gebruiken kost {charge}" - }, - "mappings": [ - { - "if": { - "and": [ - "fee=no", - "charge=" - ] - }, - "then": { - "nl": "Gratis te gebruiken", - "en": "Free to use" - } - } - ] - }, - { - "id": "payment-options", - "builtin": "payment-options", - "override": { - "condition": { - "or": [ - "fee=yes", - "charge~*" - ] - }, - "mappings+": [ - { - "if": "payment:app=yes", - "ifnot": "payment:app=no", - "then": { - "en": "Payment is done using a dedicated app", - "nl": "Betalen via een app van het netwerk" - } - }, - { - "if": "payment:membership_card=yes", - "ifnot": "payment:membership_card=no", - "then": { - "en": "Payment is done using a membership card", - "nl": "Betalen via een lidkaart van het netwerk" - } - } - ], - "mappings": [ - { - "if": "payment:app=yes", - "ifnot": "payment:app=no", - "then": { - "en": "Payment is done using a dedicated app", - "nl": "Betalen via een app van het netwerk" - } - }, - { - "if": "payment:membership_card=yes", - "ifnot": "payment:membership_card=no", - "then": { - "en": "Payment is done using a membership card", - "nl": "Betalen via een lidkaart van het netwerk" - } - } - ] - } - }, - { - "id": "maxstay", - "question": { - "en": "What is the maximum amount of time one is allowed to stay here?", - "nl": "Hoelang mag een voertuig hier blijven staan?" - }, - "freeform": { - "key": "maxstay" - }, - "render": { - "en": "One can stay at most {canonical(maxstay)}", - "nl": "De maximale parkeertijd hier is {canonical(maxstay)}" - }, - "mappings": [ - { - "if": "maxstay=unlimited", - "then": { - "en": "No timelimit on leaving your vehicle here", - "nl": "Geen maximum parkeertijd" - } - } - ] - }, - { - "id": "Network", - "render": { - "en": "Part of the network {network}" - }, - "question": { - "en": "Is this charging station part of a network?" - }, - "freeform": { - "key": "network" - }, - "mappings": [ - { - "if": "no:network=yes", - "then": { - "en": "Not part of a bigger network" - } - }, - { - "if": "network=none", - "then": { - "en": "Not part of a bigger network" - }, - "hideInAnswer": true - }, - { - "if": "network=AeroVironment", - "then": "AeroVironment" - }, - { - "if": "network=Blink", - "then": "Blink" - }, - { - "if": "network=eVgo", - "then": "eVgo" - } - ] - }, - { - "id": "Operator", - "question": { - "en": "Who is the operator of this charging station?" - }, - "render": { - "en": "This charging station is operated by {operator}" - }, - "freeform": { - "key": "operator" - }, - "mappings": [ - { - "if": { - "and": [ - "network:={operator}" - ] - }, - "then": { - "en": "Actually, {operator} is the network" - }, - "addExtraTags": [ - "operator=" - ], - "hideInAnswer": "operator=" - } - ] - }, - { - "id": "phone", - "question": { - "en": "What number can one call if there is a problem with this charging station?" - }, - "render": { - "en": "In case of problems, call {phone}" - }, - "freeform": { - "key": "phone", - "type": "phone" - } - }, - { - "id": "email", - "question": { - "en": "What is the email address of the operator?" - }, - "render": { - "en": "In case of problems, send an email to {email}" - }, - "freeform": { - "key": "email", - "type": "email" - } - }, - { - "id": "website", - "question": { - "en": "What is the website of the operator?" - }, - "render": { - "en": "More info on {website}" - }, - "freeform": { - "key": "website", - "type": "url" - } - }, - "level", - { - "id": "ref", - "question": { - "en": "What is the reference number of this charging station?" - }, - "render": { - "en": "Reference number is {ref}" - }, - "freeform": { - "key": "ref" - } - }, - { - "id": "Operational status", - "question": { - "en": "Is this charging point in use?", - "nl": "Is dit oplaadpunt operationeel?" - }, - "mappings": [ - { - "if": "operational_status=broken", - "then": { - "en": "This charging station is broken", - "nl": "Dit oplaadpunt is kapot" - } - }, - { - "if": { - "and": [ - "planned:amenity=charging_station", - "amenity=" - ] - }, - "then": { - "en": "A charging station is planned here", - "nl": "Hier zal binnenkort een oplaadpunt gebouwd worden" - } - }, - { - "if": { - "and": [ - "construction:amenity=charging_station", - "amenity=" - ] - }, - "then": { - "en": "A charging station is constructed here", - "nl": "Hier wordt op dit moment een oplaadpunt gebouwd" - } - }, - { - "if": { - "and": [ - "disused:amenity=charging_station", - "amenity=" - ] - }, - "then": { - "en": "This charging station has beed permanently disabled and is not in use anymore but is still visible", - "nl": "Dit oplaadpunt is niet meer in gebruik maar is wel nog aanwezig" - } - }, - { - "if": "amenity=charging_station", - "then": { - "en": "This charging station works", - "nl": "Dit oplaadpunt werkt" - } - } - ] - }, - { - "id": "Parking:fee", - "question": { - "en": "Does one have to pay a parking fee while charging?" - }, - "mappings": [ - { - "if": "parking:fee=no", - "then": { - "en": "No additional parking cost while charging" - } - }, - { - "if": "parking:fee=yes", - "then": { - "en": "An additional parking fee should be paid while charging" - } - } - ] - } - ], - "icon": { - "render": "pin:#fff;./assets/themes/charging_stations/plug.svg", - "mappings": [ - { - "if": "bicycle=yes", - "then": "pin:#fff;./assets/themes/charging_stations/bicycle.svg" - }, - { - "if": { - "or": [ - "car=yes", - "motorcar=yes" - ] - }, - "then": "pin:#fff;./assets/themes/charging_stations/car.svg" - } + { + "id": "access", + "question": { + "en": "Who is allowed to use this charging station?" + }, + "render": { + "en": "Access is {access}" + }, + "freeform": { + "key": "access", + "addExtraTags": [ + "fixme=Freeform field used for access - doublecheck the value" ] + }, + "mappings": [ + { + "if": "access=yes", + "then": "Anyone can use this charging station (payment might be needed)" + }, + { + "if": { + "or": [ + "access=permissive", + "access=public" + ] + }, + "then": "Anyone can use this charging station (payment might be needed)", + "hideInAnswer": true + }, + { + "if": "access=customers", + "then": "Only customers of the place this station belongs to can use this charging station
E.g. a charging station operated by hotel which is only usable by their guests " + }, + { + "if": "access=private", + "then": "Not accessible to the general public (e.g. only accessible to the owners, employees, ...)" + } + ] }, - "iconOverlays": [ + { + "id": "capacity", + "render": { + "en": "{capacity} vehicles can be charged here at the same time", + "nl": "{capacity} voertuigen kunnen hier op hetzelfde moment opgeladen worden" + }, + "question": { + "en": "How much vehicles can be charged here at the same time?", + "nl": "Hoeveel voertuigen kunnen hier opgeladen worden?" + }, + "freeform": { + "key": "capacity", + "type": "pnat" + } + }, + { + "id": "Available_charging_stations (generated)", + "question": { + "en": "Which charging stations are available here?" + }, + "multiAnswer": true, + "mappings": [ { - "if": { - "or": [ - "disused:amenity=charging_station", - "operational_status=broken" - ] - }, - "then": "cross_bottom_right:#c22;" + "if": "socket:schuko=1", + "ifnot": "socket:schuko=", + "then": { + "en": "
Schuko wall plug without ground pin (CEE7/4 type F)
", + "nl": "
Schuko stekker zonder aardingspin (CEE7/4 type F)
" + }, + "hideInAnswer": { + "or": [ + "_country!=be", + "_country!=fr", + "_country!=ma", + "_country!=tn", + "_country!=pl", + "_country!=cs", + "_country!=sk", + "_country!=mo" + ] + } }, { - "if": { - "or": [ - "proposed:amenity=charging_station", - "planned:amenity=charging_station" - ] - }, - "then": "./assets/layers/charging_station/under_construction.svg", - "badge": true + "if": { + "and": [ + "socket:schuko~*", + "socket:schuko!=1" + ] + }, + "then": { + "en": "
Schuko wall plug without ground pin (CEE7/4 type F)
", + "nl": "
Schuko stekker zonder aardingspin (CEE7/4 type F)
" + }, + "hideInAnswer": true }, { - "if": { + "if": "socket:typee=1", + "ifnot": "socket:typee=", + "then": { + "en": "
European wall plug with ground pin (CEE7/4 type E)
", + "nl": "
Europese stekker met aardingspin (CEE7/4 type E)
" + } + }, + { + "if": { + "and": [ + "socket:typee~*", + "socket:typee!=1" + ] + }, + "then": { + "en": "
European wall plug with ground pin (CEE7/4 type E)
", + "nl": "
Europese stekker met aardingspin (CEE7/4 type E)
" + }, + "hideInAnswer": true + }, + { + "if": "socket:chademo=1", + "ifnot": "socket:chademo=", + "then": { + "en": "
Chademo
", + "nl": "
Chademo
" + }, + "hideInAnswer": { + "or": [ + { "and": [ - "bicycle=yes", - { - "or": [ - "motorcar=yes", - "car=yes" - ] - } + "car=no", + "motorcar=no", + "hgv=no", + "bus=no" ] - }, - "then": "circle:#fff;./assets/themes/charging_stations/car.svg", - "badge": true - } - ], - "width": { - "render": "8" - }, - "iconSize": { - "render": "50,50,bottom" - }, - "color": { - "render": "#00f" - }, - "presets": [ + }, + { + "and": [ + { + "or": [ + "bicycle=yes", + "scooter=yes" + ] + }, + "car!=yes", + "motorcar!=yes", + "hgv!=yes", + "bus!=yes" + ] + } + ] + } + }, { - "tags": [ - "amenity=charging_station" - ], - "title": { - "en": "Charging station" - }, - "preciseInput": { - "preferredBackground": "map" + "if": { + "and": [ + "socket:chademo~*", + "socket:chademo!=1" + ] + }, + "then": { + "en": "
Chademo
", + "nl": "
Chademo
" + }, + "hideInAnswer": true + }, + { + "if": "socket:type1_cable=1", + "ifnot": "socket:type1_cable=", + "then": { + "en": "
Type 1 with cable (J1772)
", + "nl": "
Type 1 met kabel (J1772)
" + }, + "hideInAnswer": { + "or": [ + { + "and": [ + "car=no", + "motorcar=no", + "hgv=no", + "bus=no" + ] + }, + { + "and": [ + { + "or": [ + "bicycle=yes", + "scooter=yes" + ] + }, + "car!=yes", + "motorcar!=yes", + "hgv!=yes", + "bus!=yes" + ] + } + ] + } + }, + { + "if": { + "and": [ + "socket:type1_cable~*", + "socket:type1_cable!=1" + ] + }, + "then": { + "en": "
Type 1 with cable (J1772)
", + "nl": "
Type 1 met kabel (J1772)
" + }, + "hideInAnswer": true + }, + { + "if": "socket:type1=1", + "ifnot": "socket:type1=", + "then": { + "en": "
Type 1 without cable (J1772)
", + "nl": "
Type 1 zonder kabel (J1772)
" + }, + "hideInAnswer": { + "or": [ + { + "and": [ + "car=no", + "motorcar=no", + "hgv=no", + "bus=no" + ] + }, + { + "and": [ + { + "or": [ + "bicycle=yes", + "scooter=yes" + ] + }, + "car!=yes", + "motorcar!=yes", + "hgv!=yes", + "bus!=yes" + ] + } + ] + } + }, + { + "if": { + "and": [ + "socket:type1~*", + "socket:type1!=1" + ] + }, + "then": { + "en": "
Type 1 without cable (J1772)
", + "nl": "
Type 1 zonder kabel (J1772)
" + }, + "hideInAnswer": true + }, + { + "if": "socket:type1_combo=1", + "ifnot": "socket:type1_combo=", + "then": { + "en": "
Type 1 CCS (aka Type 1 Combo)
", + "nl": "
Type 1 CCS (ook gekend als Type 1 Combo)
" + }, + "hideInAnswer": { + "or": [ + { + "and": [ + "car=no", + "motorcar=no", + "hgv=no", + "bus=no" + ] + }, + { + "and": [ + { + "or": [ + "bicycle=yes", + "scooter=yes" + ] + }, + "car!=yes", + "motorcar!=yes", + "hgv!=yes", + "bus!=yes" + ] + } + ] + } + }, + { + "if": { + "and": [ + "socket:type1_combo~*", + "socket:type1_combo!=1" + ] + }, + "then": { + "en": "
Type 1 CCS (aka Type 1 Combo)
", + "nl": "
Type 1 CCS (ook gekend als Type 1 Combo)
" + }, + "hideInAnswer": true + }, + { + "if": "socket:tesla_supercharger=1", + "ifnot": "socket:tesla_supercharger=", + "then": { + "en": "
Tesla Supercharger
", + "nl": "
Tesla Supercharger
" + }, + "hideInAnswer": { + "or": [ + { + "and": [ + "car=no", + "motorcar=no", + "hgv=no", + "bus=no" + ] + }, + { + "and": [ + { + "or": [ + "bicycle=yes", + "scooter=yes" + ] + }, + "car!=yes", + "motorcar!=yes", + "hgv!=yes", + "bus!=yes" + ] + } + ] + } + }, + { + "if": { + "and": [ + "socket:tesla_supercharger~*", + "socket:tesla_supercharger!=1" + ] + }, + "then": { + "en": "
Tesla Supercharger
", + "nl": "
Tesla Supercharger
" + }, + "hideInAnswer": true + }, + { + "if": "socket:type2=1", + "ifnot": "socket:type2=", + "then": { + "en": "
Type 2 (mennekes)
", + "nl": "
Type 2 (mennekes)
" + }, + "hideInAnswer": { + "or": [ + { + "and": [ + "car=no", + "motorcar=no", + "hgv=no", + "bus=no" + ] + }, + { + "and": [ + { + "or": [ + "bicycle=yes", + "scooter=yes" + ] + }, + "car!=yes", + "motorcar!=yes", + "hgv!=yes", + "bus!=yes" + ] + } + ] + } + }, + { + "if": { + "and": [ + "socket:type2~*", + "socket:type2!=1" + ] + }, + "then": { + "en": "
Type 2 (mennekes)
", + "nl": "
Type 2 (mennekes)
" + }, + "hideInAnswer": true + }, + { + "if": "socket:type2_combo=1", + "ifnot": "socket:type2_combo=", + "then": { + "en": "
Type 2 CCS (mennekes)
", + "nl": "
Type 2 CCS (mennekes)
" + }, + "hideInAnswer": { + "or": [ + { + "and": [ + "car=no", + "motorcar=no", + "hgv=no", + "bus=no" + ] + }, + { + "and": [ + { + "or": [ + "bicycle=yes", + "scooter=yes" + ] + }, + "car!=yes", + "motorcar!=yes", + "hgv!=yes", + "bus!=yes" + ] + } + ] + } + }, + { + "if": { + "and": [ + "socket:type2_combo~*", + "socket:type2_combo!=1" + ] + }, + "then": { + "en": "
Type 2 CCS (mennekes)
", + "nl": "
Type 2 CCS (mennekes)
" + }, + "hideInAnswer": true + }, + { + "if": "socket:type2_cable=1", + "ifnot": "socket:type2_cable=", + "then": { + "en": "
Type 2 with cable (mennekes)
", + "nl": "
Type 2 met kabel (J1772)
" + }, + "hideInAnswer": { + "or": [ + { + "and": [ + "car=no", + "motorcar=no", + "hgv=no", + "bus=no" + ] + }, + { + "and": [ + { + "or": [ + "bicycle=yes", + "scooter=yes" + ] + }, + "car!=yes", + "motorcar!=yes", + "hgv!=yes", + "bus!=yes" + ] + } + ] + } + }, + { + "if": { + "and": [ + "socket:type2_cable~*", + "socket:type2_cable!=1" + ] + }, + "then": { + "en": "
Type 2 with cable (mennekes)
", + "nl": "
Type 2 met kabel (J1772)
" + }, + "hideInAnswer": true + }, + { + "if": "socket:tesla_supercharger_ccs=1", + "ifnot": "socket:tesla_supercharger_ccs=", + "then": { + "en": "
Tesla Supercharger CCS (a branded type2_css)
", + "nl": "
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
" + }, + "hideInAnswer": { + "or": [ + { + "and": [ + "car=no", + "motorcar=no", + "hgv=no", + "bus=no" + ] + }, + { + "and": [ + { + "or": [ + "bicycle=yes", + "scooter=yes" + ] + }, + "car!=yes", + "motorcar!=yes", + "hgv!=yes", + "bus!=yes" + ] + } + ] + } + }, + { + "if": { + "and": [ + "socket:tesla_supercharger_ccs~*", + "socket:tesla_supercharger_ccs!=1" + ] + }, + "then": { + "en": "
Tesla Supercharger CCS (a branded type2_css)
", + "nl": "
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
" + }, + "hideInAnswer": true + }, + { + "if": "socket:tesla_destination=1", + "ifnot": "socket:tesla_destination=", + "then": { + "en": "
Tesla Supercharger (destination)
", + "nl": "
Tesla Supercharger (destination)
" + }, + "hideInAnswer": { + "or": [ + { + "and": [ + "car=no", + "motorcar=no", + "hgv=no", + "bus=no" + ] + }, + { + "and": [ + { + "or": [ + "bicycle=yes", + "scooter=yes" + ] + }, + "car!=yes", + "motorcar!=yes", + "hgv!=yes", + "bus!=yes" + ] + }, + { + "or": [ + "_country!=us" + ] + } + ] + } + }, + { + "if": { + "and": [ + "socket:tesla_destination~*", + "socket:tesla_destination!=1" + ] + }, + "then": { + "en": "
Tesla Supercharger (destination)
", + "nl": "
Tesla Supercharger (destination)
" + }, + "hideInAnswer": true + }, + { + "if": "socket:tesla_destination=1", + "ifnot": "socket:tesla_destination=", + "then": { + "en": "
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
", + "nl": "
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
" + }, + "hideInAnswer": { + "or": [ + { + "and": [ + "car=no", + "motorcar=no", + "hgv=no", + "bus=no" + ] + }, + { + "and": [ + { + "or": [ + "bicycle=yes", + "scooter=yes" + ] + }, + "car!=yes", + "motorcar!=yes", + "hgv!=yes", + "bus!=yes" + ] + }, + { + "or": [ + "_country=us" + ] + } + ] + } + }, + { + "if": { + "and": [ + "socket:tesla_destination~*", + "socket:tesla_destination!=1" + ] + }, + "then": { + "en": "
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
", + "nl": "
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
" + }, + "hideInAnswer": true + }, + { + "if": "socket:USB-A=1", + "ifnot": "socket:USB-A=", + "then": { + "en": "
USB to charge phones and small electronics
", + "nl": "
USB om GSMs en kleine electronica op te laden
" + } + }, + { + "if": { + "and": [ + "socket:USB-A~*", + "socket:USB-A!=1" + ] + }, + "then": { + "en": "
USB to charge phones and small electronics
", + "nl": "
USB om GSMs en kleine electronica op te laden
" + }, + "hideInAnswer": true + }, + { + "if": "socket:bosch_3pin=1", + "ifnot": "socket:bosch_3pin=", + "then": { + "en": "
Bosch Active Connect with cable
", + "nl": "
Bosch Active Connect aan een kabel
" + }, + "hideInAnswer": { + "or": [ + { + "and": [ + "bicycle=no" + ] + }, + { + "and": [ + { + "or": [ + "car=yes", + "motorcar=yes", + "hgv=yes", + "bus=yes" + ] + }, + "bicycle!=yes" + ] + } + ] + } + }, + { + "if": { + "and": [ + "socket:bosch_3pin~*", + "socket:bosch_3pin!=1" + ] + }, + "then": { + "en": "
Bosch Active Connect with cable
", + "nl": "
Bosch Active Connect aan een kabel
" + }, + "hideInAnswer": true + } + ] + }, + { + "id": "plugs-0", + "question": { + "en": "How much plugs of type
Schuko wall plug without ground pin (CEE7/4 type F)
are available here?", + "nl": "Hoeveel stekkers van type
Schuko stekker zonder aardingspin (CEE7/4 type F)
heeft dit oplaadpunt?" + }, + "render": { + "en": "There are {socket:schuko} plugs of type
Schuko wall plug without ground pin (CEE7/4 type F)
available here", + "nl": "Hier zijn {socket:schuko} stekkers van het type
Schuko stekker zonder aardingspin (CEE7/4 type F)
" + }, + "freeform": { + "key": "socket:schuko", + "type": "pnat" + }, + "condition": { + "and": [ + "socket:schuko~*", + "socket:schuko!=0" + ] + } + }, + { + "id": "voltage-0", + "question": { + "en": "What voltage do the plugs with
Schuko wall plug without ground pin (CEE7/4 type F)
offer?", + "nl": "Welke spanning levert de stekker van type
Schuko stekker zonder aardingspin (CEE7/4 type F)
" + }, + "render": { + "en": "
Schuko wall plug without ground pin (CEE7/4 type F)
outputs {socket:schuko:voltage} volt", + "nl": "
Schuko stekker zonder aardingspin (CEE7/4 type F)
heeft een spanning van {socket:schuko:voltage} volt" + }, + "freeform": { + "key": "socket:schuko:voltage", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:schuko:voltage=230 V", + "then": { + "en": "
Schuko wall plug without ground pin (CEE7/4 type F)
outputs 230 volt", + "nl": "
Schuko stekker zonder aardingspin (CEE7/4 type F)
heeft een spanning van 230 volt" + } + } + ], + "condition": { + "and": [ + "socket:schuko~*", + "socket:schuko!=0" + ] + } + }, + { + "id": "current-0", + "question": { + "en": "What current do the plugs with
Schuko wall plug without ground pin (CEE7/4 type F)
offer?", + "nl": "Welke stroom levert de stekker van type
Schuko stekker zonder aardingspin (CEE7/4 type F)
?" + }, + "render": { + "en": "
Schuko wall plug without ground pin (CEE7/4 type F)
outputs at most {socket:schuko:current}A", + "nl": "
Schuko stekker zonder aardingspin (CEE7/4 type F)
levert een stroom van maximaal {socket:schuko:current}A" + }, + "freeform": { + "key": "socket:schuko:current", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:schuko:current=16 A", + "then": { + "en": "
Schuko wall plug without ground pin (CEE7/4 type F)
outputs at most 16 A", + "nl": "
Schuko stekker zonder aardingspin (CEE7/4 type F)
levert een stroom van maximaal 16 A" + } + } + ], + "condition": { + "and": [ + "socket:schuko~*", + "socket:schuko!=0" + ] + } + }, + { + "id": "power-output-0", + "question": { + "en": "What power output does a single plug of type
Schuko wall plug without ground pin (CEE7/4 type F)
offer?", + "nl": "Welk vermogen levert een enkele stekker van type
Schuko stekker zonder aardingspin (CEE7/4 type F)
?" + }, + "render": { + "en": "
Schuko wall plug without ground pin (CEE7/4 type F)
outputs at most {socket:schuko:output}", + "nl": "
Schuko stekker zonder aardingspin (CEE7/4 type F)
levert een vermogen van maximaal {socket:schuko:output}" + }, + "freeform": { + "key": "socket:schuko:output", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:schuko:output=3.6 kw", + "then": { + "en": "
Schuko wall plug without ground pin (CEE7/4 type F)
outputs at most 3.6 kw", + "nl": "
Schuko stekker zonder aardingspin (CEE7/4 type F)
levert een vermogen van maximaal 3.6 kw" + } + } + ], + "condition": { + "and": [ + "socket:schuko~*", + "socket:schuko!=0" + ] + } + }, + { + "id": "plugs-1", + "question": { + "en": "How much plugs of type
European wall plug with ground pin (CEE7/4 type E)
are available here?", + "nl": "Hoeveel stekkers van type
Europese stekker met aardingspin (CEE7/4 type E)
heeft dit oplaadpunt?" + }, + "render": { + "en": "There are {socket:typee} plugs of type
European wall plug with ground pin (CEE7/4 type E)
available here", + "nl": "Hier zijn {socket:typee} stekkers van het type
Europese stekker met aardingspin (CEE7/4 type E)
" + }, + "freeform": { + "key": "socket:typee", + "type": "pnat" + }, + "condition": { + "and": [ + "socket:typee~*", + "socket:typee!=0" + ] + } + }, + { + "id": "voltage-1", + "question": { + "en": "What voltage do the plugs with
European wall plug with ground pin (CEE7/4 type E)
offer?", + "nl": "Welke spanning levert de stekker van type
Europese stekker met aardingspin (CEE7/4 type E)
" + }, + "render": { + "en": "
European wall plug with ground pin (CEE7/4 type E)
outputs {socket:typee:voltage} volt", + "nl": "
Europese stekker met aardingspin (CEE7/4 type E)
heeft een spanning van {socket:typee:voltage} volt" + }, + "freeform": { + "key": "socket:typee:voltage", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:typee:voltage=230 V", + "then": { + "en": "
European wall plug with ground pin (CEE7/4 type E)
outputs 230 volt", + "nl": "
Europese stekker met aardingspin (CEE7/4 type E)
heeft een spanning van 230 volt" + } + } + ], + "condition": { + "and": [ + "socket:typee~*", + "socket:typee!=0" + ] + } + }, + { + "id": "current-1", + "question": { + "en": "What current do the plugs with
European wall plug with ground pin (CEE7/4 type E)
offer?", + "nl": "Welke stroom levert de stekker van type
Europese stekker met aardingspin (CEE7/4 type E)
?" + }, + "render": { + "en": "
European wall plug with ground pin (CEE7/4 type E)
outputs at most {socket:typee:current}A", + "nl": "
Europese stekker met aardingspin (CEE7/4 type E)
levert een stroom van maximaal {socket:typee:current}A" + }, + "freeform": { + "key": "socket:typee:current", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:typee:current=16 A", + "then": { + "en": "
European wall plug with ground pin (CEE7/4 type E)
outputs at most 16 A", + "nl": "
Europese stekker met aardingspin (CEE7/4 type E)
levert een stroom van maximaal 16 A" + } + } + ], + "condition": { + "and": [ + "socket:typee~*", + "socket:typee!=0" + ] + } + }, + { + "id": "power-output-1", + "question": { + "en": "What power output does a single plug of type
European wall plug with ground pin (CEE7/4 type E)
offer?", + "nl": "Welk vermogen levert een enkele stekker van type
Europese stekker met aardingspin (CEE7/4 type E)
?" + }, + "render": { + "en": "
European wall plug with ground pin (CEE7/4 type E)
outputs at most {socket:typee:output}", + "nl": "
Europese stekker met aardingspin (CEE7/4 type E)
levert een vermogen van maximaal {socket:typee:output}" + }, + "freeform": { + "key": "socket:typee:output", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:typee:output=3 kw", + "then": { + "en": "
European wall plug with ground pin (CEE7/4 type E)
outputs at most 3 kw", + "nl": "
Europese stekker met aardingspin (CEE7/4 type E)
levert een vermogen van maximaal 3 kw" + } + }, + { + "if": "socket:socket:typee:output=22 kw", + "then": { + "en": "
European wall plug with ground pin (CEE7/4 type E)
outputs at most 22 kw", + "nl": "
Europese stekker met aardingspin (CEE7/4 type E)
levert een vermogen van maximaal 22 kw" + } + } + ], + "condition": { + "and": [ + "socket:typee~*", + "socket:typee!=0" + ] + } + }, + { + "id": "plugs-2", + "question": { + "en": "How much plugs of type
Chademo
are available here?", + "nl": "Hoeveel stekkers van type
Chademo
heeft dit oplaadpunt?" + }, + "render": { + "en": "There are {socket:chademo} plugs of type
Chademo
available here", + "nl": "Hier zijn {socket:chademo} stekkers van het type
Chademo
" + }, + "freeform": { + "key": "socket:chademo", + "type": "pnat" + }, + "condition": { + "and": [ + "socket:chademo~*", + "socket:chademo!=0" + ] + } + }, + { + "id": "voltage-2", + "question": { + "en": "What voltage do the plugs with
Chademo
offer?", + "nl": "Welke spanning levert de stekker van type
Chademo
" + }, + "render": { + "en": "
Chademo
outputs {socket:chademo:voltage} volt", + "nl": "
Chademo
heeft een spanning van {socket:chademo:voltage} volt" + }, + "freeform": { + "key": "socket:chademo:voltage", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:chademo:voltage=500 V", + "then": { + "en": "
Chademo
outputs 500 volt", + "nl": "
Chademo
heeft een spanning van 500 volt" + } + } + ], + "condition": { + "and": [ + "socket:chademo~*", + "socket:chademo!=0" + ] + } + }, + { + "id": "current-2", + "question": { + "en": "What current do the plugs with
Chademo
offer?", + "nl": "Welke stroom levert de stekker van type
Chademo
?" + }, + "render": { + "en": "
Chademo
outputs at most {socket:chademo:current}A", + "nl": "
Chademo
levert een stroom van maximaal {socket:chademo:current}A" + }, + "freeform": { + "key": "socket:chademo:current", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:chademo:current=120 A", + "then": { + "en": "
Chademo
outputs at most 120 A", + "nl": "
Chademo
levert een stroom van maximaal 120 A" + } + } + ], + "condition": { + "and": [ + "socket:chademo~*", + "socket:chademo!=0" + ] + } + }, + { + "id": "power-output-2", + "question": { + "en": "What power output does a single plug of type
Chademo
offer?", + "nl": "Welk vermogen levert een enkele stekker van type
Chademo
?" + }, + "render": { + "en": "
Chademo
outputs at most {socket:chademo:output}", + "nl": "
Chademo
levert een vermogen van maximaal {socket:chademo:output}" + }, + "freeform": { + "key": "socket:chademo:output", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:chademo:output=50 kw", + "then": { + "en": "
Chademo
outputs at most 50 kw", + "nl": "
Chademo
levert een vermogen van maximaal 50 kw" + } + } + ], + "condition": { + "and": [ + "socket:chademo~*", + "socket:chademo!=0" + ] + } + }, + { + "id": "plugs-3", + "question": { + "en": "How much plugs of type
Type 1 with cable (J1772)
are available here?", + "nl": "Hoeveel stekkers van type
Type 1 met kabel (J1772)
heeft dit oplaadpunt?" + }, + "render": { + "en": "There are {socket:type1_cable} plugs of type
Type 1 with cable (J1772)
available here", + "nl": "Hier zijn {socket:type1_cable} stekkers van het type
Type 1 met kabel (J1772)
" + }, + "freeform": { + "key": "socket:type1_cable", + "type": "pnat" + }, + "condition": { + "and": [ + "socket:type1_cable~*", + "socket:type1_cable!=0" + ] + } + }, + { + "id": "voltage-3", + "question": { + "en": "What voltage do the plugs with
Type 1 with cable (J1772)
offer?", + "nl": "Welke spanning levert de stekker van type
Type 1 met kabel (J1772)
" + }, + "render": { + "en": "
Type 1 with cable (J1772)
outputs {socket:type1_cable:voltage} volt", + "nl": "
Type 1 met kabel (J1772)
heeft een spanning van {socket:type1_cable:voltage} volt" + }, + "freeform": { + "key": "socket:type1_cable:voltage", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:type1_cable:voltage=200 V", + "then": { + "en": "
Type 1 with cable (J1772)
outputs 200 volt", + "nl": "
Type 1 met kabel (J1772)
heeft een spanning van 200 volt" + } + }, + { + "if": "socket:socket:type1_cable:voltage=240 V", + "then": { + "en": "
Type 1 with cable (J1772)
outputs 240 volt", + "nl": "
Type 1 met kabel (J1772)
heeft een spanning van 240 volt" + } + } + ], + "condition": { + "and": [ + "socket:type1_cable~*", + "socket:type1_cable!=0" + ] + } + }, + { + "id": "current-3", + "question": { + "en": "What current do the plugs with
Type 1 with cable (J1772)
offer?", + "nl": "Welke stroom levert de stekker van type
Type 1 met kabel (J1772)
?" + }, + "render": { + "en": "
Type 1 with cable (J1772)
outputs at most {socket:type1_cable:current}A", + "nl": "
Type 1 met kabel (J1772)
levert een stroom van maximaal {socket:type1_cable:current}A" + }, + "freeform": { + "key": "socket:type1_cable:current", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:type1_cable:current=32 A", + "then": { + "en": "
Type 1 with cable (J1772)
outputs at most 32 A", + "nl": "
Type 1 met kabel (J1772)
levert een stroom van maximaal 32 A" + } + } + ], + "condition": { + "and": [ + "socket:type1_cable~*", + "socket:type1_cable!=0" + ] + } + }, + { + "id": "power-output-3", + "question": { + "en": "What power output does a single plug of type
Type 1 with cable (J1772)
offer?", + "nl": "Welk vermogen levert een enkele stekker van type
Type 1 met kabel (J1772)
?" + }, + "render": { + "en": "
Type 1 with cable (J1772)
outputs at most {socket:type1_cable:output}", + "nl": "
Type 1 met kabel (J1772)
levert een vermogen van maximaal {socket:type1_cable:output}" + }, + "freeform": { + "key": "socket:type1_cable:output", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:type1_cable:output=3.7 kw", + "then": { + "en": "
Type 1 with cable (J1772)
outputs at most 3.7 kw", + "nl": "
Type 1 met kabel (J1772)
levert een vermogen van maximaal 3.7 kw" + } + }, + { + "if": "socket:socket:type1_cable:output=7 kw", + "then": { + "en": "
Type 1 with cable (J1772)
outputs at most 7 kw", + "nl": "
Type 1 met kabel (J1772)
levert een vermogen van maximaal 7 kw" + } + } + ], + "condition": { + "and": [ + "socket:type1_cable~*", + "socket:type1_cable!=0" + ] + } + }, + { + "id": "plugs-4", + "question": { + "en": "How much plugs of type
Type 1 without cable (J1772)
are available here?", + "nl": "Hoeveel stekkers van type
Type 1 zonder kabel (J1772)
heeft dit oplaadpunt?" + }, + "render": { + "en": "There are {socket:type1} plugs of type
Type 1 without cable (J1772)
available here", + "nl": "Hier zijn {socket:type1} stekkers van het type
Type 1 zonder kabel (J1772)
" + }, + "freeform": { + "key": "socket:type1", + "type": "pnat" + }, + "condition": { + "and": [ + "socket:type1~*", + "socket:type1!=0" + ] + } + }, + { + "id": "voltage-4", + "question": { + "en": "What voltage do the plugs with
Type 1 without cable (J1772)
offer?", + "nl": "Welke spanning levert de stekker van type
Type 1 zonder kabel (J1772)
" + }, + "render": { + "en": "
Type 1 without cable (J1772)
outputs {socket:type1:voltage} volt", + "nl": "
Type 1 zonder kabel (J1772)
heeft een spanning van {socket:type1:voltage} volt" + }, + "freeform": { + "key": "socket:type1:voltage", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:type1:voltage=200 V", + "then": { + "en": "
Type 1 without cable (J1772)
outputs 200 volt", + "nl": "
Type 1 zonder kabel (J1772)
heeft een spanning van 200 volt" + } + }, + { + "if": "socket:socket:type1:voltage=240 V", + "then": { + "en": "
Type 1 without cable (J1772)
outputs 240 volt", + "nl": "
Type 1 zonder kabel (J1772)
heeft een spanning van 240 volt" + } + } + ], + "condition": { + "and": [ + "socket:type1~*", + "socket:type1!=0" + ] + } + }, + { + "id": "current-4", + "question": { + "en": "What current do the plugs with
Type 1 without cable (J1772)
offer?", + "nl": "Welke stroom levert de stekker van type
Type 1 zonder kabel (J1772)
?" + }, + "render": { + "en": "
Type 1 without cable (J1772)
outputs at most {socket:type1:current}A", + "nl": "
Type 1 zonder kabel (J1772)
levert een stroom van maximaal {socket:type1:current}A" + }, + "freeform": { + "key": "socket:type1:current", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:type1:current=32 A", + "then": { + "en": "
Type 1 without cable (J1772)
outputs at most 32 A", + "nl": "
Type 1 zonder kabel (J1772)
levert een stroom van maximaal 32 A" + } + } + ], + "condition": { + "and": [ + "socket:type1~*", + "socket:type1!=0" + ] + } + }, + { + "id": "power-output-4", + "question": { + "en": "What power output does a single plug of type
Type 1 without cable (J1772)
offer?", + "nl": "Welk vermogen levert een enkele stekker van type
Type 1 zonder kabel (J1772)
?" + }, + "render": { + "en": "
Type 1 without cable (J1772)
outputs at most {socket:type1:output}", + "nl": "
Type 1 zonder kabel (J1772)
levert een vermogen van maximaal {socket:type1:output}" + }, + "freeform": { + "key": "socket:type1:output", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:type1:output=3.7 kw", + "then": { + "en": "
Type 1 without cable (J1772)
outputs at most 3.7 kw", + "nl": "
Type 1 zonder kabel (J1772)
levert een vermogen van maximaal 3.7 kw" + } + }, + { + "if": "socket:socket:type1:output=6.6 kw", + "then": { + "en": "
Type 1 without cable (J1772)
outputs at most 6.6 kw", + "nl": "
Type 1 zonder kabel (J1772)
levert een vermogen van maximaal 6.6 kw" + } + }, + { + "if": "socket:socket:type1:output=7 kw", + "then": { + "en": "
Type 1 without cable (J1772)
outputs at most 7 kw", + "nl": "
Type 1 zonder kabel (J1772)
levert een vermogen van maximaal 7 kw" + } + }, + { + "if": "socket:socket:type1:output=7.2 kw", + "then": { + "en": "
Type 1 without cable (J1772)
outputs at most 7.2 kw", + "nl": "
Type 1 zonder kabel (J1772)
levert een vermogen van maximaal 7.2 kw" + } + } + ], + "condition": { + "and": [ + "socket:type1~*", + "socket:type1!=0" + ] + } + }, + { + "id": "plugs-5", + "question": { + "en": "How much plugs of type
Type 1 CCS (aka Type 1 Combo)
are available here?", + "nl": "Hoeveel stekkers van type
Type 1 CCS (ook gekend als Type 1 Combo)
heeft dit oplaadpunt?" + }, + "render": { + "en": "There are {socket:type1_combo} plugs of type
Type 1 CCS (aka Type 1 Combo)
available here", + "nl": "Hier zijn {socket:type1_combo} stekkers van het type
Type 1 CCS (ook gekend als Type 1 Combo)
" + }, + "freeform": { + "key": "socket:type1_combo", + "type": "pnat" + }, + "condition": { + "and": [ + "socket:type1_combo~*", + "socket:type1_combo!=0" + ] + } + }, + { + "id": "voltage-5", + "question": { + "en": "What voltage do the plugs with
Type 1 CCS (aka Type 1 Combo)
offer?", + "nl": "Welke spanning levert de stekker van type
Type 1 CCS (ook gekend als Type 1 Combo)
" + }, + "render": { + "en": "
Type 1 CCS (aka Type 1 Combo)
outputs {socket:type1_combo:voltage} volt", + "nl": "
Type 1 CCS (ook gekend als Type 1 Combo)
heeft een spanning van {socket:type1_combo:voltage} volt" + }, + "freeform": { + "key": "socket:type1_combo:voltage", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:type1_combo:voltage=400 V", + "then": { + "en": "
Type 1 CCS (aka Type 1 Combo)
outputs 400 volt", + "nl": "
Type 1 CCS (ook gekend als Type 1 Combo)
heeft een spanning van 400 volt" + } + }, + { + "if": "socket:socket:type1_combo:voltage=1000 V", + "then": { + "en": "
Type 1 CCS (aka Type 1 Combo)
outputs 1000 volt", + "nl": "
Type 1 CCS (ook gekend als Type 1 Combo)
heeft een spanning van 1000 volt" + } + } + ], + "condition": { + "and": [ + "socket:type1_combo~*", + "socket:type1_combo!=0" + ] + } + }, + { + "id": "current-5", + "question": { + "en": "What current do the plugs with
Type 1 CCS (aka Type 1 Combo)
offer?", + "nl": "Welke stroom levert de stekker van type
Type 1 CCS (ook gekend als Type 1 Combo)
?" + }, + "render": { + "en": "
Type 1 CCS (aka Type 1 Combo)
outputs at most {socket:type1_combo:current}A", + "nl": "
Type 1 CCS (ook gekend als Type 1 Combo)
levert een stroom van maximaal {socket:type1_combo:current}A" + }, + "freeform": { + "key": "socket:type1_combo:current", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:type1_combo:current=50 A", + "then": { + "en": "
Type 1 CCS (aka Type 1 Combo)
outputs at most 50 A", + "nl": "
Type 1 CCS (ook gekend als Type 1 Combo)
levert een stroom van maximaal 50 A" + } + }, + { + "if": "socket:socket:type1_combo:current=125 A", + "then": { + "en": "
Type 1 CCS (aka Type 1 Combo)
outputs at most 125 A", + "nl": "
Type 1 CCS (ook gekend als Type 1 Combo)
levert een stroom van maximaal 125 A" + } + } + ], + "condition": { + "and": [ + "socket:type1_combo~*", + "socket:type1_combo!=0" + ] + } + }, + { + "id": "power-output-5", + "question": { + "en": "What power output does a single plug of type
Type 1 CCS (aka Type 1 Combo)
offer?", + "nl": "Welk vermogen levert een enkele stekker van type
Type 1 CCS (ook gekend als Type 1 Combo)
?" + }, + "render": { + "en": "
Type 1 CCS (aka Type 1 Combo)
outputs at most {socket:type1_combo:output}", + "nl": "
Type 1 CCS (ook gekend als Type 1 Combo)
levert een vermogen van maximaal {socket:type1_combo:output}" + }, + "freeform": { + "key": "socket:type1_combo:output", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:type1_combo:output=50 kw", + "then": { + "en": "
Type 1 CCS (aka Type 1 Combo)
outputs at most 50 kw", + "nl": "
Type 1 CCS (ook gekend als Type 1 Combo)
levert een vermogen van maximaal 50 kw" + } + }, + { + "if": "socket:socket:type1_combo:output=62.5 kw", + "then": { + "en": "
Type 1 CCS (aka Type 1 Combo)
outputs at most 62.5 kw", + "nl": "
Type 1 CCS (ook gekend als Type 1 Combo)
levert een vermogen van maximaal 62.5 kw" + } + }, + { + "if": "socket:socket:type1_combo:output=150 kw", + "then": { + "en": "
Type 1 CCS (aka Type 1 Combo)
outputs at most 150 kw", + "nl": "
Type 1 CCS (ook gekend als Type 1 Combo)
levert een vermogen van maximaal 150 kw" + } + }, + { + "if": "socket:socket:type1_combo:output=350 kw", + "then": { + "en": "
Type 1 CCS (aka Type 1 Combo)
outputs at most 350 kw", + "nl": "
Type 1 CCS (ook gekend als Type 1 Combo)
levert een vermogen van maximaal 350 kw" + } + } + ], + "condition": { + "and": [ + "socket:type1_combo~*", + "socket:type1_combo!=0" + ] + } + }, + { + "id": "plugs-6", + "question": { + "en": "How much plugs of type
Tesla Supercharger
are available here?", + "nl": "Hoeveel stekkers van type
Tesla Supercharger
heeft dit oplaadpunt?" + }, + "render": { + "en": "There are {socket:tesla_supercharger} plugs of type
Tesla Supercharger
available here", + "nl": "Hier zijn {socket:tesla_supercharger} stekkers van het type
Tesla Supercharger
" + }, + "freeform": { + "key": "socket:tesla_supercharger", + "type": "pnat" + }, + "condition": { + "and": [ + "socket:tesla_supercharger~*", + "socket:tesla_supercharger!=0" + ] + } + }, + { + "id": "voltage-6", + "question": { + "en": "What voltage do the plugs with
Tesla Supercharger
offer?", + "nl": "Welke spanning levert de stekker van type
Tesla Supercharger
" + }, + "render": { + "en": "
Tesla Supercharger
outputs {socket:tesla_supercharger:voltage} volt", + "nl": "
Tesla Supercharger
heeft een spanning van {socket:tesla_supercharger:voltage} volt" + }, + "freeform": { + "key": "socket:tesla_supercharger:voltage", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:tesla_supercharger:voltage=480 V", + "then": { + "en": "
Tesla Supercharger
outputs 480 volt", + "nl": "
Tesla Supercharger
heeft een spanning van 480 volt" + } + } + ], + "condition": { + "and": [ + "socket:tesla_supercharger~*", + "socket:tesla_supercharger!=0" + ] + } + }, + { + "id": "current-6", + "question": { + "en": "What current do the plugs with
Tesla Supercharger
offer?", + "nl": "Welke stroom levert de stekker van type
Tesla Supercharger
?" + }, + "render": { + "en": "
Tesla Supercharger
outputs at most {socket:tesla_supercharger:current}A", + "nl": "
Tesla Supercharger
levert een stroom van maximaal {socket:tesla_supercharger:current}A" + }, + "freeform": { + "key": "socket:tesla_supercharger:current", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:tesla_supercharger:current=125 A", + "then": { + "en": "
Tesla Supercharger
outputs at most 125 A", + "nl": "
Tesla Supercharger
levert een stroom van maximaal 125 A" + } + }, + { + "if": "socket:socket:tesla_supercharger:current=350 A", + "then": { + "en": "
Tesla Supercharger
outputs at most 350 A", + "nl": "
Tesla Supercharger
levert een stroom van maximaal 350 A" + } + } + ], + "condition": { + "and": [ + "socket:tesla_supercharger~*", + "socket:tesla_supercharger!=0" + ] + } + }, + { + "id": "power-output-6", + "question": { + "en": "What power output does a single plug of type
Tesla Supercharger
offer?", + "nl": "Welk vermogen levert een enkele stekker van type
Tesla Supercharger
?" + }, + "render": { + "en": "
Tesla Supercharger
outputs at most {socket:tesla_supercharger:output}", + "nl": "
Tesla Supercharger
levert een vermogen van maximaal {socket:tesla_supercharger:output}" + }, + "freeform": { + "key": "socket:tesla_supercharger:output", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:tesla_supercharger:output=120 kw", + "then": { + "en": "
Tesla Supercharger
outputs at most 120 kw", + "nl": "
Tesla Supercharger
levert een vermogen van maximaal 120 kw" + } + }, + { + "if": "socket:socket:tesla_supercharger:output=150 kw", + "then": { + "en": "
Tesla Supercharger
outputs at most 150 kw", + "nl": "
Tesla Supercharger
levert een vermogen van maximaal 150 kw" + } + }, + { + "if": "socket:socket:tesla_supercharger:output=250 kw", + "then": { + "en": "
Tesla Supercharger
outputs at most 250 kw", + "nl": "
Tesla Supercharger
levert een vermogen van maximaal 250 kw" + } + } + ], + "condition": { + "and": [ + "socket:tesla_supercharger~*", + "socket:tesla_supercharger!=0" + ] + } + }, + { + "id": "plugs-7", + "question": { + "en": "How much plugs of type
Type 2 (mennekes)
are available here?", + "nl": "Hoeveel stekkers van type
Type 2 (mennekes)
heeft dit oplaadpunt?" + }, + "render": { + "en": "There are {socket:type2} plugs of type
Type 2 (mennekes)
available here", + "nl": "Hier zijn {socket:type2} stekkers van het type
Type 2 (mennekes)
" + }, + "freeform": { + "key": "socket:type2", + "type": "pnat" + }, + "condition": { + "and": [ + "socket:type2~*", + "socket:type2!=0" + ] + } + }, + { + "id": "voltage-7", + "question": { + "en": "What voltage do the plugs with
Type 2 (mennekes)
offer?", + "nl": "Welke spanning levert de stekker van type
Type 2 (mennekes)
" + }, + "render": { + "en": "
Type 2 (mennekes)
outputs {socket:type2:voltage} volt", + "nl": "
Type 2 (mennekes)
heeft een spanning van {socket:type2:voltage} volt" + }, + "freeform": { + "key": "socket:type2:voltage", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:type2:voltage=230 V", + "then": { + "en": "
Type 2 (mennekes)
outputs 230 volt", + "nl": "
Type 2 (mennekes)
heeft een spanning van 230 volt" + } + }, + { + "if": "socket:socket:type2:voltage=400 V", + "then": { + "en": "
Type 2 (mennekes)
outputs 400 volt", + "nl": "
Type 2 (mennekes)
heeft een spanning van 400 volt" + } + } + ], + "condition": { + "and": [ + "socket:type2~*", + "socket:type2!=0" + ] + } + }, + { + "id": "current-7", + "question": { + "en": "What current do the plugs with
Type 2 (mennekes)
offer?", + "nl": "Welke stroom levert de stekker van type
Type 2 (mennekes)
?" + }, + "render": { + "en": "
Type 2 (mennekes)
outputs at most {socket:type2:current}A", + "nl": "
Type 2 (mennekes)
levert een stroom van maximaal {socket:type2:current}A" + }, + "freeform": { + "key": "socket:type2:current", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:type2:current=16 A", + "then": { + "en": "
Type 2 (mennekes)
outputs at most 16 A", + "nl": "
Type 2 (mennekes)
levert een stroom van maximaal 16 A" + } + }, + { + "if": "socket:socket:type2:current=32 A", + "then": { + "en": "
Type 2 (mennekes)
outputs at most 32 A", + "nl": "
Type 2 (mennekes)
levert een stroom van maximaal 32 A" + } + } + ], + "condition": { + "and": [ + "socket:type2~*", + "socket:type2!=0" + ] + } + }, + { + "id": "power-output-7", + "question": { + "en": "What power output does a single plug of type
Type 2 (mennekes)
offer?", + "nl": "Welk vermogen levert een enkele stekker van type
Type 2 (mennekes)
?" + }, + "render": { + "en": "
Type 2 (mennekes)
outputs at most {socket:type2:output}", + "nl": "
Type 2 (mennekes)
levert een vermogen van maximaal {socket:type2:output}" + }, + "freeform": { + "key": "socket:type2:output", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:type2:output=11 kw", + "then": { + "en": "
Type 2 (mennekes)
outputs at most 11 kw", + "nl": "
Type 2 (mennekes)
levert een vermogen van maximaal 11 kw" + } + }, + { + "if": "socket:socket:type2:output=22 kw", + "then": { + "en": "
Type 2 (mennekes)
outputs at most 22 kw", + "nl": "
Type 2 (mennekes)
levert een vermogen van maximaal 22 kw" + } + } + ], + "condition": { + "and": [ + "socket:type2~*", + "socket:type2!=0" + ] + } + }, + { + "id": "plugs-8", + "question": { + "en": "How much plugs of type
Type 2 CCS (mennekes)
are available here?", + "nl": "Hoeveel stekkers van type
Type 2 CCS (mennekes)
heeft dit oplaadpunt?" + }, + "render": { + "en": "There are {socket:type2_combo} plugs of type
Type 2 CCS (mennekes)
available here", + "nl": "Hier zijn {socket:type2_combo} stekkers van het type
Type 2 CCS (mennekes)
" + }, + "freeform": { + "key": "socket:type2_combo", + "type": "pnat" + }, + "condition": { + "and": [ + "socket:type2_combo~*", + "socket:type2_combo!=0" + ] + } + }, + { + "id": "voltage-8", + "question": { + "en": "What voltage do the plugs with
Type 2 CCS (mennekes)
offer?", + "nl": "Welke spanning levert de stekker van type
Type 2 CCS (mennekes)
" + }, + "render": { + "en": "
Type 2 CCS (mennekes)
outputs {socket:type2_combo:voltage} volt", + "nl": "
Type 2 CCS (mennekes)
heeft een spanning van {socket:type2_combo:voltage} volt" + }, + "freeform": { + "key": "socket:type2_combo:voltage", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:type2_combo:voltage=500 V", + "then": { + "en": "
Type 2 CCS (mennekes)
outputs 500 volt", + "nl": "
Type 2 CCS (mennekes)
heeft een spanning van 500 volt" + } + }, + { + "if": "socket:socket:type2_combo:voltage=920 V", + "then": { + "en": "
Type 2 CCS (mennekes)
outputs 920 volt", + "nl": "
Type 2 CCS (mennekes)
heeft een spanning van 920 volt" + } + } + ], + "condition": { + "and": [ + "socket:type2_combo~*", + "socket:type2_combo!=0" + ] + } + }, + { + "id": "current-8", + "question": { + "en": "What current do the plugs with
Type 2 CCS (mennekes)
offer?", + "nl": "Welke stroom levert de stekker van type
Type 2 CCS (mennekes)
?" + }, + "render": { + "en": "
Type 2 CCS (mennekes)
outputs at most {socket:type2_combo:current}A", + "nl": "
Type 2 CCS (mennekes)
levert een stroom van maximaal {socket:type2_combo:current}A" + }, + "freeform": { + "key": "socket:type2_combo:current", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:type2_combo:current=125 A", + "then": { + "en": "
Type 2 CCS (mennekes)
outputs at most 125 A", + "nl": "
Type 2 CCS (mennekes)
levert een stroom van maximaal 125 A" + } + }, + { + "if": "socket:socket:type2_combo:current=350 A", + "then": { + "en": "
Type 2 CCS (mennekes)
outputs at most 350 A", + "nl": "
Type 2 CCS (mennekes)
levert een stroom van maximaal 350 A" + } + } + ], + "condition": { + "and": [ + "socket:type2_combo~*", + "socket:type2_combo!=0" + ] + } + }, + { + "id": "power-output-8", + "question": { + "en": "What power output does a single plug of type
Type 2 CCS (mennekes)
offer?", + "nl": "Welk vermogen levert een enkele stekker van type
Type 2 CCS (mennekes)
?" + }, + "render": { + "en": "
Type 2 CCS (mennekes)
outputs at most {socket:type2_combo:output}", + "nl": "
Type 2 CCS (mennekes)
levert een vermogen van maximaal {socket:type2_combo:output}" + }, + "freeform": { + "key": "socket:type2_combo:output", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:type2_combo:output=50 kw", + "then": { + "en": "
Type 2 CCS (mennekes)
outputs at most 50 kw", + "nl": "
Type 2 CCS (mennekes)
levert een vermogen van maximaal 50 kw" + } + } + ], + "condition": { + "and": [ + "socket:type2_combo~*", + "socket:type2_combo!=0" + ] + } + }, + { + "id": "plugs-9", + "question": { + "en": "How much plugs of type
Type 2 with cable (mennekes)
are available here?", + "nl": "Hoeveel stekkers van type
Type 2 met kabel (J1772)
heeft dit oplaadpunt?" + }, + "render": { + "en": "There are {socket:type2_cable} plugs of type
Type 2 with cable (mennekes)
available here", + "nl": "Hier zijn {socket:type2_cable} stekkers van het type
Type 2 met kabel (J1772)
" + }, + "freeform": { + "key": "socket:type2_cable", + "type": "pnat" + }, + "condition": { + "and": [ + "socket:type2_cable~*", + "socket:type2_cable!=0" + ] + } + }, + { + "id": "voltage-9", + "question": { + "en": "What voltage do the plugs with
Type 2 with cable (mennekes)
offer?", + "nl": "Welke spanning levert de stekker van type
Type 2 met kabel (J1772)
" + }, + "render": { + "en": "
Type 2 with cable (mennekes)
outputs {socket:type2_cable:voltage} volt", + "nl": "
Type 2 met kabel (J1772)
heeft een spanning van {socket:type2_cable:voltage} volt" + }, + "freeform": { + "key": "socket:type2_cable:voltage", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:type2_cable:voltage=230 V", + "then": { + "en": "
Type 2 with cable (mennekes)
outputs 230 volt", + "nl": "
Type 2 met kabel (J1772)
heeft een spanning van 230 volt" + } + }, + { + "if": "socket:socket:type2_cable:voltage=400 V", + "then": { + "en": "
Type 2 with cable (mennekes)
outputs 400 volt", + "nl": "
Type 2 met kabel (J1772)
heeft een spanning van 400 volt" + } + } + ], + "condition": { + "and": [ + "socket:type2_cable~*", + "socket:type2_cable!=0" + ] + } + }, + { + "id": "current-9", + "question": { + "en": "What current do the plugs with
Type 2 with cable (mennekes)
offer?", + "nl": "Welke stroom levert de stekker van type
Type 2 met kabel (J1772)
?" + }, + "render": { + "en": "
Type 2 with cable (mennekes)
outputs at most {socket:type2_cable:current}A", + "nl": "
Type 2 met kabel (J1772)
levert een stroom van maximaal {socket:type2_cable:current}A" + }, + "freeform": { + "key": "socket:type2_cable:current", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:type2_cable:current=16 A", + "then": { + "en": "
Type 2 with cable (mennekes)
outputs at most 16 A", + "nl": "
Type 2 met kabel (J1772)
levert een stroom van maximaal 16 A" + } + }, + { + "if": "socket:socket:type2_cable:current=32 A", + "then": { + "en": "
Type 2 with cable (mennekes)
outputs at most 32 A", + "nl": "
Type 2 met kabel (J1772)
levert een stroom van maximaal 32 A" + } + } + ], + "condition": { + "and": [ + "socket:type2_cable~*", + "socket:type2_cable!=0" + ] + } + }, + { + "id": "power-output-9", + "question": { + "en": "What power output does a single plug of type
Type 2 with cable (mennekes)
offer?", + "nl": "Welk vermogen levert een enkele stekker van type
Type 2 met kabel (J1772)
?" + }, + "render": { + "en": "
Type 2 with cable (mennekes)
outputs at most {socket:type2_cable:output}", + "nl": "
Type 2 met kabel (J1772)
levert een vermogen van maximaal {socket:type2_cable:output}" + }, + "freeform": { + "key": "socket:type2_cable:output", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:type2_cable:output=11 kw", + "then": { + "en": "
Type 2 with cable (mennekes)
outputs at most 11 kw", + "nl": "
Type 2 met kabel (J1772)
levert een vermogen van maximaal 11 kw" + } + }, + { + "if": "socket:socket:type2_cable:output=22 kw", + "then": { + "en": "
Type 2 with cable (mennekes)
outputs at most 22 kw", + "nl": "
Type 2 met kabel (J1772)
levert een vermogen van maximaal 22 kw" + } + } + ], + "condition": { + "and": [ + "socket:type2_cable~*", + "socket:type2_cable!=0" + ] + } + }, + { + "id": "plugs-10", + "question": { + "en": "How much plugs of type
Tesla Supercharger CCS (a branded type2_css)
are available here?", + "nl": "Hoeveel stekkers van type
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
heeft dit oplaadpunt?" + }, + "render": { + "en": "There are {socket:tesla_supercharger_ccs} plugs of type
Tesla Supercharger CCS (a branded type2_css)
available here", + "nl": "Hier zijn {socket:tesla_supercharger_ccs} stekkers van het type
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
" + }, + "freeform": { + "key": "socket:tesla_supercharger_ccs", + "type": "pnat" + }, + "condition": { + "and": [ + "socket:tesla_supercharger_ccs~*", + "socket:tesla_supercharger_ccs!=0" + ] + } + }, + { + "id": "voltage-10", + "question": { + "en": "What voltage do the plugs with
Tesla Supercharger CCS (a branded type2_css)
offer?", + "nl": "Welke spanning levert de stekker van type
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
" + }, + "render": { + "en": "
Tesla Supercharger CCS (a branded type2_css)
outputs {socket:tesla_supercharger_ccs:voltage} volt", + "nl": "
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
heeft een spanning van {socket:tesla_supercharger_ccs:voltage} volt" + }, + "freeform": { + "key": "socket:tesla_supercharger_ccs:voltage", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:tesla_supercharger_ccs:voltage=500 V", + "then": { + "en": "
Tesla Supercharger CCS (a branded type2_css)
outputs 500 volt", + "nl": "
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
heeft een spanning van 500 volt" + } + }, + { + "if": "socket:socket:tesla_supercharger_ccs:voltage=920 V", + "then": { + "en": "
Tesla Supercharger CCS (a branded type2_css)
outputs 920 volt", + "nl": "
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
heeft een spanning van 920 volt" + } + } + ], + "condition": { + "and": [ + "socket:tesla_supercharger_ccs~*", + "socket:tesla_supercharger_ccs!=0" + ] + } + }, + { + "id": "current-10", + "question": { + "en": "What current do the plugs with
Tesla Supercharger CCS (a branded type2_css)
offer?", + "nl": "Welke stroom levert de stekker van type
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
?" + }, + "render": { + "en": "
Tesla Supercharger CCS (a branded type2_css)
outputs at most {socket:tesla_supercharger_ccs:current}A", + "nl": "
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
levert een stroom van maximaal {socket:tesla_supercharger_ccs:current}A" + }, + "freeform": { + "key": "socket:tesla_supercharger_ccs:current", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:tesla_supercharger_ccs:current=125 A", + "then": { + "en": "
Tesla Supercharger CCS (a branded type2_css)
outputs at most 125 A", + "nl": "
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
levert een stroom van maximaal 125 A" + } + }, + { + "if": "socket:socket:tesla_supercharger_ccs:current=350 A", + "then": { + "en": "
Tesla Supercharger CCS (a branded type2_css)
outputs at most 350 A", + "nl": "
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
levert een stroom van maximaal 350 A" + } + } + ], + "condition": { + "and": [ + "socket:tesla_supercharger_ccs~*", + "socket:tesla_supercharger_ccs!=0" + ] + } + }, + { + "id": "power-output-10", + "question": { + "en": "What power output does a single plug of type
Tesla Supercharger CCS (a branded type2_css)
offer?", + "nl": "Welk vermogen levert een enkele stekker van type
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
?" + }, + "render": { + "en": "
Tesla Supercharger CCS (a branded type2_css)
outputs at most {socket:tesla_supercharger_ccs:output}", + "nl": "
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
levert een vermogen van maximaal {socket:tesla_supercharger_ccs:output}" + }, + "freeform": { + "key": "socket:tesla_supercharger_ccs:output", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:tesla_supercharger_ccs:output=50 kw", + "then": { + "en": "
Tesla Supercharger CCS (a branded type2_css)
outputs at most 50 kw", + "nl": "
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
levert een vermogen van maximaal 50 kw" + } + } + ], + "condition": { + "and": [ + "socket:tesla_supercharger_ccs~*", + "socket:tesla_supercharger_ccs!=0" + ] + } + }, + { + "id": "plugs-11", + "question": { + "en": "How much plugs of type
Tesla Supercharger (destination)
are available here?", + "nl": "Hoeveel stekkers van type
Tesla Supercharger (destination)
heeft dit oplaadpunt?" + }, + "render": { + "en": "There are {socket:tesla_destination} plugs of type
Tesla Supercharger (destination)
available here", + "nl": "Hier zijn {socket:tesla_destination} stekkers van het type
Tesla Supercharger (destination)
" + }, + "freeform": { + "key": "socket:tesla_destination", + "type": "pnat" + }, + "condition": { + "and": [ + "socket:tesla_destination~*", + "socket:tesla_destination!=0" + ] + } + }, + { + "id": "voltage-11", + "question": { + "en": "What voltage do the plugs with
Tesla Supercharger (destination)
offer?", + "nl": "Welke spanning levert de stekker van type
Tesla Supercharger (destination)
" + }, + "render": { + "en": "
Tesla Supercharger (destination)
outputs {socket:tesla_destination:voltage} volt", + "nl": "
Tesla Supercharger (destination)
heeft een spanning van {socket:tesla_destination:voltage} volt" + }, + "freeform": { + "key": "socket:tesla_destination:voltage", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:tesla_destination:voltage=480 V", + "then": { + "en": "
Tesla Supercharger (destination)
outputs 480 volt", + "nl": "
Tesla Supercharger (destination)
heeft een spanning van 480 volt" + } + } + ], + "condition": { + "and": [ + "socket:tesla_destination~*", + "socket:tesla_destination!=0" + ] + } + }, + { + "id": "current-11", + "question": { + "en": "What current do the plugs with
Tesla Supercharger (destination)
offer?", + "nl": "Welke stroom levert de stekker van type
Tesla Supercharger (destination)
?" + }, + "render": { + "en": "
Tesla Supercharger (destination)
outputs at most {socket:tesla_destination:current}A", + "nl": "
Tesla Supercharger (destination)
levert een stroom van maximaal {socket:tesla_destination:current}A" + }, + "freeform": { + "key": "socket:tesla_destination:current", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:tesla_destination:current=125 A", + "then": { + "en": "
Tesla Supercharger (destination)
outputs at most 125 A", + "nl": "
Tesla Supercharger (destination)
levert een stroom van maximaal 125 A" + } + }, + { + "if": "socket:socket:tesla_destination:current=350 A", + "then": { + "en": "
Tesla Supercharger (destination)
outputs at most 350 A", + "nl": "
Tesla Supercharger (destination)
levert een stroom van maximaal 350 A" + } + } + ], + "condition": { + "and": [ + "socket:tesla_destination~*", + "socket:tesla_destination!=0" + ] + } + }, + { + "id": "power-output-11", + "question": { + "en": "What power output does a single plug of type
Tesla Supercharger (destination)
offer?", + "nl": "Welk vermogen levert een enkele stekker van type
Tesla Supercharger (destination)
?" + }, + "render": { + "en": "
Tesla Supercharger (destination)
outputs at most {socket:tesla_destination:output}", + "nl": "
Tesla Supercharger (destination)
levert een vermogen van maximaal {socket:tesla_destination:output}" + }, + "freeform": { + "key": "socket:tesla_destination:output", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:tesla_destination:output=120 kw", + "then": { + "en": "
Tesla Supercharger (destination)
outputs at most 120 kw", + "nl": "
Tesla Supercharger (destination)
levert een vermogen van maximaal 120 kw" + } + }, + { + "if": "socket:socket:tesla_destination:output=150 kw", + "then": { + "en": "
Tesla Supercharger (destination)
outputs at most 150 kw", + "nl": "
Tesla Supercharger (destination)
levert een vermogen van maximaal 150 kw" + } + }, + { + "if": "socket:socket:tesla_destination:output=250 kw", + "then": { + "en": "
Tesla Supercharger (destination)
outputs at most 250 kw", + "nl": "
Tesla Supercharger (destination)
levert een vermogen van maximaal 250 kw" + } + } + ], + "condition": { + "and": [ + "socket:tesla_destination~*", + "socket:tesla_destination!=0" + ] + } + }, + { + "id": "plugs-12", + "question": { + "en": "How much plugs of type
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
are available here?", + "nl": "Hoeveel stekkers van type
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
heeft dit oplaadpunt?" + }, + "render": { + "en": "There are {socket:tesla_destination} plugs of type
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
available here", + "nl": "Hier zijn {socket:tesla_destination} stekkers van het type
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
" + }, + "freeform": { + "key": "socket:tesla_destination", + "type": "pnat" + }, + "condition": { + "and": [ + "socket:tesla_destination~*", + "socket:tesla_destination!=0" + ] + } + }, + { + "id": "voltage-12", + "question": { + "en": "What voltage do the plugs with
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
offer?", + "nl": "Welke spanning levert de stekker van type
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
" + }, + "render": { + "en": "
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
outputs {socket:tesla_destination:voltage} volt", + "nl": "
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
heeft een spanning van {socket:tesla_destination:voltage} volt" + }, + "freeform": { + "key": "socket:tesla_destination:voltage", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:tesla_destination:voltage=230 V", + "then": { + "en": "
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
outputs 230 volt", + "nl": "
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
heeft een spanning van 230 volt" + } + }, + { + "if": "socket:socket:tesla_destination:voltage=400 V", + "then": { + "en": "
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
outputs 400 volt", + "nl": "
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
heeft een spanning van 400 volt" + } + } + ], + "condition": { + "and": [ + "socket:tesla_destination~*", + "socket:tesla_destination!=0" + ] + } + }, + { + "id": "current-12", + "question": { + "en": "What current do the plugs with
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
offer?", + "nl": "Welke stroom levert de stekker van type
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
?" + }, + "render": { + "en": "
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
outputs at most {socket:tesla_destination:current}A", + "nl": "
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
levert een stroom van maximaal {socket:tesla_destination:current}A" + }, + "freeform": { + "key": "socket:tesla_destination:current", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:tesla_destination:current=16 A", + "then": { + "en": "
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
outputs at most 16 A", + "nl": "
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
levert een stroom van maximaal 16 A" + } + }, + { + "if": "socket:socket:tesla_destination:current=32 A", + "then": { + "en": "
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
outputs at most 32 A", + "nl": "
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
levert een stroom van maximaal 32 A" + } + } + ], + "condition": { + "and": [ + "socket:tesla_destination~*", + "socket:tesla_destination!=0" + ] + } + }, + { + "id": "power-output-12", + "question": { + "en": "What power output does a single plug of type
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
offer?", + "nl": "Welk vermogen levert een enkele stekker van type
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
?" + }, + "render": { + "en": "
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
outputs at most {socket:tesla_destination:output}", + "nl": "
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
levert een vermogen van maximaal {socket:tesla_destination:output}" + }, + "freeform": { + "key": "socket:tesla_destination:output", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:tesla_destination:output=11 kw", + "then": { + "en": "
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
outputs at most 11 kw", + "nl": "
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
levert een vermogen van maximaal 11 kw" + } + }, + { + "if": "socket:socket:tesla_destination:output=22 kw", + "then": { + "en": "
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
outputs at most 22 kw", + "nl": "
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
levert een vermogen van maximaal 22 kw" + } + } + ], + "condition": { + "and": [ + "socket:tesla_destination~*", + "socket:tesla_destination!=0" + ] + } + }, + { + "id": "plugs-13", + "question": { + "en": "How much plugs of type
USB to charge phones and small electronics
are available here?", + "nl": "Hoeveel stekkers van type
USB om GSMs en kleine electronica op te laden
heeft dit oplaadpunt?" + }, + "render": { + "en": "There are {socket:USB-A} plugs of type
USB to charge phones and small electronics
available here", + "nl": "Hier zijn {socket:USB-A} stekkers van het type
USB om GSMs en kleine electronica op te laden
" + }, + "freeform": { + "key": "socket:USB-A", + "type": "pnat" + }, + "condition": { + "and": [ + "socket:USB-A~*", + "socket:USB-A!=0" + ] + } + }, + { + "id": "voltage-13", + "question": { + "en": "What voltage do the plugs with
USB to charge phones and small electronics
offer?", + "nl": "Welke spanning levert de stekker van type
USB om GSMs en kleine electronica op te laden
" + }, + "render": { + "en": "
USB to charge phones and small electronics
outputs {socket:USB-A:voltage} volt", + "nl": "
USB om GSMs en kleine electronica op te laden
heeft een spanning van {socket:USB-A:voltage} volt" + }, + "freeform": { + "key": "socket:USB-A:voltage", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:USB-A:voltage=5 V", + "then": { + "en": "
USB to charge phones and small electronics
outputs 5 volt", + "nl": "
USB om GSMs en kleine electronica op te laden
heeft een spanning van 5 volt" + } + } + ], + "condition": { + "and": [ + "socket:USB-A~*", + "socket:USB-A!=0" + ] + } + }, + { + "id": "current-13", + "question": { + "en": "What current do the plugs with
USB to charge phones and small electronics
offer?", + "nl": "Welke stroom levert de stekker van type
USB om GSMs en kleine electronica op te laden
?" + }, + "render": { + "en": "
USB to charge phones and small electronics
outputs at most {socket:USB-A:current}A", + "nl": "
USB om GSMs en kleine electronica op te laden
levert een stroom van maximaal {socket:USB-A:current}A" + }, + "freeform": { + "key": "socket:USB-A:current", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:USB-A:current=1 A", + "then": { + "en": "
USB to charge phones and small electronics
outputs at most 1 A", + "nl": "
USB om GSMs en kleine electronica op te laden
levert een stroom van maximaal 1 A" + } + }, + { + "if": "socket:socket:USB-A:current=2 A", + "then": { + "en": "
USB to charge phones and small electronics
outputs at most 2 A", + "nl": "
USB om GSMs en kleine electronica op te laden
levert een stroom van maximaal 2 A" + } + } + ], + "condition": { + "and": [ + "socket:USB-A~*", + "socket:USB-A!=0" + ] + } + }, + { + "id": "power-output-13", + "question": { + "en": "What power output does a single plug of type
USB to charge phones and small electronics
offer?", + "nl": "Welk vermogen levert een enkele stekker van type
USB om GSMs en kleine electronica op te laden
?" + }, + "render": { + "en": "
USB to charge phones and small electronics
outputs at most {socket:USB-A:output}", + "nl": "
USB om GSMs en kleine electronica op te laden
levert een vermogen van maximaal {socket:USB-A:output}" + }, + "freeform": { + "key": "socket:USB-A:output", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:USB-A:output=5w", + "then": { + "en": "
USB to charge phones and small electronics
outputs at most 5w", + "nl": "
USB om GSMs en kleine electronica op te laden
levert een vermogen van maximaal 5w" + } + }, + { + "if": "socket:socket:USB-A:output=10w", + "then": { + "en": "
USB to charge phones and small electronics
outputs at most 10w", + "nl": "
USB om GSMs en kleine electronica op te laden
levert een vermogen van maximaal 10w" + } + } + ], + "condition": { + "and": [ + "socket:USB-A~*", + "socket:USB-A!=0" + ] + } + }, + { + "id": "plugs-14", + "question": { + "en": "How much plugs of type
Bosch Active Connect with cable
are available here?", + "nl": "Hoeveel stekkers van type
Bosch Active Connect aan een kabel
heeft dit oplaadpunt?" + }, + "render": { + "en": "There are {socket:bosch_3pin} plugs of type
Bosch Active Connect with cable
available here", + "nl": "Hier zijn {socket:bosch_3pin} stekkers van het type
Bosch Active Connect aan een kabel
" + }, + "freeform": { + "key": "socket:bosch_3pin", + "type": "pnat" + }, + "condition": { + "and": [ + "socket:bosch_3pin~*", + "socket:bosch_3pin!=0" + ] + } + }, + { + "id": "voltage-14", + "question": { + "en": "What voltage do the plugs with
Bosch Active Connect with cable
offer?", + "nl": "Welke spanning levert de stekker van type
Bosch Active Connect aan een kabel
" + }, + "render": { + "en": "
Bosch Active Connect with cable
outputs {socket:bosch_3pin:voltage} volt", + "nl": "
Bosch Active Connect aan een kabel
heeft een spanning van {socket:bosch_3pin:voltage} volt" + }, + "freeform": { + "key": "socket:bosch_3pin:voltage", + "type": "pfloat" + }, + "mappings": [], + "condition": { + "and": [ + "socket:bosch_3pin~*", + "socket:bosch_3pin!=0" + ] + } + }, + { + "id": "current-14", + "question": { + "en": "What current do the plugs with
Bosch Active Connect with cable
offer?", + "nl": "Welke stroom levert de stekker van type
Bosch Active Connect aan een kabel
?" + }, + "render": { + "en": "
Bosch Active Connect with cable
outputs at most {socket:bosch_3pin:current}A", + "nl": "
Bosch Active Connect aan een kabel
levert een stroom van maximaal {socket:bosch_3pin:current}A" + }, + "freeform": { + "key": "socket:bosch_3pin:current", + "type": "pfloat" + }, + "mappings": [], + "condition": { + "and": [ + "socket:bosch_3pin~*", + "socket:bosch_3pin!=0" + ] + } + }, + { + "id": "power-output-14", + "question": { + "en": "What power output does a single plug of type
Bosch Active Connect with cable
offer?", + "nl": "Welk vermogen levert een enkele stekker van type
Bosch Active Connect aan een kabel
?" + }, + "render": { + "en": "
Bosch Active Connect with cable
outputs at most {socket:bosch_3pin:output}", + "nl": "
Bosch Active Connect aan een kabel
levert een vermogen van maximaal {socket:bosch_3pin:output}" + }, + "freeform": { + "key": "socket:bosch_3pin:output", + "type": "pfloat" + }, + "mappings": [], + "condition": { + "and": [ + "socket:bosch_3pin~*", + "socket:bosch_3pin!=0" + ] + } + }, + { + "id": "Authentication", + "question": { + "en": "What kind of authentication is available at the charging station?", + "it": "Quali sono gli orari di apertura di questa stazione di ricarica?", + "ja": "この充電ステーションはいつオープンしますか?", + "nb_NO": "Når åpnet denne ladestasjonen?", + "ru": "В какое время работает эта зарядная станция?", + "zh_Hant": "何時是充電站開放使用的時間?" + }, + "multiAnswer": true, + "mappings": [ + { + "if": "authentication:membership_card=yes", + "ifnot": "authentication:membership_card=no", + "then": { + "en": "Authentication by a membership card" + } + }, + { + "if": "authentication:app=yes", + "ifnot": "authentication:app=no", + "then": { + "en": "Authentication by an app" + } + }, + { + "if": "authentication:phone_call=yes", + "ifnot": "authentication:phone_call=no", + "then": { + "en": "Authentication via phone call is available" + } + }, + { + "if": "authentication:short_message=yes", + "ifnot": "authentication:short_message=no", + "then": { + "en": "Authentication via phone call is available" + } + }, + { + "if": "authentication:nfc=yes", + "ifnot": "authentication:nfc=no", + "then": { + "en": "Authentication via NFC is available" + } + }, + { + "if": "authentication:money_card=yes", + "ifnot": "authentication:money_card=no", + "then": { + "en": "Authentication via Money Card is available" + } + }, + { + "if": "authentication:debit_card=yes", + "ifnot": "authentication:debit_card=no", + "then": { + "en": "Authentication via debit card is available" + } + }, + { + "if": "authentication:none=yes", + "ifnot": "authentication:none=no", + "then": { + "en": "No authentication is needed" + } + } + ] + }, + { + "id": "Auth phone", + "render": { + "en": "Authenticate by calling or SMS'ing to {authentication:phone_call:number}", + "it": "{network}", + "ja": "{network}", + "nb_NO": "{network}", + "ru": "{network}", + "zh_Hant": "{network}" + }, + "question": { + "en": "What's the phone number for authentication call or SMS?", + "it": "A quale rete appartiene questa stazione di ricarica?", + "ja": "この充電ステーションの運営チェーンはどこですか?", + "ru": "К какой сети относится эта станция?", + "zh_Hant": "充電站所屬的網路是?" + }, + "freeform": { + "key": "authentication:phone_call:number", + "type": "phone" + }, + "condition": { + "or": [ + "authentication:phone_call=yes", + "authentication:short_message=yes" + ] + }, + "it": { + "0": { + "then": "Non appartiene a una rete" + } + }, + "ja": { + "0": { + "then": "大規模な運営チェーンの一部ではない" + } + }, + "ru": { + "0": { + "then": "Не является частью более крупной сети" + } + }, + "zh_Hant": { + "0": { + "then": "不屬於大型網路" + } + } + }, + { + "id": "OH", + "render": "{opening_hours_table(opening_hours)}", + "freeform": { + "key": "opening_hours", + "type": "opening_hours" + }, + "question": { + "en": "When is this charging station opened?" + }, + "mappings": [ + { + "if": "opening_hours=24/7", + "then": { + "en": "24/7 opened (including holidays)" + } + } + ] + }, + { + "id": "fee/charge", + "question": { + "en": "How much does one have to pay to use this charging station?", + "nl": "Hoeveel kost het gebruik van dit oplaadpunt?" + }, + "freeform": { + "key": "charge", + "addExtraTags": [ + "fee=yes" + ] + }, + "render": { + "en": "Using this charging station costs {charge}", + "nl": "Dit oplaadpunt gebruiken kost {charge}" + }, + "mappings": [ + { + "if": { + "and": [ + "fee=no", + "charge=" + ] + }, + "then": { + "nl": "Gratis te gebruiken", + "en": "Free to use" + } + } + ] + }, + { + "id": "payment-options", + "builtin": "payment-options", + "override": { + "condition": { + "or": [ + "fee=yes", + "charge~*" + ] + }, + "mappings+": [ + { + "if": "payment:app=yes", + "ifnot": "payment:app=no", + "then": { + "en": "Payment is done using a dedicated app", + "nl": "Betalen via een app van het netwerk" } + }, + { + "if": "payment:membership_card=yes", + "ifnot": "payment:membership_card=no", + "then": { + "en": "Payment is done using a membership card", + "nl": "Betalen via een lidkaart van het netwerk" + } + } + ] + } + }, + { + "id": "maxstay", + "question": { + "en": "What is the maximum amount of time one is allowed to stay here?", + "nl": "Hoelang mag een voertuig hier blijven staan?" + }, + "freeform": { + "key": "maxstay" + }, + "render": { + "en": "One can stay at most {canonical(maxstay)}", + "nl": "De maximale parkeertijd hier is {canonical(maxstay)}" + }, + "mappings": [ + { + "if": "maxstay=unlimited", + "then": { + "en": "No timelimit on leaving your vehicle here", + "nl": "Geen maximum parkeertijd" + } } - ], - "wayHandling": 1, - "filter": [ + ] + }, + { + "id": "Network", + "render": { + "en": "Part of the network {network}" + }, + "question": { + "en": "Is this charging station part of a network?" + }, + "freeform": { + "key": "network" + }, + "mappings": [ { - "options": [ - { - "question": { - "en": "All vehicle types", - "nl": "Alle voertuigen" - } - }, - { - "question": { - "en": "Charging station for bicycles", - "nl": "Oplaadpunten voor fietsen" - }, - "osmTags": "bicycle=yes" - }, - { - "question": { - "en": "Charging station for cars", - "nl": "Oplaadpunten voor auto's" - }, - "osmTags": { - "or": [ - "car=yes", - "motorcar=yes" - ] - } - } - ] + "if": "no:network=yes", + "then": { + "en": "Not part of a bigger network" + } }, { - "options": [ - { - "question": { - "en": "Only working charging stations" - }, - "osmTags": { - "and": [ - "operational_status!=broken", - "amenity=charging_station" - ] - } - } - ] + "if": "network=none", + "then": { + "en": "Not part of a bigger network" + }, + "hideInAnswer": true }, { - "options": [ - { - "question": { - "en": "All connectors", - "nl": "Alle types" - } - }, - { - "question": { - "en": "Has a Schuko wall plug without ground pin (CEE7/4 type F) connector", - "nl": "Heeft een Schuko stekker zonder aardingspin (CEE7/4 type F) " - }, - "osmTags": "socket:schuko~*" - }, - { - "question": { - "en": "Has a European wall plug with ground pin (CEE7/4 type E) connector", - "nl": "Heeft een Europese stekker met aardingspin (CEE7/4 type E) " - }, - "osmTags": "socket:typee~*" - }, - { - "question": { - "en": "Has a Chademo connector", - "nl": "Heeft een " - }, - "osmTags": "socket:chademo~*" - }, - { - "question": { - "en": "Has a Type 1 with cable (J1772) connector", - "nl": "Heeft een Type 1 met kabel (J1772) " - }, - "osmTags": "socket:type1_cable~*" - }, - { - "question": { - "en": "Has a Type 1 without cable (J1772) connector", - "nl": "Heeft een Type 1 zonder kabel (J1772) " - }, - "osmTags": "socket:type1~*" - }, - { - "question": { - "en": "Has a Type 1 CCS (aka Type 1 Combo) connector", - "nl": "Heeft een " - }, - "osmTags": "socket:type1_combo~*" - }, - { - "question": { - "en": "Has a Tesla Supercharger connector", - "nl": "Heeft een " - }, - "osmTags": "socket:tesla_supercharger~*" - }, - { - "question": { - "en": "Has a Type 2 (mennekes) connector", - "nl": "Heeft een " - }, - "osmTags": "socket:type2~*" - }, - { - "question": { - "en": "Has a Type 2 CCS (mennekes) connector", - "nl": "Heeft een " - }, - "osmTags": "socket:type2_combo~*" - }, - { - "question": { - "en": "Has a Type 2 with cable (mennekes) connector", - "nl": "Heeft een Type 2 met kabel (J1772) " - }, - "osmTags": "socket:type2_cable~*" - }, - { - "question": { - "en": "Has a Tesla Supercharger CCS (a branded type2_css) connector", - "nl": "Heeft een " - }, - "osmTags": "socket:tesla_supercharger_ccs~*" - }, - { - "question": { - "en": "Has a Tesla Supercharger (destination) connector", - "nl": "Heeft een " - }, - "osmTags": "socket:tesla_destination~*" - }, - { - "question": { - "en": "Has a Tesla supercharger (destination (A Type 2 with cable branded as tesla) connector", - "nl": "Heeft een " - }, - "osmTags": "socket:tesla_destination~*" - } - ] + "if": "network=AeroVironment", + "then": "AeroVironment" + }, + { + "if": "network=Blink", + "then": "Blink" + }, + { + "if": "network=eVgo", + "then": "eVgo" } - ], - "units": [ + ] + }, + { + "id": "Operator", + "question": { + "en": "Who is the operator of this charging station?" + }, + "render": { + "en": "This charging station is operated by {operator}" + }, + "freeform": { + "key": "operator" + }, + "mappings": [ { - "appliesToKey": [ - "maxstay" - ], - "applicableUnits": [ - { - "canonicalDenomination": "minutes", - "canonicalDenominationSingular": "minute", - "alternativeDenomination": [ - "m", - "min", - "mins", - "minuten", - "mns" - ], - "human": { - "en": " minutes", - "nl": " minuten" - }, - "humanSingular": { - "en": " minute", - "nl": " minuut" - } - }, - { - "canonicalDenomination": "hours", - "canonicalDenominationSingular": "hour", - "alternativeDenomination": [ - "h", - "hrs", - "hours", - "u", - "uur", - "uren" - ], - "human": { - "en": " hours", - "nl": " uren" - }, - "humanSingular": { - "en": " hour", - "nl": " uur" - } - }, - { - "canonicalDenomination": "days", - "canonicalDenominationSingular": "day", - "alternativeDenomination": [ - "dys", - "dagen", - "dag" - ], - "human": { - "en": " days", - "nl": " day" - }, - "humanSingular": { - "en": " day", - "nl": " dag" - } - } + "if": { + "and": [ + "network:={operator}" ] - }, - { - "appliesToKey": [ - "socket:schuko:voltage", - "socket:typee:voltage", - "socket:chademo:voltage", - "socket:type1_cable:voltage", - "socket:type1:voltage", - "socket:type1_combo:voltage", - "socket:tesla_supercharger:voltage", - "socket:type2:voltage", - "socket:type2_combo:voltage", - "socket:type2_cable:voltage", - "socket:tesla_supercharger_ccs:voltage", - "socket:tesla_destination:voltage", - "socket:tesla_destination:voltage" - ], - "applicableUnits": [ - { - "canonicalDenomination": "V", - "alternativeDenomination": [ - "v", - "volt", - "voltage", - "V", - "Volt" - ], - "human": { - "en": "Volts", - "nl": "volt" - } - } - ], - "eraseInvalidValues": true - }, - { - "appliesToKey": [ - "socket:schuko:current", - "socket:typee:current", - "socket:chademo:current", - "socket:type1_cable:current", - "socket:type1:current", - "socket:type1_combo:current", - "socket:tesla_supercharger:current", - "socket:type2:current", - "socket:type2_combo:current", - "socket:type2_cable:current", - "socket:tesla_supercharger_ccs:current", - "socket:tesla_destination:current", - "socket:tesla_destination:current" - ], - "applicableUnits": [ - { - "canonicalDenomination": "A", - "alternativeDenomination": [ - "a", - "amp", - "amperage", - "A" - ], - "human": { - "en": "A", - "nl": "A" - } - } - ], - "eraseInvalidValues": true - }, - { - "appliesToKey": [ - "socket:schuko:output", - "socket:typee:output", - "socket:chademo:output", - "socket:type1_cable:output", - "socket:type1:output", - "socket:type1_combo:output", - "socket:tesla_supercharger:output", - "socket:type2:output", - "socket:type2_combo:output", - "socket:type2_cable:output", - "socket:tesla_supercharger_ccs:output", - "socket:tesla_destination:output", - "socket:tesla_destination:output" - ], - "applicableUnits": [ - { - "canonicalDenomination": "kW", - "alternativeDenomination": [ - "kilowatt" - ], - "human": { - "en": "kilowatt", - "nl": "kilowatt" - } - }, - { - "canonicalDenomination": "mW", - "alternativeDenomination": [ - "megawatt" - ], - "human": { - "en": "megawatt", - "nl": "megawatt" - } - } - ], - "eraseInvalidValues": true + }, + "then": { + "en": "Actually, {operator} is the network" + }, + "addExtraTags": [ + "operator=" + ], + "hideInAnswer": "operator=" } + ] + }, + { + "id": "phone", + "question": { + "en": "What number can one call if there is a problem with this charging station?" + }, + "render": { + "en": "In case of problems, call {phone}" + }, + "freeform": { + "key": "phone", + "type": "phone" + } + }, + { + "id": "email", + "question": { + "en": "What is the email address of the operator?" + }, + "render": { + "en": "In case of problems, send an email to {email}" + }, + "freeform": { + "key": "email", + "type": "email" + } + }, + { + "id": "website", + "question": { + "en": "What is the website of the operator?" + }, + "render": { + "en": "More info on {website}" + }, + "freeform": { + "key": "website", + "type": "url" + } + }, + "level", + { + "id": "ref", + "question": { + "en": "What is the reference number of this charging station?" + }, + "render": { + "en": "Reference number is {ref}" + }, + "freeform": { + "key": "ref" + } + }, + { + "id": "Operational status", + "question": { + "en": "Is this charging point in use?", + "nl": "Is dit oplaadpunt operationeel?" + }, + "mappings": [ + { + "if": "operational_status=broken", + "then": { + "en": "This charging station is broken", + "nl": "Dit oplaadpunt is kapot" + } + }, + { + "if": { + "and": [ + "planned:amenity=charging_station", + "amenity=" + ] + }, + "then": { + "en": "A charging station is planned here", + "nl": "Hier zal binnenkort een oplaadpunt gebouwd worden" + } + }, + { + "if": { + "and": [ + "construction:amenity=charging_station", + "amenity=" + ] + }, + "then": { + "en": "A charging station is constructed here", + "nl": "Hier wordt op dit moment een oplaadpunt gebouwd" + } + }, + { + "if": { + "and": [ + "disused:amenity=charging_station", + "amenity=" + ] + }, + "then": { + "en": "This charging station has beed permanently disabled and is not in use anymore but is still visible", + "nl": "Dit oplaadpunt is niet meer in gebruik maar is wel nog aanwezig" + } + }, + { + "if": { + "and": [ + "amenity=charging_station", + "operational_status=" + ] + }, + "then": { + "en": "This charging station works", + "nl": "Dit oplaadpunt werkt" + } + } + ] + }, + { + "id": "Parking:fee", + "question": { + "en": "Does one have to pay a parking fee while charging?" + }, + "mappings": [ + { + "if": "parking:fee=no", + "then": { + "en": "No additional parking cost while charging" + } + }, + { + "if": "parking:fee=yes", + "then": { + "en": "An additional parking fee should be paid while charging" + } + } + ] + } + ], + "icon": { + "render": "pin:#fff;./assets/themes/charging_stations/plug.svg", + "mappings": [ + { + "if": "bicycle=yes", + "then": "pin:#fff;./assets/themes/charging_stations/bicycle.svg" + }, + { + "if": { + "or": [ + "car=yes", + "motorcar=yes" + ] + }, + "then": "pin:#fff;./assets/themes/charging_stations/car.svg" + } ] + }, + "iconOverlays": [ + { + "if": { + "or": [ + "disused:amenity=charging_station", + "operational_status=broken" + ] + }, + "then": "cross_bottom_right:#c22;" + }, + { + "if": { + "or": [ + "proposed:amenity=charging_station", + "planned:amenity=charging_station" + ] + }, + "then": "./assets/layers/charging_station/under_construction.svg", + "badge": true + }, + { + "if": { + "and": [ + "bicycle=yes", + { + "or": [ + "motorcar=yes", + "car=yes" + ] + } + ] + }, + "then": "circle:#fff;./assets/themes/charging_stations/car.svg", + "badge": true + } + ], + "width": { + "render": "8" + }, + "iconSize": { + "render": "50,50,bottom" + }, + "color": { + "render": "#00f" + }, + "presets": [ + { + "tags": [ + "amenity=charging_station" + ], + "title": { + "en": "Charging station" + }, + "preciseInput": { + "preferredBackground": "map" + } + } + ], + "wayHandling": 1, + "filter": [ + { + "id": "vehicle-type", + "options": [ + { + "question": { + "en": "All vehicle types", + "nl": "Alle voertuigen" + } + }, + { + "question": { + "en": "Charging station for bicycles", + "nl": "Oplaadpunten voor fietsen" + }, + "osmTags": "bicycle=yes" + }, + { + "question": { + "en": "Charging station for cars", + "nl": "Oplaadpunten voor auto's" + }, + "osmTags": { + "or": [ + "car=yes", + "motorcar=yes" + ] + } + } + ] + }, + { + "id": "working", + "options": [ + { + "question": { + "en": "Only working charging stations" + }, + "osmTags": { + "and": [ + "operational_status!=broken", + "amenity=charging_station" + ] + } + } + ] + }, + { + "id": "connection_type", + "options": [ + { + "question": { + "en": "All connectors", + "nl": "Alle types" + } + }, + { + "question": { + "en": "Has a
Schuko wall plug without ground pin (CEE7/4 type F)
connector", + "nl": "Heeft een
Schuko stekker zonder aardingspin (CEE7/4 type F)
" + }, + "osmTags": "socket:schuko~*" + }, + { + "question": { + "en": "Has a
European wall plug with ground pin (CEE7/4 type E)
connector", + "nl": "Heeft een
Europese stekker met aardingspin (CEE7/4 type E)
" + }, + "osmTags": "socket:typee~*" + }, + { + "question": { + "en": "Has a
Chademo
connector", + "nl": "Heeft een
Chademo
" + }, + "osmTags": "socket:chademo~*" + }, + { + "question": { + "en": "Has a
Type 1 with cable (J1772)
connector", + "nl": "Heeft een
Type 1 met kabel (J1772)
" + }, + "osmTags": "socket:type1_cable~*" + }, + { + "question": { + "en": "Has a
Type 1 without cable (J1772)
connector", + "nl": "Heeft een
Type 1 zonder kabel (J1772)
" + }, + "osmTags": "socket:type1~*" + }, + { + "question": { + "en": "Has a
Type 1 CCS (aka Type 1 Combo)
connector", + "nl": "Heeft een
Type 1 CCS (ook gekend als Type 1 Combo)
" + }, + "osmTags": "socket:type1_combo~*" + }, + { + "question": { + "en": "Has a
Tesla Supercharger
connector", + "nl": "Heeft een
Tesla Supercharger
" + }, + "osmTags": "socket:tesla_supercharger~*" + }, + { + "question": { + "en": "Has a
Type 2 (mennekes)
connector", + "nl": "Heeft een
Type 2 (mennekes)
" + }, + "osmTags": "socket:type2~*" + }, + { + "question": { + "en": "Has a
Type 2 CCS (mennekes)
connector", + "nl": "Heeft een
Type 2 CCS (mennekes)
" + }, + "osmTags": "socket:type2_combo~*" + }, + { + "question": { + "en": "Has a
Type 2 with cable (mennekes)
connector", + "nl": "Heeft een
Type 2 met kabel (J1772)
" + }, + "osmTags": "socket:type2_cable~*" + }, + { + "question": { + "en": "Has a
Tesla Supercharger CCS (a branded type2_css)
connector", + "nl": "Heeft een
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
" + }, + "osmTags": "socket:tesla_supercharger_ccs~*" + }, + { + "question": { + "en": "Has a
Tesla Supercharger (destination)
connector", + "nl": "Heeft een
Tesla Supercharger (destination)
" + }, + "osmTags": "socket:tesla_destination~*" + }, + { + "question": { + "en": "Has a
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
connector", + "nl": "Heeft een
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
" + }, + "osmTags": "socket:tesla_destination~*" + }, + { + "question": { + "en": "Has a
USB to charge phones and small electronics
connector", + "nl": "Heeft een
USB om GSMs en kleine electronica op te laden
" + }, + "osmTags": "socket:USB-A~*" + }, + { + "question": { + "en": "Has a
Bosch Active Connect with cable
connector", + "nl": "Heeft een
Bosch Active Connect aan een kabel
" + }, + "osmTags": "socket:bosch_3pin~*" + } + ] + } + ], + "units": [ + { + "appliesToKey": [ + "maxstay" + ], + "applicableUnits": [ + { + "canonicalDenomination": "minutes", + "canonicalDenominationSingular": "minute", + "alternativeDenomination": [ + "m", + "min", + "mins", + "minuten", + "mns" + ], + "human": { + "en": " minutes", + "nl": " minuten" + }, + "humanSingular": { + "en": " minute", + "nl": " minuut" + } + }, + { + "canonicalDenomination": "hours", + "canonicalDenominationSingular": "hour", + "alternativeDenomination": [ + "h", + "hrs", + "hours", + "u", + "uur", + "uren" + ], + "human": { + "en": " hours", + "nl": " uren" + }, + "humanSingular": { + "en": " hour", + "nl": " uur" + } + }, + { + "canonicalDenomination": "days", + "canonicalDenominationSingular": "day", + "alternativeDenomination": [ + "dys", + "dagen", + "dag" + ], + "human": { + "en": " days", + "nl": " day" + }, + "humanSingular": { + "en": " day", + "nl": " dag" + } + } + ] + }, + { + "appliesToKey": [ + "socket:schuko:voltage", + "socket:typee:voltage", + "socket:chademo:voltage", + "socket:type1_cable:voltage", + "socket:type1:voltage", + "socket:type1_combo:voltage", + "socket:tesla_supercharger:voltage", + "socket:type2:voltage", + "socket:type2_combo:voltage", + "socket:type2_cable:voltage", + "socket:tesla_supercharger_ccs:voltage", + "socket:tesla_destination:voltage", + "socket:tesla_destination:voltage", + "socket:USB-A:voltage", + "socket:bosch_3pin:voltage" + ], + "applicableUnits": [ + { + "canonicalDenomination": "V", + "alternativeDenomination": [ + "v", + "volt", + "voltage", + "V", + "Volt" + ], + "human": { + "en": "Volts", + "nl": "volt" + } + } + ], + "eraseInvalidValues": true + }, + { + "appliesToKey": [ + "socket:schuko:current", + "socket:typee:current", + "socket:chademo:current", + "socket:type1_cable:current", + "socket:type1:current", + "socket:type1_combo:current", + "socket:tesla_supercharger:current", + "socket:type2:current", + "socket:type2_combo:current", + "socket:type2_cable:current", + "socket:tesla_supercharger_ccs:current", + "socket:tesla_destination:current", + "socket:tesla_destination:current", + "socket:USB-A:current", + "socket:bosch_3pin:current" + ], + "applicableUnits": [ + { + "canonicalDenomination": "A", + "alternativeDenomination": [ + "a", + "amp", + "amperage", + "A" + ], + "human": { + "en": "A", + "nl": "A" + } + } + ], + "eraseInvalidValues": true + }, + { + "appliesToKey": [ + "socket:schuko:output", + "socket:typee:output", + "socket:chademo:output", + "socket:type1_cable:output", + "socket:type1:output", + "socket:type1_combo:output", + "socket:tesla_supercharger:output", + "socket:type2:output", + "socket:type2_combo:output", + "socket:type2_cable:output", + "socket:tesla_supercharger_ccs:output", + "socket:tesla_destination:output", + "socket:tesla_destination:output", + "socket:USB-A:output", + "socket:bosch_3pin:output" + ], + "applicableUnits": [ + { + "canonicalDenomination": "kW", + "alternativeDenomination": [ + "kilowatt" + ], + "human": { + "en": "kilowatt", + "nl": "kilowatt" + } + }, + { + "canonicalDenomination": "mW", + "alternativeDenomination": [ + "megawatt" + ], + "human": { + "en": "megawatt", + "nl": "megawatt" + } + } + ], + "eraseInvalidValues": true + } + ] } \ No newline at end of file diff --git a/assets/layers/charging_station/charging_station.protojson b/assets/layers/charging_station/charging_station.protojson index e76636ba78..73aced6ad6 100644 --- a/assets/layers/charging_station/charging_station.protojson +++ b/assets/layers/charging_station/charging_station.protojson @@ -538,7 +538,9 @@ } }, { - "if": "amenity=charging_station", + "if": { + "and": ["amenity=charging_station","operational_status="] + }, "then": { "en": "This charging station works", "nl": "Dit oplaadpunt werkt" @@ -646,6 +648,7 @@ "wayHandling": 1, "filter": [ { + "id": "vehicle-type", "options": [ { "question": { @@ -675,6 +678,7 @@ ] }, { + "id": "working", "options": [ { "question": { diff --git a/assets/layers/charging_station/csvToJson.ts b/assets/layers/charging_station/csvToJson.ts index 08e33a2292..c630297c01 100644 --- a/assets/layers/charging_station/csvToJson.ts +++ b/assets/layers/charging_station/csvToJson.ts @@ -17,7 +17,9 @@ function loadCsv(file): { countryBlackList?: string[], commonVoltages?: number[], commonCurrents?: number[], - commonOutputs?: string[] + commonOutputs?: string[], + associatedVehicleTypes?: string[], + neverAssociatedWith?: string[] }[] { const entries: string[] = Utils.NoNull(readFileSync(file, "utf8").split("\n").map(str => str.trim())) const header = entries.shift().split(",") @@ -29,7 +31,7 @@ function loadCsv(file): { } const v = {} - const colonSeperated = ["commonVoltages", "commonOutputs", "commonCurrents", "countryWhiteList","countryBlackList"] + const colonSeperated = ["commonVoltages", "commonOutputs", "commonCurrents", "countryWhiteList", "countryBlackList", "associatedVehicleTypes", "neverAssociatedWith"] const descriptionTranslations = new Map() for (let j = 0; j < header.length; j++) { const key = header[j]; @@ -53,7 +55,7 @@ function loadCsv(file): { function run(file, protojson) { const overview_question_answers = [] - const questions: (TagRenderingConfigJson & {"id": string})[] = [] + const questions: (TagRenderingConfigJson & { "id": string })[] = [] const filterOptions: { question: any, osmTags?: string } [] = [ { question: { @@ -64,11 +66,11 @@ function run(file, protojson) { ] const entries = loadCsv(file) - for (let i = 0; i < entries.length; i++){ + for (let i = 0; i < entries.length; i++) { const e = entries[i]; const txt = { - en: ` ${e.description.get("en")}`, - nl: ` ${e.description.get("nl")}` + en: `
${e.description.get("en")}
`, + nl: `
${e.description.get("nl")}
` } const json = { if: `${e.key}=1`, @@ -76,17 +78,43 @@ function run(file, protojson) { then: txt, } - if(e.countryWhiteList.length > 0 && e.countryBlackList.length > 0){ - throw "Error for type "+e.key+": don't defined both a whitelist and a blacklist" + if (e.countryWhiteList.length > 0 && e.countryBlackList.length > 0) { + throw "Error for type " + e.key + ": don't defined both a whitelist and a blacklist" } if (e.countryWhiteList.length > 0) { + // This is a 'hideInAnswer', thus _reverse_ logic! const countries = e.countryWhiteList.map(country => "_country!=" + country) //HideInAnswer if it is in the wrong country json["hideInAnswer"] = {or: countries} - }else if (e.countryBlackList .length > 0) { + } else if (e.countryBlackList.length > 0) { const countries = e.countryBlackList.map(country => "_country=" + country) //HideInAnswer if it is in the wrong country json["hideInAnswer"] = {or: countries} } + if (e.associatedVehicleTypes?.length > 0 && e.associatedVehicleTypes.indexOf("*") < 0 && e.neverAssociatedWith?.length > 0) { + // This plug only occurs if some vehicle specific vehicle type is present. + // IF all of the needed vehicle types are explicitly NO, then we hide this type as well + let associatedWith = {and: [].concat(...e.associatedVehicleTypes.map(neededVehicle => [neededVehicle + "=no"]))} + + // We also hide if: + // - One of the neverAssociatedVehiclesTYpes is set to 'yes' AND none of the associated types are set/yes + let neverAssociatedIsSet = { + and: [{ + or: e.neverAssociatedWith.map(vehicleType => vehicleType + "=yes") + }, + ...e.associatedVehicleTypes.map(associated => associated + "!=yes") + ] + } + + let conditions = [associatedWith, neverAssociatedIsSet] + if (json["hideInAnswer"] !== undefined) { + conditions.push(json["hideInAnswer"]) + } + json["hideInAnswer"] = {or: conditions} + + } + + + overview_question_answers.push(json) // We add a second time for any amount to trigger a visualisation; but this is not an answer option @@ -99,18 +127,18 @@ function run(file, protojson) { } overview_question_answers.push(no_ask_json) - const descrWithImage_en = `${e.description.get("en")} ` - const descrWithImage_nl = `${e.description.get("nl")} ` + const descrWithImage_en = `
${e.description.get("en")}
` + const descrWithImage_nl = `
${e.description.get("nl")}
` questions.push({ - "id":"plugs-"+i, + "id": "plugs-" + i, question: { en: `How much plugs of type ${descrWithImage_en} are available here?`, nl: `Hoeveel stekkers van type ${descrWithImage_nl} heeft dit oplaadpunt?`, }, render: { - en: `There are ${descrWithImage_en} plugs of type ${e.description.get("en")} available here`, - nl: `Hier zijn ${descrWithImage_nl} stekkers van het type ${e.description.get("nl")}` + en: `There are {${e.key}} plugs of type ${descrWithImage_en} available here`, + nl: `Hier zijn {${e.key}} stekkers van het type ${descrWithImage_nl}` }, freeform: { key: e.key, @@ -122,7 +150,7 @@ function run(file, protojson) { }) questions.push({ - "id":"voltage-"+i, + "id": "voltage-" + i, question: { en: `What voltage do the plugs with ${descrWithImage_en} offer?`, nl: `Welke spanning levert de stekker van type ${descrWithImage_nl}` @@ -151,7 +179,7 @@ function run(file, protojson) { questions.push({ - "id":"current-"+i, + "id": "current-" + i, question: { en: `What current do the plugs with ${descrWithImage_en} offer?`, nl: `Welke stroom levert de stekker van type ${descrWithImage_nl}?`, @@ -180,7 +208,7 @@ function run(file, protojson) { questions.push({ - "id":"power-output-"+i, + "id": "power-output-" + i, question: { en: `What power output does a single plug of type ${descrWithImage_en} offer?`, nl: `Welk vermogen levert een enkele stekker van type ${descrWithImage_nl}?`, @@ -217,7 +245,7 @@ function run(file, protojson) { } const toggles = { - "id":"Available_charging_stations (generated)", + "id": "Available_charging_stations (generated)", "question": { "en": "Which charging stations are available here?" }, @@ -230,29 +258,29 @@ function run(file, protojson) { let protoString = readFileSync(protojson, "utf8") protoString = protoString.replace("{\"id\": \"$$$\"}", stringified.join(",\n")) - const proto = JSON.parse(protoString) + const proto = JSON.parse(protoString) proto.tagRenderings.forEach(tr => { - if(typeof tr === "string"){ + if (typeof tr === "string") { return; } - if(tr["id"] === undefined || typeof tr["id"] !== "string"){ + if (tr["id"] === undefined || typeof tr["id"] !== "string") { console.error(tr) throw "Every tagrendering should have an id, acting as comment" } }) - + proto["filter"].push({ + id: "connection_type", options: filterOptions }) - - + const extraUnits = [ { appliesToKey: entries.map(e => e.key + ":voltage"), applicableUnits: [{ canonicalDenomination: 'V', - alternativeDenomination: ["v", "volt", "voltage",'V','Volt'], + alternativeDenomination: ["v", "volt", "voltage", 'V', 'Volt'], human: { en: "Volts", nl: "volt" @@ -264,7 +292,7 @@ function run(file, protojson) { appliesToKey: entries.map(e => e.key + ":current"), applicableUnits: [{ canonicalDenomination: 'A', - alternativeDenomination: ["a", "amp", "amperage",'A'], + alternativeDenomination: ["a", "amp", "amperage", 'A'], human: { en: "A", nl: "A" @@ -294,13 +322,11 @@ function run(file, protojson) { }, ]; - if(proto["units"] == undefined){ + if (proto["units"] == undefined) { proto["units"] = [] } proto["units"].push(...extraUnits) - - mergeTranslations("charging_station.json",proto) - writeFileSync("charging_station.json", JSON.stringify(proto, undefined, " ")) + writeFileSync("charging_station.json", JSON.stringify(proto, undefined, " ")) } @@ -329,7 +355,6 @@ async function queryTagInfo(file, type, clean: ((s: string) => string)) { const countsArray = Array.from(counts.keys()) countsArray.sort() console.log(`${e.key}:${type} = ${countsArray.join(";")}`) - // console.log(`${countsArray.join(";")}`) } } @@ -338,22 +363,22 @@ async function queryTagInfo(file, type, clean: ((s: string) => string)) { * @param origPath * @param newConfig */ -function mergeTranslations(origPath, newConfig: LayerConfigJson){ - const oldFile = JSON.parse(readFileSync(origPath, "utf-8")) - const newFile = newConfig +function mergeTranslations(origPath, newConfig: LayerConfigJson) { + const oldFile = JSON.parse(readFileSync(origPath, "utf-8")) + const newFile = newConfig const renderingsOld = oldFile.tagRenderings delete oldFile.tagRenderings const newRenderings = newFile.tagRenderings Utils.Merge(oldFile, newFile) for (const oldRendering of renderingsOld) { - + const oldRenderingName = oldRendering["id"] - if(oldRenderingName === undefined){ + if (oldRenderingName === undefined) { continue } const applicable = newRenderings.filter(r => r["id"] === oldRenderingName)[0] - if(applicable === undefined){ + if (applicable === undefined) { continue; } Utils.Merge(oldRendering, applicable) @@ -361,6 +386,7 @@ function mergeTranslations(origPath, newConfig: LayerConfigJson){ } try { + console.log("Generating the charging_station.json file") run("types.csv", "charging_station.protojson") /*/ queryTagInfo("types.csv","voltage", s => s.trim()) diff --git a/assets/layers/charging_station/license_info.json b/assets/layers/charging_station/license_info.json index 96d5a1ef7c..dc2de4d3d2 100644 --- a/assets/layers/charging_station/license_info.json +++ b/assets/layers/charging_station/license_info.json @@ -99,6 +99,14 @@ ], "sources": [] }, + { + "path": "bosch-3pin.svg", + "license": "CC0", + "authors": [ + "Pieter Vander Vennet" + ], + "sources": [] + }, { "path": "under_construction.svg", "license": "Public domain", @@ -108,5 +116,15 @@ "sources": [ "https://upload.wikimedia.org/wikipedia/commons/2/20/UnderCon_icon.svg" ] + }, + { + "path": "usb_port.svg", + "license": "CC-BY", + "authors": [ + "Ryan Dardis" + ], + "sources": [ + "https://thenounproject.com/term/usb-port/94768/" + ] } -] \ No newline at end of file +] diff --git a/assets/layers/charging_station/types.csv b/assets/layers/charging_station/types.csv index 4b76be6a72..b8d6da5cff 100644 --- a/assets/layers/charging_station/types.csv +++ b/assets/layers/charging_station/types.csv @@ -1,14 +1,16 @@ -key,image,description:en,countryWhiteList,countryBlackList,commonVoltages,commonCurrents,commonOutputs,description:nl -socket:schuko,CEE7_4F.svg,Schuko wall plug without ground pin (CEE7/4 type F),be;fr;ma;tn;pl;cs;sk;mo,,230,16,3.6 kW,Schuko stekker zonder aardingspin (CEE7/4 type F) -socket:typee,TypeE.svg,European wall plug with ground pin (CEE7/4 type E),,,230,16,3 kW;22 kW;,Europese stekker met aardingspin (CEE7/4 type E) -socket:chademo,Chademo_type4.svg,Chademo,,,500,120,50 kW, -socket:type1_cable,Type1_J1772.svg,Type 1 with cable (J1772),,,200;240,32,3.7 kW;7 kW,Type 1 met kabel (J1772) -socket:type1,Type1_J1772.svg,Type 1 without cable (J1772),,,200;240,32,3.7 kW;6.6 kW;7 kW;7.2 kW,Type 1 zonder kabel (J1772) -socket:type1_combo,Type1-ccs.svg,Type 1 CCS (aka Type 1 Combo),,,400;1000,50;125,50 kW;62.5 kW;150 kW;350 kW;, -socket:tesla_supercharger,Tesla-hpwc-model-s.svg,Tesla Supercharger,,,480,125;350,120 kW;150 kW;250 kW, -socket:type2,Type2_socket.svg,Type 2 (mennekes),,,230;400,16;32,11 kW;22 kW, -socket:type2_combo,Type2_CCS.svg,Type 2 CCS (mennekes),,,500;920,125;350,50 kW, -socket:type2_cable,Type2_tethered.svg,Type 2 with cable (mennekes),,,230;400,16;32,11 kW;22 kW,Type 2 met kabel (J1772) -socket:tesla_supercharger_ccs,Type2_CCS.svg,Tesla Supercharger CCS (a branded type2_css),,,500;920,125;350,50 kW, -socket:tesla_destination,Tesla-hpwc-model-s.svg,Tesla Supercharger (destination),us,,480,125;350,120 kW;150 kW;250 kW, -socket:tesla_destination,Type2_tethered.svg,Tesla supercharger (destination (A Type 2 with cable branded as tesla),,us,230;400,16;32,11 kW;22 kW, +key,image,description:en,countryWhiteList,countryBlackList,commonVoltages,commonCurrents,commonOutputs,description:nl,associatedVehicleTypes,neverAssociatedWith +socket:schuko,CEE7_4F.svg,Schuko wall plug without ground pin (CEE7/4 type F),be;fr;ma;tn;pl;cs;sk;mo,,230,16,3.6 kW,Schuko stekker zonder aardingspin (CEE7/4 type F),*, +socket:typee,TypeE.svg,European wall plug with ground pin (CEE7/4 type E),,,230,16,3 kW;22 kW;,Europese stekker met aardingspin (CEE7/4 type E),*, +socket:chademo,Chademo_type4.svg,Chademo,,,500,120,50 kW,Chademo,car;motorcar;hgv;bus,bicycle;scooter +socket:type1_cable,Type1_J1772.svg,Type 1 with cable (J1772),,,200;240,32,3.7 kW;7 kW,Type 1 met kabel (J1772),car;motorcar;hgv;bus,bicycle;scooter +socket:type1,Type1_J1772.svg,Type 1 without cable (J1772),,,200;240,32,3.7 kW;6.6 kW;7 kW;7.2 kW,Type 1 zonder kabel (J1772),car;motorcar;hgv;bus,bicycle;scooter +socket:type1_combo,Type1-ccs.svg,Type 1 CCS (aka Type 1 Combo),,,400;1000,50;125,50 kW;62.5 kW;150 kW;350 kW;,Type 1 CCS (ook gekend als Type 1 Combo),car;motorcar;hgv;bus,bicycle;scooter +socket:tesla_supercharger,Tesla-hpwc-model-s.svg,Tesla Supercharger,,,480,125;350,120 kW;150 kW;250 kW,Tesla Supercharger,car;motorcar;hgv;bus,bicycle;scooter +socket:type2,Type2_socket.svg,Type 2 (mennekes),,,230;400,16;32,11 kW;22 kW,Type 2 (mennekes),car;motorcar;hgv;bus,bicycle;scooter +socket:type2_combo,Type2_CCS.svg,Type 2 CCS (mennekes),,,500;920,125;350,50 kW,Type 2 CCS (mennekes),car;motorcar;hgv;bus,bicycle;scooter +socket:type2_cable,Type2_tethered.svg,Type 2 with cable (mennekes),,,230;400,16;32,11 kW;22 kW,Type 2 met kabel (J1772),car;motorcar;hgv;bus,bicycle;scooter +socket:tesla_supercharger_ccs,Type2_CCS.svg,Tesla Supercharger CCS (a branded type2_css),,,500;920,125;350,50 kW,Tesla Supercharger CCS (een type2 CCS met Tesla-logo),car;motorcar;hgv;bus,bicycle;scooter +socket:tesla_destination,Tesla-hpwc-model-s.svg,Tesla Supercharger (destination),us,,480,125;350,120 kW;150 kW;250 kW,Tesla Supercharger (destination),car;motorcar;hgv;bus,bicycle;scooter +socket:tesla_destination,Type2_tethered.svg,Tesla supercharger (destination (A Type 2 with cable branded as tesla),,us,230;400,16;32,11 kW;22 kW,Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo),car;motorcar;hgv;bus,bicycle;scooter +socket:USB-A,usb_port.svg,USB to charge phones and small electronics,,,5,1;2,5W;10W,USB om GSMs en kleine electronica op te laden,*, +socket:bosch_3pin,bosch-3pin.svg,Bosch Active Connect with cable,,,,,,Bosch Active Connect aan een kabel,bicycle,car;motorcar;hgv;bus diff --git a/assets/layers/charging_station/usb_port.svg b/assets/layers/charging_station/usb_port.svg new file mode 100644 index 0000000000..f813f20f06 --- /dev/null +++ b/assets/layers/charging_station/usb_port.svg @@ -0,0 +1,74 @@ + +image/svg+xml \ No newline at end of file diff --git a/assets/layers/cluster_style/cluster_style.json b/assets/layers/cluster_style/cluster_style.json new file mode 100644 index 0000000000..d6b68d1133 --- /dev/null +++ b/assets/layers/cluster_style/cluster_style.json @@ -0,0 +1,36 @@ +{ + "id": "cluster_style", + "description": "The style for the clustering in all themes.", + "source": { + "osmTags": "tileId~*" + }, + "color": { + "render": "#3c3", + "mappings": [ + { + "if": "count>200", + "then": "#f33" + }, + { + "if": "count>100", + "then": "#c93" + }, + { + "if": "count>50", + "then": "#cc3" + } + ] + }, + "width": { + "render": "1" + }, + "label": { + "render": "
{count}
", + "mappings": [ + { + "if": "count>99", + "then": "
>99
" + } + ] + } +} \ No newline at end of file diff --git a/assets/layers/drinking_water/drinking_water.json b/assets/layers/drinking_water/drinking_water.json index 6d7820ab82..43af3910c7 100644 --- a/assets/layers/drinking_water/drinking_water.json +++ b/assets/layers/drinking_water/drinking_water.json @@ -48,22 +48,23 @@ } }, "calculatedTags": [ - "_closest_other_drinking_water_id=feat.closest('drinking_water').id", - "_closest_other_drinking_water_distance=Math.floor(feat.distanceTo(feat.closest('drinking_water')) * 1000)" + "_closest_other_drinking_water=feat.closestn('drinking_water', 1, undefined, 5000).map(f => ({id: f.feat.id, distance: ''+f.distance}))[0]", + "_closest_other_drinking_water_id=JSON.parse(feat.properties._closest_other_drinking_water)?.id", + "_closest_other_drinking_water_distance=Math.floor(Number(JSON.parse(feat.properties._closest_other_drinking_water)?.distance) * 1000)" ], "minzoom": 13, "wayHandling": 1, "presets": [ { "title": { - "en": "Drinking water", - "nl": "Drinkbaar water", - "fr": "Eau potable", - "gl": "Auga potábel", - "de": "Trinkwasser", - "it": "Acqua potabile", + "en": "drinking water", + "nl": "drinkbaar water", + "fr": "eau potable", + "gl": "auga potábel", + "de": "trinkwasser", + "it": "acqua potabile", "ru": "Питьевая вода", - "id": "Air minum" + "id": "air minum" }, "tags": [ "amenity=drinking_water" diff --git a/assets/layers/food/food.json b/assets/layers/food/food.json index f64472b27e..1bac9814d2 100644 --- a/assets/layers/food/food.json +++ b/assets/layers/food/food.json @@ -12,6 +12,7 @@ ] } }, + "minzoom": 12, "wayHandling": 1, "icon": { "render": "circle:white;./assets/layers/food/restaurant.svg", @@ -554,10 +555,12 @@ } ], "condition": "cuisine=friture" - } + }, + "dog-access" ], "filter": [ { + "id": "opened-now", "options": [ { "question": { @@ -569,6 +572,7 @@ ] }, { + "id": "vegetarian", "options": [ { "question": { @@ -587,6 +591,7 @@ ] }, { + "id": "vegan", "options": [ { "question": { @@ -603,6 +608,7 @@ ] }, { + "id": "halal", "options": [ { "question": { diff --git a/assets/layers/information_board/information_board.json b/assets/layers/information_board/information_board.json index f25c712458..6f240fa4fd 100644 --- a/assets/layers/information_board/information_board.json +++ b/assets/layers/information_board/information_board.json @@ -45,11 +45,11 @@ "information=board" ], "title": { - "nl": "Informatiebord", - "en": "Information board", - "it": "Pannello informativo", - "fr": "Panneau d'informations", - "de": "Informationstafel", + "nl": "informatiebord", + "en": "information board", + "it": "pannello informativo", + "fr": "panneau d'informations", + "de": "informationstafel", "ru": "Информационный щит" } } diff --git a/assets/layers/nature_reserve/nature_reserve.json b/assets/layers/nature_reserve/nature_reserve.json index 5a3551d52e..786f5e87bc 100644 --- a/assets/layers/nature_reserve/nature_reserve.json +++ b/assets/layers/nature_reserve/nature_reserve.json @@ -151,7 +151,7 @@ ] }, "then": { - "nl": "Dit gebied wordt beheerd door Natuurpunt" + "nl": "Dit gebied wordt beheerd door Natuurpunt" } }, { @@ -161,7 +161,7 @@ ] }, "then": { - "nl": "Dit gebied wordt beheerd door {operator}" + "nl": "Dit gebied wordt beheerd door {operator}" }, "hideInAnswer": true }, @@ -172,7 +172,7 @@ ] }, "then": { - "nl": "Dit gebied wordt beheerd door het Agentschap Natuur en Bos" + "nl": "Dit gebied wordt beheerd door het Agentschap Natuur en Bos" } } ], @@ -392,11 +392,12 @@ } ], "id": "Surface area" - } + }, + "wikipedia" ], "wayHandling": 2, "icon": { - "render": "./assets/themes/buurtnatuur/nature_reserve.svg" + "render": "./assets/layers/nature_reserve/nature_reserve.svg" }, "width": { "render": "1" @@ -414,7 +415,7 @@ "fixme=Toegevoegd met MapComplete, geometry nog uit te tekenen" ], "title": { - "nl": "Natuurreservaat" + "nl": "natuurreservaat" }, "description": { "nl": "Voeg een ontbrekend, erkend natuurreservaat toe, bv. een gebied dat beheerd wordt door het ANB of natuurpunt" @@ -423,6 +424,7 @@ ], "filter": [ { + "id": "access", "options": [ { "question": { @@ -433,6 +435,7 @@ ] }, { + "id": "dogs", "options": [ { "question": { diff --git a/assets/layers/observation_tower/observation_tower.json b/assets/layers/observation_tower/observation_tower.json index fa9a002621..8443b12134 100644 --- a/assets/layers/observation_tower/observation_tower.json +++ b/assets/layers/observation_tower/observation_tower.json @@ -122,7 +122,8 @@ }, "id": "Payment methods" }, - "wheelchair-access" + "wheelchair-access", + "wikipedia" ], "wayHandling": 1, "icon": { diff --git a/assets/layers/parking/parking.json b/assets/layers/parking/parking.json index b071901c6c..42f09f53e9 100644 --- a/assets/layers/parking/parking.json +++ b/assets/layers/parking/parking.json @@ -49,122 +49,7 @@ "nl": "Parking" }, "tagRenderings": [ - "images", - { - "render": { - "nl": "De toegankelijkheid van dit gebied is: {access:description}" - }, - "question": { - "nl": "Is dit gebied toegankelijk?" - }, - "freeform": { - "key": "access:description" - }, - "mappings": [ - { - "if": { - "and": [ - "access=yes", - "fee=" - ] - }, - "then": { - "nl": "Vrij toegankelijk" - } - }, - { - "if": { - "and": [ - "access=no", - "fee=" - ] - }, - "then": { - "nl": "Niet toegankelijk" - } - }, - { - "if": { - "and": [ - "access=private", - "fee=" - ] - }, - "then": { - "nl": "Niet toegankelijk, want privégebied" - } - }, - { - "if": { - "and": [ - "access=permissive", - "fee=" - ] - }, - "then": { - "nl": "Toegankelijk, ondanks dat het privegebied is" - } - }, - { - "if": { - "and": [ - "access=guided", - "fee=" - ] - }, - "then": { - "nl": "Enkel toegankelijk met een gids of tijdens een activiteit" - } - }, - { - "if": { - "and": [ - "access=yes", - "fee=yes" - ] - }, - "then": { - "nl": "Toegankelijk mits betaling" - } - } - ], - "id": "Access tag" - }, - { - "render": { - "nl": "Beheer door {operator}" - }, - "question": { - "nl": "Wie beheert dit pad?" - }, - "freeform": { - "key": "operator" - }, - "mappings": [ - { - "if": { - "and": [ - "operator=Natuurpunt" - ] - }, - "then": { - "nl": "Dit gebied wordt beheerd door Natuurpunt" - } - }, - { - "if": { - "and": [ - "operator~(n|N)atuurpunt.*" - ] - }, - "then": { - "nl": "Dit gebied wordt beheerd door {operator}" - }, - "hideInAnswer": true - } - ], - "id": "Operator tag" - } + "images" ], "wayHandling": 1, "iconSize": { @@ -176,17 +61,25 @@ "presets": [ { "tags": [ - "amenity=parking", - "amenity=motorcycle_parking", - "amenity=bicycle_parking", - "fixme=Toegevoegd met MapComplete, geometry nog uit te tekenen" + "amenity=bicycle_parking" ], "title": { - "nl": "Paden" + "nl": "fietsparking" }, "description": { - "nl": "Voeg een ontbrekend, erkend pad toe." + "nl": "Voeg hier een fietsenstalling toe" + } + }, + { + "tags": [ + "amenity=parking" + ], + "title": { + "nl": "parking" + }, + "description": { + "nl": "Voeg hier een parking voor auto's toe" } } ] -} +} \ No newline at end of file diff --git a/assets/layers/picnic_table/picnic_table.json b/assets/layers/picnic_table/picnic_table.json index dbe67a9afa..c414410fe6 100644 --- a/assets/layers/picnic_table/picnic_table.json +++ b/assets/layers/picnic_table/picnic_table.json @@ -92,12 +92,12 @@ "leisure=picnic_table" ], "title": { - "en": "Picnic table", - "nl": "Picnic-tafel", - "it": "Tavolo da picnic", + "en": "picnic table", + "nl": "picnic-tafel", + "it": "tavolo da picnic", "ru": "Стол для пикника", - "de": "Picknicktisch", - "fr": "Table de pique-nique" + "de": "picknicktisch", + "fr": "table de pique-nique" } } ], diff --git a/assets/layers/public_bookcase/public_bookcase.json b/assets/layers/public_bookcase/public_bookcase.json index f00ec7874d..64b6230c16 100644 --- a/assets/layers/public_bookcase/public_bookcase.json +++ b/assets/layers/public_bookcase/public_bookcase.json @@ -19,7 +19,7 @@ "source": { "osmTags": "amenity=public_bookcase" }, - "minzoom": 12, + "minzoom": 10, "wayHandling": 2, "title": { "render": { @@ -444,6 +444,7 @@ }, "filter": [ { + "id": "kid-books", "options": [ { "question": "Kinderboeken aanwezig?", @@ -452,6 +453,7 @@ ] }, { + "id": "adult-books", "options": [ { "question": "Boeken voor volwassenen aanwezig?", @@ -460,11 +462,12 @@ ] }, { + "id": "inside", "options": [ { - "question": "Binnen of buiten", - "osmTags": { - "and": [] + "question": { + "nl": "Binnen of buiten", + "en": "Indoor or outdoor" } }, { diff --git a/assets/layers/toilet/toilet.json b/assets/layers/toilet/toilet.json index ef92600777..d7592efc74 100644 --- a/assets/layers/toilet/toilet.json +++ b/assets/layers/toilet/toilet.json @@ -27,7 +27,7 @@ "mappings": [ { "if": "wheelchair=yes", - "then": "./assets/layers/toilet/wheelchair.svg" + "then": "circle:white;./assets/layers/toilet/wheelchair.svg" }, { "if": { @@ -47,12 +47,12 @@ "presets": [ { "title": { - "en": "Toilet", - "de": "Toilette", - "fr": "Toilettes", - "nl": "Toilet", - "ru": "Туалет", - "it": "Servizi igienici" + "en": "toilet", + "de": "toilette", + "fr": "toilettes", + "nl": "toilet", + "ru": "tуалет", + "it": "servizi igienici" }, "tags": [ "amenity=toilets" @@ -68,12 +68,12 @@ }, { "title": { - "en": "Toilets with wheelchair accessible toilet", - "de": "Toiletten mit rollstuhlgerechter Toilette", - "fr": "Toilettes accessible aux personnes à mobilité réduite", - "nl": "Een rolstoeltoegankelijke toilet", - "it": "Servizi igienici accessibili per persone in sedia a rotelle", - "ru": "Туалет с доступом для пользователей кресел-колясок" + "en": "toilets with wheelchair accessible toilet", + "de": "toiletten mit rollstuhlgerechter Toilette", + "fr": "toilettes accessible aux personnes à mobilité réduite", + "nl": "een rolstoeltoegankelijke toilet", + "it": "servizi igienici accessibili per persone in sedia a rotelle", + "ru": "tуалет с доступом для пользователей кресел-колясок" }, "tags": [ "amenity=toilets", @@ -407,6 +407,96 @@ } ], "id": "toilet-changing_table:location" + }, + { + "id": "toilet-handwashing", + "question": { + "en": "Do these toilets have a sink to wash your hands?", + "nl": "Hebben deze toiletten een lavabo om de handen te wassen?" + }, + "mappings": [ + { + "if": "toilets:handwashing=yes", + "then": { + "en": "This toilets have a sink to wash your hands", + "nl": "Deze toiletten hebben een lavabo waar men de handen kan wassen" + } + }, + { + "if": "toilets:handwashing=no", + "then": { + "en": "This toilets don't have a sink to wash your hands", + "nl": "Deze toiletten hebben geen lavabo waar men de handen kan wassen" + } + } + ] + }, + { + "id": "toilet-has-paper", + "question": { + "en": "Does one have to bring their own toilet paper to this toilet?", + "nl": "Moet je je eigen toiletpappier meenemen naar deze toilet?" + }, + "mappings": [ + { + "if": "toilets:paper_supplied=yes", + "then": { + "en": "Toilet paper is equipped with toilet paper", + "nl": "Deze toilet is voorzien van toiletpapier" + } + }, + { + "if": "toilets:paper_supplied=no", + "then": { + "en": "You have to bring your own toilet paper to this toilet", + "nl": "Je moet je eigen toiletpapier meebrengen naar deze toilet" + } + } + ] + } + ], + "filter": [ + { + "id": "wheelchair", + "options": [ + { + "question": { + "en": "Wheelchair accessible", + "nl": "Rolstoel toegankelijk" + }, + "osmTags": "wheelchair=yes" + } + ] + }, + { + "id": "changing_table", + "options": [ + { + "question": { + "en": "Has a changing table", + "nl": "Heeft een luiertafel" + }, + "osmTags": "changing_table=yes" + } + ] + }, + { + "id": "free", + "options": [ + { + "question": { + "en": "Free to use", + "nl": "Gratis toegankelijk" + }, + "osmTags": { + "or": [ + "fee=no", + "fee=0", + "charge=0" + ] + } + } + ] } ] } \ No newline at end of file diff --git a/assets/layers/toilet/wheelchair.svg b/assets/layers/toilet/wheelchair.svg index 929ea6c401..d113492add 100644 --- a/assets/layers/toilet/wheelchair.svg +++ b/assets/layers/toilet/wheelchair.svg @@ -2,76 +2,71 @@ image/svg+xml + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> + id="defs9" /> + pagecolor="#ffffff" + bordercolor="#666666" + borderopacity="1" + objecttolerance="10" + gridtolerance="10" + guidetolerance="10" + inkscape:pageopacity="0" + inkscape:pageshadow="2" + inkscape:window-width="1920" + inkscape:window-height="999" + id="namedview7" + showgrid="false" + inkscape:zoom="0.8559553" + inkscape:cx="-16.568588" + inkscape:cy="292.29436" + inkscape:window-x="0" + inkscape:window-y="0" + inkscape:window-maximized="1" + inkscape:current-layer="layer2" /> - + inkscape:groupmode="layer" + id="layer2" + inkscape:label="background"> + + inkscape:groupmode="layer" + id="layer1" + inkscape:label="wheelchair"> \ No newline at end of file + inkscape:connector-curvature="0" + style="clip-rule:evenodd;fill:#000000;fill-rule:evenodd;stroke-width:0.66635805" + d="m 310.84431,395.24795 c -20.28873,40.10642 -62.75413,66.52908 -108.0502,66.52908 -66.52908,0 -120.790412,-54.26133 -120.790412,-120.79041 0,-46.71209 28.310452,-90.1207 70.555212,-109.36341 l 2.73376,35.67521 c -24.98647,15.74498 -40.3895,44.15435 -40.3895,73.93288 0,48.26215 39.36263,87.62413 87.62413,87.62413 44.15407,0 81.80523,-33.88535 86.93958,-77.35545 z" + id="path4" /> \ No newline at end of file diff --git a/assets/layers/tree_node/tree_node.json b/assets/layers/tree_node/tree_node.json index bba09af121..59c1d03c14 100644 --- a/assets/layers/tree_node/tree_node.json +++ b/assets/layers/tree_node/tree_node.json @@ -7,7 +7,7 @@ "ru": "Дерево", "fr": "Arbre" }, - "minzoom": 14, + "minzoom": 16, "source": { "osmTags": { "and": [ diff --git a/assets/layers/watermill/watermill.json b/assets/layers/watermill/watermill.json index 54b5f056cd..6f8dcb65fe 100644 --- a/assets/layers/watermill/watermill.json +++ b/assets/layers/watermill/watermill.json @@ -170,4 +170,4 @@ "color": { "render": "#FFC0CB" } -} +} \ No newline at end of file diff --git a/assets/svg/hand.svg b/assets/svg/hand.svg index 75118da951..bbef4187a0 100644 --- a/assets/svg/hand.svg +++ b/assets/svg/hand.svg @@ -1,53 +1,30 @@ - - - - image/svg+xml - - - - - - - + xmlns:dc="http://purl.org/dc/elements/1.1/" + xmlns:cc="http://creativecommons.org/ns#" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns="http://www.w3.org/2000/svg" + id="svg6" + height="39.557907" + width="28.561806" + version="1.1"> + + + + image/svg+xml + + + + + + + diff --git a/assets/svg/license_info.json b/assets/svg/license_info.json index f8e8840dbd..b3b653d4fc 100644 --- a/assets/svg/license_info.json +++ b/assets/svg/license_info.json @@ -1153,10 +1153,10 @@ "path": "scissors.svg", "license": "CC-BY 3.0", "authors": [ - "The noun project - Basith Ibrahi" + "The noun project - Icons8" ], "sources": [ - "https://commons.wikimedia.org/wiki/File:Media-floppy.svg" + "https://commons.wikimedia.org/wiki/File:Scissors_-_The_Noun_Project.svg" ] }, { diff --git a/assets/svg/loading.svg b/assets/svg/loading.svg index 0d884dfe9d..27dc3ac6c0 100644 --- a/assets/svg/loading.svg +++ b/assets/svg/loading.svg @@ -1,63 +1,78 @@ - - - - image/svg+xml - - - - - - - - + xmlns:dc="http://purl.org/dc/elements/1.1/" + xmlns:cc="http://creativecommons.org/ns#" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns="http://www.w3.org/2000/svg" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + viewBox="0 0 24.022156 24.021992" + version="1.1" + id="svg9" + sodipodi:docname="loading.svg" + inkscape:version="0.92.5 (2060ec1f9f, 2020-04-08)" + width="24.022156" + height="24.021992"> + + + + image/svg+xml + + + + + + + + + + + diff --git a/assets/svg/plus.svg b/assets/svg/plus.svg index dcff182373..385b171528 100644 --- a/assets/svg/plus.svg +++ b/assets/svg/plus.svg @@ -1,20 +1,59 @@ - - - + xmlns:dc="http://purl.org/dc/elements/1.1/" + xmlns:cc="http://creativecommons.org/ns#" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns="http://www.w3.org/2000/svg" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + width="97.287025" + height="97.287033" + viewBox="0 0 97.287025 97.287033" + version="1.1" + id="svg132" + style="fill:none" + sodipodi:docname="plus.svg" + inkscape:version="0.92.5 (2060ec1f9f, 2020-04-08)"> + + + + image/svg+xml + + + + + + + + diff --git a/assets/svg/scissors.svg b/assets/svg/scissors.svg index 1b4c786d42..7c8df5cd0d 100644 --- a/assets/svg/scissors.svg +++ b/assets/svg/scissors.svg @@ -1,69 +1 @@ - -image/svg+xml - - - - - - - - \ No newline at end of file + \ No newline at end of file diff --git a/assets/tagRenderings/questions.json b/assets/tagRenderings/questions.json index 4f2b63c2d7..509749aa03 100644 --- a/assets/tagRenderings/questions.json +++ b/assets/tagRenderings/questions.json @@ -2,6 +2,9 @@ "images": { "render": "{image_carousel()}{image_upload()}" }, + "wikipedia": { + "render": "{wikipedia():max-height:25rem}" + }, "reviews": { "render": "{reviews()}" }, @@ -21,7 +24,8 @@ "it": "Qual è il numero di telefono di {name}?", "pt_BR": "Qual o número de telefone de {name}?", "id": "Nomor telepon dari {name|?", - "pl": "Jaki jest numer telefonu do {name}?" + "pl": "Jaki jest numer telefonu do {name}?", + "pt": "Qual é o número de telefone de {name}?" }, "render": "{phone}", "freeform": { @@ -56,7 +60,8 @@ "de": "Was ist die Mail-Adresse von {name}?", "pt_BR": "Qual o endereço de e-mail de {name}?", "pl": "Jaki jest adres e-mail do {name}?", - "sv": "Vad är e-postadressen till {name}?" + "sv": "Vad är e-postadressen till {name}?", + "pt": "Qual é o endereço de e-mail de {name}?" }, "freeform": { "key": "email", @@ -77,7 +82,8 @@ "de": "Was ist die Website von {name}?", "pt_BR": "Qual o site de {name}?", "pl": "Jaka jest strona internetowa {name}?", - "sv": "Vad är webbplatsen för {name}?" + "sv": "Vad är webbplatsen för {name}?", + "pt": "Qual é o sítio web de {name}?" }, "render": "{website}", "freeform": { @@ -88,7 +94,9 @@ "wheelchair-access": { "question": { "nl": "Is deze plaats rolstoeltoegankelijk?", - "en": "Is this place accessible with a wheelchair?" + "en": "Is this place accessible with a wheelchair?", + "pt": "Este lugar é acessível a utilizadores de cadeiras de rodas?", + "pt_BR": "Este lugar é acessível com uma cadeira de rodas?" }, "mappings": [ { @@ -99,7 +107,9 @@ }, "then": { "nl": "Deze plaats is speciaal aangepast voor gebruikers van een rolstoel", - "en": "This place is specially adapated for wheelchair users" + "en": "This place is specially adapated for wheelchair users", + "pt": "Este lugar está especialmente adaptado para utilizadores de cadeira de rodas", + "pt_BR": "Este lugar é especialmente adaptado para usuários de cadeira de rodas" } }, { @@ -110,7 +120,9 @@ }, "then": { "nl": "Deze plaats is vlot bereikbaar met een rolstoel", - "en": "This place is easily reachable with a wheelchair" + "en": "This place is easily reachable with a wheelchair", + "pt": "Este lugar é de fácil acesso com uma cadeira de rodas", + "pt_BR": "Este lugar é facilmente acessível com uma cadeira de rodas" } }, { @@ -121,7 +133,9 @@ }, "then": { "nl": "Deze plaats is bereikbaar met een rolstoel, maar het is niet makkelijk", - "en": "It is possible to reach this place in a wheelchair, but it is not easy" + "en": "It is possible to reach this place in a wheelchair, but it is not easy", + "pt": "É possível chegar a este local em cadeira de rodas, mas não é fácil", + "pt_BR": "É possível chegar a esse local em uma cadeira de rodas, mas não é fácil" } }, { @@ -132,7 +146,45 @@ }, "then": { "nl": "Niet rolstoeltoegankelijk", - "en": "This place is not reachable with a wheelchair" + "en": "This place is not reachable with a wheelchair", + "pt": "Este lugar não é acessível com uma cadeira de rodas", + "pt_BR": "Este lugar não é alcançável com uma cadeira de rodas" + } + } + ] + }, + "dog-access": { + "question": { + "en": "Are dogs allowed in this business?", + "nl": "Zijn honden toegelaten in deze zaak?" + }, + "mappings": [ + { + "if": "dog=yes", + "then": { + "en": "Dogs are allowed", + "nl": "honden zijn toegelaten" + } + }, + { + "if": "dog=no", + "then": { + "en": "Dogs are not allowed", + "nl": "honden zijn niet toegelaten" + } + }, + { + "if": "dog=leashed", + "then": { + "en": "Dogs are allowed, but they have to be leashed", + "nl": "honden zijn enkel aan de leiband welkom" + } + }, + { + "if": "dog=unleashed", + "then": { + "en": "Dogs are allowed and can run around freely", + "nl": "honden zijn welkom en mogen vrij rondlopen" } } ] @@ -148,7 +200,8 @@ "it": "C'è ancora qualche informazione importante che non è stato possibile fornire nelle domande precedenti? Aggiungila qui.
Non ripetere informazioni già fornite", "de": "Gibt es noch etwas, das die vorhergehenden Fragen nicht abgedeckt haben? Hier wäre Platz dafür.
Bitte keine bereits erhobenen Informationen.", "pl": "Czy jest jeszcze coś istotnego, czego nie mogłeś podać w poprzednich pytaniach? Dodaj to tutaj.
Nie powtarzaj już podanych faktów", - "pt_BR": "Ainda há algo de relevante que não pôde dar nas perguntas anteriores? Adicione aqui.
Não repita fatos já declarados" + "pt_BR": "Ainda há algo de relevante que não pôde dar nas perguntas anteriores? Adicione aqui.
Não repita fatos já declarados", + "pt": "Ainda há algo de relevante que não tenha podido dar nas perguntas anteriores? Adicione-o aqui.
Não repita factos já declarados" }, "render": "{description}", "freeform": { @@ -167,7 +220,8 @@ "it": "Quali sono gli orari di apertura di {name}?", "pt_BR": "Qual o horário de funcionamento de {name}?", "pl": "Jakie są godziny otwarcia {name}?", - "sv": "Vilka är öppettiderna för {name}?" + "sv": "Vilka är öppettiderna för {name}?", + "pt": "Qual é o horário de funcionamento de {name}?" }, "render": { "de": "

Öffnungszeiten

{opening_hours_table(opening_hours)}", @@ -180,7 +234,8 @@ "it": "

Orari di apertura

{opening_hours_table(opening_hours)}", "pl": "

Godziny otwarcia

{opening_hours_table(opening_hours)}", "pt_BR": "

Horário de funcionamento

{opening_hours_table(opening_hours)}", - "sv": "

Öppettider

{opening_hours_table(opening_hours)}" + "sv": "

Öppettider

{opening_hours_table(opening_hours)}", + "pt": "

Horário de funcionamento

{opening_hours_table(opening_hours)}" }, "freeform": { "key": "opening_hours", @@ -190,7 +245,9 @@ "payment-options": { "question": { "en": "Which methods of payment are accepted here?", - "nl": "Welke betaalmiddelen worden hier geaccepteerd?" + "nl": "Welke betaalmiddelen worden hier geaccepteerd?", + "pt": "Que métodos de pagamento são aceites aqui?", + "pt_BR": "Quais métodos de pagamento são aceitos aqui?" }, "multiAnswer": true, "mappings": [ @@ -199,7 +256,9 @@ "ifnot": "payment:cash=no", "then": { "en": "Cash is accepted here", - "nl": "Cash geld wordt hier aanvaard" + "nl": "Cash geld wordt hier aanvaard", + "pt": "Aceitam pagamento com dinheiro aqui", + "pt_BR": "Dinheiro é aceito aqui" } }, { @@ -207,7 +266,9 @@ "ifnot": "payment:cards=no", "then": { "en": "Payment cards are accepted here", - "nl": "Betalen met bankkaarten kan hier" + "nl": "Betalen met bankkaarten kan hier", + "pt": "Aceitam pagamento com cartões bancários aqui", + "pt_BR": "Cartões de pagamento são aceitos aqui" } } ] @@ -230,7 +291,8 @@ "fr": "À quel étage se situe l’élément ?", "pl": "Na jakim poziomie znajduje się ta funkcja?", "pt_BR": "Em que nível esse recurso está localizado?", - "ru": "На каком этаже находится этот объект?" + "ru": "На каком этаже находится этот объект?", + "pt": "Em que nível se encontra este elemento?" }, "render": { "en": "Located on the {level}th floor", @@ -241,7 +303,8 @@ "zh_Hant": "位於 {level} 樓", "fr": "Étage {level}", "pl": "Znajduje się na {level} piętrze", - "sv": "Ligger på {level}:e våningen" + "sv": "Ligger på {level}:e våningen", + "pt": "Está no {nível}º andar" }, "freeform": { "key": "level", @@ -258,7 +321,8 @@ "zh_Hant": "位於地下", "fr": "En sous-sol", "pl": "Znajduje się pod ziemią", - "sv": "Ligger under jorden" + "sv": "Ligger under jorden", + "pt": "Está no subsolo" }, "hideInAnswer": true }, @@ -273,7 +337,8 @@ "zh_Hant": "位於 1 樓", "fr": "Rez-de-chaussée", "pl": "Znajduje się na parterze", - "sv": "Ligger på bottenvåningen" + "sv": "Ligger på bottenvåningen", + "pt": "Está ao nível do rés-do-chão" } }, { @@ -288,7 +353,8 @@ "zh_Hant": "位於 1 樓", "fr": "Rez-de-chaussée", "pl": "Znajduje się na parterze", - "sv": "Ligger på bottenvåningen" + "sv": "Ligger på bottenvåningen", + "pt": "Está ao nível do rés-do-chão" } }, { @@ -302,7 +368,8 @@ "zh_Hant": "位於 2 樓", "fr": "Premier étage", "pl": "Znajduje się na pierwszym piętrze", - "sv": "Ligger på första våningen" + "sv": "Ligger på första våningen", + "pt": "Está no primeiro andar" } } ] diff --git a/assets/themes/benches/benches.json b/assets/themes/benches/benches.json index a43292dbf9..d94f88597a 100644 --- a/assets/themes/benches/benches.json +++ b/assets/themes/benches/benches.json @@ -52,7 +52,7 @@ "startLat": 0, "startLon": 0, "startZoom": 1, - "widenFactor": 0.05, + "widenFactor": 1.5, "socialImage": "", "layers": [ "bench", diff --git a/assets/themes/bicyclelib/bicyclelib.json b/assets/themes/bicyclelib/bicyclelib.json index fe40e2d342..bc251907b8 100644 --- a/assets/themes/bicyclelib/bicyclelib.json +++ b/assets/themes/bicyclelib/bicyclelib.json @@ -40,9 +40,14 @@ "startLat": 0, "startLon": 0, "startZoom": 1, - "widenFactor": 0.05, + "widenFactor": 1.5, "roamingRenderings": [], "layers": [ - "bicycle_library" + { + "builtin": "bicycle_library", + "override": { + "minZoom": 0 + } + } ] } \ No newline at end of file diff --git a/assets/themes/binoculars/binoculars.json b/assets/themes/binoculars/binoculars.json index 0f9817e46d..75622d3a5b 100644 --- a/assets/themes/binoculars/binoculars.json +++ b/assets/themes/binoculars/binoculars.json @@ -22,7 +22,7 @@ "startLat": 0, "startLon": 0, "startZoom": 1, - "widenFactor": 0.05, + "widenFactor": 1.5, "socialImage": "", "layers": [ "binocular" diff --git a/assets/themes/bookcases/bookcases.json b/assets/themes/bookcases/bookcases.json index 625dfa0ead..0862fbb379 100644 --- a/assets/themes/bookcases/bookcases.json +++ b/assets/themes/bookcases/bookcases.json @@ -39,7 +39,7 @@ "startLat": 0, "startLon": 0, "startZoom": 1, - "widenFactor": 0.05, + "widenFactor": 1, "roamingRenderings": [], "layers": [ "public_bookcase" diff --git a/assets/themes/buurtnatuur/buurtnatuur.json b/assets/themes/buurtnatuur/buurtnatuur.json index 9c82e4d443..9c4f2a1894 100644 --- a/assets/themes/buurtnatuur/buurtnatuur.json +++ b/assets/themes/buurtnatuur/buurtnatuur.json @@ -25,7 +25,7 @@ "startLat": 50.8435, "startLon": 4.3688, "startZoom": 16, - "widenFactor": 0.01, + "widenFactor": 1.2, "socialImage": "./assets/themes/buurtnatuur/social_image.jpg", "layers": [ { diff --git a/assets/themes/cafes_and_pubs/cafes_and_pubs.json b/assets/themes/cafes_and_pubs/cafes_and_pubs.json index 1f9b704f89..c7ec682d10 100644 --- a/assets/themes/cafes_and_pubs/cafes_and_pubs.json +++ b/assets/themes/cafes_and_pubs/cafes_and_pubs.json @@ -18,7 +18,7 @@ "startLat": 0, "startLon": 0, "startZoom": 1, - "widenFactor": 0.05, + "widenFactor": 1.5, "socialImage": "", "layers": [ "cafe_pub" diff --git a/assets/themes/campersite/campersite.json b/assets/themes/campersite/campersite.json index d5730d2aa4..f9951ed0a4 100644 --- a/assets/themes/campersite/campersite.json +++ b/assets/themes/campersite/campersite.json @@ -47,7 +47,7 @@ "startLat": 43.14, "startLon": 3.14, "startZoom": 14, - "widenFactor": 0.05, + "widenFactor": 1.5, "socialImage": "./assets/themes/campersite/Bar%C3%9Fel_Wohnmobilstellplatz.jpg", "layers": [ { diff --git a/assets/themes/charging_stations/charging_stations.json b/assets/themes/charging_stations/charging_stations.json index b193b4dafd..16745dc60c 100644 --- a/assets/themes/charging_stations/charging_stations.json +++ b/assets/themes/charging_stations/charging_stations.json @@ -39,7 +39,7 @@ "startLat": 0, "startLon": 0, "startZoom": 1, - "widenFactor": 0.05, + "widenFactor": 1.5, "socialImage": "", "defaultBackgroundId": "CartoDB.Voyager", "layers": [ diff --git a/assets/themes/climbing/climbing.json b/assets/themes/climbing/climbing.json index 08885ebf00..edfa859fae 100644 --- a/assets/themes/climbing/climbing.json +++ b/assets/themes/climbing/climbing.json @@ -48,7 +48,7 @@ "startLat": 0, "startLon": 0, "startZoom": 1, - "widenFactor": 0.05, + "widenFactor": 1.5, "socialImage": "", "layers": [ { diff --git a/assets/themes/cycle_highways/cycle_highways.json b/assets/themes/cycle_highways/cycle_highways.json index 180b028c10..08b7c8556e 100644 --- a/assets/themes/cycle_highways/cycle_highways.json +++ b/assets/themes/cycle_highways/cycle_highways.json @@ -21,7 +21,7 @@ "clustering": { "maxZoom": 1 }, - "widenFactor": 0.005, + "widenFactor": 1.1, "enableDownload": true, "enablePdfDownload": true, "layers": [ @@ -143,6 +143,7 @@ }, "filter": [ { + "id": "name-alt", "options": [ { "question": "Name contains 'alt'", @@ -151,6 +152,7 @@ ] }, { + "id": "name-wenslijn", "options": [ { "question": "Name contains 'wenslijn'", @@ -159,6 +161,7 @@ ] }, { + "id": "name-omleiding", "options": [ { "question": "Name contains 'omleiding'", @@ -167,6 +170,7 @@ ] }, { + "id": "ref-alt", "options": [ { "question": "Reference contains 'alt'", @@ -175,6 +179,7 @@ ] }, { + "id": "missing_link", "options": [ { "question": "No filter" @@ -194,6 +199,7 @@ ] }, { + "id": "proposed", "options": [ { "question": "No filter" diff --git a/assets/themes/cycle_infra/cycle_infra.json b/assets/themes/cycle_infra/cycle_infra.json index b91804ea97..8af56b8866 100644 --- a/assets/themes/cycle_infra/cycle_infra.json +++ b/assets/themes/cycle_infra/cycle_infra.json @@ -24,7 +24,7 @@ "startLat": 51, "startLon": 3.75, "startZoom": 11, - "widenFactor": 0.05, + "widenFactor": 1.5, "socialImage": "./assets/themes/cycle_infra/cycle-infra.svg", "enableDownload": true, "layers": [ diff --git a/assets/themes/cyclestreets/cyclestreets.json b/assets/themes/cyclestreets/cyclestreets.json index 8b607ea37a..39f75ac78e 100644 --- a/assets/themes/cyclestreets/cyclestreets.json +++ b/assets/themes/cyclestreets/cyclestreets.json @@ -34,7 +34,11 @@ "startZoom": 14, "startLon": 3.2228, "maintainer": "MapComplete", - "widenfactor": 0.01, + "widenfactor": 2, + "clustering": { + "maxZoom": 12, + "minNeededElements": 200 + }, "roamingRenderings": [ { "question": { @@ -276,8 +280,10 @@ }, "tagRenderings": [ "images" - ], - "allowSplit": false + ] } - ] + ], + "overrideAll": { + "allowSplit": true + } } \ No newline at end of file diff --git a/assets/themes/cyclofix/cyclofix.json b/assets/themes/cyclofix/cyclofix.json index 2e1016d77b..47a6267368 100644 --- a/assets/themes/cyclofix/cyclofix.json +++ b/assets/themes/cyclofix/cyclofix.json @@ -40,7 +40,7 @@ "defaultBackgroundId": "CartoDB.Voyager", "startLon": 0, "startZoom": 1, - "widenFactor": 0.05, + "widenFactor": 2, "socialImage": "assets/themes/cyclofix/logo.svg", "layers": [ "bike_cafe", diff --git a/assets/themes/drinking_water/drinking_water.json b/assets/themes/drinking_water/drinking_water.json index 9dee5a8a36..2134d0fba7 100644 --- a/assets/themes/drinking_water/drinking_water.json +++ b/assets/themes/drinking_water/drinking_water.json @@ -34,7 +34,7 @@ "defaultBackgroundId": "CartoDB.Voyager", "startLon": 4.351697, "startZoom": 16, - "widenFactor": 0.05, + "widenFactor": 2, "layers": [ "drinking_water" ], diff --git a/assets/themes/etymology.json b/assets/themes/etymology.json new file mode 100644 index 0000000000..a8ca9dfc9c --- /dev/null +++ b/assets/themes/etymology.json @@ -0,0 +1,86 @@ +{ + "id": "etymology", + "title": { + "en": "Open Etymology Map", + "nl": "Open Etymology-kaart" + }, + "shortDescription": { + "en": "What is the origin of a toponym?", + "nl": "Wat is de oorsprong van een plaatsnaam?" + }, + "description": { + "en": "On this map, you can see what an object is named after. The streets, buildings, ... come from OpenStreetMap which got linked with Wikidata. The information comes from Wpikipedia.", + "nl": "Op deze kaart zie je waar een plaats naar is vernoemd. De straten, gebouwen, ... komen uit OpenStreetMap, waar een link naar Wikidata werd gelegd. De informatie komt uit wikipedia." + }, + "language": [ + "en", + "nl" + ], + "maintainer": "", + "icon": "./assets/svg/bug.svg", + "version": "0", + "startLat": 0, + "startLon": 0, + "startZoom": 1, + "widenFactor": 2, + "socialImage": "", + "layers": [ + { + "id": "has_a_name", + "name": { + "en": "Has etymolgy", + "nl": "Heeft etymology info" + }, + "minzoom": 12, + "source": { + "osmTags": { + "or": [ + "name:etymology:wikidata~*", + "name:etymology~*" + ] + } + }, + "title": { + "render": { + "*": "{name}" + } + }, + "description": { + "en": "All objects which have an etymology known", + "nl": "Alle lagen met een gelinkt etymology" + }, + "tagRenderings": [ + { + "id": "simple etymology", + "render": { + "en": "Named after {name:etymology}", + "nl": "Vernoemd naar {name:etymology}" + }, + "freeform": { + "key": "name:etymology" + } + }, + { + "id": "wikipedia-etymology", + "render": { + "*": "{wikipedia(name:etymology:wikidata):max-height:20rem}" + } + } + ], + "icon": { + "render": "./assets/svg/bug.svg" + }, + "width": { + "render": "8" + }, + "iconSize": { + "render": "40,40,center" + }, + "color": { + "render": "#00f" + }, + "presets": [] + } + ], + "roamingRenderings": [] +} \ No newline at end of file diff --git a/assets/themes/facadegardens/facadegardens.json b/assets/themes/facadegardens/facadegardens.json index 34c574f5c5..bb3155e590 100644 --- a/assets/themes/facadegardens/facadegardens.json +++ b/assets/themes/facadegardens/facadegardens.json @@ -38,7 +38,7 @@ "startLat": 51.02768, "startLon": 4.480705, "startZoom": 15, - "widenFactor": 0.05, + "widenFactor": 1.5, "socialImage": "", "layers": [ { diff --git a/assets/themes/food/food.json b/assets/themes/food/food.json index 416f9d1614..fed26b5607 100644 --- a/assets/themes/food/food.json +++ b/assets/themes/food/food.json @@ -18,7 +18,7 @@ "startLat": 0, "startLon": 0, "startZoom": 1, - "widenFactor": 0.05, + "widenFactor": 3, "socialImage": "", "layers": [ "food" diff --git a/assets/themes/fritures/fritures.json b/assets/themes/fritures/fritures.json index 33d83c0f5d..fec9038cc6 100644 --- a/assets/themes/fritures/fritures.json +++ b/assets/themes/fritures/fritures.json @@ -24,7 +24,7 @@ "startLat": 0, "startLon": 0, "startZoom": 1, - "widenFactor": 0.05, + "widenFactor": 3, "socialImage": "", "layers": [ { diff --git a/assets/themes/fruit_trees/fruit_trees.json b/assets/themes/fruit_trees/fruit_trees.json index f5c18a0bad..68ce79228d 100644 --- a/assets/themes/fruit_trees/fruit_trees.json +++ b/assets/themes/fruit_trees/fruit_trees.json @@ -18,7 +18,7 @@ "startLat": 0, "startLon": 0, "startZoom": 1, - "widenFactor": 0.001, + "widenFactor": 2, "socialImage": "", "hideFromOverview": true, "layers": [ diff --git a/assets/themes/ghostbikes/ghostbikes.json b/assets/themes/ghostbikes/ghostbikes.json index 229a32751f..7c9aec38bd 100644 --- a/assets/themes/ghostbikes/ghostbikes.json +++ b/assets/themes/ghostbikes/ghostbikes.json @@ -52,7 +52,7 @@ "startZoom": 1, "startLat": 0, "startLon": 0, - "widenFactor": 0.1, + "widenFactor": 5, "layers": [ "ghost_bike" ], diff --git a/assets/themes/grb.json b/assets/themes/grb.json index c1c232985f..a0f32cdc6e 100644 --- a/assets/themes/grb.json +++ b/assets/themes/grb.json @@ -18,7 +18,7 @@ "startLat": 51.2132, "startLon": 3.231, "startZoom": 14, - "widenFactor": 0.05, + "widenFactor": 2, "cacheTimeout": 3600, "socialImage": "", "layers": [ diff --git a/assets/themes/hackerspaces/hackerspaces.json b/assets/themes/hackerspaces/hackerspaces.json index 051742429c..5fd86804e7 100644 --- a/assets/themes/hackerspaces/hackerspaces.json +++ b/assets/themes/hackerspaces/hackerspaces.json @@ -18,7 +18,7 @@ "startLat": 0, "startLon": 0, "startZoom": 1, - "widenFactor": 0.05, + "widenFactor": 5, "socialImage": "", "layers": [ { diff --git a/assets/themes/hailhydrant/hailhydrant.json b/assets/themes/hailhydrant/hailhydrant.json index d3c165c44e..3a94dbb1be 100644 --- a/assets/themes/hailhydrant/hailhydrant.json +++ b/assets/themes/hailhydrant/hailhydrant.json @@ -36,7 +36,7 @@ "startLat": 13.67801, "startLon": 121.6625, "startZoom": 6, - "widenFactor": 0.05, + "widenFactor": 3, "socialImage": "", "layers": [ { diff --git a/assets/themes/maps/maps.json b/assets/themes/maps/maps.json index e34cdce30b..52d757a9da 100644 --- a/assets/themes/maps/maps.json +++ b/assets/themes/maps/maps.json @@ -36,7 +36,7 @@ "startLat": 0, "startLon": 0, "startZoom": 1, - "widenFactor": 0.05, + "widenFactor": 5, "socialImage": "", "layers": [ "map" diff --git a/assets/themes/nature/nature.json b/assets/themes/nature/nature.json index 9aeb9e4983..0f8004e2c0 100644 --- a/assets/themes/nature/nature.json +++ b/assets/themes/nature/nature.json @@ -18,7 +18,7 @@ "startLat": 51.20875, "startLon": 3.22435, "startZoom": 12, - "widenFactor": 0.05, + "widenFactor": 2, "socialImage": "", "layers": [ "drinking_water", diff --git a/assets/themes/natuurpunt/natuurpunt.json b/assets/themes/natuurpunt/natuurpunt.json index 4b45e98993..4b49e7465f 100644 --- a/assets/themes/natuurpunt/natuurpunt.json +++ b/assets/themes/natuurpunt/natuurpunt.json @@ -17,18 +17,25 @@ "nl", "en" ], + "mustHaveLanguage": [ + "nl" + ], "maintainer": "", "icon": "./assets/themes/natuurpunt/natuurpunt.png", "version": "0", "startLat": 51.20875, "startLon": 3.22435, "startZoom": 15, - "widenFactor": 0.05, + "widenFactor": 2, "socialImage": "", "defaultBackgroundId": "CartoDB.Positron", "enablePdfDownload": true, "enableDownload": true, "hideFromOverview": true, + "clustering": { + "#": "Disable clustering for this theme", + "maxZoom": 0 + }, "layers": [ { "#": "Nature reserve with geometry, z>=13", @@ -55,6 +62,7 @@ { "#": "Nature reserve overview from cache, points only, z < 13", "builtin": "nature_reserve", + "wayHandling": 1, "override": { "source": { "osmTags": { @@ -63,12 +71,14 @@ ] }, "geoJson": "https://pietervdvn.github.io/natuurpunt_cache/natuurpunt_nature_reserve_points.geojson", + "geoJsonZoomLevel": 0, "isOsmCache": "duplicate" }, "minzoom": 1, "icon": { "render": "circle:#FE6F32;./assets/themes/natuurpunt/nature_reserve.svg" - } + }, + "presets": [] } }, { diff --git a/assets/themes/observation_towers/observation_towers.json b/assets/themes/observation_towers/observation_towers.json index 2bdd0cbad6..3ab5a6b58f 100644 --- a/assets/themes/observation_towers/observation_towers.json +++ b/assets/themes/observation_towers/observation_towers.json @@ -22,7 +22,7 @@ "startLat": 0, "startLon": 0, "startZoom": 1, - "widenFactor": 0.05, + "widenFactor": 5, "socialImage": "", "layers": [ "observation_tower" diff --git a/assets/themes/parkings/parkings.json b/assets/themes/parkings/parkings.json index bb25e8bfd2..ca322052ce 100644 --- a/assets/themes/parkings/parkings.json +++ b/assets/themes/parkings/parkings.json @@ -22,7 +22,7 @@ "startLat": 51.20875, "startLon": 3.22435, "startZoom": 12, - "widenFactor": 0.05, + "widenFactor": 1.2, "socialImage": "", "layers": [ "parking" diff --git a/assets/themes/personal/personal.json b/assets/themes/personal/personal.json index 02e26cdbd2..2896e5fbc4 100644 --- a/assets/themes/personal/personal.json +++ b/assets/themes/personal/personal.json @@ -12,7 +12,7 @@ "zh_Hant": "個人化主題" }, "description": { - "en": "Create a personal theme based on all the available layers of all themes", + "en": "Create a personal theme based on all the available layers of all themes. In order to show some data, open layer selection", "nl": "Stel je eigen thema samen door lagen te combineren van alle andere themas", "es": "Crea una interficie basada en todas las capas disponibles de todas las interficies", "ca": "Crea una interfície basada en totes les capes disponibles de totes les interfícies", @@ -37,11 +37,14 @@ ], "maintainer": "MapComplete", "icon": "./assets/svg/addSmall.svg", + "clustering": { + "maxZoom": 19 + }, "version": "0", "startLat": 0, "startLon": 0, "startZoom": 16, - "widenFactor": 0.05, + "widenFactor": 1.2, "layers": [], "roamingRenderings": [] } \ No newline at end of file diff --git a/assets/themes/play_forests/play_forests.json b/assets/themes/play_forests/play_forests.json index bd824ee670..5c65a702a5 100644 --- a/assets/themes/play_forests/play_forests.json +++ b/assets/themes/play_forests/play_forests.json @@ -19,7 +19,7 @@ "startLon": 0, "startZoom": 1, "hideFromOverview": true, - "widenFactor": 0.05, + "widenFactor": 3, "socialImage": "", "layers": [ "play_forest" diff --git a/assets/themes/playgrounds/playgrounds.json b/assets/themes/playgrounds/playgrounds.json index 418aa36bc4..2908a88b47 100644 --- a/assets/themes/playgrounds/playgrounds.json +++ b/assets/themes/playgrounds/playgrounds.json @@ -38,7 +38,7 @@ "startLat": 50.535, "startLon": 4.399, "startZoom": 13, - "widenFactor": 0.05, + "widenFactor": 5, "socialImage": "", "layers": [ "playground" diff --git a/assets/themes/shops/shops.json b/assets/themes/shops/shops.json index e2db84ab77..bae4c3cbd6 100644 --- a/assets/themes/shops/shops.json +++ b/assets/themes/shops/shops.json @@ -34,7 +34,7 @@ "startLat": 0, "startLon": 0, "startZoom": 1, - "widenFactor": 0.05, + "widenFactor": 3, "socialImage": "", "layers": [ { diff --git a/assets/themes/speelplekken/speelplekken.json b/assets/themes/speelplekken/speelplekken.json index 1c669b6b56..b0caffcc9c 100644 --- a/assets/themes/speelplekken/speelplekken.json +++ b/assets/themes/speelplekken/speelplekken.json @@ -22,7 +22,7 @@ "startLat": 51.17174, "startLon": 4.449462, "startZoom": 12, - "widenFactor": 0.05, + "widenFactor": 1.2, "socialImage": "./assets/themes/speelplekken/social_image.jpg", "defaultBackgroundId": "CartoDB.Positron", "layers": [ @@ -152,7 +152,7 @@ ] }, "geoJson": "https://pietervdvn.github.io/speelplekken_cache/speelplekken_{layer}_{z}_{x}_{y}.geojson", - "geoJsonZoomLevel": 14, + "geoJsonZoomLevel": 11, "isOsmCache": true }, "title": { diff --git a/assets/themes/sport_pitches/sport_pitches.json b/assets/themes/sport_pitches/sport_pitches.json index 7f1fdeebda..4db24cdd69 100644 --- a/assets/themes/sport_pitches/sport_pitches.json +++ b/assets/themes/sport_pitches/sport_pitches.json @@ -37,7 +37,7 @@ "startLat": 0, "startLon": 0, "startZoom": 1, - "widenFactor": 0.05, + "widenFactor": 2, "socialImage": "", "layers": [ "sport_pitch" diff --git a/assets/themes/surveillance/surveillance.json b/assets/themes/surveillance/surveillance.json index 001045fd11..a61633b23b 100644 --- a/assets/themes/surveillance/surveillance.json +++ b/assets/themes/surveillance/surveillance.json @@ -37,7 +37,7 @@ "startLat": 0, "startLon": 0, "startZoom": 1, - "widenFactor": 0.05, + "widenFactor": 2, "socialImage": "", "defaultBackgroundId": "osm", "layers": [ diff --git a/assets/themes/toerisme_vlaanderen/license_info.json b/assets/themes/toerisme_vlaanderen/license_info.json new file mode 100644 index 0000000000..374abea1e6 --- /dev/null +++ b/assets/themes/toerisme_vlaanderen/license_info.json @@ -0,0 +1,12 @@ +[ + { + "path": "logo.png", + "license": "Logo (all rights reserved)", + "authors": [ + "Toerisme Vlaanderen" + ], + "sources": [ + "https://www.toerismevlaanderen.be/" + ] + } +] \ No newline at end of file diff --git a/assets/themes/toerisme_vlaanderen/logo.png b/assets/themes/toerisme_vlaanderen/logo.png new file mode 100644 index 0000000000..6d0d16dd84 Binary files /dev/null and b/assets/themes/toerisme_vlaanderen/logo.png differ diff --git a/assets/themes/toerisme_vlaanderen/toerisme_vlaanderen.json b/assets/themes/toerisme_vlaanderen/toerisme_vlaanderen.json index be5567d5dc..2b303ea145 100644 --- a/assets/themes/toerisme_vlaanderen/toerisme_vlaanderen.json +++ b/assets/themes/toerisme_vlaanderen/toerisme_vlaanderen.json @@ -14,13 +14,13 @@ "nl": "Een kaart om toeristisch relevante info op aan te duiden" }, "description": { - "nl": "Op deze kaart kan je info zien voor toeristen en zelf aanpasingen maken, zichtbaar voor iedereen" + "nl": "Op deze kaart kan je info zien die relevant is voor toerisme, zoals:
  • Eetgelegenheden
  • Cafés en bars
  • (Fiets)oplaadpunten
  • Fietspompen, fietserverhuur en fietswinkels
  • Uitkijktorens
  • ...
Zie je fouten op de kaart? Dan kan je zelf makkelijk aanpasingen maken, die zichtbaar zijn voor iedereen. Hiervoor dien je een gratis OpenStreetMap account voor te maken.

Met de steun van Toerisme Vlaanderen" }, "icon": "./assets/svg/star.svg", "startZoom": 8, "startLat": 50.8536, "startLon": 4.433, - "widenFactor": 0.2, + "widenFactor": 1.5, "layers": [ { "builtin": [ diff --git a/assets/themes/toilets/toilets.json b/assets/themes/toilets/toilets.json index e343d9ea20..1d1567de42 100644 --- a/assets/themes/toilets/toilets.json +++ b/assets/themes/toilets/toilets.json @@ -35,7 +35,7 @@ "startZoom": 12, "startLat": 51.2095, "startLon": 3.2222, - "widenFactor": 0.05, + "widenFactor": 3, "icon": "./assets/themes/toilets/toilets.svg", "layers": [ "toilet" diff --git a/assets/themes/trees/trees.json b/assets/themes/trees/trees.json index ea26fdeca8..c609a43f11 100644 --- a/assets/themes/trees/trees.json +++ b/assets/themes/trees/trees.json @@ -45,10 +45,11 @@ "startLat": 50.642, "startLon": 4.482, "startZoom": 8, - "widenFactor": 0.01, + "widenFactor": 1.01, "socialImage": "./assets/themes/trees/logo.svg", "clustering": { - "maxZoom": 18 + "maxZoom": 19, + "minNeededElements": 25 }, "layers": [ "tree_node" diff --git a/assets/themes/uk_addresses/housenumber_add.svg b/assets/themes/uk_addresses/housenumber_add.svg new file mode 100644 index 0000000000..e156438b11 --- /dev/null +++ b/assets/themes/uk_addresses/housenumber_add.svg @@ -0,0 +1,289 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/themes/uk_addresses/housenumber_ok.svg b/assets/themes/uk_addresses/housenumber_ok.svg new file mode 100644 index 0000000000..bf5f1b9db0 --- /dev/null +++ b/assets/themes/uk_addresses/housenumber_ok.svg @@ -0,0 +1,75 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + diff --git a/assets/themes/uk_addresses/housenumber_text.svg b/assets/themes/uk_addresses/housenumber_text.svg new file mode 100644 index 0000000000..56d57373ee --- /dev/null +++ b/assets/themes/uk_addresses/housenumber_text.svg @@ -0,0 +1,72 @@ + + + + + + image/svg+xml + + + + + + + + + OK diff --git a/assets/themes/uk_addresses/housenumber_unknown.svg b/assets/themes/uk_addresses/housenumber_unknown.svg new file mode 100644 index 0000000000..0c6c0e5c46 --- /dev/null +++ b/assets/themes/uk_addresses/housenumber_unknown.svg @@ -0,0 +1,70 @@ + + + + + + image/svg+xml + + + + + + + + + + + + diff --git a/assets/themes/uk_addresses/housenumber_unknown_small.svg b/assets/themes/uk_addresses/housenumber_unknown_small.svg new file mode 100644 index 0000000000..398ce8f723 --- /dev/null +++ b/assets/themes/uk_addresses/housenumber_unknown_small.svg @@ -0,0 +1,60 @@ + + + + + + image/svg+xml + + + + + + + + + diff --git a/assets/themes/uk_addresses/islington_small_piece.geojson b/assets/themes/uk_addresses/islington_small_piece.geojson new file mode 100644 index 0000000000..ced6a1fb13 --- /dev/null +++ b/assets/themes/uk_addresses/islington_small_piece.geojson @@ -0,0 +1,2163 @@ + +{ + "type": "FeatureCollection", + "generator": "JOSM", + "features": [ + { + "type": "Feature", + "properties": { + "inspireid": "44760782", + "uprn_count": "19" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08528530407, + 51.52103754846 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "44760166", + "uprn_count": "18" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08518862375, + 51.52302887251 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "53875715", + "uprn_count": "176" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08768220681, + 51.52027207654 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "48199892", + "uprn_count": "32" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.09051161088, + 51.52328524465 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "44760298", + "uprn_count": "21" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08519096645, + 51.52229137569 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "44760648", + "uprn_count": "43" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.09039984580, + 51.52168966695 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "44760158", + "uprn_count": "4" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08979845152, + 51.52470373164 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "44760268", + "uprn_count": "1" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08584518403, + 51.52362781792 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "44760606", + "uprn_count": "34" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08574285775, + 51.52447487890 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "44760147", + "uprn_count": "13" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08478417875, + 51.52230226544 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "44760181", + "uprn_count": "1" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08597751642, + 51.52262480980 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "59756691", + "uprn_count": "68" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.09000674703, + 51.52412334790 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "53839893", + "uprn_count": "12" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08632490840, + 51.51956380364 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "44760254", + "uprn_count": "5" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08445931332, + 51.52362994921 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "44760985", + "uprn_count": "1" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08550522898, + 51.52481338112 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "53517508", + "uprn_count": "1" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08531729267, + 51.52055437518 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "44760172", + "uprn_count": "7" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08621709906, + 51.52245066605 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "53815743", + "uprn_count": "28" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08609559738, + 51.52000280555 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "44760229", + "uprn_count": "2" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08466859613, + 51.52247735237 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "52054693", + "uprn_count": "4" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08869160634, + 51.52496110557 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "44760329", + "uprn_count": "6" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08610136910, + 51.52318693588 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "44760753", + "uprn_count": "9" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08538513949, + 51.52424009690 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "44760164", + "uprn_count": "13" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.09008514341, + 51.52505292621 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "44760216", + "uprn_count": "190" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08700282926, + 51.52503663329 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "44760279", + "uprn_count": "3" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08530920735, + 51.52549852437 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "44613792", + "uprn_count": "72" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08833908631, + 51.52631952661 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "44760639", + "uprn_count": "7" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08580868931, + 51.52429800891 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "44760153", + "uprn_count": "4" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08456440427, + 51.52329504288 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "44760200", + "uprn_count": "95" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08770188351, + 51.52407460026 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "47582675", + "uprn_count": "8" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08892578863, + 51.52706921088 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "44760263", + "uprn_count": "5" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08583263531, + 51.52548367268 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "44613655", + "uprn_count": "18" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08843733386, + 51.52669877413 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "44760564", + "uprn_count": "4" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.09046694451, + 51.52125764642 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "44760143", + "uprn_count": "1" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08576039998, + 51.52479998964 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "57943078", + "uprn_count": "80" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08990077541, + 51.52590434415 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "44760178", + "uprn_count": "13" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08479378350, + 51.52288142387 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "44760247", + "uprn_count": "16" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08570423028, + 51.52383831047 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "60347715", + "uprn_count": "6" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08913137569, + 51.52638282816 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "44760169", + "uprn_count": "8" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08741982002, + 51.52232472527 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "53876178", + "uprn_count": "14" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08848929384, + 51.51968662476 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "58831132", + "uprn_count": "120" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08811742827, + 51.52524678003 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "44760702", + "uprn_count": "17" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08626679662, + 51.52229285532 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "56996893", + "uprn_count": "5" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08961930563, + 51.52736429296 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "44760161", + "uprn_count": "30" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08903973308, + 51.52443351442 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "57212602", + "uprn_count": "6" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.09025128250, + 51.52349660479 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "44760214", + "uprn_count": "30" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08491766007, + 51.52195690215 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "44760274", + "uprn_count": "6" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08710416098, + 51.52394294309 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "44613782", + "uprn_count": "11" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08630939921, + 51.52567781493 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "44760635", + "uprn_count": "5" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08591747289, + 51.52406421610 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "44760150", + "uprn_count": "12" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08736624492, + 51.52184217908 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "44760190", + "uprn_count": "1" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08950436522, + 51.52301269883 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "53842347", + "uprn_count": "8" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08629952517, + 51.51902467199 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "44760261", + "uprn_count": "7" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08870422830, + 51.52481415717 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "44613643", + "uprn_count": "108" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08522752552, + 51.52590169102 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "44760506", + "uprn_count": "12" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08680870061, + 51.52270885497 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "53529840", + "uprn_count": "14" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08752332940, + 51.52076211974 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "55694053", + "uprn_count": "7" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08582489157, + 51.52423214013 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "44760232", + "uprn_count": "5" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08460415534, + 51.52252827681 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "55841333", + "uprn_count": "23" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.09046401124, + 51.52500845422 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "44760366", + "uprn_count": "8" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08665901033, + 51.52347731730 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "44760167", + "uprn_count": "13" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08523817483, + 51.52199801036 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "53875840", + "uprn_count": "21" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08790573136, + 51.52005170198 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "44760300", + "uprn_count": "3" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08523971621, + 51.52550166079 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "44760673", + "uprn_count": "13" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08740702488, + 51.52227145851 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "44760159", + "uprn_count": "37" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.09015395806, + 51.52469077487 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "44760210", + "uprn_count": "10" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08735406455, + 51.52190815063 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "44760269", + "uprn_count": "3" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08517606458, + 51.52360663718 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "44613767", + "uprn_count": "38" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08894666543, + 51.52728330710 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "44760630", + "uprn_count": "9" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08596436686, + 51.52399163027 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "44760148", + "uprn_count": "90" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08559244253, + 51.52179703997 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "53555715", + "uprn_count": "23" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.09069974009, + 51.52099643929 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "54843927", + "uprn_count": "36" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08536900467, + 51.52258204957 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "44760186", + "uprn_count": "60" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08749154622, + 51.52123763708 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "59797478", + "uprn_count": "12" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08460319625, + 51.52285695300 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "53842288", + "uprn_count": "6" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08651616784, + 51.51911374360 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "44760258", + "uprn_count": "5" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08880030229, + 51.52508388296 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "44760450", + "uprn_count": "12" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08877219383, + 51.52489267275 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "44760989", + "uprn_count": "3" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.09008638232, + 51.52338474772 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "53523418", + "uprn_count": "36" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08833393118, + 51.52076103473 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "44760173", + "uprn_count": "17" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08907044601, + 51.52478857712 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "56354285", + "uprn_count": "41" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08708248675, + 51.51950432943 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "53815989", + "uprn_count": "12" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08712150084, + 51.52007985063 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "44760230", + "uprn_count": "3" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08459866660, + 51.52256307871 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "53894914", + "uprn_count": "53" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08932705394, + 51.52020780589 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "44760340", + "uprn_count": "57" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08700821071, + 51.52470369200 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "44760757", + "uprn_count": "94" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08629573687, + 51.52185922605 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "44760165", + "uprn_count": "7" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08568241478, + 51.52253231799 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "44760289", + "uprn_count": "1" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08494465189, + 51.52548899801 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "44760645", + "uprn_count": "6" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08589810776, + 51.52411473063 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "57797640", + "uprn_count": "12" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08496788091, + 51.52179114697 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "56970529", + "uprn_count": "1" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08673035105, + 51.52606173015 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "44760156", + "uprn_count": "11" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08743964400, + 51.52240633893 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "53673626", + "uprn_count": "20" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08457962682, + 51.52315791127 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "44760202", + "uprn_count": "63" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08514280715, + 51.52153941124 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "47582725", + "uprn_count": "2" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08903179064, + 51.52700439325 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "44760266", + "uprn_count": "66" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08958919395, + 51.52486571171 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "44613662", + "uprn_count": "2" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.09055456990, + 51.52704347329 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "44760589", + "uprn_count": "10" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08739339578, + 51.52221604989 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "47863432", + "uprn_count": "20" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08656652832, + 51.52377354927 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "44760179", + "uprn_count": "1" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08735058986, + 51.52204111283 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "44760248", + "uprn_count": "7" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08545068911, + 51.52547159555 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "44760404", + "uprn_count": "23" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08569047233, + 51.52316678822 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "60347880", + "uprn_count": "156" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08873887407, + 51.52589244765 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "44760981", + "uprn_count": "8" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08508447894, + 51.52250703445 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "53517488", + "uprn_count": "13" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08540249619, + 51.52063943555 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "44760170", + "uprn_count": "23" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08950340680, + 51.52518881505 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "53815719", + "uprn_count": "8" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08580214812, + 51.51976909788 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "44760228", + "uprn_count": "4" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08461864254, + 51.52260180398 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "53877676", + "uprn_count": "29" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08850676500, + 51.52023902397 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "44760314", + "uprn_count": "15" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08872036555, + 51.52471439061 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "44760751", + "uprn_count": "180" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08677326589, + 51.52308738524 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "44760162", + "uprn_count": "3" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08893070202, + 51.52527974813 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "44760215", + "uprn_count": "13" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08750749576, + 51.52270832689 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "44760278", + "uprn_count": "6" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08509855123, + 51.52550722342 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "44613787", + "uprn_count": "11" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.09045829084, + 51.52773739133 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "44760638", + "uprn_count": "6" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08587468014, + 51.52417859166 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "44760151", + "uprn_count": "15" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08738616252, + 51.52175540266 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "44760195", + "uprn_count": "8" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08577241806, + 51.52228063806 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "44760262", + "uprn_count": "7" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08881139622, + 51.52515946758 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "44613654", + "uprn_count": "30" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08841210101, + 51.52660740062 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "44760559", + "uprn_count": "2" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.09036979446, + 51.52201621806 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "44760051", + "uprn_count": "8" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08651408714, + 51.52230407716 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "53530202", + "uprn_count": "21" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08558143776, + 51.52017053246 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "44760177", + "uprn_count": "115" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.09011378562, + 51.52279114581 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "44760235", + "uprn_count": "41" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08631542933, + 51.52256625361 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "44760839", + "uprn_count": "19" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.09069863118, + 51.52235527530 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "44760168", + "uprn_count": "1" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08591546179, + 51.52346670045 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "53876171", + "uprn_count": "112" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08780026547, + 51.51966975438 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "44760301", + "uprn_count": "4" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08517005909, + 51.52550434483 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "44760686", + "uprn_count": "8" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08683964335, + 51.52238336083 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "44760160", + "uprn_count": "12" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08554780465, + 51.52227517161 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "54668028", + "uprn_count": "8" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08868565970, + 51.52707814958 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "44760271", + "uprn_count": "9" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08511551871, + 51.52564108792 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "44613778", + "uprn_count": "4" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08556279304, + 51.52566116202 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "58775318", + "uprn_count": "5" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08476391706, + 51.52322955215 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "44760634", + "uprn_count": "6" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08575556474, + 51.52523072818 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "44760149", + "uprn_count": "7" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08736477713, + 51.52209660754 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "57099529", + "uprn_count": "14" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08747715586, + 51.52255778464 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "44760189", + "uprn_count": "1" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08645676995, + 51.52485658336 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "53842336", + "uprn_count": "16" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08644851873, + 51.51932417520 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "55728650", + "uprn_count": "15" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08606772431, + 51.51932747399 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "44760259", + "uprn_count": "7" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08879089932, + 51.52501718371 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "56517732", + "uprn_count": "16" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08537944456, + 51.52045732895 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "44613640", + "uprn_count": "1" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08508351171, + 51.52602201951 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "44760498", + "uprn_count": "9" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08737904118, + 51.52215533460 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "60457716", + "uprn_count": "12" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08741608314, + 51.52162504468 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "50741600", + "uprn_count": "11" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08601118306, + 51.52237364065 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "53528831", + "uprn_count": "18" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08796303430, + 51.52075390699 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "44760174", + "uprn_count": "3" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08975339083, + 51.52521825973 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "53815990", + "uprn_count": "6" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08698937756, + 51.52000181841 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "44760231", + "uprn_count": "1" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08485451783, + 51.52255188290 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "51082874", + "uprn_count": "15" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08930634369, + 51.52701309542 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "44760352", + "uprn_count": "14" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08734604831, + 51.52197314390 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "56669648", + "uprn_count": "8" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08851621061, + 51.52684922637 + ] + } + } + ] +} \ No newline at end of file diff --git a/assets/themes/uk_addresses/license_info.json b/assets/themes/uk_addresses/license_info.json new file mode 100644 index 0000000000..7d805cee45 --- /dev/null +++ b/assets/themes/uk_addresses/license_info.json @@ -0,0 +1,51 @@ +[ + { + "path": "housenumber_add.svg", + "license": "CC0", + "authors": [ + "Pieter Vander Vennet" + ], + "sources": [] + }, + { + "path": "housenumber_ok.svg", + "license": "CC0", + "authors": [ + "Tobias Zwick (westnordost)" + ], + "sources": [ + "https://github.com/streetcomplete/StreetComplete/tree/master/res/graphics", + "https://f-droid.org/packages/de.westnordost.streetcomplete/" + ] + }, + { + "path": "housenumber_text.svg", + "license": "CC0", + "authors": [ + "Tobias Zwick (westnordost)" + ], + "sources": [ + "https://github.com/streetcomplete/StreetComplete/tree/master/res/graphics", + "https://f-droid.org/packages/de.westnordost.streetcomplete/" + ] + }, + { + "path": "housenumber_unknown.svg", + "license": "CC0", + "authors": [ + "Tobias Zwick (westnordost)" + ], + "sources": [ + "https://github.com/streetcomplete/StreetComplete/tree/master/res/graphics", + "https://f-droid.org/packages/de.westnordost.streetcomplete/" + ] + }, + { + "path": "housenumber_unknown_small.svg", + "license": "CC0", + "authors": [ + "Pieter Vander Vennet" + ], + "sources": [] + } +] \ No newline at end of file diff --git a/assets/themes/uk_addresses/uk_addresses.json b/assets/themes/uk_addresses/uk_addresses.json new file mode 100644 index 0000000000..8dd07bf47d --- /dev/null +++ b/assets/themes/uk_addresses/uk_addresses.json @@ -0,0 +1,270 @@ +{ + "id": "uk_addresses", + "title": { + "en": "UK Addresses" + }, + "shortDescription": { + "en": "Help to build an open dataset of UK addresses" + }, + "description": { + "en": "Contribute to OpenStreetMap by filling out address information" + }, + "language": [ + "en" + ], + "maintainer": "Pieter Vander Vennet, Rob Nickerson, Russ Garrett", + "icon": "./assets/themes/uk_addresses/housenumber_unknown.svg", + "version": "2021-09-17", + "startLat": -0.08706, + "startLon": 51.52224, + "startZoom": 17, + "widenFactor": 1.01, + "socialImage": "", + "clustering": { + "minNeededFeatures": 25, + "maxZoom": 17 + }, + "layers": [ + { + "id": "to_import", + "source": { + "#geoJson": "http://127.0.0.1:8080/islington_small_piece.geojson", + "geoJson": "https://raw.githubusercontent.com/pietervdvn/MapComplete/develop/assets/themes/uk_addresses/islington_small_piece.geojson", + "##geoJson": "https://raw.githubusercontent.com/russss/osm-uk-addresses/main/output/islington.geojson", + "osmTags": "inspireid~*", + "isOsmCache": false + }, + "name": "Addresses to check", + "minzoom": 12, + "wayHandling": 1, + "icon": { + "render": "./assets/themes/uk_addresses/housenumber_unknown.svg", + "mappings": [ + { + "if": "_embedding_object:id~*", + "then": "./assets/themes/uk_addresses/housenumber_unknown_small.svg" + }, + { + "if": "_imported=yes", + "then": "./assets/themes/uk_addresses/housenumber_unknown_small.svg" + } + ] + }, + "iconSize": { + "render": "40,40,center" + }, + "title": { + "render": "Address to be determined" + }, + "tagRenderings": [ + { + "id": "uk_addresses_explanation", + "render": "There probably is an address here" + }, + { + "id": "uk_addresses_embedding_outline", + "render": "An outline embedding this point with an address already exists in OpenStreetMap.
This object has address {_embedding_object:addr:street} {_embedding_object:addr:housenumber}", + "condition": "_embedding_object:id~*" + }, + { + "id": "uk_addresses_import_button", + "render": "{import_button(ref:inspireid=$inspireid, Add this address, ./assets/themes/uk_addresses/housenumber_add.svg)}" + }, + "all_tags" + ], + "calculatedTags": [ + "_embedding_object=feat.overlapWith('addresses')[0]?.feat?.properties ?? null", + "_embedding_object:addr:housenumber=JSON.parse(feat.properties._embedding_object)?.['addr:housenumber']", + "_embedding_object:addr:street=JSON.parse(feat.properties._embedding_object)?.['addr:street']", + "_embedding_object:id=JSON.parse(feat.properties._embedding_object)?.id" + ], + "filter": [ + { + "id": "to_handle", + "options": [ + { + "question": "Only show non-matched objects", + "osmTags": { + "and": [ + "_imported=", + "_embedding_object:id=" + ] + } + } + ] + } + ] + }, + { + "id": "addresses", + "name": { + "en": "Known addresses in OSM" + }, + "minzoom": 18, + "source": { + "osmTags": { + "or": [ + "addr:housenumber~*", + "addr:street~*", + "ref:inspireid~*" + ] + } + }, + "calculatedTags": [ + "_closest_3_street_names=feat.closestn('named_streets',3, 'name').map(f => ({name: f.feat.properties.name, distance: Math.round(1000*f.distance), id: f.id}))", + "_closest_street:0:name=JSON.parse(feat.properties._closest_3_street_names)[0]?.name", + "_closest_street:1:name=JSON.parse(feat.properties._closest_3_street_names)[1]?.name", + "_closest_street:2:name=JSON.parse(feat.properties._closest_3_street_names)[2]?.name", + "_closest_street:0:distance=JSON.parse(feat.properties._closest_3_street_names)[0]?.distance", + "_closest_street:1:distance=JSON.parse(feat.properties._closest_3_street_names)[1]?.distance", + "_closest_street:2:distance=JSON.parse(feat.properties._closest_3_street_names)[2]?.distance", + "_closest_street:0:id=JSON.parse(feat.properties._closest_3_street_names)[0]?.id", + "_closest_street:1:id=JSON.parse(feat.properties._closest_3_street_names)[1]?.id", + "_closest_street:2:id=JSON.parse(feat.properties._closest_3_street_names)[2]?.id" + ], + "title": { + "render": { + "en": "Known address" + } + }, + "description": { + "en": "Addresses" + }, + "tagRenderings": [ + { + "id": "uk_addresses_explanation_osm", + "render": { + "en": "This address is saved in OpenStreetMap" + } + }, + { + "id": "uk_addresses_housenumber", + "render": { + "en": "The housenumber is {addr:housenumber}" + }, + "question": { + "en": "What is the number of this house?" + }, + "freeform": { + "key": "addr:housenumber" + }, + "mappings": [ + { + "if": { + "and": [ + "nohousenumber=yes" + ] + }, + "then": { + "en": "This building has no house number" + } + } + ] + }, + { + "id": "uk_addresses_street", + "render": { + "en": "This address is in street {addr:street}" + }, + "question": { + "en": "What street is this address located in?" + }, + "freeform": { + "key": "addr:street" + }, + "mappings": [ + { + "if": "addr:street:={_closest_street:0:name}", + "then": "Located in {_closest_street:0:name} (~{_closest_street:0:distance}m away)", + "hideInAnswer": "_closest_street:0:name=" + }, + { + "if": "addr:street:={_closest_street:1:name}", + "then": "Located in {_closest_street:1:name} (~{_closest_street:1:distance}m away)", + "hideInAnswer": "_closest_street:1:name=" + }, + { + "if": "addr:street:={_closest_street:2:name}", + "then": "Located in {_closest_street:2:name} (~{_closest_street:2:distance}m away)", + "hideInAnswer": "_closest_street:2:name=" + } + ], + "condition": { + "and": [ + "nohousenumber!~yes" + ] + } + } + ], + "icon": { + "render": "./assets/themes/uk_addresses/housenumber_ok.svg", + "mappings": [ + { + "if": { + "or": [ + { + "and": [ + "addr:housenumber=", + "nohousenumber!=yes" + ] + }, + "addr:street=" + ] + }, + "then": "./assets/themes/uk_addresses/housenumber_unknown.svg" + } + ] + }, + "width": { + "render": "8" + }, + "iconSize": { + "render": "40,40,center" + }, + "color": { + "render": "#00f", + "mappings": [ + { + "if": { + "or": [ + { + "and": [ + "addr:housenumber=", + "nohousenumber!=yes" + ] + }, + "addr:street=" + ] + }, + "then": "#ff0" + } + ] + } + }, + { + "id": "named_streets", + "name": "Named streets", + "minzoom": 18, + "source": { + "osmTags": { + "and": [ + "highway~*", + "name~*" + ] + } + }, + "title": { + "render": { + "en": "{name}" + } + }, + "color": { + "render": "#ccc" + }, + "width": { + "render": "3" + } + } + ], + "roamingRenderings": [] +} \ No newline at end of file diff --git a/assets/themes/waste_basket/waste_basket.json b/assets/themes/waste_basket/waste_basket.json index 797e8d341a..eb95d85ebb 100644 --- a/assets/themes/waste_basket/waste_basket.json +++ b/assets/themes/waste_basket/waste_basket.json @@ -18,11 +18,11 @@ ], "maintainer": "", "icon": "./assets/themes/waste_basket/waste_basket.svg", - "version": "7/7/2021", + "version": "2021-07-07", "startLat": 0, "startLon": 0, "startZoom": 1, - "widenFactor": 0.05, + "widenFactor": 2, "socialImage": "", "layers": [ { diff --git a/css/index-tailwind-output.css b/css/index-tailwind-output.css new file mode 100644 index 0000000000..0549527564 --- /dev/null +++ b/css/index-tailwind-output.css @@ -0,0 +1,2420 @@ +/* + TailwindCSS JIT-Mode Input file. + Use TailwindCSS functions and directives here – https://tailwindcss.com/docs/functions-and-directives + About JIT-Mode: https://tailwindcss.com/docs/just-in-time-mode#styles-rebuild-in-an-infinite-loop + + TailwindCSS CLI generates the css/index-tailwind-output.css file based on this file. + It is not used directly in the app. +*/ + +/* + ! tailwindcss v2.2.15 | MIT License | https://tailwindcss.com +*/ + +/*! modern-normalize v1.1.0 | MIT License | https://github.com/sindresorhus/modern-normalize */ + +/* +Document +======== +*/ + +/** +Use a better box model (opinionated). +*/ + +*, +::before, +::after { + box-sizing: border-box; +} + +/** +Use a more readable tab size (opinionated). +*/ + +html { + -moz-tab-size: 4; + -o-tab-size: 4; + tab-size: 4; +} + +/** +1. Correct the line height in all browsers. +2. Prevent adjustments of font size after orientation changes in iOS. +*/ + +html { + line-height: 1.15; + /* 1 */ + -webkit-text-size-adjust: 100%; + /* 2 */ +} + +/* +Sections +======== +*/ + +/** +Remove the margin in all browsers. +*/ + +body { + margin: 0; +} + +/** +Improve consistency of default fonts in all browsers. (https://github.com/sindresorhus/modern-normalize/issues/3) +*/ + +body { + font-family: + system-ui, + -apple-system, /* Firefox supports this but not yet `system-ui` */ + 'Segoe UI', + Roboto, + Helvetica, + Arial, + sans-serif, + 'Apple Color Emoji', + 'Segoe UI Emoji'; +} + +/* +Grouping content +================ +*/ + +/** +1. Add the correct height in Firefox. +2. Correct the inheritance of border color in Firefox. (https://bugzilla.mozilla.org/show_bug.cgi?id=190655) +*/ + +hr { + height: 0; + /* 1 */ + color: inherit; + /* 2 */ +} + +/* +Text-level semantics +==================== +*/ + +/** +Add the correct text decoration in Chrome, Edge, and Safari. +*/ + +abbr[title] { + -webkit-text-decoration: underline dotted; + text-decoration: underline dotted; +} + +/** +Add the correct font weight in Edge and Safari. +*/ + +b, +strong { + font-weight: bolder; +} + +/** +1. Improve consistency of default fonts in all browsers. (https://github.com/sindresorhus/modern-normalize/issues/3) +2. Correct the odd 'em' font sizing in all browsers. +*/ + +code, +kbd, +samp, +pre { + font-family: + ui-monospace, + SFMono-Regular, + Consolas, + 'Liberation Mono', + Menlo, + monospace; + /* 1 */ + font-size: 1em; + /* 2 */ +} + +/** +Add the correct font size in all browsers. +*/ + +small { + font-size: 80%; +} + +/** +Prevent 'sub' and 'sup' elements from affecting the line height in all browsers. +*/ + +sub, +sup { + font-size: 75%; + line-height: 0; + position: relative; + vertical-align: baseline; +} + +sub { + bottom: -0.25em; +} + +sup { + top: -0.5em; +} + +/* +Tabular data +============ +*/ + +/** +1. Remove text indentation from table contents in Chrome and Safari. (https://bugs.chromium.org/p/chromium/issues/detail?id=999088, https://bugs.webkit.org/show_bug.cgi?id=201297) +2. Correct table border color inheritance in all Chrome and Safari. (https://bugs.chromium.org/p/chromium/issues/detail?id=935729, https://bugs.webkit.org/show_bug.cgi?id=195016) +*/ + +table { + text-indent: 0; + /* 1 */ + border-color: inherit; + /* 2 */ +} + +/* +Forms +===== +*/ + +/** +1. Change the font styles in all browsers. +2. Remove the margin in Firefox and Safari. +*/ + +button, +input, +optgroup, +select, +textarea { + font-family: inherit; + /* 1 */ + font-size: 100%; + /* 1 */ + line-height: 1.15; + /* 1 */ + margin: 0; + /* 2 */ +} + +/** +Remove the inheritance of text transform in Edge and Firefox. +1. Remove the inheritance of text transform in Firefox. +*/ + +button, +select { + /* 1 */ + text-transform: none; +} + +/** +Correct the inability to style clickable types in iOS and Safari. +*/ + +button, +[type='button'], +[type='reset'], +[type='submit'] { + -webkit-appearance: button; +} + +/** +Remove the inner border and padding in Firefox. +*/ + +::-moz-focus-inner { + border-style: none; + padding: 0; +} + +/** +Restore the focus styles unset by the previous rule. +*/ + +:-moz-focusring { + outline: 1px dotted ButtonText; +} + +/** +Remove the additional ':invalid' styles in Firefox. +See: https://github.com/mozilla/gecko-dev/blob/2f9eacd9d3d995c937b4251a5557d95d494c9be1/layout/style/res/forms.css#L728-L737 +*/ + +:-moz-ui-invalid { + box-shadow: none; +} + +/** +Remove the padding so developers are not caught out when they zero out 'fieldset' elements in all browsers. +*/ + +legend { + padding: 0; +} + +/** +Add the correct vertical alignment in Chrome and Firefox. +*/ + +progress { + vertical-align: baseline; +} + +/** +Correct the cursor style of increment and decrement buttons in Safari. +*/ + +::-webkit-inner-spin-button, +::-webkit-outer-spin-button { + height: auto; +} + +/** +1. Correct the odd appearance in Chrome and Safari. +2. Correct the outline style in Safari. +*/ + +[type='search'] { + -webkit-appearance: textfield; + /* 1 */ + outline-offset: -2px; + /* 2 */ +} + +/** +Remove the inner padding in Chrome and Safari on macOS. +*/ + +::-webkit-search-decoration { + -webkit-appearance: none; +} + +/** +1. Correct the inability to style clickable types in iOS and Safari. +2. Change font properties to 'inherit' in Safari. +*/ + +::-webkit-file-upload-button { + -webkit-appearance: button; + /* 1 */ + font: inherit; + /* 2 */ +} + +/* +Interactive +=========== +*/ + +/* +Add the correct display in Chrome and Safari. +*/ + +summary { + display: list-item; +} + +/** + * Manually forked from SUIT CSS Base: https://github.com/suitcss/base + * A thin layer on top of normalize.css that provides a starting point more + * suitable for web applications. + */ + +/** + * Removes the default spacing and border for appropriate elements. + */ + +blockquote, +dl, +dd, +h1, +h2, +h3, +h4, +h5, +h6, +hr, +figure, +p, +pre { + margin: 0; +} + +button { + background-color: transparent; + background-image: none; +} + +fieldset { + margin: 0; + padding: 0; +} + +ol, +ul { + list-style: none; + margin: 0; + padding: 0; +} + +/** + * Tailwind custom reset styles + */ + +/** + * 1. Use the user's configured `sans` font-family (with Tailwind's default + * sans-serif font stack as a fallback) as a sane default. + * 2. Use Tailwind's default "normal" line-height so the user isn't forced + * to override it to ensure consistency even when using the default theme. + */ + +html { + font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; + /* 1 */ + line-height: 1.5; + /* 2 */ +} + +/** + * Inherit font-family and line-height from `html` so users can set them as + * a class directly on the `html` element. + */ + +body { + font-family: inherit; + line-height: inherit; +} + +/** + * 1. Prevent padding and border from affecting element width. + * + * We used to set this in the html element and inherit from + * the parent element for everything else. This caused issues + * in shadow-dom-enhanced elements like
where the content + * is wrapped by a div with box-sizing set to `content-box`. + * + * https://github.com/mozdevs/cssremedy/issues/4 + * + * + * 2. Allow adding a border to an element by just adding a border-width. + * + * By default, the way the browser specifies that an element should have no + * border is by setting it's border-style to `none` in the user-agent + * stylesheet. + * + * In order to easily add borders to elements by just setting the `border-width` + * property, we change the default border-style for all elements to `solid`, and + * use border-width to hide them instead. This way our `border` utilities only + * need to set the `border-width` property instead of the entire `border` + * shorthand, making our border utilities much more straightforward to compose. + * + * https://github.com/tailwindcss/tailwindcss/pull/116 + */ + +*, +::before, +::after { + box-sizing: border-box; + /* 1 */ + border-width: 0; + /* 2 */ + border-style: solid; + /* 2 */ + border-color: currentColor; + /* 2 */ +} + +/* + * Ensure horizontal rules are visible by default + */ + +hr { + border-top-width: 1px; +} + +/** + * Undo the `border-style: none` reset that Normalize applies to images so that + * our `border-{width}` utilities have the expected effect. + * + * The Normalize reset is unnecessary for us since we default the border-width + * to 0 on all elements. + * + * https://github.com/tailwindcss/tailwindcss/issues/362 + */ + +img { + border-style: solid; +} + +textarea { + resize: vertical; +} + +input::-moz-placeholder, textarea::-moz-placeholder { + opacity: 1; + color: #9ca3af; +} + +input:-ms-input-placeholder, textarea:-ms-input-placeholder { + opacity: 1; + color: #9ca3af; +} + +input::placeholder, +textarea::placeholder { + opacity: 1; + color: #9ca3af; +} + +button, +[role="button"] { + cursor: pointer; +} + +/** + * Override legacy focus reset from Normalize with modern Firefox focus styles. + * + * This is actually an improvement over the new defaults in Firefox in our testing, + * as it triggers the better focus styles even for links, which still use a dotted + * outline in Firefox by default. + */ + +:-moz-focusring { + outline: auto; +} + +table { + border-collapse: collapse; +} + +h1, +h2, +h3, +h4, +h5, +h6 { + font-size: inherit; + font-weight: inherit; +} + +/** + * Reset links to optimize for opt-in styling instead of + * opt-out. + */ + +a { + color: inherit; + text-decoration: inherit; +} + +/** + * Reset form element properties that are easy to forget to + * style explicitly so you don't inadvertently introduce + * styles that deviate from your design system. These styles + * supplement a partial reset that is already applied by + * normalize.css. + */ + +button, +input, +optgroup, +select, +textarea { + padding: 0; + line-height: inherit; + color: inherit; +} + +/** + * Use the configured 'mono' font family for elements that + * are expected to be rendered with a monospace font, falling + * back to the system monospace stack if there is no configured + * 'mono' font family. + */ + +pre, +code, +kbd, +samp { + font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; +} + +/** + * 1. Make replaced elements `display: block` by default as that's + * the behavior you want almost all of the time. Inspired by + * CSS Remedy, with `svg` added as well. + * + * https://github.com/mozdevs/cssremedy/issues/14 + * + * 2. Add `vertical-align: middle` to align replaced elements more + * sensibly by default when overriding `display` by adding a + * utility like `inline`. + * + * This can trigger a poorly considered linting error in some + * tools but is included by design. + * + * https://github.com/jensimmons/cssremedy/issues/14#issuecomment-634934210 + */ + +img, +svg, +video, +canvas, +audio, +iframe, +embed, +object { + display: block; + /* 1 */ + vertical-align: middle; + /* 2 */ +} + +/** + * Constrain images and videos to the parent width and preserve + * their intrinsic aspect ratio. + * + * https://github.com/mozdevs/cssremedy/issues/14 + */ + +img, +video { + max-width: 100%; + height: auto; +} + +/** + * Ensure the default browser behavior of the `hidden` attribute. + */ + +[hidden] { + display: none; +} + +*, ::before, ::after { + --tw-translate-x: 0; + --tw-translate-y: 0; + --tw-rotate: 0; + --tw-skew-x: 0; + --tw-skew-y: 0; + --tw-scale-x: 1; + --tw-scale-y: 1; + --tw-transform: translateX(var(--tw-translate-x)) translateY(var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); + --tw-border-opacity: 1; + border-color: rgba(229, 231, 235, var(--tw-border-opacity)); + --tw-ring-offset-shadow: 0 0 #0000; + --tw-ring-shadow: 0 0 #0000; + --tw-shadow: 0 0 #0000; + --tw-ring-inset: var(--tw-empty,/*!*/ /*!*/); + --tw-ring-offset-width: 0px; + --tw-ring-offset-color: #fff; + --tw-ring-color: rgba(59, 130, 246, 0.5); + --tw-ring-offset-shadow: 0 0 #0000; + --tw-ring-shadow: 0 0 #0000; + --tw-shadow: 0 0 #0000; + --tw-blur: var(--tw-empty,/*!*/ /*!*/); + --tw-brightness: var(--tw-empty,/*!*/ /*!*/); + --tw-contrast: var(--tw-empty,/*!*/ /*!*/); + --tw-grayscale: var(--tw-empty,/*!*/ /*!*/); + --tw-hue-rotate: var(--tw-empty,/*!*/ /*!*/); + --tw-invert: var(--tw-empty,/*!*/ /*!*/); + --tw-saturate: var(--tw-empty,/*!*/ /*!*/); + --tw-sepia: var(--tw-empty,/*!*/ /*!*/); + --tw-drop-shadow: var(--tw-empty,/*!*/ /*!*/); + --tw-filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow); +} + +.container { + width: 100%; +} + +@media (min-width: 640px) { + .container { + max-width: 640px; + } +} + +@media (min-width: 768px) { + .container { + max-width: 768px; + } +} + +@media (min-width: 1024px) { + .container { + max-width: 1024px; + } +} + +@media (min-width: 1280px) { + .container { + max-width: 1280px; + } +} + +@media (min-width: 1536px) { + .container { + max-width: 1536px; + } +} + +.pointer-events-none { + pointer-events: none; +} + +.pointer-events-auto { + pointer-events: auto; +} + +.visible { + visibility: visible; +} + +.invisible { + visibility: hidden; +} + +.static { + position: static; +} + +.fixed { + position: fixed; +} + +.absolute { + position: absolute; +} + +.relative { + position: relative; +} + +.sticky { + position: sticky; +} + +.inset-0 { + top: 0px; + right: 0px; + bottom: 0px; + left: 0px; +} + +.bottom-3 { + bottom: 0.75rem; +} + +.left-3 { + left: 0.75rem; +} + +.right-2 { + right: 0.5rem; +} + +.left-24 { + left: 6rem; +} + +.right-24 { + right: 6rem; +} + +.top-56 { + top: 14rem; +} + +.top-0 { + top: 0px; +} + +.left-0 { + left: 0px; +} + +.right-0 { + right: 0px; +} + +.top-2 { + top: 0.5rem; +} + +.right-3 { + right: 0.75rem; +} + +.bottom-0 { + bottom: 0px; +} + +.isolate { + isolation: isolate; +} + +.z-10 { + z-index: 10; +} + +.z-0 { + z-index: 0; +} + +.float-right { + float: right; +} + +.float-left { + float: left; +} + +.float-none { + float: none; +} + +.m-8 { + margin: 2rem; +} + +.m-11 { + margin: 2.75rem; +} + +.m-1 { + margin: 0.25rem; +} + +.m-5 { + margin: 1.25rem; +} + +.m-0\.5 { + margin: 0.125rem; +} + +.m-0 { + margin: 0px; +} + +.m-2 { + margin: 0.5rem; +} + +.m-3 { + margin: 0.75rem; +} + +.m-4 { + margin: 1rem; +} + +.my-2 { + margin-top: 0.5rem; + margin-bottom: 0.5rem; +} + +.mx-10 { + margin-left: 2.5rem; + margin-right: 2.5rem; +} + +.my-3 { + margin-top: 0.75rem; + margin-bottom: 0.75rem; +} + +.mx-4 { + margin-left: 1rem; + margin-right: 1rem; +} + +.ml-3 { + margin-left: 0.75rem; +} + +.mr-3 { + margin-right: 0.75rem; +} + +.mb-2 { + margin-bottom: 0.5rem; +} + +.mr-4 { + margin-right: 1rem; +} + +.mt-3 { + margin-top: 0.75rem; +} + +.ml-8 { + margin-left: 2rem; +} + +.mr-2 { + margin-right: 0.5rem; +} + +.mt-2 { + margin-top: 0.5rem; +} + +.mb-10 { + margin-bottom: 2.5rem; +} + +.mt-1 { + margin-top: 0.25rem; +} + +.mt-0 { + margin-top: 0px; +} + +.ml-2 { + margin-left: 0.5rem; +} + +.mt-4 { + margin-top: 1rem; +} + +.mb-8 { + margin-bottom: 2rem; +} + +.ml-1 { + margin-left: 0.25rem; +} + +.mr-0 { + margin-right: 0px; +} + +.mb-1 { + margin-bottom: 0.25rem; +} + +.mb-4 { + margin-bottom: 1rem; +} + +.box-border { + box-sizing: border-box; +} + +.box-content { + box-sizing: content-box; +} + +.block { + display: block; +} + +.inline-block { + display: inline-block; +} + +.inline { + display: inline; +} + +.flex { + display: flex; +} + +.inline-flex { + display: inline-flex; +} + +.table { + display: table; +} + +.grid { + display: grid; +} + +.contents { + display: contents; +} + +.hidden { + display: none; +} + +.h-full { + height: 100%; +} + +.h-24 { + height: 6rem; +} + +.h-10 { + height: 2.5rem; +} + +.h-8 { + height: 2rem; +} + +.h-12 { + height: 3rem; +} + +.h-5 { + height: 1.25rem; +} + +.h-screen { + height: 100vh; +} + +.h-11 { + height: 2.75rem; +} + +.h-32 { + height: 8rem; +} + +.h-16 { + height: 4rem; +} + +.h-0 { + height: 0px; +} + +.h-6 { + height: 1.5rem; +} + +.h-3 { + height: 0.75rem; +} + +.max-h-20vh { + max-height: 20vh; +} + +.max-h-32 { + max-height: 8rem; +} + +.max-h-4 { + max-height: 1rem; +} + +.w-full { + width: 100%; +} + +.w-10 { + width: 2.5rem; +} + +.w-8 { + width: 2rem; +} + +.w-12 { + width: 3rem; +} + +.w-screen { + width: 100vw; +} + +.w-11 { + width: 2.75rem; +} + +.w-0 { + width: 0px; +} + +.w-16 { + width: 4rem; +} + +.w-min { + width: -webkit-min-content; + width: -moz-min-content; + width: min-content; +} + +.w-6 { + width: 1.5rem; +} + +.w-1\/2 { + width: 50%; +} + +.w-max { + width: -webkit-max-content; + width: -moz-max-content; + width: max-content; +} + +.min-w-min { + min-width: -webkit-min-content; + min-width: -moz-min-content; + min-width: min-content; +} + +.min-w-\[20em\] { + min-width: 20em; +} + +.max-w-full { + max-width: 100%; +} + +.flex-none { + flex: none; +} + +.flex-auto { + flex: 1 1 auto; +} + +.flex-shrink-0 { + flex-shrink: 0; +} + +.flex-grow { + flex-grow: 1; +} + +.border-collapse { + border-collapse: collapse; +} + +.transform { + transform: var(--tw-transform); +} + +@-webkit-keyframes spin { + to { + transform: rotate(360deg); + } +} + +@keyframes spin { + to { + transform: rotate(360deg); + } +} + +.animate-spin { + -webkit-animation: spin 1s linear infinite; + animation: spin 1s linear infinite; +} + +@-webkit-keyframes pulse { + 50% { + opacity: .5; + } +} + +@keyframes pulse { + 50% { + opacity: .5; + } +} + +.animate-pulse { + -webkit-animation: pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite; + animation: pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite; +} + +.cursor-pointer { + cursor: pointer; +} + +.cursor-wait { + cursor: wait; +} + +.resize { + resize: both; +} + +.flex-row { + flex-direction: row; +} + +.flex-col { + flex-direction: column; +} + +.flex-wrap { + flex-wrap: wrap; +} + +.items-end { + align-items: flex-end; +} + +.items-center { + align-items: center; +} + +.items-baseline { + align-items: baseline; +} + +.justify-end { + justify-content: flex-end; +} + +.justify-center { + justify-content: center; +} + +.justify-between { + justify-content: space-between; +} + +.gap-4 { + gap: 1rem; +} + +.overflow-auto { + overflow: auto; +} + +.overflow-hidden { + overflow: hidden; +} + +.overflow-y-auto { + overflow-y: auto; +} + +.truncate { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.overflow-ellipsis { + text-overflow: ellipsis; +} + +.whitespace-nowrap { + white-space: nowrap; +} + +.break-normal { + overflow-wrap: normal; + word-break: normal; +} + +.break-words { + overflow-wrap: break-word; +} + +.break-all { + word-break: break-all; +} + +.rounded-full { + border-radius: 9999px; +} + +.rounded-3xl { + border-radius: 1.5rem; +} + +.rounded { + border-radius: 0.25rem; +} + +.rounded-lg { + border-radius: 0.5rem; +} + +.rounded-xl { + border-radius: 0.75rem; +} + +.border { + border-width: 1px; +} + +.border-4 { + border-width: 4px; +} + +.border-2 { + border-width: 2px; +} + +.border-b-2 { + border-bottom-width: 2px; +} + +.border-b { + border-bottom-width: 1px; +} + +.border-black { + --tw-border-opacity: 1; + border-color: rgba(0, 0, 0, var(--tw-border-opacity)); +} + +.border-gray-300 { + --tw-border-opacity: 1; + border-color: rgba(209, 213, 219, var(--tw-border-opacity)); +} + +.border-gray-400 { + --tw-border-opacity: 1; + border-color: rgba(156, 163, 175, var(--tw-border-opacity)); +} + +.border-gray-200 { + --tw-border-opacity: 1; + border-color: rgba(229, 231, 235, var(--tw-border-opacity)); +} + +.border-opacity-50 { + --tw-border-opacity: 0.5; +} + +.bg-white { + --tw-bg-opacity: 1; + background-color: rgba(255, 255, 255, var(--tw-bg-opacity)); +} + +.bg-blue-50 { + --tw-bg-opacity: 1; + background-color: rgba(239, 246, 255, var(--tw-bg-opacity)); +} + +.bg-blue-100 { + --tw-bg-opacity: 1; + background-color: rgba(219, 234, 254, var(--tw-bg-opacity)); +} + +.bg-gray-400 { + --tw-bg-opacity: 1; + background-color: rgba(156, 163, 175, var(--tw-bg-opacity)); +} + +.bg-indigo-100 { + --tw-bg-opacity: 1; + background-color: rgba(224, 231, 255, var(--tw-bg-opacity)); +} + +.bg-gray-300 { + --tw-bg-opacity: 1; + background-color: rgba(209, 213, 219, var(--tw-bg-opacity)); +} + +.bg-black { + --tw-bg-opacity: 1; + background-color: rgba(0, 0, 0, var(--tw-bg-opacity)); +} + +.bg-gray-200 { + --tw-bg-opacity: 1; + background-color: rgba(229, 231, 235, var(--tw-bg-opacity)); +} + +.bg-gray-100 { + --tw-bg-opacity: 1; + background-color: rgba(243, 244, 246, var(--tw-bg-opacity)); +} + +.bg-red-500 { + --tw-bg-opacity: 1; + background-color: rgba(239, 68, 68, var(--tw-bg-opacity)); +} + +.bg-red-200 { + --tw-bg-opacity: 1; + background-color: rgba(254, 202, 202, var(--tw-bg-opacity)); +} + +.p-1\.5 { + padding: 0.375rem; +} + +.p-1 { + padding: 0.25rem; +} + +.p-3 { + padding: 0.75rem; +} + +.p-4 { + padding: 1rem; +} + +.p-2 { + padding: 0.5rem; +} + +.p-0\.5 { + padding: 0.125rem; +} + +.p-0 { + padding: 0px; +} + +.pl-6 { + padding-left: 1.5rem; +} + +.pt-2 { + padding-top: 0.5rem; +} + +.pl-2 { + padding-left: 0.5rem; +} + +.pt-3 { + padding-top: 0.75rem; +} + +.pb-3 { + padding-bottom: 0.75rem; +} + +.pl-4 { + padding-left: 1rem; +} + +.pb-0 { + padding-bottom: 0px; +} + +.pl-1 { + padding-left: 0.25rem; +} + +.pr-1 { + padding-right: 0.25rem; +} + +.pt-6 { + padding-top: 1.5rem; +} + +.pl-5 { + padding-left: 1.25rem; +} + +.pr-3 { + padding-right: 0.75rem; +} + +.pr-4 { + padding-right: 1rem; +} + +.pl-3 { + padding-left: 0.75rem; +} + +.pr-0 { + padding-right: 0px; +} + +.pb-2 { + padding-bottom: 0.5rem; +} + +.pt-0\.5 { + padding-top: 0.125rem; +} + +.pt-0 { + padding-top: 0px; +} + +.pr-2 { + padding-right: 0.5rem; +} + +.text-center { + text-align: center; +} + +.align-baseline { + vertical-align: baseline; +} + +.align-middle { + vertical-align: middle; +} + +.text-xl { + font-size: 1.25rem; + line-height: 1.75rem; +} + +.text-sm { + font-size: 0.875rem; + line-height: 1.25rem; +} + +.text-2xl { + font-size: 1.5rem; + line-height: 2rem; +} + +.text-base { + font-size: 1rem; + line-height: 1.5rem; +} + +.text-lg { + font-size: 1.125rem; + line-height: 1.75rem; +} + +.text-4xl { + font-size: 2.25rem; + line-height: 2.5rem; +} + +.font-bold { + font-weight: 700; +} + +.font-extrabold { + font-weight: 800; +} + +.font-semibold { + font-weight: 600; +} + +.font-medium { + font-weight: 500; +} + +.uppercase { + text-transform: uppercase; +} + +.lowercase { + text-transform: lowercase; +} + +.italic { + font-style: italic; +} + +.ordinal, .slashed-zero, .lining-nums, .oldstyle-nums, .proportional-nums, .tabular-nums, .diagonal-fractions, .stacked-fractions { + --tw-ordinal: var(--tw-empty,/*!*/ /*!*/); + --tw-slashed-zero: var(--tw-empty,/*!*/ /*!*/); + --tw-numeric-figure: var(--tw-empty,/*!*/ /*!*/); + --tw-numeric-spacing: var(--tw-empty,/*!*/ /*!*/); + --tw-numeric-fraction: var(--tw-empty,/*!*/ /*!*/); + font-variant-numeric: var(--tw-ordinal) var(--tw-slashed-zero) var(--tw-numeric-figure) var(--tw-numeric-spacing) var(--tw-numeric-fraction); +} + +.ordinal { + --tw-ordinal: ordinal; +} + +.leading-6 { + line-height: 1.5rem; +} + +.tracking-tight { + letter-spacing: -0.025em; +} + +.text-white { + --tw-text-opacity: 1; + color: rgba(255, 255, 255, var(--tw-text-opacity)); +} + +.text-gray-900 { + --tw-text-opacity: 1; + color: rgba(17, 24, 39, var(--tw-text-opacity)); +} + +.text-gray-800 { + --tw-text-opacity: 1; + color: rgba(31, 41, 55, var(--tw-text-opacity)); +} + +.text-gray-500 { + --tw-text-opacity: 1; + color: rgba(107, 114, 128, var(--tw-text-opacity)); +} + +.text-green-600 { + --tw-text-opacity: 1; + color: rgba(5, 150, 105, var(--tw-text-opacity)); +} + +.underline { + text-decoration: underline; +} + +.opacity-0 { + opacity: 0; +} + +.opacity-40 { + opacity: 0.4; +} + +.shadow { + --tw-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px 0 rgba(0, 0, 0, 0.06); + box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); +} + +.ring { + --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color); + --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(3px + var(--tw-ring-offset-width)) var(--tw-ring-color); + box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000); +} + +.blur { + --tw-blur: blur(8px); + filter: var(--tw-filter); +} + +.drop-shadow { + --tw-drop-shadow: drop-shadow(0 1px 2px rgba(0, 0, 0, 0.1)) drop-shadow(0 1px 1px rgba(0, 0, 0, 0.06)); + filter: var(--tw-filter); +} + +.invert { + --tw-invert: invert(100%); + filter: var(--tw-filter); +} + +.filter { + filter: var(--tw-filter); +} + +.transition { + transition-property: background-color, border-color, color, fill, stroke, opacity, box-shadow, transform, filter, -webkit-backdrop-filter; + transition-property: background-color, border-color, color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter; + transition-property: background-color, border-color, color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter, -webkit-backdrop-filter; + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); + transition-duration: 150ms; +} + +.\!transition { + transition-property: background-color, border-color, color, fill, stroke, opacity, box-shadow, transform, filter, -webkit-backdrop-filter !important; + transition-property: background-color, border-color, color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter !important; + transition-property: background-color, border-color, color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter, -webkit-backdrop-filter !important; + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1) !important; + transition-duration: 150ms !important; +} + +.duration-500 { + transition-duration: 500ms; +} + +.ease-in { + transition-timing-function: cubic-bezier(0.4, 0, 1, 1); +} + +.z-above-map { + z-index: 10000 +} + +.z-above-controls { + z-index: 10001 +} + +.btn { + display: inline-flex; + justify-content: center; + padding-top: 0.5rem; + padding-bottom: 0.5rem; + padding-left: 1rem; + padding-right: 1rem; + border-width: 1px; + border-color: transparent; + --tw-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.05); + box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); + border-radius: 1.5rem; + --tw-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.05); + box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); + --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color); + --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color); + box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000); + --tw-ring-opacity: 1; + --tw-ring-color: rgba(191, 219, 254, var(--tw-ring-opacity)); +} + +.btn:hover { + --tw-ring-opacity: 1; + --tw-ring-color: rgba(147, 197, 253, var(--tw-ring-opacity)); +} + +.btn { + margin-top: 0.25rem; + margin-right: 0.25rem; + font-size: 0.875rem; + line-height: 1.25rem; + font-weight: 500; + --tw-text-opacity: 1; + color: rgba(255, 255, 255, var(--tw-text-opacity)); + --tw-bg-opacity: 1; + background-color: rgba(37, 99, 235, var(--tw-bg-opacity)); +} + +.btn:hover { + --tw-bg-opacity: 1; + background-color: rgba(29, 78, 216, var(--tw-bg-opacity)); +} + +.btn:focus { + outline: 2px solid transparent; + outline-offset: 2px; + --tw-ring-opacity: 1; + --tw-ring-color: rgba(29, 78, 216, var(--tw-ring-opacity)); +} + +.btn-secondary { + --tw-bg-opacity: 1; + background-color: rgba(75, 85, 99, var(--tw-bg-opacity)); +} + +.btn-secondary:hover { + --tw-bg-opacity: 1; + background-color: rgba(55, 65, 81, var(--tw-bg-opacity)); +} + +.btn-disabled { + --tw-bg-opacity: 1; + background-color: rgba(107, 114, 128, var(--tw-bg-opacity)); +} + +.btn-disabled:hover { + --tw-bg-opacity: 1; + background-color: rgba(107, 114, 128, var(--tw-bg-opacity)); +} + +.btn-disabled { + --tw-text-opacity: 1; + color: rgba(209, 213, 219, var(--tw-text-opacity)); + --tw-ring-opacity: 1; + --tw-ring-color: rgba(229, 231, 235, var(--tw-ring-opacity)); +} + +.btn-disabled:hover { + --tw-ring-opacity: 1; + --tw-ring-color: rgba(229, 231, 235, var(--tw-ring-opacity)); +} + +.btn-disabled:focus { + --tw-ring-opacity: 1; + --tw-ring-color: rgba(229, 231, 235, var(--tw-ring-opacity)); +} + +.btn-disabled { + cursor: default; +} + +:root { + --subtle-detail-color: #e5f5ff; + --subtle-detail-color-contrast: black; + --subtle-detail-color-light-contrast: lightgrey; + --catch-detail-color: #3a3aeb; + --catch-detail-color-contrast: white; + --alert-color: #fee4d1; + --background-color: white; + --foreground-color: black; + --popup-border: white; + --shadow-color: #00000066; + --variable-title-height: 0px; + /* Set by javascript */ + --return-to-the-map-height: 2em; + --image-carousel-height: 350px; +} + +html, body { + height: 100%; + min-height: 100vh; + min-height: -webkit-fill-available; + margin: 0; + padding: 0; + background-color: var(--background-color); + color: var(--foreground-color); + font-family: 'Helvetica Neue', Arial, sans-serif; +} + +.leaflet-overlay-pane .leaflet-zoom-animated { + /* Another workaround to keep leaflet working */ + width: initial !important; + height: initial !important; + box-sizing: initial !important; +} + +.leaflet-control-attribution { + display: block ruby; +} + +svg, img { + box-sizing: content-box; + width: 100%; + height: 100%; +} + +.mapcontrol svg path { + fill: var(--subtle-detail-color-contrast) !important; +} + +.red-svg svg path { + stroke: #d71010 !important; +} + +a { + color: var(--foreground-color); +} + +btn { + margin-top: 0.25rem; + margin-right: 0.25rem; + font-size: 0.875rem; + line-height: 1.25rem; + font-weight: 500; + --tw-text-opacity: 1; + color: var(--catch-detail-color-contrast); + --tw-bg-opacity: 1; + background-color: var(--catch-detail-color); +} + +.h-min { + height: -webkit-min-content; + height: -moz-min-content; + height: min-content; +} + +.w-min { + width: -webkit-min-content; + width: -moz-min-content; + width: min-content; +} + +.w-16-imp { + width: 4rem !important; +} + +.link-underline a { + -webkit-text-decoration: underline 1px #0078a855; + text-decoration: underline 1px #0078a855; + color: #0078A8; +} + +.link-no-underline a { + text-decoration: none; +} + +li { + margin-left: 0.5em; + padding-left: 0.2em; + margin-top: 0.1em; +} + +h2 { + font-size: large; + margin-top: 0.5em; + margin-bottom: 0.3em; + font-weight: bold; +} + +h3 { + font-size: larger; + margin-top: 0.6em; + margin-bottom: 0; + font-weight: bold; + font-size: larger; + margin-top: 0.6em; + margin-bottom: 0; + font-weight: bolder; +} + +p { + padding-top: 0.1em; +} + +li::marker { + content: "•" +} + +.subtle-background { + background: var(--subtle-detail-color); + color: var(--subtle-detail-color-contrast); +} + +.normal-background { + background: var(--background-color); + color: var(--foreground-color) +} + +.subtle-lighter { + color: var(--subtle-detail-color-light-contrast); +} + +.border-attention-catch { + border: 5px solid var(--catch-detail-color); +} + +.direction-svg svg path { + fill: var(--catch-detail-color) !important; +} + +#leafletDiv { + height: 100%; +} + +.leaflet-popup-content-wrapper { + background-color: var(--background-color); + color: var(--foreground-color); + border: 2px solid var(--popup-border); + box-shadow: 0 3px 14px var(--shadow-color) !important; +} + +.leaflet-container { + background-color: var(--background-color) !important; +} + +.leaflet-popup-tip { + background-color: var(--popup-border) !important; + color: var(--popup-border) !important; + box-shadow: 0 3px 14px var(--shadow-color) !important; +} + +.single-layer-selection-toggle { + position: relative; + width: 2em; + height: 2em; + flex-shrink: 0; +} + +.single-layer-selection-toggle img { + max-height: 2em !important; + max-width: 2em !important; +} + +.single-layer-selection-toggle svg { + max-height: 2em !important; + max-width: 2em !important; +} + +/**************** GENERIC ****************/ + +.alert { + background-color: var(--alert-color); + font-weight: bold; + border-radius: 1em; + margin: 0.25em; + text-align: center; + padding: 0.15em 0.3em; +} + +.question form { + display: inline-block; + max-width: 90vw; + width: 100%; +} + +.invalid { + box-shadow: 0 0 10px #ff5353; + height: -webkit-min-content; + height: -moz-min-content; + height: min-content; +} + +.shadow { + box-shadow: 0 0 10px var(--shadow-color); +} + +.title-font span { + font-size: xx-large !important; + font-weight: bold; +} + +.soft { + background-color: var(--subtle-detail-color); + color: var(--subtle-detail-color-contrast); + font-weight: bold; + border-radius: 1em; + margin: 0.25em; + text-align: center; + padding: 0.15em 0.3em; +} + +.subtle { + color: #999; +} + +.link-underline .subtle a { + -webkit-text-decoration: underline 1px #7193bb88; + text-decoration: underline 1px #7193bb88; + color: #7193bb; +} + +.thanks { + background-color: #43d904; + font-weight: bold; + border-radius: 1em; + margin: 0.25em; + text-align: center; + padding: 0.15em 0.3em; +} + +.clickable { + pointer-events: all; +} + +.unclickable { + pointer-events: none !important; +} + +@-webkit-keyframes slide { + /* This is the animation on the marker to add a new point - it slides through all the possible presets */ + + from { + transform: translateX(0%); + } + + to { + transform: translateX(calc(-100% + 42px)); + } +} + +@keyframes slide { + /* This is the animation on the marker to add a new point - it slides through all the possible presets */ + + from { + transform: translateX(0%); + } + + to { + transform: translateX(calc(-100% + 42px)); + } +} + +.hand-drag-animation { + -webkit-animation: hand-drag-animation 6s ease-in-out infinite; + animation: hand-drag-animation 6s ease-in-out infinite; + transform-origin: 50% 125%; +} + +@-webkit-keyframes hand-drag-animation { + /* This is the animation on the little extra hand on the location input. If fades in, invites the user to interact/drag the map */ + + 0% { + opacity: 0; + transform: rotate(-30deg); + } + + 6% { + opacity: 1; + transform: rotate(-30deg); + } + + 12% { + opacity: 1; + transform: rotate(-45deg); + } + + 24% { + opacity: 1; + transform: rotate(-00deg); + } + + 30% { + opacity: 1; + transform: rotate(-30deg); + } + + 36% { + opacity: 0; + transform: rotate(-30deg); + } + + 100% { + opacity: 0; + transform: rotate(-30deg); + } +} + +@keyframes hand-drag-animation { + /* This is the animation on the little extra hand on the location input. If fades in, invites the user to interact/drag the map */ + + 0% { + opacity: 0; + transform: rotate(-30deg); + } + + 6% { + opacity: 1; + transform: rotate(-30deg); + } + + 12% { + opacity: 1; + transform: rotate(-45deg); + } + + 24% { + opacity: 1; + transform: rotate(-00deg); + } + + 30% { + opacity: 1; + transform: rotate(-30deg); + } + + 36% { + opacity: 0; + transform: rotate(-30deg); + } + + 100% { + opacity: 0; + transform: rotate(-30deg); + } +} + +/**************************************/ + +#topleft-tools { + display: block; + position: absolute; + z-index: 5000; + transition: all 500ms linear; + left: 0; + right: 0; +} + +.welcomeMessage { + display: block; + max-width: calc(100vw - 5em); + width: 40em; + max-height: calc(100vh - 15em); + overflow-y: auto; + border-radius: 1em; + background-color: var(--background-color); + color: var(--foreground-color); +} + +/***************** Info box (box containing features and questions ******************/ + +.leaflet-popup-content { + width: 45em !important; +} + +.leaflet-div-icon { + background-color: unset !important; + border: unset !important; +} + +.leaflet-div-icon svg { + width: calc(100%); + height: calc(100%); +} + +/****** ShareScreen *****/ + +.literal-code { + display: inline-block; + background-color: lightgray; + padding: 0.5em; + word-break: break-word; + color: black; + box-sizing: border-box; +} + +/** Switch layout **/ + +.small-image img { + height: 1em; + max-width: 1em; +} + +.small-image { + height: 1em; + max-width: 1em; +} + +.slideshow-item img { + height: var(--image-carousel-height); + width: unset; +} + +.hover\:bg-blue-200:hover { + --tw-bg-opacity: 1; + background-color: rgba(191, 219, 254, var(--tw-bg-opacity)); +} + +.hover\:bg-indigo-200:hover { + --tw-bg-opacity: 1; + background-color: rgba(199, 210, 254, var(--tw-bg-opacity)); +} + +.hover\:text-blue-800:hover { + --tw-text-opacity: 1; + color: rgba(30, 64, 175, var(--tw-text-opacity)); +} + +.hover\:shadow-xl:hover { + --tw-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04); + box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); +} + +.group:hover .group-hover\:text-blue-800 { + --tw-text-opacity: 1; + color: rgba(30, 64, 175, var(--tw-text-opacity)); +} + +.group:hover .group-hover\:text-blue-900 { + --tw-text-opacity: 1; + color: rgba(30, 58, 138, var(--tw-text-opacity)); +} + +@media (min-width: 640px) { + .sm\:mx-auto { + margin-left: auto; + margin-right: auto; + } + + .sm\:mt-5 { + margin-top: 1.25rem; + } + + .sm\:h-24 { + height: 6rem; + } + + .sm\:w-24 { + width: 6rem; + } + + .sm\:w-auto { + width: auto; + } + + .sm\:max-w-sm { + max-width: 24rem; + } + + .sm\:max-w-xl { + max-width: 36rem; + } + + .sm\:flex-row { + flex-direction: row; + } + + .sm\:flex-wrap { + flex-wrap: wrap; + } + + .sm\:items-start { + align-items: flex-start; + } + + .sm\:justify-between { + justify-content: space-between; + } + + .sm\:p-0\.5 { + padding: 0.125rem; + } + + .sm\:p-1\.5 { + padding: 0.375rem; + } + + .sm\:p-0 { + padding: 0px; + } + + .sm\:p-1 { + padding: 0.25rem; + } + + .sm\:pl-2 { + padding-left: 0.5rem; + } + + .sm\:pt-1 { + padding-top: 0.25rem; + } + + .sm\:text-center { + text-align: center; + } + + .sm\:text-xl { + font-size: 1.25rem; + line-height: 1.75rem; + } + + .sm\:text-5xl { + font-size: 3rem; + line-height: 1; + } + + .sm\:text-lg { + font-size: 1.125rem; + line-height: 1.75rem; + } +} + +@media (min-width: 768px) { + .md\:relative { + position: relative; + } + + .md\:m-1 { + margin: 0.25rem; + } + + .md\:m-2 { + margin: 0.5rem; + } + + .md\:mt-5 { + margin-top: 1.25rem; + } + + .md\:mt-4 { + margin-top: 1rem; + } + + .md\:block { + display: block; + } + + .md\:grid { + display: grid; + } + + .md\:hidden { + display: none; + } + + .md\:h-12 { + height: 3rem; + } + + .md\:max-h-65vh { + max-height: 65vh; + } + + .md\:w-auto { + width: auto; + } + + .md\:w-max { + width: -webkit-max-content; + width: -moz-max-content; + width: max-content; + } + + .md\:grid-flow-row { + grid-auto-flow: row; + } + + .md\:grid-cols-2 { + grid-template-columns: repeat(2, minmax(0, 1fr)); + } + + .md\:p-0 { + padding: 0px; + } + + .md\:p-1 { + padding: 0.25rem; + } + + .md\:p-2 { + padding: 0.5rem; + } + + .md\:pt-4 { + padding-top: 1rem; + } + + .md\:text-2xl { + font-size: 1.5rem; + line-height: 2rem; + } + + .md\:text-6xl { + font-size: 3.75rem; + line-height: 1; + } + + .md\:text-xl { + font-size: 1.25rem; + line-height: 1.75rem; + } + + .md\:shadow-none { + --tw-shadow: 0 0 #0000; + box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); + } +} + +@media (min-width: 1024px) { + .lg\:mx-0 { + margin-left: 0px; + margin-right: 0px; + } + + .lg\:ml-40 { + margin-left: 10rem; + } + + .lg\:w-3\/4 { + width: 75%; + } + + .lg\:grid-cols-3 { + grid-template-columns: repeat(3, minmax(0, 1fr)); + } + + .lg\:text-left { + text-align: left; + } +} + +@media (min-width: 1280px) { + .xl\:inline { + display: inline; + } +} diff --git a/css/mobile.css b/css/mobile.css index f863483ddf..a0b6cb896c 100644 --- a/css/mobile.css +++ b/css/mobile.css @@ -39,7 +39,7 @@ Contains tweaks for small screens } @media only screen and (max-width: 768px) { - .leaflet-control-attribution { + #leafletDiv .leaflet-control-attribution { display: none; } diff --git a/css/userbadge.css b/css/userbadge.css index 316bb7aa70..3b272cc48b 100644 --- a/css/userbadge.css +++ b/css/userbadge.css @@ -33,16 +33,7 @@ } .userbadge-login { - font-weight: bold; - font-size: large; background-color: var(--subtle-detail-color) !important; color: var(--subtle-detail-color-contrast); height: 3em; - - display: inline-block; - text-align: center; - margin: 0; - - min-width: 20em; - pointer-events: all; } diff --git a/css/wikipedia.css b/css/wikipedia.css new file mode 100644 index 0000000000..e636fb8dc2 --- /dev/null +++ b/css/wikipedia.css @@ -0,0 +1,40 @@ +/* This stylesheet reimplements a few classes from wikipedia to show their articles prettily */ + +.wikipedia-article { + font-family: sans-serif !important; +} + +.wikipedia-article .tright { + float: right; + clear: right; +} + +.wikipedia-article svg, .wikipedia-article img { + width: unset; + height: unset; + display: unset; +} + +.wikipedia-article .thumb { + background: var(--subtle-detail-color); + margin: 1rem; + padding: 0.5rem; + border: 1px solid var(--subtle-detail-color-light-contrast); + border-radius: 0.5rem; +} + +.wikipedia-article a:hover a:focus { + text-decoration: underline !important; +} + +.wikipedia-article a { + color: #0645ad !important; + background: none !important; + text-decoration: none; +} + + +.wikipedia-article p { + margin-bottom: 0.5rem; +} + diff --git a/index.css b/index.css index bf49576eef..d5cd064762 100644 --- a/index.css +++ b/index.css @@ -1,17 +1,18 @@ +/* + TailwindCSS JIT-Mode Input file. + Use TailwindCSS functions and directives here – https://tailwindcss.com/docs/functions-and-directives + About JIT-Mode: https://tailwindcss.com/docs/just-in-time-mode#styles-rebuild-in-an-infinite-loop + + TailwindCSS CLI generates the css/index-tailwind-output.css file based on this file. + It is not used directly in the app. +*/ + @tailwind base; @tailwind components; @tailwind utilities; @layer utilities { @variants responsive { - .max-h-65vh { - max-height: 65vh; - } - - .max-h-20vh { - max-height: 20vh; - } - .z-above-map { z-index: 10000 } @@ -19,7 +20,6 @@ .z-above-controls { z-index: 10001 } - } .btn { @@ -91,7 +91,6 @@ svg, img { box-sizing: content-box; width: 100%; height: 100%; - display: unset; } .mapcontrol svg path { @@ -130,10 +129,6 @@ btn { width: 4rem !important; } -.space-between { - justify-content: space-between; -} - .link-underline a { text-decoration: underline 1px #0078a855;; color: #0078A8; @@ -183,6 +178,11 @@ li::marker { color: var(--subtle-detail-color-contrast); } +.normal-background { + background: var(--background-color); + color: var(--foreground-color) +} + .subtle-lighter { color: var(--subtle-detail-color-light-contrast); } @@ -307,6 +307,7 @@ li::marker { @keyframes slide { + /* This is the animation on the marker to add a new point - it slides through all the possible presets */ from { transform: translateX(0%); } @@ -316,6 +317,51 @@ li::marker { } } +.hand-drag-animation { + animation: hand-drag-animation 6s ease-in-out infinite; + transform-origin: 50% 125%; +} + +@keyframes hand-drag-animation { + /* This is the animation on the little extra hand on the location input. If fades in, invites the user to interact/drag the map */ + 0% { + opacity: 0; + transform: rotate(-30deg); + } + + 6% { + opacity: 1; + transform: rotate(-30deg); + } + + 12% { + opacity: 1; + transform: rotate(-45deg); + } + + 24% { + opacity: 1; + transform: rotate(-00deg); + } + + 30% { + opacity: 1; + transform: rotate(-30deg); + } + + + 36% { + opacity: 0; + transform: rotate(-30deg); + } + + 100% { + opacity: 0; + transform: rotate(-30deg); + } + +} + /**************************************/ @@ -385,4 +431,4 @@ li::marker { .slideshow-item img { height: var(--image-carousel-height); width: unset; -} \ No newline at end of file +} diff --git a/index.html b/index.html index 0eccf7729b..a655722097 100644 --- a/index.html +++ b/index.html @@ -5,7 +5,6 @@ - @@ -14,6 +13,8 @@ + + diff --git a/index.ts b/index.ts index 06f2ea8510..e5e58a6f0a 100644 --- a/index.ts +++ b/index.ts @@ -8,31 +8,18 @@ import MoreScreen from "./UI/BigComponents/MoreScreen"; import State from "./State"; import Combine from "./UI/Base/Combine"; import Translations from "./UI/i18n/Translations"; - - -import CountryCoder from "latlon2country" - -import SimpleMetaTagger from "./Logic/SimpleMetaTagger"; -import Minimap from "./UI/Base/Minimap"; -import DirectionInput from "./UI/Input/DirectionInput"; -import SpecialVisualizations from "./UI/SpecialVisualizations"; -import ShowDataLayer from "./UI/ShowDataLayer"; -import * as L from "leaflet"; import ValidatedTextField from "./UI/Input/ValidatedTextField"; import AvailableBaseLayers from "./Logic/Actors/AvailableBaseLayers"; import LayoutConfig from "./Models/ThemeConfig/LayoutConfig"; import Constants from "./Models/Constants"; +import MinimapImplementation from "./UI/Base/MinimapImplementation"; +import CountryCoder from "latlon2country/index"; +import SimpleMetaTagger from "./Logic/SimpleMetaTagger"; +MinimapImplementation.initialize() // Workaround for a stupid crash: inject some functions which would give stupid circular dependencies or crash the other nodejs scripts -SimpleMetaTagger.coder = new CountryCoder("https://pietervdvn.github.io/latlon2country/"); -DirectionInput.constructMinimap = options => new Minimap(options) ValidatedTextField.bestLayerAt = (location, layerPref) => AvailableBaseLayers.SelectBestLayerAccordingTo(location, layerPref) -SpecialVisualizations.constructMiniMap = options => new Minimap(options) -SpecialVisualizations.constructShowDataLayer = (features: UIEventSource<{ feature: any, freshness: Date }[]>, - leafletMap: UIEventSource, - layoutToUse: UIEventSource, - enablePopups = true, - zoomToFeatures = false) => new ShowDataLayer(features, leafletMap, layoutToUse, enablePopups, zoomToFeatures) +SimpleMetaTagger.coder = new CountryCoder("https://pietervdvn.github.io/latlon2country/"); let defaultLayout = "" // --------------------- Special actions based on the parameters ----------------- diff --git a/langs/en.json b/langs/en.json index 709078bfe1..650963864f 100644 --- a/langs/en.json +++ b/langs/en.json @@ -13,7 +13,8 @@ "uploadDone": "Your picture has been added. Thanks for helping out!", "dontDelete": "Cancel", "doDelete": "Remove image", - "isDeleted": "Deleted" + "isDeleted": "Deleted", + "hasBeenImported": "This feature has been imported" }, "centerMessage": { "loadingData": "Loading data…", @@ -61,6 +62,7 @@ "readMessages": "You have unread messages. Read these before deleting a point - someone might have feedback" }, "general": { + "loading": "Loading...", "pdf": { "generatedWith": "Generated with MapComplete.osm.be", "attr": "Map data © OpenStreetMap Contributors, reusable under ODbL", @@ -100,7 +102,8 @@ "confirmIntro": "

Add a {title} here?

The point you create here will be visible for everyone. Please, only add things on to the map if they truly exist. A lot of applications use this data.", "confirmButton": "Add a {category} here.
Your addition is visible for everyone
", "openLayerControl": "Open the layer control box", - "layerNotEnabled": "The layer {layer} is not enabled. Enable this layer to add a point" + "layerNotEnabled": "The layer {layer} is not enabled. Enable this layer to add a point", + "hasBeenImported": "This point has already been imported" }, "pickLanguage": "Choose a language: ", "about": "Easily edit and add OpenStreetMap for a certain theme", @@ -158,7 +161,7 @@ "noTagsSelected": "No tags selected", "testing": "Testing - changes won't be saved", "customThemeIntro": "

Custom themes

These are previously visited user-generated themes.", - "aboutMapcomplete": "

About MapComplete

With MapComplete you can enrich OpenStreetMap with information on a single theme. Answer a few questions, and within minutes your contributions will be available around the globe! The theme maintainer defines elements, questions and languages for the theme.

Find out more

MapComplete always offers the next step to learn more about OpenStreetMap.

  • When embedded in a website, the iframe links to a full-screen MapComplete
  • The full-screen version offers information about OpenStreetMap
  • Viewing works without login, but editing requires an OSM login.
  • If you are not logged in, you are asked to log in
  • Once you answered a single question, you can add new points to the map
  • After a while, actual OSM-tags are shown, later linking to the wiki


Did you notice an issue? Do you have a feature request? Want to help translate? Head over to the source code or issue tracker.

Want to see your progress? Follow the edit count on OsmCha.

", + "aboutMapcomplete": "

About MapComplete

With MapComplete you can enrich OpenStreetMap with information on a single theme. Answer a few questions, and within minutes your contributions will be available around the globe! The theme maintainer defines elements, questions and languages for the theme.

Find out more

MapComplete always offers the next step to learn more about OpenStreetMap.

  • When embedded in a website, the iframe links to a full-screen MapComplete
  • The full-screen version offers information about OpenStreetMap
  • Viewing works without login, but editing requires an OSM login.
  • If you are not logged in, you are asked to log in
  • Once you answered a single question, you can add new points to the map
  • After a while, actual OSM-tags are shown, later linking to the wiki


Did you notice an issue? Do you have a feature request? Want to help translate? Head over to the source code or issue tracker.

Want to see your progress? Follow the edit count on OsmCha.

", "backgroundMap": "Background map", "openTheMap": "Open the map", "loginOnlyNeededToEdit": "if you want to edit the map", @@ -171,6 +174,7 @@ "downloadAsPdf": "Download a PDF of the current map", "downloadAsPdfHelper": "Ideal to print the current map", "downloadGeojson": "Download visible data as geojson", + "exporting": "Exporting...", "downloadGeoJsonHelper": "Compatible with QGIS, ArcGIS, ESRI, ...", "downloadCSV": "Download visible data as CSV", "downloadCSVHelper": "Compatible with LibreOffice Calc, Excel, …", @@ -213,6 +217,12 @@ }, "histogram": { "error_loading": "Could not load the histogram" + }, + "wikipedia": { + "wikipediaboxTitle": "Wikipedia", + "failed":"Loading the wikipedia entry failed", + "loading": "Loading Wikipedia...", + "noWikipediaPage": "This wikidata item has no corresponding wikipedia page yet." } }, "favourite": { @@ -236,4 +246,4 @@ "attribution": "Reviews are powered by Mangrove Reviews and are available under CC-BY 4.0.", "plz_login": "Login to leave a review" } -} +} \ No newline at end of file diff --git a/langs/layers/de.json b/langs/layers/de.json index d98acb5820..b95c28ac54 100644 --- a/langs/layers/de.json +++ b/langs/layers/de.json @@ -3,8 +3,7 @@ "name": "Sitzbänke", "presets": { "0": { - "description": "Neue Sitzbank eintragen", - "title": "Sitzbank" + "title": "sitzbank" } }, "tagRenderings": { @@ -754,7 +753,7 @@ "name": "Trinkwasser", "presets": { "0": { - "title": "Trinkwasser" + "title": "trinkwasser" } }, "tagRenderings": { @@ -823,7 +822,7 @@ "name": "Informationstafeln", "presets": { "0": { - "title": "Informationstafel" + "title": "informationstafel" } }, "title": { @@ -904,7 +903,7 @@ "name": "Picknick-Tische", "presets": { "0": { - "title": "Picknicktisch" + "title": "picknicktisch" } }, "tagRenderings": { @@ -1134,11 +1133,11 @@ "presets": { "0": { "description": "Eine öffentlich zugängliche Toilette", - "title": "Toilette" + "title": "toilette" }, "1": { "description": "Eine Toilettenanlage mit mindestens einer rollstuhlgerechten Toilette", - "title": "Toiletten mit rollstuhlgerechter Toilette" + "title": "toiletten mit rollstuhlgerechter Toilette" } }, "tagRenderings": { diff --git a/langs/layers/en.json b/langs/layers/en.json index 8908ed7ecf..ac297439b2 100644 --- a/langs/layers/en.json +++ b/langs/layers/en.json @@ -94,8 +94,7 @@ "name": "Benches", "presets": { "0": { - "description": "Add a new bench", - "title": "Bench" + "title": "bench" } }, "tagRenderings": { @@ -883,43 +882,49 @@ "question": "All connectors" }, "1": { - "question": "Has a Schuko wall plug without ground pin (CEE7/4 type F) connector" + "question": "Has a
Schuko wall plug without ground pin (CEE7/4 type F)
connector" }, "2": { - "question": "Has a European wall plug with ground pin (CEE7/4 type E) connector" + "question": "Has a
European wall plug with ground pin (CEE7/4 type E)
connector" }, "3": { - "question": "Has a Chademo connector" + "question": "Has a
Chademo
connector" }, "4": { - "question": "Has a Type 1 with cable (J1772) connector" + "question": "Has a
Type 1 with cable (J1772)
connector" }, "5": { - "question": "Has a Type 1 without cable (J1772) connector" + "question": "Has a
Type 1 without cable (J1772)
connector" }, "6": { - "question": "Has a Type 1 CCS (aka Type 1 Combo) connector" + "question": "Has a
Type 1 CCS (aka Type 1 Combo)
connector" }, "7": { - "question": "Has a Tesla Supercharger connector" + "question": "Has a
Tesla Supercharger
connector" }, "8": { - "question": "Has a Type 2 (mennekes) connector" + "question": "Has a
Type 2 (mennekes)
connector" }, "9": { - "question": "Has a Type 2 CCS (mennekes) connector" + "question": "Has a
Type 2 CCS (mennekes)
connector" }, "10": { - "question": "Has a Type 2 with cable (mennekes) connector" + "question": "Has a
Type 2 with cable (mennekes)
connector" }, "11": { - "question": "Has a Tesla Supercharger CCS (a branded type2_css) connector" + "question": "Has a
Tesla Supercharger CCS (a branded type2_css)
connector" }, "12": { - "question": "Has a Tesla Supercharger (destination) connector" + "question": "Has a
Tesla Supercharger (destination)
connector" }, "13": { - "question": "Has a Tesla supercharger (destination (A Type 2 with cable branded as tesla) connector" + "question": "Has a
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
connector" + }, + "14": { + "question": "Has a
USB to charge phones and small electronics
connector" + }, + "15": { + "question": "Has a
Bosch Active Connect with cable
connector" } } } @@ -967,82 +972,94 @@ "Available_charging_stations (generated)": { "mappings": { "0": { - "then": " Schuko wall plug without ground pin (CEE7/4 type F)" + "then": "
Schuko wall plug without ground pin (CEE7/4 type F)
" }, "1": { - "then": " Schuko wall plug without ground pin (CEE7/4 type F)" + "then": "
Schuko wall plug without ground pin (CEE7/4 type F)
" }, "2": { - "then": " European wall plug with ground pin (CEE7/4 type E)" + "then": "
European wall plug with ground pin (CEE7/4 type E)
" }, "3": { - "then": " European wall plug with ground pin (CEE7/4 type E)" + "then": "
European wall plug with ground pin (CEE7/4 type E)
" }, "4": { - "then": " Chademo" + "then": "
Chademo
" }, "5": { - "then": " Chademo" + "then": "
Chademo
" }, "6": { - "then": " Type 1 with cable (J1772)" + "then": "
Type 1 with cable (J1772)
" }, "7": { - "then": " Type 1 with cable (J1772)" + "then": "
Type 1 with cable (J1772)
" }, "8": { - "then": " Type 1 without cable (J1772)" + "then": "
Type 1 without cable (J1772)
" }, "9": { - "then": " Type 1 without cable (J1772)" + "then": "
Type 1 without cable (J1772)
" }, "10": { - "then": " Type 1 CCS (aka Type 1 Combo)" + "then": "
Type 1 CCS (aka Type 1 Combo)
" }, "11": { - "then": " Type 1 CCS (aka Type 1 Combo)" + "then": "
Type 1 CCS (aka Type 1 Combo)
" }, "12": { - "then": " Tesla Supercharger" + "then": "
Tesla Supercharger
" }, "13": { - "then": " Tesla Supercharger" + "then": "
Tesla Supercharger
" }, "14": { - "then": " Type 2 (mennekes)" + "then": "
Type 2 (mennekes)
" }, "15": { - "then": " Type 2 (mennekes)" + "then": "
Type 2 (mennekes)
" }, "16": { - "then": " Type 2 CCS (mennekes)" + "then": "
Type 2 CCS (mennekes)
" }, "17": { - "then": " Type 2 CCS (mennekes)" + "then": "
Type 2 CCS (mennekes)
" }, "18": { - "then": " Type 2 with cable (mennekes)" + "then": "
Type 2 with cable (mennekes)
" }, "19": { - "then": " Type 2 with cable (mennekes)" + "then": "
Type 2 with cable (mennekes)
" }, "20": { - "then": " Tesla Supercharger CCS (a branded type2_css)" + "then": "
Tesla Supercharger CCS (a branded type2_css)
" }, "21": { - "then": " Tesla Supercharger CCS (a branded type2_css)" + "then": "
Tesla Supercharger CCS (a branded type2_css)
" }, "22": { - "then": " Tesla Supercharger (destination)" + "then": "
Tesla Supercharger (destination)
" }, "23": { - "then": " Tesla Supercharger (destination)" + "then": "
Tesla Supercharger (destination)
" }, "24": { - "then": " Tesla supercharger (destination (A Type 2 with cable branded as tesla)" + "then": "
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
" }, "25": { - "then": " Tesla supercharger (destination (A Type 2 with cable branded as tesla)" + "then": "
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
" + }, + "26": { + "then": "
USB to charge phones and small electronics
" + }, + "27": { + "then": "
USB to charge phones and small electronics
" + }, + "28": { + "then": "
Bosch Active Connect with cable
" + }, + "29": { + "then": "
Bosch Active Connect with cable
" } }, "question": "Which charging stations are available here?" @@ -1138,143 +1155,159 @@ "current-0": { "mappings": { "0": { - "then": "Schuko wall plug without ground pin (CEE7/4 type F) outputs at most 16 A" + "then": "
Schuko wall plug without ground pin (CEE7/4 type F)
outputs at most 16 A" } }, - "question": "What current do the plugs with Schuko wall plug without ground pin (CEE7/4 type F) offer?", - "render": "Schuko wall plug without ground pin (CEE7/4 type F) outputs at most {socket:schuko:current}A" + "question": "What current do the plugs with
Schuko wall plug without ground pin (CEE7/4 type F)
offer?", + "render": "
Schuko wall plug without ground pin (CEE7/4 type F)
outputs at most {socket:schuko:current}A" }, "current-1": { "mappings": { "0": { - "then": "European wall plug with ground pin (CEE7/4 type E) outputs at most 16 A" + "then": "
European wall plug with ground pin (CEE7/4 type E)
outputs at most 16 A" } }, - "question": "What current do the plugs with European wall plug with ground pin (CEE7/4 type E) offer?", - "render": "European wall plug with ground pin (CEE7/4 type E) outputs at most {socket:typee:current}A" + "question": "What current do the plugs with
European wall plug with ground pin (CEE7/4 type E)
offer?", + "render": "
European wall plug with ground pin (CEE7/4 type E)
outputs at most {socket:typee:current}A" }, "current-10": { "mappings": { "0": { - "then": "Tesla Supercharger CCS (a branded type2_css) outputs at most 125 A" + "then": "
Tesla Supercharger CCS (a branded type2_css)
outputs at most 125 A" }, "1": { - "then": "Tesla Supercharger CCS (a branded type2_css) outputs at most 350 A" + "then": "
Tesla Supercharger CCS (a branded type2_css)
outputs at most 350 A" } }, - "question": "What current do the plugs with Tesla Supercharger CCS (a branded type2_css) offer?", - "render": "Tesla Supercharger CCS (a branded type2_css) outputs at most {socket:tesla_supercharger_ccs:current}A" + "question": "What current do the plugs with
Tesla Supercharger CCS (a branded type2_css)
offer?", + "render": "
Tesla Supercharger CCS (a branded type2_css)
outputs at most {socket:tesla_supercharger_ccs:current}A" }, "current-11": { "mappings": { "0": { - "then": "Tesla Supercharger (destination) outputs at most 125 A" + "then": "
Tesla Supercharger (destination)
outputs at most 125 A" }, "1": { - "then": "Tesla Supercharger (destination) outputs at most 350 A" + "then": "
Tesla Supercharger (destination)
outputs at most 350 A" } }, - "question": "What current do the plugs with Tesla Supercharger (destination) offer?", - "render": "Tesla Supercharger (destination) outputs at most {socket:tesla_destination:current}A" + "question": "What current do the plugs with
Tesla Supercharger (destination)
offer?", + "render": "
Tesla Supercharger (destination)
outputs at most {socket:tesla_destination:current}A" }, "current-12": { "mappings": { "0": { - "then": "Tesla supercharger (destination (A Type 2 with cable branded as tesla) outputs at most 16 A" + "then": "
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
outputs at most 16 A" }, "1": { - "then": "Tesla supercharger (destination (A Type 2 with cable branded as tesla) outputs at most 32 A" + "then": "
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
outputs at most 32 A" } }, - "question": "What current do the plugs with Tesla supercharger (destination (A Type 2 with cable branded as tesla) offer?", - "render": "Tesla supercharger (destination (A Type 2 with cable branded as tesla) outputs at most {socket:tesla_destination:current}A" + "question": "What current do the plugs with
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
offer?", + "render": "
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
outputs at most {socket:tesla_destination:current}A" + }, + "current-13": { + "mappings": { + "0": { + "then": "
USB to charge phones and small electronics
outputs at most 1 A" + }, + "1": { + "then": "
USB to charge phones and small electronics
outputs at most 2 A" + } + }, + "question": "What current do the plugs with
USB to charge phones and small electronics
offer?", + "render": "
USB to charge phones and small electronics
outputs at most {socket:USB-A:current}A" + }, + "current-14": { + "question": "What current do the plugs with
Bosch Active Connect with cable
offer?", + "render": "
Bosch Active Connect with cable
outputs at most {socket:bosch_3pin:current}A" }, "current-2": { "mappings": { "0": { - "then": "Chademo outputs at most 120 A" + "then": "
Chademo
outputs at most 120 A" } }, - "question": "What current do the plugs with Chademo offer?", - "render": "Chademo outputs at most {socket:chademo:current}A" + "question": "What current do the plugs with
Chademo
offer?", + "render": "
Chademo
outputs at most {socket:chademo:current}A" }, "current-3": { "mappings": { "0": { - "then": "Type 1 with cable (J1772) outputs at most 32 A" + "then": "
Type 1 with cable (J1772)
outputs at most 32 A" } }, - "question": "What current do the plugs with Type 1 with cable (J1772) offer?", - "render": "Type 1 with cable (J1772) outputs at most {socket:type1_cable:current}A" + "question": "What current do the plugs with
Type 1 with cable (J1772)
offer?", + "render": "
Type 1 with cable (J1772)
outputs at most {socket:type1_cable:current}A" }, "current-4": { "mappings": { "0": { - "then": "Type 1 without cable (J1772) outputs at most 32 A" + "then": "
Type 1 without cable (J1772)
outputs at most 32 A" } }, - "question": "What current do the plugs with Type 1 without cable (J1772) offer?", - "render": "Type 1 without cable (J1772) outputs at most {socket:type1:current}A" + "question": "What current do the plugs with
Type 1 without cable (J1772)
offer?", + "render": "
Type 1 without cable (J1772)
outputs at most {socket:type1:current}A" }, "current-5": { "mappings": { "0": { - "then": "Type 1 CCS (aka Type 1 Combo) outputs at most 50 A" + "then": "
Type 1 CCS (aka Type 1 Combo)
outputs at most 50 A" }, "1": { - "then": "Type 1 CCS (aka Type 1 Combo) outputs at most 125 A" + "then": "
Type 1 CCS (aka Type 1 Combo)
outputs at most 125 A" } }, - "question": "What current do the plugs with Type 1 CCS (aka Type 1 Combo) offer?", - "render": "Type 1 CCS (aka Type 1 Combo) outputs at most {socket:type1_combo:current}A" + "question": "What current do the plugs with
Type 1 CCS (aka Type 1 Combo)
offer?", + "render": "
Type 1 CCS (aka Type 1 Combo)
outputs at most {socket:type1_combo:current}A" }, "current-6": { "mappings": { "0": { - "then": "Tesla Supercharger outputs at most 125 A" + "then": "
Tesla Supercharger
outputs at most 125 A" }, "1": { - "then": "Tesla Supercharger outputs at most 350 A" + "then": "
Tesla Supercharger
outputs at most 350 A" } }, - "question": "What current do the plugs with Tesla Supercharger offer?", - "render": "Tesla Supercharger outputs at most {socket:tesla_supercharger:current}A" + "question": "What current do the plugs with
Tesla Supercharger
offer?", + "render": "
Tesla Supercharger
outputs at most {socket:tesla_supercharger:current}A" }, "current-7": { "mappings": { "0": { - "then": "Type 2 (mennekes) outputs at most 16 A" + "then": "
Type 2 (mennekes)
outputs at most 16 A" }, "1": { - "then": "Type 2 (mennekes) outputs at most 32 A" + "then": "
Type 2 (mennekes)
outputs at most 32 A" } }, - "question": "What current do the plugs with Type 2 (mennekes) offer?", - "render": "Type 2 (mennekes) outputs at most {socket:type2:current}A" + "question": "What current do the plugs with
Type 2 (mennekes)
offer?", + "render": "
Type 2 (mennekes)
outputs at most {socket:type2:current}A" }, "current-8": { "mappings": { "0": { - "then": "Type 2 CCS (mennekes) outputs at most 125 A" + "then": "
Type 2 CCS (mennekes)
outputs at most 125 A" }, "1": { - "then": "Type 2 CCS (mennekes) outputs at most 350 A" + "then": "
Type 2 CCS (mennekes)
outputs at most 350 A" } }, - "question": "What current do the plugs with Type 2 CCS (mennekes) offer?", - "render": "Type 2 CCS (mennekes) outputs at most {socket:type2_combo:current}A" + "question": "What current do the plugs with
Type 2 CCS (mennekes)
offer?", + "render": "
Type 2 CCS (mennekes)
outputs at most {socket:type2_combo:current}A" }, "current-9": { "mappings": { "0": { - "then": "Type 2 with cable (mennekes) outputs at most 16 A" + "then": "
Type 2 with cable (mennekes)
outputs at most 16 A" }, "1": { - "then": "Type 2 with cable (mennekes) outputs at most 32 A" + "then": "
Type 2 with cable (mennekes)
outputs at most 32 A" } }, - "question": "What current do the plugs with Type 2 with cable (mennekes) offer?", - "render": "Type 2 with cable (mennekes) outputs at most {socket:type2_cable:current}A" + "question": "What current do the plugs with
Type 2 with cable (mennekes)
offer?", + "render": "
Type 2 with cable (mennekes)
outputs at most {socket:type2_cable:current}A" }, "email": { "question": "What is the email address of the operator?", @@ -1300,14 +1333,6 @@ }, "payment-options": { "override": { - "mappings": { - "0": { - "then": "Payment is done using a dedicated app" - }, - "1": { - "then": "Payment is done using a membership card" - } - }, "mappings+": { "0": { "then": "Payment is done using a dedicated app" @@ -1323,218 +1348,242 @@ "render": "In case of problems, call {phone}" }, "plugs-0": { - "question": "How much plugs of type Schuko wall plug without ground pin (CEE7/4 type F) are available here?", - "render": "There are Schuko wall plug without ground pin (CEE7/4 type F) plugs of type Schuko wall plug without ground pin (CEE7/4 type F) available here" + "question": "How much plugs of type
Schuko wall plug without ground pin (CEE7/4 type F)
are available here?", + "render": "There are {socket:schuko} plugs of type
Schuko wall plug without ground pin (CEE7/4 type F)
available here" }, "plugs-1": { - "question": "How much plugs of type European wall plug with ground pin (CEE7/4 type E) are available here?", - "render": "There are European wall plug with ground pin (CEE7/4 type E) plugs of type European wall plug with ground pin (CEE7/4 type E) available here" + "question": "How much plugs of type
European wall plug with ground pin (CEE7/4 type E)
are available here?", + "render": "There are {socket:typee} plugs of type
European wall plug with ground pin (CEE7/4 type E)
available here" }, "plugs-10": { - "question": "How much plugs of type Tesla Supercharger CCS (a branded type2_css) are available here?", - "render": "There are Tesla Supercharger CCS (a branded type2_css) plugs of type Tesla Supercharger CCS (a branded type2_css) available here" + "question": "How much plugs of type
Tesla Supercharger CCS (a branded type2_css)
are available here?", + "render": "There are {socket:tesla_supercharger_ccs} plugs of type
Tesla Supercharger CCS (a branded type2_css)
available here" }, "plugs-11": { - "question": "How much plugs of type Tesla Supercharger (destination) are available here?", - "render": "There are Tesla Supercharger (destination) plugs of type Tesla Supercharger (destination) available here" + "question": "How much plugs of type
Tesla Supercharger (destination)
are available here?", + "render": "There are {socket:tesla_destination} plugs of type
Tesla Supercharger (destination)
available here" }, "plugs-12": { - "question": "How much plugs of type Tesla supercharger (destination (A Type 2 with cable branded as tesla) are available here?", - "render": "There are Tesla supercharger (destination (A Type 2 with cable branded as tesla) plugs of type Tesla supercharger (destination (A Type 2 with cable branded as tesla) available here" + "question": "How much plugs of type
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
are available here?", + "render": "There are {socket:tesla_destination} plugs of type
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
available here" + }, + "plugs-13": { + "question": "How much plugs of type
USB to charge phones and small electronics
are available here?", + "render": "There are {socket:USB-A} plugs of type
USB to charge phones and small electronics
available here" + }, + "plugs-14": { + "question": "How much plugs of type
Bosch Active Connect with cable
are available here?", + "render": "There are {socket:bosch_3pin} plugs of type
Bosch Active Connect with cable
available here" }, "plugs-2": { - "question": "How much plugs of type Chademo are available here?", - "render": "There are Chademo plugs of type Chademo available here" + "question": "How much plugs of type
Chademo
are available here?", + "render": "There are {socket:chademo} plugs of type
Chademo
available here" }, "plugs-3": { - "question": "How much plugs of type Type 1 with cable (J1772) are available here?", - "render": "There are Type 1 with cable (J1772) plugs of type Type 1 with cable (J1772) available here" + "question": "How much plugs of type
Type 1 with cable (J1772)
are available here?", + "render": "There are {socket:type1_cable} plugs of type
Type 1 with cable (J1772)
available here" }, "plugs-4": { - "question": "How much plugs of type Type 1 without cable (J1772) are available here?", - "render": "There are Type 1 without cable (J1772) plugs of type Type 1 without cable (J1772) available here" + "question": "How much plugs of type
Type 1 without cable (J1772)
are available here?", + "render": "There are {socket:type1} plugs of type
Type 1 without cable (J1772)
available here" }, "plugs-5": { - "question": "How much plugs of type Type 1 CCS (aka Type 1 Combo) are available here?", - "render": "There are Type 1 CCS (aka Type 1 Combo) plugs of type Type 1 CCS (aka Type 1 Combo) available here" + "question": "How much plugs of type
Type 1 CCS (aka Type 1 Combo)
are available here?", + "render": "There are {socket:type1_combo} plugs of type
Type 1 CCS (aka Type 1 Combo)
available here" }, "plugs-6": { - "question": "How much plugs of type Tesla Supercharger are available here?", - "render": "There are Tesla Supercharger plugs of type Tesla Supercharger available here" + "question": "How much plugs of type
Tesla Supercharger
are available here?", + "render": "There are {socket:tesla_supercharger} plugs of type
Tesla Supercharger
available here" }, "plugs-7": { - "question": "How much plugs of type Type 2 (mennekes) are available here?", - "render": "There are Type 2 (mennekes) plugs of type Type 2 (mennekes) available here" + "question": "How much plugs of type
Type 2 (mennekes)
are available here?", + "render": "There are {socket:type2} plugs of type
Type 2 (mennekes)
available here" }, "plugs-8": { - "question": "How much plugs of type Type 2 CCS (mennekes) are available here?", - "render": "There are Type 2 CCS (mennekes) plugs of type Type 2 CCS (mennekes) available here" + "question": "How much plugs of type
Type 2 CCS (mennekes)
are available here?", + "render": "There are {socket:type2_combo} plugs of type
Type 2 CCS (mennekes)
available here" }, "plugs-9": { - "question": "How much plugs of type Type 2 with cable (mennekes) are available here?", - "render": "There are Type 2 with cable (mennekes) plugs of type Type 2 with cable (mennekes) available here" + "question": "How much plugs of type
Type 2 with cable (mennekes)
are available here?", + "render": "There are {socket:type2_cable} plugs of type
Type 2 with cable (mennekes)
available here" }, "power-output-0": { "mappings": { "0": { - "then": "Schuko wall plug without ground pin (CEE7/4 type F) outputs at most 3.6 kw" + "then": "
Schuko wall plug without ground pin (CEE7/4 type F)
outputs at most 3.6 kw" } }, - "question": "What power output does a single plug of type Schuko wall plug without ground pin (CEE7/4 type F) offer?", - "render": "Schuko wall plug without ground pin (CEE7/4 type F) outputs at most {socket:schuko:output}" + "question": "What power output does a single plug of type
Schuko wall plug without ground pin (CEE7/4 type F)
offer?", + "render": "
Schuko wall plug without ground pin (CEE7/4 type F)
outputs at most {socket:schuko:output}" }, "power-output-1": { "mappings": { "0": { - "then": "European wall plug with ground pin (CEE7/4 type E) outputs at most 3 kw" + "then": "
European wall plug with ground pin (CEE7/4 type E)
outputs at most 3 kw" }, "1": { - "then": "European wall plug with ground pin (CEE7/4 type E) outputs at most 22 kw" + "then": "
European wall plug with ground pin (CEE7/4 type E)
outputs at most 22 kw" } }, - "question": "What power output does a single plug of type European wall plug with ground pin (CEE7/4 type E) offer?", - "render": "European wall plug with ground pin (CEE7/4 type E) outputs at most {socket:typee:output}" + "question": "What power output does a single plug of type
European wall plug with ground pin (CEE7/4 type E)
offer?", + "render": "
European wall plug with ground pin (CEE7/4 type E)
outputs at most {socket:typee:output}" }, "power-output-10": { "mappings": { "0": { - "then": "Tesla Supercharger CCS (a branded type2_css) outputs at most 50 kw" + "then": "
Tesla Supercharger CCS (a branded type2_css)
outputs at most 50 kw" } }, - "question": "What power output does a single plug of type Tesla Supercharger CCS (a branded type2_css) offer?", - "render": "Tesla Supercharger CCS (a branded type2_css) outputs at most {socket:tesla_supercharger_ccs:output}" + "question": "What power output does a single plug of type
Tesla Supercharger CCS (a branded type2_css)
offer?", + "render": "
Tesla Supercharger CCS (a branded type2_css)
outputs at most {socket:tesla_supercharger_ccs:output}" }, "power-output-11": { "mappings": { "0": { - "then": "Tesla Supercharger (destination) outputs at most 120 kw" + "then": "
Tesla Supercharger (destination)
outputs at most 120 kw" }, "1": { - "then": "Tesla Supercharger (destination) outputs at most 150 kw" + "then": "
Tesla Supercharger (destination)
outputs at most 150 kw" }, "2": { - "then": "Tesla Supercharger (destination) outputs at most 250 kw" + "then": "
Tesla Supercharger (destination)
outputs at most 250 kw" } }, - "question": "What power output does a single plug of type Tesla Supercharger (destination) offer?", - "render": "Tesla Supercharger (destination) outputs at most {socket:tesla_destination:output}" + "question": "What power output does a single plug of type
Tesla Supercharger (destination)
offer?", + "render": "
Tesla Supercharger (destination)
outputs at most {socket:tesla_destination:output}" }, "power-output-12": { "mappings": { "0": { - "then": "Tesla supercharger (destination (A Type 2 with cable branded as tesla) outputs at most 11 kw" + "then": "
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
outputs at most 11 kw" }, "1": { - "then": "Tesla supercharger (destination (A Type 2 with cable branded as tesla) outputs at most 22 kw" + "then": "
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
outputs at most 22 kw" } }, - "question": "What power output does a single plug of type Tesla supercharger (destination (A Type 2 with cable branded as tesla) offer?", - "render": "Tesla supercharger (destination (A Type 2 with cable branded as tesla) outputs at most {socket:tesla_destination:output}" + "question": "What power output does a single plug of type
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
offer?", + "render": "
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
outputs at most {socket:tesla_destination:output}" + }, + "power-output-13": { + "mappings": { + "0": { + "then": "
USB to charge phones and small electronics
outputs at most 5w" + }, + "1": { + "then": "
USB to charge phones and small electronics
outputs at most 10w" + } + }, + "question": "What power output does a single plug of type
USB to charge phones and small electronics
offer?", + "render": "
USB to charge phones and small electronics
outputs at most {socket:USB-A:output}" + }, + "power-output-14": { + "question": "What power output does a single plug of type
Bosch Active Connect with cable
offer?", + "render": "
Bosch Active Connect with cable
outputs at most {socket:bosch_3pin:output}" }, "power-output-2": { "mappings": { "0": { - "then": "Chademo outputs at most 50 kw" + "then": "
Chademo
outputs at most 50 kw" } }, - "question": "What power output does a single plug of type Chademo offer?", - "render": "Chademo outputs at most {socket:chademo:output}" + "question": "What power output does a single plug of type
Chademo
offer?", + "render": "
Chademo
outputs at most {socket:chademo:output}" }, "power-output-3": { "mappings": { "0": { - "then": "Type 1 with cable (J1772) outputs at most 3.7 kw" + "then": "
Type 1 with cable (J1772)
outputs at most 3.7 kw" }, "1": { - "then": "Type 1 with cable (J1772) outputs at most 7 kw" + "then": "
Type 1 with cable (J1772)
outputs at most 7 kw" } }, - "question": "What power output does a single plug of type Type 1 with cable (J1772) offer?", - "render": "Type 1 with cable (J1772) outputs at most {socket:type1_cable:output}" + "question": "What power output does a single plug of type
Type 1 with cable (J1772)
offer?", + "render": "
Type 1 with cable (J1772)
outputs at most {socket:type1_cable:output}" }, "power-output-4": { "mappings": { "0": { - "then": "Type 1 without cable (J1772) outputs at most 3.7 kw" + "then": "
Type 1 without cable (J1772)
outputs at most 3.7 kw" }, "1": { - "then": "Type 1 without cable (J1772) outputs at most 6.6 kw" + "then": "
Type 1 without cable (J1772)
outputs at most 6.6 kw" }, "2": { - "then": "Type 1 without cable (J1772) outputs at most 7 kw" + "then": "
Type 1 without cable (J1772)
outputs at most 7 kw" }, "3": { - "then": "Type 1 without cable (J1772) outputs at most 7.2 kw" + "then": "
Type 1 without cable (J1772)
outputs at most 7.2 kw" } }, - "question": "What power output does a single plug of type Type 1 without cable (J1772) offer?", - "render": "Type 1 without cable (J1772) outputs at most {socket:type1:output}" + "question": "What power output does a single plug of type
Type 1 without cable (J1772)
offer?", + "render": "
Type 1 without cable (J1772)
outputs at most {socket:type1:output}" }, "power-output-5": { "mappings": { "0": { - "then": "Type 1 CCS (aka Type 1 Combo) outputs at most 50 kw" + "then": "
Type 1 CCS (aka Type 1 Combo)
outputs at most 50 kw" }, "1": { - "then": "Type 1 CCS (aka Type 1 Combo) outputs at most 62.5 kw" + "then": "
Type 1 CCS (aka Type 1 Combo)
outputs at most 62.5 kw" }, "2": { - "then": "Type 1 CCS (aka Type 1 Combo) outputs at most 150 kw" + "then": "
Type 1 CCS (aka Type 1 Combo)
outputs at most 150 kw" }, "3": { - "then": "Type 1 CCS (aka Type 1 Combo) outputs at most 350 kw" + "then": "
Type 1 CCS (aka Type 1 Combo)
outputs at most 350 kw" } }, - "question": "What power output does a single plug of type Type 1 CCS (aka Type 1 Combo) offer?", - "render": "Type 1 CCS (aka Type 1 Combo) outputs at most {socket:type1_combo:output}" + "question": "What power output does a single plug of type
Type 1 CCS (aka Type 1 Combo)
offer?", + "render": "
Type 1 CCS (aka Type 1 Combo)
outputs at most {socket:type1_combo:output}" }, "power-output-6": { "mappings": { "0": { - "then": "Tesla Supercharger outputs at most 120 kw" + "then": "
Tesla Supercharger
outputs at most 120 kw" }, "1": { - "then": "Tesla Supercharger outputs at most 150 kw" + "then": "
Tesla Supercharger
outputs at most 150 kw" }, "2": { - "then": "Tesla Supercharger outputs at most 250 kw" + "then": "
Tesla Supercharger
outputs at most 250 kw" } }, - "question": "What power output does a single plug of type Tesla Supercharger offer?", - "render": "Tesla Supercharger outputs at most {socket:tesla_supercharger:output}" + "question": "What power output does a single plug of type
Tesla Supercharger
offer?", + "render": "
Tesla Supercharger
outputs at most {socket:tesla_supercharger:output}" }, "power-output-7": { "mappings": { "0": { - "then": "Type 2 (mennekes) outputs at most 11 kw" + "then": "
Type 2 (mennekes)
outputs at most 11 kw" }, "1": { - "then": "Type 2 (mennekes) outputs at most 22 kw" + "then": "
Type 2 (mennekes)
outputs at most 22 kw" } }, - "question": "What power output does a single plug of type Type 2 (mennekes) offer?", - "render": "Type 2 (mennekes) outputs at most {socket:type2:output}" + "question": "What power output does a single plug of type
Type 2 (mennekes)
offer?", + "render": "
Type 2 (mennekes)
outputs at most {socket:type2:output}" }, "power-output-8": { "mappings": { "0": { - "then": "Type 2 CCS (mennekes) outputs at most 50 kw" + "then": "
Type 2 CCS (mennekes)
outputs at most 50 kw" } }, - "question": "What power output does a single plug of type Type 2 CCS (mennekes) offer?", - "render": "Type 2 CCS (mennekes) outputs at most {socket:type2_combo:output}" + "question": "What power output does a single plug of type
Type 2 CCS (mennekes)
offer?", + "render": "
Type 2 CCS (mennekes)
outputs at most {socket:type2_combo:output}" }, "power-output-9": { "mappings": { "0": { - "then": "Type 2 with cable (mennekes) outputs at most 11 kw" + "then": "
Type 2 with cable (mennekes)
outputs at most 11 kw" }, "1": { - "then": "Type 2 with cable (mennekes) outputs at most 22 kw" + "then": "
Type 2 with cable (mennekes)
outputs at most 22 kw" } }, - "question": "What power output does a single plug of type Type 2 with cable (mennekes) offer?", - "render": "Type 2 with cable (mennekes) outputs at most {socket:type2_cable:output}" + "question": "What power output does a single plug of type
Type 2 with cable (mennekes)
offer?", + "render": "
Type 2 with cable (mennekes)
outputs at most {socket:type2_cable:output}" }, "ref": { "question": "What is the reference number of this charging station?", @@ -1543,143 +1592,156 @@ "voltage-0": { "mappings": { "0": { - "then": "Schuko wall plug without ground pin (CEE7/4 type F) outputs 230 volt" + "then": "
Schuko wall plug without ground pin (CEE7/4 type F)
outputs 230 volt" } }, - "question": "What voltage do the plugs with Schuko wall plug without ground pin (CEE7/4 type F) offer?", - "render": "Schuko wall plug without ground pin (CEE7/4 type F) outputs {socket:schuko:voltage} volt" + "question": "What voltage do the plugs with
Schuko wall plug without ground pin (CEE7/4 type F)
offer?", + "render": "
Schuko wall plug without ground pin (CEE7/4 type F)
outputs {socket:schuko:voltage} volt" }, "voltage-1": { "mappings": { "0": { - "then": "European wall plug with ground pin (CEE7/4 type E) outputs 230 volt" + "then": "
European wall plug with ground pin (CEE7/4 type E)
outputs 230 volt" } }, - "question": "What voltage do the plugs with European wall plug with ground pin (CEE7/4 type E) offer?", - "render": "European wall plug with ground pin (CEE7/4 type E) outputs {socket:typee:voltage} volt" + "question": "What voltage do the plugs with
European wall plug with ground pin (CEE7/4 type E)
offer?", + "render": "
European wall plug with ground pin (CEE7/4 type E)
outputs {socket:typee:voltage} volt" }, "voltage-10": { "mappings": { "0": { - "then": "Tesla Supercharger CCS (a branded type2_css) outputs 500 volt" + "then": "
Tesla Supercharger CCS (a branded type2_css)
outputs 500 volt" }, "1": { - "then": "Tesla Supercharger CCS (a branded type2_css) outputs 920 volt" + "then": "
Tesla Supercharger CCS (a branded type2_css)
outputs 920 volt" } }, - "question": "What voltage do the plugs with Tesla Supercharger CCS (a branded type2_css) offer?", - "render": "Tesla Supercharger CCS (a branded type2_css) outputs {socket:tesla_supercharger_ccs:voltage} volt" + "question": "What voltage do the plugs with
Tesla Supercharger CCS (a branded type2_css)
offer?", + "render": "
Tesla Supercharger CCS (a branded type2_css)
outputs {socket:tesla_supercharger_ccs:voltage} volt" }, "voltage-11": { "mappings": { "0": { - "then": "Tesla Supercharger (destination) outputs 480 volt" + "then": "
Tesla Supercharger (destination)
outputs 480 volt" } }, - "question": "What voltage do the plugs with Tesla Supercharger (destination) offer?", - "render": "Tesla Supercharger (destination) outputs {socket:tesla_destination:voltage} volt" + "question": "What voltage do the plugs with
Tesla Supercharger (destination)
offer?", + "render": "
Tesla Supercharger (destination)
outputs {socket:tesla_destination:voltage} volt" }, "voltage-12": { "mappings": { "0": { - "then": "Tesla supercharger (destination (A Type 2 with cable branded as tesla) outputs 230 volt" + "then": "
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
outputs 230 volt" }, "1": { - "then": "Tesla supercharger (destination (A Type 2 with cable branded as tesla) outputs 400 volt" + "then": "
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
outputs 400 volt" } }, - "question": "What voltage do the plugs with Tesla supercharger (destination (A Type 2 with cable branded as tesla) offer?", - "render": "Tesla supercharger (destination (A Type 2 with cable branded as tesla) outputs {socket:tesla_destination:voltage} volt" + "question": "What voltage do the plugs with
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
offer?", + "render": "
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
outputs {socket:tesla_destination:voltage} volt" + }, + "voltage-13": { + "mappings": { + "0": { + "then": "
USB to charge phones and small electronics
outputs 5 volt" + } + }, + "question": "What voltage do the plugs with
USB to charge phones and small electronics
offer?", + "render": "
USB to charge phones and small electronics
outputs {socket:USB-A:voltage} volt" + }, + "voltage-14": { + "question": "What voltage do the plugs with
Bosch Active Connect with cable
offer?", + "render": "
Bosch Active Connect with cable
outputs {socket:bosch_3pin:voltage} volt" }, "voltage-2": { "mappings": { "0": { - "then": "Chademo outputs 500 volt" + "then": "
Chademo
outputs 500 volt" } }, - "question": "What voltage do the plugs with Chademo offer?", - "render": "Chademo outputs {socket:chademo:voltage} volt" + "question": "What voltage do the plugs with
Chademo
offer?", + "render": "
Chademo
outputs {socket:chademo:voltage} volt" }, "voltage-3": { "mappings": { "0": { - "then": "Type 1 with cable (J1772) outputs 200 volt" + "then": "
Type 1 with cable (J1772)
outputs 200 volt" }, "1": { - "then": "Type 1 with cable (J1772) outputs 240 volt" + "then": "
Type 1 with cable (J1772)
outputs 240 volt" } }, - "question": "What voltage do the plugs with Type 1 with cable (J1772) offer?", - "render": "Type 1 with cable (J1772) outputs {socket:type1_cable:voltage} volt" + "question": "What voltage do the plugs with
Type 1 with cable (J1772)
offer?", + "render": "
Type 1 with cable (J1772)
outputs {socket:type1_cable:voltage} volt" }, "voltage-4": { "mappings": { "0": { - "then": "Type 1 without cable (J1772) outputs 200 volt" + "then": "
Type 1 without cable (J1772)
outputs 200 volt" }, "1": { - "then": "Type 1 without cable (J1772) outputs 240 volt" + "then": "
Type 1 without cable (J1772)
outputs 240 volt" } }, - "question": "What voltage do the plugs with Type 1 without cable (J1772) offer?", - "render": "Type 1 without cable (J1772) outputs {socket:type1:voltage} volt" + "question": "What voltage do the plugs with
Type 1 without cable (J1772)
offer?", + "render": "
Type 1 without cable (J1772)
outputs {socket:type1:voltage} volt" }, "voltage-5": { "mappings": { "0": { - "then": "Type 1 CCS (aka Type 1 Combo) outputs 400 volt" + "then": "
Type 1 CCS (aka Type 1 Combo)
outputs 400 volt" }, "1": { - "then": "Type 1 CCS (aka Type 1 Combo) outputs 1000 volt" + "then": "
Type 1 CCS (aka Type 1 Combo)
outputs 1000 volt" } }, - "question": "What voltage do the plugs with Type 1 CCS (aka Type 1 Combo) offer?", - "render": "Type 1 CCS (aka Type 1 Combo) outputs {socket:type1_combo:voltage} volt" + "question": "What voltage do the plugs with
Type 1 CCS (aka Type 1 Combo)
offer?", + "render": "
Type 1 CCS (aka Type 1 Combo)
outputs {socket:type1_combo:voltage} volt" }, "voltage-6": { "mappings": { "0": { - "then": "Tesla Supercharger outputs 480 volt" + "then": "
Tesla Supercharger
outputs 480 volt" } }, - "question": "What voltage do the plugs with Tesla Supercharger offer?", - "render": "Tesla Supercharger outputs {socket:tesla_supercharger:voltage} volt" + "question": "What voltage do the plugs with
Tesla Supercharger
offer?", + "render": "
Tesla Supercharger
outputs {socket:tesla_supercharger:voltage} volt" }, "voltage-7": { "mappings": { "0": { - "then": "Type 2 (mennekes) outputs 230 volt" + "then": "
Type 2 (mennekes)
outputs 230 volt" }, "1": { - "then": "Type 2 (mennekes) outputs 400 volt" + "then": "
Type 2 (mennekes)
outputs 400 volt" } }, - "question": "What voltage do the plugs with Type 2 (mennekes) offer?", - "render": "Type 2 (mennekes) outputs {socket:type2:voltage} volt" + "question": "What voltage do the plugs with
Type 2 (mennekes)
offer?", + "render": "
Type 2 (mennekes)
outputs {socket:type2:voltage} volt" }, "voltage-8": { "mappings": { "0": { - "then": "Type 2 CCS (mennekes) outputs 500 volt" + "then": "
Type 2 CCS (mennekes)
outputs 500 volt" }, "1": { - "then": "Type 2 CCS (mennekes) outputs 920 volt" + "then": "
Type 2 CCS (mennekes)
outputs 920 volt" } }, - "question": "What voltage do the plugs with Type 2 CCS (mennekes) offer?", - "render": "Type 2 CCS (mennekes) outputs {socket:type2_combo:voltage} volt" + "question": "What voltage do the plugs with
Type 2 CCS (mennekes)
offer?", + "render": "
Type 2 CCS (mennekes)
outputs {socket:type2_combo:voltage} volt" }, "voltage-9": { "mappings": { "0": { - "then": "Type 2 with cable (mennekes) outputs 230 volt" + "then": "
Type 2 with cable (mennekes)
outputs 230 volt" }, "1": { - "then": "Type 2 with cable (mennekes) outputs 400 volt" + "then": "
Type 2 with cable (mennekes)
outputs 400 volt" } }, - "question": "What voltage do the plugs with Type 2 with cable (mennekes) offer?", - "render": "Type 2 with cable (mennekes) outputs {socket:type2_cable:voltage} volt" + "question": "What voltage do the plugs with
Type 2 with cable (mennekes)
offer?", + "render": "
Type 2 with cable (mennekes)
outputs {socket:type2_cable:voltage} volt" }, "website": { "question": "What is the website of the operator?", @@ -2375,7 +2437,7 @@ "name": "Drinking water", "presets": { "0": { - "title": "Drinking water" + "title": "drinking water" } }, "tagRenderings": { @@ -2587,7 +2649,7 @@ "name": "Information boards", "presets": { "0": { - "title": "Information board" + "title": "information board" } }, "title": { @@ -2733,7 +2795,7 @@ "name": "Picnic tables", "presets": { "0": { - "title": "Picnic table" + "title": "picnic table" } }, "tagRenderings": { @@ -2884,6 +2946,15 @@ }, "public_bookcase": { "description": "A streetside cabinet with books, accessible to anyone", + "filter": { + "2": { + "options": { + "0": { + "question": "Indoor or outdoor" + } + } + } + }, "name": "Bookcases", "presets": { "0": { @@ -3236,15 +3307,38 @@ } }, "toilet": { + "filter": { + "0": { + "options": { + "0": { + "question": "Wheelchair accessible" + } + } + }, + "1": { + "options": { + "0": { + "question": "Has a changing table" + } + } + }, + "2": { + "options": { + "0": { + "question": "Free to use" + } + } + } + }, "name": "Toilets", "presets": { "0": { "description": "A publicly accessible toilet or restroom", - "title": "Toilet" + "title": "toilet" }, "1": { "description": "A restroom which has at least one wheelchair-accessible toilet", - "title": "Toilets with wheelchair accessible toilet" + "title": "toilets with wheelchair accessible toilet" } }, "tagRenderings": { @@ -3291,6 +3385,28 @@ "question": "How much does one have to pay for these toilets?", "render": "The fee is {charge}" }, + "toilet-handwashing": { + "mappings": { + "0": { + "then": "This toilets have a sink to wash your hands" + }, + "1": { + "then": "This toilets don't have a sink to wash your hands" + } + }, + "question": "Do these toilets have a sink to wash your hands?" + }, + "toilet-has-paper": { + "mappings": { + "0": { + "then": "Toilet paper is equipped with toilet paper" + }, + "1": { + "then": "You have to bring your own toilet paper to this toilet" + } + }, + "question": "Does one have to bring their own toilet paper to this toilet?" + }, "toilets-changing-table": { "mappings": { "0": { diff --git a/langs/layers/es.json b/langs/layers/es.json index 9746ea958c..44e99ca4cd 100644 --- a/langs/layers/es.json +++ b/langs/layers/es.json @@ -3,8 +3,7 @@ "name": "Bancos", "presets": { "0": { - "description": "Añadir un nuevo banco", - "title": "Banco" + "title": "banco" } }, "tagRenderings": { diff --git a/langs/layers/fi.json b/langs/layers/fi.json index abe9d2b7f3..a13231cd6f 100644 --- a/langs/layers/fi.json +++ b/langs/layers/fi.json @@ -3,8 +3,7 @@ "name": "Penkit", "presets": { "0": { - "description": "Lisää uusi penkki", - "title": "Penkki" + "title": "penkki" } }, "tagRenderings": { diff --git a/langs/layers/fr.json b/langs/layers/fr.json index 009a50fc0b..52c61d899c 100644 --- a/langs/layers/fr.json +++ b/langs/layers/fr.json @@ -3,8 +3,7 @@ "name": "Bancs", "presets": { "0": { - "description": "Ajouter un nouveau banc", - "title": "Banc" + "title": "banc" } }, "tagRenderings": { @@ -810,7 +809,7 @@ "name": "Eau potable", "presets": { "0": { - "title": "Eau potable" + "title": "eau potable" } }, "tagRenderings": { @@ -951,7 +950,7 @@ "name": "Panneaux d'informations", "presets": { "0": { - "title": "Panneau d'informations" + "title": "panneau d'informations" } }, "title": { @@ -1043,7 +1042,7 @@ "name": "Tables de pique-nique", "presets": { "0": { - "title": "Table de pique-nique" + "title": "table de pique-nique" } }, "tagRenderings": { @@ -1550,11 +1549,11 @@ "presets": { "0": { "description": "Des toilettes", - "title": "Toilettes" + "title": "toilettes" }, "1": { "description": "Toilettes avec au moins un WC accessible aux personnes à mobilité réduite", - "title": "Toilettes accessible aux personnes à mobilité réduite" + "title": "toilettes accessible aux personnes à mobilité réduite" } }, "tagRenderings": { diff --git a/langs/layers/gl.json b/langs/layers/gl.json index 44f1a21bab..aa261176f8 100644 --- a/langs/layers/gl.json +++ b/langs/layers/gl.json @@ -372,7 +372,7 @@ "name": "Auga potábel", "presets": { "0": { - "title": "Auga potábel" + "title": "auga potábel" } }, "title": { diff --git a/langs/layers/hu.json b/langs/layers/hu.json index 2842a2d33f..5fb6aa09b0 100644 --- a/langs/layers/hu.json +++ b/langs/layers/hu.json @@ -1,11 +1,6 @@ { "bench": { "name": "Padok", - "presets": { - "0": { - "description": "Pad hozzáadása" - } - }, "tagRenderings": { "bench-backrest": { "mappings": { diff --git a/langs/layers/id.json b/langs/layers/id.json index 7aacdc7aaa..54f3e27c2b 100644 --- a/langs/layers/id.json +++ b/langs/layers/id.json @@ -3,7 +3,7 @@ "name": "Bangku", "presets": { "0": { - "title": "Bangku" + "title": "bangku" } }, "tagRenderings": { @@ -65,7 +65,7 @@ "name": "Air minum", "presets": { "0": { - "title": "Air minum" + "title": "air minum" } }, "title": { diff --git a/langs/layers/it.json b/langs/layers/it.json index d5e8c40508..b8585841fe 100644 --- a/langs/layers/it.json +++ b/langs/layers/it.json @@ -3,8 +3,7 @@ "name": "Panchine", "presets": { "0": { - "description": "Aggiungi una nuova panchina", - "title": "Panchina" + "title": "panchina" } }, "tagRenderings": { @@ -826,7 +825,7 @@ "name": "Acqua potabile", "presets": { "0": { - "title": "Acqua potabile" + "title": "acqua potabile" } }, "tagRenderings": { @@ -910,7 +909,7 @@ "name": "Pannelli informativi", "presets": { "0": { - "title": "Pannello informativo" + "title": "pannello informativo" } }, "title": { @@ -1002,7 +1001,7 @@ "name": "Tavoli da picnic", "presets": { "0": { - "title": "Tavolo da picnic" + "title": "tavolo da picnic" } }, "tagRenderings": { @@ -1509,11 +1508,11 @@ "presets": { "0": { "description": "Servizi igienici aperti al pubblico", - "title": "Servizi igienici" + "title": "servizi igienici" }, "1": { "description": "Servizi igienici che hanno almeno una toilette accessibile a persone in sedia a rotelle", - "title": "Servizi igienici accessibili per persone in sedia a rotelle" + "title": "servizi igienici accessibili per persone in sedia a rotelle" } }, "tagRenderings": { diff --git a/langs/layers/nb_NO.json b/langs/layers/nb_NO.json index e22f109440..c515216b93 100644 --- a/langs/layers/nb_NO.json +++ b/langs/layers/nb_NO.json @@ -3,8 +3,7 @@ "name": "Benker", "presets": { "0": { - "description": "Legg til en ny benk", - "title": "Benk" + "title": "benk" } }, "tagRenderings": { diff --git a/langs/layers/nl.json b/langs/layers/nl.json index 8e6df7a081..0ff5387f3f 100644 --- a/langs/layers/nl.json +++ b/langs/layers/nl.json @@ -93,8 +93,7 @@ "name": "Zitbanken", "presets": { "0": { - "description": "Voeg een nieuwe zitbank toe", - "title": "Zitbank" + "title": "zitbank" } }, "tagRenderings": { @@ -828,11 +827,11 @@ "presets": { "0": { "description": "Een overdekte hut waarbinnen er warm en droog naar vogels gekeken kan worden", - "title": "Vogelkijkhut" + "title": "vogelkijkhut" }, "1": { "description": "Een vogelkijkwand waarachter men kan staan om vogels te kijken", - "title": "Vogelkijkwand" + "title": "vogelkijkwand" } }, "size": { @@ -985,43 +984,49 @@ "question": "Alle types" }, "1": { - "question": "Heeft een Schuko stekker zonder aardingspin (CEE7/4 type F) " + "question": "Heeft een
Schuko stekker zonder aardingspin (CEE7/4 type F)
" }, "2": { - "question": "Heeft een Europese stekker met aardingspin (CEE7/4 type E) " + "question": "Heeft een
Europese stekker met aardingspin (CEE7/4 type E)
" }, "3": { - "question": "Heeft een " + "question": "Heeft een
Chademo
" }, "4": { - "question": "Heeft een Type 1 met kabel (J1772) " + "question": "Heeft een
Type 1 met kabel (J1772)
" }, "5": { - "question": "Heeft een Type 1 zonder kabel (J1772) " + "question": "Heeft een
Type 1 zonder kabel (J1772)
" }, "6": { - "question": "Heeft een " + "question": "Heeft een
Type 1 CCS (ook gekend als Type 1 Combo)
" }, "7": { - "question": "Heeft een " + "question": "Heeft een
Tesla Supercharger
" }, "8": { - "question": "Heeft een " + "question": "Heeft een
Type 2 (mennekes)
" }, "9": { - "question": "Heeft een " + "question": "Heeft een
Type 2 CCS (mennekes)
" }, "10": { - "question": "Heeft een Type 2 met kabel (J1772) " + "question": "Heeft een
Type 2 met kabel (J1772)
" }, "11": { - "question": "Heeft een " + "question": "Heeft een
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
" }, "12": { - "question": "Heeft een " + "question": "Heeft een
Tesla Supercharger (destination)
" }, "13": { - "question": "Heeft een " + "question": "Heeft een
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
" + }, + "14": { + "question": "Heeft een
USB om GSMs en kleine electronica op te laden
" + }, + "15": { + "question": "Heeft een
Bosch Active Connect aan een kabel
" } } } @@ -1030,82 +1035,94 @@ "Available_charging_stations (generated)": { "mappings": { "0": { - "then": " Schuko stekker zonder aardingspin (CEE7/4 type F)" + "then": "
Schuko stekker zonder aardingspin (CEE7/4 type F)
" }, "1": { - "then": " Schuko stekker zonder aardingspin (CEE7/4 type F)" + "then": "
Schuko stekker zonder aardingspin (CEE7/4 type F)
" }, "2": { - "then": " Europese stekker met aardingspin (CEE7/4 type E)" + "then": "
Europese stekker met aardingspin (CEE7/4 type E)
" }, "3": { - "then": " Europese stekker met aardingspin (CEE7/4 type E)" + "then": "
Europese stekker met aardingspin (CEE7/4 type E)
" }, "4": { - "then": " " + "then": "
Chademo
" }, "5": { - "then": " " + "then": "
Chademo
" }, "6": { - "then": " Type 1 met kabel (J1772)" + "then": "
Type 1 met kabel (J1772)
" }, "7": { - "then": " Type 1 met kabel (J1772)" + "then": "
Type 1 met kabel (J1772)
" }, "8": { - "then": " Type 1 zonder kabel (J1772)" + "then": "
Type 1 zonder kabel (J1772)
" }, "9": { - "then": " Type 1 zonder kabel (J1772)" + "then": "
Type 1 zonder kabel (J1772)
" }, "10": { - "then": " " + "then": "
Type 1 CCS (ook gekend als Type 1 Combo)
" }, "11": { - "then": " " + "then": "
Type 1 CCS (ook gekend als Type 1 Combo)
" }, "12": { - "then": " " + "then": "
Tesla Supercharger
" }, "13": { - "then": " " + "then": "
Tesla Supercharger
" }, "14": { - "then": " " + "then": "
Type 2 (mennekes)
" }, "15": { - "then": " " + "then": "
Type 2 (mennekes)
" }, "16": { - "then": " " + "then": "
Type 2 CCS (mennekes)
" }, "17": { - "then": " " + "then": "
Type 2 CCS (mennekes)
" }, "18": { - "then": " Type 2 met kabel (J1772)" + "then": "
Type 2 met kabel (J1772)
" }, "19": { - "then": " Type 2 met kabel (J1772)" + "then": "
Type 2 met kabel (J1772)
" }, "20": { - "then": " " + "then": "
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
" }, "21": { - "then": " " + "then": "
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
" }, "22": { - "then": " " + "then": "
Tesla Supercharger (destination)
" }, "23": { - "then": " " + "then": "
Tesla Supercharger (destination)
" }, "24": { - "then": " " + "then": "
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
" }, "25": { - "then": " " + "then": "
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
" + }, + "26": { + "then": "
USB om GSMs en kleine electronica op te laden
" + }, + "27": { + "then": "
USB om GSMs en kleine electronica op te laden
" + }, + "28": { + "then": "
Bosch Active Connect aan een kabel
" + }, + "29": { + "then": "
Bosch Active Connect aan een kabel
" } } }, @@ -1136,143 +1153,159 @@ "current-0": { "mappings": { "0": { - "then": "Schuko stekker zonder aardingspin (CEE7/4 type F) levert een stroom van maximaal 16 A" + "then": "
Schuko stekker zonder aardingspin (CEE7/4 type F)
levert een stroom van maximaal 16 A" } }, - "question": "Welke stroom levert de stekker van type Schuko stekker zonder aardingspin (CEE7/4 type F) ?", - "render": "Schuko stekker zonder aardingspin (CEE7/4 type F) levert een stroom van maximaal {socket:schuko:current}A" + "question": "Welke stroom levert de stekker van type
Schuko stekker zonder aardingspin (CEE7/4 type F)
?", + "render": "
Schuko stekker zonder aardingspin (CEE7/4 type F)
levert een stroom van maximaal {socket:schuko:current}A" }, "current-1": { "mappings": { "0": { - "then": "Europese stekker met aardingspin (CEE7/4 type E) levert een stroom van maximaal 16 A" + "then": "
Europese stekker met aardingspin (CEE7/4 type E)
levert een stroom van maximaal 16 A" } }, - "question": "Welke stroom levert de stekker van type Europese stekker met aardingspin (CEE7/4 type E) ?", - "render": "Europese stekker met aardingspin (CEE7/4 type E) levert een stroom van maximaal {socket:typee:current}A" + "question": "Welke stroom levert de stekker van type
Europese stekker met aardingspin (CEE7/4 type E)
?", + "render": "
Europese stekker met aardingspin (CEE7/4 type E)
levert een stroom van maximaal {socket:typee:current}A" }, "current-10": { "mappings": { "0": { - "then": " levert een stroom van maximaal 125 A" + "then": "
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
levert een stroom van maximaal 125 A" }, "1": { - "then": " levert een stroom van maximaal 350 A" + "then": "
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
levert een stroom van maximaal 350 A" } }, - "question": "Welke stroom levert de stekker van type ?", - "render": " levert een stroom van maximaal {socket:tesla_supercharger_ccs:current}A" + "question": "Welke stroom levert de stekker van type
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
?", + "render": "
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
levert een stroom van maximaal {socket:tesla_supercharger_ccs:current}A" }, "current-11": { "mappings": { "0": { - "then": " levert een stroom van maximaal 125 A" + "then": "
Tesla Supercharger (destination)
levert een stroom van maximaal 125 A" }, "1": { - "then": " levert een stroom van maximaal 350 A" + "then": "
Tesla Supercharger (destination)
levert een stroom van maximaal 350 A" } }, - "question": "Welke stroom levert de stekker van type ?", - "render": " levert een stroom van maximaal {socket:tesla_destination:current}A" + "question": "Welke stroom levert de stekker van type
Tesla Supercharger (destination)
?", + "render": "
Tesla Supercharger (destination)
levert een stroom van maximaal {socket:tesla_destination:current}A" }, "current-12": { "mappings": { "0": { - "then": " levert een stroom van maximaal 16 A" + "then": "
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
levert een stroom van maximaal 16 A" }, "1": { - "then": " levert een stroom van maximaal 32 A" + "then": "
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
levert een stroom van maximaal 32 A" } }, - "question": "Welke stroom levert de stekker van type ?", - "render": " levert een stroom van maximaal {socket:tesla_destination:current}A" + "question": "Welke stroom levert de stekker van type
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
?", + "render": "
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
levert een stroom van maximaal {socket:tesla_destination:current}A" + }, + "current-13": { + "mappings": { + "0": { + "then": "
USB om GSMs en kleine electronica op te laden
levert een stroom van maximaal 1 A" + }, + "1": { + "then": "
USB om GSMs en kleine electronica op te laden
levert een stroom van maximaal 2 A" + } + }, + "question": "Welke stroom levert de stekker van type
USB om GSMs en kleine electronica op te laden
?", + "render": "
USB om GSMs en kleine electronica op te laden
levert een stroom van maximaal {socket:USB-A:current}A" + }, + "current-14": { + "question": "Welke stroom levert de stekker van type
Bosch Active Connect aan een kabel
?", + "render": "
Bosch Active Connect aan een kabel
levert een stroom van maximaal {socket:bosch_3pin:current}A" }, "current-2": { "mappings": { "0": { - "then": " levert een stroom van maximaal 120 A" + "then": "
Chademo
levert een stroom van maximaal 120 A" } }, - "question": "Welke stroom levert de stekker van type ?", - "render": " levert een stroom van maximaal {socket:chademo:current}A" + "question": "Welke stroom levert de stekker van type
Chademo
?", + "render": "
Chademo
levert een stroom van maximaal {socket:chademo:current}A" }, "current-3": { "mappings": { "0": { - "then": "Type 1 met kabel (J1772) levert een stroom van maximaal 32 A" + "then": "
Type 1 met kabel (J1772)
levert een stroom van maximaal 32 A" } }, - "question": "Welke stroom levert de stekker van type Type 1 met kabel (J1772) ?", - "render": "Type 1 met kabel (J1772) levert een stroom van maximaal {socket:type1_cable:current}A" + "question": "Welke stroom levert de stekker van type
Type 1 met kabel (J1772)
?", + "render": "
Type 1 met kabel (J1772)
levert een stroom van maximaal {socket:type1_cable:current}A" }, "current-4": { "mappings": { "0": { - "then": "Type 1 zonder kabel (J1772) levert een stroom van maximaal 32 A" + "then": "
Type 1 zonder kabel (J1772)
levert een stroom van maximaal 32 A" } }, - "question": "Welke stroom levert de stekker van type Type 1 zonder kabel (J1772) ?", - "render": "Type 1 zonder kabel (J1772) levert een stroom van maximaal {socket:type1:current}A" + "question": "Welke stroom levert de stekker van type
Type 1 zonder kabel (J1772)
?", + "render": "
Type 1 zonder kabel (J1772)
levert een stroom van maximaal {socket:type1:current}A" }, "current-5": { "mappings": { "0": { - "then": " levert een stroom van maximaal 50 A" + "then": "
Type 1 CCS (ook gekend als Type 1 Combo)
levert een stroom van maximaal 50 A" }, "1": { - "then": " levert een stroom van maximaal 125 A" + "then": "
Type 1 CCS (ook gekend als Type 1 Combo)
levert een stroom van maximaal 125 A" } }, - "question": "Welke stroom levert de stekker van type ?", - "render": " levert een stroom van maximaal {socket:type1_combo:current}A" + "question": "Welke stroom levert de stekker van type
Type 1 CCS (ook gekend als Type 1 Combo)
?", + "render": "
Type 1 CCS (ook gekend als Type 1 Combo)
levert een stroom van maximaal {socket:type1_combo:current}A" }, "current-6": { "mappings": { "0": { - "then": " levert een stroom van maximaal 125 A" + "then": "
Tesla Supercharger
levert een stroom van maximaal 125 A" }, "1": { - "then": " levert een stroom van maximaal 350 A" + "then": "
Tesla Supercharger
levert een stroom van maximaal 350 A" } }, - "question": "Welke stroom levert de stekker van type ?", - "render": " levert een stroom van maximaal {socket:tesla_supercharger:current}A" + "question": "Welke stroom levert de stekker van type
Tesla Supercharger
?", + "render": "
Tesla Supercharger
levert een stroom van maximaal {socket:tesla_supercharger:current}A" }, "current-7": { "mappings": { "0": { - "then": " levert een stroom van maximaal 16 A" + "then": "
Type 2 (mennekes)
levert een stroom van maximaal 16 A" }, "1": { - "then": " levert een stroom van maximaal 32 A" + "then": "
Type 2 (mennekes)
levert een stroom van maximaal 32 A" } }, - "question": "Welke stroom levert de stekker van type ?", - "render": " levert een stroom van maximaal {socket:type2:current}A" + "question": "Welke stroom levert de stekker van type
Type 2 (mennekes)
?", + "render": "
Type 2 (mennekes)
levert een stroom van maximaal {socket:type2:current}A" }, "current-8": { "mappings": { "0": { - "then": " levert een stroom van maximaal 125 A" + "then": "
Type 2 CCS (mennekes)
levert een stroom van maximaal 125 A" }, "1": { - "then": " levert een stroom van maximaal 350 A" + "then": "
Type 2 CCS (mennekes)
levert een stroom van maximaal 350 A" } }, - "question": "Welke stroom levert de stekker van type ?", - "render": " levert een stroom van maximaal {socket:type2_combo:current}A" + "question": "Welke stroom levert de stekker van type
Type 2 CCS (mennekes)
?", + "render": "
Type 2 CCS (mennekes)
levert een stroom van maximaal {socket:type2_combo:current}A" }, "current-9": { "mappings": { "0": { - "then": "Type 2 met kabel (J1772) levert een stroom van maximaal 16 A" + "then": "
Type 2 met kabel (J1772)
levert een stroom van maximaal 16 A" }, "1": { - "then": "Type 2 met kabel (J1772) levert een stroom van maximaal 32 A" + "then": "
Type 2 met kabel (J1772)
levert een stroom van maximaal 32 A" } }, - "question": "Welke stroom levert de stekker van type Type 2 met kabel (J1772) ?", - "render": "Type 2 met kabel (J1772) levert een stroom van maximaal {socket:type2_cable:current}A" + "question": "Welke stroom levert de stekker van type
Type 2 met kabel (J1772)
?", + "render": "
Type 2 met kabel (J1772)
levert een stroom van maximaal {socket:type2_cable:current}A" }, "fee/charge": { "mappings": { @@ -1294,14 +1327,6 @@ }, "payment-options": { "override": { - "mappings": { - "0": { - "then": "Betalen via een app van het netwerk" - }, - "1": { - "then": "Betalen via een lidkaart van het netwerk" - } - }, "mappings+": { "0": { "then": "Betalen via een app van het netwerk" @@ -1313,359 +1338,396 @@ } }, "plugs-0": { - "question": "Hoeveel stekkers van type Schuko stekker zonder aardingspin (CEE7/4 type F) heeft dit oplaadpunt?", - "render": "Hier zijn Schuko stekker zonder aardingspin (CEE7/4 type F) stekkers van het type Schuko stekker zonder aardingspin (CEE7/4 type F)" + "question": "Hoeveel stekkers van type
Schuko stekker zonder aardingspin (CEE7/4 type F)
heeft dit oplaadpunt?", + "render": "Hier zijn {socket:schuko} stekkers van het type
Schuko stekker zonder aardingspin (CEE7/4 type F)
" }, "plugs-1": { - "question": "Hoeveel stekkers van type Europese stekker met aardingspin (CEE7/4 type E) heeft dit oplaadpunt?", - "render": "Hier zijn Europese stekker met aardingspin (CEE7/4 type E) stekkers van het type Europese stekker met aardingspin (CEE7/4 type E)" + "question": "Hoeveel stekkers van type
Europese stekker met aardingspin (CEE7/4 type E)
heeft dit oplaadpunt?", + "render": "Hier zijn {socket:typee} stekkers van het type
Europese stekker met aardingspin (CEE7/4 type E)
" }, "plugs-10": { - "question": "Hoeveel stekkers van type heeft dit oplaadpunt?", - "render": "Hier zijn stekkers van het type " + "question": "Hoeveel stekkers van type
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
heeft dit oplaadpunt?", + "render": "Hier zijn {socket:tesla_supercharger_ccs} stekkers van het type
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
" }, "plugs-11": { - "question": "Hoeveel stekkers van type heeft dit oplaadpunt?", - "render": "Hier zijn stekkers van het type " + "question": "Hoeveel stekkers van type
Tesla Supercharger (destination)
heeft dit oplaadpunt?", + "render": "Hier zijn {socket:tesla_destination} stekkers van het type
Tesla Supercharger (destination)
" }, "plugs-12": { - "question": "Hoeveel stekkers van type heeft dit oplaadpunt?", - "render": "Hier zijn stekkers van het type " + "question": "Hoeveel stekkers van type
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
heeft dit oplaadpunt?", + "render": "Hier zijn {socket:tesla_destination} stekkers van het type
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
" + }, + "plugs-13": { + "question": "Hoeveel stekkers van type
USB om GSMs en kleine electronica op te laden
heeft dit oplaadpunt?", + "render": "Hier zijn {socket:USB-A} stekkers van het type
USB om GSMs en kleine electronica op te laden
" + }, + "plugs-14": { + "question": "Hoeveel stekkers van type
Bosch Active Connect aan een kabel
heeft dit oplaadpunt?", + "render": "Hier zijn {socket:bosch_3pin} stekkers van het type
Bosch Active Connect aan een kabel
" }, "plugs-2": { - "question": "Hoeveel stekkers van type heeft dit oplaadpunt?", - "render": "Hier zijn stekkers van het type " + "question": "Hoeveel stekkers van type
Chademo
heeft dit oplaadpunt?", + "render": "Hier zijn {socket:chademo} stekkers van het type
Chademo
" }, "plugs-3": { - "question": "Hoeveel stekkers van type Type 1 met kabel (J1772) heeft dit oplaadpunt?", - "render": "Hier zijn Type 1 met kabel (J1772) stekkers van het type Type 1 met kabel (J1772)" + "question": "Hoeveel stekkers van type
Type 1 met kabel (J1772)
heeft dit oplaadpunt?", + "render": "Hier zijn {socket:type1_cable} stekkers van het type
Type 1 met kabel (J1772)
" }, "plugs-4": { - "question": "Hoeveel stekkers van type Type 1 zonder kabel (J1772) heeft dit oplaadpunt?", - "render": "Hier zijn Type 1 zonder kabel (J1772) stekkers van het type Type 1 zonder kabel (J1772)" + "question": "Hoeveel stekkers van type
Type 1 zonder kabel (J1772)
heeft dit oplaadpunt?", + "render": "Hier zijn {socket:type1} stekkers van het type
Type 1 zonder kabel (J1772)
" }, "plugs-5": { - "question": "Hoeveel stekkers van type heeft dit oplaadpunt?", - "render": "Hier zijn stekkers van het type " + "question": "Hoeveel stekkers van type
Type 1 CCS (ook gekend als Type 1 Combo)
heeft dit oplaadpunt?", + "render": "Hier zijn {socket:type1_combo} stekkers van het type
Type 1 CCS (ook gekend als Type 1 Combo)
" }, "plugs-6": { - "question": "Hoeveel stekkers van type heeft dit oplaadpunt?", - "render": "Hier zijn stekkers van het type " + "question": "Hoeveel stekkers van type
Tesla Supercharger
heeft dit oplaadpunt?", + "render": "Hier zijn {socket:tesla_supercharger} stekkers van het type
Tesla Supercharger
" }, "plugs-7": { - "question": "Hoeveel stekkers van type heeft dit oplaadpunt?", - "render": "Hier zijn stekkers van het type " + "question": "Hoeveel stekkers van type
Type 2 (mennekes)
heeft dit oplaadpunt?", + "render": "Hier zijn {socket:type2} stekkers van het type
Type 2 (mennekes)
" }, "plugs-8": { - "question": "Hoeveel stekkers van type heeft dit oplaadpunt?", - "render": "Hier zijn stekkers van het type " + "question": "Hoeveel stekkers van type
Type 2 CCS (mennekes)
heeft dit oplaadpunt?", + "render": "Hier zijn {socket:type2_combo} stekkers van het type
Type 2 CCS (mennekes)
" }, "plugs-9": { - "question": "Hoeveel stekkers van type Type 2 met kabel (J1772) heeft dit oplaadpunt?", - "render": "Hier zijn Type 2 met kabel (J1772) stekkers van het type Type 2 met kabel (J1772)" + "question": "Hoeveel stekkers van type
Type 2 met kabel (J1772)
heeft dit oplaadpunt?", + "render": "Hier zijn {socket:type2_cable} stekkers van het type
Type 2 met kabel (J1772)
" }, "power-output-0": { "mappings": { "0": { - "then": "Schuko stekker zonder aardingspin (CEE7/4 type F) levert een vermogen van maximaal 3.6 kw" + "then": "
Schuko stekker zonder aardingspin (CEE7/4 type F)
levert een vermogen van maximaal 3.6 kw" } }, - "question": "Welk vermogen levert een enkele stekker van type Schuko stekker zonder aardingspin (CEE7/4 type F) ?", - "render": "Schuko stekker zonder aardingspin (CEE7/4 type F) levert een vermogen van maximaal {socket:schuko:output}" + "question": "Welk vermogen levert een enkele stekker van type
Schuko stekker zonder aardingspin (CEE7/4 type F)
?", + "render": "
Schuko stekker zonder aardingspin (CEE7/4 type F)
levert een vermogen van maximaal {socket:schuko:output}" }, "power-output-1": { "mappings": { "0": { - "then": "Europese stekker met aardingspin (CEE7/4 type E) levert een vermogen van maximaal 3 kw" + "then": "
Europese stekker met aardingspin (CEE7/4 type E)
levert een vermogen van maximaal 3 kw" }, "1": { - "then": "Europese stekker met aardingspin (CEE7/4 type E) levert een vermogen van maximaal 22 kw" + "then": "
Europese stekker met aardingspin (CEE7/4 type E)
levert een vermogen van maximaal 22 kw" } }, - "question": "Welk vermogen levert een enkele stekker van type Europese stekker met aardingspin (CEE7/4 type E) ?", - "render": "Europese stekker met aardingspin (CEE7/4 type E) levert een vermogen van maximaal {socket:typee:output}" + "question": "Welk vermogen levert een enkele stekker van type
Europese stekker met aardingspin (CEE7/4 type E)
?", + "render": "
Europese stekker met aardingspin (CEE7/4 type E)
levert een vermogen van maximaal {socket:typee:output}" }, "power-output-10": { "mappings": { "0": { - "then": " levert een vermogen van maximaal 50 kw" + "then": "
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
levert een vermogen van maximaal 50 kw" } }, - "question": "Welk vermogen levert een enkele stekker van type ?", - "render": " levert een vermogen van maximaal {socket:tesla_supercharger_ccs:output}" + "question": "Welk vermogen levert een enkele stekker van type
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
?", + "render": "
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
levert een vermogen van maximaal {socket:tesla_supercharger_ccs:output}" }, "power-output-11": { "mappings": { "0": { - "then": " levert een vermogen van maximaal 120 kw" + "then": "
Tesla Supercharger (destination)
levert een vermogen van maximaal 120 kw" }, "1": { - "then": " levert een vermogen van maximaal 150 kw" + "then": "
Tesla Supercharger (destination)
levert een vermogen van maximaal 150 kw" }, "2": { - "then": " levert een vermogen van maximaal 250 kw" + "then": "
Tesla Supercharger (destination)
levert een vermogen van maximaal 250 kw" } }, - "question": "Welk vermogen levert een enkele stekker van type ?", - "render": " levert een vermogen van maximaal {socket:tesla_destination:output}" + "question": "Welk vermogen levert een enkele stekker van type
Tesla Supercharger (destination)
?", + "render": "
Tesla Supercharger (destination)
levert een vermogen van maximaal {socket:tesla_destination:output}" }, "power-output-12": { "mappings": { "0": { - "then": " levert een vermogen van maximaal 11 kw" + "then": "
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
levert een vermogen van maximaal 11 kw" }, "1": { - "then": " levert een vermogen van maximaal 22 kw" + "then": "
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
levert een vermogen van maximaal 22 kw" } }, - "question": "Welk vermogen levert een enkele stekker van type ?", - "render": " levert een vermogen van maximaal {socket:tesla_destination:output}" + "question": "Welk vermogen levert een enkele stekker van type
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
?", + "render": "
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
levert een vermogen van maximaal {socket:tesla_destination:output}" + }, + "power-output-13": { + "mappings": { + "0": { + "then": "
USB om GSMs en kleine electronica op te laden
levert een vermogen van maximaal 5w" + }, + "1": { + "then": "
USB om GSMs en kleine electronica op te laden
levert een vermogen van maximaal 10w" + } + }, + "question": "Welk vermogen levert een enkele stekker van type
USB om GSMs en kleine electronica op te laden
?", + "render": "
USB om GSMs en kleine electronica op te laden
levert een vermogen van maximaal {socket:USB-A:output}" + }, + "power-output-14": { + "question": "Welk vermogen levert een enkele stekker van type
Bosch Active Connect aan een kabel
?", + "render": "
Bosch Active Connect aan een kabel
levert een vermogen van maximaal {socket:bosch_3pin:output}" }, "power-output-2": { "mappings": { "0": { - "then": " levert een vermogen van maximaal 50 kw" + "then": "
Chademo
levert een vermogen van maximaal 50 kw" } }, - "question": "Welk vermogen levert een enkele stekker van type ?", - "render": " levert een vermogen van maximaal {socket:chademo:output}" + "question": "Welk vermogen levert een enkele stekker van type
Chademo
?", + "render": "
Chademo
levert een vermogen van maximaal {socket:chademo:output}" }, "power-output-3": { "mappings": { "0": { - "then": "Type 1 met kabel (J1772) levert een vermogen van maximaal 3.7 kw" + "then": "
Type 1 met kabel (J1772)
levert een vermogen van maximaal 3.7 kw" }, "1": { - "then": "Type 1 met kabel (J1772) levert een vermogen van maximaal 7 kw" + "then": "
Type 1 met kabel (J1772)
levert een vermogen van maximaal 7 kw" } }, - "question": "Welk vermogen levert een enkele stekker van type Type 1 met kabel (J1772) ?", - "render": "Type 1 met kabel (J1772) levert een vermogen van maximaal {socket:type1_cable:output}" + "question": "Welk vermogen levert een enkele stekker van type
Type 1 met kabel (J1772)
?", + "render": "
Type 1 met kabel (J1772)
levert een vermogen van maximaal {socket:type1_cable:output}" }, "power-output-4": { "mappings": { "0": { - "then": "Type 1 zonder kabel (J1772) levert een vermogen van maximaal 3.7 kw" + "then": "
Type 1 zonder kabel (J1772)
levert een vermogen van maximaal 3.7 kw" }, "1": { - "then": "Type 1 zonder kabel (J1772) levert een vermogen van maximaal 6.6 kw" + "then": "
Type 1 zonder kabel (J1772)
levert een vermogen van maximaal 6.6 kw" }, "2": { - "then": "Type 1 zonder kabel (J1772) levert een vermogen van maximaal 7 kw" + "then": "
Type 1 zonder kabel (J1772)
levert een vermogen van maximaal 7 kw" }, "3": { - "then": "Type 1 zonder kabel (J1772) levert een vermogen van maximaal 7.2 kw" + "then": "
Type 1 zonder kabel (J1772)
levert een vermogen van maximaal 7.2 kw" } }, - "question": "Welk vermogen levert een enkele stekker van type Type 1 zonder kabel (J1772) ?", - "render": "Type 1 zonder kabel (J1772) levert een vermogen van maximaal {socket:type1:output}" + "question": "Welk vermogen levert een enkele stekker van type
Type 1 zonder kabel (J1772)
?", + "render": "
Type 1 zonder kabel (J1772)
levert een vermogen van maximaal {socket:type1:output}" }, "power-output-5": { "mappings": { "0": { - "then": " levert een vermogen van maximaal 50 kw" + "then": "
Type 1 CCS (ook gekend als Type 1 Combo)
levert een vermogen van maximaal 50 kw" }, "1": { - "then": " levert een vermogen van maximaal 62.5 kw" + "then": "
Type 1 CCS (ook gekend als Type 1 Combo)
levert een vermogen van maximaal 62.5 kw" }, "2": { - "then": " levert een vermogen van maximaal 150 kw" + "then": "
Type 1 CCS (ook gekend als Type 1 Combo)
levert een vermogen van maximaal 150 kw" }, "3": { - "then": " levert een vermogen van maximaal 350 kw" + "then": "
Type 1 CCS (ook gekend als Type 1 Combo)
levert een vermogen van maximaal 350 kw" } }, - "question": "Welk vermogen levert een enkele stekker van type ?", - "render": " levert een vermogen van maximaal {socket:type1_combo:output}" + "question": "Welk vermogen levert een enkele stekker van type
Type 1 CCS (ook gekend als Type 1 Combo)
?", + "render": "
Type 1 CCS (ook gekend als Type 1 Combo)
levert een vermogen van maximaal {socket:type1_combo:output}" }, "power-output-6": { "mappings": { "0": { - "then": " levert een vermogen van maximaal 120 kw" + "then": "
Tesla Supercharger
levert een vermogen van maximaal 120 kw" }, "1": { - "then": " levert een vermogen van maximaal 150 kw" + "then": "
Tesla Supercharger
levert een vermogen van maximaal 150 kw" }, "2": { - "then": " levert een vermogen van maximaal 250 kw" + "then": "
Tesla Supercharger
levert een vermogen van maximaal 250 kw" } }, - "question": "Welk vermogen levert een enkele stekker van type ?", - "render": " levert een vermogen van maximaal {socket:tesla_supercharger:output}" + "question": "Welk vermogen levert een enkele stekker van type
Tesla Supercharger
?", + "render": "
Tesla Supercharger
levert een vermogen van maximaal {socket:tesla_supercharger:output}" }, "power-output-7": { "mappings": { "0": { - "then": " levert een vermogen van maximaal 11 kw" + "then": "
Type 2 (mennekes)
levert een vermogen van maximaal 11 kw" }, "1": { - "then": " levert een vermogen van maximaal 22 kw" + "then": "
Type 2 (mennekes)
levert een vermogen van maximaal 22 kw" } }, - "question": "Welk vermogen levert een enkele stekker van type ?", - "render": " levert een vermogen van maximaal {socket:type2:output}" + "question": "Welk vermogen levert een enkele stekker van type
Type 2 (mennekes)
?", + "render": "
Type 2 (mennekes)
levert een vermogen van maximaal {socket:type2:output}" }, "power-output-8": { "mappings": { "0": { - "then": " levert een vermogen van maximaal 50 kw" + "then": "
Type 2 CCS (mennekes)
levert een vermogen van maximaal 50 kw" } }, - "question": "Welk vermogen levert een enkele stekker van type ?", - "render": " levert een vermogen van maximaal {socket:type2_combo:output}" + "question": "Welk vermogen levert een enkele stekker van type
Type 2 CCS (mennekes)
?", + "render": "
Type 2 CCS (mennekes)
levert een vermogen van maximaal {socket:type2_combo:output}" }, "power-output-9": { "mappings": { "0": { - "then": "Type 2 met kabel (J1772) levert een vermogen van maximaal 11 kw" + "then": "
Type 2 met kabel (J1772)
levert een vermogen van maximaal 11 kw" }, "1": { - "then": "Type 2 met kabel (J1772) levert een vermogen van maximaal 22 kw" + "then": "
Type 2 met kabel (J1772)
levert een vermogen van maximaal 22 kw" } }, - "question": "Welk vermogen levert een enkele stekker van type Type 2 met kabel (J1772) ?", - "render": "Type 2 met kabel (J1772) levert een vermogen van maximaal {socket:type2_cable:output}" + "question": "Welk vermogen levert een enkele stekker van type
Type 2 met kabel (J1772)
?", + "render": "
Type 2 met kabel (J1772)
levert een vermogen van maximaal {socket:type2_cable:output}" }, "voltage-0": { "mappings": { "0": { - "then": "Schuko stekker zonder aardingspin (CEE7/4 type F) heeft een spanning van 230 volt" + "then": "
Schuko stekker zonder aardingspin (CEE7/4 type F)
heeft een spanning van 230 volt" } }, - "question": "Welke spanning levert de stekker van type Schuko stekker zonder aardingspin (CEE7/4 type F) ", - "render": "Schuko stekker zonder aardingspin (CEE7/4 type F) heeft een spanning van {socket:schuko:voltage} volt" + "question": "Welke spanning levert de stekker van type
Schuko stekker zonder aardingspin (CEE7/4 type F)
", + "render": "
Schuko stekker zonder aardingspin (CEE7/4 type F)
heeft een spanning van {socket:schuko:voltage} volt" }, "voltage-1": { "mappings": { "0": { - "then": "Europese stekker met aardingspin (CEE7/4 type E) heeft een spanning van 230 volt" + "then": "
Europese stekker met aardingspin (CEE7/4 type E)
heeft een spanning van 230 volt" } }, - "question": "Welke spanning levert de stekker van type Europese stekker met aardingspin (CEE7/4 type E) ", - "render": "Europese stekker met aardingspin (CEE7/4 type E) heeft een spanning van {socket:typee:voltage} volt" + "question": "Welke spanning levert de stekker van type
Europese stekker met aardingspin (CEE7/4 type E)
", + "render": "
Europese stekker met aardingspin (CEE7/4 type E)
heeft een spanning van {socket:typee:voltage} volt" }, "voltage-10": { "mappings": { "0": { - "then": " heeft een spanning van 500 volt" + "then": "
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
heeft een spanning van 500 volt" }, "1": { - "then": " heeft een spanning van 920 volt" + "then": "
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
heeft een spanning van 920 volt" } }, - "question": "Welke spanning levert de stekker van type ", - "render": " heeft een spanning van {socket:tesla_supercharger_ccs:voltage} volt" + "question": "Welke spanning levert de stekker van type
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
", + "render": "
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
heeft een spanning van {socket:tesla_supercharger_ccs:voltage} volt" }, "voltage-11": { "mappings": { "0": { - "then": " heeft een spanning van 480 volt" + "then": "
Tesla Supercharger (destination)
heeft een spanning van 480 volt" } }, - "question": "Welke spanning levert de stekker van type ", - "render": " heeft een spanning van {socket:tesla_destination:voltage} volt" + "question": "Welke spanning levert de stekker van type
Tesla Supercharger (destination)
", + "render": "
Tesla Supercharger (destination)
heeft een spanning van {socket:tesla_destination:voltage} volt" }, "voltage-12": { "mappings": { "0": { - "then": " heeft een spanning van 230 volt" + "then": "
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
heeft een spanning van 230 volt" }, "1": { - "then": " heeft een spanning van 400 volt" + "then": "
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
heeft een spanning van 400 volt" } }, - "question": "Welke spanning levert de stekker van type ", - "render": " heeft een spanning van {socket:tesla_destination:voltage} volt" + "question": "Welke spanning levert de stekker van type
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
", + "render": "
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
heeft een spanning van {socket:tesla_destination:voltage} volt" + }, + "voltage-13": { + "mappings": { + "0": { + "then": "
USB om GSMs en kleine electronica op te laden
heeft een spanning van 5 volt" + } + }, + "question": "Welke spanning levert de stekker van type
USB om GSMs en kleine electronica op te laden
", + "render": "
USB om GSMs en kleine electronica op te laden
heeft een spanning van {socket:USB-A:voltage} volt" + }, + "voltage-14": { + "question": "Welke spanning levert de stekker van type
Bosch Active Connect aan een kabel
", + "render": "
Bosch Active Connect aan een kabel
heeft een spanning van {socket:bosch_3pin:voltage} volt" }, "voltage-2": { "mappings": { "0": { - "then": " heeft een spanning van 500 volt" + "then": "
Chademo
heeft een spanning van 500 volt" } }, - "question": "Welke spanning levert de stekker van type ", - "render": " heeft een spanning van {socket:chademo:voltage} volt" + "question": "Welke spanning levert de stekker van type
Chademo
", + "render": "
Chademo
heeft een spanning van {socket:chademo:voltage} volt" }, "voltage-3": { "mappings": { "0": { - "then": "Type 1 met kabel (J1772) heeft een spanning van 200 volt" + "then": "
Type 1 met kabel (J1772)
heeft een spanning van 200 volt" }, "1": { - "then": "Type 1 met kabel (J1772) heeft een spanning van 240 volt" + "then": "
Type 1 met kabel (J1772)
heeft een spanning van 240 volt" } }, - "question": "Welke spanning levert de stekker van type Type 1 met kabel (J1772) ", - "render": "Type 1 met kabel (J1772) heeft een spanning van {socket:type1_cable:voltage} volt" + "question": "Welke spanning levert de stekker van type
Type 1 met kabel (J1772)
", + "render": "
Type 1 met kabel (J1772)
heeft een spanning van {socket:type1_cable:voltage} volt" }, "voltage-4": { "mappings": { "0": { - "then": "Type 1 zonder kabel (J1772) heeft een spanning van 200 volt" + "then": "
Type 1 zonder kabel (J1772)
heeft een spanning van 200 volt" }, "1": { - "then": "Type 1 zonder kabel (J1772) heeft een spanning van 240 volt" + "then": "
Type 1 zonder kabel (J1772)
heeft een spanning van 240 volt" } }, - "question": "Welke spanning levert de stekker van type Type 1 zonder kabel (J1772) ", - "render": "Type 1 zonder kabel (J1772) heeft een spanning van {socket:type1:voltage} volt" + "question": "Welke spanning levert de stekker van type
Type 1 zonder kabel (J1772)
", + "render": "
Type 1 zonder kabel (J1772)
heeft een spanning van {socket:type1:voltage} volt" }, "voltage-5": { "mappings": { "0": { - "then": " heeft een spanning van 400 volt" + "then": "
Type 1 CCS (ook gekend als Type 1 Combo)
heeft een spanning van 400 volt" }, "1": { - "then": " heeft een spanning van 1000 volt" + "then": "
Type 1 CCS (ook gekend als Type 1 Combo)
heeft een spanning van 1000 volt" } }, - "question": "Welke spanning levert de stekker van type ", - "render": " heeft een spanning van {socket:type1_combo:voltage} volt" + "question": "Welke spanning levert de stekker van type
Type 1 CCS (ook gekend als Type 1 Combo)
", + "render": "
Type 1 CCS (ook gekend als Type 1 Combo)
heeft een spanning van {socket:type1_combo:voltage} volt" }, "voltage-6": { "mappings": { "0": { - "then": " heeft een spanning van 480 volt" + "then": "
Tesla Supercharger
heeft een spanning van 480 volt" } }, - "question": "Welke spanning levert de stekker van type ", - "render": " heeft een spanning van {socket:tesla_supercharger:voltage} volt" + "question": "Welke spanning levert de stekker van type
Tesla Supercharger
", + "render": "
Tesla Supercharger
heeft een spanning van {socket:tesla_supercharger:voltage} volt" }, "voltage-7": { "mappings": { "0": { - "then": " heeft een spanning van 230 volt" + "then": "
Type 2 (mennekes)
heeft een spanning van 230 volt" }, "1": { - "then": " heeft een spanning van 400 volt" + "then": "
Type 2 (mennekes)
heeft een spanning van 400 volt" } }, - "question": "Welke spanning levert de stekker van type ", - "render": " heeft een spanning van {socket:type2:voltage} volt" + "question": "Welke spanning levert de stekker van type
Type 2 (mennekes)
", + "render": "
Type 2 (mennekes)
heeft een spanning van {socket:type2:voltage} volt" }, "voltage-8": { "mappings": { "0": { - "then": " heeft een spanning van 500 volt" + "then": "
Type 2 CCS (mennekes)
heeft een spanning van 500 volt" }, "1": { - "then": " heeft een spanning van 920 volt" + "then": "
Type 2 CCS (mennekes)
heeft een spanning van 920 volt" } }, - "question": "Welke spanning levert de stekker van type ", - "render": " heeft een spanning van {socket:type2_combo:voltage} volt" + "question": "Welke spanning levert de stekker van type
Type 2 CCS (mennekes)
", + "render": "
Type 2 CCS (mennekes)
heeft een spanning van {socket:type2_combo:voltage} volt" }, "voltage-9": { "mappings": { "0": { - "then": "Type 2 met kabel (J1772) heeft een spanning van 230 volt" + "then": "
Type 2 met kabel (J1772)
heeft een spanning van 230 volt" }, "1": { - "then": "Type 2 met kabel (J1772) heeft een spanning van 400 volt" + "then": "
Type 2 met kabel (J1772)
heeft een spanning van 400 volt" } }, - "question": "Welke spanning levert de stekker van type Type 2 met kabel (J1772) ", - "render": "Type 2 met kabel (J1772) heeft een spanning van {socket:type2_cable:voltage} volt" + "question": "Welke spanning levert de stekker van type
Type 2 met kabel (J1772)
", + "render": "
Type 2 met kabel (J1772)
heeft een spanning van {socket:type2_cable:voltage} volt" } }, "units": { @@ -2321,7 +2383,7 @@ "name": "Drinkbaar water", "presets": { "0": { - "title": "Drinkbaar water" + "title": "drinkbaar water" } }, "tagRenderings": { @@ -2660,7 +2722,7 @@ "name": "Informatieborden", "presets": { "0": { - "title": "Informatiebord" + "title": "informatiebord" } }, "title": { @@ -2739,7 +2801,7 @@ "presets": { "0": { "description": "Voeg een ontbrekend, erkend natuurreservaat toe, bv. een gebied dat beheerd wordt door het ANB of natuurpunt", - "title": "Natuurreservaat" + "title": "natuurreservaat" } }, "tagRenderings": { @@ -2811,13 +2873,13 @@ "Operator tag": { "mappings": { "0": { - "then": "Dit gebied wordt beheerd door Natuurpunt" + "then": "Dit gebied wordt beheerd door Natuurpunt" }, "1": { - "then": "Dit gebied wordt beheerd door {operator}" + "then": "Dit gebied wordt beheerd door {operator}" }, "2": { - "then": "Dit gebied wordt beheerd door het Agentschap Natuur en Bos" + "then": "Dit gebied wordt beheerd door het Agentschap Natuur en Bos" } }, "question": "Wie beheert dit gebied?", @@ -2906,46 +2968,12 @@ "name": "Parking", "presets": { "0": { - "description": "Voeg een ontbrekend, erkend pad toe.", - "title": "Paden" - } - }, - "tagRenderings": { - "Access tag": { - "mappings": { - "0": { - "then": "Vrij toegankelijk" - }, - "1": { - "then": "Niet toegankelijk" - }, - "2": { - "then": "Niet toegankelijk, want privégebied" - }, - "3": { - "then": "Toegankelijk, ondanks dat het privegebied is" - }, - "4": { - "then": "Enkel toegankelijk met een gids of tijdens een activiteit" - }, - "5": { - "then": "Toegankelijk mits betaling" - } - }, - "question": "Is dit gebied toegankelijk?", - "render": "De toegankelijkheid van dit gebied is: {access:description}" + "description": "Voeg hier een fietsenstalling toe", + "title": "fietsparking" }, - "Operator tag": { - "mappings": { - "0": { - "then": "Dit gebied wordt beheerd door Natuurpunt" - }, - "1": { - "then": "Dit gebied wordt beheerd door {operator}" - } - }, - "question": "Wie beheert dit pad?", - "render": "Beheer door {operator}" + "1": { + "description": "Voeg hier een parking voor auto's toe", + "title": "parking" } }, "title": { @@ -2968,7 +2996,7 @@ "name": "Picnictafels", "presets": { "0": { - "title": "Picnic-tafel" + "title": "picnic-tafel" } }, "tagRenderings": { @@ -3134,6 +3162,15 @@ }, "public_bookcase": { "description": "Een straatkastje met boeken voor iedereen", + "filter": { + "2": { + "options": { + "0": { + "question": "Binnen of buiten" + } + } + } + }, "name": "Boekenruilkastjes", "presets": { "0": { @@ -3527,15 +3564,38 @@ } }, "toilet": { + "filter": { + "0": { + "options": { + "0": { + "question": "Rolstoel toegankelijk" + } + } + }, + "1": { + "options": { + "0": { + "question": "Heeft een luiertafel" + } + } + }, + "2": { + "options": { + "0": { + "question": "Gratis toegankelijk" + } + } + } + }, "name": "Toiletten", "presets": { "0": { "description": "Een publieke toilet", - "title": "Toilet" + "title": "toilet" }, "1": { "description": "Deze toiletten hebben op zijn minst één rolstoeltoegankelijke WC", - "title": "Een rolstoeltoegankelijke toilet" + "title": "een rolstoeltoegankelijke toilet" } }, "tagRenderings": { @@ -3582,6 +3642,28 @@ "question": "Hoeveel moet men betalen om deze toiletten te gebruiken?", "render": "De toiletten gebruiken kost {charge}" }, + "toilet-handwashing": { + "mappings": { + "0": { + "then": "Deze toiletten hebben een lavabo waar men de handen kan wassen" + }, + "1": { + "then": "Deze toiletten hebben geen lavabo waar men de handen kan wassen" + } + }, + "question": "Hebben deze toiletten een lavabo om de handen te wassen?" + }, + "toilet-has-paper": { + "mappings": { + "0": { + "then": "Deze toilet is voorzien van toiletpapier" + }, + "1": { + "then": "Je moet je eigen toiletpapier meebrengen naar deze toilet" + } + }, + "question": "Moet je je eigen toiletpappier meenemen naar deze toilet?" + }, "toilets-changing-table": { "mappings": { "0": { diff --git a/langs/layers/pl.json b/langs/layers/pl.json index 7c91508bba..64b52a16b9 100644 --- a/langs/layers/pl.json +++ b/langs/layers/pl.json @@ -3,7 +3,6 @@ "name": "Ławki", "presets": { "0": { - "description": "Dodaj nową ławkę", "title": "Ławka" } }, diff --git a/langs/layers/pt_BR.json b/langs/layers/pt_BR.json index 602d1ea854..923a5d76d5 100644 --- a/langs/layers/pt_BR.json +++ b/langs/layers/pt_BR.json @@ -3,8 +3,7 @@ "name": "Bancos", "presets": { "0": { - "description": "Adicionar um novo banco", - "title": "Banco" + "title": "banco" } }, "tagRenderings": { diff --git a/langs/layers/ru.json b/langs/layers/ru.json index 2c6485f51a..4e776b4523 100644 --- a/langs/layers/ru.json +++ b/langs/layers/ru.json @@ -3,7 +3,6 @@ "name": "Скамейки", "presets": { "0": { - "description": "Добавить новую скамейку", "title": "Скамейка" } }, @@ -1023,10 +1022,10 @@ "presets": { "0": { "description": "Туалет или комната отдыха со свободным доступом", - "title": "Туалет" + "title": "tуалет" }, "1": { - "title": "Туалет с доступом для пользователей кресел-колясок" + "title": "tуалет с доступом для пользователей кресел-колясок" } }, "tagRenderings": { diff --git a/langs/layers/zh_Hans.json b/langs/layers/zh_Hans.json index 90b74fe669..360a52c611 100644 --- a/langs/layers/zh_Hans.json +++ b/langs/layers/zh_Hans.json @@ -3,7 +3,6 @@ "name": "长椅", "presets": { "0": { - "description": "增加一个新的长椅", "title": "长椅" } }, diff --git a/langs/layers/zh_Hant.json b/langs/layers/zh_Hant.json index 994ecb4863..ccacafa7f4 100644 --- a/langs/layers/zh_Hant.json +++ b/langs/layers/zh_Hant.json @@ -3,7 +3,6 @@ "name": "長椅", "presets": { "0": { - "description": "新增長椅", "title": "長椅" } }, diff --git a/langs/nl.json b/langs/nl.json index 1113c6e154..702ae077cb 100644 --- a/langs/nl.json +++ b/langs/nl.json @@ -50,8 +50,8 @@ "openLayerControl": "Open de laag-instellingen", "layerNotEnabled": "De laag {layer} is gedeactiveerd. Activeer deze om een punt toe te voegen", "presetInfo": "Het nieuwe punt krijgt de attributen {tags}", - "disableFilters": "Zet alle filters af", - "disableFiltersExplanation": "Interessepunten kunnen verborgen zijn door een filter" + "disableFiltersExplanation": "Interessepunten kunnen verborgen zijn door een filter", + "disableFilters": "Zet alle filters af" }, "pickLanguage": "Kies je taal: ", "about": "Bewerk en voeg data toe aan OpenStreetMap over een specifiek onderwerp op een gemakkelijke manier", @@ -108,131 +108,6 @@ "intro": "MapComplete is een OpenStreetMap applicatie waar informatie over een specifiek thema bekeken en aangepast kan worden.", "pickTheme": "Kies hieronder een thema om te beginnen." }, - "general": { - "loginWithOpenStreetMap": "Aanmelden met OpenStreetMap", - "welcomeBack": "Je bent aangemeld. Welkom terug!", - "loginToStart": "Meld je aan om deze vraag te beantwoorden", - "search": { - "search": "Zoek naar een locatie", - "searching": "Aan het zoeken…", - "nothing": "Niets gevonden…", - "error": "Iets ging mis…" - }, - "returnToTheMap": "Naar de kaart", - "save": "Opslaan", - "cancel": "Annuleren", - "skip": "Vraag overslaan", - "oneSkippedQuestion": "Een vraag is overgeslaan", - "skippedQuestions": "Sommige vragen zijn overgeslaan", - "number": "getal", - "osmLinkTooltip": "Bekijk dit object op OpenStreetMap waar geschiedenis en meer aanpasopties zijn", - "add": { - "addNew": "Voeg hier een {category} toe", - "title": "Nieuw punt toevoegen?", - "intro": "Je klikte ergens waar er nog geen data is. Kies hieronder welk punt je wilt toevoegen
", - "pleaseLogin": "Gelieve je aan te melden om een punt to te voegen", - "zoomInFurther": "Gelieve verder in te zoomen om een punt toe te voegen.", - "stillLoading": "De data wordt nog geladen. Nog even geduld en dan kan je een punt toevoegen.", - "confirmIntro": "

Voeg hier een {title} toe?

Het punt dat je hier toevoegt, is zichtbaar voor iedereen. Veel applicaties gebruiken deze data, voeg dus enkel punten toe die echt bestaan.", - "confirmButton": "Voeg hier een {category} toe
Je toevoeging is voor iedereen zichtbaar
", - "openLayerControl": "Open de laag-instellingen", - "layerNotEnabled": "De laag {layer} is gedeactiveerd. Activeer deze om een punt toe te voegen" - }, - "pickLanguage": "Kies je taal: ", - "about": "Bewerk en voeg data toe aan OpenStreetMap over een specifiek onderwerp op een gemakkelijke manier", - "nameInlineQuestion": "De naam van dit {category} is $$$", - "noNameCategory": "{category} zonder naam", - "questions": { - "phoneNumberOf": "Wat is het telefoonnummer van {category}?", - "phoneNumberIs": "Het telefoonnummer van {category} is {phone}", - "websiteOf": "Wat is de website van {category}?", - "websiteIs": "Website: {website}", - "emailOf": "Wat is het email-adres van {category}?", - "emailIs": "Het email-adres van {category} is {email}" - }, - "openStreetMapIntro": "

Een open kaart

Zou het niet fantastisch zijn als er een open kaart zou zijn die door iedereen aangepast én gebruikt kan worden? Een kaart waar iedereen zijn interesses aan zou kunnen toevoegen? Dan zouden er geen duizend-en-één verschillende kleine kaartjes, websites, ... meer nodig zijn

OpenStreetMap is deze open kaart. Je mag de kaartdata gratis gebruiken (mits bronvermelding en herpublicatie van aanpassingen). Daarenboven mag je de kaart ook gratis aanpassen als je een account maakt. Ook deze website is gebaseerd op OpenStreetMap. Als je hier een vraag beantwoord, gaat het antwoord daar ook naartoe

Tenslotte zijn er reeds vele gebruikers van OpenStreetMap. Denk maar Organic Maps, OsmAnd, verschillende gespecialiseerde routeplanners, de achtergrondkaarten op Facebook, Instagram,...
Zelfs Apple Maps en Bing-Maps gebruiken OpenStreetMap in hun kaarten!

Kortom, als je hier een punt toevoegd of een vraag beantwoord, zal dat na een tijdje ook in al dié applicaties te zien zijn.

", - "attribution": { - "attributionTitle": "Met dank aan", - "attributionContent": "

Alle data is voorzien door OpenStreetMap, gratis en vrij te hergebruiken onder de Open DataBase Licentie.

", - "themeBy": "Thema gemaakt door {author}", - "iconAttribution": { - "title": "Iconen en afbeeldingen" - }, - "mapContributionsByAndHidden": "De zichtbare data heeft bijdragen van {contributors} en {hiddenCount} andere bijdragers", - "mapContributionsBy": "De huidige data is bijgedragen door {contributors}", - "codeContributionsBy": "MapComplete is gebouwd door {contributors} en {hiddenCount} andere bijdragers" - }, - "sharescreen": { - "intro": "

Deel deze kaart

Kopieer onderstaande link om deze kaart naar vrienden en familie door te sturen:", - "addToHomeScreen": "

Voeg toe aan je thuis-scherm

Je kan deze website gemakkelijk aan het thuisscherm van je smartphone toevoegen voor een native feel. Klik op de 'voeg toe aan thuis-scherm' knop in de URL-balk om dit te doen.", - "embedIntro": "

Plaats dit op je website

Voeg dit kaartje toe op je eigen website.
We moedigen dit zelfs aan - je hoeft geen toestemming te vragen.
Het is gratis en zal dat altijd blijven. Hoe meer het gebruikt wordt, hoe waardevoller.", - "copiedToClipboard": "Link gekopieerd naar klembord", - "thanksForSharing": "Bedankt om te delen!", - "editThisTheme": "Pas dit thema aan", - "editThemeDescription": "Pas vragen aan of voeg vragen toe aan dit kaartthema", - "fsUserbadge": "Activeer de login-knop", - "fsSearch": "Activeer de zoekbalk", - "fsWelcomeMessage": "Toon het welkomstbericht en de bijhorende tabbladen", - "fsLayers": "Toon de knop voor laagbediening", - "fsLayerControlToggle": "Toon de laagbediening meteen volledig", - "fsAddNew": "Activeer het toevoegen van nieuwe POI", - "fsGeolocation": "Toon het knopje voor geolocalisatie (enkel op mobiel)", - "fsIncludeCurrentBackgroundMap": "Gebruik de huidige achtergrond {name}", - "fsIncludeCurrentLayers": "Toon enkel de huidig getoonde lagen", - "fsIncludeCurrentLocation": "Start op de huidige locatie" - }, - "morescreen": { - "intro": "

Meer thematische kaarten

Vind je het leuk om geodata te verzamelen?
Hier vind je meer kaartthemas.", - "requestATheme": "Wil je een eigen kaartthema, vraag dit in de issue tracker.", - "streetcomplete": "Een andere, gelijkaardige Android-applicatie is StreetComplete.", - "createYourOwnTheme": "Maak je eigen MapComplete-kaart" - }, - "readYourMessages": "Gelieve eerst je berichten op OpenStreetMap te lezen alvorens nieuwe punten toe te voegen.", - "fewChangesBefore": "Gelieve eerst enkele vragen van bestaande punten te beantwoorden vooraleer zelf punten toe te voegen.", - "goToInbox": "Ga naar de berichten", - "getStartedLogin": "Login met OpenStreetMap om te beginnen", - "getStartedNewAccount": " of maak een nieuwe account aan", - "noTagsSelected": "Geen tags geselecteerd", - "customThemeIntro": "

Onofficiële thema's

De onderstaande thema's heb je eerder bezocht en zijn gemaakt door andere OpenStreetMappers.", - "aboutMapcomplete": "

Over MapComplete

Met MapComplete kun je OpenStreetMap verrijken met informatie over een bepaald thema. Beantwoord enkele vragen, en binnen een paar minuten is jouw bijdrage wereldwijd beschikbaar! De maker van het thema bepaalt de elementen, vragen en taalversies voor het thema.

Ontdek meer

MapComplete biedt altijd de volgende stap naar meer OpenStreetMap:

  • Indien ingebed in een website linkt het iframe naar de volledige MapComplete
  • De volledige versie heeft uitleg over OpenStreetMap
  • Bekijken kan altijd, maar wijzigen vereist een OSM-account
  • Als je niet aangemeld bent, wordt je gevraagd dit te doen
  • Als je minstens één vraag hebt beantwoord, kan je ook elementen toevoegen
  • Heb je genoeg changesets, dan verschijnen de OSM-tags, nog later links naar de wiki

Merk je een bug of wil je een extra feature? Wil je helpen vertalen? Bezoek dan de broncode en issue tracker.

Wil je je vorderingen zien? Volg de edits op OsmCha.

", - "backgroundMap": "Achtergrondkaart", - "layerSelection": { - "zoomInToSeeThisLayer": "Vergroot de kaart om deze laag te zien", - "title": "Selecteer lagen" - }, - "weekdays": { - "abbreviations": { - "monday": "Maan", - "tuesday": "Din", - "wednesday": "Woe", - "thursday": "Don", - "friday": "Vrij", - "saturday": "Zat", - "sunday": "Zon" - }, - "monday": "Maandag", - "tuesday": "Dinsdag", - "wednesday": "Woensdag", - "thursday": "Donderdag", - "friday": "Vrijdag", - "saturday": "Zaterdag", - "sunday": "Zondag" - }, - "opening_hours": { - "error_loading": "Fout: kon deze openingstijden niet weergeven.", - "open_during_ph": "Op een feestdag is deze zaak", - "opensAt": "vanaf", - "openTill": "tot", - "closed_until": "Gesloten - open op {date}", - "closed_permanently": "Gesloten voor onbepaalde tijd", - "open_24_7": "Dag en nacht open", - "ph_not_known": " ", - "ph_closed": "gesloten", - "ph_open": "open", - "ph_open_as_usual": "geopend zoals gewoonlijk", - "not_all_rules_parsed": "De openingsuren van deze zaak zijn ingewikkeld. De volgende regels worden niet getoond bij het ingeven:" - } - }, "reviews": { "title": "{count} beoordelingen", "title_singular": "Eén beoordeling", @@ -301,6 +176,17 @@ "not_all_rules_parsed": "De openingsuren van deze zaak zijn ingewikkeld. De volgende regels worden niet getoond bij het ingeven:", "loadingCountry": "Het land wordt nog bepaald…" }, + "skippedQuestions": "Enkele vragen werden overgeslaan", + "skip": "Sla deze vraag over", + "save": "Opslaan", + "returnToTheMap": "Ga terug naar de kaart", + "pdf": { + "versionInfo": "v{version} - gemaakt op {date}" + }, + "osmLinkTooltip": "Bekijk dit object op OpenStreetMap om de geschiedenis te zien en meer te kunnen aanpassen", + "oneSkippedQuestion": "Een vraag werd overgeslaan", + "number": "getal", + "loginOnlyNeededToEdit": "als je de kaart wilt aanpassen", "download": { "noDataLoaded": "Er is nog geen data ingeladen. Downloaden kan zodra de data geladen is.", "licenseInfo": "

Copyright

De voorziene data is beschikbaar onder de ODbL. Het hergebruiken van deze data is gratis voor elke toepassing, maar
  • de bronvermelding © OpenStreetMap bijdragers is vereist
  • Elke wijziging aan deze data moet opnieuw gepubliceerd worden onder dezelfde licentie
Gelieve de volledige licentie te lezen voor details", @@ -312,18 +198,7 @@ "downloadAsPdfHelper": "Perfect om de huidige kaart af te printen", "downloadAsPdf": "Download een PDF van de huidig zichtbare kaart" }, - "loginOnlyNeededToEdit": "als je de kaart wilt aanpassen", - "pdf": { - "versionInfo": "v{version} - gemaakt op {date}" - }, - "osmLinkTooltip": "Bekijk dit object op OpenStreetMap om de geschiedenis te zien en meer te kunnen aanpassen", - "number": "getal", - "skippedQuestions": "Enkele vragen werden overgeslaan", - "oneSkippedQuestion": "Een vraag werd overgeslaan", - "skip": "Sla deze vraag over", - "cancel": "Annuleren", - "save": "Opslaan", - "returnToTheMap": "Ga terug naar de kaart" + "cancel": "Annuleren" }, "reviews": { "title": "{count} beoordelingen", @@ -359,4 +234,4 @@ "test": "Dit punt was een test en was nooit echt aanwezig" } } -} +} \ No newline at end of file diff --git a/langs/shared-questions/en.json b/langs/shared-questions/en.json index 9821d40399..709534fcc2 100644 --- a/langs/shared-questions/en.json +++ b/langs/shared-questions/en.json @@ -3,6 +3,23 @@ "description": { "question": "Is there still something relevant you couldn't give in the previous questions? Add it here.
Don't repeat already stated facts" }, + "dog-access": { + "mappings": { + "0": { + "then": "Dogs are allowed" + }, + "1": { + "then": "Dogs are not allowed" + }, + "2": { + "then": "Dogs are allowed, but they have to be leashed" + }, + "3": { + "then": "Dogs are allowed and can run around freely" + } + }, + "question": "Are dogs allowed in this business?" + }, "email": { "question": "What is the email address of {name}?" }, diff --git a/langs/shared-questions/nl.json b/langs/shared-questions/nl.json index b6749039b8..8c7d6b5148 100644 --- a/langs/shared-questions/nl.json +++ b/langs/shared-questions/nl.json @@ -3,6 +3,23 @@ "description": { "question": "Zijn er nog andere relevante zaken die je niet in de bovenstaande vragen kwijt kon? Vul ze hier in.
Herhaal geen antwoorden die je reeds gaf" }, + "dog-access": { + "mappings": { + "0": { + "then": "honden zijn toegelaten" + }, + "1": { + "then": "honden zijn niet toegelaten" + }, + "2": { + "then": "honden zijn enkel aan de leiband welkom" + }, + "3": { + "then": "honden zijn welkom en mogen vrij rondlopen" + } + }, + "question": "Zijn honden toegelaten in deze zaak?" + }, "email": { "question": "Wat is het e-mailadres van {name}?" }, diff --git a/langs/shared-questions/pt.json b/langs/shared-questions/pt.json index 311754567b..9524e139d5 100644 --- a/langs/shared-questions/pt.json +++ b/langs/shared-questions/pt.json @@ -1,66 +1,66 @@ { "undefined": { - "level": { - "question": "Em que nível se encontra este elemento?", - "mappings": { - "3": { - "then": "Está no primeiro andar" - }, - "2": { - "then": "Está ao nível do rés-do-chão" - }, - "1": { - "then": "Está ao nível do rés-do-chão" - }, - "0": { - "then": "Está no subsolo" - } - }, - "render": "Está no {nível}º andar" + "description": { + "question": "Ainda há algo de relevante que não tenha podido dar nas perguntas anteriores? Adicione-o aqui.
Não repita factos já declarados" }, "email": { "question": "Qual é o endereço de e-mail de {name}?" }, - "description": { - "question": "Ainda há algo de relevante que não tenha podido dar nas perguntas anteriores? Adicione-o aqui.
Não repita factos já declarados" - }, - "wheelchair-access": { - "question": "Este lugar é acessível a utilizadores de cadeiras de rodas?", + "level": { "mappings": { - "3": { - "then": "Este lugar não é acessível com uma cadeira de rodas" - }, - "2": { - "then": "É possível chegar a este local em cadeira de rodas, mas não é fácil" + "0": { + "then": "Está no subsolo" }, "1": { - "then": "Este lugar é de fácil acesso com uma cadeira de rodas" + "then": "Está ao nível do rés-do-chão" }, - "0": { - "then": "Este lugar está especialmente adaptado para utilizadores de cadeira de rodas" + "2": { + "then": "Está ao nível do rés-do-chão" + }, + "3": { + "then": "Está no primeiro andar" } - } + }, + "question": "Em que nível se encontra este elemento?", + "render": "Está no {nível}º andar" }, - "website": { - "question": "Qual é o sítio web de {name}?" + "opening_hours": { + "question": "Qual é o horário de funcionamento de {name}?", + "render": "

Horário de funcionamento

{opening_hours_table(opening_hours)}" + }, + "payment-options": { + "mappings": { + "0": { + "then": "Aceitam pagamento com dinheiro aqui" + }, + "1": { + "then": "Aceitam pagamento com cartões bancários aqui" + } + }, + "question": "Que métodos de pagamento são aceites aqui?" }, "phone": { "question": "Qual é o número de telefone de {name}?" }, - "payment-options": { - "question": "Que métodos de pagamento são aceites aqui?", - "mappings": { - "1": { - "then": "Aceitam pagamento com cartões bancários aqui" - }, - "0": { - "then": "Aceitam pagamento com dinheiro aqui" - } - } + "website": { + "question": "Qual é o sítio web de {name}?" }, - "opening_hours": { - "render": "

Horário de funcionamento

{opening_hours_table(opening_hours)}", - "question": "Qual é o horário de funcionamento de {name}?" + "wheelchair-access": { + "mappings": { + "0": { + "then": "Este lugar está especialmente adaptado para utilizadores de cadeira de rodas" + }, + "1": { + "then": "Este lugar é de fácil acesso com uma cadeira de rodas" + }, + "2": { + "then": "É possível chegar a este local em cadeira de rodas, mas não é fácil" + }, + "3": { + "then": "Este lugar não é acessível com uma cadeira de rodas" + } + }, + "question": "Este lugar é acessível a utilizadores de cadeiras de rodas?" } } -} +} \ No newline at end of file diff --git a/langs/shared-questions/pt_BR.json b/langs/shared-questions/pt_BR.json index 38a5868bc5..6599af377e 100644 --- a/langs/shared-questions/pt_BR.json +++ b/langs/shared-questions/pt_BR.json @@ -28,6 +28,17 @@ "question": "Qual o horário de funcionamento de {name}?", "render": "

Horário de funcionamento

{opening_hours_table(opening_hours)}" }, + "payment-options": { + "mappings": { + "0": { + "then": "Dinheiro é aceito aqui" + }, + "1": { + "then": "Cartões de pagamento são aceitos aqui" + } + }, + "question": "Quais métodos de pagamento são aceitos aqui?" + }, "phone": { "question": "Qual o número de telefone de {name}?" }, @@ -35,32 +46,21 @@ "question": "Qual o site de {name}?" }, "wheelchair-access": { - "question": "Este lugar é acessível com uma cadeira de rodas?", "mappings": { - "3": { - "then": "Este lugar não é alcançável com uma cadeira de rodas" - }, - "2": { - "then": "É possível chegar a esse local em uma cadeira de rodas, mas não é fácil" + "0": { + "then": "Este lugar é especialmente adaptado para usuários de cadeira de rodas" }, "1": { "then": "Este lugar é facilmente acessível com uma cadeira de rodas" }, - "0": { - "then": "Este lugar é especialmente adaptado para usuários de cadeira de rodas" - } - } - }, - "payment-options": { - "question": "Quais métodos de pagamento são aceitos aqui?", - "mappings": { - "1": { - "then": "Cartões de pagamento são aceitos aqui" + "2": { + "then": "É possível chegar a esse local em uma cadeira de rodas, mas não é fácil" }, - "0": { - "then": "Dinheiro é aceito aqui" + "3": { + "then": "Este lugar não é alcançável com uma cadeira de rodas" } - } + }, + "question": "Este lugar é acessível com uma cadeira de rodas?" } } -} +} \ No newline at end of file diff --git a/langs/themes/en.json b/langs/themes/en.json index cf9c493760..9dcb4b54af 100644 --- a/langs/themes/en.json +++ b/langs/themes/en.json @@ -743,6 +743,22 @@ "description": "On this map, publicly accessible drinking water spots are shown and can be easily added", "title": "Drinking Water" }, + "etymology": { + "description": "On this map, you can see what an object is named after. The streets, buildings, ... come from OpenStreetMap which got linked with Wikidata. The information comes from Wpikipedia.", + "layers": { + "0": { + "description": "All objects which have an etymology known", + "name": "Has etymolgy", + "tagRenderings": { + "simple etymology": { + "render": "Named after {name:etymology}" + } + } + } + }, + "shortDescription": "What is the origin of a toponym?", + "title": "Open Etymology Map" + }, "facadegardens": { "description": "Facade gardens, green facades and trees in the city not only bring peace and quiet, but also a more beautiful city, greater biodiversity, a cooling effect and better air quality.
Klimaan VZW and Mechelen Klimaatneutraal want to map existing and new facade gardens as an example for people who want to build their own garden or for city walkers who love nature.
More info about the project at klimaan.be.", "layers": { @@ -1216,7 +1232,7 @@ "title": "Parking" }, "personal": { - "description": "Create a personal theme based on all the available layers of all themes", + "description": "Create a personal theme based on all the available layers of all themes. In order to show some data, open layer selection", "title": "Personal theme" }, "playgrounds": { @@ -1319,6 +1335,43 @@ "shortDescription": "Map all the trees", "title": "Trees" }, + "uk_addresses": { + "description": "Contribute to OpenStreetMap by filling out address information", + "layers": { + "1": { + "description": "Addresses", + "name": "Known addresses in OSM", + "tagRenderings": { + "uk_addresses_explanation_osm": { + "render": "This address is saved in OpenStreetMap" + }, + "uk_addresses_housenumber": { + "mappings": { + "0": { + "then": "This building has no house number" + } + }, + "question": "What is the number of this house?", + "render": "The housenumber is {addr:housenumber}" + }, + "uk_addresses_street": { + "question": "What street is this address located in?", + "render": "This address is in street {addr:street}" + } + }, + "title": { + "render": "Known address" + } + }, + "2": { + "title": { + "render": "{name}" + } + } + }, + "shortDescription": "Help to build an open dataset of UK addresses", + "title": "UK Addresses" + }, "waste_basket": { "description": "On this map, you'll find waste baskets near you. If a waste basket is missing on this map, you can add it yourself", "shortDescription": "A map with waste baskets", diff --git a/langs/themes/nl.json b/langs/themes/nl.json index a1e3dee82a..409d96feaa 100644 --- a/langs/themes/nl.json +++ b/langs/themes/nl.json @@ -624,6 +624,22 @@ "description": "Op deze kaart staan publiek toegankelijke drinkwaterpunten en kan je makkelijk een nieuw drinkwaterpunt toevoegen", "title": "Drinkwaterpunten" }, + "etymology": { + "description": "Op deze kaart zie je waar een plaats naar is vernoemd. De straten, gebouwen, ... komen uit OpenStreetMap, waar een link naar Wikidata werd gelegd. De informatie komt uit wikipedia.", + "layers": { + "0": { + "description": "Alle lagen met een gelinkt etymology", + "name": "Heeft etymology info", + "tagRenderings": { + "simple etymology": { + "render": "Vernoemd naar {name:etymology}" + } + } + } + }, + "shortDescription": "Wat is de oorsprong van een plaatsnaam?", + "title": "Open Etymology-kaart" + }, "facadegardens": { "description": "Ontharde voortuintjes, groene gevels en bomen ín de stad brengen naast rust ook een mooiere stad, een grotere biodiversiteit, een verkoelend effect en een betere luchtkwaliteit.
Klimaan VZW en 'Mechelen Klimaatneutraal' willen met het project Klim(t)aan je Gevel bestaande en nieuwe geveltuintjes in kaart brengen als voorbeeld voor mensen zelf een tuintje willen aanleggen of voor stadwandelaars die houden van de natuur.
Meer info over het project op klimaan.be.", "layers": { @@ -1008,7 +1024,7 @@ "title": "Surveillance under Surveillance" }, "toerisme_vlaanderen": { - "description": "Op deze kaart kan je info zien voor toeristen en zelf aanpasingen maken, zichtbaar voor iedereen", + "description": "Op deze kaart kan je info zien die relevant is voor toerisme, zoals:
  • Eetgelegenheden
  • Cafés en bars
  • (Fiets)oplaadpunten
  • Fietspompen, fietserverhuur en fietswinkels
  • Uitkijktorens
  • ...
Zie je fouten op de kaart? Dan kan je zelf makkelijk aanpasingen maken, die zichtbaar zijn voor iedereen. Hiervoor dien je een gratis OpenStreetMap account voor te maken.

Met de steun van Toerisme Vlaanderen", "shortDescription": "Een kaart om toeristisch relevante info op aan te duiden", "title": "Toeristisch relevante info" }, diff --git a/package-lock.json b/package-lock.json index 3c57957da6..d0501ac778 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,8 +1,16437 @@ { "name": "mapcomplete", "version": "0.0.5", - "lockfileVersion": 1, + "lockfileVersion": 2, "requires": true, + "packages": { + "": { + "name": "mapcomplete", + "version": "0.0.5", + "license": "GPL", + "dependencies": { + "@babel/preset-env": "7.13.8", + "@turf/buffer": "^6.3.0", + "@turf/collect": "^6.3.0", + "@turf/distance": "^6.3.0", + "@turf/length": "^6.3.0", + "@turf/turf": "^6.3.0", + "@types/jquery": "^3.5.5", + "@types/leaflet-markercluster": "^1.0.3", + "@types/leaflet-providers": "^1.2.0", + "@types/leaflet.markercluster": "^1.4.3", + "@types/lz-string": "^1.3.34", + "@types/prompt-sync": "^4.1.0", + "country-language": "^0.1.7", + "email-validator": "^2.0.4", + "escape-html": "^1.0.3", + "i18next-client": "^1.11.4", + "jquery": "^3.6.0", + "jspdf": "^2.3.1", + "latlon2country": "^1.1.3", + "leaflet": "^1.7.1", + "leaflet-providers": "^1.10.2", + "leaflet-simple-map-screenshoter": "^0.4.4", + "leaflet.markercluster": "^1.4.1", + "libphonenumber": "0.0.10", + "libphonenumber-js": "^1.7.55", + "lz-string": "^1.4.4", + "mangrove-reviews": "^0.1.3", + "moment": "^2.29.0", + "npm-run-all": "^4.1.5", + "opening_hours": "^3.6.0", + "osm-auth": "^1.0.2", + "osmtogeojson": "^3.0.0-beta.4", + "parcel": "^1.2.4", + "prompt-sync": "^4.2.0", + "tailwindcss": "^2.2.15", + "tslint": "^6.1.3" + }, + "devDependencies": { + "@babel/polyfill": "^7.10.4", + "@types/node": "^7.0.5", + "assert": "^2.0.0", + "fs": "0.0.1-security", + "git-json-merge": "^0.4.5", + "marked": "^2.0.0", + "read-file": "^0.2.0", + "sharp": "^0.28.3", + "ts-node": "^9.0.0", + "ts-node-dev": "^1.0.0-pre.63", + "tslint-no-circular-imports": "^0.7.0", + "typescript": "^3.9.7", + "write-file": "^1.0.0" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.13.tgz", + "integrity": "sha512-HV1Cm0Q3ZrpCR93tkWOYiuYIgLxZXZFVG2VgK+MBWjUqZTundupbfx2aXarXuw5Ko5aMcjtJgbSs4vUGBS5v6g==", + "dependencies": { + "@babel/highlight": "^7.12.13" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.14.0", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.14.0.tgz", + "integrity": "sha512-vu9V3uMM/1o5Hl5OekMUowo3FqXLJSw+s+66nt0fSWVWTtmosdzn45JHOB3cPtZoe6CTBDzvSw0RdOY85Q37+Q==" + }, + "node_modules/@babel/core": { + "version": "7.14.0", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.14.0.tgz", + "integrity": "sha512-8YqpRig5NmIHlMLw09zMlPTvUVMILjqCOtVgu+TVNWEBvy9b5I3RRyhqnrV4hjgEK7n8P9OqvkWJAFmEL6Wwfw==", + "dependencies": { + "@babel/code-frame": "^7.12.13", + "@babel/generator": "^7.14.0", + "@babel/helper-compilation-targets": "^7.13.16", + "@babel/helper-module-transforms": "^7.14.0", + "@babel/helpers": "^7.14.0", + "@babel/parser": "^7.14.0", + "@babel/template": "^7.12.13", + "@babel/traverse": "^7.14.0", + "@babel/types": "^7.14.0", + "convert-source-map": "^1.7.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.1.2", + "semver": "^6.3.0", + "source-map": "^0.5.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/core/node_modules/json5": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.0.tgz", + "integrity": "sha512-f+8cldu7X/y7RAJurMEJmdoKXGB/X550w2Nr3tTbezL6RwEE/iMcm+tZnXeoZtKuOq6ft8+CqzEkrIgx1fPoQA==", + "dependencies": { + "minimist": "^1.2.5" + }, + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@babel/generator": { + "version": "7.14.1", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.14.1.tgz", + "integrity": "sha512-TMGhsXMXCP/O1WtQmZjpEYDhCYC9vFhayWZPJSZCGkPJgUqX0rF0wwtrYvnzVxIjcF80tkUertXVk5cwqi5cAQ==", + "dependencies": { + "@babel/types": "^7.14.1", + "jsesc": "^2.5.1", + "source-map": "^0.5.0" + } + }, + "node_modules/@babel/helper-annotate-as-pure": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.12.13.tgz", + "integrity": "sha512-7YXfX5wQ5aYM/BOlbSccHDbuXXFPxeoUmfWtz8le2yTkTZc+BxsiEnENFoi2SlmA8ewDkG2LgIMIVzzn2h8kfw==", + "dependencies": { + "@babel/types": "^7.12.13" + } + }, + "node_modules/@babel/helper-builder-binary-assignment-operator-visitor": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.12.13.tgz", + "integrity": "sha512-CZOv9tGphhDRlVjVkAgm8Nhklm9RzSmWpX2my+t7Ua/KT616pEzXsQCjinzvkRvHWJ9itO4f296efroX23XCMA==", + "dependencies": { + "@babel/helper-explode-assignable-expression": "^7.12.13", + "@babel/types": "^7.12.13" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.13.16", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.13.16.tgz", + "integrity": "sha512-3gmkYIrpqsLlieFwjkGgLaSHmhnvlAYzZLlYVjlW+QwI+1zE17kGxuJGmIqDQdYp56XdmGeD+Bswx0UTyG18xA==", + "dependencies": { + "@babel/compat-data": "^7.13.15", + "@babel/helper-validator-option": "^7.12.17", + "browserslist": "^4.14.5", + "semver": "^6.3.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-create-class-features-plugin": { + "version": "7.14.1", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.14.1.tgz", + "integrity": "sha512-r8rsUahG4ywm0QpGcCrLaUSOuNAISR3IZCg4Fx05Ozq31aCUrQsTLH6KPxy0N5ULoQ4Sn9qjNdGNtbPWAC6hYg==", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.12.13", + "@babel/helper-function-name": "^7.12.13", + "@babel/helper-member-expression-to-functions": "^7.13.12", + "@babel/helper-optimise-call-expression": "^7.12.13", + "@babel/helper-replace-supers": "^7.13.12", + "@babel/helper-split-export-declaration": "^7.12.13" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-create-regexp-features-plugin": { + "version": "7.12.17", + "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.12.17.tgz", + "integrity": "sha512-p2VGmBu9oefLZ2nQpgnEnG0ZlRPvL8gAGvPUMQwUdaE8k49rOMuZpOwdQoy5qJf6K8jL3bcAMhVUlHAjIgJHUg==", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.12.13", + "regexpu-core": "^4.7.1" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-define-polyfill-provider": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.1.5.tgz", + "integrity": "sha512-nXuzCSwlJ/WKr8qxzW816gwyT6VZgiJG17zR40fou70yfAcqjoNyTLl/DQ+FExw5Hx5KNqshmN8Ldl/r2N7cTg==", + "dependencies": { + "@babel/helper-compilation-targets": "^7.13.0", + "@babel/helper-module-imports": "^7.12.13", + "@babel/helper-plugin-utils": "^7.13.0", + "@babel/traverse": "^7.13.0", + "debug": "^4.1.1", + "lodash.debounce": "^4.0.8", + "resolve": "^1.14.2", + "semver": "^6.1.2" + }, + "peerDependencies": { + "@babel/core": "^7.4.0-0" + } + }, + "node_modules/@babel/helper-explode-assignable-expression": { + "version": "7.13.0", + "resolved": "https://registry.npmjs.org/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.13.0.tgz", + "integrity": "sha512-qS0peLTDP8kOisG1blKbaoBg/o9OSa1qoumMjTK5pM+KDTtpxpsiubnCGP34vK8BXGcb2M9eigwgvoJryrzwWA==", + "dependencies": { + "@babel/types": "^7.13.0" + } + }, + "node_modules/@babel/helper-function-name": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.12.13.tgz", + "integrity": "sha512-TZvmPn0UOqmvi5G4vvw0qZTpVptGkB1GL61R6lKvrSdIxGm5Pky7Q3fpKiIkQCAtRCBUwB0PaThlx9vebCDSwA==", + "dependencies": { + "@babel/helper-get-function-arity": "^7.12.13", + "@babel/template": "^7.12.13", + "@babel/types": "^7.12.13" + } + }, + "node_modules/@babel/helper-get-function-arity": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.12.13.tgz", + "integrity": "sha512-DjEVzQNz5LICkzN0REdpD5prGoidvbdYk1BVgRUOINaWJP2t6avB27X1guXK1kXNrX0WMfsrm1A/ZBthYuIMQg==", + "dependencies": { + "@babel/types": "^7.12.13" + } + }, + "node_modules/@babel/helper-hoist-variables": { + "version": "7.13.16", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.13.16.tgz", + "integrity": "sha512-1eMtTrXtrwscjcAeO4BVK+vvkxaLJSPFz1w1KLawz6HLNi9bPFGBNwwDyVfiu1Tv/vRRFYfoGaKhmAQPGPn5Wg==", + "dependencies": { + "@babel/traverse": "^7.13.15", + "@babel/types": "^7.13.16" + } + }, + "node_modules/@babel/helper-member-expression-to-functions": { + "version": "7.13.12", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.13.12.tgz", + "integrity": "sha512-48ql1CLL59aKbU94Y88Xgb2VFy7a95ykGRbJJaaVv+LX5U8wFpLfiGXJJGUozsmA1oEh/o5Bp60Voq7ACyA/Sw==", + "dependencies": { + "@babel/types": "^7.13.12" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.13.12", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.13.12.tgz", + "integrity": "sha512-4cVvR2/1B693IuOvSI20xqqa/+bl7lqAMR59R4iu39R9aOX8/JoYY1sFaNvUMyMBGnHdwvJgUrzNLoUZxXypxA==", + "dependencies": { + "@babel/types": "^7.13.12" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.14.0", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.14.0.tgz", + "integrity": "sha512-L40t9bxIuGOfpIGA3HNkJhU9qYrf4y5A5LUSw7rGMSn+pcG8dfJ0g6Zval6YJGd2nEjI7oP00fRdnhLKndx6bw==", + "dependencies": { + "@babel/helper-module-imports": "^7.13.12", + "@babel/helper-replace-supers": "^7.13.12", + "@babel/helper-simple-access": "^7.13.12", + "@babel/helper-split-export-declaration": "^7.12.13", + "@babel/helper-validator-identifier": "^7.14.0", + "@babel/template": "^7.12.13", + "@babel/traverse": "^7.14.0", + "@babel/types": "^7.14.0" + } + }, + "node_modules/@babel/helper-optimise-call-expression": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.12.13.tgz", + "integrity": "sha512-BdWQhoVJkp6nVjB7nkFWcn43dkprYauqtk++Py2eaf/GRDFm5BxRqEIZCiHlZUGAVmtwKcsVL1dC68WmzeFmiA==", + "dependencies": { + "@babel/types": "^7.12.13" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.13.0", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.13.0.tgz", + "integrity": "sha512-ZPafIPSwzUlAoWT8DKs1W2VyF2gOWthGd5NGFMsBcMMol+ZhK+EQY/e6V96poa6PA/Bh+C9plWN0hXO1uB8AfQ==" + }, + "node_modules/@babel/helper-remap-async-to-generator": { + "version": "7.13.0", + "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.13.0.tgz", + "integrity": "sha512-pUQpFBE9JvC9lrQbpX0TmeNIy5s7GnZjna2lhhcHC7DzgBs6fWn722Y5cfwgrtrqc7NAJwMvOa0mKhq6XaE4jg==", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.12.13", + "@babel/helper-wrap-function": "^7.13.0", + "@babel/types": "^7.13.0" + } + }, + "node_modules/@babel/helper-replace-supers": { + "version": "7.13.12", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.13.12.tgz", + "integrity": "sha512-Gz1eiX+4yDO8mT+heB94aLVNCL+rbuT2xy4YfyNqu8F+OI6vMvJK891qGBTqL9Uc8wxEvRW92Id6G7sDen3fFw==", + "dependencies": { + "@babel/helper-member-expression-to-functions": "^7.13.12", + "@babel/helper-optimise-call-expression": "^7.12.13", + "@babel/traverse": "^7.13.0", + "@babel/types": "^7.13.12" + } + }, + "node_modules/@babel/helper-simple-access": { + "version": "7.13.12", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.13.12.tgz", + "integrity": "sha512-7FEjbrx5SL9cWvXioDbnlYTppcZGuCY6ow3/D5vMggb2Ywgu4dMrpTJX0JdQAIcRRUElOIxF3yEooa9gUb9ZbA==", + "dependencies": { + "@babel/types": "^7.13.12" + } + }, + "node_modules/@babel/helper-skip-transparent-expression-wrappers": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.12.1.tgz", + "integrity": "sha512-Mf5AUuhG1/OCChOJ/HcADmvcHM42WJockombn8ATJG3OnyiSxBK/Mm5x78BQWvmtXZKHgbjdGL2kin/HOLlZGA==", + "dependencies": { + "@babel/types": "^7.12.1" + } + }, + "node_modules/@babel/helper-split-export-declaration": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.12.13.tgz", + "integrity": "sha512-tCJDltF83htUtXx5NLcaDqRmknv652ZWCHyoTETf1CXYJdPC7nohZohjUgieXhv0hTJdRf2FjDueFehdNucpzg==", + "dependencies": { + "@babel/types": "^7.12.13" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.14.0", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.14.0.tgz", + "integrity": "sha512-V3ts7zMSu5lfiwWDVWzRDGIN+lnCEUdaXgtVHJgLb1rGaA6jMrtB9EmE7L18foXJIE8Un/A/h6NJfGQp/e1J4A==" + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.12.17", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.12.17.tgz", + "integrity": "sha512-TopkMDmLzq8ngChwRlyjR6raKD6gMSae4JdYDB8bByKreQgG0RBTuKe9LRxW3wFtUnjxOPRKBDwEH6Mg5KeDfw==" + }, + "node_modules/@babel/helper-wrap-function": { + "version": "7.13.0", + "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.13.0.tgz", + "integrity": "sha512-1UX9F7K3BS42fI6qd2A4BjKzgGjToscyZTdp1DjknHLCIvpgne6918io+aL5LXFcER/8QWiwpoY902pVEqgTXA==", + "dependencies": { + "@babel/helper-function-name": "^7.12.13", + "@babel/template": "^7.12.13", + "@babel/traverse": "^7.13.0", + "@babel/types": "^7.13.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.14.0", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.14.0.tgz", + "integrity": "sha512-+ufuXprtQ1D1iZTO/K9+EBRn+qPWMJjZSw/S0KlFrxCw4tkrzv9grgpDHkY9MeQTjTY8i2sp7Jep8DfU6tN9Mg==", + "dependencies": { + "@babel/template": "^7.12.13", + "@babel/traverse": "^7.14.0", + "@babel/types": "^7.14.0" + } + }, + "node_modules/@babel/highlight": { + "version": "7.14.0", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.14.0.tgz", + "integrity": "sha512-YSCOwxvTYEIMSGaBQb5kDDsCopDdiUGsqpatp3fOlI4+2HQSkTmEVWnVuySdAC5EWCqSWWTv0ib63RjR7dTBdg==", + "dependencies": { + "@babel/helper-validator-identifier": "^7.14.0", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.14.1", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.14.1.tgz", + "integrity": "sha512-muUGEKu8E/ftMTPlNp+mc6zL3E9zKWmF5sDHZ5MSsoTP9Wyz64AhEf9kD08xYJ7w6Hdcu8H550ircnPyWSIF0Q==", + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-proposal-async-generator-functions": { + "version": "7.13.15", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.13.15.tgz", + "integrity": "sha512-VapibkWzFeoa6ubXy/NgV5U2U4MVnUlvnx6wo1XhlsaTrLYWE0UFpDQsVrmn22q5CzeloqJ8gEMHSKxuee6ZdA==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.13.0", + "@babel/helper-remap-async-to-generator": "^7.13.0", + "@babel/plugin-syntax-async-generators": "^7.8.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-class-properties": { + "version": "7.13.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.13.0.tgz", + "integrity": "sha512-KnTDjFNC1g+45ka0myZNvSBFLhNCLN+GeGYLDEA8Oq7MZ6yMgfLoIRh86GRT0FjtJhZw8JyUskP9uvj5pHM9Zg==", + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.13.0", + "@babel/helper-plugin-utils": "^7.13.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-dynamic-import": { + "version": "7.13.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.13.8.tgz", + "integrity": "sha512-ONWKj0H6+wIRCkZi9zSbZtE/r73uOhMVHh256ys0UzfM7I3d4n+spZNWjOnJv2gzopumP2Wxi186vI8N0Y2JyQ==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.13.0", + "@babel/plugin-syntax-dynamic-import": "^7.8.3" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-export-namespace-from": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-export-namespace-from/-/plugin-proposal-export-namespace-from-7.12.13.tgz", + "integrity": "sha512-INAgtFo4OnLN3Y/j0VwAgw3HDXcDtX+C/erMvWzuV9v71r7urb6iyMXu7eM9IgLr1ElLlOkaHjJ0SbCmdOQ3Iw==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.12.13", + "@babel/plugin-syntax-export-namespace-from": "^7.8.3" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-json-strings": { + "version": "7.13.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.13.8.tgz", + "integrity": "sha512-w4zOPKUFPX1mgvTmL/fcEqy34hrQ1CRcGxdphBc6snDnnqJ47EZDIyop6IwXzAC8G916hsIuXB2ZMBCExC5k7Q==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.13.0", + "@babel/plugin-syntax-json-strings": "^7.8.3" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-logical-assignment-operators": { + "version": "7.13.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.13.8.tgz", + "integrity": "sha512-aul6znYB4N4HGweImqKn59Su9RS8lbUIqxtXTOcAGtNIDczoEFv+l1EhmX8rUBp3G1jMjKJm8m0jXVp63ZpS4A==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.13.0", + "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-nullish-coalescing-operator": { + "version": "7.13.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.13.8.tgz", + "integrity": "sha512-iePlDPBn//UhxExyS9KyeYU7RM9WScAG+D3Hhno0PLJebAEpDZMocbDe64eqynhNAnwz/vZoL/q/QB2T1OH39A==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.13.0", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-numeric-separator": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.12.13.tgz", + "integrity": "sha512-O1jFia9R8BUCl3ZGB7eitaAPu62TXJRHn7rh+ojNERCFyqRwJMTmhz+tJ+k0CwI6CLjX/ee4qW74FSqlq9I35w==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.12.13", + "@babel/plugin-syntax-numeric-separator": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-object-rest-spread": { + "version": "7.13.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.13.8.tgz", + "integrity": "sha512-DhB2EuB1Ih7S3/IRX5AFVgZ16k3EzfRbq97CxAVI1KSYcW+lexV8VZb7G7L8zuPVSdQMRn0kiBpf/Yzu9ZKH0g==", + "dependencies": { + "@babel/compat-data": "^7.13.8", + "@babel/helper-compilation-targets": "^7.13.8", + "@babel/helper-plugin-utils": "^7.13.0", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-transform-parameters": "^7.13.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-optional-catch-binding": { + "version": "7.13.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.13.8.tgz", + "integrity": "sha512-0wS/4DUF1CuTmGo+NiaHfHcVSeSLj5S3e6RivPTg/2k3wOv3jO35tZ6/ZWsQhQMvdgI7CwphjQa/ccarLymHVA==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.13.0", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-optional-chaining": { + "version": "7.13.12", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.13.12.tgz", + "integrity": "sha512-fcEdKOkIB7Tf4IxrgEVeFC4zeJSTr78no9wTdBuZZbqF64kzllU0ybo2zrzm7gUQfxGhBgq4E39oRs8Zx/RMYQ==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.13.0", + "@babel/helper-skip-transparent-expression-wrappers": "^7.12.1", + "@babel/plugin-syntax-optional-chaining": "^7.8.3" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-private-methods": { + "version": "7.13.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.13.0.tgz", + "integrity": "sha512-MXyyKQd9inhx1kDYPkFRVOBXQ20ES8Pto3T7UZ92xj2mY0EVD8oAVzeyYuVfy/mxAdTSIayOvg+aVzcHV2bn6Q==", + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.13.0", + "@babel/helper-plugin-utils": "^7.13.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-unicode-property-regex": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.12.13.tgz", + "integrity": "sha512-XyJmZidNfofEkqFV5VC/bLabGmO5QzenPO/YOfGuEbgU+2sSwMmio3YLb4WtBgcmmdwZHyVyv8on77IUjQ5Gvg==", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.12.13", + "@babel/helper-plugin-utils": "^7.12.13" + }, + "engines": { + "node": ">=4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-async-generators": { + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", + "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-class-properties": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", + "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.12.13" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-dynamic-import": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz", + "integrity": "sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-export-namespace-from": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-export-namespace-from/-/plugin-syntax-export-namespace-from-7.8.3.tgz", + "integrity": "sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.3" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-flow": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-flow/-/plugin-syntax-flow-7.12.13.tgz", + "integrity": "sha512-J/RYxnlSLXZLVR7wTRsozxKT8qbsx1mNKJzXEEjQ0Kjx1ZACcyHgbanNWNCFtc36IzuWhYWPpvJFFoexoOWFmA==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.12.13" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-json-strings": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", + "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-jsx": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.12.13.tgz", + "integrity": "sha512-d4HM23Q1K7oq/SLNmG6mRt85l2csmQ0cHRaxRXjKW0YFdEXqlZ5kzFQKH5Uc3rDJECgu+yCRgPkG04Mm98R/1g==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.12.13" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-logical-assignment-operators": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", + "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-nullish-coalescing-operator": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", + "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-numeric-separator": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", + "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-object-rest-spread": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", + "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-optional-catch-binding": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", + "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-optional-chaining": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", + "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-top-level-await": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.12.13.tgz", + "integrity": "sha512-A81F9pDwyS7yM//KwbCSDqy3Uj4NMIurtplxphWxoYtNPov7cJsDkAFNNyVlIZ3jwGycVsurZ+LtOA8gZ376iQ==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.12.13" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-arrow-functions": { + "version": "7.13.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.13.0.tgz", + "integrity": "sha512-96lgJagobeVmazXFaDrbmCLQxBysKu7U6Do3mLsx27gf5Dk85ezysrs2BZUpXD703U/Su1xTBDxxar2oa4jAGg==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.13.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-async-to-generator": { + "version": "7.13.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.13.0.tgz", + "integrity": "sha512-3j6E004Dx0K3eGmhxVJxwwI89CTJrce7lg3UrtFuDAVQ/2+SJ/h/aSFOeE6/n0WB1GsOffsJp6MnPQNQ8nmwhg==", + "dependencies": { + "@babel/helper-module-imports": "^7.12.13", + "@babel/helper-plugin-utils": "^7.13.0", + "@babel/helper-remap-async-to-generator": "^7.13.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-block-scoped-functions": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.12.13.tgz", + "integrity": "sha512-zNyFqbc3kI/fVpqwfqkg6RvBgFpC4J18aKKMmv7KdQ/1GgREapSJAykLMVNwfRGO3BtHj3YQZl8kxCXPcVMVeg==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.12.13" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-block-scoping": { + "version": "7.14.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.14.1.tgz", + "integrity": "sha512-2mQXd0zBrwfp0O1moWIhPpEeTKDvxyHcnma3JATVP1l+CctWBuot6OJG8LQ4DnBj4ZZPSmlb/fm4mu47EOAnVA==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.13.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-classes": { + "version": "7.13.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.13.0.tgz", + "integrity": "sha512-9BtHCPUARyVH1oXGcSJD3YpsqRLROJx5ZNP6tN5vnk17N0SVf9WCtf8Nuh1CFmgByKKAIMstitKduoCmsaDK5g==", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.12.13", + "@babel/helper-function-name": "^7.12.13", + "@babel/helper-optimise-call-expression": "^7.12.13", + "@babel/helper-plugin-utils": "^7.13.0", + "@babel/helper-replace-supers": "^7.13.0", + "@babel/helper-split-export-declaration": "^7.12.13", + "globals": "^11.1.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-computed-properties": { + "version": "7.13.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.13.0.tgz", + "integrity": "sha512-RRqTYTeZkZAz8WbieLTvKUEUxZlUTdmL5KGMyZj7FnMfLNKV4+r5549aORG/mgojRmFlQMJDUupwAMiF2Q7OUg==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.13.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-destructuring": { + "version": "7.13.17", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.13.17.tgz", + "integrity": "sha512-UAUqiLv+uRLO+xuBKKMEpC+t7YRNVRqBsWWq1yKXbBZBje/t3IXCiSinZhjn/DC3qzBfICeYd2EFGEbHsh5RLA==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.13.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-dotall-regex": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.12.13.tgz", + "integrity": "sha512-foDrozE65ZFdUC2OfgeOCrEPTxdB3yjqxpXh8CH+ipd9CHd4s/iq81kcUpyH8ACGNEPdFqbtzfgzbT/ZGlbDeQ==", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.12.13", + "@babel/helper-plugin-utils": "^7.12.13" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-duplicate-keys": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.12.13.tgz", + "integrity": "sha512-NfADJiiHdhLBW3pulJlJI2NB0t4cci4WTZ8FtdIuNc2+8pslXdPtRRAEWqUY+m9kNOk2eRYbTAOipAxlrOcwwQ==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.12.13" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-exponentiation-operator": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.12.13.tgz", + "integrity": "sha512-fbUelkM1apvqez/yYx1/oICVnGo2KM5s63mhGylrmXUxK/IAXSIf87QIxVfZldWf4QsOafY6vV3bX8aMHSvNrA==", + "dependencies": { + "@babel/helper-builder-binary-assignment-operator-visitor": "^7.12.13", + "@babel/helper-plugin-utils": "^7.12.13" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-flow-strip-types": { + "version": "7.13.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-flow-strip-types/-/plugin-transform-flow-strip-types-7.13.0.tgz", + "integrity": "sha512-EXAGFMJgSX8gxWD7PZtW/P6M+z74jpx3wm/+9pn+c2dOawPpBkUX7BrfyPvo6ZpXbgRIEuwgwDb/MGlKvu2pOg==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.13.0", + "@babel/plugin-syntax-flow": "^7.12.13" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-for-of": { + "version": "7.13.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.13.0.tgz", + "integrity": "sha512-IHKT00mwUVYE0zzbkDgNRP6SRzvfGCYsOxIRz8KsiaaHCcT9BWIkO+H9QRJseHBLOGBZkHUdHiqj6r0POsdytg==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.13.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-function-name": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.12.13.tgz", + "integrity": "sha512-6K7gZycG0cmIwwF7uMK/ZqeCikCGVBdyP2J5SKNCXO5EOHcqi+z7Jwf8AmyDNcBgxET8DrEtCt/mPKPyAzXyqQ==", + "dependencies": { + "@babel/helper-function-name": "^7.12.13", + "@babel/helper-plugin-utils": "^7.12.13" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-literals": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.12.13.tgz", + "integrity": "sha512-FW+WPjSR7hiUxMcKqyNjP05tQ2kmBCdpEpZHY1ARm96tGQCCBvXKnpjILtDplUnJ/eHZ0lALLM+d2lMFSpYJrQ==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.12.13" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-member-expression-literals": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.12.13.tgz", + "integrity": "sha512-kxLkOsg8yir4YeEPHLuO2tXP9R/gTjpuTOjshqSpELUN3ZAg2jfDnKUvzzJxObun38sw3wm4Uu69sX/zA7iRvg==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.12.13" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-amd": { + "version": "7.14.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.14.0.tgz", + "integrity": "sha512-CF4c5LX4LQ03LebQxJ5JZes2OYjzBuk1TdiF7cG7d5dK4lAdw9NZmaxq5K/mouUdNeqwz3TNjnW6v01UqUNgpQ==", + "dependencies": { + "@babel/helper-module-transforms": "^7.14.0", + "@babel/helper-plugin-utils": "^7.13.0", + "babel-plugin-dynamic-import-node": "^2.3.3" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-commonjs": { + "version": "7.14.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.14.0.tgz", + "integrity": "sha512-EX4QePlsTaRZQmw9BsoPeyh5OCtRGIhwfLquhxGp5e32w+dyL8htOcDwamlitmNFK6xBZYlygjdye9dbd9rUlQ==", + "dependencies": { + "@babel/helper-module-transforms": "^7.14.0", + "@babel/helper-plugin-utils": "^7.13.0", + "@babel/helper-simple-access": "^7.13.12", + "babel-plugin-dynamic-import-node": "^2.3.3" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-systemjs": { + "version": "7.13.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.13.8.tgz", + "integrity": "sha512-hwqctPYjhM6cWvVIlOIe27jCIBgHCsdH2xCJVAYQm7V5yTMoilbVMi9f6wKg0rpQAOn6ZG4AOyvCqFF/hUh6+A==", + "dependencies": { + "@babel/helper-hoist-variables": "^7.13.0", + "@babel/helper-module-transforms": "^7.13.0", + "@babel/helper-plugin-utils": "^7.13.0", + "@babel/helper-validator-identifier": "^7.12.11", + "babel-plugin-dynamic-import-node": "^2.3.3" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-umd": { + "version": "7.14.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.14.0.tgz", + "integrity": "sha512-nPZdnWtXXeY7I87UZr9VlsWme3Y0cfFFE41Wbxz4bbaexAjNMInXPFUpRRUJ8NoMm0Cw+zxbqjdPmLhcjfazMw==", + "dependencies": { + "@babel/helper-module-transforms": "^7.14.0", + "@babel/helper-plugin-utils": "^7.13.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-named-capturing-groups-regex": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.12.13.tgz", + "integrity": "sha512-Xsm8P2hr5hAxyYblrfACXpQKdQbx4m2df9/ZZSQ8MAhsadw06+jW7s9zsSw6he+mJZXRlVMyEnVktJo4zjk1WA==", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.12.13" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-transform-new-target": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.12.13.tgz", + "integrity": "sha512-/KY2hbLxrG5GTQ9zzZSc3xWiOy379pIETEhbtzwZcw9rvuaVV4Fqy7BYGYOWZnaoXIQYbbJ0ziXLa/sKcGCYEQ==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.12.13" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-object-super": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.12.13.tgz", + "integrity": "sha512-JzYIcj3XtYspZDV8j9ulnoMPZZnF/Cj0LUxPOjR89BdBVx+zYJI9MdMIlUZjbXDX+6YVeS6I3e8op+qQ3BYBoQ==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.12.13", + "@babel/helper-replace-supers": "^7.12.13" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-parameters": { + "version": "7.13.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.13.0.tgz", + "integrity": "sha512-Jt8k/h/mIwE2JFEOb3lURoY5C85ETcYPnbuAJ96zRBzh1XHtQZfs62ChZ6EP22QlC8c7Xqr9q+e1SU5qttwwjw==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.13.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-property-literals": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.12.13.tgz", + "integrity": "sha512-nqVigwVan+lR+g8Fj8Exl0UQX2kymtjcWfMOYM1vTYEKujeyv2SkMgazf2qNcK7l4SDiKyTA/nHCPqL4e2zo1A==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.12.13" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx": { + "version": "7.13.12", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.13.12.tgz", + "integrity": "sha512-jcEI2UqIcpCqB5U5DRxIl0tQEProI2gcu+g8VTIqxLO5Iidojb4d77q+fwGseCvd8af/lJ9masp4QWzBXFE2xA==", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.12.13", + "@babel/helper-module-imports": "^7.13.12", + "@babel/helper-plugin-utils": "^7.13.0", + "@babel/plugin-syntax-jsx": "^7.12.13", + "@babel/types": "^7.13.12" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-regenerator": { + "version": "7.13.15", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.13.15.tgz", + "integrity": "sha512-Bk9cOLSz8DiurcMETZ8E2YtIVJbFCPGW28DJWUakmyVWtQSm6Wsf0p4B4BfEr/eL2Nkhe/CICiUiMOCi1TPhuQ==", + "dependencies": { + "regenerator-transform": "^0.14.2" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-reserved-words": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.12.13.tgz", + "integrity": "sha512-xhUPzDXxZN1QfiOy/I5tyye+TRz6lA7z6xaT4CLOjPRMVg1ldRf0LHw0TDBpYL4vG78556WuHdyO9oi5UmzZBg==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.12.13" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-shorthand-properties": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.12.13.tgz", + "integrity": "sha512-xpL49pqPnLtf0tVluuqvzWIgLEhuPpZzvs2yabUHSKRNlN7ScYU7aMlmavOeyXJZKgZKQRBlh8rHbKiJDraTSw==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.12.13" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-spread": { + "version": "7.13.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.13.0.tgz", + "integrity": "sha512-V6vkiXijjzYeFmQTr3dBxPtZYLPcUfY34DebOU27jIl2M/Y8Egm52Hw82CSjjPqd54GTlJs5x+CR7HeNr24ckg==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.13.0", + "@babel/helper-skip-transparent-expression-wrappers": "^7.12.1" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-sticky-regex": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.12.13.tgz", + "integrity": "sha512-Jc3JSaaWT8+fr7GRvQP02fKDsYk4K/lYwWq38r/UGfaxo89ajud321NH28KRQ7xy1Ybc0VUE5Pz8psjNNDUglg==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.12.13" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-template-literals": { + "version": "7.13.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.13.0.tgz", + "integrity": "sha512-d67umW6nlfmr1iehCcBv69eSUSySk1EsIS8aTDX4Xo9qajAh6mYtcl4kJrBkGXuxZPEgVr7RVfAvNW6YQkd4Mw==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.13.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-typeof-symbol": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.12.13.tgz", + "integrity": "sha512-eKv/LmUJpMnu4npgfvs3LiHhJua5fo/CysENxa45YCQXZwKnGCQKAg87bvoqSW1fFT+HA32l03Qxsm8ouTY3ZQ==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.12.13" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-unicode-escapes": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.12.13.tgz", + "integrity": "sha512-0bHEkdwJ/sN/ikBHfSmOXPypN/beiGqjo+o4/5K+vxEFNPRPdImhviPakMKG4x96l85emoa0Z6cDflsdBusZbw==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.12.13" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-unicode-regex": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.12.13.tgz", + "integrity": "sha512-mDRzSNY7/zopwisPZ5kM9XKCfhchqIYwAKRERtEnhYscZB79VRekuRSoYbN0+KVe3y8+q1h6A4svXtP7N+UoCA==", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.12.13", + "@babel/helper-plugin-utils": "^7.12.13" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/polyfill": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/polyfill/-/polyfill-7.12.1.tgz", + "integrity": "sha512-X0pi0V6gxLi6lFZpGmeNa4zxtwEmCs42isWLNjZZDE0Y8yVfgu0T2OAHlzBbdYlqbW/YXVvoBHpATEM+goCj8g==", + "deprecated": "🚨 This package has been deprecated in favor of separate inclusion of a polyfill and regenerator-runtime (when needed). See the @babel/polyfill docs (https://babeljs.io/docs/en/babel-polyfill) for more information.", + "dev": true, + "dependencies": { + "core-js": "^2.6.5", + "regenerator-runtime": "^0.13.4" + } + }, + "node_modules/@babel/preset-env": { + "version": "7.13.8", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.13.8.tgz", + "integrity": "sha512-Sso1xOpV4S3ofnxW2DsWTE5ziRk62jEAKLGuQ+EJHC+YHTbFG38QUTixO3JVa1cYET9gkJhO1pMu+/+2dDhKvw==", + "dependencies": { + "@babel/compat-data": "^7.13.8", + "@babel/helper-compilation-targets": "^7.13.8", + "@babel/helper-plugin-utils": "^7.13.0", + "@babel/helper-validator-option": "^7.12.17", + "@babel/plugin-proposal-async-generator-functions": "^7.13.8", + "@babel/plugin-proposal-class-properties": "^7.13.0", + "@babel/plugin-proposal-dynamic-import": "^7.13.8", + "@babel/plugin-proposal-export-namespace-from": "^7.12.13", + "@babel/plugin-proposal-json-strings": "^7.13.8", + "@babel/plugin-proposal-logical-assignment-operators": "^7.13.8", + "@babel/plugin-proposal-nullish-coalescing-operator": "^7.13.8", + "@babel/plugin-proposal-numeric-separator": "^7.12.13", + "@babel/plugin-proposal-object-rest-spread": "^7.13.8", + "@babel/plugin-proposal-optional-catch-binding": "^7.13.8", + "@babel/plugin-proposal-optional-chaining": "^7.13.8", + "@babel/plugin-proposal-private-methods": "^7.13.0", + "@babel/plugin-proposal-unicode-property-regex": "^7.12.13", + "@babel/plugin-syntax-async-generators": "^7.8.4", + "@babel/plugin-syntax-class-properties": "^7.12.13", + "@babel/plugin-syntax-dynamic-import": "^7.8.3", + "@babel/plugin-syntax-export-namespace-from": "^7.8.3", + "@babel/plugin-syntax-json-strings": "^7.8.3", + "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", + "@babel/plugin-syntax-numeric-separator": "^7.10.4", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", + "@babel/plugin-syntax-optional-chaining": "^7.8.3", + "@babel/plugin-syntax-top-level-await": "^7.12.13", + "@babel/plugin-transform-arrow-functions": "^7.13.0", + "@babel/plugin-transform-async-to-generator": "^7.13.0", + "@babel/plugin-transform-block-scoped-functions": "^7.12.13", + "@babel/plugin-transform-block-scoping": "^7.12.13", + "@babel/plugin-transform-classes": "^7.13.0", + "@babel/plugin-transform-computed-properties": "^7.13.0", + "@babel/plugin-transform-destructuring": "^7.13.0", + "@babel/plugin-transform-dotall-regex": "^7.12.13", + "@babel/plugin-transform-duplicate-keys": "^7.12.13", + "@babel/plugin-transform-exponentiation-operator": "^7.12.13", + "@babel/plugin-transform-for-of": "^7.13.0", + "@babel/plugin-transform-function-name": "^7.12.13", + "@babel/plugin-transform-literals": "^7.12.13", + "@babel/plugin-transform-member-expression-literals": "^7.12.13", + "@babel/plugin-transform-modules-amd": "^7.13.0", + "@babel/plugin-transform-modules-commonjs": "^7.13.8", + "@babel/plugin-transform-modules-systemjs": "^7.13.8", + "@babel/plugin-transform-modules-umd": "^7.13.0", + "@babel/plugin-transform-named-capturing-groups-regex": "^7.12.13", + "@babel/plugin-transform-new-target": "^7.12.13", + "@babel/plugin-transform-object-super": "^7.12.13", + "@babel/plugin-transform-parameters": "^7.13.0", + "@babel/plugin-transform-property-literals": "^7.12.13", + "@babel/plugin-transform-regenerator": "^7.12.13", + "@babel/plugin-transform-reserved-words": "^7.12.13", + "@babel/plugin-transform-shorthand-properties": "^7.12.13", + "@babel/plugin-transform-spread": "^7.13.0", + "@babel/plugin-transform-sticky-regex": "^7.12.13", + "@babel/plugin-transform-template-literals": "^7.13.0", + "@babel/plugin-transform-typeof-symbol": "^7.12.13", + "@babel/plugin-transform-unicode-escapes": "^7.12.13", + "@babel/plugin-transform-unicode-regex": "^7.12.13", + "@babel/preset-modules": "^0.1.4", + "@babel/types": "^7.13.0", + "babel-plugin-polyfill-corejs2": "^0.1.4", + "babel-plugin-polyfill-corejs3": "^0.1.3", + "babel-plugin-polyfill-regenerator": "^0.1.2", + "core-js-compat": "^3.9.0", + "semver": "^6.3.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/preset-modules": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.4.tgz", + "integrity": "sha512-J36NhwnfdzpmH41M1DrnkkgAqhZaqr/NBdPfQ677mLzlaXo+oDiv1deyCDtgAhz8p328otdob0Du7+xgHGZbKg==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.0.0", + "@babel/plugin-proposal-unicode-property-regex": "^7.4.4", + "@babel/plugin-transform-dotall-regex": "^7.4.4", + "@babel/types": "^7.4.4", + "esutils": "^2.0.2" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/runtime": { + "version": "7.14.0", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.14.0.tgz", + "integrity": "sha512-JELkvo/DlpNdJ7dlyw/eY7E0suy5i5GQH+Vlxaq1nsNJ+H7f4Vtv3jMeCEgRhZZQFXTjldYfQgv2qmM6M1v5wA==", + "dependencies": { + "regenerator-runtime": "^0.13.4" + } + }, + "node_modules/@babel/runtime-corejs3": { + "version": "7.14.8", + "resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.14.8.tgz", + "integrity": "sha512-4dMD5QRBkumn45oweR0SxoNtt15oz3BUBAQ8cIx7HJqZTtE8zjpM0My8aHJHVnyf4XfRg6DNzaE1080WLBiC1w==", + "optional": true, + "dependencies": { + "core-js-pure": "^3.15.0", + "regenerator-runtime": "^0.13.4" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/template": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.12.13.tgz", + "integrity": "sha512-/7xxiGA57xMo/P2GVvdEumr8ONhFOhfgq2ihK3h1e6THqzTAkHbkXgB0xI9yeTfIUoH3+oAeHhqm/I43OTbbjA==", + "dependencies": { + "@babel/code-frame": "^7.12.13", + "@babel/parser": "^7.12.13", + "@babel/types": "^7.12.13" + } + }, + "node_modules/@babel/traverse": { + "version": "7.14.0", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.14.0.tgz", + "integrity": "sha512-dZ/a371EE5XNhTHomvtuLTUyx6UEoJmYX+DT5zBCQN3McHemsuIaKKYqsc/fs26BEkHs/lBZy0J571LP5z9kQA==", + "dependencies": { + "@babel/code-frame": "^7.12.13", + "@babel/generator": "^7.14.0", + "@babel/helper-function-name": "^7.12.13", + "@babel/helper-split-export-declaration": "^7.12.13", + "@babel/parser": "^7.14.0", + "@babel/types": "^7.14.0", + "debug": "^4.1.0", + "globals": "^11.1.0" + } + }, + "node_modules/@babel/types": { + "version": "7.14.1", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.14.1.tgz", + "integrity": "sha512-S13Qe85fzLs3gYRUnrpyeIrBJIMYv33qSTg1qoBwiG6nPKwUWAD9odSzWhEedpwOIzSEI6gbdQIWEMiCI42iBA==", + "dependencies": { + "@babel/helper-validator-identifier": "^7.14.0", + "to-fast-properties": "^2.0.0" + } + }, + "node_modules/@iarna/toml": { + "version": "2.2.5", + "resolved": "https://registry.npmjs.org/@iarna/toml/-/toml-2.2.5.tgz", + "integrity": "sha512-trnsAYxU3xnS1gPHPyU961coFyLkh4gAD/0zQ5mymY4yOZ+CYvsPqUbOFSw0aDM4y0tV7tiFxL/1XfXPNC6IPg==" + }, + "node_modules/@mapbox/geojson-area": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/@mapbox/geojson-area/-/geojson-area-0.2.2.tgz", + "integrity": "sha1-GNeBSqNr8j+7zDefjiaiKSfevxA=", + "dependencies": { + "wgs84": "0.0.0" + } + }, + "node_modules/@mapbox/geojson-rewind": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@mapbox/geojson-rewind/-/geojson-rewind-0.4.0.tgz", + "integrity": "sha512-b+1uPWBERW4Pet/969BNu61ZPDyH2ilIxBjJDFzxyS9TyszF9UrTQyYIl/G38clux3rtpAGGFSGTCSF/qR6UjA==", + "dependencies": { + "@mapbox/geojson-area": "0.2.2", + "concat-stream": "~1.6.0", + "minimist": "1.2.0", + "sharkdown": "^0.1.0" + }, + "bin": { + "geojson-rewind": "geojson-rewind" + } + }, + "node_modules/@mapbox/geojson-rewind/node_modules/concat-stream": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", + "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", + "engines": [ + "node >= 0.8" + ], + "dependencies": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^2.2.2", + "typedarray": "^0.0.6" + } + }, + "node_modules/@mapbox/geojson-rewind/node_modules/minimist": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=" + }, + "node_modules/@mrmlnc/readdir-enhanced": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/@mrmlnc/readdir-enhanced/-/readdir-enhanced-2.2.1.tgz", + "integrity": "sha512-bPHp6Ji8b41szTOcaP63VlnbbO5Ny6dwAATtY6JTjh5N2OLrb5Qk/Th5cRkRQhkWCt+EJsYrNB0MiL+Gpn6e3g==", + "dependencies": { + "call-me-maybe": "^1.0.1", + "glob-to-regexp": "^0.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@parcel/fs": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/@parcel/fs/-/fs-1.11.0.tgz", + "integrity": "sha512-86RyEqULbbVoeo8OLcv+LQ1Vq2PKBAvWTU9fCgALxuCTbbs5Ppcvll4Vr+Ko1AnmMzja/k++SzNAwJfeQXVlpA==", + "dependencies": { + "@parcel/utils": "^1.11.0", + "mkdirp": "^0.5.1", + "rimraf": "^2.6.2" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/@parcel/logger": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@parcel/logger/-/logger-1.11.1.tgz", + "integrity": "sha512-9NF3M6UVeP2udOBDILuoEHd8VrF4vQqoWHEafymO1pfSoOMfxrSJZw1MfyAAIUN/IFp9qjcpDCUbDZB+ioVevA==", + "dependencies": { + "@parcel/workers": "^1.11.0", + "chalk": "^2.1.0", + "grapheme-breaker": "^0.3.2", + "ora": "^2.1.0", + "strip-ansi": "^4.0.0" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/@parcel/utils": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/@parcel/utils/-/utils-1.11.0.tgz", + "integrity": "sha512-cA3p4jTlaMeOtAKR/6AadanOPvKeg8VwgnHhOyfi0yClD0TZS/hi9xu12w4EzA/8NtHu0g6o4RDfcNjqN8l1AQ==", + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/@parcel/watcher": { + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-1.12.1.tgz", + "integrity": "sha512-od+uCtCxC/KoNQAIE1vWx1YTyKYY+7CTrxBJPRh3cDWw/C0tCtlBMVlrbplscGoEpt6B27KhJDCv82PBxOERNA==", + "dependencies": { + "@parcel/utils": "^1.11.0", + "chokidar": "^2.1.5" + } + }, + "node_modules/@parcel/watcher/node_modules/anymatch": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", + "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", + "dependencies": { + "micromatch": "^3.1.4", + "normalize-path": "^2.1.1" + } + }, + "node_modules/@parcel/watcher/node_modules/anymatch/node_modules/normalize-path": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", + "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", + "dependencies": { + "remove-trailing-separator": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@parcel/watcher/node_modules/binary-extensions": { + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.1.tgz", + "integrity": "sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@parcel/watcher/node_modules/braces": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", + "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", + "dependencies": { + "arr-flatten": "^1.1.0", + "array-unique": "^0.3.2", + "extend-shallow": "^2.0.1", + "fill-range": "^4.0.0", + "isobject": "^3.0.1", + "repeat-element": "^1.1.2", + "snapdragon": "^0.8.1", + "snapdragon-node": "^2.0.1", + "split-string": "^3.0.2", + "to-regex": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@parcel/watcher/node_modules/braces/node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@parcel/watcher/node_modules/chokidar": { + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.8.tgz", + "integrity": "sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg==", + "deprecated": "Chokidar 2 will break on node v14+. Upgrade to chokidar 3 with 15x less dependencies.", + "dependencies": { + "anymatch": "^2.0.0", + "async-each": "^1.0.1", + "braces": "^2.3.2", + "glob-parent": "^3.1.0", + "inherits": "^2.0.3", + "is-binary-path": "^1.0.0", + "is-glob": "^4.0.0", + "normalize-path": "^3.0.0", + "path-is-absolute": "^1.0.0", + "readdirp": "^2.2.1", + "upath": "^1.1.1" + }, + "optionalDependencies": { + "fsevents": "^1.2.7" + } + }, + "node_modules/@parcel/watcher/node_modules/fill-range": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", + "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", + "dependencies": { + "extend-shallow": "^2.0.1", + "is-number": "^3.0.0", + "repeat-string": "^1.6.1", + "to-regex-range": "^2.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@parcel/watcher/node_modules/fill-range/node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@parcel/watcher/node_modules/fsevents": { + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.13.tgz", + "integrity": "sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw==", + "deprecated": "fsevents 1 will break on node v14+ and could be using insecure binaries. Upgrade to fsevents 2.", + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "dependencies": { + "bindings": "^1.5.0", + "nan": "^2.12.1" + }, + "engines": { + "node": ">= 4.0" + } + }, + "node_modules/@parcel/watcher/node_modules/glob-parent": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", + "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=", + "dependencies": { + "is-glob": "^3.1.0", + "path-dirname": "^1.0.0" + } + }, + "node_modules/@parcel/watcher/node_modules/glob-parent/node_modules/is-glob": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", + "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", + "dependencies": { + "is-extglob": "^2.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@parcel/watcher/node_modules/is-binary-path": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz", + "integrity": "sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg=", + "dependencies": { + "binary-extensions": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@parcel/watcher/node_modules/is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@parcel/watcher/node_modules/is-number/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@parcel/watcher/node_modules/micromatch": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", + "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", + "dependencies": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "braces": "^2.3.1", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "extglob": "^2.0.4", + "fragment-cache": "^0.2.1", + "kind-of": "^6.0.2", + "nanomatch": "^1.2.9", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@parcel/watcher/node_modules/readdirp": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz", + "integrity": "sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==", + "dependencies": { + "graceful-fs": "^4.1.11", + "micromatch": "^3.1.10", + "readable-stream": "^2.0.2" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/@parcel/watcher/node_modules/to-regex-range": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", + "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", + "dependencies": { + "is-number": "^3.0.0", + "repeat-string": "^1.6.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@parcel/workers": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/@parcel/workers/-/workers-1.11.0.tgz", + "integrity": "sha512-USSjRAAQYsZFlv43FUPdD+jEGML5/8oLF0rUzPQTtK4q9kvaXr49F5ZplyLz5lox78cLZ0TxN2bIDQ1xhOkulQ==", + "dependencies": { + "@parcel/utils": "^1.11.0", + "physical-cpu-count": "^2.0.0" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/@turf/along": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/along/-/along-6.3.0.tgz", + "integrity": "sha512-2j0nHp38IuzESyv5/9hLYM2MuUe155Kw390lkQtiLjhRtTeYQNEaRy+uhZhf3/DWrjGULH1HatLc5j0CmiwrJA==", + "dependencies": { + "@turf/bearing": "^6.3.0", + "@turf/destination": "^6.3.0", + "@turf/distance": "^6.3.0", + "@turf/helpers": "^6.3.0", + "@turf/invariant": "^6.3.0" + } + }, + "node_modules/@turf/angle": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/angle/-/angle-6.3.0.tgz", + "integrity": "sha512-wCWoK+7JKGYPZKYxdWwJJfqm1IQbUdOf4j5SENO6WJryXViM/ogRu2eAEqrmyrMYO84vonMSqiuPEuGoLqo9Xg==", + "dependencies": { + "@turf/bearing": "^6.3.0", + "@turf/helpers": "^6.3.0", + "@turf/invariant": "^6.3.0", + "@turf/rhumb-bearing": "^6.3.0" + } + }, + "node_modules/@turf/area": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/area/-/area-6.3.0.tgz", + "integrity": "sha512-Y1cYyAQ2fk94npdgOeMF4msc2uabHY1m7A7ntixf1I8rkyDd6/iHh1IMy1QsM+VZXAEwDwsXhu+ZFYd3Jkeg4A==", + "dependencies": { + "@turf/helpers": "^6.3.0", + "@turf/meta": "^6.3.0" + } + }, + "node_modules/@turf/bbox": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/bbox/-/bbox-6.3.0.tgz", + "integrity": "sha512-N4ue5Xopu1qieSHP2MA/CJGWHPKaTrVXQJjzHRNcY1vtsO126xbSaJhWUrFc5x5vVkXp0dcucGryO0r5m4o/KA==", + "dependencies": { + "@turf/helpers": "^6.3.0", + "@turf/meta": "^6.3.0" + } + }, + "node_modules/@turf/bbox-clip": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/bbox-clip/-/bbox-clip-6.3.0.tgz", + "integrity": "sha512-DCFs1MdX3P7SzZiBjT1kWBp4g0cfv8Yn2/Ccq3JP4iVaqNQJujPfe0WwZjjTdXLbLLFTjoxnCJBjy3WZDmLvlw==", + "dependencies": { + "@turf/helpers": "^6.3.0", + "@turf/invariant": "^6.3.0" + } + }, + "node_modules/@turf/bbox-polygon": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/bbox-polygon/-/bbox-polygon-6.3.0.tgz", + "integrity": "sha512-CCyTBM8LzGRu/lReNlgDyjRO8NojtJ7EPPvSl3bdKQbNFsCm25gwe7Y3xsaCkWLNn5g89lQJI9Izf9xdEsENjQ==", + "dependencies": { + "@turf/helpers": "^6.3.0" + } + }, + "node_modules/@turf/bearing": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/bearing/-/bearing-6.3.0.tgz", + "integrity": "sha512-apuUm9xN6VQLO33m7F2mmzlm3dHfeesJjMSzh9iehGtgmp1IaVndjdcIvs0ieiwm8bN9UhwXpfPtO3pV0n9SFw==", + "dependencies": { + "@turf/helpers": "^6.3.0", + "@turf/invariant": "^6.3.0" + } + }, + "node_modules/@turf/bezier-spline": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/bezier-spline/-/bezier-spline-6.3.0.tgz", + "integrity": "sha512-5kJv7zLjuZPhjO8Z/eNT68UHwiDru6ihn2He0VFrnSJQJZI8V/TFXCob7GxncYFlKk7uHru8iMXGxFe3Y3P44w==", + "dependencies": { + "@turf/helpers": "^6.3.0", + "@turf/invariant": "^6.3.0" + } + }, + "node_modules/@turf/boolean-clockwise": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/boolean-clockwise/-/boolean-clockwise-6.3.0.tgz", + "integrity": "sha512-zW0j8uPjBS5QJqNmJIeatTH02E1S7OCuBNBvkoOUPifC/c2xJ120a1r73prBj1zMFr6k3UCjwG9V8whUMxIAYA==", + "dependencies": { + "@turf/helpers": "^6.3.0", + "@turf/invariant": "^6.3.0" + } + }, + "node_modules/@turf/boolean-contains": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/boolean-contains/-/boolean-contains-6.3.0.tgz", + "integrity": "sha512-1MW7B5G5tIu1lnAv3pXyFzl75wfBYnbA2GhwHDb4okIXMhloy/r5uIqAZHo0fOXykKVJS/gIfA/MioKIftoTug==", + "dependencies": { + "@turf/bbox": "^6.3.0", + "@turf/boolean-point-in-polygon": "^6.3.0", + "@turf/boolean-point-on-line": "^6.3.0", + "@turf/helpers": "^6.3.0", + "@turf/invariant": "^6.3.0" + } + }, + "node_modules/@turf/boolean-crosses": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/boolean-crosses/-/boolean-crosses-6.3.0.tgz", + "integrity": "sha512-ajCuNSSqQPN2p3Y1ERX4E/wEsNn5JANI2uNgGOpVAeNX48prQGCBANcG2FTMMB+WVqq9iIdQ4eB5mEg6I8TS4w==", + "dependencies": { + "@turf/boolean-point-in-polygon": "^6.3.0", + "@turf/helpers": "^6.3.0", + "@turf/invariant": "^6.3.0", + "@turf/line-intersect": "^6.3.0", + "@turf/polygon-to-line": "^6.3.0" + } + }, + "node_modules/@turf/boolean-disjoint": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/boolean-disjoint/-/boolean-disjoint-6.3.0.tgz", + "integrity": "sha512-bVAwAJF05QPH0tf+qjR3kUcCyqTgYcCbXSMgXl6LQF6mSGuOutzNq1gCyRLCOdOcZtw4Oh4dqeP3ykwv8kDibw==", + "dependencies": { + "@turf/boolean-point-in-polygon": "^6.3.0", + "@turf/helpers": "^6.3.0", + "@turf/line-intersect": "^6.3.0", + "@turf/meta": "^6.3.0", + "@turf/polygon-to-line": "^6.3.0" + } + }, + "node_modules/@turf/boolean-equal": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/boolean-equal/-/boolean-equal-6.3.0.tgz", + "integrity": "sha512-eXr3oSHTvJYGyu/v57uNg0tnDHFnu+triwAaXtBh7lozt4d2riU8Ow71B+tjT9mBe/JRFfXIDsBWjbyB37y/6w==", + "dependencies": { + "@turf/clean-coords": "^6.3.0", + "@turf/helpers": "^6.3.0", + "@turf/invariant": "^6.3.0", + "geojson-equality": "0.1.6" + } + }, + "node_modules/@turf/boolean-intersects": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/boolean-intersects/-/boolean-intersects-6.3.0.tgz", + "integrity": "sha512-2pHOYqHSKDo0rzHTiqwdAaxa+tHLwr4NaTAjOpuN2hipv9bErzGtv3e5IYceJBnT0u4akK17NTn6qAr7/7g2aQ==", + "dependencies": { + "@turf/boolean-disjoint": "^6.3.0", + "@turf/helpers": "^6.3.0", + "@turf/meta": "^6.3.0" + } + }, + "node_modules/@turf/boolean-overlap": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/boolean-overlap/-/boolean-overlap-6.3.0.tgz", + "integrity": "sha512-rWh8JKTqlJ1m27FY8YeWcGoXutLyCVfSi2/8AOkXi2F+36P9GM4tHz19yKY3btbnHJTgSZf1xO2YhX2d0BmNqg==", + "dependencies": { + "@turf/helpers": "^6.3.0", + "@turf/invariant": "^6.3.0", + "@turf/line-intersect": "^6.3.0", + "@turf/line-overlap": "^6.3.0", + "@turf/meta": "^6.3.0", + "geojson-equality": "0.1.6" + } + }, + "node_modules/@turf/boolean-parallel": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/boolean-parallel/-/boolean-parallel-6.3.0.tgz", + "integrity": "sha512-p5YcKtVON6fTE3+pffw16QZyg3uXRmZ8CNxZM7lhGrJrPnny7BD2Kz1z2fp+8EElf00kjX2vFbDjDftte4Xh3g==", + "dependencies": { + "@turf/clean-coords": "^6.3.0", + "@turf/helpers": "^6.3.0", + "@turf/line-segment": "^6.3.0", + "@turf/rhumb-bearing": "^6.3.0" + } + }, + "node_modules/@turf/boolean-point-in-polygon": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/boolean-point-in-polygon/-/boolean-point-in-polygon-6.3.0.tgz", + "integrity": "sha512-NqFSsoE6OwhDK19IllDQRhEQEkF7UVEOlqH9vgS1fGg4T6NcyKvACJs05c9457tL7QSbV9ZS53f2qiLneFL+qg==", + "dependencies": { + "@turf/helpers": "^6.3.0", + "@turf/invariant": "^6.3.0" + } + }, + "node_modules/@turf/boolean-point-on-line": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/boolean-point-on-line/-/boolean-point-on-line-6.3.0.tgz", + "integrity": "sha512-eScH8sfKJVjfbEX5Hgkt1nA7A8DUoiYD1riUVqTp2xikujrMfnYRjFpL/UAo01v33cPKZlhCXp7NE86bdOSrYg==", + "dependencies": { + "@turf/helpers": "^6.3.0", + "@turf/invariant": "^6.3.0" + } + }, + "node_modules/@turf/boolean-within": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/boolean-within/-/boolean-within-6.3.0.tgz", + "integrity": "sha512-8XtVbzPp6J+lqZtDWVyIwSyVAVcnuie82ub56JEAhCf9w8FX5Db3qXQ76pFcOyy/woeXLZY/nIR58Q79PusrRw==", + "dependencies": { + "@turf/bbox": "^6.3.0", + "@turf/boolean-point-in-polygon": "^6.3.0", + "@turf/boolean-point-on-line": "^6.3.0", + "@turf/helpers": "^6.3.0", + "@turf/invariant": "^6.3.0" + } + }, + "node_modules/@turf/buffer": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/buffer/-/buffer-6.3.0.tgz", + "integrity": "sha512-B0GWgJzmTaaw1GvTd+Df+ToKSYphz9d6hPCOwXbE2vS5DdZryoxBfxQ32LSX/hW/vx7TLf7E4M0VJBb+Sn1DKA==", + "dependencies": { + "@turf/bbox": "^6.3.0", + "@turf/center": "^6.3.0", + "@turf/helpers": "^6.3.0", + "@turf/meta": "^6.3.0", + "@turf/projection": "^6.3.0", + "d3-geo": "1.7.1", + "turf-jsts": "*" + } + }, + "node_modules/@turf/center": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/center/-/center-6.3.0.tgz", + "integrity": "sha512-41g/ZYwoBs2PK7tpAHhf4D6llHdRvY827HLXCld5D0IOnzsWPqDk7WnV8P5uq4g/gyH1/WfKQYn5SgfSj4sSfw==", + "dependencies": { + "@turf/bbox": "^6.3.0", + "@turf/helpers": "^6.3.0" + } + }, + "node_modules/@turf/center-mean": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/center-mean/-/center-mean-6.3.0.tgz", + "integrity": "sha512-BZsqThJmc7wUTxPj7/RYztaegPntR2bBFDPTJ/C+qN8lnRhCccCZ81npYunriwMQC1kyXd1BChGMwjFh3jfB+Q==", + "dependencies": { + "@turf/bbox": "^6.3.0", + "@turf/helpers": "^6.3.0", + "@turf/meta": "^6.3.0" + } + }, + "node_modules/@turf/center-median": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/center-median/-/center-median-6.3.0.tgz", + "integrity": "sha512-jMQzp4YLIPDWKAMpvyRmNOLcoCHy/OMsLIv6odmfBJc6q+5GkulXz4QW61a5o6XZNDkZiYe9f0QgNGaKH+HTWg==", + "dependencies": { + "@turf/center-mean": "^6.3.0", + "@turf/centroid": "^6.3.0", + "@turf/distance": "^6.3.0", + "@turf/helpers": "^6.3.0", + "@turf/meta": "^6.3.0" + } + }, + "node_modules/@turf/center-of-mass": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/center-of-mass/-/center-of-mass-6.3.0.tgz", + "integrity": "sha512-dbiNo4VjNOskK/9hlifmb+cIsFgLqru3m/U1b+btDrliLzrFw3BEeLquZf3IZkOGMpVdIi5/F7IbkrPPz7HgWw==", + "dependencies": { + "@turf/centroid": "^6.3.0", + "@turf/convex": "^6.3.0", + "@turf/helpers": "^6.3.0", + "@turf/invariant": "^6.3.0", + "@turf/meta": "^6.3.0" + } + }, + "node_modules/@turf/centroid": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/centroid/-/centroid-6.3.0.tgz", + "integrity": "sha512-7KTyqhUEqXDoyR/nf/jAXiW8ZVszEnrp5XZkgYyrf2GWdSovSO0iCN1J3bE2jkJv7IWyeDmGYL61GGzuTSZS2Q==", + "dependencies": { + "@turf/helpers": "^6.3.0", + "@turf/meta": "^6.3.0" + } + }, + "node_modules/@turf/circle": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/circle/-/circle-6.3.0.tgz", + "integrity": "sha512-5N3J4YQr1efidvPgvtIQYpxb7gBVEoo00IFC0JNH6KqIVBMttFZw3Wsqor34ya91m58A5m6HTiz9Cdm1ktrEdw==", + "dependencies": { + "@turf/destination": "^6.3.0", + "@turf/helpers": "^6.3.0" + } + }, + "node_modules/@turf/clean-coords": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/clean-coords/-/clean-coords-6.3.0.tgz", + "integrity": "sha512-Ns7+vXHigKTclzqlFrUnXsXjtEWAu2YYurDxD5mrKXcncuisUIoKbFM55ZxeiiBj0ji8c1huR1xSqs8GVxZJJA==", + "dependencies": { + "@turf/helpers": "^6.3.0", + "@turf/invariant": "^6.3.0" + } + }, + "node_modules/@turf/clone": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/clone/-/clone-6.3.0.tgz", + "integrity": "sha512-GAgN89/9GCqUKECB1oY2hcTs0K2rZj+a2tY6VfM0ef9wwckuQZCKi+kKGUzhKVrmHee15jKV8n6DY0er8OndKg==", + "dependencies": { + "@turf/helpers": "^6.3.0" + } + }, + "node_modules/@turf/clusters": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/clusters/-/clusters-6.3.0.tgz", + "integrity": "sha512-NIT6LZ/zawt1nN7eC0VEII8J1QUx5qvUahtPKsADxHP27vDJDjnmGvUXvvC0XmibXt/RR9VRM5Rej04yn53g0A==", + "dependencies": { + "@turf/helpers": "^6.3.0", + "@turf/meta": "^6.3.0" + } + }, + "node_modules/@turf/clusters-dbscan": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/clusters-dbscan/-/clusters-dbscan-6.3.0.tgz", + "integrity": "sha512-EHWHMEBSGf4dvobfvifMl2G9p9KATP9TSeSf1WY+ajLRPfn3slUPSM9hP+7eisDBgb/tS+wqQNcl7pEoo72pnw==", + "dependencies": { + "@turf/clone": "^6.3.0", + "@turf/distance": "^6.3.0", + "@turf/helpers": "^6.3.0", + "@turf/meta": "^6.3.0", + "density-clustering": "1.3.0" + } + }, + "node_modules/@turf/clusters-kmeans": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/clusters-kmeans/-/clusters-kmeans-6.3.0.tgz", + "integrity": "sha512-cyHtW5nsOcs1p8l3mflX2805fOxR99FanXCP95U+001S4AwVSgxiOfTg8PUHg9nui2Qcq/PMBRQz80exb2UzyA==", + "dependencies": { + "@turf/clone": "^6.3.0", + "@turf/helpers": "^6.3.0", + "@turf/invariant": "^6.3.0", + "@turf/meta": "^6.3.0", + "skmeans": "0.9.7" + } + }, + "node_modules/@turf/collect": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/collect/-/collect-6.3.0.tgz", + "integrity": "sha512-alkKujZ02m2wYNixYjF4AFSzXTMbewf1QnJRrtog3snJHFN/tZB9iU3ZcwvxOSbO2Zwrw89A90HLe8k7oGUqXw==", + "dependencies": { + "@turf/bbox": "^6.3.0", + "@turf/boolean-point-in-polygon": "^6.3.0", + "@turf/helpers": "^6.3.0", + "rbush": "2.x" + } + }, + "node_modules/@turf/combine": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/combine/-/combine-6.3.0.tgz", + "integrity": "sha512-/FKUxUvQhKDDBJ4CTr49rvanYbdrtlsbr+7p6H8Vv0EyfeWqwJ3qA8lRuAjPtK0StviYg2t6XTucvKd/3PPX3Q==", + "dependencies": { + "@turf/helpers": "^6.3.0", + "@turf/meta": "^6.3.0" + } + }, + "node_modules/@turf/concave": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/concave/-/concave-6.3.0.tgz", + "integrity": "sha512-9BPctrW2Oy9K2jjKv80tR26RQEJjwAAFwgG8JEBK8hSF9zdqa07fzx7Ncj+8hM9+3vF30f2TvQ8yxvoH7HSvXA==", + "dependencies": { + "@turf/clone": "^6.3.0", + "@turf/distance": "^6.3.0", + "@turf/helpers": "^6.3.0", + "@turf/invariant": "^6.3.0", + "@turf/meta": "^6.3.0", + "@turf/tin": "^6.3.0", + "topojson": "3.x" + } + }, + "node_modules/@turf/convex": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/convex/-/convex-6.3.0.tgz", + "integrity": "sha512-YpiLKRu1suwbI/knCOd7Fg7LojV6Beonu8gQjCoaPdkBEz0/W3XqNpfWQhcqp+XR10a2g4RK5mi6bUUejToFBw==", + "dependencies": { + "@turf/helpers": "^6.3.0", + "@turf/meta": "^6.3.0", + "concaveman": "*" + } + }, + "node_modules/@turf/destination": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/destination/-/destination-6.3.0.tgz", + "integrity": "sha512-aLt3U/XkJWyZW08Ln1qZwBNAGh27yhmYLu892+dBj3gKP6UUiR6ZopXxrBwjBVe00A6k2ktftKDn79qe0hptuw==", + "dependencies": { + "@turf/helpers": "^6.3.0", + "@turf/invariant": "^6.3.0" + } + }, + "node_modules/@turf/difference": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/difference/-/difference-6.3.0.tgz", + "integrity": "sha512-f4P0ra0jBOFk4HO8n/9FZ3NEmOX7FHCXHy/4Z1RSUUQsUQDCkx6/cyqbi8BCy2ZSDUSCGHV+iPgs4fRphMzCHQ==", + "dependencies": { + "@turf/helpers": "^6.3.0", + "@turf/invariant": "^6.3.0", + "polygon-clipping": "^0.15.2" + } + }, + "node_modules/@turf/difference/node_modules/polygon-clipping": { + "version": "0.15.3", + "resolved": "https://registry.npmjs.org/polygon-clipping/-/polygon-clipping-0.15.3.tgz", + "integrity": "sha512-ho0Xx5DLkgxRx/+n4O74XyJ67DcyN3Tu9bGYKsnTukGAW6ssnuak6Mwcyb1wHy9MZc9xsUWqIoiazkZB5weECg==", + "dependencies": { + "splaytree": "^3.1.0" + } + }, + "node_modules/@turf/difference/node_modules/splaytree": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/splaytree/-/splaytree-3.1.0.tgz", + "integrity": "sha512-gvUGR7xnOy0fLKTCxDeUZYgU/I1Tdf8M/lM1Qrf8L2TIOR5ipZjGk02uYcdv0o2x7WjVRgpm3iS2clLyuVAt0Q==" + }, + "node_modules/@turf/dissolve": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/dissolve/-/dissolve-6.3.0.tgz", + "integrity": "sha512-DxFH+3MQpBo3rIZSh9gjcdl00ZkyHAEK0DzTLq6JOS4vTHpYvFvDT07j/Vr+9cqfvWrAjGpQg92I8zMzh4XA6Q==", + "dependencies": { + "@turf/boolean-overlap": "^6.3.0", + "@turf/clone": "^6.3.0", + "@turf/helpers": "^6.3.0", + "@turf/invariant": "^6.3.0", + "@turf/line-intersect": "^6.3.0", + "@turf/meta": "^6.3.0", + "@turf/union": "^6.3.0", + "geojson-rbush": "3.x", + "get-closest": "*" + } + }, + "node_modules/@turf/distance": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/distance/-/distance-6.3.0.tgz", + "integrity": "sha512-basi24ssNFnH3iXPFjp/aNUrukjObiFWoIyDRqKyBJxVwVOwAWvfk4d38QQyBj5nDo5IahYRq/Q+T47/5hSs9w==", + "dependencies": { + "@turf/helpers": "^6.3.0", + "@turf/invariant": "^6.3.0" + } + }, + "node_modules/@turf/distance-weight": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/distance-weight/-/distance-weight-6.3.0.tgz", + "integrity": "sha512-o85n4q3WM0L292FV7ZKBtSdTzn20JRqcZSSktkJoxeuQJMHXlstRwviLiF5pTl5oDXO/mRdq6aPecvWkMAaiCQ==", + "dependencies": { + "@turf/centroid": "^6.3.0", + "@turf/helpers": "^6.3.0", + "@turf/invariant": "^6.3.0", + "@turf/meta": "^6.3.0" + } + }, + "node_modules/@turf/ellipse": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/ellipse/-/ellipse-6.3.0.tgz", + "integrity": "sha512-r+EvUK+IGgc3shvS/T1Wof2uCptS2fYmtcwMSFHnHjRnmUyrD4YFjPZT7ygxcDB91+UClZ6cdozR6vqBYzPAog==", + "dependencies": { + "@turf/helpers": "^6.3.0", + "@turf/invariant": "^6.3.0", + "@turf/rhumb-destination": "^6.3.0", + "@turf/transform-rotate": "^6.3.0" + } + }, + "node_modules/@turf/envelope": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/envelope/-/envelope-6.3.0.tgz", + "integrity": "sha512-9xmDTCogXJsAO0TrARA/lniMSEtAil9HIKXHDJ5N6zlZ2K5wfRdD2zDlqkgDT3t9oSvttSP3ltBf03fjMDt6Wg==", + "dependencies": { + "@turf/bbox": "^6.3.0", + "@turf/bbox-polygon": "^6.3.0", + "@turf/helpers": "^6.3.0" + } + }, + "node_modules/@turf/explode": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/explode/-/explode-6.3.0.tgz", + "integrity": "sha512-J3vOGwf2EJXfh1gifFtxAuuhVYWAMTRQL6jE3h9a8osNLO1nj8JGVxaL6fmJgdZ/A9cFPv1OYUndBzi86UYZvw==", + "dependencies": { + "@turf/helpers": "^6.3.0", + "@turf/meta": "^6.3.0" + } + }, + "node_modules/@turf/flatten": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/flatten/-/flatten-6.3.0.tgz", + "integrity": "sha512-0V3qxOGqb0NulEpADPCs/+i/AUQuNSChGA4oy/YGicfMHjnMNapZfOVg3LJEAkd/Kqpw2eJjjKe0gaX5aXo/1w==", + "dependencies": { + "@turf/helpers": "^6.3.0", + "@turf/meta": "^6.3.0" + } + }, + "node_modules/@turf/flip": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/flip/-/flip-6.3.0.tgz", + "integrity": "sha512-VTST1oaJFRyHOAbvY9kt8yKKCQt6aXpXeyVQRjhNESzlYLIQlTx3v+lI+eSSu+sc+SX4EDQltB1UdaVk7BIRJg==", + "dependencies": { + "@turf/clone": "^6.3.0", + "@turf/helpers": "^6.3.0", + "@turf/meta": "^6.3.0" + } + }, + "node_modules/@turf/great-circle": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/great-circle/-/great-circle-6.3.0.tgz", + "integrity": "sha512-dpGJcRf2TLzBvVUZa0Eej3edXOQofLcp9qgotqDHK68spqYK8lnrXrdyyqzLlTHx3nxZkHvFUOl1lqj8G4NraQ==", + "dependencies": { + "@turf/helpers": "^6.3.0", + "@turf/invariant": "^6.3.0" + } + }, + "node_modules/@turf/helpers": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/helpers/-/helpers-6.3.0.tgz", + "integrity": "sha512-kr6KuD4Z0GZ30tblTEvi90rvvVNlKieXuMC8CTzE/rVQb0/f/Cb29zCXxTD7giQTEQY/P2nRW23wEqqyNHulCg==" + }, + "node_modules/@turf/hex-grid": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/hex-grid/-/hex-grid-6.3.0.tgz", + "integrity": "sha512-adqOgpBJB+87bjnm5EKVklDuWsYtCrETlLrXpOw4CVyaqYEE2/Mvid25se/0TeGDfvIcnvIQvrApYL5O/sDaMw==", + "dependencies": { + "@turf/distance": "^6.3.0", + "@turf/helpers": "^6.3.0", + "@turf/intersect": "^6.3.0", + "@turf/invariant": "^6.3.0" + } + }, + "node_modules/@turf/interpolate": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/interpolate/-/interpolate-6.3.0.tgz", + "integrity": "sha512-2gVMSj/Ri8l5KGkCTyTJTqSbZwfWco6tWGMZyG0fqcB61PA6pEedU+TShBOOEKu7eBlpSyHlkS7+uii1bEGUCA==", + "dependencies": { + "@turf/bbox": "^6.3.0", + "@turf/centroid": "^6.3.0", + "@turf/clone": "^6.3.0", + "@turf/distance": "^6.3.0", + "@turf/helpers": "^6.3.0", + "@turf/hex-grid": "^6.3.0", + "@turf/invariant": "^6.3.0", + "@turf/meta": "^6.3.0", + "@turf/point-grid": "^6.3.0", + "@turf/square-grid": "^6.3.0", + "@turf/triangle-grid": "^6.3.0" + } + }, + "node_modules/@turf/intersect": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/intersect/-/intersect-6.3.0.tgz", + "integrity": "sha512-1YCIkyKjuTlX7HaTjtyE7ZRxLCmcu0BYr6jqoVl7TjyF2NUiNpPm3m4X1ZrSF6MfjIt5NFSGYCdNMEPgREq19w==", + "dependencies": { + "@turf/helpers": "^6.3.0", + "@turf/invariant": "^6.3.0", + "polygon-clipping": "^0.15.2" + } + }, + "node_modules/@turf/intersect/node_modules/polygon-clipping": { + "version": "0.15.3", + "resolved": "https://registry.npmjs.org/polygon-clipping/-/polygon-clipping-0.15.3.tgz", + "integrity": "sha512-ho0Xx5DLkgxRx/+n4O74XyJ67DcyN3Tu9bGYKsnTukGAW6ssnuak6Mwcyb1wHy9MZc9xsUWqIoiazkZB5weECg==", + "dependencies": { + "splaytree": "^3.1.0" + } + }, + "node_modules/@turf/intersect/node_modules/splaytree": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/splaytree/-/splaytree-3.1.0.tgz", + "integrity": "sha512-gvUGR7xnOy0fLKTCxDeUZYgU/I1Tdf8M/lM1Qrf8L2TIOR5ipZjGk02uYcdv0o2x7WjVRgpm3iS2clLyuVAt0Q==" + }, + "node_modules/@turf/invariant": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/invariant/-/invariant-6.3.0.tgz", + "integrity": "sha512-2OFOi9p+QOrcIMySEnr+WlOiKaFZ1bY56jA98YyECewJHfhPFWUBZEhc4nWGRT0ahK08Vus9+gcuBX8QIpCIIw==", + "dependencies": { + "@turf/helpers": "^6.3.0" + } + }, + "node_modules/@turf/isobands": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/isobands/-/isobands-6.3.0.tgz", + "integrity": "sha512-Ikk8LyVQJKsLH6nFYKEeUi9sShMVP9S63zy5CPMPvwRhZf0ix59tAEBfnk6DOfd0EzLLmEdfaAM2U0cRhkh9jA==", + "dependencies": { + "@turf/area": "^6.3.0", + "@turf/bbox": "^6.3.0", + "@turf/boolean-point-in-polygon": "^6.3.0", + "@turf/explode": "^6.3.0", + "@turf/helpers": "^6.3.0", + "@turf/invariant": "^6.3.0", + "@turf/meta": "^6.3.0", + "object-assign": "*" + } + }, + "node_modules/@turf/isolines": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/isolines/-/isolines-6.3.0.tgz", + "integrity": "sha512-z5hUIUcSaInGUhrx+vDZcCNWLS3MawzQGfc0TOUVDe03bO5sqUlaNyvx7C09Js4LEzsqqZ1GPIUvFPjePaXaVQ==", + "dependencies": { + "@turf/bbox": "^6.3.0", + "@turf/helpers": "^6.3.0", + "@turf/invariant": "^6.3.0", + "@turf/meta": "^6.3.0", + "object-assign": "*" + } + }, + "node_modules/@turf/kinks": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/kinks/-/kinks-6.3.0.tgz", + "integrity": "sha512-BLWvbl2/fa4SeJzVMbleT6Vo1cmzwmzRfxL2xxMei2jmf6JSvqDoMJFwIHGXrLZXvhOCb1b2C+MhBfhtc7kYkQ==", + "dependencies": { + "@turf/helpers": "^6.3.0" + } + }, + "node_modules/@turf/length": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/length/-/length-6.3.0.tgz", + "integrity": "sha512-91MHtigpV7mbrMW3xyaPVtLWQU3p487t3YHU4vdxih03p+dFI512dX/FtWbd9LNgrtBt4PM1uo1WmafGvfStKA==", + "dependencies": { + "@turf/distance": "^6.3.0", + "@turf/helpers": "^6.3.0", + "@turf/meta": "^6.3.0" + } + }, + "node_modules/@turf/line-arc": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/line-arc/-/line-arc-6.3.0.tgz", + "integrity": "sha512-WAAUgAWGf+U02GhXWrplODyUm3X6LZnYyn4VJQ9BPsKyawfK+NtjP7KsZ1MipIgtixNq3Ceexep0AHGHos4Prw==", + "dependencies": { + "@turf/circle": "^6.3.0", + "@turf/destination": "^6.3.0", + "@turf/helpers": "^6.3.0" + } + }, + "node_modules/@turf/line-chunk": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/line-chunk/-/line-chunk-6.3.0.tgz", + "integrity": "sha512-Xfja7H6XEgFPaK37sg7WBb0pIiA9hfjXtF7A1QPrh8z+JFyuVJzveBG2mYvin5UKTwsMKXuby6s4FUvmoEFqjQ==", + "dependencies": { + "@turf/helpers": "^6.3.0", + "@turf/length": "^6.3.0", + "@turf/line-slice-along": "^6.3.0", + "@turf/meta": "^6.3.0" + } + }, + "node_modules/@turf/line-intersect": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/line-intersect/-/line-intersect-6.3.0.tgz", + "integrity": "sha512-3naxR7XpkPd2vst3Mw6DFry4C9m3o0/f2n/xu5UAyxb88Ie4m2k+1eqkhzMMx/0L+E6iThWpLx7DASM6q6o9ow==", + "dependencies": { + "@turf/helpers": "^6.3.0", + "@turf/invariant": "^6.3.0", + "@turf/line-segment": "^6.3.0", + "@turf/meta": "^6.3.0", + "geojson-rbush": "3.x" + } + }, + "node_modules/@turf/line-offset": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/line-offset/-/line-offset-6.3.0.tgz", + "integrity": "sha512-yzgmNc/8miyn+pH2ubT4rZb9uAPY6oLqkwmEdzy2fuU4yUFnCNN/nWvYP4acGdgaSfprJd+4MdlLFzWBJxSplw==", + "dependencies": { + "@turf/helpers": "^6.3.0", + "@turf/invariant": "^6.3.0", + "@turf/meta": "^6.3.0" + } + }, + "node_modules/@turf/line-overlap": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/line-overlap/-/line-overlap-6.3.0.tgz", + "integrity": "sha512-fVyXfTpr/A+ZXZWG6PbuYz5rAGbTQWyrMZveCl2049SbOXSkVXGjUfpnLaklP0p+adw7eRR0LhZn6FGz9CQaFg==", + "dependencies": { + "@turf/boolean-point-on-line": "^6.3.0", + "@turf/helpers": "^6.3.0", + "@turf/invariant": "^6.3.0", + "@turf/line-segment": "^6.3.0", + "@turf/meta": "^6.3.0", + "@turf/nearest-point-on-line": "^6.3.0", + "deep-equal": "1.x", + "geojson-rbush": "3.x" + } + }, + "node_modules/@turf/line-segment": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/line-segment/-/line-segment-6.3.0.tgz", + "integrity": "sha512-M+aDy83V+E7jYWNaf+b+A88yhnMrJhyg/lhAj6mU6UeB2PbruXB2qgSmmVDSE2dIknOvZZuIWNzEzUI07RO2kw==", + "dependencies": { + "@turf/helpers": "^6.3.0", + "@turf/invariant": "^6.3.0", + "@turf/meta": "^6.3.0" + } + }, + "node_modules/@turf/line-slice": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/line-slice/-/line-slice-6.3.0.tgz", + "integrity": "sha512-HEgVY7TcoRxh59DCb/7SUlX6x3RJWSEBspIfsxCv+2lhgb3aRekn+aELvr3VeY9fWPCXvOfELBH3PNjMhJMY2Q==", + "dependencies": { + "@turf/helpers": "^6.3.0", + "@turf/invariant": "^6.3.0", + "@turf/nearest-point-on-line": "^6.3.0" + } + }, + "node_modules/@turf/line-slice-along": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/line-slice-along/-/line-slice-along-6.3.0.tgz", + "integrity": "sha512-3s6vGTxGgCTb3Wd1seyir49rRc0GsX6OZXiRP5VdlT3Aq0cuuCNJycgHCH+H8LiYrEQDUhNUWbGljreCH0/JCg==", + "dependencies": { + "@turf/bearing": "^6.3.0", + "@turf/destination": "^6.3.0", + "@turf/distance": "^6.3.0", + "@turf/helpers": "^6.3.0" + } + }, + "node_modules/@turf/line-split": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/line-split/-/line-split-6.3.0.tgz", + "integrity": "sha512-Q0nUJ0vczy11piyEz0FaKScFwSQtb1HJ2RPEMCw1coUJhTCB02KBWQLImhYqwsD3uLg+H/fxaJ1Gva6EPWoDNQ==", + "dependencies": { + "@turf/bbox": "^6.3.0", + "@turf/helpers": "^6.3.0", + "@turf/invariant": "^6.3.0", + "@turf/line-intersect": "^6.3.0", + "@turf/line-segment": "^6.3.0", + "@turf/meta": "^6.3.0", + "@turf/nearest-point-on-line": "^6.3.0", + "@turf/square": "^6.3.0", + "@turf/truncate": "^6.3.0", + "geojson-rbush": "3.x" + } + }, + "node_modules/@turf/line-to-polygon": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/line-to-polygon/-/line-to-polygon-6.3.0.tgz", + "integrity": "sha512-754ywhQzcAylVSqQQwlv0TUMC5nCHp4nDle3X48tkHIKcnn4fJkW8O0YNhhQCE8p6NDcs0Ayi4qR0uHLPTzUWQ==", + "dependencies": { + "@turf/bbox": "^6.3.0", + "@turf/clone": "^6.3.0", + "@turf/helpers": "^6.3.0", + "@turf/invariant": "^6.3.0" + } + }, + "node_modules/@turf/mask": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/mask/-/mask-6.3.0.tgz", + "integrity": "sha512-2DbaHvmxz0ueQpGCo+6nXHhLqlmTjzGDkUL/ys6rgWTXj40udKakPwMNa2WrvzqHwowJsXWaWDp2GogRT5foDA==", + "dependencies": { + "@turf/bbox": "^6.3.0", + "@turf/helpers": "^6.3.0", + "@turf/meta": "^6.3.0", + "@turf/union": "^6.3.0", + "rbush": "^2.0.1" + } + }, + "node_modules/@turf/meta": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/meta/-/meta-6.3.0.tgz", + "integrity": "sha512-qBJjaAJS9H3ap0HlGXyF/Bzfl0qkA9suafX/jnDsZvWMfVLt+s+o6twKrXOGk5t7nnNON2NFRC8+czxpu104EQ==", + "dependencies": { + "@turf/helpers": "^6.3.0" + } + }, + "node_modules/@turf/midpoint": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/midpoint/-/midpoint-6.3.0.tgz", + "integrity": "sha512-ImiYK5l/QZh5aCynxCyHoaJYn4j1VhorVyw2XihHuwAtebTc+KRaBJpWSD2eJxo3Q3J+QepWMiiMvQFJgQ5uCQ==", + "dependencies": { + "@turf/bearing": "^6.3.0", + "@turf/destination": "^6.3.0", + "@turf/distance": "^6.3.0", + "@turf/helpers": "^6.3.0" + } + }, + "node_modules/@turf/moran-index": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/moran-index/-/moran-index-6.3.0.tgz", + "integrity": "sha512-qRsSqmYtvnKiGFbz3aU1up8Q8jY9MCflRdvKeTOJ2E3Z4xOIyOLXOrNvpLIM8CFcLwY06IInMRoaKi/CVOC54g==", + "dependencies": { + "@turf/distance-weight": "^6.3.0", + "@turf/helpers": "^6.3.0", + "@turf/meta": "^6.3.0" + } + }, + "node_modules/@turf/nearest-point": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/nearest-point/-/nearest-point-6.3.0.tgz", + "integrity": "sha512-eovLuWxO2cQaKETbf1OhnWYkRYYgwuDhJAvLU9ZpXnqk2tNE06gt/2C5oJJiSlh4ZksDM8ryHZicswaXrYz+qA==", + "dependencies": { + "@turf/clone": "^6.3.0", + "@turf/distance": "^6.3.0", + "@turf/helpers": "^6.3.0", + "@turf/meta": "^6.3.0" + } + }, + "node_modules/@turf/nearest-point-on-line": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/nearest-point-on-line/-/nearest-point-on-line-6.3.0.tgz", + "integrity": "sha512-b4C9Md1VbGn9chMgdSj2grJD4w4t0owEWOKEBwOZfdhrcksyOedVvKB7XqOFdj/8Jitel40EKAC5LQTNu24kEQ==", + "dependencies": { + "@turf/bearing": "^6.3.0", + "@turf/destination": "^6.3.0", + "@turf/distance": "^6.3.0", + "@turf/helpers": "^6.3.0", + "@turf/invariant": "^6.3.0", + "@turf/line-intersect": "^6.3.0", + "@turf/meta": "^6.3.0" + } + }, + "node_modules/@turf/nearest-point-to-line": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/nearest-point-to-line/-/nearest-point-to-line-6.3.0.tgz", + "integrity": "sha512-1ut4u1KXHwXc6qdnDLkhTdPUdeHOmdmysMBxnNNFH7UTefi3XfR8BF/NOxNP8g7OKJrZ2vhDeR4PCL5xAsVH5A==", + "dependencies": { + "@turf/helpers": "^6.3.0", + "@turf/invariant": "^6.3.0", + "@turf/meta": "^6.3.0", + "@turf/point-to-line-distance": "^6.3.0", + "object-assign": "*" + } + }, + "node_modules/@turf/planepoint": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/planepoint/-/planepoint-6.3.0.tgz", + "integrity": "sha512-RDfzUiwB3P3bGeRBZf/czZdtQsqUIVQePaAU5ijCqTBdR1V0TuVbRig1WE0XD4j5dM242OEezHJ3Xqgo71Nzww==", + "dependencies": { + "@turf/helpers": "^6.3.0", + "@turf/invariant": "^6.3.0" + } + }, + "node_modules/@turf/point-grid": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/point-grid/-/point-grid-6.3.0.tgz", + "integrity": "sha512-1ERghdRXtA/5Z/To7X1Y9D1cvej3+ZCZXNZnM/0c+3sAioohjK5IXv2enR23p1ftA6Z3H7wug5IB4YmVzs4MaA==", + "dependencies": { + "@turf/boolean-within": "^6.3.0", + "@turf/distance": "^6.3.0", + "@turf/helpers": "^6.3.0", + "@turf/invariant": "^6.3.0" + } + }, + "node_modules/@turf/point-on-feature": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/point-on-feature/-/point-on-feature-6.3.0.tgz", + "integrity": "sha512-zN35KN/IUAgOyVtlEQg1j71U8eoav2JPZOdWlEFHsjYQVm9cF+AKOkvBdm6LQWMWvCtwSqqghwe/zRKvzJPynw==", + "dependencies": { + "@turf/boolean-point-in-polygon": "^6.3.0", + "@turf/center": "^6.3.0", + "@turf/explode": "^6.3.0", + "@turf/helpers": "^6.3.0", + "@turf/nearest-point": "^6.3.0" + } + }, + "node_modules/@turf/point-to-line-distance": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/point-to-line-distance/-/point-to-line-distance-6.3.0.tgz", + "integrity": "sha512-AqCcj4A0GPzKb3w+q+C9ex0r5mC+u+Ee6VN2jY1p25dxBQJNpMZKDE5LcWtaXeD+pAk3ZGmvea8LR5S0AJukxA==", + "dependencies": { + "@turf/bearing": "^6.3.0", + "@turf/distance": "^6.3.0", + "@turf/helpers": "^6.3.0", + "@turf/invariant": "^6.3.0", + "@turf/meta": "^6.3.0", + "@turf/projection": "^6.3.0", + "@turf/rhumb-bearing": "^6.3.0", + "@turf/rhumb-distance": "^6.3.0" + } + }, + "node_modules/@turf/points-within-polygon": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/points-within-polygon/-/points-within-polygon-6.3.0.tgz", + "integrity": "sha512-ES/tLj5oZR7TBg7FSOy8bypBvXALwl2f36MmQ3AJfK0KvAeQ+mxFXTGslAK3ewL9fVVxWLsmbP9bPLSzWeuPAw==", + "dependencies": { + "@turf/boolean-point-in-polygon": "^6.3.0", + "@turf/helpers": "^6.3.0", + "@turf/meta": "^6.3.0" + } + }, + "node_modules/@turf/polygon-smooth": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/polygon-smooth/-/polygon-smooth-6.3.0.tgz", + "integrity": "sha512-60aMw3d57DXqdFyWU43c5gHaumCZ9jn6K5GqgeKTfmElIumdSspg9MEIW7d7z6qkPufPY34FczJ9yapMih5SIQ==", + "dependencies": { + "@turf/helpers": "^6.3.0", + "@turf/meta": "^6.3.0" + } + }, + "node_modules/@turf/polygon-tangents": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/polygon-tangents/-/polygon-tangents-6.3.0.tgz", + "integrity": "sha512-QEXsXgZKWV3mPPqxERIQ+DzBSvnO0R1c9FsHuHE0F49Cic+CRMPjEpnzQj39cOUQfwPlQl2ThuaKAljlQ5QNMQ==", + "dependencies": { + "@turf/bbox": "^6.3.0", + "@turf/boolean-within": "^6.3.0", + "@turf/explode": "^6.3.0", + "@turf/helpers": "^6.3.0", + "@turf/invariant": "^6.3.0", + "@turf/nearest-point": "^6.3.0" + } + }, + "node_modules/@turf/polygon-to-line": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/polygon-to-line/-/polygon-to-line-6.3.0.tgz", + "integrity": "sha512-KFGlQlGOBayBvELz+tip1zCa3eB8xyZePZUZ3I3OnU7mk0FFzJzvLTmPUc7MupgqORT4LkNGmyKSVWaz38NTig==", + "dependencies": { + "@turf/helpers": "^6.3.0", + "@turf/invariant": "^6.3.0" + } + }, + "node_modules/@turf/polygonize": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/polygonize/-/polygonize-6.3.0.tgz", + "integrity": "sha512-v1w5ibIJ5to3+nuitVNyukPMMY+z++y3e55TBuot1vkAEyCi538Kc8Qz0eWONPGZKzwYtQtkve2NIp0BBeNd5g==", + "dependencies": { + "@turf/boolean-point-in-polygon": "^6.3.0", + "@turf/envelope": "^6.3.0", + "@turf/helpers": "^6.3.0", + "@turf/invariant": "^6.3.0", + "@turf/meta": "^6.3.0" + } + }, + "node_modules/@turf/projection": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/projection/-/projection-6.3.0.tgz", + "integrity": "sha512-IpSs7Q6G6xi47ynVlYYVegPLy6Jc0yo3/DcIm83jaJa4NnzPFXIFZT0v9Fe1N8MraHZqiqaSPbVnJXCGwR12lg==", + "dependencies": { + "@turf/clone": "^6.3.0", + "@turf/helpers": "^6.3.0", + "@turf/meta": "^6.3.0" + } + }, + "node_modules/@turf/random": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/random/-/random-6.3.0.tgz", + "integrity": "sha512-jSKNqLCOc/xUPoQp8jZLUYTrtID1PNJV7eLXMbJdHdcYwU7d6dTkrdgI08ZU/Nc4qJv1ZAlWO/xEyKGtC1RgrQ==", + "dependencies": { + "@turf/helpers": "^6.3.0" + } + }, + "node_modules/@turf/rectangle-grid": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/rectangle-grid/-/rectangle-grid-6.3.0.tgz", + "integrity": "sha512-XQAjpprUhGA9aoVH8H6lqZb0Dk8SZ2djKAPD6dDplFgrufdmP1Fe1BfbsdBgjyfPrdR7hSffLyEAwC3bhfJo2w==", + "dependencies": { + "@turf/boolean-intersects": "^6.3.0", + "@turf/distance": "^6.3.0", + "@turf/helpers": "^6.3.0" + } + }, + "node_modules/@turf/rewind": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/rewind/-/rewind-6.3.0.tgz", + "integrity": "sha512-56HwvOZ4r4/wXr8l8zCpdjZ3bxY6Ee7aokuJr/+BlVqikHdRHRx+FJpLGpykZU1YWdO7IiLK7ajX+clYPaqRKg==", + "dependencies": { + "@turf/boolean-clockwise": "^6.3.0", + "@turf/clone": "^6.3.0", + "@turf/helpers": "^6.3.0", + "@turf/invariant": "^6.3.0", + "@turf/meta": "^6.3.0" + } + }, + "node_modules/@turf/rhumb-bearing": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/rhumb-bearing/-/rhumb-bearing-6.3.0.tgz", + "integrity": "sha512-/c/BE3huEUrwN6gx7Bg2FzfJqeU+TWk/slQPDHpbVunlIPbS6L28brqSVD+KXfMG8HQIzynz6Pm4Y+j5Iv4aWA==", + "dependencies": { + "@turf/helpers": "^6.3.0", + "@turf/invariant": "^6.3.0" + } + }, + "node_modules/@turf/rhumb-destination": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/rhumb-destination/-/rhumb-destination-6.3.0.tgz", + "integrity": "sha512-MaQf5wldfERfn8cjtbkD/6GUurAwD+sjedvDgV/chZ83yx7kXmRgrVMpRSGUbmGQ3Ww8dn38sUCapnM6M07+Rg==", + "dependencies": { + "@turf/helpers": "^6.3.0", + "@turf/invariant": "^6.3.0" + } + }, + "node_modules/@turf/rhumb-distance": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/rhumb-distance/-/rhumb-distance-6.3.0.tgz", + "integrity": "sha512-wMIQVvznusonnp/POeucFdA4Rubn0NrkcEMdxdcCgFK7OmTz0zU4CEnNONF2IUGkQ5WwoKiuS7MOTQ8OuCjSfQ==", + "dependencies": { + "@turf/helpers": "^6.3.0", + "@turf/invariant": "^6.3.0" + } + }, + "node_modules/@turf/sample": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/sample/-/sample-6.3.0.tgz", + "integrity": "sha512-CmUkpoLIi+57jxBmYh4KW7S4Vculty84NC2ERNFZrLkVquewVYSppwKsaZtc0Hbap6a1N7hP4C80e2bPzRC4fg==", + "dependencies": { + "@turf/helpers": "^6.3.0" + } + }, + "node_modules/@turf/sector": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/sector/-/sector-6.3.0.tgz", + "integrity": "sha512-bHaDlHzCKEl5G+EEXdMTk3MFC8Yl5QjwrMVakF2Usi0P0c7hp6r10QVOjq9nmn6jvZHTPaiG2A4z9unkWIFxIg==", + "dependencies": { + "@turf/circle": "^6.3.0", + "@turf/helpers": "^6.3.0", + "@turf/invariant": "^6.3.0", + "@turf/line-arc": "^6.3.0", + "@turf/meta": "^6.3.0" + } + }, + "node_modules/@turf/shortest-path": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/shortest-path/-/shortest-path-6.3.0.tgz", + "integrity": "sha512-dc50vcgb6G/nyljCdfxS4T3tGb2f45MkKEFdz6sVTYqjNakPnRoJao8xvInVsf1i2J53dWNU635oZhW9P1nqKg==", + "dependencies": { + "@turf/bbox": "^6.3.0", + "@turf/bbox-polygon": "^6.3.0", + "@turf/boolean-point-in-polygon": "^6.3.0", + "@turf/clean-coords": "^6.3.0", + "@turf/distance": "^6.3.0", + "@turf/helpers": "^6.3.0", + "@turf/invariant": "^6.3.0", + "@turf/meta": "^6.3.0", + "@turf/transform-scale": "^6.3.0" + } + }, + "node_modules/@turf/simplify": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/simplify/-/simplify-6.3.0.tgz", + "integrity": "sha512-6a+9oKwZpZk3Oohz9koQZGXh1qb+/UgUz2yW2bunjjlKpBdBFhRbEKi0KeprgPGFLLTMjf0tybhO1rFwiz6S1w==", + "dependencies": { + "@turf/clean-coords": "^6.3.0", + "@turf/clone": "^6.3.0", + "@turf/helpers": "^6.3.0", + "@turf/meta": "^6.3.0" + } + }, + "node_modules/@turf/square": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/square/-/square-6.3.0.tgz", + "integrity": "sha512-/nRGsV0DlUcOYv+gKAkIADSf+HooNLbOLBTUdhq9Piy3LuAWIXT+Rt5XN+NuNZP+84Al34GA1fR+BxqQ4reh7w==", + "dependencies": { + "@turf/distance": "^6.3.0", + "@turf/helpers": "^6.3.0" + } + }, + "node_modules/@turf/square-grid": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/square-grid/-/square-grid-6.3.0.tgz", + "integrity": "sha512-ZCgThI5hPLJNVErCB9zkJ3w3OpW6BbrOqyrxFbwlYGZrZ6uj52/j8PWQtwnmiqdv0k8+Cbxrap7E6//Oks4jIw==", + "dependencies": { + "@turf/helpers": "^6.3.0", + "@turf/rectangle-grid": "^6.3.0" + } + }, + "node_modules/@turf/standard-deviational-ellipse": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/standard-deviational-ellipse/-/standard-deviational-ellipse-6.3.0.tgz", + "integrity": "sha512-e8CeSUv5FLpzlJxiOr9lDtJIY3e/JKW4is+gBO8rMTQNFbWyrqXtzhyTfrwXEPKmaeei1DK9ixxj/oRDna25Hw==", + "dependencies": { + "@turf/center-mean": "^6.3.0", + "@turf/ellipse": "^6.3.0", + "@turf/helpers": "^6.3.0", + "@turf/invariant": "^6.3.0", + "@turf/meta": "^6.3.0", + "@turf/points-within-polygon": "^6.3.0" + } + }, + "node_modules/@turf/tag": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/tag/-/tag-6.3.0.tgz", + "integrity": "sha512-3L//rLql+ILeFuZ5L/sPm0f5NcHrNgUnGiB1hSIp3kdhhIIiZUpcktJUbksTvID67JJlP3smfyIQiU++LZW21w==", + "dependencies": { + "@turf/boolean-point-in-polygon": "^6.3.0", + "@turf/clone": "^6.3.0", + "@turf/helpers": "^6.3.0", + "@turf/meta": "^6.3.0" + } + }, + "node_modules/@turf/tesselate": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/tesselate/-/tesselate-6.3.0.tgz", + "integrity": "sha512-SkBHJCci/ergp/Y1TIfBRavdEJgFatQDz+ySdggXHT+mBiJEOEia3N+8V89RVOnORXTCDsjzWOWwftCS/J2sKQ==", + "dependencies": { + "@turf/helpers": "^6.3.0", + "earcut": "^2.0.0" + } + }, + "node_modules/@turf/tin": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/tin/-/tin-6.3.0.tgz", + "integrity": "sha512-obk9vyzKo3o3Dy4fPlb8IROb9LdMlz4LvKZ63DNtQsxwrWsc+og0EOh2mpvZrCIeoObx3ah5SnuAh14xH4JybA==", + "dependencies": { + "@turf/helpers": "^6.3.0" + } + }, + "node_modules/@turf/transform-rotate": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/transform-rotate/-/transform-rotate-6.3.0.tgz", + "integrity": "sha512-6CPfmDdaXjbBoPeyHkui704vz6MD3MoI09LGRVJ/RIo1uH/OL6RDSlCfLxFtkE33FJ7VV4giczc3LF1UP5Oh9w==", + "dependencies": { + "@turf/centroid": "^6.3.0", + "@turf/clone": "^6.3.0", + "@turf/helpers": "^6.3.0", + "@turf/invariant": "^6.3.0", + "@turf/meta": "^6.3.0", + "@turf/rhumb-bearing": "^6.3.0", + "@turf/rhumb-destination": "^6.3.0", + "@turf/rhumb-distance": "^6.3.0" + } + }, + "node_modules/@turf/transform-scale": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/transform-scale/-/transform-scale-6.3.0.tgz", + "integrity": "sha512-UnLWEXAUdZy7JYbylMjYczPUkxXlUK1nMgv7zEzQ+8mczysPVsgB/FDyiexY2bgVEEBMeDqFSHtqLRavXljI0A==", + "dependencies": { + "@turf/bbox": "^6.3.0", + "@turf/center": "^6.3.0", + "@turf/centroid": "^6.3.0", + "@turf/clone": "^6.3.0", + "@turf/helpers": "^6.3.0", + "@turf/invariant": "^6.3.0", + "@turf/meta": "^6.3.0", + "@turf/rhumb-bearing": "^6.3.0", + "@turf/rhumb-destination": "^6.3.0", + "@turf/rhumb-distance": "^6.3.0" + } + }, + "node_modules/@turf/transform-translate": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/transform-translate/-/transform-translate-6.3.0.tgz", + "integrity": "sha512-ZGAK3T6wdYLOIKr/FHl+i09b1vhPV3XWHw4/M27xA6US2rNcO6/jkLjskdME/3JzJDFmGa8F2vlPqlhtWWoRSw==", + "dependencies": { + "@turf/clone": "^6.3.0", + "@turf/helpers": "^6.3.0", + "@turf/invariant": "^6.3.0", + "@turf/meta": "^6.3.0", + "@turf/rhumb-destination": "^6.3.0" + } + }, + "node_modules/@turf/triangle-grid": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/triangle-grid/-/triangle-grid-6.3.0.tgz", + "integrity": "sha512-2AExXl7pTvRKOyGowuvvUm0tTyLQl+xzvv+mgWgNyg84qQptGN3HFH/QS4quoQdEzOyHNLFHgloNn6cWFX9v4A==", + "dependencies": { + "@turf/distance": "^6.3.0", + "@turf/helpers": "^6.3.0", + "@turf/intersect": "^6.3.0" + } + }, + "node_modules/@turf/truncate": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/truncate/-/truncate-6.3.0.tgz", + "integrity": "sha512-fvzR3BUODPciEBELLqqAggEEeb1L0d79WZYb9HKaoSB0GKTTgNrEbkTXiiGEjGJ1s1FMqXOEp0DKsLvvb1h4OA==", + "dependencies": { + "@turf/helpers": "^6.3.0", + "@turf/meta": "^6.3.0" + } + }, + "node_modules/@turf/turf": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/turf/-/turf-6.3.0.tgz", + "integrity": "sha512-6CcUammJKsn6mI7/+DlnXqf1iAk5HZ86/wmHIVG6VTmmPBP5drWSjoRUcaiXQADzLLuR9eZ3kl11KEOdvn9DmQ==", + "dependencies": { + "@turf/along": "^6.3.0", + "@turf/angle": "^6.3.0", + "@turf/area": "^6.3.0", + "@turf/bbox": "^6.3.0", + "@turf/bbox-clip": "^6.3.0", + "@turf/bbox-polygon": "^6.3.0", + "@turf/bearing": "^6.3.0", + "@turf/bezier-spline": "^6.3.0", + "@turf/boolean-clockwise": "^6.3.0", + "@turf/boolean-contains": "^6.3.0", + "@turf/boolean-crosses": "^6.3.0", + "@turf/boolean-disjoint": "^6.3.0", + "@turf/boolean-equal": "^6.3.0", + "@turf/boolean-intersects": "^6.3.0", + "@turf/boolean-overlap": "^6.3.0", + "@turf/boolean-parallel": "^6.3.0", + "@turf/boolean-point-in-polygon": "^6.3.0", + "@turf/boolean-point-on-line": "^6.3.0", + "@turf/boolean-within": "^6.3.0", + "@turf/buffer": "^6.3.0", + "@turf/center": "^6.3.0", + "@turf/center-mean": "^6.3.0", + "@turf/center-median": "^6.3.0", + "@turf/center-of-mass": "^6.3.0", + "@turf/centroid": "^6.3.0", + "@turf/circle": "^6.3.0", + "@turf/clean-coords": "^6.3.0", + "@turf/clone": "^6.3.0", + "@turf/clusters": "^6.3.0", + "@turf/clusters-dbscan": "^6.3.0", + "@turf/clusters-kmeans": "^6.3.0", + "@turf/collect": "^6.3.0", + "@turf/combine": "^6.3.0", + "@turf/concave": "^6.3.0", + "@turf/convex": "^6.3.0", + "@turf/destination": "^6.3.0", + "@turf/difference": "^6.3.0", + "@turf/dissolve": "^6.3.0", + "@turf/distance": "^6.3.0", + "@turf/distance-weight": "^6.3.0", + "@turf/ellipse": "^6.3.0", + "@turf/envelope": "^6.3.0", + "@turf/explode": "^6.3.0", + "@turf/flatten": "^6.3.0", + "@turf/flip": "^6.3.0", + "@turf/great-circle": "^6.3.0", + "@turf/helpers": "^6.3.0", + "@turf/hex-grid": "^6.3.0", + "@turf/interpolate": "^6.3.0", + "@turf/intersect": "^6.3.0", + "@turf/invariant": "^6.3.0", + "@turf/isobands": "^6.3.0", + "@turf/isolines": "^6.3.0", + "@turf/kinks": "^6.3.0", + "@turf/length": "^6.3.0", + "@turf/line-arc": "^6.3.0", + "@turf/line-chunk": "^6.3.0", + "@turf/line-intersect": "^6.3.0", + "@turf/line-offset": "^6.3.0", + "@turf/line-overlap": "^6.3.0", + "@turf/line-segment": "^6.3.0", + "@turf/line-slice": "^6.3.0", + "@turf/line-slice-along": "^6.3.0", + "@turf/line-split": "^6.3.0", + "@turf/line-to-polygon": "^6.3.0", + "@turf/mask": "^6.3.0", + "@turf/meta": "^6.3.0", + "@turf/midpoint": "^6.3.0", + "@turf/moran-index": "^6.3.0", + "@turf/nearest-point": "^6.3.0", + "@turf/nearest-point-on-line": "^6.3.0", + "@turf/nearest-point-to-line": "^6.3.0", + "@turf/planepoint": "^6.3.0", + "@turf/point-grid": "^6.3.0", + "@turf/point-on-feature": "^6.3.0", + "@turf/point-to-line-distance": "^6.3.0", + "@turf/points-within-polygon": "^6.3.0", + "@turf/polygon-smooth": "^6.3.0", + "@turf/polygon-tangents": "^6.3.0", + "@turf/polygon-to-line": "^6.3.0", + "@turf/polygonize": "^6.3.0", + "@turf/projection": "^6.3.0", + "@turf/random": "^6.3.0", + "@turf/rewind": "^6.3.0", + "@turf/rhumb-bearing": "^6.3.0", + "@turf/rhumb-destination": "^6.3.0", + "@turf/rhumb-distance": "^6.3.0", + "@turf/sample": "^6.3.0", + "@turf/sector": "^6.3.0", + "@turf/shortest-path": "^6.3.0", + "@turf/simplify": "^6.3.0", + "@turf/square": "^6.3.0", + "@turf/square-grid": "^6.3.0", + "@turf/standard-deviational-ellipse": "^6.3.0", + "@turf/tag": "^6.3.0", + "@turf/tesselate": "^6.3.0", + "@turf/tin": "^6.3.0", + "@turf/transform-rotate": "^6.3.0", + "@turf/transform-scale": "^6.3.0", + "@turf/transform-translate": "^6.3.0", + "@turf/triangle-grid": "^6.3.0", + "@turf/truncate": "^6.3.0", + "@turf/union": "^6.3.0", + "@turf/unkink-polygon": "^6.3.0", + "@turf/voronoi": "^6.3.0" + } + }, + "node_modules/@turf/union": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/union/-/union-6.3.0.tgz", + "integrity": "sha512-m8yh13Q5E0Y+YC10+iI/Qq0Txt7UmSIFByc7DfNVlMMGTceqLFa8xGwSVdFuB/d6MWwKuzKonQMl1PUx/Vd2Iw==", + "dependencies": { + "@turf/helpers": "^6.3.0", + "@turf/invariant": "^6.3.0", + "polygon-clipping": "^0.15.2" + } + }, + "node_modules/@turf/union/node_modules/polygon-clipping": { + "version": "0.15.3", + "resolved": "https://registry.npmjs.org/polygon-clipping/-/polygon-clipping-0.15.3.tgz", + "integrity": "sha512-ho0Xx5DLkgxRx/+n4O74XyJ67DcyN3Tu9bGYKsnTukGAW6ssnuak6Mwcyb1wHy9MZc9xsUWqIoiazkZB5weECg==", + "dependencies": { + "splaytree": "^3.1.0" + } + }, + "node_modules/@turf/union/node_modules/splaytree": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/splaytree/-/splaytree-3.1.0.tgz", + "integrity": "sha512-gvUGR7xnOy0fLKTCxDeUZYgU/I1Tdf8M/lM1Qrf8L2TIOR5ipZjGk02uYcdv0o2x7WjVRgpm3iS2clLyuVAt0Q==" + }, + "node_modules/@turf/unkink-polygon": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/unkink-polygon/-/unkink-polygon-6.3.0.tgz", + "integrity": "sha512-XBUJkuDEr2R8cHpl+sHtV15J1S28/HCxhAHqfV+As3bTi81KhVhBK9EBwFGYCu9aerVgBK129FjRKXjnTYqtDw==", + "dependencies": { + "@turf/area": "^6.3.0", + "@turf/boolean-point-in-polygon": "^6.3.0", + "@turf/helpers": "^6.3.0", + "@turf/meta": "^6.3.0", + "rbush": "^2.0.1" + } + }, + "node_modules/@turf/voronoi": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/voronoi/-/voronoi-6.3.0.tgz", + "integrity": "sha512-M0C6Kfo+qvKk4veRD7xW1PjMitJ0vqN6F4OOczxyX3tkj/oMyhWg+YbWk7mo/wKdSo9gCvHhnIVNkPsSSaFmyQ==", + "dependencies": { + "@turf/helpers": "^6.3.0", + "@turf/invariant": "^6.3.0", + "d3-voronoi": "1.1.2" + } + }, + "node_modules/@types/geojson": { + "version": "7946.0.7", + "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.7.tgz", + "integrity": "sha512-wE2v81i4C4Ol09RtsWFAqg3BUitWbHSpSlIo+bNdsCJijO9sjme+zm+73ZMCa/qMC8UEERxzGbvmr1cffo2SiQ==" + }, + "node_modules/@types/jquery": { + "version": "3.5.5", + "resolved": "https://registry.npmjs.org/@types/jquery/-/jquery-3.5.5.tgz", + "integrity": "sha512-6RXU9Xzpc6vxNrS6FPPapN1SxSHgQ336WC6Jj/N8q30OiaBZ00l1GBgeP7usjVZPivSkGUfL1z/WW6TX989M+w==", + "dependencies": { + "@types/sizzle": "*" + } + }, + "node_modules/@types/leaflet": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@types/leaflet/-/leaflet-1.7.0.tgz", + "integrity": "sha512-ltv5jR+VjKSMtoDkxH61Rsbo0zLU7iqyOXpVPkAX4F+79fg2eymC7t0msWsfNaEZO1FGTIQATCCCQe+ijWoicg==", + "dependencies": { + "@types/geojson": "*" + } + }, + "node_modules/@types/leaflet-markercluster": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@types/leaflet-markercluster/-/leaflet-markercluster-1.0.3.tgz", + "integrity": "sha1-ZBUb5FP2SQ6HUVAEgt65YQZOeCw=", + "deprecated": "'@types/leaflet-markercluster' is now '@types/leaflet.markercluster'.", + "dependencies": { + "@types/leaflet": "*" + } + }, + "node_modules/@types/leaflet-providers": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@types/leaflet-providers/-/leaflet-providers-1.2.0.tgz", + "integrity": "sha512-xuIUo0rEtuKdKIWwtLH0mD+dgSy/CRspVPa0nI/SuPLS29H7q+GPO4Qluxzkk+u9qvMtTjxqkrJjtnsx96+aPQ==", + "dependencies": { + "@types/leaflet": "*" + } + }, + "node_modules/@types/leaflet.markercluster": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/@types/leaflet.markercluster/-/leaflet.markercluster-1.4.4.tgz", + "integrity": "sha512-BQAilNWlBpYl4+PrsJXLOh4vyv7KfWi5kh3Fclg5y4gEeNeXKqhS6y1zzBB4+wcTuVUnMWfm2G0MfqA4yA5A5A==", + "dependencies": { + "@types/leaflet": "*" + } + }, + "node_modules/@types/lz-string": { + "version": "1.3.34", + "resolved": "https://registry.npmjs.org/@types/lz-string/-/lz-string-1.3.34.tgz", + "integrity": "sha512-j6G1e8DULJx3ONf6NdR5JiR2ZY3K3PaaqiEuKYkLQO0Czfi1AzrtjfnfCROyWGeDd5IVMKCwsgSmMip9OWijow==" + }, + "node_modules/@types/node": { + "version": "7.10.14", + "resolved": "https://registry.npmjs.org/@types/node/-/node-7.10.14.tgz", + "integrity": "sha512-29GS75BE8asnTno3yB6ubOJOO0FboExEqNJy4bpz0GSmW/8wPTNL4h9h63c6s1uTrOopCmJYe/4yJLh5r92ZUA==", + "dev": true + }, + "node_modules/@types/parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==" + }, + "node_modules/@types/prompt-sync": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@types/prompt-sync/-/prompt-sync-4.1.0.tgz", + "integrity": "sha1-utMynv9bQRXjTvRpgjckTUEdRHA=" + }, + "node_modules/@types/q": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/@types/q/-/q-1.5.4.tgz", + "integrity": "sha512-1HcDas8SEj4z1Wc696tH56G8OlRaH/sqZOynNNB+HF0WOeXPaxTtbYzJY2oEfiUxjSKjhCKr+MvR7dCHcEelug==" + }, + "node_modules/@types/raf": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/@types/raf/-/raf-3.4.0.tgz", + "integrity": "sha512-taW5/WYqo36N7V39oYyHP9Ipfd5pNFvGTIQsNGj86xV88YQ7GnI30/yMfKDF7Zgin0m3e+ikX88FvImnK4RjGw==", + "optional": true + }, + "node_modules/@types/sizzle": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/@types/sizzle/-/sizzle-2.3.3.tgz", + "integrity": "sha512-JYM8x9EGF163bEyhdJBpR2QX1R5naCJHC8ucJylJ3w9/CVBaskdQ8WqBf8MmQrd1kRvp/a4TS8HJ+bxzR7ZJYQ==" + }, + "node_modules/@types/strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha1-FKjsOVbC6B7bdSB5CuzyHCkK69I=", + "dev": true + }, + "node_modules/@types/strip-json-comments": { + "version": "0.0.30", + "resolved": "https://registry.npmjs.org/@types/strip-json-comments/-/strip-json-comments-0.0.30.tgz", + "integrity": "sha512-7NQmHra/JILCd1QqpSzl8+mJRc8ZHz3uDm8YV1Ks9IhK0epEiTw8aIErbvH9PI+6XbqhyIQy3462nEsn7UVzjQ==", + "dev": true + }, + "node_modules/abab": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.5.tgz", + "integrity": "sha512-9IK9EadsbHo6jLWIpxpR6pL0sazTXV6+SQv25ZB+F7Bj9mJNaOc4nCRabwd5M/JwmUa8idz6Eci6eKfJryPs6Q==" + }, + "node_modules/acorn": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", + "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-globals": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-4.3.4.tgz", + "integrity": "sha512-clfQEh21R+D0leSbUdWf3OcfqyaCSAQ8Ryq00bofSekfr9W8u1jyYZo6ir0xu9Gtcf7BjcHJpnbZH7JOCpP60A==", + "dependencies": { + "acorn": "^6.0.1", + "acorn-walk": "^6.0.1" + } + }, + "node_modules/acorn-globals/node_modules/acorn": { + "version": "6.4.2", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.4.2.tgz", + "integrity": "sha512-XtGIhXwF8YM8bJhGxG5kXgjkEuNGLTkoYqVE+KMR+aspr4KGYmKYg7yUe3KghyQ9yheNwLnjmzh/7+gfDBmHCQ==", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-globals/node_modules/acorn-walk": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-6.2.0.tgz", + "integrity": "sha512-7evsyfH1cLOCdAzZAd43Cic04yKydNx0cF+7tiA19p1XnLLPU4dpCQOqpjqwokFe//vS0QqfqqjCS2JkiIs0cA==", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-node": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/acorn-node/-/acorn-node-1.8.2.tgz", + "integrity": "sha512-8mt+fslDufLYntIoPAaIMUe/lrbrehIiwmR3t2k9LljIzoigEPF27eLk2hy8zSGzmR/ogr7zbRKINMo1u0yh5A==", + "dependencies": { + "acorn": "^7.0.0", + "acorn-walk": "^7.0.0", + "xtend": "^4.0.2" + } + }, + "node_modules/acorn-walk": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-7.2.0.tgz", + "integrity": "sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/adiff": { + "version": "0.2.13", + "resolved": "https://registry.npmjs.org/adiff/-/adiff-0.2.13.tgz", + "integrity": "sha1-3D3TL5RNl/J366WM5SmXrf8fdyg=", + "dev": true + }, + "node_modules/affine-hull": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/affine-hull/-/affine-hull-1.0.0.tgz", + "integrity": "sha1-dj/x040GPOt+Jy8X7k17vK+QXF0=", + "dependencies": { + "robust-orientation": "^1.1.3" + } + }, + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/alphanum-sort": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/alphanum-sort/-/alphanum-sort-1.0.2.tgz", + "integrity": "sha1-l6ERlkmyEa0zaR2fn0hqjsn74KM=" + }, + "node_modules/ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", + "engines": { + "node": ">=4" + } + }, + "node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/ansi-to-html": { + "version": "0.6.14", + "resolved": "https://registry.npmjs.org/ansi-to-html/-/ansi-to-html-0.6.14.tgz", + "integrity": "sha512-7ZslfB1+EnFSDO5Ju+ue5Y6It19DRnZXWv8jrGHgIlPna5Mh4jz7BV5jCbQneXNFurQcKoolaaAjHtgSBfOIuA==", + "dependencies": { + "entities": "^1.1.2" + }, + "bin": { + "ansi-to-html": "bin/ansi-to-html" + }, + "engines": { + "node": "*" + } + }, + "node_modules/ansicolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/ansicolors/-/ansicolors-0.2.1.tgz", + "integrity": "sha1-vgiVmQl7dKXJxKhKDNvNtivYeu8=" + }, + "node_modules/anymatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", + "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/aproba": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", + "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==", + "dev": true + }, + "node_modules/are-we-there-yet": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz", + "integrity": "sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==", + "dev": true, + "dependencies": { + "delegates": "^1.0.0", + "readable-stream": "^2.0.6" + } + }, + "node_modules/arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==" + }, + "node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/arr-diff": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", + "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/arr-flatten": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz", + "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/arr-union": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz", + "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/array-equal": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/array-equal/-/array-equal-1.0.0.tgz", + "integrity": "sha1-jCpe8kcv2ep0KwTHenUJO6J1fJM=" + }, + "node_modules/array-filter": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/array-filter/-/array-filter-1.0.0.tgz", + "integrity": "sha1-uveeYubvTCpMC4MSMtr/7CUfnYM=", + "dev": true + }, + "node_modules/array-find-index": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-find-index/-/array-find-index-1.0.2.tgz", + "integrity": "sha1-3wEKoSh+Fku9pvlyOwqWoexBh6E=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/array-unique": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", + "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/asn1": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", + "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==", + "dependencies": { + "safer-buffer": "~2.1.0" + } + }, + "node_modules/asn1.js": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-5.4.1.tgz", + "integrity": "sha512-+I//4cYPccV8LdmBLiX8CYvf9Sp3vQsrqu2QNXRcrbiWvcx/UdlFiqUJJzxRQxgsZmvhXhn4cSKeSmoFjVdupA==", + "dependencies": { + "bn.js": "^4.0.0", + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0", + "safer-buffer": "^2.1.0" + } + }, + "node_modules/assert": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/assert/-/assert-2.0.0.tgz", + "integrity": "sha512-se5Cd+js9dXJnu6Ag2JFc00t+HmHOen+8Q+L7O9zI0PqQXr20uk2J0XQqMxZEeo5U50o8Nvmmx7dZrl+Ufr35A==", + "dev": true, + "dependencies": { + "es6-object-assign": "^1.1.0", + "is-nan": "^1.2.1", + "object-is": "^1.0.1", + "util": "^0.12.0" + } + }, + "node_modules/assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/assert/node_modules/util": { + "version": "0.12.3", + "resolved": "https://registry.npmjs.org/util/-/util-0.12.3.tgz", + "integrity": "sha512-I8XkoQwE+fPQEhy9v012V+TSdH2kp9ts29i20TaaDUXsg7x/onePbhFJUExBfv/2ay1ZOp/Vsm3nDlmnFGSAog==", + "dev": true, + "dependencies": { + "inherits": "^2.0.3", + "is-arguments": "^1.0.4", + "is-generator-function": "^1.0.7", + "is-typed-array": "^1.1.3", + "safe-buffer": "^5.1.2", + "which-typed-array": "^1.1.2" + } + }, + "node_modules/assign-symbols": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", + "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/async-each": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.3.tgz", + "integrity": "sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ==" + }, + "node_modules/async-limiter": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz", + "integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==" + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" + }, + "node_modules/atob": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", + "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==", + "bin": { + "atob": "bin/atob.js" + }, + "engines": { + "node": ">= 4.5.0" + } + }, + "node_modules/autoprefixer": { + "version": "10.3.5", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.3.5.tgz", + "integrity": "sha512-2H5kQSsyoOMdIehTzIt/sC9ZDIgWqlkG/dbevm9B9xQZ1TDPBHpNUDW5ENqqQQzuaBWEo75JkV0LJe+o5Lnr5g==", + "peer": true, + "dependencies": { + "browserslist": "^4.17.1", + "caniuse-lite": "^1.0.30001259", + "fraction.js": "^4.1.1", + "nanocolors": "^0.1.5", + "normalize-range": "^0.1.2", + "postcss-value-parser": "^4.1.0" + }, + "bin": { + "autoprefixer": "bin/autoprefixer" + }, + "engines": { + "node": "^10 || ^12 || >=14" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/available-typed-arrays": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.2.tgz", + "integrity": "sha512-XWX3OX8Onv97LMk/ftVyBibpGwY5a8SmuxZPzeOxqmuEqUCOM9ZE+uIaD1VNJ5QnvU2UQusvmKbuM1FR8QWGfQ==", + "dev": true, + "dependencies": { + "array-filter": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/aws-sign2": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", + "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=", + "engines": { + "node": "*" + } + }, + "node_modules/aws4": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.11.0.tgz", + "integrity": "sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==" + }, + "node_modules/axios": { + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.19.2.tgz", + "integrity": "sha512-fjgm5MvRHLhx+osE2xoekY70AhARk3a6hkN+3Io1jc00jtquGvxYlKlsFUhmUET0V5te6CcZI7lcv2Ym61mjHA==", + "deprecated": "Critical security vulnerability fixed in v0.21.1. For more information, see https://github.com/axios/axios/pull/3410", + "dependencies": { + "follow-redirects": "1.5.10" + } + }, + "node_modules/babel-plugin-dynamic-import-node": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.3.tgz", + "integrity": "sha512-jZVI+s9Zg3IqA/kdi0i6UDCybUI3aSBLnglhYbSSjKlV7yF1F/5LWv8MakQmvYpnbJDS6fcBL2KzHSxNCMtWSQ==", + "dependencies": { + "object.assign": "^4.1.0" + } + }, + "node_modules/babel-plugin-polyfill-corejs2": { + "version": "0.1.10", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.1.10.tgz", + "integrity": "sha512-DO95wD4g0A8KRaHKi0D51NdGXzvpqVLnLu5BTvDlpqUEpTmeEtypgC1xqesORaWmiUOQI14UHKlzNd9iZ2G3ZA==", + "dependencies": { + "@babel/compat-data": "^7.13.0", + "@babel/helper-define-polyfill-provider": "^0.1.5", + "semver": "^6.1.1" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/babel-plugin-polyfill-corejs3": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.1.7.tgz", + "integrity": "sha512-u+gbS9bbPhZWEeyy1oR/YaaSpod/KDT07arZHb80aTpl8H5ZBq+uN1nN9/xtX7jQyfLdPfoqI4Rue/MQSWJquw==", + "dependencies": { + "@babel/helper-define-polyfill-provider": "^0.1.5", + "core-js-compat": "^3.8.1" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/babel-plugin-polyfill-regenerator": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.1.6.tgz", + "integrity": "sha512-OUrYG9iKPKz8NxswXbRAdSwF0GhRdIEMTloQATJi4bDuFqrXaXcCUT/VGNrr8pBcjMh1RxZ7Xt9cytVJTJfvMg==", + "dependencies": { + "@babel/helper-define-polyfill-provider": "^0.1.5" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/babel-runtime": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz", + "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", + "dependencies": { + "core-js": "^2.4.0", + "regenerator-runtime": "^0.11.0" + } + }, + "node_modules/babel-runtime/node_modules/regenerator-runtime": { + "version": "0.11.1", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz", + "integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==" + }, + "node_modules/babel-types": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-types/-/babel-types-6.26.0.tgz", + "integrity": "sha1-o7Bz+Uq0nrb6Vc1lInozQ4BjJJc=", + "dependencies": { + "babel-runtime": "^6.26.0", + "esutils": "^2.0.2", + "lodash": "^4.17.4", + "to-fast-properties": "^1.0.3" + } + }, + "node_modules/babel-types/node_modules/to-fast-properties": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-1.0.3.tgz", + "integrity": "sha1-uDVx+k2MJbguIxsG46MFXeTKGkc=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/babylon-walk": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/babylon-walk/-/babylon-walk-1.0.2.tgz", + "integrity": "sha1-OxWl3btIKni0zpwByLoYFwLZ1s4=", + "dependencies": { + "babel-runtime": "^6.11.6", + "babel-types": "^6.15.0", + "lodash.clone": "^4.5.0" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" + }, + "node_modules/base": { + "version": "0.11.2", + "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz", + "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==", + "dependencies": { + "cache-base": "^1.0.1", + "class-utils": "^0.3.5", + "component-emitter": "^1.2.1", + "define-property": "^1.0.0", + "isobject": "^3.0.1", + "mixin-deep": "^1.2.0", + "pascalcase": "^0.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/base/node_modules/define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "dependencies": { + "is-descriptor": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/base/node_modules/is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dependencies": { + "kind-of": "^6.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/base/node_modules/is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dependencies": { + "kind-of": "^6.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/base/node_modules/is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dependencies": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/base64-arraybuffer": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-0.2.0.tgz", + "integrity": "sha512-7emyCsu1/xiBXgQZrscw/8KPRT44I4Yq9Pe6EGs3aPRTsWuggML1/1DTuZUuIaJPIm1FTDUVXl4x/yW8s0kQDQ==", + "optional": true, + "engines": { + "node": ">= 0.6.0" + } + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/bcrypt-pbkdf": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", + "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", + "dependencies": { + "tweetnacl": "^0.14.3" + } + }, + "node_modules/binary-extensions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", + "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "engines": { + "node": ">=8" + } + }, + "node_modules/bindings": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", + "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", + "dependencies": { + "file-uri-to-path": "1.0.0" + } + }, + "node_modules/bit-twiddle": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/bit-twiddle/-/bit-twiddle-1.0.2.tgz", + "integrity": "sha1-DGwfq+KyPRcXPZpht7cJPrnhdp4=" + }, + "node_modules/bl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "dev": true, + "dependencies": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, + "node_modules/bl/node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "node_modules/bl/node_modules/readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "dev": true, + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" + }, + "node_modules/boolbase": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", + "integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24=" + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dependencies": { + "fill-range": "^7.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/brfs": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/brfs/-/brfs-1.6.1.tgz", + "integrity": "sha512-OfZpABRQQf+Xsmju8XE9bDjs+uU4vLREGolP7bDgcpsI17QREyZ4Bl+2KLxxx1kCgA0fAIhKQBaBYh+PEcCqYQ==", + "dependencies": { + "quote-stream": "^1.0.1", + "resolve": "^1.1.5", + "static-module": "^2.2.0", + "through2": "^2.0.0" + }, + "bin": { + "brfs": "bin/cmd.js" + } + }, + "node_modules/brorand": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", + "integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=" + }, + "node_modules/browser-process-hrtime": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz", + "integrity": "sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow==" + }, + "node_modules/browserify-aes": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz", + "integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==", + "dependencies": { + "buffer-xor": "^1.0.3", + "cipher-base": "^1.0.0", + "create-hash": "^1.1.0", + "evp_bytestokey": "^1.0.3", + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/browserify-cipher": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/browserify-cipher/-/browserify-cipher-1.0.1.tgz", + "integrity": "sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w==", + "dependencies": { + "browserify-aes": "^1.0.4", + "browserify-des": "^1.0.0", + "evp_bytestokey": "^1.0.0" + } + }, + "node_modules/browserify-des": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/browserify-des/-/browserify-des-1.0.2.tgz", + "integrity": "sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A==", + "dependencies": { + "cipher-base": "^1.0.1", + "des.js": "^1.0.0", + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "node_modules/browserify-rsa": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.1.0.tgz", + "integrity": "sha512-AdEER0Hkspgno2aR97SAf6vi0y0k8NuOpGnVH3O99rcA5Q6sh8QxcngtHuJ6uXwnfAXNM4Gn1Gb7/MV1+Ymbog==", + "dependencies": { + "bn.js": "^5.0.0", + "randombytes": "^2.0.1" + } + }, + "node_modules/browserify-rsa/node_modules/bn.js": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.0.tgz", + "integrity": "sha512-D7iWRBvnZE8ecXiLj/9wbxH7Tk79fAh8IHaTNq1RWRixsS02W+5qS+iE9yq6RYl0asXx5tw0bLhmT5pIfbSquw==" + }, + "node_modules/browserify-sign": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.2.1.tgz", + "integrity": "sha512-/vrA5fguVAKKAVTNJjgSm1tRQDHUU6DbwO9IROu/0WAzC8PKhucDSh18J0RMvVeHAn5puMd+QHC2erPRNf8lmg==", + "dependencies": { + "bn.js": "^5.1.1", + "browserify-rsa": "^4.0.1", + "create-hash": "^1.2.0", + "create-hmac": "^1.1.7", + "elliptic": "^6.5.3", + "inherits": "^2.0.4", + "parse-asn1": "^5.1.5", + "readable-stream": "^3.6.0", + "safe-buffer": "^5.2.0" + } + }, + "node_modules/browserify-sign/node_modules/bn.js": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.0.tgz", + "integrity": "sha512-D7iWRBvnZE8ecXiLj/9wbxH7Tk79fAh8IHaTNq1RWRixsS02W+5qS+iE9yq6RYl0asXx5tw0bLhmT5pIfbSquw==" + }, + "node_modules/browserify-sign/node_modules/readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/browserify-zlib": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/browserify-zlib/-/browserify-zlib-0.2.0.tgz", + "integrity": "sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA==", + "dependencies": { + "pako": "~1.0.5" + } + }, + "node_modules/browserify-zlib/node_modules/pako": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", + "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==" + }, + "node_modules/browserslist": { + "version": "4.17.1", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.17.1.tgz", + "integrity": "sha512-aLD0ZMDSnF4lUt4ZDNgqi5BUn9BZ7YdQdI/cYlILrhdSSZJLU9aNZoD5/NBmM4SK34APB2e83MOsRt1EnkuyaQ==", + "dependencies": { + "caniuse-lite": "^1.0.30001259", + "electron-to-chromium": "^1.3.846", + "escalade": "^3.1.1", + "nanocolors": "^0.1.5", + "node-releases": "^1.1.76" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + } + }, + "node_modules/btoa": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/btoa/-/btoa-1.2.1.tgz", + "integrity": "sha512-SB4/MIGlsiVkMcHmT+pSmIPoNDoHg+7cMzmt3Uxt628MTz2487DKSqK/fuhFBrkuqrYv5UCEnACpF4dTFNKc/g==", + "bin": { + "btoa": "bin/btoa.js" + }, + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/buffer": { + "version": "4.9.2", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.2.tgz", + "integrity": "sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg==", + "dependencies": { + "base64-js": "^1.0.2", + "ieee754": "^1.1.4", + "isarray": "^1.0.0" + } + }, + "node_modules/buffer-equal": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal/-/buffer-equal-0.0.1.tgz", + "integrity": "sha1-kbx0sR6kBbyRa8aqkI+q+ltKrEs=", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/buffer-equal-constant-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", + "integrity": "sha1-+OcRMvf/5uAaXJaXpMbz5I1cyBk=" + }, + "node_modules/buffer-from": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", + "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==" + }, + "node_modules/buffer-xor": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz", + "integrity": "sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk=" + }, + "node_modules/builtin-modules": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz", + "integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/builtin-status-codes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz", + "integrity": "sha1-hZgoeOIbmOHGZCXgPQF0eI9Wnug=" + }, + "node_modules/bytes": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", + "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/cache-base": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz", + "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==", + "dependencies": { + "collection-visit": "^1.0.0", + "component-emitter": "^1.2.1", + "get-value": "^2.0.6", + "has-value": "^1.0.0", + "isobject": "^3.0.1", + "set-value": "^2.0.0", + "to-object-path": "^0.3.0", + "union-value": "^1.0.0", + "unset-value": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/call-bind": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", + "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "dependencies": { + "function-bind": "^1.1.1", + "get-intrinsic": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/call-me-maybe": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/call-me-maybe/-/call-me-maybe-1.0.1.tgz", + "integrity": "sha1-JtII6onje1y95gJQoV8DHBak1ms=" + }, + "node_modules/caller-callsite": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/caller-callsite/-/caller-callsite-2.0.0.tgz", + "integrity": "sha1-hH4PzgoiN1CpoCfFSzNzGtMVQTQ=", + "dependencies": { + "callsites": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/caller-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/caller-path/-/caller-path-2.0.0.tgz", + "integrity": "sha1-Ro+DBE42mrIBD6xfBs7uFbsssfQ=", + "dependencies": { + "caller-callsite": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/callsites": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-2.0.0.tgz", + "integrity": "sha1-BuuE8A7qQT2oav/vrL/7Ngk7PFA=", + "engines": { + "node": ">=4" + } + }, + "node_modules/camelcase": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-2.1.1.tgz", + "integrity": "sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/camelcase-css": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz", + "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==", + "engines": { + "node": ">= 6" + } + }, + "node_modules/camelcase-keys": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-2.1.0.tgz", + "integrity": "sha1-MIvur/3ygRkFHvodkyITyRuPkuc=", + "dev": true, + "dependencies": { + "camelcase": "^2.0.0", + "map-obj": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/caniuse-api": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/caniuse-api/-/caniuse-api-3.0.0.tgz", + "integrity": "sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw==", + "dependencies": { + "browserslist": "^4.0.0", + "caniuse-lite": "^1.0.0", + "lodash.memoize": "^4.1.2", + "lodash.uniq": "^4.5.0" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001259", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001259.tgz", + "integrity": "sha512-V7mQTFhjITxuk9zBpI6nYsiTXhcPe05l+364nZjK7MFK/E7ibvYBSAXr4YcA6oPR8j3ZLM/LN+lUqUVAQEUZFg==", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + } + }, + "node_modules/canvg": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/canvg/-/canvg-3.0.7.tgz", + "integrity": "sha512-4sq6iL5Q4VOXS3PL1BapiXIZItpxYyANVzsAKpTPS5oq4u3SKbGfUcbZh2gdLCQ3jWpG/y5wRkMlBBAJhXeiZA==", + "optional": true, + "dependencies": { + "@babel/runtime-corejs3": "^7.9.6", + "@types/raf": "^3.4.0", + "raf": "^3.4.1", + "rgbcolor": "^1.0.1", + "stackblur-canvas": "^2.0.0", + "svg-pathdata": "^5.0.5" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/cardinal": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/cardinal/-/cardinal-0.4.4.tgz", + "integrity": "sha1-ylu2iltRG5D+k7ms6km97lwyv+I=", + "dependencies": { + "ansicolors": "~0.2.1", + "redeyed": "~0.4.0" + }, + "bin": { + "cdl": "bin/cdl.js" + } + }, + "node_modules/caseless": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", + "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=" + }, + "node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/chokidar": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.2.tgz", + "integrity": "sha512-ekGhOnNVPgT77r4K/U3GDhu+FQ2S8TnK/s2KbIGXi0SZWuwkZ2QNyfWdZW+TVfn84DpEP7rLeCt2UI6bJ8GwbQ==", + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/chownr": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", + "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", + "dev": true + }, + "node_modules/cipher-base": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz", + "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==", + "dependencies": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/class-utils": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz", + "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==", + "dependencies": { + "arr-union": "^3.1.0", + "define-property": "^0.2.5", + "isobject": "^3.0.0", + "static-extend": "^0.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/class-utils/node_modules/define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dependencies": { + "is-descriptor": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/cli-cursor": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz", + "integrity": "sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU=", + "dependencies": { + "restore-cursor": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/cli-spinners": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-1.3.1.tgz", + "integrity": "sha512-1QL4544moEsDVH9T/l6Cemov/37iv1RtoKf7NJ04A60+4MREXNfx/QvavbH6QoGdsD4N4Mwy49cmaINR/o2mdg==", + "engines": { + "node": ">=4" + } + }, + "node_modules/clone": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz", + "integrity": "sha1-G39Ln1kfHo+DZwQBYANFoCiHQ18=", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/closure": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/closure/-/closure-1.0.3.tgz", + "integrity": "sha1-5BD7MJWJaIGHfTeLjhSV93S2HEk=", + "engines": { + "node": ">=0.3.2" + } + }, + "node_modules/coa": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/coa/-/coa-2.0.2.tgz", + "integrity": "sha512-q5/jG+YQnSy4nRTV4F7lPepBJZ8qBNJJDBuJdoejDyLXgmL7IEo+Le2JDZudFTFt7mrCqIRaSjws4ygRCTCAXA==", + "dependencies": { + "@types/q": "^1.5.1", + "chalk": "^2.4.1", + "q": "^1.1.2" + }, + "engines": { + "node": ">= 4.0" + } + }, + "node_modules/code-point-at": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", + "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/collection-visit": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz", + "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=", + "dependencies": { + "map-visit": "^1.0.0", + "object-visit": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/color": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/color/-/color-3.1.3.tgz", + "integrity": "sha512-xgXAcTHa2HeFCGLE9Xs/R82hujGtu9Jd9x4NW3T34+OMs7VoPsjwzRczKHvTAHeJwWFwX5j15+MgAppE8ztObQ==", + "dependencies": { + "color-convert": "^1.9.1", + "color-string": "^1.5.4" + } + }, + "node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" + }, + "node_modules/color-string": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.6.0.tgz", + "integrity": "sha512-c/hGS+kRWJutUBEngKKmk4iH3sD59MBkoxVapS/0wgpCz2u7XsNloxknyvBhzwEs1IbV36D9PwqLPJ2DTu3vMA==", + "dependencies": { + "color-name": "^1.0.0", + "simple-swizzle": "^0.2.2" + } + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/command-exists": { + "version": "1.2.9", + "resolved": "https://registry.npmjs.org/command-exists/-/command-exists-1.2.9.tgz", + "integrity": "sha512-LTQ/SGc+s0Xc0Fu5WaKnR0YiygZkm9eKFvyS+fRsU7/ZWFF8ykFM6Pc9aCVf1+xasOOZpO3BAVgVrKvsqKHV7w==" + }, + "node_modules/commander": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-6.2.1.tgz", + "integrity": "sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==", + "engines": { + "node": ">= 6" + } + }, + "node_modules/component-emitter": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", + "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==" + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" + }, + "node_modules/concat-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-2.0.0.tgz", + "integrity": "sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A==", + "engines": [ + "node >= 6.0" + ], + "dependencies": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.0.2", + "typedarray": "^0.0.6" + } + }, + "node_modules/concat-stream/node_modules/readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/concaveman": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/concaveman/-/concaveman-1.2.0.tgz", + "integrity": "sha512-OcqechF2/kubbffomKqjGEkb0ndlYhEbmyg/fxIGqdfYp5AZjD2Kl5hc97Hh3ngEuHU2314Z4KDbxL7qXGWrQQ==", + "dependencies": { + "point-in-polygon": "^1.0.1", + "rbush": "^3.0.0", + "robust-predicates": "^2.0.4", + "tinyqueue": "^2.0.3" + } + }, + "node_modules/concaveman/node_modules/quickselect": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/quickselect/-/quickselect-2.0.0.tgz", + "integrity": "sha512-RKJ22hX8mHe3Y6wH/N3wCM6BWtjaxIyyUIkpHOvfFnxdI4yD4tBXEBKSbriGujF6jnSVkJrffuo6vxACiSSxIw==" + }, + "node_modules/concaveman/node_modules/rbush": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/rbush/-/rbush-3.0.1.tgz", + "integrity": "sha512-XRaVO0YecOpEuIvbhbpTrZgoiI6xBlz6hnlr6EHhd+0x9ase6EmeN+hdwwUaJvLcsFFQ8iWVF1GAK1yB0BWi0w==", + "dependencies": { + "quickselect": "^2.0.0" + } + }, + "node_modules/console-browserify": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.2.0.tgz", + "integrity": "sha512-ZMkYO/LkF17QvCPqM0gxw8yUzigAOZOSWSHg91FH6orS7vcEj5dVZTidN2fQ14yBSdg97RqhSNwLUXInd52OTA==" + }, + "node_modules/console-control-strings": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", + "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=", + "dev": true + }, + "node_modules/constants-browserify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/constants-browserify/-/constants-browserify-1.0.0.tgz", + "integrity": "sha1-wguW2MYXdIqvHBYCF2DNJ/y4y3U=" + }, + "node_modules/convert-source-map": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.7.0.tgz", + "integrity": "sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA==", + "dependencies": { + "safe-buffer": "~5.1.1" + } + }, + "node_modules/convert-source-map/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "node_modules/convex-hull": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/convex-hull/-/convex-hull-1.0.3.tgz", + "integrity": "sha1-IKOqbOh/St6i/30XlxyfwcZ+H/8=", + "dependencies": { + "affine-hull": "^1.0.0", + "incremental-convex-hull": "^1.0.1", + "monotone-convex-hull-2d": "^1.0.1" + } + }, + "node_modules/copy-descriptor": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz", + "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/core-js": { + "version": "2.6.12", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.12.tgz", + "integrity": "sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ==", + "deprecated": "core-js@<3.3 is no longer maintained and not recommended for usage due to the number of issues. Because of the V8 engine whims, feature detection in old core-js versions could cause a slowdown up to 100x even if nothing is polyfilled. Please, upgrade your dependencies to the actual version of core-js.", + "hasInstallScript": true + }, + "node_modules/core-js-compat": { + "version": "3.12.0", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.12.0.tgz", + "integrity": "sha512-vvaN8EOvYBEjrr+MN3vCKrMNc/xdYZI+Rt/uPMROi4T5Hj8Fz6TiPQm2mrB9aZoQVW1lCFHYmMrv99aUct9mkg==", + "dependencies": { + "browserslist": "^4.16.6", + "semver": "7.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" + } + }, + "node_modules/core-js-compat/node_modules/semver": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.0.0.tgz", + "integrity": "sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A==", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/core-js-pure": { + "version": "3.15.2", + "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.15.2.tgz", + "integrity": "sha512-D42L7RYh1J2grW8ttxoY1+17Y4wXZeKe7uyplAI3FkNQyI5OgBIAjUfFiTPfL1rs0qLpxaabITNbjKl1Sp82tA==", + "hasInstallScript": true, + "optional": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" + } + }, + "node_modules/core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" + }, + "node_modules/cosmiconfig": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-5.2.1.tgz", + "integrity": "sha512-H65gsXo1SKjf8zmrJ67eJk8aIRKV5ff2D4uKZIBZShbhGSpEmsQOPW/SKMKYhSTrqR7ufy6RP69rPogdaPh/kA==", + "dependencies": { + "import-fresh": "^2.0.0", + "is-directory": "^0.3.1", + "js-yaml": "^3.13.1", + "parse-json": "^4.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/country-language": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/country-language/-/country-language-0.1.7.tgz", + "integrity": "sha1-eHD0uhJduaYHHxlze9nvk0OuNds=", + "dependencies": { + "underscore": "~1.7.0", + "underscore.deep": "~0.5.1" + }, + "engines": { + "node": "*" + } + }, + "node_modules/create-ecdh": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.4.tgz", + "integrity": "sha512-mf+TCx8wWc9VpuxfP2ht0iSISLZnt0JgWlrOKZiNqyUZWnjIaCIVNQArMHnCZKfEYRg6IM7A+NeJoN8gf/Ws0A==", + "dependencies": { + "bn.js": "^4.1.0", + "elliptic": "^6.5.3" + } + }, + "node_modules/create-hash": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", + "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==", + "dependencies": { + "cipher-base": "^1.0.1", + "inherits": "^2.0.1", + "md5.js": "^1.3.4", + "ripemd160": "^2.0.1", + "sha.js": "^2.4.0" + } + }, + "node_modules/create-hmac": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", + "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==", + "dependencies": { + "cipher-base": "^1.0.3", + "create-hash": "^1.1.0", + "inherits": "^2.0.1", + "ripemd160": "^2.0.0", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + } + }, + "node_modules/create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==" + }, + "node_modules/cross-spawn": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", + "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", + "dependencies": { + "nice-try": "^1.0.4", + "path-key": "^2.0.1", + "semver": "^5.5.0", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + }, + "engines": { + "node": ">=4.8" + } + }, + "node_modules/cross-spawn/node_modules/semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/crypto-browserify": { + "version": "3.12.0", + "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.12.0.tgz", + "integrity": "sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg==", + "dependencies": { + "browserify-cipher": "^1.0.0", + "browserify-sign": "^4.0.0", + "create-ecdh": "^4.0.0", + "create-hash": "^1.1.0", + "create-hmac": "^1.1.0", + "diffie-hellman": "^5.0.0", + "inherits": "^2.0.1", + "pbkdf2": "^3.0.3", + "public-encrypt": "^4.0.0", + "randombytes": "^2.0.0", + "randomfill": "^1.0.3" + }, + "engines": { + "node": "*" + } + }, + "node_modules/css-color-names": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/css-color-names/-/css-color-names-0.0.4.tgz", + "integrity": "sha1-gIrcLnnPhHOAabZGyyDsJ762KeA=", + "engines": { + "node": "*" + } + }, + "node_modules/css-declaration-sorter": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/css-declaration-sorter/-/css-declaration-sorter-4.0.1.tgz", + "integrity": "sha512-BcxQSKTSEEQUftYpBVnsH4SF05NTuBokb19/sBt6asXGKZ/6VP7PLG1CBCkFDYOnhXhPh0jMhO6xZ71oYHXHBA==", + "dependencies": { + "postcss": "^7.0.1", + "timsort": "^0.3.0" + }, + "engines": { + "node": ">4" + } + }, + "node_modules/css-declaration-sorter/node_modules/postcss": { + "version": "7.0.36", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.36.tgz", + "integrity": "sha512-BebJSIUMwJHRH0HAQoxN4u1CN86glsrwsW0q7T+/m44eXOUAxSNdHRkNZPYz5vVUbg17hFgOQDE7fZk7li3pZw==", + "dependencies": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, + "node_modules/css-declaration-sorter/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/css-declaration-sorter/node_modules/supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/css-line-break": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/css-line-break/-/css-line-break-1.1.1.tgz", + "integrity": "sha512-1feNVaM4Fyzdj4mKPIQNL2n70MmuYzAXZ1aytlROFX1JsOo070OsugwGjj7nl6jnDJWHDM8zRZswkmeYVWZJQA==", + "optional": true, + "dependencies": { + "base64-arraybuffer": "^0.2.0" + } + }, + "node_modules/css-modules-loader-core": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/css-modules-loader-core/-/css-modules-loader-core-1.1.0.tgz", + "integrity": "sha1-WQhmgpShvs0mGuCkziGwtVHyHRY=", + "dependencies": { + "icss-replace-symbols": "1.1.0", + "postcss": "6.0.1", + "postcss-modules-extract-imports": "1.1.0", + "postcss-modules-local-by-default": "1.2.0", + "postcss-modules-scope": "1.1.0", + "postcss-modules-values": "1.3.0" + } + }, + "node_modules/css-modules-loader-core/node_modules/ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/css-modules-loader-core/node_modules/ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/css-modules-loader-core/node_modules/chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "dependencies": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/css-modules-loader-core/node_modules/chalk/node_modules/supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/css-modules-loader-core/node_modules/has-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", + "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/css-modules-loader-core/node_modules/postcss": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.1.tgz", + "integrity": "sha1-AA29H47vIXqjaLmiEsX8QLKo8/I=", + "dependencies": { + "chalk": "^1.1.3", + "source-map": "^0.5.6", + "supports-color": "^3.2.3" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/css-modules-loader-core/node_modules/strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dependencies": { + "ansi-regex": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/css-modules-loader-core/node_modules/supports-color": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", + "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", + "dependencies": { + "has-flag": "^1.0.0" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/css-select": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-2.1.0.tgz", + "integrity": "sha512-Dqk7LQKpwLoH3VovzZnkzegqNSuAziQyNZUcrdDM401iY+R5NkGBXGmtO05/yaXQziALuPogeG0b7UAgjnTJTQ==", + "dependencies": { + "boolbase": "^1.0.0", + "css-what": "^3.2.1", + "domutils": "^1.7.0", + "nth-check": "^1.0.2" + } + }, + "node_modules/css-select-base-adapter": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/css-select-base-adapter/-/css-select-base-adapter-0.1.1.tgz", + "integrity": "sha512-jQVeeRG70QI08vSTwf1jHxp74JoZsr2XSgETae8/xC8ovSnL2WF87GTLO86Sbwdt2lK4Umg4HnnwMO4YF3Ce7w==" + }, + "node_modules/css-select/node_modules/domutils": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.7.0.tgz", + "integrity": "sha512-Lgd2XcJ/NjEw+7tFvfKxOzCYKZsdct5lczQ2ZaQY8Djz7pfAD3Gbp8ySJWtreII/vDlMVmxwa6pHmdxIYgttDg==", + "dependencies": { + "dom-serializer": "0", + "domelementtype": "1" + } + }, + "node_modules/css-selector-tokenizer": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/css-selector-tokenizer/-/css-selector-tokenizer-0.7.3.tgz", + "integrity": "sha512-jWQv3oCEL5kMErj4wRnK/OPoBi0D+P1FR2cDCKYPaMeD2eW3/mttav8HT4hT1CKopiJI/psEULjkClhvJo4Lvg==", + "dependencies": { + "cssesc": "^3.0.0", + "fastparse": "^1.1.2" + } + }, + "node_modules/css-tree": { + "version": "1.0.0-alpha.37", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.0.0-alpha.37.tgz", + "integrity": "sha512-DMxWJg0rnz7UgxKT0Q1HU/L9BeJI0M6ksor0OgqOnF+aRCDWg/N2641HmVyU9KVIu0OVVWOb2IpC9A+BJRnejg==", + "dependencies": { + "mdn-data": "2.0.4", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/css-tree/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/css-unit-converter": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/css-unit-converter/-/css-unit-converter-1.1.2.tgz", + "integrity": "sha512-IiJwMC8rdZE0+xiEZHeru6YoONC4rfPMqGm2W85jMIbkFvv5nFTwJVFHam2eFrN6txmoUYFAFXiv8ICVeTO0MA==" + }, + "node_modules/css-what": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-3.4.2.tgz", + "integrity": "sha512-ACUm3L0/jiZTqfzRM3Hi9Q8eZqd6IK37mMWPLz9PJxkLWllYeRf+EHUSHYEtFop2Eqytaq1FizFVh7XfBnXCDQ==", + "engines": { + "node": ">= 6" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", + "bin": { + "cssesc": "bin/cssesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/cssnano": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/cssnano/-/cssnano-4.1.11.tgz", + "integrity": "sha512-6gZm2htn7xIPJOHY824ERgj8cNPgPxyCSnkXc4v7YvNW+TdVfzgngHcEhy/8D11kUWRUMbke+tC+AUcUsnMz2g==", + "dependencies": { + "cosmiconfig": "^5.0.0", + "cssnano-preset-default": "^4.0.8", + "is-resolvable": "^1.0.0", + "postcss": "^7.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/cssnano-preset-default": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/cssnano-preset-default/-/cssnano-preset-default-4.0.8.tgz", + "integrity": "sha512-LdAyHuq+VRyeVREFmuxUZR1TXjQm8QQU/ktoo/x7bz+SdOge1YKc5eMN6pRW7YWBmyq59CqYba1dJ5cUukEjLQ==", + "dependencies": { + "css-declaration-sorter": "^4.0.1", + "cssnano-util-raw-cache": "^4.0.1", + "postcss": "^7.0.0", + "postcss-calc": "^7.0.1", + "postcss-colormin": "^4.0.3", + "postcss-convert-values": "^4.0.1", + "postcss-discard-comments": "^4.0.2", + "postcss-discard-duplicates": "^4.0.2", + "postcss-discard-empty": "^4.0.1", + "postcss-discard-overridden": "^4.0.1", + "postcss-merge-longhand": "^4.0.11", + "postcss-merge-rules": "^4.0.3", + "postcss-minify-font-values": "^4.0.2", + "postcss-minify-gradients": "^4.0.2", + "postcss-minify-params": "^4.0.2", + "postcss-minify-selectors": "^4.0.2", + "postcss-normalize-charset": "^4.0.1", + "postcss-normalize-display-values": "^4.0.2", + "postcss-normalize-positions": "^4.0.2", + "postcss-normalize-repeat-style": "^4.0.2", + "postcss-normalize-string": "^4.0.2", + "postcss-normalize-timing-functions": "^4.0.2", + "postcss-normalize-unicode": "^4.0.1", + "postcss-normalize-url": "^4.0.1", + "postcss-normalize-whitespace": "^4.0.2", + "postcss-ordered-values": "^4.1.2", + "postcss-reduce-initial": "^4.0.3", + "postcss-reduce-transforms": "^4.0.2", + "postcss-svgo": "^4.0.3", + "postcss-unique-selectors": "^4.0.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/cssnano-preset-default/node_modules/postcss": { + "version": "7.0.36", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.36.tgz", + "integrity": "sha512-BebJSIUMwJHRH0HAQoxN4u1CN86glsrwsW0q7T+/m44eXOUAxSNdHRkNZPYz5vVUbg17hFgOQDE7fZk7li3pZw==", + "dependencies": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, + "node_modules/cssnano-preset-default/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/cssnano-preset-default/node_modules/supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/cssnano-util-get-arguments": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/cssnano-util-get-arguments/-/cssnano-util-get-arguments-4.0.0.tgz", + "integrity": "sha1-7ToIKZ8h11dBsg87gfGU7UnMFQ8=", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/cssnano-util-get-match": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/cssnano-util-get-match/-/cssnano-util-get-match-4.0.0.tgz", + "integrity": "sha1-wOTKB/U4a7F+xeUiULT1lhNlFW0=", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/cssnano-util-raw-cache": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/cssnano-util-raw-cache/-/cssnano-util-raw-cache-4.0.1.tgz", + "integrity": "sha512-qLuYtWK2b2Dy55I8ZX3ky1Z16WYsx544Q0UWViebptpwn/xDBmog2TLg4f+DBMg1rJ6JDWtn96WHbOKDWt1WQA==", + "dependencies": { + "postcss": "^7.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/cssnano-util-raw-cache/node_modules/postcss": { + "version": "7.0.36", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.36.tgz", + "integrity": "sha512-BebJSIUMwJHRH0HAQoxN4u1CN86glsrwsW0q7T+/m44eXOUAxSNdHRkNZPYz5vVUbg17hFgOQDE7fZk7li3pZw==", + "dependencies": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, + "node_modules/cssnano-util-raw-cache/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/cssnano-util-raw-cache/node_modules/supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/cssnano-util-same-parent": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/cssnano-util-same-parent/-/cssnano-util-same-parent-4.0.1.tgz", + "integrity": "sha512-WcKx5OY+KoSIAxBW6UBBRay1U6vkYheCdjyVNDm85zt5K9mHoGOfsOsqIszfAqrQQFIIKgjh2+FDgIj/zsl21Q==", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/cssnano/node_modules/postcss": { + "version": "7.0.36", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.36.tgz", + "integrity": "sha512-BebJSIUMwJHRH0HAQoxN4u1CN86glsrwsW0q7T+/m44eXOUAxSNdHRkNZPYz5vVUbg17hFgOQDE7fZk7li3pZw==", + "dependencies": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, + "node_modules/cssnano/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/cssnano/node_modules/supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/csso": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/csso/-/csso-4.2.0.tgz", + "integrity": "sha512-wvlcdIbf6pwKEk7vHj8/Bkc0B4ylXZruLvOgs9doS5eOsOpuodOV2zJChSpkp+pRpYQLQMeF04nr3Z68Sta9jA==", + "dependencies": { + "css-tree": "^1.1.2" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/csso/node_modules/css-tree": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.1.3.tgz", + "integrity": "sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q==", + "dependencies": { + "mdn-data": "2.0.14", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/csso/node_modules/mdn-data": { + "version": "2.0.14", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.14.tgz", + "integrity": "sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==" + }, + "node_modules/csso/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/cssom": { + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.8.tgz", + "integrity": "sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==" + }, + "node_modules/cssstyle": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-1.4.0.tgz", + "integrity": "sha512-GBrLZYZ4X4x6/QEoBnIrqb8B/f5l4+8me2dkom/j1Gtbxy0kBv6OGzKuAsGM75bkGwGAFkt56Iwg28S3XTZgSA==", + "dependencies": { + "cssom": "0.3.x" + } + }, + "node_modules/currently-unhandled": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/currently-unhandled/-/currently-unhandled-0.4.1.tgz", + "integrity": "sha1-mI3zP+qxke95mmE2nddsF635V+o=", + "dev": true, + "dependencies": { + "array-find-index": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/d3-array": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-1.2.4.tgz", + "integrity": "sha512-KHW6M86R+FUPYGb3R5XiYjXPq7VzwxZ22buHhAEVG5ztoEcZZMLov530mmccaqA1GghZArjQV46fuc8kUqhhHw==" + }, + "node_modules/d3-geo": { + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/d3-geo/-/d3-geo-1.7.1.tgz", + "integrity": "sha512-O4AempWAr+P5qbk2bC2FuN/sDW4z+dN2wDf9QV3bxQt4M5HfOEeXLgJ/UKQW0+o1Dj8BE+L5kiDbdWUMjsmQpw==", + "dependencies": { + "d3-array": "1" + } + }, + "node_modules/d3-voronoi": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/d3-voronoi/-/d3-voronoi-1.1.2.tgz", + "integrity": "sha1-Fodmfo8TotFYyAwUgMWinLDYlzw=" + }, + "node_modules/dashdash": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", + "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", + "dependencies": { + "assert-plus": "^1.0.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/data-urls": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-1.1.0.tgz", + "integrity": "sha512-YTWYI9se1P55u58gL5GkQHW4P6VJBJ5iBT+B5a7i2Tjadhv52paJG0qHX4A0OR6/t52odI64KP2YvFpkDOi3eQ==", + "dependencies": { + "abab": "^2.0.0", + "whatwg-mimetype": "^2.2.0", + "whatwg-url": "^7.0.0" + } + }, + "node_modules/dateformat": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-1.0.12.tgz", + "integrity": "sha1-nxJLZ1lMk3/3BpMuSmQsyo27/uk=", + "dev": true, + "dependencies": { + "get-stdin": "^4.0.1", + "meow": "^3.3.0" + }, + "bin": { + "dateformat": "bin/cli.js" + }, + "engines": { + "node": "*" + } + }, + "node_modules/deasync": { + "version": "0.1.21", + "resolved": "https://registry.npmjs.org/deasync/-/deasync-0.1.21.tgz", + "integrity": "sha512-kUmM8Y+PZpMpQ+B4AuOW9k2Pfx/mSupJtxOsLzmnHY2WqZUYRFccFn2RhzPAqt3Xb+sorK/badW2D4zNzqZz5w==", + "hasInstallScript": true, + "dependencies": { + "bindings": "^1.5.0", + "node-addon-api": "^1.7.1" + }, + "engines": { + "node": ">=0.11.0" + } + }, + "node_modules/debug": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", + "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/decode-uri-component": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", + "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=", + "engines": { + "node": ">=0.10" + } + }, + "node_modules/decompress-response": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-4.2.1.tgz", + "integrity": "sha512-jOSne2qbyE+/r8G1VU+G/82LBs2Fs4LAsTiLSHOCOMZQl2OKZ6i8i4IyHemTe+/yIXOtTcRQMzPcgyhoFlqPkw==", + "dev": true, + "dependencies": { + "mimic-response": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/deep-equal": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.1.1.tgz", + "integrity": "sha512-yd9c5AdiqVcR+JjcwUQb9DkhJc8ngNr0MahEBGvDiJw8puWab2yZlh+nkasOnZP+EGTAP6rRp2JzJhJZzvNF8g==", + "dependencies": { + "is-arguments": "^1.0.4", + "is-date-object": "^1.0.1", + "is-regex": "^1.0.4", + "object-is": "^1.0.1", + "object-keys": "^1.1.1", + "regexp.prototype.flags": "^1.2.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/deep-extend": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", + "dev": true, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/deep-is": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", + "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=" + }, + "node_modules/defaults": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.3.tgz", + "integrity": "sha1-xlYFHpgX2f8I7YgUd/P+QBnz730=", + "dependencies": { + "clone": "^1.0.2" + } + }, + "node_modules/defaults/node_modules/clone": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", + "integrity": "sha1-2jCcwmPfFZlMaIypAheco8fNfH4=", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/define-properties": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", + "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", + "dependencies": { + "object-keys": "^1.0.12" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/define-property": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", + "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", + "dependencies": { + "is-descriptor": "^1.0.2", + "isobject": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/define-property/node_modules/is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dependencies": { + "kind-of": "^6.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/define-property/node_modules/is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dependencies": { + "kind-of": "^6.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/define-property/node_modules/is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dependencies": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/defined": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/defined/-/defined-1.0.0.tgz", + "integrity": "sha1-yY2bzvdWdBiOEQlpFRGZ45sfppM=" + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/delegates": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", + "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=", + "dev": true + }, + "node_modules/density-clustering": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/density-clustering/-/density-clustering-1.3.0.tgz", + "integrity": "sha1-3J9ZyPCrl+FiSsZJMP0xlIF9ysU=" + }, + "node_modules/depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/des.js": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.0.1.tgz", + "integrity": "sha512-Q0I4pfFrv2VPd34/vfLrFOoRmlYj3OV50i7fskps1jZWK1kApMWWT9G6RRUeYedLcBDIhnSDaUvJMb3AhUlaEA==", + "dependencies": { + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0" + } + }, + "node_modules/destroy": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", + "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" + }, + "node_modules/detect-indent": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-6.1.0.tgz", + "integrity": "sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/detect-libc": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", + "integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=", + "dev": true, + "bin": { + "detect-libc": "bin/detect-libc.js" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/detective": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/detective/-/detective-5.2.0.tgz", + "integrity": "sha512-6SsIx+nUUbuK0EthKjv0zrdnajCCXVYGmbYYiYjFVpzcjwEs/JMDZ8tPRG29J/HhN56t3GJp2cGSWDRjjot8Pg==", + "dependencies": { + "acorn-node": "^1.6.1", + "defined": "^1.0.0", + "minimist": "^1.1.1" + }, + "bin": { + "detective": "bin/detective.js" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/didyoumean": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", + "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==" + }, + "node_modules/diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/diffie-hellman": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz", + "integrity": "sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==", + "dependencies": { + "bn.js": "^4.1.0", + "miller-rabin": "^4.0.0", + "randombytes": "^2.0.0" + } + }, + "node_modules/dlv": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", + "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==" + }, + "node_modules/dom-serializer": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.2.2.tgz", + "integrity": "sha512-2/xPb3ORsQ42nHYiSunXkDjPLBaEj/xTwUO4B7XCZQTRk7EBtTOPaygh10YAAh2OI1Qrp6NWfpAhzswj0ydt9g==", + "dependencies": { + "domelementtype": "^2.0.1", + "entities": "^2.0.0" + } + }, + "node_modules/dom-serializer/node_modules/domelementtype": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.2.0.tgz", + "integrity": "sha512-DtBMo82pv1dFtUmHyr48beiuq792Sxohr+8Hm9zoxklYPfa6n0Z3Byjj2IV7bmr2IyqClnqEQhfgHJJ5QF0R5A==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ] + }, + "node_modules/dom-serializer/node_modules/entities": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", + "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==", + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/dom-to-image-more": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/dom-to-image-more/-/dom-to-image-more-2.8.0.tgz", + "integrity": "sha512-YqlHI1i+TMuaKwkFRO5oDPjC3eWf+6Hln9rHZcnFYvmoXwCrGZmZ7BYXBJOjw5utYg2Lp+QF9YO96F7CsDC4eQ==" + }, + "node_modules/domain-browser": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-1.2.0.tgz", + "integrity": "sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA==", + "engines": { + "node": ">=0.4", + "npm": ">=1.2" + } + }, + "node_modules/domelementtype": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.1.tgz", + "integrity": "sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==" + }, + "node_modules/domexception": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/domexception/-/domexception-1.0.1.tgz", + "integrity": "sha512-raigMkn7CJNNo6Ihro1fzG7wr3fHuYVytzquZKX5n0yizGsTcYgzdIUwj1X9pK0VvjeihV+XiclP+DjwbsSKug==", + "dependencies": { + "webidl-conversions": "^4.0.2" + } + }, + "node_modules/domhandler": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.2.1.tgz", + "integrity": "sha1-Wd+dzSJ+gIs2Wuc+H2aErD2Ub8I=", + "dependencies": { + "domelementtype": "1" + } + }, + "node_modules/dompurify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-2.3.0.tgz", + "integrity": "sha512-VV5C6Kr53YVHGOBKO/F86OYX6/iLTw2yVSI721gKetxpHCK/V5TaLEf9ODjRgl1KLSWRMY6cUhAbv/c+IUnwQw==", + "optional": true + }, + "node_modules/domutils": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.3.0.tgz", + "integrity": "sha1-mtTVm1r2ymhMYv5tdo7xcOcN8ZI=", + "dependencies": { + "domelementtype": "1" + } + }, + "node_modules/dot-prop": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.3.0.tgz", + "integrity": "sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==", + "dependencies": { + "is-obj": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/dotenv": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-5.0.1.tgz", + "integrity": "sha512-4As8uPrjfwb7VXC+WnLCbXK7y+Ueb2B3zgNCePYfhxS1PYeaO1YTeplffTEcbfLhvFNGLAz90VvJs9yomG7bow==", + "engines": { + "node": ">=4.6.0" + } + }, + "node_modules/dotenv-expand": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/dotenv-expand/-/dotenv-expand-5.1.0.tgz", + "integrity": "sha512-YXQl1DSa4/PQyRfgrv6aoNjhasp/p4qs9FjJ4q4cQk+8m4r6k4ZSiEyytKG8f8W9gi8WsQtIObNmKd+tMzNTmA==" + }, + "node_modules/duplexer2": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz", + "integrity": "sha1-ixLauHjA1p4+eJEFFmKjL8a93ME=", + "dependencies": { + "readable-stream": "^2.0.2" + } + }, + "node_modules/dynamic-dedupe": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/dynamic-dedupe/-/dynamic-dedupe-0.3.0.tgz", + "integrity": "sha1-BuRMIj9eTpTXjvnbI6ZRXOL5YqE=", + "dev": true, + "dependencies": { + "xtend": "^4.0.0" + } + }, + "node_modules/earcut": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/earcut/-/earcut-2.2.2.tgz", + "integrity": "sha512-eZoZPPJcUHnfRZ0PjLvx2qBordSiO8ofC3vt+qACLM95u+4DovnbYNpQtJh0DNsWj8RnxrQytD4WA8gj5cRIaQ==" + }, + "node_modules/ecc-jsbn": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", + "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", + "dependencies": { + "jsbn": "~0.1.0", + "safer-buffer": "^2.1.0" + } + }, + "node_modules/ecdsa-sig-formatter": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", + "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", + "dependencies": { + "safe-buffer": "^5.0.1" + } + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" + }, + "node_modules/electron-to-chromium": { + "version": "1.3.846", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.846.tgz", + "integrity": "sha512-2jtSwgyiRzybHRxrc2nKI+39wH3AwQgn+sogQ+q814gv8hIFwrcZbV07Ea9f8AmK0ufPVZUvvAG1uZJ+obV4Jw==" + }, + "node_modules/elliptic": { + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.4.tgz", + "integrity": "sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ==", + "dependencies": { + "bn.js": "^4.11.9", + "brorand": "^1.1.0", + "hash.js": "^1.0.0", + "hmac-drbg": "^1.0.1", + "inherits": "^2.0.4", + "minimalistic-assert": "^1.0.1", + "minimalistic-crypto-utils": "^1.0.1" + } + }, + "node_modules/email-validator": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/email-validator/-/email-validator-2.0.4.tgz", + "integrity": "sha512-gYCwo7kh5S3IDyZPLZf6hSS0MnZT8QmJFqYvbqlDZSbwdZlY6QZWxJ4i/6UhITOJ4XzyI647Bm2MXKCLqnJ4nQ==", + "engines": { + "node": ">4.0" + } + }, + "node_modules/encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/end-of-stream": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "dev": true, + "dependencies": { + "once": "^1.4.0" + } + }, + "node_modules/entities": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.2.tgz", + "integrity": "sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==" + }, + "node_modules/envinfo": { + "version": "7.8.1", + "resolved": "https://registry.npmjs.org/envinfo/-/envinfo-7.8.1.tgz", + "integrity": "sha512-/o+BXHmB7ocbHEAs6F2EnG0ogybVVUdkRunTT2glZU9XAaGmhqskrvKwqXuDfNjEO0LZKWdejEEpnq8aM0tOaw==", + "bin": { + "envinfo": "dist/cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, + "node_modules/error-ex/node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=" + }, + "node_modules/es-abstract": { + "version": "1.18.0", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.0.tgz", + "integrity": "sha512-LJzK7MrQa8TS0ja2w3YNLzUgJCGPdPOV1yVvezjNnS89D+VR08+Szt2mz3YB2Dck/+w5tfIq/RoUAFqJJGM2yw==", + "dependencies": { + "call-bind": "^1.0.2", + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "get-intrinsic": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.2", + "is-callable": "^1.2.3", + "is-negative-zero": "^2.0.1", + "is-regex": "^1.1.2", + "is-string": "^1.0.5", + "object-inspect": "^1.9.0", + "object-keys": "^1.1.1", + "object.assign": "^4.1.2", + "string.prototype.trimend": "^1.0.4", + "string.prototype.trimstart": "^1.0.4", + "unbox-primitive": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/es-abstract/node_modules/object-inspect": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.10.2.tgz", + "integrity": "sha512-gz58rdPpadwztRrPjZE9DZLOABUpTGdcANUgOwBFO1C+HZZhePoP83M65WGDmbpwFYJSWqavbl4SgDn4k8RYTA==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/es-to-primitive": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", + "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", + "dependencies": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/es6-object-assign": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/es6-object-assign/-/es6-object-assign-1.1.0.tgz", + "integrity": "sha1-wsNYJlYkfDnqEHyx5mUrb58kUjw=", + "dev": true + }, + "node_modules/escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" + }, + "node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/escodegen": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.9.1.tgz", + "integrity": "sha512-6hTjO1NAWkHnDk3OqQ4YrCuwwmGHL9S3nPlzBOUG/R44rda3wLNrfvQ5fkSGjyhHFKM7ALPKcKGrwvCLe0lC7Q==", + "dependencies": { + "esprima": "^3.1.3", + "estraverse": "^4.2.0", + "esutils": "^2.0.2", + "optionator": "^0.8.1" + }, + "bin": { + "escodegen": "bin/escodegen.js", + "esgenerate": "bin/esgenerate.js" + }, + "engines": { + "node": ">=4.0" + }, + "optionalDependencies": { + "source-map": "~0.6.1" + } + }, + "node_modules/escodegen/node_modules/esprima": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-3.1.3.tgz", + "integrity": "sha1-/cpRzuYTOJXjyI1TXOSdv/YqRjM=", + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/escodegen/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "optional": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/esprima": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-1.0.4.tgz", + "integrity": "sha1-n1V+CPw7TSbs6d00+Pv0drYlha0=", + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", + "engines": { + "node": ">=0.8.x" + } + }, + "node_modules/evp_bytestokey": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz", + "integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==", + "dependencies": { + "md5.js": "^1.3.4", + "safe-buffer": "^5.1.1" + } + }, + "node_modules/expand-brackets": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", + "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", + "dependencies": { + "debug": "^2.3.3", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "posix-character-classes": "^0.1.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/expand-brackets/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/expand-brackets/node_modules/define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dependencies": { + "is-descriptor": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/expand-brackets/node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/expand-brackets/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + }, + "node_modules/expand-template": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz", + "integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" + }, + "node_modules/extend-shallow": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", + "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", + "dependencies": { + "assign-symbols": "^1.0.0", + "is-extendable": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/extend-shallow/node_modules/is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "dependencies": { + "is-plain-object": "^2.0.4" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/extglob": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", + "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", + "dependencies": { + "array-unique": "^0.3.2", + "define-property": "^1.0.0", + "expand-brackets": "^2.1.4", + "extend-shallow": "^2.0.1", + "fragment-cache": "^0.2.1", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/extglob/node_modules/define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "dependencies": { + "is-descriptor": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/extglob/node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/extglob/node_modules/is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dependencies": { + "kind-of": "^6.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/extglob/node_modules/is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dependencies": { + "kind-of": "^6.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/extglob/node_modules/is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dependencies": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/extsprintf": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", + "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=", + "engines": [ + "node >=0.6.0" + ] + }, + "node_modules/falafel": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/falafel/-/falafel-2.2.4.tgz", + "integrity": "sha512-0HXjo8XASWRmsS0X1EkhwEMZaD3Qvp7FfURwjLKjG1ghfRm/MGZl2r4cWUTv41KdNghTw4OUMmVtdGQp3+H+uQ==", + "dependencies": { + "acorn": "^7.1.1", + "foreach": "^2.0.5", + "isarray": "^2.0.1", + "object-keys": "^1.0.6" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/falafel/node_modules/isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==" + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" + }, + "node_modules/fast-glob": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.7.tgz", + "integrity": "sha512-rYGMRwip6lUMvYD3BTScMwT1HtAs2d71SMv66Vrxs0IekGZEjhM0pcMfjQPnknBt2zeCwQMEupiN02ZP4DiT1Q==", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==" + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=" + }, + "node_modules/fastparse": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/fastparse/-/fastparse-1.1.2.tgz", + "integrity": "sha512-483XLLxTVIwWK3QTrMGRqUfUpoOs/0hbQrl2oz4J0pAcm3A3bu84wxTFqGqkJzewCLdME38xJLJAxBABfQT8sQ==" + }, + "node_modules/fastq": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.11.1.tgz", + "integrity": "sha512-HOnr8Mc60eNYl1gzwp6r5RoUyAn5/glBolUzP/Ez6IFVPMPirxn/9phgL6zhOtaTy7ISwPvQ+wT+hfcRZh/bzw==", + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/fflate": { + "version": "0.4.8", + "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.4.8.tgz", + "integrity": "sha512-FJqqoDBR00Mdj9ppamLa/Y7vxm+PRmNWA67N846RvsoYVMKB4q3y/de5PA7gUmRMYK/8CMz2GDZQmCRN1wBcWA==" + }, + "node_modules/file-saver": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/file-saver/-/file-saver-2.0.5.tgz", + "integrity": "sha512-P9bmyZ3h/PRG+Nzga+rbdI4OEpNDzAVyy74uVO9ATgzLK6VtAsYybF/+TOCvrc0MO793d6+42lLyZTw7/ArVzA==" + }, + "node_modules/file-uri-to-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", + "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==" + }, + "node_modules/filesize": { + "version": "3.6.1", + "resolved": "https://registry.npmjs.org/filesize/-/filesize-3.6.1.tgz", + "integrity": "sha512-7KjR1vv6qnicaPMi1iiTcI85CyYwRO/PSFCu6SvqL8jN2Wjt/NIYQTFtFs7fSDCYOstUkEWIQGFUg5YZQfjlcg==", + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-up": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz", + "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=", + "dev": true, + "dependencies": { + "path-exists": "^2.0.0", + "pinkie-promise": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/follow-redirects": { + "version": "1.5.10", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.5.10.tgz", + "integrity": "sha512-0V5l4Cizzvqt5D44aTXbFZz+FtyXV1vrDN6qrelxtfYQKW0KO0W2T/hkE8xvGa/540LkZlkaUjO4ailYTFtHVQ==", + "dependencies": { + "debug": "=3.1.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/follow-redirects/node_modules/debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/follow-redirects/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + }, + "node_modules/for-in": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", + "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/foreach": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/foreach/-/foreach-2.0.5.tgz", + "integrity": "sha1-C+4AUBiusmDQo6865ljdATbsG5k=" + }, + "node_modules/forever-agent": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", + "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=", + "engines": { + "node": "*" + } + }, + "node_modules/form-data": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", + "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 0.12" + } + }, + "node_modules/fraction.js": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.1.1.tgz", + "integrity": "sha512-MHOhvvxHTfRFpF1geTK9czMIZ6xclsEor2wkIGYYq+PxcQqT7vStJqjhe6S1TenZrMZzo+wlqOufBDVepUEgPg==", + "peer": true, + "engines": { + "node": "*" + }, + "funding": { + "type": "patreon", + "url": "https://www.patreon.com/infusion" + } + }, + "node_modules/fragment-cache": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz", + "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=", + "dependencies": { + "map-cache": "^0.2.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fs": { + "version": "0.0.1-security", + "resolved": "https://registry.npmjs.org/fs/-/fs-0.0.1-security.tgz", + "integrity": "sha1-invTcYa23d84E/I4WLV+yq9eQdQ=", + "dev": true + }, + "node_modules/fs-constants": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", + "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", + "dev": true + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" + }, + "node_modules/fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" + }, + "node_modules/gauge": { + "version": "2.7.4", + "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz", + "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=", + "dev": true, + "dependencies": { + "aproba": "^1.0.3", + "console-control-strings": "^1.0.0", + "has-unicode": "^2.0.0", + "object-assign": "^4.1.0", + "signal-exit": "^3.0.0", + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1", + "wide-align": "^1.1.0" + } + }, + "node_modules/gauge/node_modules/ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/gauge/node_modules/strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dev": true, + "dependencies": { + "ansi-regex": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/geojson-area": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/geojson-area/-/geojson-area-0.2.1.tgz", + "integrity": "sha1-JTewmC24YwnyHSxCikJXx6YoLMY=", + "deprecated": "This module is now under the @mapbox namespace: install @mapbox/geojson-area instead", + "dependencies": { + "wgs84": "0.0.0" + } + }, + "node_modules/geojson-equality": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/geojson-equality/-/geojson-equality-0.1.6.tgz", + "integrity": "sha1-oXE3TvBD5dR5eZWEC65GSOB1LXI=", + "dependencies": { + "deep-equal": "^1.0.0" + } + }, + "node_modules/geojson-normalize": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/geojson-normalize/-/geojson-normalize-0.0.0.tgz", + "integrity": "sha1-Lbw2eM0bMbgXnodr2nDNEg3eNcA=", + "deprecated": "This module is now under the @mapbox namespace: install @mapbox/geojson-normalize instead" + }, + "node_modules/geojson-numeric": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/geojson-numeric/-/geojson-numeric-0.2.1.tgz", + "integrity": "sha512-rvItMp3W7pe16o2EQTnRw54v6WHdiE4bYjUsdr3FZskFb6oPC7gjLe4zginP+Wd1B/HLl2acTukfn16Lmwn7lg==", + "dependencies": { + "concat-stream": "2.0.0", + "optimist": "~0.3.5" + }, + "bin": { + "geojson-numeric": "geojson-numeric" + } + }, + "node_modules/geojson-random": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/geojson-random/-/geojson-random-0.2.2.tgz", + "integrity": "sha1-q0g48SatxeFvj5TmVd74IPkRnbw=", + "bin": { + "geojson-random": "geojson-random" + } + }, + "node_modules/geojson-rbush": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/geojson-rbush/-/geojson-rbush-3.1.2.tgz", + "integrity": "sha512-grkfdg3HIeTjwTfiJe5FT8+fGU3fABCc+vRJDBwdQz9kkLF0Sbif2gs2JUzjewwgmnvLGy9fInySDeADoNuk7w==", + "dependencies": { + "@turf/bbox": "*", + "@turf/helpers": "6.x", + "@turf/meta": "6.x", + "rbush": "^2.0.0" + } + }, + "node_modules/get-closest": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/get-closest/-/get-closest-0.0.4.tgz", + "integrity": "sha1-JprHdtHmAiqg/Vht1wjop9Miaa8=" + }, + "node_modules/get-intrinsic": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz", + "integrity": "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==", + "dependencies": { + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-port": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/get-port/-/get-port-3.2.0.tgz", + "integrity": "sha1-3Xzn3hh8Bsi/NTeWrHHgmfCYDrw=", + "engines": { + "node": ">=4" + } + }, + "node_modules/get-stdin": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz", + "integrity": "sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/get-value": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", + "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/getpass": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", + "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", + "dependencies": { + "assert-plus": "^1.0.0" + } + }, + "node_modules/git-json-merge": { + "version": "0.4.5", + "resolved": "https://registry.npmjs.org/git-json-merge/-/git-json-merge-0.4.5.tgz", + "integrity": "sha512-akVUhyzRtkXGe5uAcw3AijF/253RA7tAPdfHtKLawYAhDjuyP+Ebr1YvZUv+7Jyr41g+IVRpKPBd2h+m6AHNqQ==", + "dev": true, + "dependencies": { + "detect-indent": "^6.0.0", + "xdiff": "^0.2.11" + }, + "bin": { + "git-json-merge": "bin/git-json-merge" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/github-from-package": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz", + "integrity": "sha1-l/tdlr/eiXMxPyDoKI75oWf6ZM4=", + "dev": true + }, + "node_modules/glob": { + "version": "7.1.7", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz", + "integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/glob-to-regexp": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.3.0.tgz", + "integrity": "sha1-jFoUlNIGbFcMw7/kSWF1rMTVAqs=" + }, + "node_modules/globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "engines": { + "node": ">=4" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.6", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.6.tgz", + "integrity": "sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ==" + }, + "node_modules/grapheme-breaker": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/grapheme-breaker/-/grapheme-breaker-0.3.2.tgz", + "integrity": "sha1-W55reMODJFLSuiuxy4MPlidkEKw=", + "dependencies": { + "brfs": "^1.2.0", + "unicode-trie": "^0.3.1" + } + }, + "node_modules/growl": { + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/growl/-/growl-1.9.2.tgz", + "integrity": "sha1-Dqd0NxXbjY3ixe3hd14bRayFwC8=" + }, + "node_modules/har-schema": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", + "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=", + "engines": { + "node": ">=4" + } + }, + "node_modules/har-validator": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz", + "integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==", + "deprecated": "this library is no longer supported", + "dependencies": { + "ajv": "^6.12.3", + "har-schema": "^2.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dependencies": { + "function-bind": "^1.1.1" + }, + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/has-ansi": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", + "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", + "dependencies": { + "ansi-regex": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/has-ansi/node_modules/ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/has-bigints": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.1.tgz", + "integrity": "sha512-LSBS2LjbNBTf6287JEbEzvJgftkF5qFkmCo9hDRpAzKhUOlJ+hx8dd4USs00SgsUNwc4617J9ki5YtEClM2ffA==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "engines": { + "node": ">=4" + } + }, + "node_modules/has-symbols": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz", + "integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-unicode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", + "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=", + "dev": true + }, + "node_modules/has-value": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz", + "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=", + "dependencies": { + "get-value": "^2.0.6", + "has-values": "^1.0.0", + "isobject": "^3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/has-values": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz", + "integrity": "sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=", + "dependencies": { + "is-number": "^3.0.0", + "kind-of": "^4.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/has-values/node_modules/is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/has-values/node_modules/is-number/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/has-values/node_modules/kind-of": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", + "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/hash-base": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.1.0.tgz", + "integrity": "sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA==", + "dependencies": { + "inherits": "^2.0.4", + "readable-stream": "^3.6.0", + "safe-buffer": "^5.2.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/hash-base/node_modules/readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/hash.js": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", + "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==", + "dependencies": { + "inherits": "^2.0.3", + "minimalistic-assert": "^1.0.1" + } + }, + "node_modules/hex-color-regex": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/hex-color-regex/-/hex-color-regex-1.1.0.tgz", + "integrity": "sha512-l9sfDFsuqtOqKDsQdqrMRk0U85RZc0RtOR9yPI7mRVOa4FsR/BVnZ0shmQRM96Ji99kYZP/7hn1cedc1+ApsTQ==" + }, + "node_modules/hmac-drbg": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", + "integrity": "sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=", + "dependencies": { + "hash.js": "^1.0.3", + "minimalistic-assert": "^1.0.0", + "minimalistic-crypto-utils": "^1.0.1" + } + }, + "node_modules/hosted-git-info": { + "version": "2.8.9", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", + "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==" + }, + "node_modules/hsl-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/hsl-regex/-/hsl-regex-1.0.0.tgz", + "integrity": "sha1-1JMwx4ntgZ4nakwNJy3/owsY/m4=" + }, + "node_modules/hsla-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/hsla-regex/-/hsla-regex-1.0.0.tgz", + "integrity": "sha1-wc56MWjIxmFAM6S194d/OyJfnDg=" + }, + "node_modules/html-encoding-sniffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-1.0.2.tgz", + "integrity": "sha512-71lZziiDnsuabfdYiUeWdCVyKuqwWi23L8YeIgV9jSSZHCtb6wB1BKWooH7L3tn4/FuZJMVWyNaIDr4RGmaSYw==", + "dependencies": { + "whatwg-encoding": "^1.0.1" + } + }, + "node_modules/html-tags": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/html-tags/-/html-tags-3.1.0.tgz", + "integrity": "sha512-1qYz89hW3lFDEazhjW0yVAV87lw8lVkrJocr72XmBkMKsoSVJCQx3W8BXsC7hO2qAt8BoVjYjtAcZ9perqGnNg==", + "engines": { + "node": ">=8" + } + }, + "node_modules/html2canvas": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/html2canvas/-/html2canvas-1.1.4.tgz", + "integrity": "sha512-uHgQDwrXsRmFdnlOVFvHin9R7mdjjZvoBoXxicPR+NnucngkaLa5zIDW9fzMkiip0jSffyTyWedE8iVogYOeWg==", + "optional": true, + "dependencies": { + "css-line-break": "1.1.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/htmlnano": { + "version": "0.2.9", + "resolved": "https://registry.npmjs.org/htmlnano/-/htmlnano-0.2.9.tgz", + "integrity": "sha512-jWTtP3dCd7R8x/tt9DK3pvpcQd7HDMcRPUqPxr/i9989q2k5RHIhmlRDFeyQ/LSd8IKrteG8Ce5g0Ig4eGIipg==", + "dependencies": { + "cssnano": "^4.1.11", + "posthtml": "^0.15.1", + "purgecss": "^2.3.0", + "relateurl": "^0.2.7", + "srcset": "^3.0.0", + "svgo": "^1.3.2", + "terser": "^5.6.1", + "timsort": "^0.3.0", + "uncss": "^0.17.3" + } + }, + "node_modules/htmlnano/node_modules/commander": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-5.1.0.tgz", + "integrity": "sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg==", + "engines": { + "node": ">= 6" + } + }, + "node_modules/htmlnano/node_modules/dom-serializer": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.3.1.tgz", + "integrity": "sha512-Pv2ZluG5ife96udGgEDovOOOA5UELkltfJpnIExPrAk1LTvecolUGn6lIaoLh86d83GiB86CjzciMd9BuRB71Q==", + "dependencies": { + "domelementtype": "^2.0.1", + "domhandler": "^4.0.0", + "entities": "^2.0.0" + }, + "funding": { + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + } + }, + "node_modules/htmlnano/node_modules/domelementtype": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.2.0.tgz", + "integrity": "sha512-DtBMo82pv1dFtUmHyr48beiuq792Sxohr+8Hm9zoxklYPfa6n0Z3Byjj2IV7bmr2IyqClnqEQhfgHJJ5QF0R5A==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ] + }, + "node_modules/htmlnano/node_modules/domhandler": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.2.0.tgz", + "integrity": "sha512-zk7sgt970kzPks2Bf+dwT/PLzghLnsivb9CcxkvR8Mzr66Olr0Ofd8neSbglHJHaHa2MadfoSdNlKYAaafmWfA==", + "dependencies": { + "domelementtype": "^2.2.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/htmlnano/node_modules/domutils": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.6.0.tgz", + "integrity": "sha512-y0BezHuy4MDYxh6OvolXYsH+1EMGmFbwv5FKW7ovwMG6zTPWqNPq3WF9ayZssFq+UlKdffGLbOEaghNdaOm1WA==", + "dependencies": { + "dom-serializer": "^1.0.1", + "domelementtype": "^2.2.0", + "domhandler": "^4.2.0" + }, + "funding": { + "url": "https://github.com/fb55/domutils?sponsor=1" + } + }, + "node_modules/htmlnano/node_modules/entities": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", + "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==", + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/htmlnano/node_modules/htmlparser2": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-6.1.0.tgz", + "integrity": "sha512-gyyPk6rgonLFEDGoeRgQNaEUvdJ4ktTmmUh/h2t7s+M8oPpIPxgNACWa+6ESR57kXstwqPiCut0V8NRpcwgU7A==", + "funding": [ + "https://github.com/fb55/htmlparser2?sponsor=1", + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "dependencies": { + "domelementtype": "^2.0.1", + "domhandler": "^4.0.0", + "domutils": "^2.5.2", + "entities": "^2.0.0" + } + }, + "node_modules/htmlnano/node_modules/postcss": { + "version": "7.0.32", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.32.tgz", + "integrity": "sha512-03eXong5NLnNCD05xscnGKGDZ98CyzoqPSMjOe6SuoQY7Z2hIj0Ld1g/O/UQRuOle2aRtiIRDg9tDcTGAkLfKw==", + "dependencies": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + } + }, + "node_modules/htmlnano/node_modules/posthtml": { + "version": "0.15.2", + "resolved": "https://registry.npmjs.org/posthtml/-/posthtml-0.15.2.tgz", + "integrity": "sha512-YugEJ5ze/0DLRIVBjCpDwANWL4pPj1kHJ/2llY8xuInr0nbkon3qTiMPe5LQa+cCwNjxS7nAZZTp+1M+6mT4Zg==", + "dependencies": { + "posthtml-parser": "^0.7.2", + "posthtml-render": "^1.3.1" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/htmlnano/node_modules/posthtml-parser": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/posthtml-parser/-/posthtml-parser-0.7.2.tgz", + "integrity": "sha512-LjEEG/3fNcWZtBfsOE3Gbyg1Li4CmsZRkH1UmbMR7nKdMXVMYI3B4/ZMiCpaq8aI1Aym4FRMMW9SAOLSwOnNsQ==", + "dependencies": { + "htmlparser2": "^6.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/htmlnano/node_modules/purgecss": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/purgecss/-/purgecss-2.3.0.tgz", + "integrity": "sha512-BE5CROfVGsx2XIhxGuZAT7rTH9lLeQx/6M0P7DTXQH4IUc3BBzs9JUzt4yzGf3JrH9enkeq6YJBe9CTtkm1WmQ==", + "dependencies": { + "commander": "^5.0.0", + "glob": "^7.0.0", + "postcss": "7.0.32", + "postcss-selector-parser": "^6.0.2" + }, + "bin": { + "purgecss": "bin/purgecss" + } + }, + "node_modules/htmlnano/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/htmlnano/node_modules/supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/htmlnano/node_modules/terser": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.7.0.tgz", + "integrity": "sha512-HP5/9hp2UaZt5fYkuhNBR8YyRcT8juw8+uFbAme53iN9hblvKnLUTKkmwJG6ocWpIKf8UK4DoeWG4ty0J6S6/g==", + "dependencies": { + "commander": "^2.20.0", + "source-map": "~0.7.2", + "source-map-support": "~0.5.19" + }, + "bin": { + "terser": "bin/terser" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/htmlnano/node_modules/terser/node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==" + }, + "node_modules/htmlnano/node_modules/terser/node_modules/source-map": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz", + "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==", + "engines": { + "node": ">= 8" + } + }, + "node_modules/htmlparser2": { + "version": "3.5.1", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.5.1.tgz", + "integrity": "sha1-b0L3ZX3RnBP31l3pEYQXOUoL5tA=", + "dependencies": { + "domelementtype": "1", + "domhandler": "2.2", + "domutils": "1.3", + "readable-stream": "1.1" + } + }, + "node_modules/htmlparser2/node_modules/isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=" + }, + "node_modules/htmlparser2/node_modules/readable-stream": { + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", + "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "0.0.1", + "string_decoder": "~0.10.x" + } + }, + "node_modules/htmlparser2/node_modules/string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=" + }, + "node_modules/http-errors": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.3.tgz", + "integrity": "sha512-ZTTX0MWrsQ2ZAhA1cejAwDLycFsd7I7nVtnkT3Ol0aqodaKW+0CTZDQ1uBv5whptCnc8e8HeRRJxRs0kmm/Qfw==", + "dependencies": { + "depd": "~1.1.2", + "inherits": "2.0.4", + "setprototypeof": "1.1.1", + "statuses": ">= 1.5.0 < 2", + "toidentifier": "1.0.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/http-signature": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", + "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", + "dependencies": { + "assert-plus": "^1.0.0", + "jsprim": "^1.2.2", + "sshpk": "^1.7.0" + }, + "engines": { + "node": ">=0.8", + "npm": ">=1.3.7" + } + }, + "node_modules/https-browserify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-1.0.0.tgz", + "integrity": "sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM=" + }, + "node_modules/i18next": { + "version": "20.3.2", + "resolved": "https://registry.npmjs.org/i18next/-/i18next-20.3.2.tgz", + "integrity": "sha512-e8CML2R9Ng2sSQOM80wb/PrM2j8mDm84o/T4Amzn9ArVyNX5/ENWxxAXkRpZdTQNDaxKImF93Wep4mAoozFrKw==", + "dependencies": { + "@babel/runtime": "^7.12.0" + } + }, + "node_modules/i18next-browser-languagedetector": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/i18next-browser-languagedetector/-/i18next-browser-languagedetector-6.1.2.tgz", + "integrity": "sha512-YDzIGHhMRvr7M+c8B3EQUKyiMBhfqox4o1qkFvt4QXuu5V2cxf74+NCr+VEkUuU0y+RwcupA238eeolW1Yn80g==", + "dependencies": { + "@babel/runtime": "^7.14.6" + } + }, + "node_modules/i18next-browser-languagedetector/node_modules/@babel/runtime": { + "version": "7.14.6", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.14.6.tgz", + "integrity": "sha512-/PCB2uJ7oM44tz8YhC4Z/6PeOKXp4K588f+5M3clr1M4zbqztlo0XEfJ2LEzj/FgwfgGcIdl8n7YYjTCI0BYwg==", + "dependencies": { + "regenerator-runtime": "^0.13.4" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/i18next-client": { + "version": "1.11.4", + "resolved": "https://registry.npmjs.org/i18next-client/-/i18next-client-1.11.4.tgz", + "integrity": "sha1-BILrG2Q+z3qEBPe1kuujKOXwmuc=", + "deprecated": "you can use npm install i18next from version 2.0.0", + "engines": { + "node": ">=0.4.12" + } + }, + "node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/icss-replace-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/icss-replace-symbols/-/icss-replace-symbols-1.1.0.tgz", + "integrity": "sha1-Bupvg2ead0njhs/h/oEq5dsiPe0=" + }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/import-cwd": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/import-cwd/-/import-cwd-3.0.0.tgz", + "integrity": "sha512-4pnzH16plW+hgvRECbDWpQl3cqtvSofHWh44met7ESfZ8UZOWWddm8hEyDTqREJ9RbYHY8gi8DqmaelApoOGMg==", + "dependencies": { + "import-from": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/import-fresh": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-2.0.0.tgz", + "integrity": "sha1-2BNVwVYS04bGH53dOSLUMEgipUY=", + "dependencies": { + "caller-path": "^2.0.0", + "resolve-from": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/import-from": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/import-from/-/import-from-3.0.0.tgz", + "integrity": "sha512-CiuXOFFSzkU5x/CR0+z7T91Iht4CXgfCxVOFRhh2Zyhg5wOpWvvDLQUsWl+gcN+QscYBjez8hDCt85O7RLDttQ==", + "dependencies": { + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/import-from/node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "engines": { + "node": ">=8" + } + }, + "node_modules/incremental-convex-hull": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/incremental-convex-hull/-/incremental-convex-hull-1.0.1.tgz", + "integrity": "sha1-UUKMFMudmmFEv+abKFH7N3M0vh4=", + "dependencies": { + "robust-orientation": "^1.1.2", + "simplicial-complex": "^1.0.0" + } + }, + "node_modules/indent-string": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-2.1.0.tgz", + "integrity": "sha1-ji1INIdCEhtKghi3oTfppSBJ3IA=", + "dev": true, + "dependencies": { + "repeating": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/indexes-of": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/indexes-of/-/indexes-of-1.0.1.tgz", + "integrity": "sha1-8w9xbI4r00bHtn0985FVZqfAVgc=" + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "node_modules/ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "dev": true + }, + "node_modules/is-absolute-url": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-absolute-url/-/is-absolute-url-2.1.0.tgz", + "integrity": "sha1-UFMN+4T8yap9vnhS6Do3uTufKqY=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-accessor-descriptor/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-arguments": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.0.tgz", + "integrity": "sha512-1Ij4lOMPl/xB5kBDn7I+b2ttPMKa8szhEIrXDuXQD/oe3HJLTLhqhgGspwgyGd6MOywBUqVvYicF72lkgDnIHg==", + "dependencies": { + "call-bind": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-arrayish": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", + "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==" + }, + "node_modules/is-bigint": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.2.tgz", + "integrity": "sha512-0JV5+SOCQkIdzjBK9buARcV804Ddu7A0Qet6sHi3FimE9ne6m4BGQZfRn+NZiXbBk4F4XmHfDZIipLj9pX8dSA==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-boolean-object": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.0.tgz", + "integrity": "sha512-a7Uprx8UtD+HWdyYwnD1+ExtTgqQtD2k/1yJgtXP6wnMm8byhkoTZRl+95LLThpzNZJ5aEvi46cdH+ayMFRwmA==", + "dependencies": { + "call-bind": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" + }, + "node_modules/is-callable": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.3.tgz", + "integrity": "sha512-J1DcMe8UYTBSrKezuIUTUwjXsho29693unXM2YhJUTR2txK/eG47bvNa/wipPFmZFgr/N6f1GA66dv0mEyTIyQ==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-color-stop": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-color-stop/-/is-color-stop-1.1.0.tgz", + "integrity": "sha1-z/9HGu5N1cnhWFmPvhKWe1za00U=", + "dependencies": { + "css-color-names": "^0.0.4", + "hex-color-regex": "^1.1.0", + "hsl-regex": "^1.0.0", + "hsla-regex": "^1.0.0", + "rgb-regex": "^1.0.1", + "rgba-regex": "^1.0.0" + } + }, + "node_modules/is-core-module": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.3.0.tgz", + "integrity": "sha512-xSphU2KG9867tsYdLD4RWQ1VqdFl4HTO9Thf3I/3dLEfr0dbPTWKsuCKrgqMljg4nPE+Gq0VCnzT3gr0CyBmsw==", + "dependencies": { + "has": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-data-descriptor/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-date-object": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.3.tgz", + "integrity": "sha512-tDpEUInNcy2Yw3lNSepK3Wdw1RnXLcIVienz6Ou631Acl15cJyRWK4dgA1vCmOEgIbtOV0W7MHg+AR2Gdg1NXQ==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "dependencies": { + "is-accessor-descriptor": "^0.1.6", + "is-data-descriptor": "^0.1.4", + "kind-of": "^5.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-descriptor/node_modules/kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-directory": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/is-directory/-/is-directory-0.3.1.tgz", + "integrity": "sha1-YTObbyR1/Hcv2cnYP1yFddwVSuE=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-finite": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.1.0.tgz", + "integrity": "sha512-cdyMtqX/BOqqNBBiKlIVkytNHm49MtMlYyn1zxzvJKWmFMlGzm+ry5BBfYyeY9YmNKbRSo/o7OX9w9ale0wg3w==", + "dev": true, + "engines": { + "node": ">=0.10.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", + "dev": true, + "dependencies": { + "number-is-nan": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-generator-function": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.9.tgz", + "integrity": "sha512-ZJ34p1uvIfptHCN7sFTjGibB9/oBg17sHqzDLfuwhvmN/qLVvIQXRQ8licZQ35WJ8KuEQt/etnnzQFI9C9Ue/A==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-glob": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", + "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-html": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-html/-/is-html-1.1.0.tgz", + "integrity": "sha1-4E8cGNOUhRETlvmgJz6rUa8hhGQ=", + "dependencies": { + "html-tags": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-html/node_modules/html-tags": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/html-tags/-/html-tags-1.2.0.tgz", + "integrity": "sha1-x43mW1Zjqll5id0rerSSANfk25g=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-nan": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/is-nan/-/is-nan-1.3.2.tgz", + "integrity": "sha512-E+zBKpQ2t6MEo1VsonYmluk9NxGrbzpeeLC2xIViuO2EjU2xsXsBPwTr3Ykv9l08UYEVEdWeRZNouaZqF6RN0w==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.0", + "define-properties": "^1.1.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-negative-zero": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.1.tgz", + "integrity": "sha512-2z6JzQvZRa9A2Y7xC6dQQm4FSTSTNWjKIYYTt4246eMTJmIo0Q+ZyOsU66X8lxK1AbB92dFeglPLrhwpeRKO6w==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-number-object": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.4.tgz", + "integrity": "sha512-zohwelOAur+5uXtk8O3GPQ1eAcu4ZX3UwxQhUlfFFMNpUd83gXgjbhJh6HmB6LUNV/ieOLQuDwJO3dWJosUeMw==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-obj": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz", + "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-plain-object": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", + "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", + "dependencies": { + "isobject": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-regex": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.2.tgz", + "integrity": "sha512-axvdhb5pdhEVThqJzYXwMlVuZwC+FF2DpcOhTS+y/8jVq4trxyPgfcwIxIKiyeuLlSQYKkmUaPQJ8ZE4yNKXDg==", + "dependencies": { + "call-bind": "^1.0.2", + "has-symbols": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-resolvable": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-resolvable/-/is-resolvable-1.1.0.tgz", + "integrity": "sha512-qgDYXFSR5WvEfuS5dMj6oTMEbrrSaM0CrFk2Yiq/gXnBvD9pMa2jGXxyhGLfvhZpuMZe18CJpFxAt3CRs42NMg==" + }, + "node_modules/is-string": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.5.tgz", + "integrity": "sha512-buY6VNRjhQMiF1qWDouloZlQbRhDPCebwxSjxMjxgemYT46YMd2NR0/H+fBhEfWX4A/w9TBJ+ol+okqJKFE6vQ==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-symbol": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.3.tgz", + "integrity": "sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ==", + "dependencies": { + "has-symbols": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-typed-array": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.5.tgz", + "integrity": "sha512-S+GRDgJlR3PyEbsX/Fobd9cqpZBuvUS+8asRqYDMLCb2qMzt1oz5m5oxQCxOgUDxiWsOVNi4yaF+/uvdlHlYug==", + "dev": true, + "dependencies": { + "available-typed-arrays": "^1.0.2", + "call-bind": "^1.0.2", + "es-abstract": "^1.18.0-next.2", + "foreach": "^2.0.5", + "has-symbols": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=" + }, + "node_modules/is-url": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/is-url/-/is-url-1.2.4.tgz", + "integrity": "sha512-ITvGim8FhRiYe4IQ5uHSkj7pVaPDrCTkNd3yq3cV7iZAcJdHTUMPMEHcqSOy9xZ9qFenQCvi+2wjH9a1nXqHww==" + }, + "node_modules/is-utf8": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz", + "integrity": "sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=", + "dev": true + }, + "node_modules/is-windows": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", + "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-wsl": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-1.1.0.tgz", + "integrity": "sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0=", + "engines": { + "node": ">=4" + } + }, + "node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=" + }, + "node_modules/isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/isstream": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", + "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=" + }, + "node_modules/jade": { + "version": "0.26.3", + "resolved": "https://registry.npmjs.org/jade/-/jade-0.26.3.tgz", + "integrity": "sha1-jxDXl32NefL2/4YqgbBRPMslaGw=", + "deprecated": "Jade has been renamed to pug, please install the latest version of pug instead of jade", + "dependencies": { + "commander": "0.6.1", + "mkdirp": "0.3.0" + }, + "bin": { + "jade": "bin/jade" + } + }, + "node_modules/jade/node_modules/commander": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-0.6.1.tgz", + "integrity": "sha1-+mihT2qUXVTbvlDYzbMyDp47GgY=", + "engines": { + "node": ">= 0.4.x" + } + }, + "node_modules/jade/node_modules/mkdirp": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.3.0.tgz", + "integrity": "sha1-G79asbqCevI1dRQ0kEJkVfSB/h4=", + "deprecated": "Legacy versions of mkdirp are no longer supported. Please update to mkdirp 1.x. (Note that the API surface has changed to use Promises in 1.x.)", + "engines": { + "node": "*" + } + }, + "node_modules/jquery": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.6.0.tgz", + "integrity": "sha512-JVzAR/AjBvVt2BmYhxRCSYysDsPcssdmTFnzyLEts9qNwmjmu4JTAMYubEfwVOSwpQ1I1sKKFcxhZCI2buerfw==" + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" + }, + "node_modules/js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/js-yaml/node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", + "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=" + }, + "node_modules/jsdom": { + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-14.1.0.tgz", + "integrity": "sha512-O901mfJSuTdwU2w3Sn+74T+RnDVP+FuV5fH8tcPWyqrseRAb0s5xOtPgCFiPOtLcyK7CLIJwPyD83ZqQWvA5ng==", + "dependencies": { + "abab": "^2.0.0", + "acorn": "^6.0.4", + "acorn-globals": "^4.3.0", + "array-equal": "^1.0.0", + "cssom": "^0.3.4", + "cssstyle": "^1.1.1", + "data-urls": "^1.1.0", + "domexception": "^1.0.1", + "escodegen": "^1.11.0", + "html-encoding-sniffer": "^1.0.2", + "nwsapi": "^2.1.3", + "parse5": "5.1.0", + "pn": "^1.1.0", + "request": "^2.88.0", + "request-promise-native": "^1.0.5", + "saxes": "^3.1.9", + "symbol-tree": "^3.2.2", + "tough-cookie": "^2.5.0", + "w3c-hr-time": "^1.0.1", + "w3c-xmlserializer": "^1.1.2", + "webidl-conversions": "^4.0.2", + "whatwg-encoding": "^1.0.5", + "whatwg-mimetype": "^2.3.0", + "whatwg-url": "^7.0.0", + "ws": "^6.1.2", + "xml-name-validator": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jsdom/node_modules/acorn": { + "version": "6.4.2", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.4.2.tgz", + "integrity": "sha512-XtGIhXwF8YM8bJhGxG5kXgjkEuNGLTkoYqVE+KMR+aspr4KGYmKYg7yUe3KghyQ9yheNwLnjmzh/7+gfDBmHCQ==", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/jsdom/node_modules/escodegen": { + "version": "1.14.3", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.14.3.tgz", + "integrity": "sha512-qFcX0XJkdg+PB3xjZZG/wKSuT1PnQWx57+TVSjIMmILd2yC/6ByYElPwJnslDsuWuSAp4AwJGumarAAmJch5Kw==", + "dependencies": { + "esprima": "^4.0.1", + "estraverse": "^4.2.0", + "esutils": "^2.0.2", + "optionator": "^0.8.1" + }, + "bin": { + "escodegen": "bin/escodegen.js", + "esgenerate": "bin/esgenerate.js" + }, + "engines": { + "node": ">=4.0" + }, + "optionalDependencies": { + "source-map": "~0.6.1" + } + }, + "node_modules/jsdom/node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/jsdom/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "optional": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/jsdom/node_modules/ws": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-6.2.1.tgz", + "integrity": "sha512-GIyAXC2cB7LjvpgMt9EKS2ldqr0MTrORaleiOno6TweZ6r3TKtoFQWay/2PceJ3RuBasOHzXNn5Lrw1X0bEjqA==", + "dependencies": { + "async-limiter": "~1.0.0" + } + }, + "node_modules/jsesc": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/jshashes": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/jshashes/-/jshashes-1.0.8.tgz", + "integrity": "sha512-btmQZ/w1rj8Lb6nEwvhjM7nBYoj54yaEFo2PWh3RkxZ8qNwuvOxvQYN/JxVuwoMmdIluL+XwYVJ+pEEZoSYybQ==", + "bin": { + "hashes": "bin/hashes" + }, + "engines": { + "node": "*" + } + }, + "node_modules/json-parse-better-errors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", + "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==" + }, + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==" + }, + "node_modules/json-schema": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", + "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=" + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" + }, + "node_modules/json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=" + }, + "node_modules/json5": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", + "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", + "dependencies": { + "minimist": "^1.2.0" + }, + "bin": { + "json5": "lib/cli.js" + } + }, + "node_modules/jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/jsonparse": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-0.0.5.tgz", + "integrity": "sha1-MwVCrT8KZUZlt3jz6y2an6UHrGQ=", + "engines": [ + "node >= 0.2.0" + ] + }, + "node_modules/JSONStream": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-0.8.0.tgz", + "integrity": "sha1-78Ri1aW8lOwAf0siVxrNf28q4BM=", + "dependencies": { + "jsonparse": "0.0.5", + "through": "~2.2.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/JSONStream/node_modules/through": { + "version": "2.2.7", + "resolved": "https://registry.npmjs.org/through/-/through-2.2.7.tgz", + "integrity": "sha1-bo4hIAGR1OtqmfbwEN9Gqhxusr0=" + }, + "node_modules/jsonwebtoken": { + "version": "8.5.1", + "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-8.5.1.tgz", + "integrity": "sha512-XjwVfRS6jTMsqYs0EsuJ4LGxXV14zQybNd4L2r0UvbVnSF9Af8x7p5MzbJ90Ioz/9TI41/hTCvznF/loiSzn8w==", + "dependencies": { + "jws": "^3.2.2", + "lodash.includes": "^4.3.0", + "lodash.isboolean": "^3.0.3", + "lodash.isinteger": "^4.0.4", + "lodash.isnumber": "^3.0.3", + "lodash.isplainobject": "^4.0.6", + "lodash.isstring": "^4.0.1", + "lodash.once": "^4.0.0", + "ms": "^2.1.1", + "semver": "^5.6.0" + }, + "engines": { + "node": ">=4", + "npm": ">=1.4.28" + } + }, + "node_modules/jsonwebtoken/node_modules/semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/jspdf": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/jspdf/-/jspdf-2.3.1.tgz", + "integrity": "sha512-1vp0USP1mQi1h7NKpwxjFgQkJ5ncZvtH858aLpycUc/M+r/RpWJT8PixAU7Cw/3fPd4fpC8eB/Bj42LnsR21YQ==", + "dependencies": { + "atob": "^2.1.2", + "btoa": "^1.2.1", + "fflate": "^0.4.8" + }, + "optionalDependencies": { + "canvg": "^3.0.6", + "core-js": "^3.6.0", + "dompurify": "^2.2.0", + "html2canvas": "^1.0.0-rc.5" + } + }, + "node_modules/jspdf/node_modules/core-js": { + "version": "3.15.2", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.15.2.tgz", + "integrity": "sha512-tKs41J7NJVuaya8DxIOCnl8QuPHx5/ZVbFo1oKgVl1qHFBBrDctzQGtuLjPpRdNTWmKPH6oEvgN/MUID+l485Q==", + "hasInstallScript": true, + "optional": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" + } + }, + "node_modules/jsprim": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", + "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", + "engines": [ + "node >=0.6.0" + ], + "dependencies": { + "assert-plus": "1.0.0", + "extsprintf": "1.3.0", + "json-schema": "0.2.3", + "verror": "1.10.0" + } + }, + "node_modules/jsts": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/jsts/-/jsts-1.1.2.tgz", + "integrity": "sha1-0gXSzIOTCB2eSErjYoIRBpXtwjA=", + "engines": { + "node": ">= 4" + } + }, + "node_modules/jwa": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz", + "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==", + "dependencies": { + "buffer-equal-constant-time": "1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/jwk-to-pem": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/jwk-to-pem/-/jwk-to-pem-2.0.5.tgz", + "integrity": "sha512-L90jwellhO8jRKYwbssU9ifaMVqajzj3fpRjDKcsDzrslU9syRbFqfkXtT4B89HYAap+xsxNcxgBSB09ig+a7A==", + "dependencies": { + "asn1.js": "^5.3.0", + "elliptic": "^6.5.4", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/jws": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", + "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", + "dependencies": { + "jwa": "^1.4.1", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/latlon2country": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/latlon2country/-/latlon2country-1.1.3.tgz", + "integrity": "sha512-jk4xlaG7jkt4qPBZlhjUK60yo9qYs5LrAqf0hDTfHV6KXuR7qp84CgIq34Zm1xhH6PEQG4TknLWJrJWk23qNVA==", + "dependencies": { + "@turf/boolean-point-in-polygon": "^6.0.1", + "@turf/turf": "^5.1.6", + "@types/node": "^14.14.10", + "jquery": "^3.5.1", + "ts-node": "^9.1.1", + "turf": "^3.0.14" + } + }, + "node_modules/latlon2country/node_modules/@turf/along": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/along/-/along-5.1.5.tgz", + "integrity": "sha1-YdbmplhKzdq1asVYTge/jL5fi+s=", + "dependencies": { + "@turf/bearing": "^5.1.5", + "@turf/destination": "^5.1.5", + "@turf/distance": "^5.1.5", + "@turf/helpers": "^5.1.5" + } + }, + "node_modules/latlon2country/node_modules/@turf/area": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/area/-/area-5.1.5.tgz", + "integrity": "sha1-79iZv9Jgzb0VQbKjwVX4pdLu+h0=", + "dependencies": { + "@turf/helpers": "^5.1.5", + "@turf/meta": "^5.1.5" + } + }, + "node_modules/latlon2country/node_modules/@turf/bbox": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/bbox/-/bbox-5.1.5.tgz", + "integrity": "sha1-MFHfUUrUxQ9KT5uKLRX9i2hA7aM=", + "dependencies": { + "@turf/helpers": "^5.1.5", + "@turf/meta": "^5.1.5" + } + }, + "node_modules/latlon2country/node_modules/@turf/bbox-clip": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/bbox-clip/-/bbox-clip-5.1.5.tgz", + "integrity": "sha1-M2S1Mo3/nzz0HZ4C7a/zdNFQzIQ=", + "dependencies": { + "@turf/helpers": "^5.1.5", + "@turf/invariant": "^5.1.5", + "lineclip": "^1.1.5" + } + }, + "node_modules/latlon2country/node_modules/@turf/bbox-polygon": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/bbox-polygon/-/bbox-polygon-5.1.5.tgz", + "integrity": "sha1-auuk7VHYXSluD3w4uIwznwHu4CQ=", + "dependencies": { + "@turf/helpers": "^5.1.5" + } + }, + "node_modules/latlon2country/node_modules/@turf/bearing": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/bearing/-/bearing-5.1.5.tgz", + "integrity": "sha1-egt5ATbE70eX8CRjBdRcvi0ns/c=", + "dependencies": { + "@turf/helpers": "^5.1.5", + "@turf/invariant": "^5.1.5" + } + }, + "node_modules/latlon2country/node_modules/@turf/bezier-spline": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/bezier-spline/-/bezier-spline-5.1.5.tgz", + "integrity": "sha1-WaJ7ul17l+8Vqz/VpA+9I4cEm8o=", + "dependencies": { + "@turf/helpers": "^5.1.5", + "@turf/invariant": "^5.1.5" + } + }, + "node_modules/latlon2country/node_modules/@turf/boolean-clockwise": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/boolean-clockwise/-/boolean-clockwise-5.1.5.tgz", + "integrity": "sha1-MwK32sYsXikaB4nimvcoM4f6nes=", + "dependencies": { + "@turf/helpers": "^5.1.5", + "@turf/invariant": "^5.1.5" + } + }, + "node_modules/latlon2country/node_modules/@turf/boolean-contains": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/boolean-contains/-/boolean-contains-5.1.5.tgz", + "integrity": "sha1-WW1jruY2961T7pn5/yTJaZSg7xQ=", + "dependencies": { + "@turf/bbox": "^5.1.5", + "@turf/boolean-point-in-polygon": "^5.1.5", + "@turf/boolean-point-on-line": "^5.1.5", + "@turf/helpers": "^5.1.5", + "@turf/invariant": "^5.1.5" + } + }, + "node_modules/latlon2country/node_modules/@turf/boolean-contains/node_modules/@turf/boolean-point-in-polygon": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/boolean-point-in-polygon/-/boolean-point-in-polygon-5.1.5.tgz", + "integrity": "sha1-8BzBlNHgMKVIv9qYHLpDz9YpQbc=", + "dependencies": { + "@turf/helpers": "^5.1.5", + "@turf/invariant": "^5.1.5" + } + }, + "node_modules/latlon2country/node_modules/@turf/boolean-crosses": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/boolean-crosses/-/boolean-crosses-5.1.5.tgz", + "integrity": "sha1-Ab+uollvFk3kpNMlCU3HwlXHFdY=", + "dependencies": { + "@turf/boolean-point-in-polygon": "^5.1.5", + "@turf/helpers": "^5.1.5", + "@turf/invariant": "^5.1.5", + "@turf/line-intersect": "^5.1.5", + "@turf/polygon-to-line": "^5.1.5" + } + }, + "node_modules/latlon2country/node_modules/@turf/boolean-crosses/node_modules/@turf/boolean-point-in-polygon": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/boolean-point-in-polygon/-/boolean-point-in-polygon-5.1.5.tgz", + "integrity": "sha1-8BzBlNHgMKVIv9qYHLpDz9YpQbc=", + "dependencies": { + "@turf/helpers": "^5.1.5", + "@turf/invariant": "^5.1.5" + } + }, + "node_modules/latlon2country/node_modules/@turf/boolean-disjoint": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/@turf/boolean-disjoint/-/boolean-disjoint-5.1.6.tgz", + "integrity": "sha512-KHvUS6SBNYHBCLIJEJrg04pF5Oy+Fqn8V5G9U+9pti5vI9tyX7Ln2g7RSB7iJ1Cxsz8QAi6OukhXjEF2/8ZpGg==", + "dependencies": { + "@turf/boolean-point-in-polygon": "^5.1.5", + "@turf/helpers": "^5.1.5", + "@turf/line-intersect": "^5.1.5", + "@turf/meta": "^5.1.5", + "@turf/polygon-to-line": "^5.1.5" + } + }, + "node_modules/latlon2country/node_modules/@turf/boolean-disjoint/node_modules/@turf/boolean-point-in-polygon": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/boolean-point-in-polygon/-/boolean-point-in-polygon-5.1.5.tgz", + "integrity": "sha1-8BzBlNHgMKVIv9qYHLpDz9YpQbc=", + "dependencies": { + "@turf/helpers": "^5.1.5", + "@turf/invariant": "^5.1.5" + } + }, + "node_modules/latlon2country/node_modules/@turf/boolean-equal": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/boolean-equal/-/boolean-equal-5.1.5.tgz", + "integrity": "sha1-Kfj21gu4RQff12WzIlTbjnLJOKQ=", + "dependencies": { + "@turf/clean-coords": "^5.1.5", + "@turf/helpers": "^5.1.5", + "@turf/invariant": "^5.1.5", + "geojson-equality": "0.1.6" + } + }, + "node_modules/latlon2country/node_modules/@turf/boolean-overlap": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/boolean-overlap/-/boolean-overlap-5.1.5.tgz", + "integrity": "sha1-DU5kxSx3CijpPZ7834qLg3OsznU=", + "dependencies": { + "@turf/helpers": "^5.1.5", + "@turf/invariant": "^5.1.5", + "@turf/line-intersect": "^5.1.5", + "@turf/line-overlap": "^5.1.5", + "@turf/meta": "^5.1.5", + "geojson-equality": "0.1.6" + } + }, + "node_modules/latlon2country/node_modules/@turf/boolean-parallel": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/boolean-parallel/-/boolean-parallel-5.1.5.tgz", + "integrity": "sha1-c5NYR16ltlx+GCejw+DopofTqF0=", + "dependencies": { + "@turf/clean-coords": "^5.1.5", + "@turf/helpers": "^5.1.5", + "@turf/line-segment": "^5.1.5", + "@turf/rhumb-bearing": "^5.1.5" + } + }, + "node_modules/latlon2country/node_modules/@turf/boolean-point-on-line": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/boolean-point-on-line/-/boolean-point-on-line-5.1.5.tgz", + "integrity": "sha1-9jPF/4Aq0ku48Vja269v9KAj3Xs=", + "dependencies": { + "@turf/helpers": "^5.1.5", + "@turf/invariant": "^5.1.5" + } + }, + "node_modules/latlon2country/node_modules/@turf/boolean-within": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/boolean-within/-/boolean-within-5.1.5.tgz", + "integrity": "sha1-RxBdVtB1Kp0Pv81Dw2pfkUnchpc=", + "dependencies": { + "@turf/bbox": "^5.1.5", + "@turf/boolean-point-in-polygon": "^5.1.5", + "@turf/boolean-point-on-line": "^5.1.5", + "@turf/helpers": "^5.1.5", + "@turf/invariant": "^5.1.5" + } + }, + "node_modules/latlon2country/node_modules/@turf/boolean-within/node_modules/@turf/boolean-point-in-polygon": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/boolean-point-in-polygon/-/boolean-point-in-polygon-5.1.5.tgz", + "integrity": "sha1-8BzBlNHgMKVIv9qYHLpDz9YpQbc=", + "dependencies": { + "@turf/helpers": "^5.1.5", + "@turf/invariant": "^5.1.5" + } + }, + "node_modules/latlon2country/node_modules/@turf/buffer": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/buffer/-/buffer-5.1.5.tgz", + "integrity": "sha1-hByWJ8+5dLEirE4alW8EZrwCMcQ=", + "dependencies": { + "@turf/bbox": "^5.1.5", + "@turf/center": "^5.1.5", + "@turf/helpers": "^5.1.5", + "@turf/meta": "^5.1.5", + "@turf/projection": "^5.1.5", + "d3-geo": "1.7.1", + "turf-jsts": "*" + } + }, + "node_modules/latlon2country/node_modules/@turf/center": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/center/-/center-5.1.5.tgz", + "integrity": "sha1-RKss2VT2PA03dX9xWKmcPvURS4A=", + "dependencies": { + "@turf/bbox": "^5.1.5", + "@turf/helpers": "^5.1.5" + } + }, + "node_modules/latlon2country/node_modules/@turf/center-mean": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/center-mean/-/center-mean-5.1.5.tgz", + "integrity": "sha1-jI6YdTkeXwnw5uePXWYbiLIQigo=", + "dependencies": { + "@turf/bbox": "^5.1.5", + "@turf/helpers": "^5.1.5", + "@turf/meta": "^5.1.5" + } + }, + "node_modules/latlon2country/node_modules/@turf/center-median": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/center-median/-/center-median-5.1.5.tgz", + "integrity": "sha1-u0Yb/noqSGAdikcnaFcYcjoUqHI=", + "dependencies": { + "@turf/center-mean": "^5.1.5", + "@turf/centroid": "^5.1.5", + "@turf/distance": "^5.1.5", + "@turf/helpers": "^5.1.5", + "@turf/meta": "^5.1.5" + } + }, + "node_modules/latlon2country/node_modules/@turf/center-of-mass": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/center-of-mass/-/center-of-mass-5.1.5.tgz", + "integrity": "sha1-TTvXnYhJjbq4Mk1PafAyL2Uguco=", + "dependencies": { + "@turf/centroid": "^5.1.5", + "@turf/convex": "^5.1.5", + "@turf/helpers": "^5.1.5", + "@turf/invariant": "^5.1.5", + "@turf/meta": "^5.1.5" + } + }, + "node_modules/latlon2country/node_modules/@turf/centroid": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/centroid/-/centroid-5.1.5.tgz", + "integrity": "sha1-d4radCFjNQIa2P0OemWoNJ1Tx2k=", + "dependencies": { + "@turf/helpers": "^5.1.5", + "@turf/meta": "^5.1.5" + } + }, + "node_modules/latlon2country/node_modules/@turf/circle": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/circle/-/circle-5.1.5.tgz", + "integrity": "sha1-mxV3g1UIq1L7HBCypQZcuiuHtqU=", + "dependencies": { + "@turf/destination": "^5.1.5", + "@turf/helpers": "^5.1.5" + } + }, + "node_modules/latlon2country/node_modules/@turf/clean-coords": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/clean-coords/-/clean-coords-5.1.5.tgz", + "integrity": "sha1-EoAKmKeMmkUqcuxChJPEOs8q2h8=", + "dependencies": { + "@turf/helpers": "^5.1.5", + "@turf/invariant": "^5.1.5" + } + }, + "node_modules/latlon2country/node_modules/@turf/clone": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/clone/-/clone-5.1.5.tgz", + "integrity": "sha1-JT6NNUdxgZduM636tQoPAqfw42c=", + "dependencies": { + "@turf/helpers": "^5.1.5" + } + }, + "node_modules/latlon2country/node_modules/@turf/clusters": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/clusters/-/clusters-5.1.5.tgz", + "integrity": "sha1-ZzpeXxsZycq6vFfJCO6t1oIiTdQ=", + "dependencies": { + "@turf/helpers": "^5.1.5", + "@turf/meta": "^5.1.5" + } + }, + "node_modules/latlon2country/node_modules/@turf/clusters-dbscan": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/clusters-dbscan/-/clusters-dbscan-5.1.5.tgz", + "integrity": "sha1-V4H7TmVsdHoLjpk333MYHAMJ4m8=", + "dependencies": { + "@turf/clone": "^5.1.5", + "@turf/distance": "^5.1.5", + "@turf/helpers": "^5.1.5", + "@turf/invariant": "^5.1.5", + "@turf/meta": "^5.1.5", + "density-clustering": "1.3.0" + } + }, + "node_modules/latlon2country/node_modules/@turf/clusters-kmeans": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/clusters-kmeans/-/clusters-kmeans-5.1.5.tgz", + "integrity": "sha1-/W3+qLEzuovcI3CsPKzuFYejAvE=", + "dependencies": { + "@turf/clone": "^5.1.5", + "@turf/helpers": "^5.1.5", + "@turf/invariant": "^5.1.5", + "@turf/meta": "^5.1.5", + "skmeans": "0.9.7" + } + }, + "node_modules/latlon2country/node_modules/@turf/collect": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/collect/-/collect-5.1.5.tgz", + "integrity": "sha1-/pjJqMIY7PJP/DPXApUXt8GbKj4=", + "dependencies": { + "@turf/bbox": "^5.1.5", + "@turf/boolean-point-in-polygon": "^5.1.5", + "@turf/helpers": "^5.1.5", + "rbush": "^2.0.1" + } + }, + "node_modules/latlon2country/node_modules/@turf/collect/node_modules/@turf/boolean-point-in-polygon": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/boolean-point-in-polygon/-/boolean-point-in-polygon-5.1.5.tgz", + "integrity": "sha1-8BzBlNHgMKVIv9qYHLpDz9YpQbc=", + "dependencies": { + "@turf/helpers": "^5.1.5", + "@turf/invariant": "^5.1.5" + } + }, + "node_modules/latlon2country/node_modules/@turf/combine": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/combine/-/combine-5.1.5.tgz", + "integrity": "sha1-uxS976VVBDVxlfwaEkzX1TqMiQU=", + "dependencies": { + "@turf/helpers": "^5.1.5", + "@turf/meta": "^5.1.5" + } + }, + "node_modules/latlon2country/node_modules/@turf/concave": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/concave/-/concave-5.1.5.tgz", + "integrity": "sha1-I7uqw4fQNLlldKG9cNBZI3qdIRA=", + "dependencies": { + "@turf/clone": "^5.1.5", + "@turf/distance": "^5.1.5", + "@turf/helpers": "^5.1.5", + "@turf/invariant": "^5.1.5", + "@turf/meta": "^5.1.5", + "@turf/tin": "^5.1.5", + "topojson-client": "3.x", + "topojson-server": "3.x" + } + }, + "node_modules/latlon2country/node_modules/@turf/convex": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/convex/-/convex-5.1.5.tgz", + "integrity": "sha1-Dfk3fdACIWzpghsH9wXgN9rj4B0=", + "dependencies": { + "@turf/helpers": "^5.1.5", + "@turf/meta": "^5.1.5", + "concaveman": "*" + } + }, + "node_modules/latlon2country/node_modules/@turf/destination": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/destination/-/destination-5.1.5.tgz", + "integrity": "sha1-7TU4G9zoO73cvQei4rzivd/7zCY=", + "dependencies": { + "@turf/helpers": "^5.1.5", + "@turf/invariant": "^5.1.5" + } + }, + "node_modules/latlon2country/node_modules/@turf/difference": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/difference/-/difference-5.1.5.tgz", + "integrity": "sha1-ok1pCnvKgD8QkKnuO52Qb8Q3H0I=", + "dependencies": { + "@turf/area": "^5.1.5", + "@turf/helpers": "^5.1.5", + "@turf/invariant": "^5.1.5", + "@turf/meta": "^5.1.5", + "turf-jsts": "*" + } + }, + "node_modules/latlon2country/node_modules/@turf/dissolve": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/dissolve/-/dissolve-5.1.5.tgz", + "integrity": "sha1-LPEzqQIdIWODHD16lY1lB/nYGTg=", + "dependencies": { + "@turf/boolean-overlap": "^5.1.5", + "@turf/clone": "^5.1.5", + "@turf/helpers": "^5.1.5", + "@turf/invariant": "^5.1.5", + "@turf/line-intersect": "^5.1.5", + "@turf/meta": "^5.1.5", + "@turf/union": "^5.1.5", + "geojson-rbush": "2.1.0", + "get-closest": "*" + } + }, + "node_modules/latlon2country/node_modules/@turf/distance": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/distance/-/distance-5.1.5.tgz", + "integrity": "sha1-Oc8YIEu/h1h9cH5gmmARiQkVZAk=", + "dependencies": { + "@turf/helpers": "^5.1.5", + "@turf/invariant": "^5.1.5" + } + }, + "node_modules/latlon2country/node_modules/@turf/ellipse": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/ellipse/-/ellipse-5.1.5.tgz", + "integrity": "sha1-1XyrhTmFkgzeYCKKeNgEWAJcVL4=", + "dependencies": { + "@turf/helpers": "^5.1.5", + "@turf/invariant": "^5.1.5", + "@turf/rhumb-destination": "^5.1.5", + "@turf/transform-rotate": "^5.1.5" + } + }, + "node_modules/latlon2country/node_modules/@turf/envelope": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/envelope/-/envelope-5.1.5.tgz", + "integrity": "sha1-UBMwnFP91D369LWIplw/7X28EIo=", + "dependencies": { + "@turf/bbox": "^5.1.5", + "@turf/bbox-polygon": "^5.1.5", + "@turf/helpers": "^5.1.5" + } + }, + "node_modules/latlon2country/node_modules/@turf/explode": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/explode/-/explode-5.1.5.tgz", + "integrity": "sha1-sSsvd0AEobSPYrqVsgocZVo94Rg=", + "dependencies": { + "@turf/helpers": "^5.1.5", + "@turf/meta": "^5.1.5" + } + }, + "node_modules/latlon2country/node_modules/@turf/flatten": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/flatten/-/flatten-5.1.5.tgz", + "integrity": "sha1-2iknBnEz7WFpsLnWB7khVoiqE1g=", + "dependencies": { + "@turf/helpers": "^5.1.5", + "@turf/meta": "^5.1.5" + } + }, + "node_modules/latlon2country/node_modules/@turf/flip": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/flip/-/flip-5.1.5.tgz", + "integrity": "sha1-Q29kOnIvDKU7n85jjkaT2zYIpoo=", + "dependencies": { + "@turf/clone": "^5.1.5", + "@turf/helpers": "^5.1.5", + "@turf/meta": "^5.1.5" + } + }, + "node_modules/latlon2country/node_modules/@turf/great-circle": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/great-circle/-/great-circle-5.1.5.tgz", + "integrity": "sha1-3r+2cc5HVQnLY3MBwV/PzPo1mpM=", + "dependencies": { + "@turf/helpers": "^5.1.5", + "@turf/invariant": "^5.1.5" + } + }, + "node_modules/latlon2country/node_modules/@turf/helpers": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/helpers/-/helpers-5.1.5.tgz", + "integrity": "sha1-FTQFInq5M9AEpbuWQantmZ/L4M8=" + }, + "node_modules/latlon2country/node_modules/@turf/hex-grid": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/hex-grid/-/hex-grid-5.1.5.tgz", + "integrity": "sha1-m3ul/s9QUfHoWJL3E/zlxVBQKmo=", + "dependencies": { + "@turf/distance": "^5.1.5", + "@turf/helpers": "^5.1.5", + "@turf/intersect": "^5.1.5", + "@turf/invariant": "^5.1.5" + } + }, + "node_modules/latlon2country/node_modules/@turf/interpolate": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/interpolate/-/interpolate-5.1.5.tgz", + "integrity": "sha1-DxLwq3VtbdEK+ykMpuh3ve8BPqo=", + "dependencies": { + "@turf/bbox": "^5.1.5", + "@turf/centroid": "^5.1.5", + "@turf/clone": "^5.1.5", + "@turf/distance": "^5.1.5", + "@turf/helpers": "^5.1.5", + "@turf/hex-grid": "^5.1.5", + "@turf/invariant": "^5.1.5", + "@turf/meta": "^5.1.5", + "@turf/point-grid": "^5.1.5", + "@turf/square-grid": "^5.1.5", + "@turf/triangle-grid": "^5.1.5" + } + }, + "node_modules/latlon2country/node_modules/@turf/intersect": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/@turf/intersect/-/intersect-5.1.6.tgz", + "integrity": "sha512-KXyNv/GXdoGAOy03qZF53rgtXC2tNhF/4jLwTKiVRrBQH6kcEpipGStdJ+QkYIlarQPa8f7I9UlVAB19et4MfQ==", + "dependencies": { + "@turf/clean-coords": "^5.1.5", + "@turf/helpers": "^5.1.5", + "@turf/invariant": "^5.1.5", + "@turf/truncate": "^5.1.5", + "turf-jsts": "*" + } + }, + "node_modules/latlon2country/node_modules/@turf/invariant": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/invariant/-/invariant-5.1.5.tgz", + "integrity": "sha1-9Z9P76CSJLFdzhZR+QPIaNV6JOE=", + "dependencies": { + "@turf/helpers": "^5.1.5" + } + }, + "node_modules/latlon2country/node_modules/@turf/isobands": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/isobands/-/isobands-5.1.5.tgz", + "integrity": "sha1-a0TO9YTVUaMTBBh68jtKFYLj8I0=", + "dependencies": { + "@turf/area": "^5.1.5", + "@turf/bbox": "^5.1.5", + "@turf/boolean-point-in-polygon": "^5.1.5", + "@turf/explode": "^5.1.5", + "@turf/helpers": "^5.1.5", + "@turf/invariant": "^5.1.5", + "@turf/meta": "^5.1.5" + } + }, + "node_modules/latlon2country/node_modules/@turf/isobands/node_modules/@turf/boolean-point-in-polygon": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/boolean-point-in-polygon/-/boolean-point-in-polygon-5.1.5.tgz", + "integrity": "sha1-8BzBlNHgMKVIv9qYHLpDz9YpQbc=", + "dependencies": { + "@turf/helpers": "^5.1.5", + "@turf/invariant": "^5.1.5" + } + }, + "node_modules/latlon2country/node_modules/@turf/isolines": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/isolines/-/isolines-5.1.5.tgz", + "integrity": "sha1-irTn9Cuz38VGFOW/FVln9+VdLeE=", + "dependencies": { + "@turf/bbox": "^5.1.5", + "@turf/helpers": "^5.1.5", + "@turf/invariant": "^5.1.5", + "@turf/meta": "^5.1.5" + } + }, + "node_modules/latlon2country/node_modules/@turf/kinks": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/kinks/-/kinks-5.1.5.tgz", + "integrity": "sha1-irtpYdm7AQchO63fLCwmQNAlaYA=", + "dependencies": { + "@turf/helpers": "^5.1.5" + } + }, + "node_modules/latlon2country/node_modules/@turf/length": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/length/-/length-5.1.5.tgz", + "integrity": "sha1-86X4ZMK5lqi7RxeUU1ofrxLuvvs=", + "dependencies": { + "@turf/distance": "^5.1.5", + "@turf/helpers": "^5.1.5", + "@turf/meta": "^5.1.5" + } + }, + "node_modules/latlon2country/node_modules/@turf/line-arc": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/line-arc/-/line-arc-5.1.5.tgz", + "integrity": "sha1-AHinRHg1oSrkFKIR+aZNEYYVDhU=", + "dependencies": { + "@turf/circle": "^5.1.5", + "@turf/destination": "^5.1.5", + "@turf/helpers": "^5.1.5" + } + }, + "node_modules/latlon2country/node_modules/@turf/line-chunk": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/line-chunk/-/line-chunk-5.1.5.tgz", + "integrity": "sha1-kQqFwFwG2dD5w4l3oF4IGNUIXEI=", + "dependencies": { + "@turf/helpers": "^5.1.5", + "@turf/length": "^5.1.5", + "@turf/line-slice-along": "^5.1.5", + "@turf/meta": "^5.1.5" + } + }, + "node_modules/latlon2country/node_modules/@turf/line-intersect": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/line-intersect/-/line-intersect-5.1.5.tgz", + "integrity": "sha1-DikHGuQDKV5JFyO8SfXPrI0R3fM=", + "dependencies": { + "@turf/helpers": "^5.1.5", + "@turf/invariant": "^5.1.5", + "@turf/line-segment": "^5.1.5", + "@turf/meta": "^5.1.5", + "geojson-rbush": "2.1.0" + } + }, + "node_modules/latlon2country/node_modules/@turf/line-offset": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/line-offset/-/line-offset-5.1.5.tgz", + "integrity": "sha1-KrWy8In4yRPiMdmUN4553KkLWh4=", + "dependencies": { + "@turf/helpers": "^5.1.5", + "@turf/invariant": "^5.1.5", + "@turf/meta": "^5.1.5" + } + }, + "node_modules/latlon2country/node_modules/@turf/line-overlap": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/line-overlap/-/line-overlap-5.1.5.tgz", + "integrity": "sha1-lDxvh6A4bcQ9+sEdKz/5wRLNP2A=", + "dependencies": { + "@turf/boolean-point-on-line": "^5.1.5", + "@turf/helpers": "^5.1.5", + "@turf/invariant": "^5.1.5", + "@turf/line-segment": "^5.1.5", + "@turf/meta": "^5.1.5", + "@turf/nearest-point-on-line": "^5.1.5", + "geojson-rbush": "2.1.0" + } + }, + "node_modules/latlon2country/node_modules/@turf/line-segment": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/line-segment/-/line-segment-5.1.5.tgz", + "integrity": "sha1-Mgeq7lRqskw9jcPMY/kcdwuAE+U=", + "dependencies": { + "@turf/helpers": "^5.1.5", + "@turf/invariant": "^5.1.5", + "@turf/meta": "^5.1.5" + } + }, + "node_modules/latlon2country/node_modules/@turf/line-slice": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/line-slice/-/line-slice-5.1.5.tgz", + "integrity": "sha1-Hs/OFGKjeFeXVM7fRGTN4mgp8rU=", + "dependencies": { + "@turf/helpers": "^5.1.5", + "@turf/invariant": "^5.1.5", + "@turf/nearest-point-on-line": "^5.1.5" + } + }, + "node_modules/latlon2country/node_modules/@turf/line-slice-along": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/line-slice-along/-/line-slice-along-5.1.5.tgz", + "integrity": "sha1-7drQoh70efKWihG9LdcomiEy6aU=", + "dependencies": { + "@turf/bearing": "^5.1.5", + "@turf/destination": "^5.1.5", + "@turf/distance": "^5.1.5", + "@turf/helpers": "^5.1.5" + } + }, + "node_modules/latlon2country/node_modules/@turf/line-split": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/line-split/-/line-split-5.1.5.tgz", + "integrity": "sha1-Wy30w3YZty73JbUWPPmSbVVArLc=", + "dependencies": { + "@turf/bbox": "^5.1.5", + "@turf/helpers": "^5.1.5", + "@turf/invariant": "^5.1.5", + "@turf/line-intersect": "^5.1.5", + "@turf/line-segment": "^5.1.5", + "@turf/meta": "^5.1.5", + "@turf/nearest-point-on-line": "^5.1.5", + "@turf/square": "^5.1.5", + "@turf/truncate": "^5.1.5", + "geojson-rbush": "2.1.0" + } + }, + "node_modules/latlon2country/node_modules/@turf/line-to-polygon": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/line-to-polygon/-/line-to-polygon-5.1.5.tgz", + "integrity": "sha1-ITz0Gmj4Ikd4ujnTGH3sPouBhlo=", + "dependencies": { + "@turf/bbox": "^5.1.5", + "@turf/helpers": "^5.1.5", + "@turf/invariant": "^5.1.5" + } + }, + "node_modules/latlon2country/node_modules/@turf/mask": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/mask/-/mask-5.1.5.tgz", + "integrity": "sha1-mrD+8aJyyY/j70kvn/thggayQtU=", + "dependencies": { + "@turf/bbox": "^5.1.5", + "@turf/helpers": "^5.1.5", + "@turf/meta": "^5.1.5", + "@turf/union": "^5.1.5", + "rbush": "^2.0.1" + } + }, + "node_modules/latlon2country/node_modules/@turf/meta": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/@turf/meta/-/meta-5.1.6.tgz", + "integrity": "sha1-wgqGPt7Qhp+yhUje6Ik0G8y0akY=", + "dependencies": { + "@turf/helpers": "^5.1.5" + } + }, + "node_modules/latlon2country/node_modules/@turf/midpoint": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/midpoint/-/midpoint-5.1.5.tgz", + "integrity": "sha1-4mH2srDqgSTM7/VSomLdRlydBfA=", + "dependencies": { + "@turf/bearing": "^5.1.5", + "@turf/destination": "^5.1.5", + "@turf/distance": "^5.1.5", + "@turf/helpers": "^5.1.5" + } + }, + "node_modules/latlon2country/node_modules/@turf/nearest-point": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/nearest-point/-/nearest-point-5.1.5.tgz", + "integrity": "sha1-EgUN5Bw5hEMiTHl43g9iE5ANNPs=", + "dependencies": { + "@turf/clone": "^5.1.5", + "@turf/distance": "^5.1.5", + "@turf/helpers": "^5.1.5", + "@turf/meta": "^5.1.5" + } + }, + "node_modules/latlon2country/node_modules/@turf/nearest-point-on-line": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/nearest-point-on-line/-/nearest-point-on-line-5.1.5.tgz", + "integrity": "sha1-VgauKX8VlHUkvqUaKp71HsG/nDY=", + "dependencies": { + "@turf/bearing": "^5.1.5", + "@turf/destination": "^5.1.5", + "@turf/distance": "^5.1.5", + "@turf/helpers": "^5.1.5", + "@turf/invariant": "^5.1.5", + "@turf/line-intersect": "^5.1.5", + "@turf/meta": "^5.1.5" + } + }, + "node_modules/latlon2country/node_modules/@turf/nearest-point-to-line": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/@turf/nearest-point-to-line/-/nearest-point-to-line-5.1.6.tgz", + "integrity": "sha512-ZSvDIEiHhifn/vNwLXZI/E8xmEz5yBPqfUR7BVHRZrB1cP7jLhKZvkbidjG//uW8Fr1Ulc+PFOXczLspIcx/lw==", + "dependencies": { + "@turf/helpers": "6.x", + "@turf/invariant": "6.x", + "@turf/meta": "6.x", + "@turf/point-to-line-distance": "^5.1.5", + "object-assign": "*" + } + }, + "node_modules/latlon2country/node_modules/@turf/nearest-point-to-line/node_modules/@turf/helpers": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/helpers/-/helpers-6.3.0.tgz", + "integrity": "sha512-kr6KuD4Z0GZ30tblTEvi90rvvVNlKieXuMC8CTzE/rVQb0/f/Cb29zCXxTD7giQTEQY/P2nRW23wEqqyNHulCg==" + }, + "node_modules/latlon2country/node_modules/@turf/nearest-point-to-line/node_modules/@turf/invariant": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/invariant/-/invariant-6.3.0.tgz", + "integrity": "sha512-2OFOi9p+QOrcIMySEnr+WlOiKaFZ1bY56jA98YyECewJHfhPFWUBZEhc4nWGRT0ahK08Vus9+gcuBX8QIpCIIw==", + "dependencies": { + "@turf/helpers": "^6.3.0" + } + }, + "node_modules/latlon2country/node_modules/@turf/nearest-point-to-line/node_modules/@turf/meta": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/meta/-/meta-6.3.0.tgz", + "integrity": "sha512-qBJjaAJS9H3ap0HlGXyF/Bzfl0qkA9suafX/jnDsZvWMfVLt+s+o6twKrXOGk5t7nnNON2NFRC8+czxpu104EQ==", + "dependencies": { + "@turf/helpers": "^6.3.0" + } + }, + "node_modules/latlon2country/node_modules/@turf/planepoint": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/planepoint/-/planepoint-5.1.5.tgz", + "integrity": "sha1-GLvfAG91ne9eQsagBsn53oGyt/8=", + "dependencies": { + "@turf/helpers": "^5.1.5", + "@turf/invariant": "^5.1.5" + } + }, + "node_modules/latlon2country/node_modules/@turf/point-grid": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/point-grid/-/point-grid-5.1.5.tgz", + "integrity": "sha1-MFFBJI9Quv42zn5mukuX56sjaIc=", + "dependencies": { + "@turf/boolean-within": "^5.1.5", + "@turf/distance": "^5.1.5", + "@turf/helpers": "^5.1.5", + "@turf/invariant": "^5.1.5" + } + }, + "node_modules/latlon2country/node_modules/@turf/point-on-feature": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/point-on-feature/-/point-on-feature-5.1.5.tgz", + "integrity": "sha1-MMfwMkMCd8ZBjZbSieRba/shP+c=", + "dependencies": { + "@turf/boolean-point-in-polygon": "^5.1.5", + "@turf/center": "^5.1.5", + "@turf/explode": "^5.1.5", + "@turf/helpers": "^5.1.5", + "@turf/nearest-point": "^5.1.5" + } + }, + "node_modules/latlon2country/node_modules/@turf/point-on-feature/node_modules/@turf/boolean-point-in-polygon": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/boolean-point-in-polygon/-/boolean-point-in-polygon-5.1.5.tgz", + "integrity": "sha1-8BzBlNHgMKVIv9qYHLpDz9YpQbc=", + "dependencies": { + "@turf/helpers": "^5.1.5", + "@turf/invariant": "^5.1.5" + } + }, + "node_modules/latlon2country/node_modules/@turf/point-to-line-distance": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/@turf/point-to-line-distance/-/point-to-line-distance-5.1.6.tgz", + "integrity": "sha512-PE3hiTeeDEi4ZLPtI8XAzFYW9nHo1EVsZGm/4ZVV8jo39d3X1oLVHxY3e1PkCmWwRapXy4QLqvnTQ7nU4wspNw==", + "dependencies": { + "@turf/bearing": "6.x", + "@turf/distance": "6.x", + "@turf/helpers": "6.x", + "@turf/invariant": "6.x", + "@turf/meta": "6.x", + "@turf/projection": "6.x", + "@turf/rhumb-bearing": "6.x", + "@turf/rhumb-distance": "6.x" + } + }, + "node_modules/latlon2country/node_modules/@turf/point-to-line-distance/node_modules/@turf/bearing": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/bearing/-/bearing-6.3.0.tgz", + "integrity": "sha512-apuUm9xN6VQLO33m7F2mmzlm3dHfeesJjMSzh9iehGtgmp1IaVndjdcIvs0ieiwm8bN9UhwXpfPtO3pV0n9SFw==", + "dependencies": { + "@turf/helpers": "^6.3.0", + "@turf/invariant": "^6.3.0" + } + }, + "node_modules/latlon2country/node_modules/@turf/point-to-line-distance/node_modules/@turf/clone": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/clone/-/clone-6.3.0.tgz", + "integrity": "sha512-GAgN89/9GCqUKECB1oY2hcTs0K2rZj+a2tY6VfM0ef9wwckuQZCKi+kKGUzhKVrmHee15jKV8n6DY0er8OndKg==", + "dependencies": { + "@turf/helpers": "^6.3.0" + } + }, + "node_modules/latlon2country/node_modules/@turf/point-to-line-distance/node_modules/@turf/distance": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/distance/-/distance-6.3.0.tgz", + "integrity": "sha512-basi24ssNFnH3iXPFjp/aNUrukjObiFWoIyDRqKyBJxVwVOwAWvfk4d38QQyBj5nDo5IahYRq/Q+T47/5hSs9w==", + "dependencies": { + "@turf/helpers": "^6.3.0", + "@turf/invariant": "^6.3.0" + } + }, + "node_modules/latlon2country/node_modules/@turf/point-to-line-distance/node_modules/@turf/helpers": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/helpers/-/helpers-6.3.0.tgz", + "integrity": "sha512-kr6KuD4Z0GZ30tblTEvi90rvvVNlKieXuMC8CTzE/rVQb0/f/Cb29zCXxTD7giQTEQY/P2nRW23wEqqyNHulCg==" + }, + "node_modules/latlon2country/node_modules/@turf/point-to-line-distance/node_modules/@turf/invariant": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/invariant/-/invariant-6.3.0.tgz", + "integrity": "sha512-2OFOi9p+QOrcIMySEnr+WlOiKaFZ1bY56jA98YyECewJHfhPFWUBZEhc4nWGRT0ahK08Vus9+gcuBX8QIpCIIw==", + "dependencies": { + "@turf/helpers": "^6.3.0" + } + }, + "node_modules/latlon2country/node_modules/@turf/point-to-line-distance/node_modules/@turf/meta": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/meta/-/meta-6.3.0.tgz", + "integrity": "sha512-qBJjaAJS9H3ap0HlGXyF/Bzfl0qkA9suafX/jnDsZvWMfVLt+s+o6twKrXOGk5t7nnNON2NFRC8+czxpu104EQ==", + "dependencies": { + "@turf/helpers": "^6.3.0" + } + }, + "node_modules/latlon2country/node_modules/@turf/point-to-line-distance/node_modules/@turf/projection": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/projection/-/projection-6.3.0.tgz", + "integrity": "sha512-IpSs7Q6G6xi47ynVlYYVegPLy6Jc0yo3/DcIm83jaJa4NnzPFXIFZT0v9Fe1N8MraHZqiqaSPbVnJXCGwR12lg==", + "dependencies": { + "@turf/clone": "^6.3.0", + "@turf/helpers": "^6.3.0", + "@turf/meta": "^6.3.0" + } + }, + "node_modules/latlon2country/node_modules/@turf/point-to-line-distance/node_modules/@turf/rhumb-bearing": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/rhumb-bearing/-/rhumb-bearing-6.3.0.tgz", + "integrity": "sha512-/c/BE3huEUrwN6gx7Bg2FzfJqeU+TWk/slQPDHpbVunlIPbS6L28brqSVD+KXfMG8HQIzynz6Pm4Y+j5Iv4aWA==", + "dependencies": { + "@turf/helpers": "^6.3.0", + "@turf/invariant": "^6.3.0" + } + }, + "node_modules/latlon2country/node_modules/@turf/point-to-line-distance/node_modules/@turf/rhumb-distance": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/rhumb-distance/-/rhumb-distance-6.3.0.tgz", + "integrity": "sha512-wMIQVvznusonnp/POeucFdA4Rubn0NrkcEMdxdcCgFK7OmTz0zU4CEnNONF2IUGkQ5WwoKiuS7MOTQ8OuCjSfQ==", + "dependencies": { + "@turf/helpers": "^6.3.0", + "@turf/invariant": "^6.3.0" + } + }, + "node_modules/latlon2country/node_modules/@turf/points-within-polygon": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/points-within-polygon/-/points-within-polygon-5.1.5.tgz", + "integrity": "sha1-K4VaXfOq2lfC7oIKB1SrlJKKIzc=", + "dependencies": { + "@turf/boolean-point-in-polygon": "^5.1.5", + "@turf/helpers": "^5.1.5", + "@turf/meta": "^5.1.5" + } + }, + "node_modules/latlon2country/node_modules/@turf/points-within-polygon/node_modules/@turf/boolean-point-in-polygon": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/boolean-point-in-polygon/-/boolean-point-in-polygon-5.1.5.tgz", + "integrity": "sha1-8BzBlNHgMKVIv9qYHLpDz9YpQbc=", + "dependencies": { + "@turf/helpers": "^5.1.5", + "@turf/invariant": "^5.1.5" + } + }, + "node_modules/latlon2country/node_modules/@turf/polygon-tangents": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/polygon-tangents/-/polygon-tangents-5.1.5.tgz", + "integrity": "sha1-K/AJkUcwJbF44lDcfLmuVAm71lI=", + "dependencies": { + "@turf/helpers": "^5.1.5", + "@turf/invariant": "^5.1.5" + } + }, + "node_modules/latlon2country/node_modules/@turf/polygon-to-line": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/polygon-to-line/-/polygon-to-line-5.1.5.tgz", + "integrity": "sha1-I7tEjYTcTGUZmaxhGjbZHFklA2o=", + "dependencies": { + "@turf/helpers": "^5.1.5", + "@turf/invariant": "^5.1.5" + } + }, + "node_modules/latlon2country/node_modules/@turf/polygonize": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/polygonize/-/polygonize-5.1.5.tgz", + "integrity": "sha1-BJP6EYefOdELmtAs5qI+lC0IqjI=", + "dependencies": { + "@turf/boolean-point-in-polygon": "^5.1.5", + "@turf/envelope": "^5.1.5", + "@turf/helpers": "^5.1.5", + "@turf/invariant": "^5.1.5", + "@turf/meta": "^5.1.5" + } + }, + "node_modules/latlon2country/node_modules/@turf/polygonize/node_modules/@turf/boolean-point-in-polygon": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/boolean-point-in-polygon/-/boolean-point-in-polygon-5.1.5.tgz", + "integrity": "sha1-8BzBlNHgMKVIv9qYHLpDz9YpQbc=", + "dependencies": { + "@turf/helpers": "^5.1.5", + "@turf/invariant": "^5.1.5" + } + }, + "node_modules/latlon2country/node_modules/@turf/projection": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/projection/-/projection-5.1.5.tgz", + "integrity": "sha1-JFF+7rLzaBa6n3EueubWo2jt91c=", + "dependencies": { + "@turf/clone": "^5.1.5", + "@turf/helpers": "^5.1.5", + "@turf/meta": "^5.1.5" + } + }, + "node_modules/latlon2country/node_modules/@turf/random": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/random/-/random-5.1.5.tgz", + "integrity": "sha1-sy78k0Vgroulfo67UfJBw5+6Lns=", + "dependencies": { + "@turf/helpers": "^5.1.5" + } + }, + "node_modules/latlon2country/node_modules/@turf/rewind": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/rewind/-/rewind-5.1.5.tgz", + "integrity": "sha1-nqPbSmi3PB/R3RH1djGxQ8/vock=", + "dependencies": { + "@turf/boolean-clockwise": "^5.1.5", + "@turf/clone": "^5.1.5", + "@turf/helpers": "^5.1.5", + "@turf/invariant": "^5.1.5", + "@turf/meta": "^5.1.5" + } + }, + "node_modules/latlon2country/node_modules/@turf/rhumb-bearing": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/rhumb-bearing/-/rhumb-bearing-5.1.5.tgz", + "integrity": "sha1-rPalAkJ+uMSeGM2mrg7/qwxd3NI=", + "dependencies": { + "@turf/helpers": "^5.1.5", + "@turf/invariant": "^5.1.5" + } + }, + "node_modules/latlon2country/node_modules/@turf/rhumb-destination": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/rhumb-destination/-/rhumb-destination-5.1.5.tgz", + "integrity": "sha1-sbKuuSFUfyrAwamUtqEw+SRjx0I=", + "dependencies": { + "@turf/helpers": "^5.1.5", + "@turf/invariant": "^5.1.5" + } + }, + "node_modules/latlon2country/node_modules/@turf/rhumb-distance": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/rhumb-distance/-/rhumb-distance-5.1.5.tgz", + "integrity": "sha1-GAaFdiX0IlOE2tQT5p85U4/192U=", + "dependencies": { + "@turf/helpers": "^5.1.5", + "@turf/invariant": "^5.1.5" + } + }, + "node_modules/latlon2country/node_modules/@turf/sample": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/sample/-/sample-5.1.5.tgz", + "integrity": "sha1-6ctEikeJzFbuPeLdZ4HiNDQ1tBE=", + "dependencies": { + "@turf/helpers": "^5.1.5" + } + }, + "node_modules/latlon2country/node_modules/@turf/sector": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/sector/-/sector-5.1.5.tgz", + "integrity": "sha1-rCu5TBPt1gNPb9wrZwCBNdIPXgc=", + "dependencies": { + "@turf/circle": "^5.1.5", + "@turf/helpers": "^5.1.5", + "@turf/invariant": "^5.1.5", + "@turf/line-arc": "^5.1.5", + "@turf/meta": "^5.1.5" + } + }, + "node_modules/latlon2country/node_modules/@turf/shortest-path": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/shortest-path/-/shortest-path-5.1.5.tgz", + "integrity": "sha1-hUroCW9rw+EwD6ynfz6PZ9j5Nas=", + "dependencies": { + "@turf/bbox": "^5.1.5", + "@turf/bbox-polygon": "^5.1.5", + "@turf/boolean-point-in-polygon": "^5.1.5", + "@turf/clean-coords": "^5.1.5", + "@turf/distance": "^5.1.5", + "@turf/helpers": "^5.1.5", + "@turf/invariant": "^5.1.5", + "@turf/meta": "^5.1.5", + "@turf/transform-scale": "^5.1.5" + } + }, + "node_modules/latlon2country/node_modules/@turf/shortest-path/node_modules/@turf/boolean-point-in-polygon": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/boolean-point-in-polygon/-/boolean-point-in-polygon-5.1.5.tgz", + "integrity": "sha1-8BzBlNHgMKVIv9qYHLpDz9YpQbc=", + "dependencies": { + "@turf/helpers": "^5.1.5", + "@turf/invariant": "^5.1.5" + } + }, + "node_modules/latlon2country/node_modules/@turf/simplify": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/simplify/-/simplify-5.1.5.tgz", + "integrity": "sha1-Csjyei60IYGD7dmZjDJ1q+QIuSY=", + "dependencies": { + "@turf/clean-coords": "^5.1.5", + "@turf/clone": "^5.1.5", + "@turf/helpers": "^5.1.5", + "@turf/meta": "^5.1.5" + } + }, + "node_modules/latlon2country/node_modules/@turf/square": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/square/-/square-5.1.5.tgz", + "integrity": "sha1-qnsh5gM8ySUsOlvW89iNq9b+0YA=", + "dependencies": { + "@turf/distance": "^5.1.5", + "@turf/helpers": "^5.1.5" + } + }, + "node_modules/latlon2country/node_modules/@turf/square-grid": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/square-grid/-/square-grid-5.1.5.tgz", + "integrity": "sha1-G9X3uesU8LYLwjH+/nNR0aMvGlE=", + "dependencies": { + "@turf/boolean-contains": "^5.1.5", + "@turf/boolean-overlap": "^5.1.5", + "@turf/distance": "^5.1.5", + "@turf/helpers": "^5.1.5", + "@turf/intersect": "^5.1.5", + "@turf/invariant": "^5.1.5" + } + }, + "node_modules/latlon2country/node_modules/@turf/standard-deviational-ellipse": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/standard-deviational-ellipse/-/standard-deviational-ellipse-5.1.5.tgz", + "integrity": "sha1-hc0oO14ayljyG9ZkEuQUtW2FIyQ=", + "dependencies": { + "@turf/center-mean": "^5.1.5", + "@turf/ellipse": "^5.1.5", + "@turf/helpers": "^5.1.5", + "@turf/invariant": "^5.1.5", + "@turf/meta": "^5.1.5", + "@turf/points-within-polygon": "^5.1.5" + } + }, + "node_modules/latlon2country/node_modules/@turf/tag": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/tag/-/tag-5.1.5.tgz", + "integrity": "sha1-0e4aUIjs/UoUEQGcmCOczypJfSA=", + "dependencies": { + "@turf/boolean-point-in-polygon": "^5.1.5", + "@turf/clone": "^5.1.5", + "@turf/helpers": "^5.1.5", + "@turf/meta": "^5.1.5" + } + }, + "node_modules/latlon2country/node_modules/@turf/tag/node_modules/@turf/boolean-point-in-polygon": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/boolean-point-in-polygon/-/boolean-point-in-polygon-5.1.5.tgz", + "integrity": "sha1-8BzBlNHgMKVIv9qYHLpDz9YpQbc=", + "dependencies": { + "@turf/helpers": "^5.1.5", + "@turf/invariant": "^5.1.5" + } + }, + "node_modules/latlon2country/node_modules/@turf/tesselate": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/tesselate/-/tesselate-5.1.5.tgz", + "integrity": "sha1-MqWU6cIaAEIKn5DSxD3z4RZgYc0=", + "dependencies": { + "@turf/helpers": "^5.1.5", + "earcut": "^2.0.0" + } + }, + "node_modules/latlon2country/node_modules/@turf/tin": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/tin/-/tin-5.1.5.tgz", + "integrity": "sha1-KCI+r8X76a6azKgc3P6l0UJMkX0=", + "dependencies": { + "@turf/helpers": "^5.1.5" + } + }, + "node_modules/latlon2country/node_modules/@turf/transform-rotate": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/transform-rotate/-/transform-rotate-5.1.5.tgz", + "integrity": "sha1-0Jbt2eMA/jFQadVNjkWMQJIh7fs=", + "dependencies": { + "@turf/centroid": "^5.1.5", + "@turf/clone": "^5.1.5", + "@turf/helpers": "^5.1.5", + "@turf/invariant": "^5.1.5", + "@turf/meta": "^5.1.5", + "@turf/rhumb-bearing": "^5.1.5", + "@turf/rhumb-destination": "^5.1.5", + "@turf/rhumb-distance": "^5.1.5" + } + }, + "node_modules/latlon2country/node_modules/@turf/transform-scale": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/transform-scale/-/transform-scale-5.1.5.tgz", + "integrity": "sha1-cP064BhWz3uunxWtVhzf6PiQAbk=", + "dependencies": { + "@turf/bbox": "^5.1.5", + "@turf/center": "^5.1.5", + "@turf/centroid": "^5.1.5", + "@turf/clone": "^5.1.5", + "@turf/helpers": "^5.1.5", + "@turf/invariant": "^5.1.5", + "@turf/meta": "^5.1.5", + "@turf/rhumb-bearing": "^5.1.5", + "@turf/rhumb-destination": "^5.1.5", + "@turf/rhumb-distance": "^5.1.5" + } + }, + "node_modules/latlon2country/node_modules/@turf/transform-translate": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/transform-translate/-/transform-translate-5.1.5.tgz", + "integrity": "sha1-Uwolf7Hccmja3Ks05nkB6yo97GM=", + "dependencies": { + "@turf/clone": "^5.1.5", + "@turf/helpers": "^5.1.5", + "@turf/invariant": "^5.1.5", + "@turf/meta": "^5.1.5", + "@turf/rhumb-destination": "^5.1.5" + } + }, + "node_modules/latlon2country/node_modules/@turf/triangle-grid": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/triangle-grid/-/triangle-grid-5.1.5.tgz", + "integrity": "sha1-ezZ2IQhVTBTyjK/zxIsc/ILI3IE=", + "dependencies": { + "@turf/distance": "^5.1.5", + "@turf/helpers": "^5.1.5", + "@turf/intersect": "^5.1.5", + "@turf/invariant": "^5.1.5" + } + }, + "node_modules/latlon2country/node_modules/@turf/truncate": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/truncate/-/truncate-5.1.5.tgz", + "integrity": "sha1-nu37Oxi6gfLJjT6tCUMcyhiErYk=", + "dependencies": { + "@turf/helpers": "^5.1.5", + "@turf/meta": "^5.1.5" + } + }, + "node_modules/latlon2country/node_modules/@turf/turf": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/@turf/turf/-/turf-5.1.6.tgz", + "integrity": "sha1-wxIlkoh+0jS3VGi4qMRb+Ib7+PY=", + "dependencies": { + "@turf/along": "5.1.x", + "@turf/area": "5.1.x", + "@turf/bbox": "5.1.x", + "@turf/bbox-clip": "5.1.x", + "@turf/bbox-polygon": "5.1.x", + "@turf/bearing": "5.1.x", + "@turf/bezier-spline": "5.1.x", + "@turf/boolean-clockwise": "5.1.x", + "@turf/boolean-contains": "5.1.x", + "@turf/boolean-crosses": "5.1.x", + "@turf/boolean-disjoint": "5.1.x", + "@turf/boolean-equal": "5.1.x", + "@turf/boolean-overlap": "5.1.x", + "@turf/boolean-parallel": "5.1.x", + "@turf/boolean-point-in-polygon": "5.1.x", + "@turf/boolean-point-on-line": "5.1.x", + "@turf/boolean-within": "5.1.x", + "@turf/buffer": "5.1.x", + "@turf/center": "5.1.x", + "@turf/center-mean": "5.1.x", + "@turf/center-median": "5.1.x", + "@turf/center-of-mass": "5.1.x", + "@turf/centroid": "5.1.x", + "@turf/circle": "5.1.x", + "@turf/clean-coords": "5.1.x", + "@turf/clone": "5.1.x", + "@turf/clusters": "5.1.x", + "@turf/clusters-dbscan": "5.1.x", + "@turf/clusters-kmeans": "5.1.x", + "@turf/collect": "5.1.x", + "@turf/combine": "5.1.x", + "@turf/concave": "5.1.x", + "@turf/convex": "5.1.x", + "@turf/destination": "5.1.x", + "@turf/difference": "5.1.x", + "@turf/dissolve": "5.1.x", + "@turf/distance": "5.1.x", + "@turf/ellipse": "5.1.x", + "@turf/envelope": "5.1.x", + "@turf/explode": "5.1.x", + "@turf/flatten": "5.1.x", + "@turf/flip": "5.1.x", + "@turf/great-circle": "5.1.x", + "@turf/helpers": "5.1.x", + "@turf/hex-grid": "5.1.x", + "@turf/interpolate": "5.1.x", + "@turf/intersect": "5.1.x", + "@turf/invariant": "5.1.x", + "@turf/isobands": "5.1.x", + "@turf/isolines": "5.1.x", + "@turf/kinks": "5.1.x", + "@turf/length": "5.1.x", + "@turf/line-arc": "5.1.x", + "@turf/line-chunk": "5.1.x", + "@turf/line-intersect": "5.1.x", + "@turf/line-offset": "5.1.x", + "@turf/line-overlap": "5.1.x", + "@turf/line-segment": "5.1.x", + "@turf/line-slice": "5.1.x", + "@turf/line-slice-along": "5.1.x", + "@turf/line-split": "5.1.x", + "@turf/line-to-polygon": "5.1.x", + "@turf/mask": "5.1.x", + "@turf/meta": "5.1.x", + "@turf/midpoint": "5.1.x", + "@turf/nearest-point": "5.1.x", + "@turf/nearest-point-on-line": "5.1.x", + "@turf/nearest-point-to-line": "5.1.x", + "@turf/planepoint": "5.1.x", + "@turf/point-grid": "5.1.x", + "@turf/point-on-feature": "5.1.x", + "@turf/point-to-line-distance": "5.1.x", + "@turf/points-within-polygon": "5.1.x", + "@turf/polygon-tangents": "5.1.x", + "@turf/polygon-to-line": "5.1.x", + "@turf/polygonize": "5.1.x", + "@turf/projection": "5.1.x", + "@turf/random": "5.1.x", + "@turf/rewind": "5.1.x", + "@turf/rhumb-bearing": "5.1.x", + "@turf/rhumb-destination": "5.1.x", + "@turf/rhumb-distance": "5.1.x", + "@turf/sample": "5.1.x", + "@turf/sector": "5.1.x", + "@turf/shortest-path": "5.1.x", + "@turf/simplify": "5.1.x", + "@turf/square": "5.1.x", + "@turf/square-grid": "5.1.x", + "@turf/standard-deviational-ellipse": "5.1.x", + "@turf/tag": "5.1.x", + "@turf/tesselate": "5.1.x", + "@turf/tin": "5.1.x", + "@turf/transform-rotate": "5.1.x", + "@turf/transform-scale": "5.1.x", + "@turf/transform-translate": "5.1.x", + "@turf/triangle-grid": "5.1.x", + "@turf/truncate": "5.1.x", + "@turf/union": "5.1.x", + "@turf/unkink-polygon": "5.1.x", + "@turf/voronoi": "5.1.x" + } + }, + "node_modules/latlon2country/node_modules/@turf/turf/node_modules/@turf/boolean-point-in-polygon": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/boolean-point-in-polygon/-/boolean-point-in-polygon-5.1.5.tgz", + "integrity": "sha1-8BzBlNHgMKVIv9qYHLpDz9YpQbc=", + "dependencies": { + "@turf/helpers": "^5.1.5", + "@turf/invariant": "^5.1.5" + } + }, + "node_modules/latlon2country/node_modules/@turf/union": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/union/-/union-5.1.5.tgz", + "integrity": "sha1-UyhbYJQEf8WNlqrA6pCGXsNNRUs=", + "dependencies": { + "@turf/helpers": "^5.1.5", + "turf-jsts": "*" + } + }, + "node_modules/latlon2country/node_modules/@turf/unkink-polygon": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/unkink-polygon/-/unkink-polygon-5.1.5.tgz", + "integrity": "sha1-ewGEfFD7V0riV54Z5Ey6hSbSE8M=", + "dependencies": { + "@turf/area": "^5.1.5", + "@turf/boolean-point-in-polygon": "^5.1.5", + "@turf/helpers": "^5.1.5", + "@turf/meta": "^5.1.5", + "rbush": "^2.0.1" + } + }, + "node_modules/latlon2country/node_modules/@turf/unkink-polygon/node_modules/@turf/boolean-point-in-polygon": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/boolean-point-in-polygon/-/boolean-point-in-polygon-5.1.5.tgz", + "integrity": "sha1-8BzBlNHgMKVIv9qYHLpDz9YpQbc=", + "dependencies": { + "@turf/helpers": "^5.1.5", + "@turf/invariant": "^5.1.5" + } + }, + "node_modules/latlon2country/node_modules/@turf/voronoi": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/voronoi/-/voronoi-5.1.5.tgz", + "integrity": "sha1-6FbpQG3MLyXWbdyJhYTifC6/ymY=", + "dependencies": { + "@turf/helpers": "^5.1.5", + "@turf/invariant": "^5.1.5", + "d3-voronoi": "1.1.2" + } + }, + "node_modules/latlon2country/node_modules/@types/node": { + "version": "14.14.44", + "resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.44.tgz", + "integrity": "sha512-+gaugz6Oce6ZInfI/tK4Pq5wIIkJMEJUu92RB3Eu93mtj4wjjjz9EB5mLp5s1pSsLXdC/CPut/xF20ZzAQJbTA==" + }, + "node_modules/latlon2country/node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==" + }, + "node_modules/latlon2country/node_modules/geojson-rbush": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/geojson-rbush/-/geojson-rbush-2.1.0.tgz", + "integrity": "sha1-O9c745H8ELCuaT2bis6iquC4Oo0=", + "dependencies": { + "@turf/helpers": "*", + "@turf/meta": "*", + "rbush": "*" + } + }, + "node_modules/latlon2country/node_modules/topojson-client": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/topojson-client/-/topojson-client-3.1.0.tgz", + "integrity": "sha512-605uxS6bcYxGXw9qi62XyrV6Q3xwbndjachmNxu8HWTtVPxZfEJN9fd/SZS1Q54Sn2y0TMyMxFj/cJINqGHrKw==", + "dependencies": { + "commander": "2" + }, + "bin": { + "topo2geo": "bin/topo2geo", + "topomerge": "bin/topomerge", + "topoquantize": "bin/topoquantize" + } + }, + "node_modules/latlon2country/node_modules/topojson-server": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/topojson-server/-/topojson-server-3.0.1.tgz", + "integrity": "sha512-/VS9j/ffKr2XAOjlZ9CgyyeLmgJ9dMwq6Y0YEON8O7p/tGGk+dCWnrE03zEdu7i4L7YsFZLEPZPzCvcB7lEEXw==", + "dependencies": { + "commander": "2" + }, + "bin": { + "geo2topo": "bin/geo2topo" + } + }, + "node_modules/latlon2country/node_modules/turf": { + "version": "3.0.14", + "resolved": "https://registry.npmjs.org/turf/-/turf-3.0.14.tgz", + "integrity": "sha1-6y9KgKLVg7jGSGvHtccZBGaGbCc=", + "deprecated": "This package has moved: use @turf/turf instead", + "dependencies": { + "turf-along": "^3.0.12", + "turf-area": "^3.0.12", + "turf-bbox": "^3.0.12", + "turf-bbox-polygon": "^3.0.12", + "turf-bearing": "^3.0.12", + "turf-bezier": "^3.0.12", + "turf-buffer": "^3.0.12", + "turf-center": "^3.0.12", + "turf-centroid": "^3.0.12", + "turf-circle": "^3.0.12", + "turf-collect": "^3.0.12", + "turf-combine": "^3.0.12", + "turf-concave": "^3.0.12", + "turf-convex": "^3.0.12", + "turf-destination": "^3.0.12", + "turf-difference": "^3.0.12", + "turf-distance": "^3.0.12", + "turf-envelope": "^3.0.12", + "turf-explode": "^3.0.12", + "turf-flip": "^3.0.12", + "turf-helpers": "^3.0.12", + "turf-hex-grid": "^3.0.12", + "turf-inside": "^3.0.12", + "turf-intersect": "^3.0.12", + "turf-isolines": "^3.0.12", + "turf-kinks": "^3.0.12", + "turf-line-distance": "^3.0.12", + "turf-line-slice": "^3.0.12", + "turf-meta": "^3.0.12", + "turf-midpoint": "^3.0.12", + "turf-nearest": "^3.0.12", + "turf-planepoint": "^3.0.12", + "turf-point-grid": "^3.0.12", + "turf-point-on-line": "^3.0.12", + "turf-point-on-surface": "^3.0.12", + "turf-random": "^3.0.12", + "turf-sample": "^3.0.12", + "turf-simplify": "^3.0.12", + "turf-square": "^3.0.12", + "turf-square-grid": "^3.0.12", + "turf-tag": "^3.0.12", + "turf-tesselate": "^3.0.12", + "turf-tin": "^3.0.12", + "turf-triangle-grid": "^3.0.12", + "turf-union": "^3.0.12", + "turf-within": "^3.0.12" + } + }, + "node_modules/leaflet": { + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/leaflet/-/leaflet-1.7.1.tgz", + "integrity": "sha512-/xwPEBidtg69Q3HlqPdU3DnrXQOvQU/CCHA1tcDQVzOwm91YMYaILjNp7L4Eaw5Z4sOYdbBz6koWyibppd8Zqw==" + }, + "node_modules/leaflet-providers": { + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/leaflet-providers/-/leaflet-providers-1.12.0.tgz", + "integrity": "sha512-pU/mR4B+NbayBGCg5/88dmRq7t1EGiNPhsVGV3yqHuDn594vIwus4CiPVW0RtiKJNKg8Vf1pILAbFl0i+yk+lQ==" + }, + "node_modules/leaflet-simple-map-screenshoter": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/leaflet-simple-map-screenshoter/-/leaflet-simple-map-screenshoter-0.4.4.tgz", + "integrity": "sha512-n5r04/PxXvqPQUJH+kP+vYj1Sg231YITPwoPMmdHwe+nSB+NJtQS0emEh9BaXXIbkZxubxeWQ1mKXpJYOxCAmw==", + "dependencies": { + "dom-to-image-more": "^2.8.0", + "file-saver": "^2.0.2" + } + }, + "node_modules/leaflet.markercluster": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/leaflet.markercluster/-/leaflet.markercluster-1.5.0.tgz", + "integrity": "sha512-Fvf/cq4o806mJL50n+fZW9+QALDDLPvt7vuAjlD2vfnxx3srMDs2vWINJze4nKYJYRY45OC6tM/669C3pLwMCA==", + "peerDependencies": { + "leaflet": "^1.3.1" + } + }, + "node_modules/levn": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", + "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", + "dependencies": { + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/libphonenumber": { + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/libphonenumber/-/libphonenumber-0.0.10.tgz", + "integrity": "sha1-54u/ZgGYnNCfpDUEdkaI1BeUFIw=", + "dependencies": { + "closure": "1.0.3", + "mocha": "^2.4.5" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/libphonenumber-js": { + "version": "1.9.17", + "resolved": "https://registry.npmjs.org/libphonenumber-js/-/libphonenumber-js-1.9.17.tgz", + "integrity": "sha512-ElJki901OynMg1l+evooPH1VyHrECuLqpgc12z2BkK25dFU5lUKTuMHEYV2jXxvtns/PIuJax56cBeoSK7ANow==" + }, + "node_modules/lilconfig": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.0.3.tgz", + "integrity": "sha512-EHKqr/+ZvdKCifpNrJCKxBTgk5XupZA3y/aCPY9mxfgBzmgh93Mt/WqjjQ38oMxXuvDokaKiM3lAgvSH2sjtHg==", + "engines": { + "node": ">=10" + } + }, + "node_modules/lineclip": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/lineclip/-/lineclip-1.1.5.tgz", + "integrity": "sha1-K/JgZ9lDVP6r+R5CdoI221YW/RM=" + }, + "node_modules/lines-and-columns": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.1.6.tgz", + "integrity": "sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA=" + }, + "node_modules/load-json-file": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", + "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=", + "dev": true, + "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" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/load-json-file/node_modules/parse-json": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", + "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", + "dev": true, + "dependencies": { + "error-ex": "^1.2.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" + }, + "node_modules/lodash.clone": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.clone/-/lodash.clone-4.5.0.tgz", + "integrity": "sha1-GVhwRQ9aExkkeN9Lw9I9LeoZB7Y=" + }, + "node_modules/lodash.debounce": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", + "integrity": "sha1-gteb/zCmfEAF/9XiUVMArZyk168=" + }, + "node_modules/lodash.includes": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", + "integrity": "sha1-YLuYqHy5I8aMoeUTJUgzFISfVT8=" + }, + "node_modules/lodash.isboolean": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", + "integrity": "sha1-bC4XHbKiV82WgC/UOwGyDV9YcPY=" + }, + "node_modules/lodash.isinteger": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", + "integrity": "sha1-YZwK89A/iwTDH1iChAt3sRzWg0M=" + }, + "node_modules/lodash.isnumber": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", + "integrity": "sha1-POdoEMWSjQM1IwGsKHMX8RwLH/w=" + }, + "node_modules/lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs=" + }, + "node_modules/lodash.isstring": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", + "integrity": "sha1-1SfftUVuynzJu5XV2ur4i6VKVFE=" + }, + "node_modules/lodash.memoize": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", + "integrity": "sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4=" + }, + "node_modules/lodash.once": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", + "integrity": "sha1-DdOXEhPHxW34gJd9UEyI+0cal6w=" + }, + "node_modules/lodash.sortby": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz", + "integrity": "sha1-7dFMgk4sycHgsKG0K7UhBRakJDg=" + }, + "node_modules/lodash.topath": { + "version": "4.5.2", + "resolved": "https://registry.npmjs.org/lodash.topath/-/lodash.topath-4.5.2.tgz", + "integrity": "sha1-NhY1Hzu6YZlKCTGYlmC9AyVP0Ak=" + }, + "node_modules/lodash.uniq": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", + "integrity": "sha1-0CJTc662Uq3BvILklFM5qEJ1R3M=" + }, + "node_modules/log-symbols": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-2.2.0.tgz", + "integrity": "sha512-VeIAFslyIerEJLXHziedo2basKbMKtTw3vfn5IzG0XTjhAVEJyNHnL2p7vc+wBDSdQuUpNw3M2u6xb9QsAY5Eg==", + "dependencies": { + "chalk": "^2.0.1" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/loud-rejection": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/loud-rejection/-/loud-rejection-1.6.0.tgz", + "integrity": "sha1-W0b4AUft7leIcPCG0Eghz5mOVR8=", + "dev": true, + "dependencies": { + "currently-unhandled": "^0.4.1", + "signal-exit": "^3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/lru-cache": { + "version": "2.7.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.7.3.tgz", + "integrity": "sha1-bUUk6LlV+V1PW1iFHOId1y+06VI=" + }, + "node_modules/lz-string": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/lz-string/-/lz-string-1.4.4.tgz", + "integrity": "sha1-wNjq82BZ9wV5bh40SBHPTEmNOiY=", + "bin": { + "lz-string": "bin/bin.js" + } + }, + "node_modules/magic-string": { + "version": "0.22.5", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.22.5.tgz", + "integrity": "sha512-oreip9rJZkzvA8Qzk9HFs8fZGF/u7H/gtrE8EN6RjKJ9kh2HlC+yQ2QezifqTZfGyiuAV0dRv5a+y/8gBb1m9w==", + "dependencies": { + "vlq": "^0.2.2" + } + }, + "node_modules/make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==" + }, + "node_modules/mangrove-reviews": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/mangrove-reviews/-/mangrove-reviews-0.1.4.tgz", + "integrity": "sha512-HRef9CXlIgxmTSBx9dikyfO8DxTYQ6baQ9AU9ZOK71lLgV7OzbW4jqnhgUfHAIsteA3scdl4QPHyKtUNgw8edg==", + "dependencies": { + "axios": "^0.19.2", + "jsonwebtoken": "^8.5.1", + "jwk-to-pem": "^2.0.3" + } + }, + "node_modules/map-cache": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", + "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/map-obj": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz", + "integrity": "sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/map-visit": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz", + "integrity": "sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=", + "dependencies": { + "object-visit": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/marked": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/marked/-/marked-2.0.3.tgz", + "integrity": "sha512-5otztIIcJfPc2qGTN8cVtOJEjNJZ0jwa46INMagrYfk0EvqtRuEHLsEe0LrFS0/q+ZRKT0+kXK7P2T1AN5lWRA==", + "dev": true, + "bin": { + "marked": "bin/marked" + }, + "engines": { + "node": ">= 8.16.2" + } + }, + "node_modules/md5.js": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", + "integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==", + "dependencies": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "node_modules/mdn-data": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.4.tgz", + "integrity": "sha512-iV3XNKw06j5Q7mi6h+9vbx23Tv7JkjEVgKHW4pimwyDGWm0OIQntJJ+u1C6mg6mK1EaTv42XQ7w76yuzH7M2cA==" + }, + "node_modules/memorystream": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/memorystream/-/memorystream-0.3.1.tgz", + "integrity": "sha1-htcJCzDORV1j+64S3aUaR93K+bI=", + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/meow": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/meow/-/meow-3.7.0.tgz", + "integrity": "sha1-cstmi0JSKCkKu/qFaJJYcwioAfs=", + "dev": true, + "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" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/merge-source-map": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/merge-source-map/-/merge-source-map-1.0.4.tgz", + "integrity": "sha1-pd5GU42uhNQRTMXqArR3KmNGcB8=", + "dependencies": { + "source-map": "^0.5.6" + } + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz", + "integrity": "sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==", + "dependencies": { + "braces": "^3.0.1", + "picomatch": "^2.2.3" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/miller-rabin": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/miller-rabin/-/miller-rabin-4.0.1.tgz", + "integrity": "sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==", + "dependencies": { + "bn.js": "^4.0.0", + "brorand": "^1.0.1" + }, + "bin": { + "miller-rabin": "bin/miller-rabin" + } + }, + "node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/mime-db": { + "version": "1.47.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.47.0.tgz", + "integrity": "sha512-QBmA/G2y+IfeS4oktet3qRZ+P5kPhCKRXxXnQEudYqUaEioAU1/Lq2us3D/t1Jfo4hE9REQPrbB7K5sOczJVIw==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.30", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.30.tgz", + "integrity": "sha512-crmjA4bLtR8m9qLpHvgxSChT+XoSlZi8J4n/aIdn3z92e/U47Z0V/yl+Wh9W046GgFVAmoNR/fmdbZYcSSIUeg==", + "dependencies": { + "mime-db": "1.47.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mimic-fn": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz", + "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==", + "engines": { + "node": ">=4" + } + }, + "node_modules/mimic-response": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-2.1.0.tgz", + "integrity": "sha512-wXqjST+SLt7R009ySCglWBCFpjUygmCIfD790/kVbiGmUgfYGuB14PiTd5DwVxSV4NcYHjzMkoj5LjQZwTQLEA==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/minimalistic-assert": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", + "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==" + }, + "node_modules/minimalistic-crypto-utils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", + "integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=" + }, + "node_modules/minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/minimist": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==" + }, + "node_modules/mixin-deep": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz", + "integrity": "sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==", + "dependencies": { + "for-in": "^1.0.2", + "is-extendable": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/mixin-deep/node_modules/is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "dependencies": { + "is-plain-object": "^2.0.4" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/mkdirp": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", + "deprecated": "Legacy versions of mkdirp are no longer supported. Please update to mkdirp 1.x. (Note that the API surface has changed to use Promises in 1.x.)", + "dependencies": { + "minimist": "0.0.8" + }, + "bin": { + "mkdirp": "bin/cmd.js" + } + }, + "node_modules/mkdirp-classic": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", + "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==", + "dev": true + }, + "node_modules/mkdirp/node_modules/minimist": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=" + }, + "node_modules/mocha": { + "version": "2.5.3", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-2.5.3.tgz", + "integrity": "sha1-FhvlvetJZ3HrmzV0UFC2IrWu/Fg=", + "dependencies": { + "commander": "2.3.0", + "debug": "2.2.0", + "diff": "1.4.0", + "escape-string-regexp": "1.0.2", + "glob": "3.2.11", + "growl": "1.9.2", + "jade": "0.26.3", + "mkdirp": "0.5.1", + "supports-color": "1.2.0", + "to-iso-string": "0.0.2" + }, + "bin": { + "_mocha": "bin/_mocha", + "mocha": "bin/mocha" + }, + "engines": { + "node": ">= 0.8.x" + } + }, + "node_modules/mocha/node_modules/commander": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.3.0.tgz", + "integrity": "sha1-/UMOiJgy7DU7ms0d4hfBHLPu+HM=", + "engines": { + "node": ">= 0.6.x" + } + }, + "node_modules/mocha/node_modules/debug": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz", + "integrity": "sha1-+HBX6ZWxofauaklgZkE3vFbwOdo=", + "dependencies": { + "ms": "0.7.1" + } + }, + "node_modules/mocha/node_modules/diff": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-1.4.0.tgz", + "integrity": "sha1-fyjS657nsVqX79ic5j3P2qPMur8=", + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/mocha/node_modules/escape-string-regexp": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.2.tgz", + "integrity": "sha1-Tbwv5nTnGUnK8/smlc5/LcHZqNE=", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/mocha/node_modules/glob": { + "version": "3.2.11", + "resolved": "https://registry.npmjs.org/glob/-/glob-3.2.11.tgz", + "integrity": "sha1-Spc/Y1uRkPcV0QmH1cAP0oFevj0=", + "dependencies": { + "inherits": "2", + "minimatch": "0.3" + }, + "engines": { + "node": "*" + } + }, + "node_modules/mocha/node_modules/minimatch": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-0.3.0.tgz", + "integrity": "sha1-J12O2qxPG7MyZHIInnlJyDlGmd0=", + "deprecated": "Please update to minimatch 3.0.2 or higher to avoid a RegExp DoS issue", + "dependencies": { + "lru-cache": "2", + "sigmund": "~1.0.0" + }, + "engines": { + "node": "*" + } + }, + "node_modules/mocha/node_modules/ms": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.1.tgz", + "integrity": "sha1-nNE8A62/8ltl7/3nzoZO6VIBcJg=" + }, + "node_modules/mocha/node_modules/supports-color": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-1.2.0.tgz", + "integrity": "sha1-/x7R5hFp0Gs88tWI4YixjYhH4X4=", + "bin": { + "supports-color": "cli.js" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/modern-normalize": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/modern-normalize/-/modern-normalize-1.1.0.tgz", + "integrity": "sha512-2lMlY1Yc1+CUy0gw4H95uNN7vjbpoED7NNRSBHE25nWfLBdmMzFCsPshlzbxHz+gYMcBEUN8V4pU16prcdPSgA==", + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/moment": { + "version": "2.29.1", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.1.tgz", + "integrity": "sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ==", + "engines": { + "node": "*" + } + }, + "node_modules/monotone-convex-hull-2d": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/monotone-convex-hull-2d/-/monotone-convex-hull-2d-1.0.1.tgz", + "integrity": "sha1-R/Xa6t88Sv03dkuqGqh4ekDu4Iw=", + "dependencies": { + "robust-orientation": "^1.1.3" + } + }, + "node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "node_modules/nan": { + "version": "2.14.2", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.2.tgz", + "integrity": "sha512-M2ufzIiINKCuDfBSAUr1vWQ+vuVcA9kqx8JJUsbQi6yf1uGRyb7HfpdfUr5qLXf3B/t8dPvcjhKMmlfnP47EzQ==", + "optional": true + }, + "node_modules/nanocolors": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/nanocolors/-/nanocolors-0.1.6.tgz", + "integrity": "sha512-2pvTw6vYRaBLGir2xR7MxaJtyWkrn+C53EpW8yPotG+pdAwBvt0Xwk4VJ6VHLY0aLthVZPvDfm9TdZvrvAm5UQ==", + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/nanoid": { + "version": "3.1.25", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.25.tgz", + "integrity": "sha512-rdwtIXaXCLFAQbnfqDRnI6jaRHp9fTcYBjtFKE8eezcZ7LuLjhUaQGNeMXf1HmRoCH32CLz6XwX0TtxEOS/A3Q==", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/nanomatch": { + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz", + "integrity": "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==", + "dependencies": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "fragment-cache": "^0.2.1", + "is-windows": "^1.0.2", + "kind-of": "^6.0.2", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/napi-build-utils": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-1.0.2.tgz", + "integrity": "sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg==", + "dev": true + }, + "node_modules/nice-try": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", + "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==" + }, + "node_modules/node-abi": { + "version": "2.30.0", + "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-2.30.0.tgz", + "integrity": "sha512-g6bZh3YCKQRdwuO/tSZZYJAw622SjsRfJ2X0Iy4sSOHZ34/sPPdVBn8fev2tj7njzLwuqPw9uMtGsGkO5kIQvg==", + "dev": true, + "dependencies": { + "semver": "^5.4.1" + } + }, + "node_modules/node-abi/node_modules/semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true, + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/node-addon-api": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-1.7.2.tgz", + "integrity": "sha512-ibPK3iA+vaY1eEjESkQkM0BbCqFOaZMiXRTtdB0u7b4djtY6JnsjvPdUHVMg6xQt3B8fpTTWHI9A+ADjM9frzg==" + }, + "node_modules/node-emoji": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/node-emoji/-/node-emoji-1.11.0.tgz", + "integrity": "sha512-wo2DpQkQp7Sjm2A0cq+sN7EHKO6Sl0ctXeBdFZrL9T9+UywORbufTcTZxom8YqpLQt/FqNMUkOpkZrJVYSKD3A==", + "dependencies": { + "lodash": "^4.17.21" + } + }, + "node_modules/node-forge": { + "version": "0.7.6", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.7.6.tgz", + "integrity": "sha512-sol30LUpz1jQFBjOKwbjxijiE3b6pjd74YwfD0fJOKPjF+fONKb2Yg8rYgS6+bK6VDl+/wfr4IYpC7jDzLUIfw==", + "engines": { + "node": "*" + } + }, + "node_modules/node-libs-browser": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/node-libs-browser/-/node-libs-browser-2.2.1.tgz", + "integrity": "sha512-h/zcD8H9kaDZ9ALUWwlBUDo6TKF8a7qBSCSEGfjTVIYeqsioSKaAX+BN7NgiMGp6iSIXZ3PxgCu8KS3b71YK5Q==", + "dependencies": { + "assert": "^1.1.1", + "browserify-zlib": "^0.2.0", + "buffer": "^4.3.0", + "console-browserify": "^1.1.0", + "constants-browserify": "^1.0.0", + "crypto-browserify": "^3.11.0", + "domain-browser": "^1.1.1", + "events": "^3.0.0", + "https-browserify": "^1.0.0", + "os-browserify": "^0.3.0", + "path-browserify": "0.0.1", + "process": "^0.11.10", + "punycode": "^1.2.4", + "querystring-es3": "^0.2.0", + "readable-stream": "^2.3.3", + "stream-browserify": "^2.0.1", + "stream-http": "^2.7.2", + "string_decoder": "^1.0.0", + "timers-browserify": "^2.0.4", + "tty-browserify": "0.0.0", + "url": "^0.11.0", + "util": "^0.11.0", + "vm-browserify": "^1.0.1" + } + }, + "node_modules/node-libs-browser/node_modules/assert": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/assert/-/assert-1.5.0.tgz", + "integrity": "sha512-EDsgawzwoun2CZkCgtxJbv392v4nbk9XDD06zI+kQYoBM/3RBWLlEyJARDOmhAAosBjWACEkKL6S+lIZtcAubA==", + "dependencies": { + "object-assign": "^4.1.1", + "util": "0.10.3" + } + }, + "node_modules/node-libs-browser/node_modules/assert/node_modules/util": { + "version": "0.10.3", + "resolved": "https://registry.npmjs.org/util/-/util-0.10.3.tgz", + "integrity": "sha1-evsa/lCAUkZInj23/g7TeTNqwPk=", + "dependencies": { + "inherits": "2.0.1" + } + }, + "node_modules/node-libs-browser/node_modules/inherits": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz", + "integrity": "sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE=" + }, + "node_modules/node-libs-browser/node_modules/punycode": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=" + }, + "node_modules/node-releases": { + "version": "1.1.76", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.76.tgz", + "integrity": "sha512-9/IECtNr8dXNmPWmFXepT0/7o5eolGesHUa3mtr0KlgnCvnZxwh2qensKL42JJY2vQKC3nIBXetFAqR+PW1CmA==" + }, + "node_modules/normalize-package-data": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "dependencies": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + } + }, + "node_modules/normalize-package-data/node_modules/semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/normalize-range": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", + "integrity": "sha1-LRDAa9/TEuqXd2laTShDlFa3WUI=", + "peer": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/normalize-url": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-3.3.0.tgz", + "integrity": "sha512-U+JJi7duF1o+u2pynbp2zXDW2/PADgC30f0GsHZtRh+HOcXHnw137TrNlyxxRvWW5fjKd3bcLHPxofWuCjaeZg==", + "engines": { + "node": ">=6" + } + }, + "node_modules/npm-run-all": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/npm-run-all/-/npm-run-all-4.1.5.tgz", + "integrity": "sha512-Oo82gJDAVcaMdi3nuoKFavkIHBRVqQ1qvMb+9LHk/cF4P6B2m8aP04hGf7oL6wZ9BuGwX1onlLhpuoofSyoQDQ==", + "dependencies": { + "ansi-styles": "^3.2.1", + "chalk": "^2.4.1", + "cross-spawn": "^6.0.5", + "memorystream": "^0.3.1", + "minimatch": "^3.0.4", + "pidtree": "^0.3.0", + "read-pkg": "^3.0.0", + "shell-quote": "^1.6.1", + "string.prototype.padend": "^3.0.0" + }, + "bin": { + "npm-run-all": "bin/npm-run-all/index.js", + "run-p": "bin/run-p/index.js", + "run-s": "bin/run-s/index.js" + }, + "engines": { + "node": ">= 4" + } + }, + "node_modules/npm-run-all/node_modules/load-json-file": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", + "integrity": "sha1-L19Fq5HjMhYjT9U62rZo607AmTs=", + "dependencies": { + "graceful-fs": "^4.1.2", + "parse-json": "^4.0.0", + "pify": "^3.0.0", + "strip-bom": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/npm-run-all/node_modules/path-type": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", + "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", + "dependencies": { + "pify": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/npm-run-all/node_modules/pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", + "engines": { + "node": ">=4" + } + }, + "node_modules/npm-run-all/node_modules/read-pkg": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", + "integrity": "sha1-nLxoaXj+5l0WwA4rGcI3/Pbjg4k=", + "dependencies": { + "load-json-file": "^4.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/npm-run-all/node_modules/strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", + "engines": { + "node": ">=4" + } + }, + "node_modules/npmlog": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz", + "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==", + "dev": true, + "dependencies": { + "are-we-there-yet": "~1.1.2", + "console-control-strings": "~1.1.0", + "gauge": "~2.7.3", + "set-blocking": "~2.0.0" + } + }, + "node_modules/nth-check": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-1.0.2.tgz", + "integrity": "sha512-WeBOdju8SnzPN5vTUJYxYUxLeXpCaVP5i5e0LF8fg7WORF2Wd7wFX/pk0tYZk7s8T+J7VLy0Da6J1+wCT0AtHg==", + "dependencies": { + "boolbase": "~1.0.0" + } + }, + "node_modules/number-is-nan": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", + "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/nwsapi": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.0.tgz", + "integrity": "sha512-h2AatdwYH+JHiZpv7pt/gSX1XoRGb7L/qSIeuqA6GwYoF9w1vP1cw42TO0aI2pNyshRK5893hNSl+1//vHK7hQ==" + }, + "node_modules/oauth-sign": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", + "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==", + "engines": { + "node": "*" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-copy": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz", + "integrity": "sha1-fn2Fi3gb18mRpBupde04EnVOmYw=", + "dependencies": { + "copy-descriptor": "^0.1.0", + "define-property": "^0.2.5", + "kind-of": "^3.0.3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-copy/node_modules/define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dependencies": { + "is-descriptor": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-copy/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-hash": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-2.2.0.tgz", + "integrity": "sha512-gScRMn0bS5fH+IuwyIFgnh9zBdo4DV+6GhygmWM9HyNJSgS0hScp1f5vjtm7oIIOiT9trXrShAkLFSc2IqKNgw==", + "engines": { + "node": ">= 6" + } + }, + "node_modules/object-inspect": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.4.1.tgz", + "integrity": "sha512-wqdhLpfCUbEsoEwl3FXwGyv8ief1k/1aUdIPCqVnupM6e8l63BEJdiF/0swtn04/8p05tG/T0FrpTlfwvljOdw==" + }, + "node_modules/object-is": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.5.tgz", + "integrity": "sha512-3cyDsyHgtmi7I7DfSSI2LDp6SK2lwvtbg0p0R1e0RvTqF5ceGx+K2dfSjm1bKDMVCFEDAQvy+o8c6a7VujOddw==", + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object-visit": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz", + "integrity": "sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=", + "dependencies": { + "isobject": "^3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object.assign": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz", + "integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==", + "dependencies": { + "call-bind": "^1.0.0", + "define-properties": "^1.1.3", + "has-symbols": "^1.0.1", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object.getownpropertydescriptors": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.2.tgz", + "integrity": "sha512-WtxeKSzfBjlzL+F9b7M7hewDzMwy+C8NRssHd1YrNlzHzIDrXcXiNOMrezdAEM4UXixgV+vvnyBeN7Rygl2ttQ==", + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.18.0-next.2" + }, + "engines": { + "node": ">= 0.8" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object.pick": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", + "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=", + "dependencies": { + "isobject": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object.values": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.3.tgz", + "integrity": "sha512-nkF6PfDB9alkOUxpf1HNm/QlkeW3SReqL5WXeBLpEJJnlPSvRaDQpW3gQTksTN3fgJX4hL42RzKyOin6ff3tyw==", + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.18.0-next.2", + "has": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/ohauth": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/ohauth/-/ohauth-1.0.0.tgz", + "integrity": "sha1-wui/877wrkkUR5IiQ+G6cFEX3ks=", + "dependencies": { + "jshashes": "~1.0.0", + "xtend": "~4.0.0" + } + }, + "node_modules/on-finished": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", + "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/onetime": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz", + "integrity": "sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ=", + "dependencies": { + "mimic-fn": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/opening_hours": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/opening_hours/-/opening_hours-3.6.0.tgz", + "integrity": "sha512-ETHEchqvpZxJiLznNSdYHiGyeIMikJVfYEjMjYe0oRAxcQejilyXWWGjJBcIOXLwgU6LaATeFb6LRTgEguz0yw==", + "dependencies": { + "i18next": "^20.2.1", + "i18next-browser-languagedetector": "^6.1.0", + "suncalc": "^1.8.0" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/opn": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/opn/-/opn-5.5.0.tgz", + "integrity": "sha512-PqHpggC9bLV0VeWcdKhkpxY+3JTzetLSqTCWL/z/tFIbI6G8JCjondXklT1JinczLz2Xib62sSp0T/gKT4KksA==", + "dependencies": { + "is-wsl": "^1.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/optimist": { + "version": "0.3.7", + "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.3.7.tgz", + "integrity": "sha1-yQlBrVnkJzMokjB00s8ufLxuwNk=", + "dependencies": { + "wordwrap": "~0.0.2" + } + }, + "node_modules/optionator": { + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", + "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==", + "dependencies": { + "deep-is": "~0.1.3", + "fast-levenshtein": "~2.0.6", + "levn": "~0.3.0", + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2", + "word-wrap": "~1.2.3" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/ora": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/ora/-/ora-2.1.0.tgz", + "integrity": "sha512-hNNlAd3gfv/iPmsNxYoAPLvxg7HuPozww7fFonMZvL84tP6Ox5igfk5j/+a9rtJJwqMgKK+JgWsAQik5o0HTLA==", + "dependencies": { + "chalk": "^2.3.1", + "cli-cursor": "^2.1.0", + "cli-spinners": "^1.1.0", + "log-symbols": "^2.2.0", + "strip-ansi": "^4.0.0", + "wcwidth": "^1.0.1" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/os-browserify": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/os-browserify/-/os-browserify-0.3.0.tgz", + "integrity": "sha1-hUNzx/XCMVkU/Jv8a9gjj92h7Cc=" + }, + "node_modules/osm-auth": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/osm-auth/-/osm-auth-1.1.0.tgz", + "integrity": "sha512-e/ecarlh2N/FMfiNa/ZChUIZN8q1LqK4F0iPV4g1MmfHi3/VJ+STJ0qE7ALL3OgHR5IQc3JuI63SCDqHNGt4ew==", + "dependencies": { + "ohauth": "~1.0.0", + "resolve-url": "~0.2.1", + "store": "~2.0.4", + "xtend": "~4.0.0" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/osm-polygon-features": { + "version": "0.9.2", + "resolved": "https://registry.npmjs.org/osm-polygon-features/-/osm-polygon-features-0.9.2.tgz", + "integrity": "sha1-IK5BEwxIbkmjsqPCtYoUGcSYZ3g=" + }, + "node_modules/osmtogeojson": { + "version": "3.0.0-beta.4", + "resolved": "https://registry.npmjs.org/osmtogeojson/-/osmtogeojson-3.0.0-beta.4.tgz", + "integrity": "sha512-GwNy2w5JKOplOBspagcNhCDhBRV6Du2BCvcLkaA7nX12U86Dl2Ciw9zs/VzFFTXfyZlaK+7bGCWN2SNlfn/jOA==", + "dependencies": { + "@mapbox/geojson-rewind": "0.4.0", + "concat-stream": "2.0.0", + "geojson-numeric": "0.2.1", + "htmlparser2": "3.5.1", + "JSONStream": "0.8.0", + "optimist": "~0.3.5", + "osm-polygon-features": "^0.9.1", + "tiny-osmpbf": "^0.1.0", + "xmldom": "~0.1.16" + }, + "bin": { + "osmtogeojson": "osmtogeojson" + }, + "engines": { + "node": ">=0.5" + }, + "optionalDependencies": { + "@types/geojson": "^1.0.2" + } + }, + "node_modules/osmtogeojson/node_modules/@types/geojson": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-1.0.6.tgz", + "integrity": "sha512-Xqg/lIZMrUd0VRmSRbCAewtwGZiAk3mEUDvV4op1tGl+LvyPcb/MIOSxTl9z+9+J+R4/vpjiCAT4xeKzH9ji1w==", + "optional": true + }, + "node_modules/pako": { + "version": "0.2.9", + "resolved": "https://registry.npmjs.org/pako/-/pako-0.2.9.tgz", + "integrity": "sha1-8/dSL073gjSNqBYbrZ7P1Rv4OnU=" + }, + "node_modules/parcel": { + "version": "1.12.4", + "resolved": "https://registry.npmjs.org/parcel/-/parcel-1.12.4.tgz", + "integrity": "sha512-qfc74e2/R4pCoU6L/ZZnK9k3iDS6ir4uHea0e9th9w52eehcAGf2ido/iABq9PBXdsIOe4NSY3oUm7Khe7+S3w==", + "hasInstallScript": true, + "dependencies": { + "@babel/code-frame": "^7.0.0", + "@babel/core": "^7.4.4", + "@babel/generator": "^7.4.4", + "@babel/parser": "^7.4.4", + "@babel/plugin-transform-flow-strip-types": "^7.4.4", + "@babel/plugin-transform-modules-commonjs": "^7.4.4", + "@babel/plugin-transform-react-jsx": "^7.0.0", + "@babel/preset-env": "^7.4.4", + "@babel/runtime": "^7.4.4", + "@babel/template": "^7.4.4", + "@babel/traverse": "^7.4.4", + "@babel/types": "^7.4.4", + "@iarna/toml": "^2.2.0", + "@parcel/fs": "^1.11.0", + "@parcel/logger": "^1.11.1", + "@parcel/utils": "^1.11.0", + "@parcel/watcher": "^1.12.1", + "@parcel/workers": "^1.11.0", + "ansi-to-html": "^0.6.4", + "babylon-walk": "^1.0.2", + "browserslist": "^4.1.0", + "chalk": "^2.1.0", + "clone": "^2.1.1", + "command-exists": "^1.2.6", + "commander": "^2.11.0", + "core-js": "^2.6.5", + "cross-spawn": "^6.0.4", + "css-modules-loader-core": "^1.1.0", + "cssnano": "^4.0.0", + "deasync": "^0.1.14", + "dotenv": "^5.0.0", + "dotenv-expand": "^5.1.0", + "envinfo": "^7.3.1", + "fast-glob": "^2.2.2", + "filesize": "^3.6.0", + "get-port": "^3.2.0", + "htmlnano": "^0.2.2", + "is-glob": "^4.0.0", + "is-url": "^1.2.2", + "js-yaml": "^3.10.0", + "json5": "^1.0.1", + "micromatch": "^3.0.4", + "mkdirp": "^0.5.1", + "node-forge": "^0.7.1", + "node-libs-browser": "^2.0.0", + "opn": "^5.1.0", + "postcss": "^7.0.11", + "postcss-value-parser": "^3.3.1", + "posthtml": "^0.11.2", + "posthtml-parser": "^0.4.0", + "posthtml-render": "^1.1.3", + "resolve": "^1.4.0", + "semver": "^5.4.1", + "serialize-to-js": "^3.0.0", + "serve-static": "^1.12.4", + "source-map": "0.6.1", + "terser": "^3.7.3", + "v8-compile-cache": "^2.0.0", + "ws": "^5.1.1" + }, + "bin": { + "parcel": "bin/cli.js" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/parcel/node_modules/@nodelib/fs.stat": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-1.1.3.tgz", + "integrity": "sha512-shAmDyaQC4H92APFoIaVDHCx5bStIocgvbwQyxPRrbUY20V1EYTbSDchWbuwlMG3V17cprZhA6+78JfB+3DTPw==", + "engines": { + "node": ">= 6" + } + }, + "node_modules/parcel/node_modules/braces": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", + "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", + "dependencies": { + "arr-flatten": "^1.1.0", + "array-unique": "^0.3.2", + "extend-shallow": "^2.0.1", + "fill-range": "^4.0.0", + "isobject": "^3.0.1", + "repeat-element": "^1.1.2", + "snapdragon": "^0.8.1", + "snapdragon-node": "^2.0.1", + "split-string": "^3.0.2", + "to-regex": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/parcel/node_modules/braces/node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/parcel/node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==" + }, + "node_modules/parcel/node_modules/fast-glob": { + "version": "2.2.7", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-2.2.7.tgz", + "integrity": "sha512-g1KuQwHOZAmOZMuBtHdxDtju+T2RT8jgCC9aANsbpdiDDTSnjgfuVsIBNKbUeJI3oKMRExcfNDtJl4OhbffMsw==", + "dependencies": { + "@mrmlnc/readdir-enhanced": "^2.2.1", + "@nodelib/fs.stat": "^1.1.2", + "glob-parent": "^3.1.0", + "is-glob": "^4.0.0", + "merge2": "^1.2.3", + "micromatch": "^3.1.10" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/parcel/node_modules/fill-range": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", + "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", + "dependencies": { + "extend-shallow": "^2.0.1", + "is-number": "^3.0.0", + "repeat-string": "^1.6.1", + "to-regex-range": "^2.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/parcel/node_modules/fill-range/node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/parcel/node_modules/glob-parent": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", + "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=", + "dependencies": { + "is-glob": "^3.1.0", + "path-dirname": "^1.0.0" + } + }, + "node_modules/parcel/node_modules/glob-parent/node_modules/is-glob": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", + "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", + "dependencies": { + "is-extglob": "^2.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/parcel/node_modules/is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/parcel/node_modules/is-number/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/parcel/node_modules/micromatch": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", + "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", + "dependencies": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "braces": "^2.3.1", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "extglob": "^2.0.4", + "fragment-cache": "^0.2.1", + "kind-of": "^6.0.2", + "nanomatch": "^1.2.9", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/parcel/node_modules/postcss": { + "version": "7.0.36", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.36.tgz", + "integrity": "sha512-BebJSIUMwJHRH0HAQoxN4u1CN86glsrwsW0q7T+/m44eXOUAxSNdHRkNZPYz5vVUbg17hFgOQDE7fZk7li3pZw==", + "dependencies": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, + "node_modules/parcel/node_modules/postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==" + }, + "node_modules/parcel/node_modules/semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/parcel/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/parcel/node_modules/supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/parcel/node_modules/to-regex-range": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", + "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", + "dependencies": { + "is-number": "^3.0.0", + "repeat-string": "^1.6.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/parent-module/node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "engines": { + "node": ">=6" + } + }, + "node_modules/parse-asn1": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.6.tgz", + "integrity": "sha512-RnZRo1EPU6JBnra2vGHj0yhp6ebyjBZpmUCLHWiFhxlzvBCCpAuZ7elsBp1PVAbQN0/04VD/19rfzlBSwLstMw==", + "dependencies": { + "asn1.js": "^5.2.0", + "browserify-aes": "^1.0.0", + "evp_bytestokey": "^1.0.0", + "pbkdf2": "^3.0.3", + "safe-buffer": "^5.1.1" + } + }, + "node_modules/parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=", + "dependencies": { + "error-ex": "^1.3.1", + "json-parse-better-errors": "^1.0.1" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/parse5": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-5.1.0.tgz", + "integrity": "sha512-fxNG2sQjHvlVAYmzBZS9YlDp6PTSSDwa98vkD4QgVDDCAo84z5X1t5XyJQ62ImdLXx5NdIIfihey6xpum9/gRQ==" + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/pascalcase": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz", + "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-browserify": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-0.0.1.tgz", + "integrity": "sha512-BapA40NHICOS+USX9SN4tyhq+A2RrN/Ws5F0Z5aMHDp98Fl86lX8Oti8B7uN93L4Ifv4fHOEA+pQw87gmMO/lQ==" + }, + "node_modules/path-dirname": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz", + "integrity": "sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA=" + }, + "node_modules/path-exists": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz", + "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=", + "dev": true, + "dependencies": { + "pinkie-promise": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-key": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", + "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", + "engines": { + "node": ">=4" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" + }, + "node_modules/path-type": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz", + "integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=", + "dev": true, + "dependencies": { + "graceful-fs": "^4.1.2", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pbf": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/pbf/-/pbf-3.2.1.tgz", + "integrity": "sha512-ClrV7pNOn7rtmoQVF4TS1vyU0WhYRnP92fzbfF75jAIwpnzdJXf8iTd4CMEqO4yUenH6NDqLiwjqlh6QgZzgLQ==", + "dependencies": { + "ieee754": "^1.1.12", + "resolve-protobuf-schema": "^2.1.0" + }, + "bin": { + "pbf": "bin/pbf" + } + }, + "node_modules/pbkdf2": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.1.2.tgz", + "integrity": "sha512-iuh7L6jA7JEGu2WxDwtQP1ddOpaJNC4KlDEFfdQajSGgGPNi4OyDc2R7QnbY2bR9QjBVGwgvTdNJZoE7RaxUMA==", + "dependencies": { + "create-hash": "^1.1.2", + "create-hmac": "^1.1.4", + "ripemd160": "^2.0.1", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + }, + "engines": { + "node": ">=0.12" + } + }, + "node_modules/performance-now": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", + "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=" + }, + "node_modules/physical-cpu-count": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/physical-cpu-count/-/physical-cpu-count-2.0.0.tgz", + "integrity": "sha1-GN4vl+S/epVRrXURlCtUlverpmA=" + }, + "node_modules/picomatch": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.0.tgz", + "integrity": "sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pidtree": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/pidtree/-/pidtree-0.3.1.tgz", + "integrity": "sha512-qQbW94hLHEqCg7nhby4yRC7G2+jYHY4Rguc2bjw7Uug4GIJuu1tvf2uHaZv5Q8zdt+WKJ6qK1FOI6amaWUo5FA==", + "bin": { + "pidtree": "bin/pidtree.js" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pinkie": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", + "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pinkie-promise": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", + "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", + "dev": true, + "dependencies": { + "pinkie": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pn": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/pn/-/pn-1.1.0.tgz", + "integrity": "sha512-2qHaIQr2VLRFoxe2nASzsV6ef4yOOH+Fi9FBOVH6cqeSgUnoyySPZkxzLuzd+RYOQTRpROA0ztTMqxROKSb/nA==" + }, + "node_modules/point-in-polygon": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/point-in-polygon/-/point-in-polygon-1.1.0.tgz", + "integrity": "sha512-3ojrFwjnnw8Q9242TzgXuTD+eKiutbzyslcq1ydfu82Db2y+Ogbmyrkpv0Hgj31qwT3lbS9+QAAO/pIQM35XRw==" + }, + "node_modules/posix-character-classes": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", + "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss": { + "version": "8.3.7", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.3.7.tgz", + "integrity": "sha512-9SaY7nnyQ63/WittqZYAvkkYPyKxchMKH71UDzeTmWuLSvxTRpeEeABZAzlCi55cuGcoFyoV/amX2BdsafQidQ==", + "dependencies": { + "nanocolors": "^0.1.5", + "nanoid": "^3.1.25", + "source-map-js": "^0.6.2" + }, + "engines": { + "node": "^10 || ^12 || >=14" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, + "node_modules/postcss-calc": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/postcss-calc/-/postcss-calc-7.0.5.tgz", + "integrity": "sha512-1tKHutbGtLtEZF6PT4JSihCHfIVldU72mZ8SdZHIYriIZ9fh9k9aWSppaT8rHsyI3dX+KSR+W+Ix9BMY3AODrg==", + "dependencies": { + "postcss": "^7.0.27", + "postcss-selector-parser": "^6.0.2", + "postcss-value-parser": "^4.0.2" + } + }, + "node_modules/postcss-calc/node_modules/postcss": { + "version": "7.0.36", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.36.tgz", + "integrity": "sha512-BebJSIUMwJHRH0HAQoxN4u1CN86glsrwsW0q7T+/m44eXOUAxSNdHRkNZPYz5vVUbg17hFgOQDE7fZk7li3pZw==", + "dependencies": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, + "node_modules/postcss-calc/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-calc/node_modules/supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/postcss-colormin": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/postcss-colormin/-/postcss-colormin-4.0.3.tgz", + "integrity": "sha512-WyQFAdDZpExQh32j0U0feWisZ0dmOtPl44qYmJKkq9xFWY3p+4qnRzCHeNrkeRhwPHz9bQ3mo0/yVkaply0MNw==", + "dependencies": { + "browserslist": "^4.0.0", + "color": "^3.0.0", + "has": "^1.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/postcss-colormin/node_modules/postcss": { + "version": "7.0.36", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.36.tgz", + "integrity": "sha512-BebJSIUMwJHRH0HAQoxN4u1CN86glsrwsW0q7T+/m44eXOUAxSNdHRkNZPYz5vVUbg17hFgOQDE7fZk7li3pZw==", + "dependencies": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, + "node_modules/postcss-colormin/node_modules/postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==" + }, + "node_modules/postcss-colormin/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-colormin/node_modules/supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/postcss-convert-values": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-convert-values/-/postcss-convert-values-4.0.1.tgz", + "integrity": "sha512-Kisdo1y77KUC0Jmn0OXU/COOJbzM8cImvw1ZFsBgBgMgb1iL23Zs/LXRe3r+EZqM3vGYKdQ2YJVQ5VkJI+zEJQ==", + "dependencies": { + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/postcss-convert-values/node_modules/postcss": { + "version": "7.0.36", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.36.tgz", + "integrity": "sha512-BebJSIUMwJHRH0HAQoxN4u1CN86glsrwsW0q7T+/m44eXOUAxSNdHRkNZPYz5vVUbg17hFgOQDE7fZk7li3pZw==", + "dependencies": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, + "node_modules/postcss-convert-values/node_modules/postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==" + }, + "node_modules/postcss-convert-values/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-convert-values/node_modules/supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/postcss-discard-comments": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-4.0.2.tgz", + "integrity": "sha512-RJutN259iuRf3IW7GZyLM5Sw4GLTOH8FmsXBnv8Ab/Tc2k4SR4qbV4DNbyyY4+Sjo362SyDmW2DQ7lBSChrpkg==", + "dependencies": { + "postcss": "^7.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/postcss-discard-comments/node_modules/postcss": { + "version": "7.0.36", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.36.tgz", + "integrity": "sha512-BebJSIUMwJHRH0HAQoxN4u1CN86glsrwsW0q7T+/m44eXOUAxSNdHRkNZPYz5vVUbg17hFgOQDE7fZk7li3pZw==", + "dependencies": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, + "node_modules/postcss-discard-comments/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-discard-comments/node_modules/supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/postcss-discard-duplicates": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-discard-duplicates/-/postcss-discard-duplicates-4.0.2.tgz", + "integrity": "sha512-ZNQfR1gPNAiXZhgENFfEglF93pciw0WxMkJeVmw8eF+JZBbMD7jp6C67GqJAXVZP2BWbOztKfbsdmMp/k8c6oQ==", + "dependencies": { + "postcss": "^7.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/postcss-discard-duplicates/node_modules/postcss": { + "version": "7.0.36", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.36.tgz", + "integrity": "sha512-BebJSIUMwJHRH0HAQoxN4u1CN86glsrwsW0q7T+/m44eXOUAxSNdHRkNZPYz5vVUbg17hFgOQDE7fZk7li3pZw==", + "dependencies": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, + "node_modules/postcss-discard-duplicates/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-discard-duplicates/node_modules/supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/postcss-discard-empty": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-discard-empty/-/postcss-discard-empty-4.0.1.tgz", + "integrity": "sha512-B9miTzbznhDjTfjvipfHoqbWKwd0Mj+/fL5s1QOz06wufguil+Xheo4XpOnc4NqKYBCNqqEzgPv2aPBIJLox0w==", + "dependencies": { + "postcss": "^7.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/postcss-discard-empty/node_modules/postcss": { + "version": "7.0.36", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.36.tgz", + "integrity": "sha512-BebJSIUMwJHRH0HAQoxN4u1CN86glsrwsW0q7T+/m44eXOUAxSNdHRkNZPYz5vVUbg17hFgOQDE7fZk7li3pZw==", + "dependencies": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, + "node_modules/postcss-discard-empty/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-discard-empty/node_modules/supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/postcss-discard-overridden": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-discard-overridden/-/postcss-discard-overridden-4.0.1.tgz", + "integrity": "sha512-IYY2bEDD7g1XM1IDEsUT4//iEYCxAmP5oDSFMVU/JVvT7gh+l4fmjciLqGgwjdWpQIdb0Che2VX00QObS5+cTg==", + "dependencies": { + "postcss": "^7.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/postcss-discard-overridden/node_modules/postcss": { + "version": "7.0.36", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.36.tgz", + "integrity": "sha512-BebJSIUMwJHRH0HAQoxN4u1CN86glsrwsW0q7T+/m44eXOUAxSNdHRkNZPYz5vVUbg17hFgOQDE7fZk7li3pZw==", + "dependencies": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, + "node_modules/postcss-discard-overridden/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-discard-overridden/node_modules/supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/postcss-merge-longhand": { + "version": "4.0.11", + "resolved": "https://registry.npmjs.org/postcss-merge-longhand/-/postcss-merge-longhand-4.0.11.tgz", + "integrity": "sha512-alx/zmoeXvJjp7L4mxEMjh8lxVlDFX1gqWHzaaQewwMZiVhLo42TEClKaeHbRf6J7j82ZOdTJ808RtN0ZOZwvw==", + "dependencies": { + "css-color-names": "0.0.4", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0", + "stylehacks": "^4.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/postcss-merge-longhand/node_modules/postcss": { + "version": "7.0.36", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.36.tgz", + "integrity": "sha512-BebJSIUMwJHRH0HAQoxN4u1CN86glsrwsW0q7T+/m44eXOUAxSNdHRkNZPYz5vVUbg17hFgOQDE7fZk7li3pZw==", + "dependencies": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, + "node_modules/postcss-merge-longhand/node_modules/postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==" + }, + "node_modules/postcss-merge-longhand/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-merge-longhand/node_modules/supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/postcss-merge-rules": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/postcss-merge-rules/-/postcss-merge-rules-4.0.3.tgz", + "integrity": "sha512-U7e3r1SbvYzO0Jr3UT/zKBVgYYyhAz0aitvGIYOYK5CPmkNih+WDSsS5tvPrJ8YMQYlEMvsZIiqmn7HdFUaeEQ==", + "dependencies": { + "browserslist": "^4.0.0", + "caniuse-api": "^3.0.0", + "cssnano-util-same-parent": "^4.0.0", + "postcss": "^7.0.0", + "postcss-selector-parser": "^3.0.0", + "vendors": "^1.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/postcss-merge-rules/node_modules/postcss": { + "version": "7.0.36", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.36.tgz", + "integrity": "sha512-BebJSIUMwJHRH0HAQoxN4u1CN86glsrwsW0q7T+/m44eXOUAxSNdHRkNZPYz5vVUbg17hFgOQDE7fZk7li3pZw==", + "dependencies": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, + "node_modules/postcss-merge-rules/node_modules/postcss-selector-parser": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-3.1.2.tgz", + "integrity": "sha512-h7fJ/5uWuRVyOtkO45pnt1Ih40CEleeyCHzipqAZO2e5H20g25Y48uYnFUiShvY4rZWNJ/Bib/KVPmanaCtOhA==", + "dependencies": { + "dot-prop": "^5.2.0", + "indexes-of": "^1.0.1", + "uniq": "^1.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/postcss-merge-rules/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-merge-rules/node_modules/supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/postcss-minify-font-values": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-minify-font-values/-/postcss-minify-font-values-4.0.2.tgz", + "integrity": "sha512-j85oO6OnRU9zPf04+PZv1LYIYOprWm6IA6zkXkrJXyRveDEuQggG6tvoy8ir8ZwjLxLuGfNkCZEQG7zan+Hbtg==", + "dependencies": { + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/postcss-minify-font-values/node_modules/postcss": { + "version": "7.0.36", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.36.tgz", + "integrity": "sha512-BebJSIUMwJHRH0HAQoxN4u1CN86glsrwsW0q7T+/m44eXOUAxSNdHRkNZPYz5vVUbg17hFgOQDE7fZk7li3pZw==", + "dependencies": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, + "node_modules/postcss-minify-font-values/node_modules/postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==" + }, + "node_modules/postcss-minify-font-values/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-minify-font-values/node_modules/supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/postcss-minify-gradients": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-minify-gradients/-/postcss-minify-gradients-4.0.2.tgz", + "integrity": "sha512-qKPfwlONdcf/AndP1U8SJ/uzIJtowHlMaSioKzebAXSG4iJthlWC9iSWznQcX4f66gIWX44RSA841HTHj3wK+Q==", + "dependencies": { + "cssnano-util-get-arguments": "^4.0.0", + "is-color-stop": "^1.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/postcss-minify-gradients/node_modules/postcss": { + "version": "7.0.36", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.36.tgz", + "integrity": "sha512-BebJSIUMwJHRH0HAQoxN4u1CN86glsrwsW0q7T+/m44eXOUAxSNdHRkNZPYz5vVUbg17hFgOQDE7fZk7li3pZw==", + "dependencies": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, + "node_modules/postcss-minify-gradients/node_modules/postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==" + }, + "node_modules/postcss-minify-gradients/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-minify-gradients/node_modules/supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/postcss-minify-params": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-minify-params/-/postcss-minify-params-4.0.2.tgz", + "integrity": "sha512-G7eWyzEx0xL4/wiBBJxJOz48zAKV2WG3iZOqVhPet/9geefm/Px5uo1fzlHu+DOjT+m0Mmiz3jkQzVHe6wxAWg==", + "dependencies": { + "alphanum-sort": "^1.0.0", + "browserslist": "^4.0.0", + "cssnano-util-get-arguments": "^4.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0", + "uniqs": "^2.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/postcss-minify-params/node_modules/postcss": { + "version": "7.0.36", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.36.tgz", + "integrity": "sha512-BebJSIUMwJHRH0HAQoxN4u1CN86glsrwsW0q7T+/m44eXOUAxSNdHRkNZPYz5vVUbg17hFgOQDE7fZk7li3pZw==", + "dependencies": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, + "node_modules/postcss-minify-params/node_modules/postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==" + }, + "node_modules/postcss-minify-params/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-minify-params/node_modules/supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/postcss-minify-selectors": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-minify-selectors/-/postcss-minify-selectors-4.0.2.tgz", + "integrity": "sha512-D5S1iViljXBj9kflQo4YutWnJmwm8VvIsU1GeXJGiG9j8CIg9zs4voPMdQDUmIxetUOh60VilsNzCiAFTOqu3g==", + "dependencies": { + "alphanum-sort": "^1.0.0", + "has": "^1.0.0", + "postcss": "^7.0.0", + "postcss-selector-parser": "^3.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/postcss-minify-selectors/node_modules/postcss": { + "version": "7.0.36", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.36.tgz", + "integrity": "sha512-BebJSIUMwJHRH0HAQoxN4u1CN86glsrwsW0q7T+/m44eXOUAxSNdHRkNZPYz5vVUbg17hFgOQDE7fZk7li3pZw==", + "dependencies": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, + "node_modules/postcss-minify-selectors/node_modules/postcss-selector-parser": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-3.1.2.tgz", + "integrity": "sha512-h7fJ/5uWuRVyOtkO45pnt1Ih40CEleeyCHzipqAZO2e5H20g25Y48uYnFUiShvY4rZWNJ/Bib/KVPmanaCtOhA==", + "dependencies": { + "dot-prop": "^5.2.0", + "indexes-of": "^1.0.1", + "uniq": "^1.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/postcss-minify-selectors/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-minify-selectors/node_modules/supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/postcss-modules-extract-imports": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-1.1.0.tgz", + "integrity": "sha1-thTJcgvmgW6u41+zpfqh26agXds=", + "dependencies": { + "postcss": "^6.0.1" + } + }, + "node_modules/postcss-modules-extract-imports/node_modules/postcss": { + "version": "6.0.23", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.23.tgz", + "integrity": "sha512-soOk1h6J3VMTZtVeVpv15/Hpdl2cBLX3CAw4TAbkpTJiNPk9YP/zWcD1ND+xEtvyuuvKzbxliTOIyvkSeSJ6ag==", + "dependencies": { + "chalk": "^2.4.1", + "source-map": "^0.6.1", + "supports-color": "^5.4.0" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/postcss-modules-extract-imports/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-modules-local-by-default": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-1.2.0.tgz", + "integrity": "sha1-99gMOYxaOT+nlkRmvRlQCn1hwGk=", + "dependencies": { + "css-selector-tokenizer": "^0.7.0", + "postcss": "^6.0.1" + } + }, + "node_modules/postcss-modules-local-by-default/node_modules/postcss": { + "version": "6.0.23", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.23.tgz", + "integrity": "sha512-soOk1h6J3VMTZtVeVpv15/Hpdl2cBLX3CAw4TAbkpTJiNPk9YP/zWcD1ND+xEtvyuuvKzbxliTOIyvkSeSJ6ag==", + "dependencies": { + "chalk": "^2.4.1", + "source-map": "^0.6.1", + "supports-color": "^5.4.0" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/postcss-modules-local-by-default/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-modules-scope": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-1.1.0.tgz", + "integrity": "sha1-1upkmUx5+XtipytCb75gVqGUu5A=", + "dependencies": { + "css-selector-tokenizer": "^0.7.0", + "postcss": "^6.0.1" + } + }, + "node_modules/postcss-modules-scope/node_modules/postcss": { + "version": "6.0.23", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.23.tgz", + "integrity": "sha512-soOk1h6J3VMTZtVeVpv15/Hpdl2cBLX3CAw4TAbkpTJiNPk9YP/zWcD1ND+xEtvyuuvKzbxliTOIyvkSeSJ6ag==", + "dependencies": { + "chalk": "^2.4.1", + "source-map": "^0.6.1", + "supports-color": "^5.4.0" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/postcss-modules-scope/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-modules-values": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-1.3.0.tgz", + "integrity": "sha1-7P+p1+GSUYOJ9CrQ6D9yrsRW6iA=", + "dependencies": { + "icss-replace-symbols": "^1.1.0", + "postcss": "^6.0.1" + } + }, + "node_modules/postcss-modules-values/node_modules/postcss": { + "version": "6.0.23", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.23.tgz", + "integrity": "sha512-soOk1h6J3VMTZtVeVpv15/Hpdl2cBLX3CAw4TAbkpTJiNPk9YP/zWcD1ND+xEtvyuuvKzbxliTOIyvkSeSJ6ag==", + "dependencies": { + "chalk": "^2.4.1", + "source-map": "^0.6.1", + "supports-color": "^5.4.0" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/postcss-modules-values/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-normalize-charset": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-normalize-charset/-/postcss-normalize-charset-4.0.1.tgz", + "integrity": "sha512-gMXCrrlWh6G27U0hF3vNvR3w8I1s2wOBILvA87iNXaPvSNo5uZAMYsZG7XjCUf1eVxuPfyL4TJ7++SGZLc9A3g==", + "dependencies": { + "postcss": "^7.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/postcss-normalize-charset/node_modules/postcss": { + "version": "7.0.36", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.36.tgz", + "integrity": "sha512-BebJSIUMwJHRH0HAQoxN4u1CN86glsrwsW0q7T+/m44eXOUAxSNdHRkNZPYz5vVUbg17hFgOQDE7fZk7li3pZw==", + "dependencies": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, + "node_modules/postcss-normalize-charset/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-normalize-charset/node_modules/supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/postcss-normalize-display-values": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-normalize-display-values/-/postcss-normalize-display-values-4.0.2.tgz", + "integrity": "sha512-3F2jcsaMW7+VtRMAqf/3m4cPFhPD3EFRgNs18u+k3lTJJlVe7d0YPO+bnwqo2xg8YiRpDXJI2u8A0wqJxMsQuQ==", + "dependencies": { + "cssnano-util-get-match": "^4.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/postcss-normalize-display-values/node_modules/postcss": { + "version": "7.0.36", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.36.tgz", + "integrity": "sha512-BebJSIUMwJHRH0HAQoxN4u1CN86glsrwsW0q7T+/m44eXOUAxSNdHRkNZPYz5vVUbg17hFgOQDE7fZk7li3pZw==", + "dependencies": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, + "node_modules/postcss-normalize-display-values/node_modules/postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==" + }, + "node_modules/postcss-normalize-display-values/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-normalize-display-values/node_modules/supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/postcss-normalize-positions": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-normalize-positions/-/postcss-normalize-positions-4.0.2.tgz", + "integrity": "sha512-Dlf3/9AxpxE+NF1fJxYDeggi5WwV35MXGFnnoccP/9qDtFrTArZ0D0R+iKcg5WsUd8nUYMIl8yXDCtcrT8JrdA==", + "dependencies": { + "cssnano-util-get-arguments": "^4.0.0", + "has": "^1.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/postcss-normalize-positions/node_modules/postcss": { + "version": "7.0.36", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.36.tgz", + "integrity": "sha512-BebJSIUMwJHRH0HAQoxN4u1CN86glsrwsW0q7T+/m44eXOUAxSNdHRkNZPYz5vVUbg17hFgOQDE7fZk7li3pZw==", + "dependencies": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, + "node_modules/postcss-normalize-positions/node_modules/postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==" + }, + "node_modules/postcss-normalize-positions/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-normalize-positions/node_modules/supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/postcss-normalize-repeat-style": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-normalize-repeat-style/-/postcss-normalize-repeat-style-4.0.2.tgz", + "integrity": "sha512-qvigdYYMpSuoFs3Is/f5nHdRLJN/ITA7huIoCyqqENJe9PvPmLhNLMu7QTjPdtnVf6OcYYO5SHonx4+fbJE1+Q==", + "dependencies": { + "cssnano-util-get-arguments": "^4.0.0", + "cssnano-util-get-match": "^4.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/postcss-normalize-repeat-style/node_modules/postcss": { + "version": "7.0.36", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.36.tgz", + "integrity": "sha512-BebJSIUMwJHRH0HAQoxN4u1CN86glsrwsW0q7T+/m44eXOUAxSNdHRkNZPYz5vVUbg17hFgOQDE7fZk7li3pZw==", + "dependencies": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, + "node_modules/postcss-normalize-repeat-style/node_modules/postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==" + }, + "node_modules/postcss-normalize-repeat-style/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-normalize-repeat-style/node_modules/supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/postcss-normalize-string": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-normalize-string/-/postcss-normalize-string-4.0.2.tgz", + "integrity": "sha512-RrERod97Dnwqq49WNz8qo66ps0swYZDSb6rM57kN2J+aoyEAJfZ6bMx0sx/F9TIEX0xthPGCmeyiam/jXif0eA==", + "dependencies": { + "has": "^1.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/postcss-normalize-string/node_modules/postcss": { + "version": "7.0.36", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.36.tgz", + "integrity": "sha512-BebJSIUMwJHRH0HAQoxN4u1CN86glsrwsW0q7T+/m44eXOUAxSNdHRkNZPYz5vVUbg17hFgOQDE7fZk7li3pZw==", + "dependencies": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, + "node_modules/postcss-normalize-string/node_modules/postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==" + }, + "node_modules/postcss-normalize-string/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-normalize-string/node_modules/supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/postcss-normalize-timing-functions": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-normalize-timing-functions/-/postcss-normalize-timing-functions-4.0.2.tgz", + "integrity": "sha512-acwJY95edP762e++00Ehq9L4sZCEcOPyaHwoaFOhIwWCDfik6YvqsYNxckee65JHLKzuNSSmAdxwD2Cud1Z54A==", + "dependencies": { + "cssnano-util-get-match": "^4.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/postcss-normalize-timing-functions/node_modules/postcss": { + "version": "7.0.36", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.36.tgz", + "integrity": "sha512-BebJSIUMwJHRH0HAQoxN4u1CN86glsrwsW0q7T+/m44eXOUAxSNdHRkNZPYz5vVUbg17hFgOQDE7fZk7li3pZw==", + "dependencies": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, + "node_modules/postcss-normalize-timing-functions/node_modules/postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==" + }, + "node_modules/postcss-normalize-timing-functions/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-normalize-timing-functions/node_modules/supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/postcss-normalize-unicode": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-normalize-unicode/-/postcss-normalize-unicode-4.0.1.tgz", + "integrity": "sha512-od18Uq2wCYn+vZ/qCOeutvHjB5jm57ToxRaMeNuf0nWVHaP9Hua56QyMF6fs/4FSUnVIw0CBPsU0K4LnBPwYwg==", + "dependencies": { + "browserslist": "^4.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/postcss-normalize-unicode/node_modules/postcss": { + "version": "7.0.36", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.36.tgz", + "integrity": "sha512-BebJSIUMwJHRH0HAQoxN4u1CN86glsrwsW0q7T+/m44eXOUAxSNdHRkNZPYz5vVUbg17hFgOQDE7fZk7li3pZw==", + "dependencies": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, + "node_modules/postcss-normalize-unicode/node_modules/postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==" + }, + "node_modules/postcss-normalize-unicode/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-normalize-unicode/node_modules/supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/postcss-normalize-url": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-normalize-url/-/postcss-normalize-url-4.0.1.tgz", + "integrity": "sha512-p5oVaF4+IHwu7VpMan/SSpmpYxcJMtkGppYf0VbdH5B6hN8YNmVyJLuY9FmLQTzY3fag5ESUUHDqM+heid0UVA==", + "dependencies": { + "is-absolute-url": "^2.0.0", + "normalize-url": "^3.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/postcss-normalize-url/node_modules/postcss": { + "version": "7.0.36", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.36.tgz", + "integrity": "sha512-BebJSIUMwJHRH0HAQoxN4u1CN86glsrwsW0q7T+/m44eXOUAxSNdHRkNZPYz5vVUbg17hFgOQDE7fZk7li3pZw==", + "dependencies": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, + "node_modules/postcss-normalize-url/node_modules/postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==" + }, + "node_modules/postcss-normalize-url/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-normalize-url/node_modules/supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/postcss-normalize-whitespace": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-normalize-whitespace/-/postcss-normalize-whitespace-4.0.2.tgz", + "integrity": "sha512-tO8QIgrsI3p95r8fyqKV+ufKlSHh9hMJqACqbv2XknufqEDhDvbguXGBBqxw9nsQoXWf0qOqppziKJKHMD4GtA==", + "dependencies": { + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/postcss-normalize-whitespace/node_modules/postcss": { + "version": "7.0.36", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.36.tgz", + "integrity": "sha512-BebJSIUMwJHRH0HAQoxN4u1CN86glsrwsW0q7T+/m44eXOUAxSNdHRkNZPYz5vVUbg17hFgOQDE7fZk7li3pZw==", + "dependencies": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, + "node_modules/postcss-normalize-whitespace/node_modules/postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==" + }, + "node_modules/postcss-normalize-whitespace/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-normalize-whitespace/node_modules/supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/postcss-ordered-values": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/postcss-ordered-values/-/postcss-ordered-values-4.1.2.tgz", + "integrity": "sha512-2fCObh5UanxvSxeXrtLtlwVThBvHn6MQcu4ksNT2tsaV2Fg76R2CV98W7wNSlX+5/pFwEyaDwKLLoEV7uRybAw==", + "dependencies": { + "cssnano-util-get-arguments": "^4.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/postcss-ordered-values/node_modules/postcss": { + "version": "7.0.36", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.36.tgz", + "integrity": "sha512-BebJSIUMwJHRH0HAQoxN4u1CN86glsrwsW0q7T+/m44eXOUAxSNdHRkNZPYz5vVUbg17hFgOQDE7fZk7li3pZw==", + "dependencies": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, + "node_modules/postcss-ordered-values/node_modules/postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==" + }, + "node_modules/postcss-ordered-values/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-ordered-values/node_modules/supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/postcss-reduce-initial": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/postcss-reduce-initial/-/postcss-reduce-initial-4.0.3.tgz", + "integrity": "sha512-gKWmR5aUulSjbzOfD9AlJiHCGH6AEVLaM0AV+aSioxUDd16qXP1PCh8d1/BGVvpdWn8k/HiK7n6TjeoXN1F7DA==", + "dependencies": { + "browserslist": "^4.0.0", + "caniuse-api": "^3.0.0", + "has": "^1.0.0", + "postcss": "^7.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/postcss-reduce-initial/node_modules/postcss": { + "version": "7.0.36", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.36.tgz", + "integrity": "sha512-BebJSIUMwJHRH0HAQoxN4u1CN86glsrwsW0q7T+/m44eXOUAxSNdHRkNZPYz5vVUbg17hFgOQDE7fZk7li3pZw==", + "dependencies": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, + "node_modules/postcss-reduce-initial/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-reduce-initial/node_modules/supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/postcss-reduce-transforms": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-reduce-transforms/-/postcss-reduce-transforms-4.0.2.tgz", + "integrity": "sha512-EEVig1Q2QJ4ELpJXMZR8Vt5DQx8/mo+dGWSR7vWXqcob2gQLyQGsionYcGKATXvQzMPn6DSN1vTN7yFximdIAg==", + "dependencies": { + "cssnano-util-get-match": "^4.0.0", + "has": "^1.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/postcss-reduce-transforms/node_modules/postcss": { + "version": "7.0.36", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.36.tgz", + "integrity": "sha512-BebJSIUMwJHRH0HAQoxN4u1CN86glsrwsW0q7T+/m44eXOUAxSNdHRkNZPYz5vVUbg17hFgOQDE7fZk7li3pZw==", + "dependencies": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, + "node_modules/postcss-reduce-transforms/node_modules/postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==" + }, + "node_modules/postcss-reduce-transforms/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-reduce-transforms/node_modules/supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/postcss-selector-parser": { + "version": "6.0.6", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.6.tgz", + "integrity": "sha512-9LXrvaaX3+mcv5xkg5kFwqSzSH1JIObIx51PrndZwlmznwXRfxMddDvo9gve3gVR8ZTKgoFDdWkbRFmEhT4PMg==", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-svgo": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/postcss-svgo/-/postcss-svgo-4.0.3.tgz", + "integrity": "sha512-NoRbrcMWTtUghzuKSoIm6XV+sJdvZ7GZSc3wdBN0W19FTtp2ko8NqLsgoh/m9CzNhU3KLPvQmjIwtaNFkaFTvw==", + "dependencies": { + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0", + "svgo": "^1.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/postcss-svgo/node_modules/postcss": { + "version": "7.0.36", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.36.tgz", + "integrity": "sha512-BebJSIUMwJHRH0HAQoxN4u1CN86glsrwsW0q7T+/m44eXOUAxSNdHRkNZPYz5vVUbg17hFgOQDE7fZk7li3pZw==", + "dependencies": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, + "node_modules/postcss-svgo/node_modules/postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==" + }, + "node_modules/postcss-svgo/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-svgo/node_modules/supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/postcss-unique-selectors": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-unique-selectors/-/postcss-unique-selectors-4.0.1.tgz", + "integrity": "sha512-+JanVaryLo9QwZjKrmJgkI4Fn8SBgRO6WXQBJi7KiAVPlmxikB5Jzc4EvXMT2H0/m0RjrVVm9rGNhZddm/8Spg==", + "dependencies": { + "alphanum-sort": "^1.0.0", + "postcss": "^7.0.0", + "uniqs": "^2.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/postcss-unique-selectors/node_modules/postcss": { + "version": "7.0.36", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.36.tgz", + "integrity": "sha512-BebJSIUMwJHRH0HAQoxN4u1CN86glsrwsW0q7T+/m44eXOUAxSNdHRkNZPYz5vVUbg17hFgOQDE7fZk7li3pZw==", + "dependencies": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, + "node_modules/postcss-unique-selectors/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-unique-selectors/node_modules/supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/postcss-value-parser": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.1.0.tgz", + "integrity": "sha512-97DXOFbQJhk71ne5/Mt6cOu6yxsSfM0QGQyl0L25Gca4yGWEGJaig7l7gbCX623VqTBNGLRLaVUCnNkcedlRSQ==" + }, + "node_modules/posthtml": { + "version": "0.11.6", + "resolved": "https://registry.npmjs.org/posthtml/-/posthtml-0.11.6.tgz", + "integrity": "sha512-C2hrAPzmRdpuL3iH0TDdQ6XCc9M7Dcc3zEW5BLerY65G4tWWszwv6nG/ksi6ul5i2mx22ubdljgktXCtNkydkw==", + "dependencies": { + "posthtml-parser": "^0.4.1", + "posthtml-render": "^1.1.5" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/posthtml-parser": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/posthtml-parser/-/posthtml-parser-0.4.2.tgz", + "integrity": "sha512-BUIorsYJTvS9UhXxPTzupIztOMVNPa/HtAm9KHni9z6qEfiJ1bpOBL5DfUOL9XAc3XkLIEzBzpph+Zbm4AdRAg==", + "dependencies": { + "htmlparser2": "^3.9.2" + } + }, + "node_modules/posthtml-parser/node_modules/domhandler": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.4.2.tgz", + "integrity": "sha512-JiK04h0Ht5u/80fdLMCEmV4zkNh2BcoMFBmZ/91WtYZ8qVXSKjiw7fXMgFPnHcSZgOo3XdinHvmnDUeMf5R4wA==", + "dependencies": { + "domelementtype": "1" + } + }, + "node_modules/posthtml-parser/node_modules/domutils": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.7.0.tgz", + "integrity": "sha512-Lgd2XcJ/NjEw+7tFvfKxOzCYKZsdct5lczQ2ZaQY8Djz7pfAD3Gbp8ySJWtreII/vDlMVmxwa6pHmdxIYgttDg==", + "dependencies": { + "dom-serializer": "0", + "domelementtype": "1" + } + }, + "node_modules/posthtml-parser/node_modules/htmlparser2": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.10.1.tgz", + "integrity": "sha512-IgieNijUMbkDovyoKObU1DUhm1iwNYE/fuifEoEHfd1oZKZDaONBSkal7Y01shxsM49R4XaMdGez3WnF9UfiCQ==", + "dependencies": { + "domelementtype": "^1.3.1", + "domhandler": "^2.3.0", + "domutils": "^1.5.1", + "entities": "^1.1.1", + "inherits": "^2.0.1", + "readable-stream": "^3.1.1" + } + }, + "node_modules/posthtml-parser/node_modules/readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/posthtml-render": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/posthtml-render/-/posthtml-render-1.3.1.tgz", + "integrity": "sha512-eSToKjNLu0FiF76SSGMHjOFXYzAc/CJqi677Sq6hYvcvFCBtD6de/W5l+0IYPf7ypscqAfjCttxvTdMJt5Gj8Q==", + "engines": { + "node": ">=10" + } + }, + "node_modules/prebuild-install": { + "version": "6.1.3", + "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-6.1.3.tgz", + "integrity": "sha512-iqqSR84tNYQUQHRXalSKdIaM8Ov1QxOVuBNWI7+BzZWv6Ih9k75wOnH1rGQ9WWTaaLkTpxWKIciOF0KyfM74+Q==", + "dev": true, + "dependencies": { + "detect-libc": "^1.0.3", + "expand-template": "^2.0.3", + "github-from-package": "0.0.0", + "minimist": "^1.2.3", + "mkdirp-classic": "^0.5.3", + "napi-build-utils": "^1.0.1", + "node-abi": "^2.21.0", + "npmlog": "^4.0.1", + "pump": "^3.0.0", + "rc": "^1.2.7", + "simple-get": "^3.0.3", + "tar-fs": "^2.0.0", + "tunnel-agent": "^0.6.0" + }, + "bin": { + "prebuild-install": "bin.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/prelude-ls": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", + "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/pretty-hrtime": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/pretty-hrtime/-/pretty-hrtime-1.0.3.tgz", + "integrity": "sha1-t+PqQkNaTJsnWdmeDyAesZWALuE=", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha1-czIwDoQBYb2j5podHZGn1LwW8YI=", + "engines": { + "node": ">= 0.6.0" + } + }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" + }, + "node_modules/prompt-sync": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/prompt-sync/-/prompt-sync-4.2.0.tgz", + "integrity": "sha512-BuEzzc5zptP5LsgV5MZETjDaKSWfchl5U9Luiu8SKp7iZWD5tZalOxvNcZRwv+d2phNFr8xlbxmFNcRKfJOzJw==", + "dependencies": { + "strip-ansi": "^5.0.0" + } + }, + "node_modules/prompt-sync/node_modules/ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "engines": { + "node": ">=6" + } + }, + "node_modules/prompt-sync/node_modules/strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dependencies": { + "ansi-regex": "^4.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/protocol-buffers-schema": { + "version": "3.5.1", + "resolved": "https://registry.npmjs.org/protocol-buffers-schema/-/protocol-buffers-schema-3.5.1.tgz", + "integrity": "sha512-YVCvdhxWNDP8/nJDyXLuM+UFsuPk4+1PB7WGPVDzm3HTHbzFLxQYeW2iZpS4mmnXrQJGBzt230t/BbEb7PrQaw==" + }, + "node_modules/psl": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz", + "integrity": "sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ==" + }, + "node_modules/public-encrypt": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.3.tgz", + "integrity": "sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q==", + "dependencies": { + "bn.js": "^4.1.0", + "browserify-rsa": "^4.0.0", + "create-hash": "^1.1.0", + "parse-asn1": "^5.0.0", + "randombytes": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "node_modules/pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "dev": true, + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "node_modules/punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "engines": { + "node": ">=6" + } + }, + "node_modules/q": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz", + "integrity": "sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc=", + "engines": { + "node": ">=0.6.0", + "teleport": ">=0.2.0" + } + }, + "node_modules/qs": { + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", + "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/querystring": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", + "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=", + "deprecated": "The querystring API is considered Legacy. new code should use the URLSearchParams API instead.", + "engines": { + "node": ">=0.4.x" + } + }, + "node_modules/querystring-es3": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/querystring-es3/-/querystring-es3-0.2.1.tgz", + "integrity": "sha1-nsYfeQSYdXB9aUFFlv2Qek1xHnM=", + "engines": { + "node": ">=0.4.x" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/quick-lru": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", + "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/quickselect": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/quickselect/-/quickselect-1.1.1.tgz", + "integrity": "sha512-qN0Gqdw4c4KGPsBOQafj6yj/PA6c/L63f6CaZ/DCF/xF4Esu3jVmKLUDYxghFx8Kb/O7y9tI7x2RjTSXwdK1iQ==" + }, + "node_modules/quote-stream": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/quote-stream/-/quote-stream-1.0.2.tgz", + "integrity": "sha1-hJY/jJwmuULhU/7rU6rnRlK34LI=", + "dependencies": { + "buffer-equal": "0.0.1", + "minimist": "^1.1.3", + "through2": "^2.0.0" + }, + "bin": { + "quote-stream": "bin/cmd.js" + } + }, + "node_modules/raf": { + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/raf/-/raf-3.4.1.tgz", + "integrity": "sha512-Sq4CW4QhwOHE8ucn6J34MqtZCeWFP2aQSmrlroYgqAV1PjStIhJXxYuTgUIfkEk7zTLjmIjLmU5q+fbD1NnOJA==", + "optional": true, + "dependencies": { + "performance-now": "^2.1.0" + } + }, + "node_modules/randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dependencies": { + "safe-buffer": "^5.1.0" + } + }, + "node_modules/randomfill": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/randomfill/-/randomfill-1.0.4.tgz", + "integrity": "sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw==", + "dependencies": { + "randombytes": "^2.0.5", + "safe-buffer": "^5.1.0" + } + }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/rbush": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/rbush/-/rbush-2.0.2.tgz", + "integrity": "sha512-XBOuALcTm+O/H8G90b6pzu6nX6v2zCKiFG4BJho8a+bY6AER6t8uQUZdi5bomQc0AprCWhEGa7ncAbbRap0bRA==", + "dependencies": { + "quickselect": "^1.0.1" + } + }, + "node_modules/rc": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", + "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", + "dev": true, + "dependencies": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + }, + "bin": { + "rc": "cli.js" + } + }, + "node_modules/read-file": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/read-file/-/read-file-0.2.0.tgz", + "integrity": "sha1-cMa6+IQux9FUD5gf0Oau1Mgb1UU=", + "dev": true, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/read-pkg": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz", + "integrity": "sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=", + "dev": true, + "dependencies": { + "load-json-file": "^1.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/read-pkg-up": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz", + "integrity": "sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI=", + "dev": true, + "dependencies": { + "find-up": "^1.0.0", + "read-pkg": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/readable-stream/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/redent": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/redent/-/redent-1.0.0.tgz", + "integrity": "sha1-z5Fqsf1fHxbfsggi3W7H9zDCr94=", + "dev": true, + "dependencies": { + "indent-string": "^2.1.0", + "strip-indent": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/redeyed": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/redeyed/-/redeyed-0.4.4.tgz", + "integrity": "sha1-N+mQpvKyGyoRwuakj9QTVpjLqX8=", + "dependencies": { + "esprima": "~1.0.4" + } + }, + "node_modules/reduce-css-calc": { + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/reduce-css-calc/-/reduce-css-calc-2.1.8.tgz", + "integrity": "sha512-8liAVezDmUcH+tdzoEGrhfbGcP7nOV4NkGE3a74+qqvE7nt9i4sKLGBuZNOnpI4WiGksiNPklZxva80061QiPg==", + "dependencies": { + "css-unit-converter": "^1.1.1", + "postcss-value-parser": "^3.3.0" + } + }, + "node_modules/reduce-css-calc/node_modules/postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==" + }, + "node_modules/regenerate": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", + "integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==" + }, + "node_modules/regenerate-unicode-properties": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-8.2.0.tgz", + "integrity": "sha512-F9DjY1vKLo/tPePDycuH3dn9H1OTPIkVD9Kz4LODu+F2C75mgjAJ7x/gwy6ZcSNRAAkhNlJSOHRe8k3p+K9WhA==", + "dependencies": { + "regenerate": "^1.4.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/regenerator-runtime": { + "version": "0.13.7", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz", + "integrity": "sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew==" + }, + "node_modules/regenerator-transform": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.14.5.tgz", + "integrity": "sha512-eOf6vka5IO151Jfsw2NO9WpGX58W6wWmefK3I1zEGr0lOD0u8rwPaNqQL1aRxUaxLeKO3ArNh3VYg1KbaD+FFw==", + "dependencies": { + "@babel/runtime": "^7.8.4" + } + }, + "node_modules/regex-not": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz", + "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==", + "dependencies": { + "extend-shallow": "^3.0.2", + "safe-regex": "^1.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/regexp.prototype.flags": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.3.1.tgz", + "integrity": "sha512-JiBdRBq91WlY7uRJ0ds7R+dU02i6LKi8r3BuQhNXn+kmeLN+EfHhfjqMRis1zJxnlu88hq/4dx0P2OP3APRTOA==", + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/regexpu-core": { + "version": "4.7.1", + "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-4.7.1.tgz", + "integrity": "sha512-ywH2VUraA44DZQuRKzARmw6S66mr48pQVva4LBeRhcOltJ6hExvWly5ZjFLYo67xbIxb6W1q4bAGtgfEl20zfQ==", + "dependencies": { + "regenerate": "^1.4.0", + "regenerate-unicode-properties": "^8.2.0", + "regjsgen": "^0.5.1", + "regjsparser": "^0.6.4", + "unicode-match-property-ecmascript": "^1.0.4", + "unicode-match-property-value-ecmascript": "^1.2.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/regjsgen": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.5.2.tgz", + "integrity": "sha512-OFFT3MfrH90xIW8OOSyUrk6QHD5E9JOTeGodiJeBS3J6IwlgzJMNE/1bZklWz5oTg+9dCMyEetclvCVXOPoN3A==" + }, + "node_modules/regjsparser": { + "version": "0.6.9", + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.6.9.tgz", + "integrity": "sha512-ZqbNRz1SNjLAiYuwY0zoXW8Ne675IX5q+YHioAGbCw4X96Mjl2+dcX9B2ciaeyYjViDAfvIjFpQjJgLttTEERQ==", + "dependencies": { + "jsesc": "~0.5.0" + }, + "bin": { + "regjsparser": "bin/parser" + } + }, + "node_modules/regjsparser/node_modules/jsesc": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", + "integrity": "sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0=", + "bin": { + "jsesc": "bin/jsesc" + } + }, + "node_modules/relateurl": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz", + "integrity": "sha1-VNvzd+UUQKypCkzSdGANP/LYiKk=", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/remove-trailing-separator": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", + "integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8=" + }, + "node_modules/repeat-element": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.4.tgz", + "integrity": "sha512-LFiNfRcSu7KK3evMyYOuCzv3L10TW7yC1G2/+StMjK8Y6Vqd2MG7r/Qjw4ghtuCOjFvlnms/iMmLqpvW/ES/WQ==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/repeat-string": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", + "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=", + "engines": { + "node": ">=0.10" + } + }, + "node_modules/repeating": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/repeating/-/repeating-2.0.1.tgz", + "integrity": "sha1-UhTFOpJtNVJwdSf7q0FdvAjQbdo=", + "dev": true, + "dependencies": { + "is-finite": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/request": { + "version": "2.88.2", + "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz", + "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==", + "deprecated": "request has been deprecated, see https://github.com/request/request/issues/3142", + "dependencies": { + "aws-sign2": "~0.7.0", + "aws4": "^1.8.0", + "caseless": "~0.12.0", + "combined-stream": "~1.0.6", + "extend": "~3.0.2", + "forever-agent": "~0.6.1", + "form-data": "~2.3.2", + "har-validator": "~5.1.3", + "http-signature": "~1.2.0", + "is-typedarray": "~1.0.0", + "isstream": "~0.1.2", + "json-stringify-safe": "~5.0.1", + "mime-types": "~2.1.19", + "oauth-sign": "~0.9.0", + "performance-now": "^2.1.0", + "qs": "~6.5.2", + "safe-buffer": "^5.1.2", + "tough-cookie": "~2.5.0", + "tunnel-agent": "^0.6.0", + "uuid": "^3.3.2" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/request-promise-core": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/request-promise-core/-/request-promise-core-1.1.4.tgz", + "integrity": "sha512-TTbAfBBRdWD7aNNOoVOBH4pN/KigV6LyapYNNlAPA8JwbovRti1E88m3sYAwsLi5ryhPKsE9APwnjFTgdUjTpw==", + "dependencies": { + "lodash": "^4.17.19" + }, + "engines": { + "node": ">=0.10.0" + }, + "peerDependencies": { + "request": "^2.34" + } + }, + "node_modules/request-promise-native": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/request-promise-native/-/request-promise-native-1.0.9.tgz", + "integrity": "sha512-wcW+sIUiWnKgNY0dqCpOZkUbF/I+YPi+f09JZIDa39Ec+q82CpSYniDp+ISgTTbKmnpJWASeJBPZmoxH84wt3g==", + "deprecated": "request-promise-native has been deprecated because it extends the now deprecated request package, see https://github.com/request/request/issues/3142", + "dependencies": { + "request-promise-core": "1.1.4", + "stealthy-require": "^1.1.1", + "tough-cookie": "^2.3.3" + }, + "engines": { + "node": ">=0.12.0" + }, + "peerDependencies": { + "request": "^2.34" + } + }, + "node_modules/resolve": { + "version": "1.20.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz", + "integrity": "sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==", + "dependencies": { + "is-core-module": "^2.2.0", + "path-parse": "^1.0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-from": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz", + "integrity": "sha1-six699nWiBvItuZTM17rywoYh0g=", + "engines": { + "node": ">=4" + } + }, + "node_modules/resolve-protobuf-schema": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/resolve-protobuf-schema/-/resolve-protobuf-schema-2.1.0.tgz", + "integrity": "sha512-kI5ffTiZWmJaS/huM8wZfEMer1eRd7oJQhDuxeCLe3t7N7mX3z94CN0xPxBQxFYQTSNz9T0i+v6inKqSdK8xrQ==", + "dependencies": { + "protocol-buffers-schema": "^3.3.1" + } + }, + "node_modules/resolve-url": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", + "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=", + "deprecated": "https://github.com/lydell/resolve-url#deprecated" + }, + "node_modules/restore-cursor": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz", + "integrity": "sha1-n37ih/gv0ybU/RYpI9YhKe7g368=", + "dependencies": { + "onetime": "^2.0.0", + "signal-exit": "^3.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/ret": { + "version": "0.1.15", + "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", + "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==", + "engines": { + "node": ">=0.12" + } + }, + "node_modules/reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rgb-regex": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/rgb-regex/-/rgb-regex-1.0.1.tgz", + "integrity": "sha1-wODWiC3w4jviVKR16O3UGRX+rrE=" + }, + "node_modules/rgba-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/rgba-regex/-/rgba-regex-1.0.0.tgz", + "integrity": "sha1-QzdOLiyglosO8VI0YLfXMP8i7rM=" + }, + "node_modules/rgbcolor": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/rgbcolor/-/rgbcolor-1.0.1.tgz", + "integrity": "sha1-1lBezbMEplldom+ktDMHMGd1lF0=", + "optional": true, + "engines": { + "node": ">= 0.8.15" + } + }, + "node_modules/rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + } + }, + "node_modules/ripemd160": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz", + "integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==", + "dependencies": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1" + } + }, + "node_modules/robust-orientation": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/robust-orientation/-/robust-orientation-1.1.3.tgz", + "integrity": "sha1-2v9bANO+TmByLw6cAVbvln8cIEk=", + "dependencies": { + "robust-scale": "^1.0.2", + "robust-subtract": "^1.0.0", + "robust-sum": "^1.0.0", + "two-product": "^1.0.2" + } + }, + "node_modules/robust-predicates": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/robust-predicates/-/robust-predicates-2.0.4.tgz", + "integrity": "sha512-l4NwboJM74Ilm4VKfbAtFeGq7aEjWL+5kVFcmgFA2MrdnQWx9iE/tUGvxY5HyMI7o/WpSIUFLbC5fbeaHgSCYg==" + }, + "node_modules/robust-scale": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/robust-scale/-/robust-scale-1.0.2.tgz", + "integrity": "sha1-d1Ey7QlULQKOWLLMecBikLz3jDI=", + "dependencies": { + "two-product": "^1.0.2", + "two-sum": "^1.0.0" + } + }, + "node_modules/robust-subtract": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/robust-subtract/-/robust-subtract-1.0.0.tgz", + "integrity": "sha1-4LFk4e2LpOOl3aRaEgODSNvtPpo=" + }, + "node_modules/robust-sum": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/robust-sum/-/robust-sum-1.0.0.tgz", + "integrity": "sha1-FmRuUlKStNJdgnV6KGlV4Lv6U9k=" + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/safe-regex": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", + "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=", + "dependencies": { + "ret": "~0.1.10" + } + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "node_modules/sax": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", + "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==" + }, + "node_modules/saxes": { + "version": "3.1.11", + "resolved": "https://registry.npmjs.org/saxes/-/saxes-3.1.11.tgz", + "integrity": "sha512-Ydydq3zC+WYDJK1+gRxRapLIED9PWeSuuS41wqyoRmzvhhh9nc+QQrVMKJYzJFULazeGhzSV0QleN2wD3boh2g==", + "dependencies": { + "xmlchars": "^2.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/send": { + "version": "0.17.1", + "resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz", + "integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==", + "dependencies": { + "debug": "2.6.9", + "depd": "~1.1.2", + "destroy": "~1.0.4", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "~1.7.2", + "mime": "1.6.0", + "ms": "2.1.1", + "on-finished": "~2.3.0", + "range-parser": "~1.2.1", + "statuses": "~1.5.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/send/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/send/node_modules/debug/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + }, + "node_modules/send/node_modules/ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==" + }, + "node_modules/serialize-to-js": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/serialize-to-js/-/serialize-to-js-3.1.1.tgz", + "integrity": "sha512-F+NGU0UHMBO4Q965tjw7rvieNVjlH6Lqi2emq/Lc9LUURYJbiCzmpi4Cy1OOjjVPtxu0c+NE85LU6968Wko5ZA==", + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/serve-static": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz", + "integrity": "sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==", + "dependencies": { + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.17.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", + "dev": true + }, + "node_modules/set-value": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz", + "integrity": "sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==", + "dependencies": { + "extend-shallow": "^2.0.1", + "is-extendable": "^0.1.1", + "is-plain-object": "^2.0.3", + "split-string": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/set-value/node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/setimmediate": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", + "integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=" + }, + "node_modules/setprototypeof": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", + "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==" + }, + "node_modules/sha.js": { + "version": "2.4.11", + "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", + "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", + "dependencies": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + }, + "bin": { + "sha.js": "bin.js" + } + }, + "node_modules/shallow-copy": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/shallow-copy/-/shallow-copy-0.0.1.tgz", + "integrity": "sha1-QV9CcC1z2BAzApLMXuhurhoRoXA=" + }, + "node_modules/sharkdown": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/sharkdown/-/sharkdown-0.1.1.tgz", + "integrity": "sha512-exwooSpmo5s45lrexgz6Q0rFQM574wYIX3iDZ7RLLqOb7IAoQZu9nxlZODU972g19sR69OIpKP2cpHTzU+PHIg==", + "dependencies": { + "cardinal": "~0.4.2", + "minimist": "0.0.5", + "split": "~0.2.10" + }, + "bin": { + "sharkdown": "sharkdown" + } + }, + "node_modules/sharkdown/node_modules/minimist": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.5.tgz", + "integrity": "sha1-16oye87PUY+RBqxrjwA/o7zqhWY=" + }, + "node_modules/sharp": { + "version": "0.28.3", + "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.28.3.tgz", + "integrity": "sha512-21GEP45Rmr7q2qcmdnjDkNP04Ooh5v0laGS5FDpojOO84D1DJwUijLiSq8XNNM6e8aGXYtoYRh3sVNdm8NodMA==", + "dev": true, + "hasInstallScript": true, + "dependencies": { + "color": "^3.1.3", + "detect-libc": "^1.0.3", + "node-addon-api": "^3.2.0", + "prebuild-install": "^6.1.2", + "semver": "^7.3.5", + "simple-get": "^3.1.0", + "tar-fs": "^2.1.1", + "tunnel-agent": "^0.6.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/sharp/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/sharp/node_modules/node-addon-api": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-3.2.1.tgz", + "integrity": "sha512-mmcei9JghVNDYydghQmeDX8KoAm0FAiYyIcUt/N4nhyAipB17pllZQDOJD2fotxABnt4Mdz+dKTO7eftLg4d0A==", + "dev": true + }, + "node_modules/sharp/node_modules/semver": { + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/shebang-command": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", + "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", + "dependencies": { + "shebang-regex": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/shebang-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", + "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/shell-quote": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.7.2.tgz", + "integrity": "sha512-mRz/m/JVscCrkMyPqHc/bczi3OQHkLTqXHEFu0zDhK/qfv3UcOA4SVmRCLmos4bhjr9ekVQubj/R7waKapmiQg==" + }, + "node_modules/sigmund": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.1.tgz", + "integrity": "sha1-P/IfGYytIXX587eBhT/ZTQ0ZtZA=" + }, + "node_modules/signal-exit": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz", + "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==" + }, + "node_modules/simple-concat": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", + "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/simple-get": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-3.1.0.tgz", + "integrity": "sha512-bCR6cP+aTdScaQCnQKbPKtJOKDp/hj9EDLJo3Nw4y1QksqaovlW/bnptB6/c1e+qmNIDHRK+oXFDdEqBT8WzUA==", + "dev": true, + "dependencies": { + "decompress-response": "^4.2.0", + "once": "^1.3.1", + "simple-concat": "^1.0.0" + } + }, + "node_modules/simple-swizzle": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", + "integrity": "sha1-pNprY1/8zMoz9w0Xy5JZLeleVXo=", + "dependencies": { + "is-arrayish": "^0.3.1" + } + }, + "node_modules/simplicial-complex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/simplicial-complex/-/simplicial-complex-1.0.0.tgz", + "integrity": "sha1-bDOk7Wn81Nkbe8rdOzC2NoPq4kE=", + "dependencies": { + "bit-twiddle": "^1.0.0", + "union-find": "^1.0.0" + } + }, + "node_modules/simplify-js": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/simplify-js/-/simplify-js-1.2.4.tgz", + "integrity": "sha512-vITfSlwt7h/oyrU42R83mtzFpwYk3+mkH9bOHqq/Qw6n8rtR7aE3NZQ5fbcyCUVVmuMJR6ynsAhOfK2qoah8Jg==" + }, + "node_modules/skmeans": { + "version": "0.9.7", + "resolved": "https://registry.npmjs.org/skmeans/-/skmeans-0.9.7.tgz", + "integrity": "sha512-hNj1/oZ7ygsfmPZ7ZfN5MUBRoGg1gtpnImuJBgLO0ljQ67DtJuiQaiYdS4lUA6s0KCwnPhGivtC/WRwIZLkHyg==" + }, + "node_modules/snapdragon": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", + "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==", + "dependencies": { + "base": "^0.11.1", + "debug": "^2.2.0", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "map-cache": "^0.2.2", + "source-map": "^0.5.6", + "source-map-resolve": "^0.5.0", + "use": "^3.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon-node": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz", + "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==", + "dependencies": { + "define-property": "^1.0.0", + "isobject": "^3.0.0", + "snapdragon-util": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon-node/node_modules/define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "dependencies": { + "is-descriptor": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon-node/node_modules/is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dependencies": { + "kind-of": "^6.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon-node/node_modules/is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dependencies": { + "kind-of": "^6.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon-node/node_modules/is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dependencies": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon-util": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz", + "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==", + "dependencies": { + "kind-of": "^3.2.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon-util/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/snapdragon/node_modules/define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dependencies": { + "is-descriptor": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon/node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + }, + "node_modules/source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-js": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-0.6.2.tgz", + "integrity": "sha512-/3GptzWzu0+0MBQFrDKzw/DvvMTUORvgY6k6jd/VS6iCR4RDTKWH6v6WPwQoUO8667uQEf9Oe38DxAYWY5F/Ug==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-resolve": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.3.tgz", + "integrity": "sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw==", + "dependencies": { + "atob": "^2.1.2", + "decode-uri-component": "^0.2.0", + "resolve-url": "^0.2.1", + "source-map-url": "^0.4.0", + "urix": "^0.1.0" + } + }, + "node_modules/source-map-support": { + "version": "0.5.19", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.19.tgz", + "integrity": "sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==", + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/source-map-support/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-url": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.1.tgz", + "integrity": "sha512-cPiFOTLUKvJFIg4SKVScy4ilPPW6rFgMgfuZJPNoDuMs3nC1HbMUycBoJw77xFIp6z1UJQJOfx6C9GMH80DiTw==" + }, + "node_modules/spdx-correct": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz", + "integrity": "sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==", + "dependencies": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-exceptions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz", + "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==" + }, + "node_modules/spdx-expression-parse": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", + "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-license-ids": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.7.tgz", + "integrity": "sha512-U+MTEOO0AiDzxwFvoa4JVnMV6mZlJKk2sBLt90s7G0Gd0Mlknc7kxEn3nuDPNZRta7O2uy8oLcZLVT+4sqNZHQ==" + }, + "node_modules/split": { + "version": "0.2.10", + "resolved": "https://registry.npmjs.org/split/-/split-0.2.10.tgz", + "integrity": "sha1-Zwl8YB1pfOE2j0GPBs0gHPBSGlc=", + "dependencies": { + "through": "2" + }, + "engines": { + "node": "*" + } + }, + "node_modules/split-string": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz", + "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==", + "dependencies": { + "extend-shallow": "^3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=" + }, + "node_modules/srcset": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/srcset/-/srcset-3.0.1.tgz", + "integrity": "sha512-MM8wDGg5BQJEj94tDrZDrX9wrC439/Eoeg3sgmVLPMjHgrAFeXAKk3tmFlCbKw5k+yOEhPXRpPlRcisQmqWVSQ==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/sshpk": { + "version": "1.16.1", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz", + "integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==", + "dependencies": { + "asn1": "~0.2.3", + "assert-plus": "^1.0.0", + "bcrypt-pbkdf": "^1.0.0", + "dashdash": "^1.12.0", + "ecc-jsbn": "~0.1.1", + "getpass": "^0.1.1", + "jsbn": "~0.1.0", + "safer-buffer": "^2.0.2", + "tweetnacl": "~0.14.0" + }, + "bin": { + "sshpk-conv": "bin/sshpk-conv", + "sshpk-sign": "bin/sshpk-sign", + "sshpk-verify": "bin/sshpk-verify" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/stable": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/stable/-/stable-0.1.8.tgz", + "integrity": "sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==" + }, + "node_modules/stackblur-canvas": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/stackblur-canvas/-/stackblur-canvas-2.5.0.tgz", + "integrity": "sha512-EeNzTVfj+1In7aSLPKDD03F/ly4RxEuF/EX0YcOG0cKoPXs+SLZxDawQbexQDBzwROs4VKLWTOaZQlZkGBFEIQ==", + "optional": true, + "engines": { + "node": ">=0.1.14" + } + }, + "node_modules/static-eval": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/static-eval/-/static-eval-2.1.0.tgz", + "integrity": "sha512-agtxZ/kWSsCkI5E4QifRwsaPs0P0JmZV6dkLz6ILYfFYQGn+5plctanRN+IC8dJRiFkyXHrwEE3W9Wmx67uDbw==", + "dependencies": { + "escodegen": "^1.11.1" + } + }, + "node_modules/static-eval/node_modules/escodegen": { + "version": "1.14.3", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.14.3.tgz", + "integrity": "sha512-qFcX0XJkdg+PB3xjZZG/wKSuT1PnQWx57+TVSjIMmILd2yC/6ByYElPwJnslDsuWuSAp4AwJGumarAAmJch5Kw==", + "dependencies": { + "esprima": "^4.0.1", + "estraverse": "^4.2.0", + "esutils": "^2.0.2", + "optionator": "^0.8.1" + }, + "bin": { + "escodegen": "bin/escodegen.js", + "esgenerate": "bin/esgenerate.js" + }, + "engines": { + "node": ">=4.0" + }, + "optionalDependencies": { + "source-map": "~0.6.1" + } + }, + "node_modules/static-eval/node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/static-eval/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "optional": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/static-extend": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz", + "integrity": "sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=", + "dependencies": { + "define-property": "^0.2.5", + "object-copy": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/static-extend/node_modules/define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dependencies": { + "is-descriptor": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/static-module": { + "version": "2.2.5", + "resolved": "https://registry.npmjs.org/static-module/-/static-module-2.2.5.tgz", + "integrity": "sha512-D8vv82E/Kpmz3TXHKG8PPsCPg+RAX6cbCOyvjM6x04qZtQ47EtJFVwRsdov3n5d6/6ynrOY9XB4JkaZwB2xoRQ==", + "dependencies": { + "concat-stream": "~1.6.0", + "convert-source-map": "^1.5.1", + "duplexer2": "~0.1.4", + "escodegen": "~1.9.0", + "falafel": "^2.1.0", + "has": "^1.0.1", + "magic-string": "^0.22.4", + "merge-source-map": "1.0.4", + "object-inspect": "~1.4.0", + "quote-stream": "~1.0.2", + "readable-stream": "~2.3.3", + "shallow-copy": "~0.0.1", + "static-eval": "^2.0.0", + "through2": "~2.0.3" + } + }, + "node_modules/static-module/node_modules/concat-stream": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", + "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", + "engines": [ + "node >= 0.8" + ], + "dependencies": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^2.2.2", + "typedarray": "^0.0.6" + } + }, + "node_modules/statuses": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/stealthy-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/stealthy-require/-/stealthy-require-1.1.1.tgz", + "integrity": "sha1-NbCYdbT/SfJqd35QmzCQoyJr8ks=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/store": { + "version": "2.0.12", + "resolved": "https://registry.npmjs.org/store/-/store-2.0.12.tgz", + "integrity": "sha1-jFNOKguDH3K3X8XxEZhXxE711ZM=", + "engines": { + "node": "*" + } + }, + "node_modules/stream-browserify": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-2.0.2.tgz", + "integrity": "sha512-nX6hmklHs/gr2FuxYDltq8fJA1GDlxKQCz8O/IM4atRqBH8OORmBNgfvW5gG10GT/qQ9u0CzIvr2X5Pkt6ntqg==", + "dependencies": { + "inherits": "~2.0.1", + "readable-stream": "^2.0.2" + } + }, + "node_modules/stream-http": { + "version": "2.8.3", + "resolved": "https://registry.npmjs.org/stream-http/-/stream-http-2.8.3.tgz", + "integrity": "sha512-+TSkfINHDo4J+ZobQLWiMouQYB+UVYFttRA94FpEzzJ7ZdqcL4uUUQ7WkdkI4DSozGmgBUE/a47L+38PenXhUw==", + "dependencies": { + "builtin-status-codes": "^3.0.0", + "inherits": "^2.0.1", + "readable-stream": "^2.3.6", + "to-arraybuffer": "^1.0.0", + "xtend": "^4.0.0" + } + }, + "node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/string_decoder/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "node_modules/string-width": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "dev": true, + "dependencies": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/string-width/node_modules/ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/string-width/node_modules/strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dev": true, + "dependencies": { + "ansi-regex": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/string.prototype.padend": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/string.prototype.padend/-/string.prototype.padend-3.1.2.tgz", + "integrity": "sha512-/AQFLdYvePENU3W5rgurfWSMU6n+Ww8n/3cUt7E+vPBB/D7YDG8x+qjoFs4M/alR2bW7Qg6xMjVwWUOvuQ0XpQ==", + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.18.0-next.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimend": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz", + "integrity": "sha512-y9xCjw1P23Awk8EvTpcyL2NIr1j7wJ39f+k6lvRnSMz+mz9CGz9NYPelDk42kOz6+ql8xjfK8oYzy3jAP5QU5A==", + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimstart": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.4.tgz", + "integrity": "sha512-jh6e984OBfvxS50tdY2nRZnoC5/mLFKOREQfw8t5yytkoUsJRNxvI/E39qu1sD0OtWI3OC0XgKSmcWwziwYuZw==", + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "dependencies": { + "ansi-regex": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/strip-bom": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", + "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=", + "dev": true, + "dependencies": { + "is-utf8": "^0.2.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/strip-indent": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-1.0.1.tgz", + "integrity": "sha1-DHlipq3vp7vUrDZkYKY4VSrhoKI=", + "dev": true, + "dependencies": { + "get-stdin": "^4.0.1" + }, + "bin": { + "strip-indent": "cli.js" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/stylehacks": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/stylehacks/-/stylehacks-4.0.3.tgz", + "integrity": "sha512-7GlLk9JwlElY4Y6a/rmbH2MhVlTyVmiJd1PfTCqFaIBEGMYNsrO/v3SeGTdhBThLg4Z+NbOk/qFMwCa+J+3p/g==", + "dependencies": { + "browserslist": "^4.0.0", + "postcss": "^7.0.0", + "postcss-selector-parser": "^3.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/stylehacks/node_modules/postcss": { + "version": "7.0.36", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.36.tgz", + "integrity": "sha512-BebJSIUMwJHRH0HAQoxN4u1CN86glsrwsW0q7T+/m44eXOUAxSNdHRkNZPYz5vVUbg17hFgOQDE7fZk7li3pZw==", + "dependencies": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, + "node_modules/stylehacks/node_modules/postcss-selector-parser": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-3.1.2.tgz", + "integrity": "sha512-h7fJ/5uWuRVyOtkO45pnt1Ih40CEleeyCHzipqAZO2e5H20g25Y48uYnFUiShvY4rZWNJ/Bib/KVPmanaCtOhA==", + "dependencies": { + "dot-prop": "^5.2.0", + "indexes-of": "^1.0.1", + "uniq": "^1.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/stylehacks/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/stylehacks/node_modules/supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/suncalc": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/suncalc/-/suncalc-1.8.0.tgz", + "integrity": "sha1-HZiYEJVjB4dQ9JlKlZ5lTYdqy/U=" + }, + "node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/svg-pathdata": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/svg-pathdata/-/svg-pathdata-5.0.5.tgz", + "integrity": "sha512-TAAvLNSE3fEhyl/Da19JWfMAdhSXTYeviXsLSoDT1UM76ADj5ndwAPX1FKQEgB/gFMPavOy6tOqfalXKUiXrow==", + "optional": true, + "engines": { + "node": ">=6.9.5" + } + }, + "node_modules/svgo": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/svgo/-/svgo-1.3.2.tgz", + "integrity": "sha512-yhy/sQYxR5BkC98CY7o31VGsg014AKLEPxdfhora76l36hD9Rdy5NZA/Ocn6yayNPgSamYdtX2rFJdcv07AYVw==", + "dependencies": { + "chalk": "^2.4.1", + "coa": "^2.0.2", + "css-select": "^2.0.0", + "css-select-base-adapter": "^0.1.1", + "css-tree": "1.0.0-alpha.37", + "csso": "^4.0.2", + "js-yaml": "^3.13.1", + "mkdirp": "~0.5.1", + "object.values": "^1.1.0", + "sax": "~1.2.4", + "stable": "^0.1.8", + "unquote": "~1.1.1", + "util.promisify": "~1.0.0" + }, + "bin": { + "svgo": "bin/svgo" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/symbol-tree": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", + "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==" + }, + "node_modules/tailwindcss": { + "version": "2.2.15", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-2.2.15.tgz", + "integrity": "sha512-WgV41xTMbnSoTNMNnJvShQZ+8GmY86DmXTrCgnsveNZJdlybfwCItV8kAqjYmU49YiFr+ofzmT1JlAKajBZboQ==", + "dependencies": { + "arg": "^5.0.1", + "bytes": "^3.0.0", + "chalk": "^4.1.2", + "chokidar": "^3.5.2", + "color": "^4.0.1", + "cosmiconfig": "^7.0.1", + "detective": "^5.2.0", + "didyoumean": "^1.2.2", + "dlv": "^1.1.3", + "fast-glob": "^3.2.7", + "fs-extra": "^10.0.0", + "glob-parent": "^6.0.1", + "html-tags": "^3.1.0", + "is-color-stop": "^1.1.0", + "is-glob": "^4.0.1", + "lodash": "^4.17.21", + "lodash.topath": "^4.5.2", + "modern-normalize": "^1.1.0", + "node-emoji": "^1.11.0", + "normalize-path": "^3.0.0", + "object-hash": "^2.2.0", + "postcss-js": "^3.0.3", + "postcss-load-config": "^3.1.0", + "postcss-nested": "5.0.6", + "postcss-selector-parser": "^6.0.6", + "postcss-value-parser": "^4.1.0", + "pretty-hrtime": "^1.0.3", + "purgecss": "^4.0.3", + "quick-lru": "^5.1.1", + "reduce-css-calc": "^2.1.8", + "resolve": "^1.20.0", + "tmp": "^0.2.1" + }, + "bin": { + "tailwind": "lib/cli.js", + "tailwindcss": "lib/cli.js" + }, + "engines": { + "node": ">=12.13.0" + }, + "peerDependencies": { + "autoprefixer": "^10.0.2", + "postcss": "^8.0.9" + } + }, + "node_modules/tailwindcss/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/tailwindcss/node_modules/arg": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.1.tgz", + "integrity": "sha512-e0hDa9H2Z9AwFkk2qDlwhoMYE4eToKarchkQHovNdLTCYMHZHeRjI71crOh+dio4K6u1IcwubQqo79Ga4CyAQA==" + }, + "node_modules/tailwindcss/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/tailwindcss/node_modules/color": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/color/-/color-4.0.1.tgz", + "integrity": "sha512-rpZjOKN5O7naJxkH2Rx1sZzzBgaiWECc6BYXjeCE6kF0kcASJYbUq02u7JqIHwCb/j3NhV+QhRL2683aICeGZA==", + "dependencies": { + "color-convert": "^2.0.1", + "color-string": "^1.6.0" + } + }, + "node_modules/tailwindcss/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/tailwindcss/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "node_modules/tailwindcss/node_modules/cosmiconfig": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.0.1.tgz", + "integrity": "sha512-a1YWNUV2HwGimB7dU2s1wUMurNKjpx60HxBB6xUM8Re+2s1g1IIfJvFR0/iCF+XHdE0GMTKTuLR32UQff4TEyQ==", + "dependencies": { + "@types/parse-json": "^4.0.0", + "import-fresh": "^3.2.1", + "parse-json": "^5.0.0", + "path-type": "^4.0.0", + "yaml": "^1.10.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/tailwindcss/node_modules/fs-extra": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.0.0.tgz", + "integrity": "sha512-C5owb14u9eJwizKGdchcDUQeFtlSHHthBk8pbX9Vc1PFZrLombudjDnNns88aYslCyF6IY5SUw3Roz6xShcEIQ==", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/tailwindcss/node_modules/glob-parent": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.1.tgz", + "integrity": "sha512-kEVjS71mQazDBHKcsq4E9u/vUzaLcw1A8EtUeydawvIWQCJM0qQ08G1H7/XTjFUulla6XQiDOG6MXSaG0HDKog==", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/tailwindcss/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/tailwindcss/node_modules/import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/tailwindcss/node_modules/parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "dependencies": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/tailwindcss/node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "engines": { + "node": ">=8" + } + }, + "node_modules/tailwindcss/node_modules/postcss-js": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-3.0.3.tgz", + "integrity": "sha512-gWnoWQXKFw65Hk/mi2+WTQTHdPD5UJdDXZmX073EY/B3BWnYjO4F4t0VneTCnCGQ5E5GsCdMkzPaTXwl3r5dJw==", + "dependencies": { + "camelcase-css": "^2.0.1", + "postcss": "^8.1.6" + }, + "engines": { + "node": ">=10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, + "node_modules/tailwindcss/node_modules/postcss-load-config": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-3.1.0.tgz", + "integrity": "sha512-ipM8Ds01ZUophjDTQYSVP70slFSYg3T0/zyfII5vzhN6V57YSxMgG5syXuwi5VtS8wSf3iL30v0uBdoIVx4Q0g==", + "dependencies": { + "import-cwd": "^3.0.0", + "lilconfig": "^2.0.3", + "yaml": "^1.10.2" + }, + "engines": { + "node": ">= 10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + "peerDependencies": { + "ts-node": ">=9.0.0" + }, + "peerDependenciesMeta": { + "ts-node": { + "optional": true + } + } + }, + "node_modules/tailwindcss/node_modules/postcss-nested": { + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-5.0.6.tgz", + "integrity": "sha512-rKqm2Fk0KbA8Vt3AdGN0FB9OBOMDVajMG6ZCf/GoHgdxUJ4sBFp0A/uMIRm+MJUdo33YXEtjqIz8u7DAp8B7DA==", + "dependencies": { + "postcss-selector-parser": "^6.0.6" + }, + "engines": { + "node": ">=12.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + "peerDependencies": { + "postcss": "^8.2.14" + } + }, + "node_modules/tailwindcss/node_modules/purgecss": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/purgecss/-/purgecss-4.0.3.tgz", + "integrity": "sha512-PYOIn5ibRIP34PBU9zohUcCI09c7drPJJtTDAc0Q6QlRz2/CHQ8ywGLdE7ZhxU2VTqB7p5wkvj5Qcm05Rz3Jmw==", + "dependencies": { + "commander": "^6.0.0", + "glob": "^7.0.0", + "postcss": "^8.2.1", + "postcss-selector-parser": "^6.0.2" + }, + "bin": { + "purgecss": "bin/purgecss.js" + } + }, + "node_modules/tailwindcss/node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "engines": { + "node": ">=4" + } + }, + "node_modules/tailwindcss/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/tar-fs": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.1.tgz", + "integrity": "sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng==", + "dev": true, + "dependencies": { + "chownr": "^1.1.1", + "mkdirp-classic": "^0.5.2", + "pump": "^3.0.0", + "tar-stream": "^2.1.4" + } + }, + "node_modules/tar-stream": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", + "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", + "dev": true, + "dependencies": { + "bl": "^4.0.3", + "end-of-stream": "^1.4.1", + "fs-constants": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.1.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/tar-stream/node_modules/readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "dev": true, + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/terser": { + "version": "3.17.0", + "resolved": "https://registry.npmjs.org/terser/-/terser-3.17.0.tgz", + "integrity": "sha512-/FQzzPJmCpjAH9Xvk2paiWrFq+5M6aVOf+2KRbwhByISDX/EujxsK+BAvrhb6H+2rtrLCHK9N01wO014vrIwVQ==", + "dependencies": { + "commander": "^2.19.0", + "source-map": "~0.6.1", + "source-map-support": "~0.5.10" + }, + "bin": { + "terser": "bin/uglifyjs" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/terser/node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==" + }, + "node_modules/terser/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=" + }, + "node_modules/through2": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", + "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", + "dependencies": { + "readable-stream": "~2.3.6", + "xtend": "~4.0.1" + } + }, + "node_modules/timers-browserify": { + "version": "2.0.12", + "resolved": "https://registry.npmjs.org/timers-browserify/-/timers-browserify-2.0.12.tgz", + "integrity": "sha512-9phl76Cqm6FhSX9Xe1ZUAMLtm1BLkKj2Qd5ApyWkXzsMRaA7dgr81kf4wJmQf/hAvg8EEyJxDo3du/0KlhPiKQ==", + "dependencies": { + "setimmediate": "^1.0.4" + }, + "engines": { + "node": ">=0.6.0" + } + }, + "node_modules/timsort": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/timsort/-/timsort-0.3.0.tgz", + "integrity": "sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q=" + }, + "node_modules/tiny-inflate": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/tiny-inflate/-/tiny-inflate-1.0.3.tgz", + "integrity": "sha512-pkY1fj1cKHb2seWDy0B16HeWyczlJA9/WW3u3c4z/NiWDsO3DOU5D7nhTLE9CF0yXv/QZFY7sEJmj24dK+Rrqw==" + }, + "node_modules/tiny-osmpbf": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/tiny-osmpbf/-/tiny-osmpbf-0.1.0.tgz", + "integrity": "sha1-ColXFxE+vmquNjxL5e76js2vuSc=", + "dependencies": { + "pbf": "^3.0.4", + "tiny-inflate": "^1.0.2" + } + }, + "node_modules/tinyqueue": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/tinyqueue/-/tinyqueue-2.0.3.tgz", + "integrity": "sha512-ppJZNDuKGgxzkHihX8v9v9G5f+18gzaTfrukGrq6ueg0lmH4nqVnA2IPG0AEH3jKEk2GRJCUhDoqpoiw3PHLBA==" + }, + "node_modules/tmp": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz", + "integrity": "sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ==", + "dependencies": { + "rimraf": "^3.0.0" + }, + "engines": { + "node": ">=8.17.0" + } + }, + "node_modules/tmp/node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/to-arraybuffer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz", + "integrity": "sha1-fSKbH8xjfkZsoIEYCDanqr/4P0M=" + }, + "node_modules/to-fast-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=", + "engines": { + "node": ">=4" + } + }, + "node_modules/to-iso-string": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/to-iso-string/-/to-iso-string-0.0.2.tgz", + "integrity": "sha1-TcGeZk38y+Jb2NtQiwDG2hWCVdE=", + "deprecated": "to-iso-string has been deprecated, use @segment/to-iso-string instead." + }, + "node_modules/to-object-path": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz", + "integrity": "sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=", + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/to-object-path/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/to-regex": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz", + "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==", + "dependencies": { + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "regex-not": "^1.0.2", + "safe-regex": "^1.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/toidentifier": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", + "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/topojson": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/topojson/-/topojson-3.0.2.tgz", + "integrity": "sha512-u3zeuL6WEVL0dmsRn7uHZKc4Ao4gpW3sORUv+N3ezLTvY3JdCuyg0hvpWiIfFw8p/JwVN++SvAsFgcFEeR15rQ==", + "deprecated": "Use topojson-client, topojson-server or topojson-simplify directly.", + "dependencies": { + "topojson-client": "3.0.0", + "topojson-server": "3.0.0", + "topojson-simplify": "3.0.2" + }, + "bin": { + "geo2topo": "node_modules/topojson-server/bin/geo2topo", + "topo2geo": "node_modules/topojson-client/bin/topo2geo", + "topomerge": "node_modules/topojson-client/bin/topomerge", + "topoquantize": "node_modules/topojson-client/bin/topoquantize", + "toposimplify": "node_modules/topojson-simplify/bin/toposimplify" + } + }, + "node_modules/topojson/node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==" + }, + "node_modules/topojson/node_modules/topojson-client": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/topojson-client/-/topojson-client-3.0.0.tgz", + "integrity": "sha1-H5kpOnfvQqRI0DKoGqmCtz82DS8=", + "dependencies": { + "commander": "2" + }, + "bin": { + "topo2geo": "bin/topo2geo", + "topomerge": "bin/topomerge", + "topoquantize": "bin/topoquantize" + } + }, + "node_modules/topojson/node_modules/topojson-server": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/topojson-server/-/topojson-server-3.0.0.tgz", + "integrity": "sha1-N4546Hw5cqe1vixdYENptrrmnF4=", + "dependencies": { + "commander": "2" + }, + "bin": { + "geo2topo": "bin/geo2topo" + } + }, + "node_modules/topojson/node_modules/topojson-simplify": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/topojson-simplify/-/topojson-simplify-3.0.2.tgz", + "integrity": "sha512-gyYSVRt4jO/0RJXKZQPzTDQRWV+D/nOfiljNUv0HBXslFLtq3yxRHrl7jbrjdbda5Ytdr7M8BZUI4OxU7tnbRQ==", + "dependencies": { + "commander": "2", + "topojson-client": "3" + }, + "bin": { + "toposimplify": "bin/toposimplify" + } + }, + "node_modules/tough-cookie": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", + "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", + "dependencies": { + "psl": "^1.1.28", + "punycode": "^2.1.1" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/tr46": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-1.0.1.tgz", + "integrity": "sha1-qLE/1r/SSJUZZ0zN5VujaTtwbQk=", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/tree-kill": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", + "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==", + "dev": true, + "bin": { + "tree-kill": "cli.js" + } + }, + "node_modules/trim-newlines": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-1.0.0.tgz", + "integrity": "sha1-WIeWa7WCpFA6QetST301ARgVphM=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ts-node": { + "version": "9.1.1", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-9.1.1.tgz", + "integrity": "sha512-hPlt7ZACERQGf03M253ytLY3dHbGNGrAq9qIHWUY9XHYl1z7wYngSr3OQ5xmui8o2AaxsONxIzjafLUiWBo1Fg==", + "dependencies": { + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "source-map-support": "^0.5.17", + "yn": "3.1.1" + }, + "bin": { + "ts-node": "dist/bin.js", + "ts-node-script": "dist/bin-script.js", + "ts-node-transpile-only": "dist/bin-transpile.js", + "ts-script": "dist/bin-script-deprecated.js" + }, + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "typescript": ">=2.7" + } + }, + "node_modules/ts-node-dev": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/ts-node-dev/-/ts-node-dev-1.1.6.tgz", + "integrity": "sha512-RTUi7mHMNQospArGz07KiraQcdgUVNXKsgO2HAi7FoiyPMdTDqdniB6K1dqyaIxT7c9v/VpSbfBZPS6uVpaFLQ==", + "dev": true, + "dependencies": { + "chokidar": "^3.5.1", + "dateformat": "~1.0.4-1.2.3", + "dynamic-dedupe": "^0.3.0", + "minimist": "^1.2.5", + "mkdirp": "^1.0.4", + "resolve": "^1.0.0", + "rimraf": "^2.6.1", + "source-map-support": "^0.5.12", + "tree-kill": "^1.2.2", + "ts-node": "^9.0.0", + "tsconfig": "^7.0.0" + }, + "bin": { + "ts-node-dev": "lib/bin.js", + "tsnd": "lib/bin.js" + }, + "engines": { + "node": ">=0.8.0" + }, + "peerDependencies": { + "node-notifier": "*", + "typescript": "*" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/ts-node-dev/node_modules/mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "dev": true, + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/tsconfig": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/tsconfig/-/tsconfig-7.0.0.tgz", + "integrity": "sha512-vZXmzPrL+EmC4T/4rVlT2jNVMWCi/O4DIiSj3UHg1OE5kCKbk4mfrXc6dZksLgRM/TZlKnousKH9bbTazUWRRw==", + "dev": true, + "dependencies": { + "@types/strip-bom": "^3.0.0", + "@types/strip-json-comments": "0.0.30", + "strip-bom": "^3.0.0", + "strip-json-comments": "^2.0.0" + } + }, + "node_modules/tsconfig/node_modules/strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + }, + "node_modules/tslint": { + "version": "6.1.3", + "resolved": "https://registry.npmjs.org/tslint/-/tslint-6.1.3.tgz", + "integrity": "sha512-IbR4nkT96EQOvKE2PW/djGz8iGNeJ4rF2mBfiYaR/nvUWYKJhLwimoJKgjIFEIDibBtOevj7BqCRL4oHeWWUCg==", + "deprecated": "TSLint has been deprecated in favor of ESLint. Please see https://github.com/palantir/tslint/issues/4534 for more information.", + "dependencies": { + "@babel/code-frame": "^7.0.0", + "builtin-modules": "^1.1.1", + "chalk": "^2.3.0", + "commander": "^2.12.1", + "diff": "^4.0.1", + "glob": "^7.1.1", + "js-yaml": "^3.13.1", + "minimatch": "^3.0.4", + "mkdirp": "^0.5.3", + "resolve": "^1.3.2", + "semver": "^5.3.0", + "tslib": "^1.13.0", + "tsutils": "^2.29.0" + }, + "bin": { + "tslint": "bin/tslint" + }, + "engines": { + "node": ">=4.8.0" + }, + "peerDependencies": { + "typescript": ">=2.3.0-dev || >=2.4.0-dev || >=2.5.0-dev || >=2.6.0-dev || >=2.7.0-dev || >=2.8.0-dev || >=2.9.0-dev || >=3.0.0-dev || >= 3.1.0-dev || >= 3.2.0-dev || >= 4.0.0-dev" + } + }, + "node_modules/tslint-no-circular-imports": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/tslint-no-circular-imports/-/tslint-no-circular-imports-0.7.0.tgz", + "integrity": "sha512-k3wxpeMC4ef40UbpfBVHEHIzKfNZq5/SCtAO1YjGsaNTklo+K53/TWLrym+poA65RJFDiYgYNWvkeIIkJNA0Vw==", + "dev": true, + "peerDependencies": { + "tslint": ">=5.0.0", + "typescript": ">=2.1.0" + } + }, + "node_modules/tslint/node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==" + }, + "node_modules/tslint/node_modules/mkdirp": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", + "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", + "dependencies": { + "minimist": "^1.2.5" + }, + "bin": { + "mkdirp": "bin/cmd.js" + } + }, + "node_modules/tslint/node_modules/semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/tsutils": { + "version": "2.29.0", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-2.29.0.tgz", + "integrity": "sha512-g5JVHCIJwzfISaXpXE1qvNalca5Jwob6FjI4AoPlqMusJ6ftFE7IkkFoMhVLRgK+4Kx3gkzb8UZK5t5yTTvEmA==", + "dependencies": { + "tslib": "^1.8.1" + }, + "peerDependencies": { + "typescript": ">=2.1.0 || >=2.1.0-dev || >=2.2.0-dev || >=2.3.0-dev || >=2.4.0-dev || >=2.5.0-dev || >=2.6.0-dev || >=2.7.0-dev || >=2.8.0-dev || >=2.9.0-dev || >= 3.0.0-dev || >= 3.1.0-dev" + } + }, + "node_modules/tty-browserify": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.0.tgz", + "integrity": "sha1-oVe6QC2iTpv5V/mqadUk7tQpAaY=" + }, + "node_modules/tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", + "dependencies": { + "safe-buffer": "^5.0.1" + }, + "engines": { + "node": "*" + } + }, + "node_modules/turf-along": { + "version": "3.0.12", + "resolved": "https://registry.npmjs.org/turf-along/-/turf-along-3.0.12.tgz", + "integrity": "sha1-5iK956S9E4wJZH1LFKoOpwBIXeY=", + "deprecated": "Turf packages are now namespaced: please use @turf/along instead", + "dependencies": { + "turf-bearing": "^3.0.12", + "turf-destination": "^3.0.12", + "turf-distance": "^3.0.12", + "turf-helpers": "^3.0.12" + } + }, + "node_modules/turf-area": { + "version": "3.0.12", + "resolved": "https://registry.npmjs.org/turf-area/-/turf-area-3.0.12.tgz", + "integrity": "sha1-m35Gnvn7VY/RR7sMIUgjJjvb8Tw=", + "deprecated": "Turf packages are now namespaced: please use @turf/area instead", + "dependencies": { + "geojson-area": "^0.2.1" + } + }, + "node_modules/turf-bbox": { + "version": "3.0.12", + "resolved": "https://registry.npmjs.org/turf-bbox/-/turf-bbox-3.0.12.tgz", + "integrity": "sha1-P6BhF8hEOGDsgKxg/V0vEyC/sb4=", + "deprecated": "Turf packages are now namespaced: please use @turf/bbox instead", + "dependencies": { + "turf-meta": "^3.0.12" + } + }, + "node_modules/turf-bbox-polygon": { + "version": "3.0.12", + "resolved": "https://registry.npmjs.org/turf-bbox-polygon/-/turf-bbox-polygon-3.0.12.tgz", + "integrity": "sha1-Mw3AuzgyLWFUXflmzmyA9oWs9PI=", + "deprecated": "Turf packages are now namespaced: please use @turf/bbox-polygon instead", + "dependencies": { + "turf-helpers": "^3.0.12" + } + }, + "node_modules/turf-bearing": { + "version": "3.0.12", + "resolved": "https://registry.npmjs.org/turf-bearing/-/turf-bearing-3.0.12.tgz", + "integrity": "sha1-ZfYJ3YUOc2THdxqm3th7DhkX/SA=", + "deprecated": "Turf packages are now namespaced: please use @turf/bearing instead", + "dependencies": { + "turf-invariant": "^3.0.12" + } + }, + "node_modules/turf-bezier": { + "version": "3.0.12", + "resolved": "https://registry.npmjs.org/turf-bezier/-/turf-bezier-3.0.12.tgz", + "integrity": "sha1-EC791KY7Jl7pyMFydjGSCzb03QI=", + "deprecated": "Turf packages are now namespaced: please use @turf/bezier instead", + "dependencies": { + "turf-helpers": "^3.0.12" + } + }, + "node_modules/turf-buffer": { + "version": "3.0.12", + "resolved": "https://registry.npmjs.org/turf-buffer/-/turf-buffer-3.0.12.tgz", + "integrity": "sha1-IIQP58aqZ7JL4cq3/8xagv1r2XE=", + "deprecated": "Turf packages are now namespaced: please use @turf/buffer instead", + "dependencies": { + "geojson-normalize": "0.0.0", + "jsts": "1.1.2", + "turf-combine": "^3.0.12", + "turf-helpers": "^3.0.12" + } + }, + "node_modules/turf-center": { + "version": "3.0.12", + "resolved": "https://registry.npmjs.org/turf-center/-/turf-center-3.0.12.tgz", + "integrity": "sha1-Rd1sFym7hnKR4+AC6cdQb4xEAZY=", + "deprecated": "Turf packages are now namespaced: please use @turf/center instead", + "dependencies": { + "turf-bbox": "^3.0.12", + "turf-helpers": "^3.0.12" + } + }, + "node_modules/turf-centroid": { + "version": "3.0.12", + "resolved": "https://registry.npmjs.org/turf-centroid/-/turf-centroid-3.0.12.tgz", + "integrity": "sha1-6u4NaYIEtX/DOZS7G8hnuNopP48=", + "deprecated": "Turf packages are now namespaced: please use @turf/centroid instead", + "dependencies": { + "turf-helpers": "^3.0.12", + "turf-meta": "^3.0.12" + } + }, + "node_modules/turf-circle": { + "version": "3.0.12", + "resolved": "https://registry.npmjs.org/turf-circle/-/turf-circle-3.0.12.tgz", + "integrity": "sha1-FAshy0lQ8tPLxw0t8BKTaGf1iTA=", + "deprecated": "Turf packages are now namespaced: please use @turf/circle instead", + "dependencies": { + "turf-destination": "^3.0.12", + "turf-helpers": "^3.0.12" + } + }, + "node_modules/turf-collect": { + "version": "3.0.12", + "resolved": "https://registry.npmjs.org/turf-collect/-/turf-collect-3.0.12.tgz", + "integrity": "sha1-bphtGnB9oxnMg+cjjQvN8Zqjx/I=", + "deprecated": "Turf packages are now namespaced: please use @turf/collect instead", + "dependencies": { + "turf-inside": "^3.0.12" + } + }, + "node_modules/turf-combine": { + "version": "3.0.12", + "resolved": "https://registry.npmjs.org/turf-combine/-/turf-combine-3.0.12.tgz", + "integrity": "sha1-FnB0bw/c4NHqiqain/5UONRGz3M=", + "deprecated": "Turf packages are now namespaced: please use @turf/combine instead", + "dependencies": { + "turf-meta": "^3.0.12" + } + }, + "node_modules/turf-concave": { + "version": "3.0.12", + "resolved": "https://registry.npmjs.org/turf-concave/-/turf-concave-3.0.12.tgz", + "integrity": "sha1-/KtgVpZbCoMZ9s2AJgEJXy/TqOs=", + "deprecated": "Turf packages are now namespaced: please use @turf/concave instead", + "dependencies": { + "turf-distance": "^3.0.12", + "turf-meta": "^3.0.12", + "turf-tin": "^3.0.12", + "turf-union": "^3.0.12" + } + }, + "node_modules/turf-convex": { + "version": "3.0.12", + "resolved": "https://registry.npmjs.org/turf-convex/-/turf-convex-3.0.12.tgz", + "integrity": "sha1-qI3cPiLRy2WHlqnIXTraO9Pso1c=", + "deprecated": "Turf packages are now namespaced: please use @turf/convex instead", + "dependencies": { + "convex-hull": "^1.0.3", + "turf-helpers": "^3.0.12", + "turf-meta": "^3.0.12" + } + }, + "node_modules/turf-destination": { + "version": "3.0.12", + "resolved": "https://registry.npmjs.org/turf-destination/-/turf-destination-3.0.12.tgz", + "integrity": "sha1-fdb7+X6G+DGibIPvLVovjR2KbeI=", + "deprecated": "Turf packages are now namespaced: please use @turf/destination instead", + "dependencies": { + "turf-helpers": "^3.0.12", + "turf-invariant": "^3.0.12" + } + }, + "node_modules/turf-difference": { + "version": "3.0.12", + "resolved": "https://registry.npmjs.org/turf-difference/-/turf-difference-3.0.12.tgz", + "integrity": "sha1-nD0NdjBCEAW4slt/Bo7Z77S8bqc=", + "deprecated": "Turf packages are now namespaced: please use @turf/difference instead", + "dependencies": { + "jsts": "1.1.2", + "turf-helpers": "^3.0.12" + } + }, + "node_modules/turf-distance": { + "version": "3.0.12", + "resolved": "https://registry.npmjs.org/turf-distance/-/turf-distance-3.0.12.tgz", + "integrity": "sha1-+5e4cF+s2ZOxReAUtBhiYQ7spEk=", + "deprecated": "Turf packages are now namespaced: please use @turf/distance instead", + "dependencies": { + "turf-helpers": "^3.0.12", + "turf-invariant": "^3.0.12" + } + }, + "node_modules/turf-envelope": { + "version": "3.0.12", + "resolved": "https://registry.npmjs.org/turf-envelope/-/turf-envelope-3.0.12.tgz", + "integrity": "sha1-lpIdJ4zIxmRpLjIOJUO5FAgNeGs=", + "deprecated": "Turf packages are now namespaced: please use @turf/envelope instead", + "dependencies": { + "turf-bbox": "^3.0.12", + "turf-bbox-polygon": "^3.0.12" + } + }, + "node_modules/turf-explode": { + "version": "3.0.12", + "resolved": "https://registry.npmjs.org/turf-explode/-/turf-explode-3.0.12.tgz", + "integrity": "sha1-xa4owoTNAGxWUR7H1AjEilQU7P4=", + "deprecated": "Turf packages are now namespaced: please use @turf/explode instead", + "dependencies": { + "turf-helpers": "^3.0.12", + "turf-meta": "^3.0.12" + } + }, + "node_modules/turf-flip": { + "version": "3.0.12", + "resolved": "https://registry.npmjs.org/turf-flip/-/turf-flip-3.0.12.tgz", + "integrity": "sha1-3rhoF3uf87sxDF1BqqxhqRVqPLs=", + "deprecated": "Turf packages are now namespaced: please use @turf/flip instead", + "dependencies": { + "turf-meta": "^3.0.12" + } + }, + "node_modules/turf-grid": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/turf-grid/-/turf-grid-1.0.1.tgz", + "integrity": "sha1-uQSrxWS5ObYnpmrBXrFuBTgpuA8=", + "dependencies": { + "turf-point": "^2.0.0" + } + }, + "node_modules/turf-helpers": { + "version": "3.0.12", + "resolved": "https://registry.npmjs.org/turf-helpers/-/turf-helpers-3.0.12.tgz", + "integrity": "sha1-3UJy50s618lu7LeuXFf+jspUS3s=", + "deprecated": "Turf packages are now namespaced: please use @turf/helpers instead" + }, + "node_modules/turf-hex-grid": { + "version": "3.0.12", + "resolved": "https://registry.npmjs.org/turf-hex-grid/-/turf-hex-grid-3.0.12.tgz", + "integrity": "sha1-BpjvZpAguzHY6cwgVtCr/K/ITo8=", + "deprecated": "Turf packages are now namespaced: please use @turf/hex-grid instead", + "dependencies": { + "turf-distance": "^3.0.12", + "turf-helpers": "^3.0.12" + } + }, + "node_modules/turf-inside": { + "version": "3.0.12", + "resolved": "https://registry.npmjs.org/turf-inside/-/turf-inside-3.0.12.tgz", + "integrity": "sha1-m6QPpu7WO+x+fYiqZCdiLE3wcGY=", + "deprecated": "Turf packages are now namespaced: please use @turf/inside instead", + "dependencies": { + "turf-invariant": "^3.0.12" + } + }, + "node_modules/turf-intersect": { + "version": "3.0.12", + "resolved": "https://registry.npmjs.org/turf-intersect/-/turf-intersect-3.0.12.tgz", + "integrity": "sha1-wNf7MFhDoZJ1ZwBXo50mixeDDYM=", + "deprecated": "Turf packages are now namespaced: please use @turf/intersect instead", + "dependencies": { + "jsts": "1.1.2" + } + }, + "node_modules/turf-invariant": { + "version": "3.0.12", + "resolved": "https://registry.npmjs.org/turf-invariant/-/turf-invariant-3.0.12.tgz", + "integrity": "sha1-O5UlOVOZHr2WLdNdT2cEwofejr4=", + "deprecated": "Turf packages are now namespaced: please use @turf/invariant instead" + }, + "node_modules/turf-isolines": { + "version": "3.0.12", + "resolved": "https://registry.npmjs.org/turf-isolines/-/turf-isolines-3.0.12.tgz", + "integrity": "sha1-ALIz3+LuvU7LR6lPySPG7OyJx6s=", + "deprecated": "Turf packages are now namespaced: please use @turf/isolines instead", + "dependencies": { + "turf-bbox": "^3.0.12", + "turf-grid": "1.0.1", + "turf-helpers": "^3.0.12", + "turf-inside": "^3.0.12", + "turf-planepoint": "^3.0.12", + "turf-square": "^3.0.12", + "turf-tin": "^3.0.12" + } + }, + "node_modules/turf-jsts": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/turf-jsts/-/turf-jsts-1.2.3.tgz", + "integrity": "sha512-Ja03QIJlPuHt4IQ2FfGex4F4JAr8m3jpaHbFbQrgwr7s7L6U8ocrHiF3J1+wf9jzhGKxvDeaCAnGDot8OjGFyA==" + }, + "node_modules/turf-kinks": { + "version": "3.0.12", + "resolved": "https://registry.npmjs.org/turf-kinks/-/turf-kinks-3.0.12.tgz", + "integrity": "sha1-6cmo26VyTZjyNQ/FveugaewzN1U=", + "deprecated": "Turf packages are now namespaced: please use @turf/kinks instead", + "dependencies": { + "turf-helpers": "^3.0.12" + } + }, + "node_modules/turf-line-distance": { + "version": "3.0.12", + "resolved": "https://registry.npmjs.org/turf-line-distance/-/turf-line-distance-3.0.12.tgz", + "integrity": "sha1-cQj1smkH97jC3Rs5l4Zt06YOj18=", + "deprecated": "Turf packages are now namespaced: please use @turf/line-distance instead", + "dependencies": { + "turf-distance": "^3.0.12", + "turf-helpers": "^3.0.12" + } + }, + "node_modules/turf-line-slice": { + "version": "3.0.12", + "resolved": "https://registry.npmjs.org/turf-line-slice/-/turf-line-slice-3.0.12.tgz", + "integrity": "sha1-9fGszJKtrmnqGsCynwdSmijd6RY=", + "deprecated": "Turf packages are now namespaced: please use @turf/line-slice instead", + "dependencies": { + "turf-bearing": "^3.0.12", + "turf-destination": "^3.0.12", + "turf-distance": "^3.0.12", + "turf-helpers": "^3.0.12", + "turf-point-on-line": "^3.0.12" + } + }, + "node_modules/turf-meta": { + "version": "3.0.12", + "resolved": "https://registry.npmjs.org/turf-meta/-/turf-meta-3.0.12.tgz", + "integrity": "sha1-CqmhyvgrKloI1U4IMLW1o/oOijg=", + "deprecated": "Turf packages are now namespaced: please use @turf/meta instead" + }, + "node_modules/turf-midpoint": { + "version": "3.0.12", + "resolved": "https://registry.npmjs.org/turf-midpoint/-/turf-midpoint-3.0.12.tgz", + "integrity": "sha1-sSdlroms3uhVb9XibJxfoEGgLL4=", + "deprecated": "Turf packages are now namespaced: please use @turf/midpoint instead", + "dependencies": { + "turf-bearing": "^3.0.12", + "turf-destination": "^3.0.12", + "turf-distance": "^3.0.12", + "turf-invariant": "^3.0.12" + } + }, + "node_modules/turf-nearest": { + "version": "3.0.12", + "resolved": "https://registry.npmjs.org/turf-nearest/-/turf-nearest-3.0.12.tgz", + "integrity": "sha1-cAIH9EQ/BQlvhs0kb5KfFw369G0=", + "deprecated": "Turf packages are now namespaced: please use @turf/nearest instead", + "dependencies": { + "turf-distance": "^3.0.12" + } + }, + "node_modules/turf-planepoint": { + "version": "3.0.12", + "resolved": "https://registry.npmjs.org/turf-planepoint/-/turf-planepoint-3.0.12.tgz", + "integrity": "sha1-LDeuDxf8sw22448NWe5sDdbKqa8=", + "deprecated": "Turf packages are now namespaced: please use @turf/planepoint instead" + }, + "node_modules/turf-point": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/turf-point/-/turf-point-2.0.1.tgz", + "integrity": "sha1-otzDCi0g9Ez1xicd97riwOIUYGk=", + "dependencies": { + "minimist": "^1.1.0" + }, + "bin": { + "turf-point": "bin/point.js" + } + }, + "node_modules/turf-point-grid": { + "version": "3.0.12", + "resolved": "https://registry.npmjs.org/turf-point-grid/-/turf-point-grid-3.0.12.tgz", + "integrity": "sha1-1gSXi+ELyeUzBq4CzvcJhDHbSXE=", + "deprecated": "Turf packages are now namespaced: please use @turf/point-grid instead", + "dependencies": { + "turf-distance": "^3.0.12", + "turf-helpers": "^3.0.12" + } + }, + "node_modules/turf-point-on-line": { + "version": "3.0.12", + "resolved": "https://registry.npmjs.org/turf-point-on-line/-/turf-point-on-line-3.0.12.tgz", + "integrity": "sha1-HYZjNU5wNy2xhj5iU+kEDEcSew8=", + "deprecated": "Turf packages are now namespaced: please use @turf/point-on-line instead", + "dependencies": { + "turf-bearing": "^3.0.12", + "turf-destination": "^3.0.12", + "turf-distance": "^3.0.12", + "turf-helpers": "^3.0.12" + } + }, + "node_modules/turf-point-on-surface": { + "version": "3.0.12", + "resolved": "https://registry.npmjs.org/turf-point-on-surface/-/turf-point-on-surface-3.0.12.tgz", + "integrity": "sha1-m+UFtrC6eOmFZQAd47OkJnEVJAo=", + "deprecated": "Turf packages are now namespaced: please use @turf/point-on-surface instead", + "dependencies": { + "turf-center": "^3.0.12", + "turf-distance": "^3.0.12", + "turf-explode": "^3.0.12", + "turf-helpers": "^3.0.12", + "turf-inside": "^3.0.12" + } + }, + "node_modules/turf-random": { + "version": "3.0.12", + "resolved": "https://registry.npmjs.org/turf-random/-/turf-random-3.0.12.tgz", + "integrity": "sha1-NNuxQcPx6urhQk/Wxeq6H2+5seg=", + "deprecated": "Turf packages are now namespaced: please use @turf/random instead", + "dependencies": { + "geojson-random": "^0.2.2" + } + }, + "node_modules/turf-sample": { + "version": "3.0.12", + "resolved": "https://registry.npmjs.org/turf-sample/-/turf-sample-3.0.12.tgz", + "integrity": "sha1-eUn4YgYSBH4TFMHO2H6ZwUJGPNI=", + "deprecated": "Turf packages are now namespaced: please use @turf/sample instead", + "dependencies": { + "turf-helpers": "^3.0.12" + } + }, + "node_modules/turf-simplify": { + "version": "3.0.12", + "resolved": "https://registry.npmjs.org/turf-simplify/-/turf-simplify-3.0.12.tgz", + "integrity": "sha1-heRDyLRqordSY4lETHOB2qKtGec=", + "deprecated": "Turf packages are now namespaced: please use @turf/simplify instead", + "dependencies": { + "simplify-js": "^1.2.1" + } + }, + "node_modules/turf-square": { + "version": "3.0.12", + "resolved": "https://registry.npmjs.org/turf-square/-/turf-square-3.0.12.tgz", + "integrity": "sha1-Gjix4PsF/+D8qkMYji83lCpRW2Q=", + "deprecated": "Turf packages are now namespaced: please use @turf/square instead", + "dependencies": { + "turf-distance": "^3.0.12", + "turf-helpers": "^3.0.12" + } + }, + "node_modules/turf-square-grid": { + "version": "3.0.12", + "resolved": "https://registry.npmjs.org/turf-square-grid/-/turf-square-grid-3.0.12.tgz", + "integrity": "sha1-PB2ArBRVbGgTtHi9oBJRLtS5Psg=", + "deprecated": "Turf packages are now namespaced: please use @turf/square-grid instead", + "dependencies": { + "turf-distance": "^3.0.12", + "turf-helpers": "^3.0.12" + } + }, + "node_modules/turf-tag": { + "version": "3.0.12", + "resolved": "https://registry.npmjs.org/turf-tag/-/turf-tag-3.0.12.tgz", + "integrity": "sha1-IoT/8Oih6Son1Kx/10cbPEjd0ag=", + "deprecated": "Turf packages are now namespaced: please use @turf/tag instead", + "dependencies": { + "turf-inside": "^3.0.12" + } + }, + "node_modules/turf-tesselate": { + "version": "3.0.12", + "resolved": "https://registry.npmjs.org/turf-tesselate/-/turf-tesselate-3.0.12.tgz", + "integrity": "sha1-QUdLe1s4ILzyc/tx4YlNjDzUDTU=", + "deprecated": "Turf packages are now namespaced: please use @turf/tesselate instead", + "dependencies": { + "earcut": "^2.0.0", + "turf-helpers": "^3.0.12" + } + }, + "node_modules/turf-tin": { + "version": "3.0.12", + "resolved": "https://registry.npmjs.org/turf-tin/-/turf-tin-3.0.12.tgz", + "integrity": "sha1-tlNGRHY6zhyd8kHJWNI4SFUlc4U=", + "deprecated": "Turf packages are now namespaced: please use @turf/tin instead", + "dependencies": { + "turf-helpers": "^3.0.12" + } + }, + "node_modules/turf-triangle-grid": { + "version": "3.0.12", + "resolved": "https://registry.npmjs.org/turf-triangle-grid/-/turf-triangle-grid-3.0.12.tgz", + "integrity": "sha1-gGR+V9r+CTRoeaKaGKDmKUrPEVk=", + "deprecated": "Turf packages are now namespaced: please use @turf/triangle-grid instead", + "dependencies": { + "turf-distance": "^3.0.12", + "turf-helpers": "^3.0.12" + } + }, + "node_modules/turf-union": { + "version": "3.0.12", + "resolved": "https://registry.npmjs.org/turf-union/-/turf-union-3.0.12.tgz", + "integrity": "sha1-3+0OVUC4woVeSZTBRiHjpgyCnI4=", + "deprecated": "Turf packages are now namespaced: please use @turf/union instead", + "dependencies": { + "jsts": "1.1.2" + } + }, + "node_modules/turf-within": { + "version": "3.0.12", + "resolved": "https://registry.npmjs.org/turf-within/-/turf-within-3.0.12.tgz", + "integrity": "sha1-937q83cjhWG3+xM4526dEph0H5Q=", + "deprecated": "Turf packages are now namespaced: please use @turf/within instead", + "dependencies": { + "turf-helpers": "^3.0.12", + "turf-inside": "^3.0.12" + } + }, + "node_modules/tweetnacl": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", + "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=" + }, + "node_modules/two-product": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/two-product/-/two-product-1.0.2.tgz", + "integrity": "sha1-Z9ldSyV6kh4stL16+VEfkIhSLqo=" + }, + "node_modules/two-sum": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/two-sum/-/two-sum-1.0.0.tgz", + "integrity": "sha1-MdPzIjnk9zHsqd+RVeKyl/AIq2Q=" + }, + "node_modules/type-check": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", + "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", + "dependencies": { + "prelude-ls": "~1.1.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=" + }, + "node_modules/typescript": { + "version": "3.9.9", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.9.9.tgz", + "integrity": "sha512-kdMjTiekY+z/ubJCATUPlRDl39vXYiMV9iyeMuEuXZh2we6zz80uovNN2WlAxmmdE/Z/YQe+EbOEXB5RHEED3w==", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=4.2.0" + } + }, + "node_modules/unbox-primitive": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.1.tgz", + "integrity": "sha512-tZU/3NqK3dA5gpE1KtyiJUrEB0lxnGkMFHptJ7q6ewdZ8s12QrODwNbhIJStmJkd1QDXa1NRA8aF2A1zk/Ypyw==", + "dependencies": { + "function-bind": "^1.1.1", + "has-bigints": "^1.0.1", + "has-symbols": "^1.0.2", + "which-boxed-primitive": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/uncss": { + "version": "0.17.3", + "resolved": "https://registry.npmjs.org/uncss/-/uncss-0.17.3.tgz", + "integrity": "sha512-ksdDWl81YWvF/X14fOSw4iu8tESDHFIeyKIeDrK6GEVTQvqJc1WlOEXqostNwOCi3qAj++4EaLsdAgPmUbEyog==", + "dependencies": { + "commander": "^2.20.0", + "glob": "^7.1.4", + "is-absolute-url": "^3.0.1", + "is-html": "^1.1.0", + "jsdom": "^14.1.0", + "lodash": "^4.17.15", + "postcss": "^7.0.17", + "postcss-selector-parser": "6.0.2", + "request": "^2.88.0" + }, + "bin": { + "uncss": "bin/uncss" + }, + "engines": { + "node": ">=6.0" + } + }, + "node_modules/uncss/node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==" + }, + "node_modules/uncss/node_modules/is-absolute-url": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-absolute-url/-/is-absolute-url-3.0.3.tgz", + "integrity": "sha512-opmNIX7uFnS96NtPmhWQgQx6/NYFgsUXYMllcfzwWKUMwfo8kku1TvE6hkNcH+Q1ts5cMVrsY7j0bxXQDciu9Q==", + "engines": { + "node": ">=8" + } + }, + "node_modules/uncss/node_modules/postcss": { + "version": "7.0.36", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.36.tgz", + "integrity": "sha512-BebJSIUMwJHRH0HAQoxN4u1CN86glsrwsW0q7T+/m44eXOUAxSNdHRkNZPYz5vVUbg17hFgOQDE7fZk7li3pZw==", + "dependencies": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, + "node_modules/uncss/node_modules/postcss-selector-parser": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.2.tgz", + "integrity": "sha512-36P2QR59jDTOAiIkqEprfJDsoNrvwFei3eCqKd1Y0tUsBimsq39BLp7RD+JWny3WgB1zGhJX8XVePwm9k4wdBg==", + "dependencies": { + "cssesc": "^3.0.0", + "indexes-of": "^1.0.1", + "uniq": "^1.0.1" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/uncss/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/uncss/node_modules/supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/underscore": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.7.0.tgz", + "integrity": "sha1-a7rwh3UA02vjTsqlhODbn+8DUgk=" + }, + "node_modules/underscore.deep": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/underscore.deep/-/underscore.deep-0.5.1.tgz", + "integrity": "sha1-ByZx9I1oc1w0Ij/P72PmnlJ2zCs=", + "engines": { + "node": ">=0.10.x" + }, + "peerDependencies": { + "underscore": "1.x" + } + }, + "node_modules/unicode-canonical-property-names-ecmascript": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-1.0.4.tgz", + "integrity": "sha512-jDrNnXWHd4oHiTZnx/ZG7gtUTVp+gCcTTKr8L0HjlwphROEW3+Him+IpvC+xcJEFegapiMZyZe02CyuOnRmbnQ==", + "engines": { + "node": ">=4" + } + }, + "node_modules/unicode-match-property-ecmascript": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-1.0.4.tgz", + "integrity": "sha512-L4Qoh15vTfntsn4P1zqnHulG0LdXgjSO035fEpdtp6YxXhMT51Q6vgM5lYdG/5X3MjS+k/Y9Xw4SFCY9IkR0rg==", + "dependencies": { + "unicode-canonical-property-names-ecmascript": "^1.0.4", + "unicode-property-aliases-ecmascript": "^1.0.4" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/unicode-match-property-value-ecmascript": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-1.2.0.tgz", + "integrity": "sha512-wjuQHGQVofmSJv1uVISKLE5zO2rNGzM/KCYZch/QQvez7C1hUhBIuZ701fYXExuufJFMPhv2SyL8CyoIfMLbIQ==", + "engines": { + "node": ">=4" + } + }, + "node_modules/unicode-property-aliases-ecmascript": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-1.1.0.tgz", + "integrity": "sha512-PqSoPh/pWetQ2phoj5RLiaqIk4kCNwoV3CI+LfGmWLKI3rE3kl1h59XpX2BjgDrmbxD9ARtQobPGU1SguCYuQg==", + "engines": { + "node": ">=4" + } + }, + "node_modules/unicode-trie": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/unicode-trie/-/unicode-trie-0.3.1.tgz", + "integrity": "sha1-1nHd3YkQGgi6w3tqUWEBBgIFIIU=", + "dependencies": { + "pako": "^0.2.5", + "tiny-inflate": "^1.0.0" + } + }, + "node_modules/union-find": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/union-find/-/union-find-1.0.2.tgz", + "integrity": "sha1-KSusQV5q06iVNdI3AQ20pTYoTlg=" + }, + "node_modules/union-value": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz", + "integrity": "sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==", + "dependencies": { + "arr-union": "^3.1.0", + "get-value": "^2.0.6", + "is-extendable": "^0.1.1", + "set-value": "^2.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/uniq": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/uniq/-/uniq-1.0.1.tgz", + "integrity": "sha1-sxxa6CVIRKOoKBVBzisEuGWnNP8=" + }, + "node_modules/uniqs": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/uniqs/-/uniqs-2.0.0.tgz", + "integrity": "sha1-/+3ks2slKQaW5uFl1KWe25mOawI=" + }, + "node_modules/universalify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", + "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/unquote": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/unquote/-/unquote-1.1.1.tgz", + "integrity": "sha1-j97XMk7G6IoP+LkF58CYzcCG1UQ=" + }, + "node_modules/unset-value": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz", + "integrity": "sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=", + "dependencies": { + "has-value": "^0.3.1", + "isobject": "^3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/unset-value/node_modules/has-value": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz", + "integrity": "sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=", + "dependencies": { + "get-value": "^2.0.3", + "has-values": "^0.1.4", + "isobject": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/unset-value/node_modules/has-value/node_modules/isobject": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", + "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", + "dependencies": { + "isarray": "1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/unset-value/node_modules/has-values": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz", + "integrity": "sha1-bWHeldkd/Km5oCCJrThL/49it3E=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/upath": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/upath/-/upath-1.2.0.tgz", + "integrity": "sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==", + "engines": { + "node": ">=4", + "yarn": "*" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/urix": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz", + "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=", + "deprecated": "Please see https://github.com/lydell/urix#deprecated" + }, + "node_modules/url": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/url/-/url-0.11.0.tgz", + "integrity": "sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE=", + "dependencies": { + "punycode": "1.3.2", + "querystring": "0.2.0" + } + }, + "node_modules/url/node_modules/punycode": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", + "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=" + }, + "node_modules/use": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz", + "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/util": { + "version": "0.11.1", + "resolved": "https://registry.npmjs.org/util/-/util-0.11.1.tgz", + "integrity": "sha512-HShAsny+zS2TZfaXxD9tYj4HQGlBezXZMZuM/S5PKLLoZkShZiGk9o5CzukI1LVHZvjdvZ2Sj1aW/Ndn2NB/HQ==", + "dependencies": { + "inherits": "2.0.3" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" + }, + "node_modules/util.promisify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/util.promisify/-/util.promisify-1.0.1.tgz", + "integrity": "sha512-g9JpC/3He3bm38zsLupWryXHoEcS22YHthuPQSJdMy6KNrzIRzWqcsHzD/WUnqe45whVou4VIsPew37DoXWNrA==", + "dependencies": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.2", + "has-symbols": "^1.0.1", + "object.getownpropertydescriptors": "^2.1.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/util/node_modules/inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" + }, + "node_modules/uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", + "deprecated": "Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.", + "bin": { + "uuid": "bin/uuid" + } + }, + "node_modules/v8-compile-cache": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz", + "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==" + }, + "node_modules/validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "dependencies": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "node_modules/vendors": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/vendors/-/vendors-1.0.4.tgz", + "integrity": "sha512-/juG65kTL4Cy2su4P8HjtkTxk6VmJDiOPBufWniqQ6wknac6jNiXS9vU+hO3wgusiyqWlzTbVHi0dyJqRONg3w==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/verror": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", + "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", + "engines": [ + "node >=0.6.0" + ], + "dependencies": { + "assert-plus": "^1.0.0", + "core-util-is": "1.0.2", + "extsprintf": "^1.2.0" + } + }, + "node_modules/vlq": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/vlq/-/vlq-0.2.3.tgz", + "integrity": "sha512-DRibZL6DsNhIgYQ+wNdWDL2SL3bKPlVrRiBqV5yuMm++op8W4kGFtaQfCs4KEJn0wBZcHVHJ3eoywX8983k1ow==" + }, + "node_modules/vm-browserify": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-1.1.2.tgz", + "integrity": "sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ==" + }, + "node_modules/w3c-hr-time": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz", + "integrity": "sha512-z8P5DvDNjKDoFIHK7q8r8lackT6l+jo/Ye3HOle7l9nICP9lf1Ci25fy9vHd0JOWewkIFzXIEig3TdKT7JQ5fQ==", + "dependencies": { + "browser-process-hrtime": "^1.0.0" + } + }, + "node_modules/w3c-xmlserializer": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-1.1.2.tgz", + "integrity": "sha512-p10l/ayESzrBMYWRID6xbuCKh2Fp77+sA0doRuGn4tTIMrrZVeqfpKjXHY+oDh3K4nLdPgNwMTVP6Vp4pvqbNg==", + "dependencies": { + "domexception": "^1.0.1", + "webidl-conversions": "^4.0.2", + "xml-name-validator": "^3.0.0" + } + }, + "node_modules/wcwidth": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", + "integrity": "sha1-8LDc+RW8X/FSivrbLA4XtTLaL+g=", + "dependencies": { + "defaults": "^1.0.3" + } + }, + "node_modules/webidl-conversions": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-4.0.2.tgz", + "integrity": "sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==" + }, + "node_modules/wgs84": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/wgs84/-/wgs84-0.0.0.tgz", + "integrity": "sha1-NP3FVZF7blfPKigu0ENxDASc3HY=" + }, + "node_modules/whatwg-encoding": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-1.0.5.tgz", + "integrity": "sha512-b5lim54JOPN9HtzvK9HFXvBma/rnfFeqsic0hSpjtDbVxR3dJKLc+KB4V6GgiGOvl7CY/KNh8rxSo9DKQrnUEw==", + "dependencies": { + "iconv-lite": "0.4.24" + } + }, + "node_modules/whatwg-mimetype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz", + "integrity": "sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g==" + }, + "node_modules/whatwg-url": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-7.1.0.tgz", + "integrity": "sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg==", + "dependencies": { + "lodash.sortby": "^4.7.0", + "tr46": "^1.0.1", + "webidl-conversions": "^4.0.2" + } + }, + "node_modules/which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "which": "bin/which" + } + }, + "node_modules/which-boxed-primitive": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", + "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", + "dependencies": { + "is-bigint": "^1.0.1", + "is-boolean-object": "^1.1.0", + "is-number-object": "^1.0.4", + "is-string": "^1.0.5", + "is-symbol": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-typed-array": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.4.tgz", + "integrity": "sha512-49E0SpUe90cjpoc7BOJwyPHRqSAd12c10Qm2amdEZrJPCY2NDxaW01zHITrem+rnETY3dwrbH3UUrUwagfCYDA==", + "dev": true, + "dependencies": { + "available-typed-arrays": "^1.0.2", + "call-bind": "^1.0.0", + "es-abstract": "^1.18.0-next.1", + "foreach": "^2.0.5", + "function-bind": "^1.1.1", + "has-symbols": "^1.0.1", + "is-typed-array": "^1.1.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/wide-align": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", + "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", + "dev": true, + "dependencies": { + "string-width": "^1.0.2 || 2" + } + }, + "node_modules/word-wrap": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", + "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/wordwrap": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.3.tgz", + "integrity": "sha1-o9XabNXAvAAI03I0u68b7WMFkQc=", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" + }, + "node_modules/write-file": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/write-file/-/write-file-1.0.0.tgz", + "integrity": "sha1-Xi2sf9QMYxZIkfoN8GC4EEsFgGk=", + "dev": true, + "dependencies": { + "is-buffer": "^1.1.4", + "mkdirp": "^0.5.1" + } + }, + "node_modules/ws": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/ws/-/ws-5.2.2.tgz", + "integrity": "sha512-jaHFD6PFv6UgoIVda6qZllptQsMlDEJkTQcybzzXDYM1XO9Y8em691FGMPmM46WGyLU4z9KMgQN+qrux/nhlHA==", + "dependencies": { + "async-limiter": "~1.0.0" + } + }, + "node_modules/xdiff": { + "version": "0.2.11", + "resolved": "https://registry.npmjs.org/xdiff/-/xdiff-0.2.11.tgz", + "integrity": "sha1-cj1SPhtvJlojPK/HeGBiLqXS2Mg=", + "dev": true, + "dependencies": { + "adiff": "~0.2.4" + }, + "engines": { + "node": "*" + } + }, + "node_modules/xml-name-validator": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-3.0.0.tgz", + "integrity": "sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw==" + }, + "node_modules/xmlchars": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", + "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==" + }, + "node_modules/xmldom": { + "version": "0.1.31", + "resolved": "https://registry.npmjs.org/xmldom/-/xmldom-0.1.31.tgz", + "integrity": "sha512-yS2uJflVQs6n+CyjHoaBmVSqIDevTAWrzMmjG1Gc7h1qQ7uVozNhEPJAwZXWyGQ/Gafo3fCwrcaokezLPupVyQ==", + "deprecated": "Deprecated due to CVE-2021-21366 resolved in 0.5.0", + "engines": { + "node": ">=0.1" + } + }, + "node_modules/xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "engines": { + "node": ">=0.4" + } + }, + "node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "node_modules/yaml": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", + "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", + "engines": { + "node": ">= 6" + } + }, + "node_modules/yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "engines": { + "node": ">=6" + } + } + }, "dependencies": { "@babel/code-frame": { "version": "7.12.13", @@ -978,14 +17407,6 @@ "to-fast-properties": "^2.0.0" } }, - "@fullhuman/postcss-purgecss": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/@fullhuman/postcss-purgecss/-/postcss-purgecss-3.1.3.tgz", - "integrity": "sha512-kwOXw8fZ0Lt1QmeOOrd+o4Ibvp4UTEBFQbzvWldjlKv5n+G9sXfIPn1hh63IQIL8K8vbvv1oYMJiIUbuy9bGaA==", - "requires": { - "purgecss": "^3.1.3" - } - }, "@iarna/toml": { "version": "2.2.5", "resolved": "https://registry.npmjs.org/@iarna/toml/-/toml-2.2.5.tgz", @@ -1292,87 +17713,6 @@ "physical-cpu-count": "^2.0.0" } }, - "@tailwindcss/postcss7-compat": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@tailwindcss/postcss7-compat/-/postcss7-compat-2.1.2.tgz", - "integrity": "sha512-bH2kw6uyqLnDMP8wzDUsis5ovrsRzfHEyiL1McADvqlW54g6y0KVHX1xzO7PH8Fl5s0Sq8vDOAp4+3V8MEcZ9g==", - "requires": { - "@fullhuman/postcss-purgecss": "^3.1.3", - "autoprefixer": "^9", - "bytes": "^3.0.0", - "chalk": "^4.1.0", - "chokidar": "^3.5.1", - "color": "^3.1.3", - "detective": "^5.2.0", - "didyoumean": "^1.2.1", - "dlv": "^1.1.3", - "fast-glob": "^3.2.5", - "fs-extra": "^9.1.0", - "html-tags": "^3.1.0", - "lodash": "^4.17.21", - "lodash.topath": "^4.5.2", - "modern-normalize": "^1.0.0", - "node-emoji": "^1.8.1", - "normalize-path": "^3.0.0", - "object-hash": "^2.1.1", - "parse-glob": "^3.0.4", - "postcss": "^7", - "postcss-functions": "^3", - "postcss-js": "^2", - "postcss-nested": "^4", - "postcss-selector-parser": "^6.0.4", - "postcss-value-parser": "^4.1.0", - "pretty-hrtime": "^1.0.3", - "quick-lru": "^5.1.1", - "reduce-css-calc": "^2.1.8", - "resolve": "^1.20.0" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", - "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, "@turf/along": { "version": "6.3.0", "resolved": "https://registry.npmjs.org/@turf/along/-/along-6.3.0.tgz", @@ -2727,8 +19067,7 @@ "@types/parse-json": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz", - "integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==", - "dev": true + "integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==" }, "@types/prompt-sync": { "version": "4.1.0", @@ -2763,22 +19102,6 @@ "integrity": "sha512-7NQmHra/JILCd1QqpSzl8+mJRc8ZHz3uDm8YV1Ks9IhK0epEiTw8aIErbvH9PI+6XbqhyIQy3462nEsn7UVzjQ==", "dev": true }, - "JSONStream": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-0.8.0.tgz", - "integrity": "sha1-78Ri1aW8lOwAf0siVxrNf28q4BM=", - "requires": { - "jsonparse": "0.0.5", - "through": "~2.2.7" - }, - "dependencies": { - "through": { - "version": "2.2.7", - "resolved": "https://registry.npmjs.org/through/-/through-2.2.7.tgz", - "integrity": "sha1-bo4hIAGR1OtqmfbwEN9Gqhxusr0=" - } - } - }, "abab": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.5.tgz", @@ -2825,6 +19148,12 @@ "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-7.2.0.tgz", "integrity": "sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==" }, + "adiff": { + "version": "0.2.13", + "resolved": "https://registry.npmjs.org/adiff/-/adiff-0.2.13.tgz", + "integrity": "sha1-3D3TL5RNl/J366WM5SmXrf8fdyg=", + "dev": true + }, "affine-hull": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/affine-hull/-/affine-hull-1.0.0.tgz", @@ -3022,27 +19351,22 @@ "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" }, - "at-least-node": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", - "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==" - }, "atob": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==" }, "autoprefixer": { - "version": "9.8.6", - "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-9.8.6.tgz", - "integrity": "sha512-XrvP4VVHdRBCdX1S3WXVD8+RyG9qeb1D5Sn1DeLiG2xfSpzellk5k54xbUERJ3M5DggQxes39UGOTP8CFrEGbg==", + "version": "10.3.5", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.3.5.tgz", + "integrity": "sha512-2H5kQSsyoOMdIehTzIt/sC9ZDIgWqlkG/dbevm9B9xQZ1TDPBHpNUDW5ENqqQQzuaBWEo75JkV0LJe+o5Lnr5g==", + "peer": true, "requires": { - "browserslist": "^4.12.0", - "caniuse-lite": "^1.0.30001109", - "colorette": "^1.2.1", + "browserslist": "^4.17.1", + "caniuse-lite": "^1.0.30001259", + "fraction.js": "^4.1.1", + "nanocolors": "^0.1.5", "normalize-range": "^0.1.2", - "num2fraction": "^1.2.2", - "postcss": "^7.0.32", "postcss-value-parser": "^4.1.0" } }, @@ -3425,15 +19749,15 @@ } }, "browserslist": { - "version": "4.16.6", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.16.6.tgz", - "integrity": "sha512-Wspk/PqO+4W9qp5iUTJsa1B/QrYn1keNCcEP5OvP7WBwT4KaDly0uONYmC6Xa3Z5IqnUgS0KcgLYu1l74x0ZXQ==", + "version": "4.17.1", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.17.1.tgz", + "integrity": "sha512-aLD0ZMDSnF4lUt4ZDNgqi5BUn9BZ7YdQdI/cYlILrhdSSZJLU9aNZoD5/NBmM4SK34APB2e83MOsRt1EnkuyaQ==", "requires": { - "caniuse-lite": "^1.0.30001219", - "colorette": "^1.2.2", - "electron-to-chromium": "^1.3.723", + "caniuse-lite": "^1.0.30001259", + "electron-to-chromium": "^1.3.846", "escalade": "^3.1.1", - "node-releases": "^1.1.71" + "nanocolors": "^0.1.5", + "node-releases": "^1.1.76" } }, "btoa": { @@ -3570,9 +19894,9 @@ } }, "caniuse-lite": { - "version": "1.0.30001223", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001223.tgz", - "integrity": "sha512-k/RYs6zc/fjbxTjaWZemeSmOjO0JJV+KguOBA3NwPup8uzxM1cMhR2BD9XmO86GuqaqTCO8CgkgH9Rz//vdDiA==" + "version": "1.0.30001259", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001259.tgz", + "integrity": "sha512-V7mQTFhjITxuk9zBpI6nYsiTXhcPe05l+364nZjK7MFK/E7ibvYBSAXr4YcA6oPR8j3ZLM/LN+lUqUVAQEUZFg==" }, "canvg": { "version": "3.0.7", @@ -3613,18 +19937,18 @@ } }, "chokidar": { - "version": "3.5.1", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.1.tgz", - "integrity": "sha512-9+s+Od+W0VJJzawDma/gvBNQqkTiqYTWLuZoyAsivsI4AaWTCzHG06/TMjsf1cYe9Cb97UCEhjz7HvnPk2p/tw==", + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.2.tgz", + "integrity": "sha512-ekGhOnNVPgT77r4K/U3GDhu+FQ2S8TnK/s2KbIGXi0SZWuwkZ2QNyfWdZW+TVfn84DpEP7rLeCt2UI6bJ8GwbQ==", "requires": { - "anymatch": "~3.1.1", + "anymatch": "~3.1.2", "braces": "~3.0.2", - "fsevents": "~2.3.1", - "glob-parent": "~5.1.0", + "fsevents": "~2.3.2", + "glob-parent": "~5.1.2", "is-binary-path": "~2.1.0", "is-glob": "~4.0.1", "normalize-path": "~3.0.0", - "readdirp": "~3.5.0" + "readdirp": "~3.6.0" } }, "chownr": { @@ -3734,19 +20058,14 @@ "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" }, "color-string": { - "version": "1.5.5", - "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.5.5.tgz", - "integrity": "sha512-jgIoum0OfQfq9Whcfc2z/VhCNcmQjWbey6qBX0vqt7YICflUmBCh9E9CiQD5GSJ+Uehixm3NUwHVhqUAWRivZg==", + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.6.0.tgz", + "integrity": "sha512-c/hGS+kRWJutUBEngKKmk4iH3sD59MBkoxVapS/0wgpCz2u7XsNloxknyvBhzwEs1IbV36D9PwqLPJ2DTu3vMA==", "requires": { "color-name": "^1.0.0", "simple-swizzle": "^0.2.2" } }, - "colorette": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/colorette/-/colorette-1.2.2.tgz", - "integrity": "sha512-MKGMzyfeuutC/ZJ1cba9NqcNpfeqMUcYmyF1ZFY6/Cn7CNSAKx6a+s48sqLqyAiZuaP2TcqMhoo+dlwFnVxT9w==" - }, "combined-stream": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", @@ -4010,6 +20329,31 @@ "requires": { "postcss": "^7.0.1", "timsort": "^0.3.0" + }, + "dependencies": { + "postcss": { + "version": "7.0.36", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.36.tgz", + "integrity": "sha512-BebJSIUMwJHRH0HAQoxN4u1CN86glsrwsW0q7T+/m44eXOUAxSNdHRkNZPYz5vVUbg17hFgOQDE7fZk7li3pZw==", + "requires": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + }, + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "requires": { + "has-flag": "^3.0.0" + } + } } }, "css-line-break": { @@ -4172,6 +20516,31 @@ "cssnano-preset-default": "^4.0.8", "is-resolvable": "^1.0.0", "postcss": "^7.0.0" + }, + "dependencies": { + "postcss": { + "version": "7.0.36", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.36.tgz", + "integrity": "sha512-BebJSIUMwJHRH0HAQoxN4u1CN86glsrwsW0q7T+/m44eXOUAxSNdHRkNZPYz5vVUbg17hFgOQDE7fZk7li3pZw==", + "requires": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + }, + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "requires": { + "has-flag": "^3.0.0" + } + } } }, "cssnano-preset-default": { @@ -4209,6 +20578,31 @@ "postcss-reduce-transforms": "^4.0.2", "postcss-svgo": "^4.0.3", "postcss-unique-selectors": "^4.0.1" + }, + "dependencies": { + "postcss": { + "version": "7.0.36", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.36.tgz", + "integrity": "sha512-BebJSIUMwJHRH0HAQoxN4u1CN86glsrwsW0q7T+/m44eXOUAxSNdHRkNZPYz5vVUbg17hFgOQDE7fZk7li3pZw==", + "requires": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + }, + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "requires": { + "has-flag": "^3.0.0" + } + } } }, "cssnano-util-get-arguments": { @@ -4227,6 +20621,31 @@ "integrity": "sha512-qLuYtWK2b2Dy55I8ZX3ky1Z16WYsx544Q0UWViebptpwn/xDBmog2TLg4f+DBMg1rJ6JDWtn96WHbOKDWt1WQA==", "requires": { "postcss": "^7.0.0" + }, + "dependencies": { + "postcss": { + "version": "7.0.36", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.36.tgz", + "integrity": "sha512-BebJSIUMwJHRH0HAQoxN4u1CN86glsrwsW0q7T+/m44eXOUAxSNdHRkNZPYz5vVUbg17hFgOQDE7fZk7li3pZw==", + "requires": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + }, + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "requires": { + "has-flag": "^3.0.0" + } + } } }, "cssnano-util-same-parent": { @@ -4492,6 +20911,12 @@ "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" }, + "detect-indent": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-6.1.0.tgz", + "integrity": "sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA==", + "dev": true + }, "detect-libc": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", @@ -4509,9 +20934,9 @@ } }, "didyoumean": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.1.tgz", - "integrity": "sha1-6S7f2tplN9SE1zwBcv0eugxJdv8=" + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", + "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==" }, "diff": { "version": "4.0.2", @@ -4662,9 +21087,9 @@ "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" }, "electron-to-chromium": { - "version": "1.3.727", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.727.tgz", - "integrity": "sha512-Mfz4FIB4FSvEwBpDfdipRIrwd6uo8gUDoRDF4QEYb4h4tSuI3ov594OrjU6on042UlFHouIJpClDODGkPcBSbg==" + "version": "1.3.846", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.846.tgz", + "integrity": "sha512-2jtSwgyiRzybHRxrc2nKI+39wH3AwQgn+sogQ+q814gv8hIFwrcZbV07Ea9f8AmK0ufPVZUvvAG1uZJ+obV4Jw==" }, "elliptic": { "version": "6.5.4", @@ -5007,16 +21432,15 @@ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" }, "fast-glob": { - "version": "3.2.5", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.5.tgz", - "integrity": "sha512-2DtFcgT68wiTTiwZ2hNdJfcHNke9XOfnwmBRWXhmeKM8rF0TGwmC/Qto3S7RoZKp5cilZbxzO5iTNTQsJ+EeDg==", + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.7.tgz", + "integrity": "sha512-rYGMRwip6lUMvYD3BTScMwT1HtAs2d71SMv66Vrxs0IekGZEjhM0pcMfjQPnknBt2zeCwQMEupiN02ZP4DiT1Q==", "requires": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.0", + "glob-parent": "^5.1.2", "merge2": "^1.3.0", - "micromatch": "^4.0.2", - "picomatch": "^2.2.1" + "micromatch": "^4.0.4" } }, "fast-json-stable-stringify": { @@ -5128,6 +21552,12 @@ "mime-types": "^2.1.12" } }, + "fraction.js": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.1.1.tgz", + "integrity": "sha512-MHOhvvxHTfRFpF1geTK9czMIZ6xclsEor2wkIGYYq+PxcQqT7vStJqjhe6S1TenZrMZzo+wlqOufBDVepUEgPg==", + "peer": true + }, "fragment-cache": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz", @@ -5153,17 +21583,6 @@ "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", "dev": true }, - "fs-extra": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", - "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", - "requires": { - "at-least-node": "^1.0.0", - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - } - }, "fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", @@ -5303,6 +21722,16 @@ "assert-plus": "^1.0.0" } }, + "git-json-merge": { + "version": "0.4.5", + "resolved": "https://registry.npmjs.org/git-json-merge/-/git-json-merge-0.4.5.tgz", + "integrity": "sha512-akVUhyzRtkXGe5uAcw3AijF/253RA7tAPdfHtKLawYAhDjuyP+Ebr1YvZUv+7Jyr41g+IVRpKPBd2h+m6AHNqQ==", + "dev": true, + "requires": { + "detect-indent": "^6.0.0", + "xdiff": "^0.2.11" + } + }, "github-from-package": { "version": "0.0.0", "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz", @@ -5322,38 +21751,6 @@ "path-is-absolute": "^1.0.0" } }, - "glob-base": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/glob-base/-/glob-base-0.3.0.tgz", - "integrity": "sha1-27Fk9iIbHAscz4Kuoyi0l98Oo8Q=", - "requires": { - "glob-parent": "^2.0.0", - "is-glob": "^2.0.0" - }, - "dependencies": { - "glob-parent": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-2.0.0.tgz", - "integrity": "sha1-gTg9ctsFT8zPUzbaqQLxgvbtuyg=", - "requires": { - "is-glob": "^2.0.0" - } - }, - "is-extglob": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", - "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=" - }, - "is-glob": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz", - "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=", - "requires": { - "is-extglob": "^1.0.0" - } - } - } - }, "glob-parent": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", @@ -5545,8 +21942,7 @@ "hosted-git-info": { "version": "2.8.9", "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", - "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", - "dev": true + "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==" }, "hsl-regex": { "version": "1.0.0", @@ -5839,7 +22235,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/import-cwd/-/import-cwd-3.0.0.tgz", "integrity": "sha512-4pnzH16plW+hgvRECbDWpQl3cqtvSofHWh44met7ESfZ8UZOWWddm8hEyDTqREJ9RbYHY8gi8DqmaelApoOGMg==", - "dev": true, "requires": { "import-from": "^3.0.0" } @@ -5857,7 +22252,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/import-from/-/import-from-3.0.0.tgz", "integrity": "sha512-CiuXOFFSzkU5x/CR0+z7T91Iht4CXgfCxVOFRhh2Zyhg5wOpWvvDLQUsWl+gcN+QscYBjez8hDCt85O7RLDttQ==", - "dev": true, "requires": { "resolve-from": "^5.0.0" }, @@ -5865,8 +22259,7 @@ "resolve-from": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", - "dev": true + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==" } } }, @@ -6046,11 +22439,6 @@ "resolved": "https://registry.npmjs.org/is-directory/-/is-directory-0.3.1.tgz", "integrity": "sha1-YTObbyR1/Hcv2cnYP1yFddwVSuE=" }, - "is-dotfile": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/is-dotfile/-/is-dotfile-1.0.3.tgz", - "integrity": "sha1-pqLzL/0t+wT1yiXs0Pa4PPeYoeE=" - }, "is-extendable": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", @@ -6370,8 +22758,7 @@ "json-parse-even-better-errors": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", - "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", - "dev": true + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==" }, "json-schema": { "version": "0.2.3", @@ -6410,6 +22797,22 @@ "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-0.0.5.tgz", "integrity": "sha1-MwVCrT8KZUZlt3jz6y2an6UHrGQ=" }, + "JSONStream": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-0.8.0.tgz", + "integrity": "sha1-78Ri1aW8lOwAf0siVxrNf28q4BM=", + "requires": { + "jsonparse": "0.0.5", + "through": "~2.2.7" + }, + "dependencies": { + "through": { + "version": "2.2.7", + "resolved": "https://registry.npmjs.org/through/-/through-2.2.7.tgz", + "integrity": "sha1-bo4hIAGR1OtqmfbwEN9Gqhxusr0=" + } + } + }, "jsonwebtoken": { "version": "8.5.1", "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-8.5.1.tgz", @@ -8052,7 +24455,8 @@ "leaflet.markercluster": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/leaflet.markercluster/-/leaflet.markercluster-1.5.0.tgz", - "integrity": "sha512-Fvf/cq4o806mJL50n+fZW9+QALDDLPvt7vuAjlD2vfnxx3srMDs2vWINJze4nKYJYRY45OC6tM/669C3pLwMCA==" + "integrity": "sha512-Fvf/cq4o806mJL50n+fZW9+QALDDLPvt7vuAjlD2vfnxx3srMDs2vWINJze4nKYJYRY45OC6tM/669C3pLwMCA==", + "requires": {} }, "levn": { "version": "0.3.0", @@ -8080,8 +24484,7 @@ "lilconfig": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.0.3.tgz", - "integrity": "sha512-EHKqr/+ZvdKCifpNrJCKxBTgk5XupZA3y/aCPY9mxfgBzmgh93Mt/WqjjQ38oMxXuvDokaKiM3lAgvSH2sjtHg==", - "dev": true + "integrity": "sha512-EHKqr/+ZvdKCifpNrJCKxBTgk5XupZA3y/aCPY9mxfgBzmgh93Mt/WqjjQ38oMxXuvDokaKiM3lAgvSH2sjtHg==" }, "lineclip": { "version": "1.1.5", @@ -8091,8 +24494,7 @@ "lines-and-columns": { "version": "1.1.6", "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.1.6.tgz", - "integrity": "sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA=", - "dev": true + "integrity": "sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA=" }, "load-json-file": { "version": "1.1.0", @@ -8178,11 +24580,6 @@ "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz", "integrity": "sha1-7dFMgk4sycHgsKG0K7UhBRakJDg=" }, - "lodash.toarray": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/lodash.toarray/-/lodash.toarray-4.4.0.tgz", - "integrity": "sha1-JMS/zWsvuji/0FlNsRedjptlZWE=" - }, "lodash.topath": { "version": "4.5.2", "resolved": "https://registry.npmjs.org/lodash.topath/-/lodash.topath-4.5.2.tgz", @@ -8284,6 +24681,11 @@ "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.4.tgz", "integrity": "sha512-iV3XNKw06j5Q7mi6h+9vbx23Tv7JkjEVgKHW4pimwyDGWm0OIQntJJ+u1C6mg6mK1EaTv42XQ7w76yuzH7M2cA==" }, + "memorystream": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/memorystream/-/memorystream-0.3.1.tgz", + "integrity": "sha1-htcJCzDORV1j+64S3aUaR93K+bI=" + }, "meow": { "version": "3.7.0", "resolved": "https://registry.npmjs.org/meow/-/meow-3.7.0.tgz", @@ -8524,11 +24926,15 @@ "integrity": "sha512-M2ufzIiINKCuDfBSAUr1vWQ+vuVcA9kqx8JJUsbQi6yf1uGRyb7HfpdfUr5qLXf3B/t8dPvcjhKMmlfnP47EzQ==", "optional": true }, + "nanocolors": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/nanocolors/-/nanocolors-0.1.6.tgz", + "integrity": "sha512-2pvTw6vYRaBLGir2xR7MxaJtyWkrn+C53EpW8yPotG+pdAwBvt0Xwk4VJ6VHLY0aLthVZPvDfm9TdZvrvAm5UQ==" + }, "nanoid": { - "version": "3.1.23", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.23.tgz", - "integrity": "sha512-FiB0kzdP0FFVGDKlRLEQ1BgDzU87dy5NnzjeW9YZNt+/c3+q82EQDUwniSAUxp/F0gFNI1ZhKU1FqYsMuqZVnw==", - "dev": true + "version": "3.1.25", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.25.tgz", + "integrity": "sha512-rdwtIXaXCLFAQbnfqDRnI6jaRHp9fTcYBjtFKE8eezcZ7LuLjhUaQGNeMXf1HmRoCH32CLz6XwX0TtxEOS/A3Q==" }, "nanomatch": { "version": "1.2.13", @@ -8582,11 +24988,11 @@ "integrity": "sha512-ibPK3iA+vaY1eEjESkQkM0BbCqFOaZMiXRTtdB0u7b4djtY6JnsjvPdUHVMg6xQt3B8fpTTWHI9A+ADjM9frzg==" }, "node-emoji": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/node-emoji/-/node-emoji-1.10.0.tgz", - "integrity": "sha512-Yt3384If5H6BYGVHiHwTL+99OzJKHhgp82S8/dktEK73T26BazdgZ4JZh92xSVtGNJvz9UbXdNAc5hcrXV42vw==", + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/node-emoji/-/node-emoji-1.11.0.tgz", + "integrity": "sha512-wo2DpQkQp7Sjm2A0cq+sN7EHKO6Sl0ctXeBdFZrL9T9+UywORbufTcTZxom8YqpLQt/FqNMUkOpkZrJVYSKD3A==", "requires": { - "lodash.toarray": "^4.4.0" + "lodash": "^4.17.21" } }, "node-forge": { @@ -8656,15 +25062,14 @@ } }, "node-releases": { - "version": "1.1.71", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.71.tgz", - "integrity": "sha512-zR6HoT6LrLCRBwukmrVbHv0EpEQjksO6GmFcZQQuCAy139BEsoVKPYnf3jongYW83fAa1torLGYwxxky/p28sg==" + "version": "1.1.76", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.76.tgz", + "integrity": "sha512-9/IECtNr8dXNmPWmFXepT0/7o5eolGesHUa3mtr0KlgnCvnZxwh2qensKL42JJY2vQKC3nIBXetFAqR+PW1CmA==" }, "normalize-package-data": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", - "dev": true, "requires": { "hosted-git-info": "^2.1.4", "resolve": "^1.10.0", @@ -8675,8 +25080,7 @@ "semver": { "version": "5.7.1", "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "dev": true + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" } } }, @@ -8688,13 +25092,71 @@ "normalize-range": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", - "integrity": "sha1-LRDAa9/TEuqXd2laTShDlFa3WUI=" + "integrity": "sha1-LRDAa9/TEuqXd2laTShDlFa3WUI=", + "peer": true }, "normalize-url": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-3.3.0.tgz", "integrity": "sha512-U+JJi7duF1o+u2pynbp2zXDW2/PADgC30f0GsHZtRh+HOcXHnw137TrNlyxxRvWW5fjKd3bcLHPxofWuCjaeZg==" }, + "npm-run-all": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/npm-run-all/-/npm-run-all-4.1.5.tgz", + "integrity": "sha512-Oo82gJDAVcaMdi3nuoKFavkIHBRVqQ1qvMb+9LHk/cF4P6B2m8aP04hGf7oL6wZ9BuGwX1onlLhpuoofSyoQDQ==", + "requires": { + "ansi-styles": "^3.2.1", + "chalk": "^2.4.1", + "cross-spawn": "^6.0.5", + "memorystream": "^0.3.1", + "minimatch": "^3.0.4", + "pidtree": "^0.3.0", + "read-pkg": "^3.0.0", + "shell-quote": "^1.6.1", + "string.prototype.padend": "^3.0.0" + }, + "dependencies": { + "load-json-file": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", + "integrity": "sha1-L19Fq5HjMhYjT9U62rZo607AmTs=", + "requires": { + "graceful-fs": "^4.1.2", + "parse-json": "^4.0.0", + "pify": "^3.0.0", + "strip-bom": "^3.0.0" + } + }, + "path-type": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", + "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", + "requires": { + "pify": "^3.0.0" + } + }, + "pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=" + }, + "read-pkg": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", + "integrity": "sha1-nLxoaXj+5l0WwA4rGcI3/Pbjg4k=", + "requires": { + "load-json-file": "^4.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^3.0.0" + } + }, + "strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=" + } + } + }, "npmlog": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz", @@ -8715,11 +25177,6 @@ "boolbase": "~1.0.0" } }, - "num2fraction": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/num2fraction/-/num2fraction-1.2.2.tgz", - "integrity": "sha1-b2gragJ6Tp3fpFZM0lidHU5mnt4=" - }, "number-is-nan": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", @@ -8770,9 +25227,9 @@ } }, "object-hash": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-2.1.1.tgz", - "integrity": "sha512-VOJmgmS+7wvXf8CjbQmimtCnEx3IAoLxI3fp2fbWehxrWBcAQFbk+vcwb6vzR0VZv/eNCJ/27j151ZTwqW/JeQ==" + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-2.2.0.tgz", + "integrity": "sha512-gScRMn0bS5fH+IuwyIFgnh9zBdo4DV+6GhygmWM9HyNJSgS0hScp1f5vjtm7oIIOiT9trXrShAkLFSc2IqKNgw==" }, "object-inspect": { "version": "1.4.1", @@ -8954,10 +25411,10 @@ "requires": { "@mapbox/geojson-rewind": "0.4.0", "@types/geojson": "^1.0.2", - "JSONStream": "0.8.0", "concat-stream": "2.0.0", "geojson-numeric": "0.2.1", "htmlparser2": "3.5.1", + "JSONStream": "0.8.0", "optimist": "~0.3.5", "osm-polygon-features": "^0.9.1", "tiny-osmpbf": "^0.1.0", @@ -9171,6 +25628,16 @@ "to-regex": "^3.0.2" } }, + "postcss": { + "version": "7.0.36", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.36.tgz", + "integrity": "sha512-BebJSIUMwJHRH0HAQoxN4u1CN86glsrwsW0q7T+/m44eXOUAxSNdHRkNZPYz5vVUbg17hFgOQDE7fZk7li3pZw==", + "requires": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + } + }, "postcss-value-parser": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", @@ -9186,6 +25653,14 @@ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" }, + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "requires": { + "has-flag": "^3.0.0" + } + }, "to-regex-range": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", @@ -9201,7 +25676,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", - "dev": true, "requires": { "callsites": "^3.0.0" }, @@ -9209,8 +25683,7 @@ "callsites": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "dev": true + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==" } } }, @@ -9226,32 +25699,6 @@ "safe-buffer": "^5.1.1" } }, - "parse-glob": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/parse-glob/-/parse-glob-3.0.4.tgz", - "integrity": "sha1-ssN2z7EfNVE7rdFz7wu246OIORw=", - "requires": { - "glob-base": "^0.3.0", - "is-dotfile": "^1.0.0", - "is-extglob": "^1.0.0", - "is-glob": "^2.0.0" - }, - "dependencies": { - "is-extglob": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", - "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=" - }, - "is-glob": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz", - "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=", - "requires": { - "is-extglob": "^1.0.0" - } - } - } - }, "parse-json": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", @@ -9357,6 +25804,11 @@ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.0.tgz", "integrity": "sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==" }, + "pidtree": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/pidtree/-/pidtree-0.3.1.tgz", + "integrity": "sha512-qQbW94hLHEqCg7nhby4yRC7G2+jYHY4Rguc2bjw7Uug4GIJuu1tvf2uHaZv5Q8zdt+WKJ6qK1FOI6amaWUo5FA==" + }, "pify": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", @@ -9394,15 +25846,35 @@ "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=" }, "postcss": { - "version": "7.0.36", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.36.tgz", - "integrity": "sha512-BebJSIUMwJHRH0HAQoxN4u1CN86glsrwsW0q7T+/m44eXOUAxSNdHRkNZPYz5vVUbg17hFgOQDE7fZk7li3pZw==", + "version": "8.3.7", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.3.7.tgz", + "integrity": "sha512-9SaY7nnyQ63/WittqZYAvkkYPyKxchMKH71UDzeTmWuLSvxTRpeEeABZAzlCi55cuGcoFyoV/amX2BdsafQidQ==", "requires": { - "chalk": "^2.4.2", - "source-map": "^0.6.1", - "supports-color": "^6.1.0" + "nanocolors": "^0.1.5", + "nanoid": "^3.1.25", + "source-map-js": "^0.6.2" + } + }, + "postcss-calc": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/postcss-calc/-/postcss-calc-7.0.5.tgz", + "integrity": "sha512-1tKHutbGtLtEZF6PT4JSihCHfIVldU72mZ8SdZHIYriIZ9fh9k9aWSppaT8rHsyI3dX+KSR+W+Ix9BMY3AODrg==", + "requires": { + "postcss": "^7.0.27", + "postcss-selector-parser": "^6.0.2", + "postcss-value-parser": "^4.0.2" }, "dependencies": { + "postcss": { + "version": "7.0.36", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.36.tgz", + "integrity": "sha512-BebJSIUMwJHRH0HAQoxN4u1CN86glsrwsW0q7T+/m44eXOUAxSNdHRkNZPYz5vVUbg17hFgOQDE7fZk7li3pZw==", + "requires": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + } + }, "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", @@ -9418,16 +25890,6 @@ } } }, - "postcss-calc": { - "version": "7.0.5", - "resolved": "https://registry.npmjs.org/postcss-calc/-/postcss-calc-7.0.5.tgz", - "integrity": "sha512-1tKHutbGtLtEZF6PT4JSihCHfIVldU72mZ8SdZHIYriIZ9fh9k9aWSppaT8rHsyI3dX+KSR+W+Ix9BMY3AODrg==", - "requires": { - "postcss": "^7.0.27", - "postcss-selector-parser": "^6.0.2", - "postcss-value-parser": "^4.0.2" - } - }, "postcss-colormin": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/postcss-colormin/-/postcss-colormin-4.0.3.tgz", @@ -9440,10 +25902,33 @@ "postcss-value-parser": "^3.0.0" }, "dependencies": { + "postcss": { + "version": "7.0.36", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.36.tgz", + "integrity": "sha512-BebJSIUMwJHRH0HAQoxN4u1CN86glsrwsW0q7T+/m44eXOUAxSNdHRkNZPYz5vVUbg17hFgOQDE7fZk7li3pZw==", + "requires": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + } + }, "postcss-value-parser": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==" + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + }, + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "requires": { + "has-flag": "^3.0.0" + } } } }, @@ -9455,65 +25940,15 @@ "postcss": "^7.0.0", "postcss-value-parser": "^3.0.0" }, - "dependencies": { - "postcss-value-parser": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", - "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==" - } - } - }, - "postcss-discard-comments": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-4.0.2.tgz", - "integrity": "sha512-RJutN259iuRf3IW7GZyLM5Sw4GLTOH8FmsXBnv8Ab/Tc2k4SR4qbV4DNbyyY4+Sjo362SyDmW2DQ7lBSChrpkg==", - "requires": { - "postcss": "^7.0.0" - } - }, - "postcss-discard-duplicates": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/postcss-discard-duplicates/-/postcss-discard-duplicates-4.0.2.tgz", - "integrity": "sha512-ZNQfR1gPNAiXZhgENFfEglF93pciw0WxMkJeVmw8eF+JZBbMD7jp6C67GqJAXVZP2BWbOztKfbsdmMp/k8c6oQ==", - "requires": { - "postcss": "^7.0.0" - } - }, - "postcss-discard-empty": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/postcss-discard-empty/-/postcss-discard-empty-4.0.1.tgz", - "integrity": "sha512-B9miTzbznhDjTfjvipfHoqbWKwd0Mj+/fL5s1QOz06wufguil+Xheo4XpOnc4NqKYBCNqqEzgPv2aPBIJLox0w==", - "requires": { - "postcss": "^7.0.0" - } - }, - "postcss-discard-overridden": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/postcss-discard-overridden/-/postcss-discard-overridden-4.0.1.tgz", - "integrity": "sha512-IYY2bEDD7g1XM1IDEsUT4//iEYCxAmP5oDSFMVU/JVvT7gh+l4fmjciLqGgwjdWpQIdb0Che2VX00QObS5+cTg==", - "requires": { - "postcss": "^7.0.0" - } - }, - "postcss-functions": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/postcss-functions/-/postcss-functions-3.0.0.tgz", - "integrity": "sha1-DpTQFERwCkgd4g3k1V+yZAVkJQ4=", - "requires": { - "glob": "^7.1.2", - "object-assign": "^4.1.1", - "postcss": "^6.0.9", - "postcss-value-parser": "^3.3.0" - }, "dependencies": { "postcss": { - "version": "6.0.23", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.23.tgz", - "integrity": "sha512-soOk1h6J3VMTZtVeVpv15/Hpdl2cBLX3CAw4TAbkpTJiNPk9YP/zWcD1ND+xEtvyuuvKzbxliTOIyvkSeSJ6ag==", + "version": "7.0.36", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.36.tgz", + "integrity": "sha512-BebJSIUMwJHRH0HAQoxN4u1CN86glsrwsW0q7T+/m44eXOUAxSNdHRkNZPYz5vVUbg17hFgOQDE7fZk7li3pZw==", "requires": { - "chalk": "^2.4.1", + "chalk": "^2.4.2", "source-map": "^0.6.1", - "supports-color": "^5.4.0" + "supports-color": "^6.1.0" } }, "postcss-value-parser": { @@ -9525,27 +25960,147 @@ "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + }, + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "requires": { + "has-flag": "^3.0.0" + } } } }, - "postcss-js": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-2.0.3.tgz", - "integrity": "sha512-zS59pAk3deu6dVHyrGqmC3oDXBdNdajk4k1RyxeVXCrcEDBUBHoIhE4QTsmhxgzXxsaqFDAkUZfmMa5f/N/79w==", + "postcss-discard-comments": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-4.0.2.tgz", + "integrity": "sha512-RJutN259iuRf3IW7GZyLM5Sw4GLTOH8FmsXBnv8Ab/Tc2k4SR4qbV4DNbyyY4+Sjo362SyDmW2DQ7lBSChrpkg==", "requires": { - "camelcase-css": "^2.0.1", - "postcss": "^7.0.18" + "postcss": "^7.0.0" + }, + "dependencies": { + "postcss": { + "version": "7.0.36", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.36.tgz", + "integrity": "sha512-BebJSIUMwJHRH0HAQoxN4u1CN86glsrwsW0q7T+/m44eXOUAxSNdHRkNZPYz5vVUbg17hFgOQDE7fZk7li3pZw==", + "requires": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + }, + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "requires": { + "has-flag": "^3.0.0" + } + } } }, - "postcss-load-config": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-3.1.0.tgz", - "integrity": "sha512-ipM8Ds01ZUophjDTQYSVP70slFSYg3T0/zyfII5vzhN6V57YSxMgG5syXuwi5VtS8wSf3iL30v0uBdoIVx4Q0g==", - "dev": true, + "postcss-discard-duplicates": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-discard-duplicates/-/postcss-discard-duplicates-4.0.2.tgz", + "integrity": "sha512-ZNQfR1gPNAiXZhgENFfEglF93pciw0WxMkJeVmw8eF+JZBbMD7jp6C67GqJAXVZP2BWbOztKfbsdmMp/k8c6oQ==", "requires": { - "import-cwd": "^3.0.0", - "lilconfig": "^2.0.3", - "yaml": "^1.10.2" + "postcss": "^7.0.0" + }, + "dependencies": { + "postcss": { + "version": "7.0.36", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.36.tgz", + "integrity": "sha512-BebJSIUMwJHRH0HAQoxN4u1CN86glsrwsW0q7T+/m44eXOUAxSNdHRkNZPYz5vVUbg17hFgOQDE7fZk7li3pZw==", + "requires": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + }, + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "postcss-discard-empty": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-discard-empty/-/postcss-discard-empty-4.0.1.tgz", + "integrity": "sha512-B9miTzbznhDjTfjvipfHoqbWKwd0Mj+/fL5s1QOz06wufguil+Xheo4XpOnc4NqKYBCNqqEzgPv2aPBIJLox0w==", + "requires": { + "postcss": "^7.0.0" + }, + "dependencies": { + "postcss": { + "version": "7.0.36", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.36.tgz", + "integrity": "sha512-BebJSIUMwJHRH0HAQoxN4u1CN86glsrwsW0q7T+/m44eXOUAxSNdHRkNZPYz5vVUbg17hFgOQDE7fZk7li3pZw==", + "requires": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + }, + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "postcss-discard-overridden": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-discard-overridden/-/postcss-discard-overridden-4.0.1.tgz", + "integrity": "sha512-IYY2bEDD7g1XM1IDEsUT4//iEYCxAmP5oDSFMVU/JVvT7gh+l4fmjciLqGgwjdWpQIdb0Che2VX00QObS5+cTg==", + "requires": { + "postcss": "^7.0.0" + }, + "dependencies": { + "postcss": { + "version": "7.0.36", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.36.tgz", + "integrity": "sha512-BebJSIUMwJHRH0HAQoxN4u1CN86glsrwsW0q7T+/m44eXOUAxSNdHRkNZPYz5vVUbg17hFgOQDE7fZk7li3pZw==", + "requires": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + }, + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "requires": { + "has-flag": "^3.0.0" + } + } } }, "postcss-merge-longhand": { @@ -9559,10 +26114,33 @@ "stylehacks": "^4.0.0" }, "dependencies": { + "postcss": { + "version": "7.0.36", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.36.tgz", + "integrity": "sha512-BebJSIUMwJHRH0HAQoxN4u1CN86glsrwsW0q7T+/m44eXOUAxSNdHRkNZPYz5vVUbg17hFgOQDE7fZk7li3pZw==", + "requires": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + } + }, "postcss-value-parser": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==" + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + }, + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "requires": { + "has-flag": "^3.0.0" + } } } }, @@ -9579,6 +26157,16 @@ "vendors": "^1.0.0" }, "dependencies": { + "postcss": { + "version": "7.0.36", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.36.tgz", + "integrity": "sha512-BebJSIUMwJHRH0HAQoxN4u1CN86glsrwsW0q7T+/m44eXOUAxSNdHRkNZPYz5vVUbg17hFgOQDE7fZk7li3pZw==", + "requires": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + } + }, "postcss-selector-parser": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-3.1.2.tgz", @@ -9588,6 +26176,19 @@ "indexes-of": "^1.0.1", "uniq": "^1.0.1" } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + }, + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "requires": { + "has-flag": "^3.0.0" + } } } }, @@ -9600,10 +26201,33 @@ "postcss-value-parser": "^3.0.0" }, "dependencies": { + "postcss": { + "version": "7.0.36", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.36.tgz", + "integrity": "sha512-BebJSIUMwJHRH0HAQoxN4u1CN86glsrwsW0q7T+/m44eXOUAxSNdHRkNZPYz5vVUbg17hFgOQDE7fZk7li3pZw==", + "requires": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + } + }, "postcss-value-parser": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==" + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + }, + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "requires": { + "has-flag": "^3.0.0" + } } } }, @@ -9618,10 +26242,33 @@ "postcss-value-parser": "^3.0.0" }, "dependencies": { + "postcss": { + "version": "7.0.36", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.36.tgz", + "integrity": "sha512-BebJSIUMwJHRH0HAQoxN4u1CN86glsrwsW0q7T+/m44eXOUAxSNdHRkNZPYz5vVUbg17hFgOQDE7fZk7li3pZw==", + "requires": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + } + }, "postcss-value-parser": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==" + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + }, + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "requires": { + "has-flag": "^3.0.0" + } } } }, @@ -9638,10 +26285,33 @@ "uniqs": "^2.0.0" }, "dependencies": { + "postcss": { + "version": "7.0.36", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.36.tgz", + "integrity": "sha512-BebJSIUMwJHRH0HAQoxN4u1CN86glsrwsW0q7T+/m44eXOUAxSNdHRkNZPYz5vVUbg17hFgOQDE7fZk7li3pZw==", + "requires": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + } + }, "postcss-value-parser": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==" + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + }, + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "requires": { + "has-flag": "^3.0.0" + } } } }, @@ -9656,6 +26326,16 @@ "postcss-selector-parser": "^3.0.0" }, "dependencies": { + "postcss": { + "version": "7.0.36", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.36.tgz", + "integrity": "sha512-BebJSIUMwJHRH0HAQoxN4u1CN86glsrwsW0q7T+/m44eXOUAxSNdHRkNZPYz5vVUbg17hFgOQDE7fZk7li3pZw==", + "requires": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + } + }, "postcss-selector-parser": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-3.1.2.tgz", @@ -9665,6 +26345,19 @@ "indexes-of": "^1.0.1", "uniq": "^1.0.1" } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + }, + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "requires": { + "has-flag": "^3.0.0" + } } } }, @@ -9771,21 +26464,37 @@ } } }, - "postcss-nested": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-4.2.3.tgz", - "integrity": "sha512-rOv0W1HquRCamWy2kFl3QazJMMe1ku6rCFoAAH+9AcxdbpDeBr6k968MLWuLjvjMcGEip01ak09hKOEgpK9hvw==", - "requires": { - "postcss": "^7.0.32", - "postcss-selector-parser": "^6.0.2" - } - }, "postcss-normalize-charset": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/postcss-normalize-charset/-/postcss-normalize-charset-4.0.1.tgz", "integrity": "sha512-gMXCrrlWh6G27U0hF3vNvR3w8I1s2wOBILvA87iNXaPvSNo5uZAMYsZG7XjCUf1eVxuPfyL4TJ7++SGZLc9A3g==", "requires": { "postcss": "^7.0.0" + }, + "dependencies": { + "postcss": { + "version": "7.0.36", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.36.tgz", + "integrity": "sha512-BebJSIUMwJHRH0HAQoxN4u1CN86glsrwsW0q7T+/m44eXOUAxSNdHRkNZPYz5vVUbg17hFgOQDE7fZk7li3pZw==", + "requires": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + }, + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "requires": { + "has-flag": "^3.0.0" + } + } } }, "postcss-normalize-display-values": { @@ -9798,10 +26507,33 @@ "postcss-value-parser": "^3.0.0" }, "dependencies": { + "postcss": { + "version": "7.0.36", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.36.tgz", + "integrity": "sha512-BebJSIUMwJHRH0HAQoxN4u1CN86glsrwsW0q7T+/m44eXOUAxSNdHRkNZPYz5vVUbg17hFgOQDE7fZk7li3pZw==", + "requires": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + } + }, "postcss-value-parser": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==" + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + }, + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "requires": { + "has-flag": "^3.0.0" + } } } }, @@ -9816,10 +26548,33 @@ "postcss-value-parser": "^3.0.0" }, "dependencies": { + "postcss": { + "version": "7.0.36", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.36.tgz", + "integrity": "sha512-BebJSIUMwJHRH0HAQoxN4u1CN86glsrwsW0q7T+/m44eXOUAxSNdHRkNZPYz5vVUbg17hFgOQDE7fZk7li3pZw==", + "requires": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + } + }, "postcss-value-parser": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==" + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + }, + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "requires": { + "has-flag": "^3.0.0" + } } } }, @@ -9834,10 +26589,33 @@ "postcss-value-parser": "^3.0.0" }, "dependencies": { + "postcss": { + "version": "7.0.36", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.36.tgz", + "integrity": "sha512-BebJSIUMwJHRH0HAQoxN4u1CN86glsrwsW0q7T+/m44eXOUAxSNdHRkNZPYz5vVUbg17hFgOQDE7fZk7li3pZw==", + "requires": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + } + }, "postcss-value-parser": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==" + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + }, + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "requires": { + "has-flag": "^3.0.0" + } } } }, @@ -9851,10 +26629,33 @@ "postcss-value-parser": "^3.0.0" }, "dependencies": { + "postcss": { + "version": "7.0.36", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.36.tgz", + "integrity": "sha512-BebJSIUMwJHRH0HAQoxN4u1CN86glsrwsW0q7T+/m44eXOUAxSNdHRkNZPYz5vVUbg17hFgOQDE7fZk7li3pZw==", + "requires": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + } + }, "postcss-value-parser": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==" + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + }, + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "requires": { + "has-flag": "^3.0.0" + } } } }, @@ -9868,10 +26669,33 @@ "postcss-value-parser": "^3.0.0" }, "dependencies": { + "postcss": { + "version": "7.0.36", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.36.tgz", + "integrity": "sha512-BebJSIUMwJHRH0HAQoxN4u1CN86glsrwsW0q7T+/m44eXOUAxSNdHRkNZPYz5vVUbg17hFgOQDE7fZk7li3pZw==", + "requires": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + } + }, "postcss-value-parser": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==" + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + }, + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "requires": { + "has-flag": "^3.0.0" + } } } }, @@ -9885,10 +26709,33 @@ "postcss-value-parser": "^3.0.0" }, "dependencies": { + "postcss": { + "version": "7.0.36", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.36.tgz", + "integrity": "sha512-BebJSIUMwJHRH0HAQoxN4u1CN86glsrwsW0q7T+/m44eXOUAxSNdHRkNZPYz5vVUbg17hFgOQDE7fZk7li3pZw==", + "requires": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + } + }, "postcss-value-parser": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==" + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + }, + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "requires": { + "has-flag": "^3.0.0" + } } } }, @@ -9903,10 +26750,33 @@ "postcss-value-parser": "^3.0.0" }, "dependencies": { + "postcss": { + "version": "7.0.36", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.36.tgz", + "integrity": "sha512-BebJSIUMwJHRH0HAQoxN4u1CN86glsrwsW0q7T+/m44eXOUAxSNdHRkNZPYz5vVUbg17hFgOQDE7fZk7li3pZw==", + "requires": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + } + }, "postcss-value-parser": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==" + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + }, + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "requires": { + "has-flag": "^3.0.0" + } } } }, @@ -9919,10 +26789,33 @@ "postcss-value-parser": "^3.0.0" }, "dependencies": { + "postcss": { + "version": "7.0.36", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.36.tgz", + "integrity": "sha512-BebJSIUMwJHRH0HAQoxN4u1CN86glsrwsW0q7T+/m44eXOUAxSNdHRkNZPYz5vVUbg17hFgOQDE7fZk7li3pZw==", + "requires": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + } + }, "postcss-value-parser": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==" + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + }, + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "requires": { + "has-flag": "^3.0.0" + } } } }, @@ -9936,10 +26829,33 @@ "postcss-value-parser": "^3.0.0" }, "dependencies": { + "postcss": { + "version": "7.0.36", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.36.tgz", + "integrity": "sha512-BebJSIUMwJHRH0HAQoxN4u1CN86glsrwsW0q7T+/m44eXOUAxSNdHRkNZPYz5vVUbg17hFgOQDE7fZk7li3pZw==", + "requires": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + } + }, "postcss-value-parser": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==" + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + }, + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "requires": { + "has-flag": "^3.0.0" + } } } }, @@ -9952,6 +26868,31 @@ "caniuse-api": "^3.0.0", "has": "^1.0.0", "postcss": "^7.0.0" + }, + "dependencies": { + "postcss": { + "version": "7.0.36", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.36.tgz", + "integrity": "sha512-BebJSIUMwJHRH0HAQoxN4u1CN86glsrwsW0q7T+/m44eXOUAxSNdHRkNZPYz5vVUbg17hFgOQDE7fZk7li3pZw==", + "requires": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + }, + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "requires": { + "has-flag": "^3.0.0" + } + } } }, "postcss-reduce-transforms": { @@ -9965,17 +26906,40 @@ "postcss-value-parser": "^3.0.0" }, "dependencies": { + "postcss": { + "version": "7.0.36", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.36.tgz", + "integrity": "sha512-BebJSIUMwJHRH0HAQoxN4u1CN86glsrwsW0q7T+/m44eXOUAxSNdHRkNZPYz5vVUbg17hFgOQDE7fZk7li3pZw==", + "requires": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + } + }, "postcss-value-parser": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==" + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + }, + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "requires": { + "has-flag": "^3.0.0" + } } } }, "postcss-selector-parser": { - "version": "6.0.5", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.5.tgz", - "integrity": "sha512-aFYPoYmXbZ1V6HZaSvat08M97A8HqO6Pjz+PiNpw/DhuRrC72XWAdp3hL6wusDCN31sSmcZyMGa2hZEuX+Xfhg==", + "version": "6.0.6", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.6.tgz", + "integrity": "sha512-9LXrvaaX3+mcv5xkg5kFwqSzSH1JIObIx51PrndZwlmznwXRfxMddDvo9gve3gVR8ZTKgoFDdWkbRFmEhT4PMg==", "requires": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" @@ -9991,10 +26955,33 @@ "svgo": "^1.0.0" }, "dependencies": { + "postcss": { + "version": "7.0.36", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.36.tgz", + "integrity": "sha512-BebJSIUMwJHRH0HAQoxN4u1CN86glsrwsW0q7T+/m44eXOUAxSNdHRkNZPYz5vVUbg17hFgOQDE7fZk7li3pZw==", + "requires": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + } + }, "postcss-value-parser": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==" + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + }, + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "requires": { + "has-flag": "^3.0.0" + } } } }, @@ -10006,6 +26993,31 @@ "alphanum-sort": "^1.0.0", "postcss": "^7.0.0", "uniqs": "^2.0.0" + }, + "dependencies": { + "postcss": { + "version": "7.0.36", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.36.tgz", + "integrity": "sha512-BebJSIUMwJHRH0HAQoxN4u1CN86glsrwsW0q7T+/m44eXOUAxSNdHRkNZPYz5vVUbg17hFgOQDE7fZk7li3pZw==", + "requires": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + }, + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "requires": { + "has-flag": "^3.0.0" + } + } } }, "postcss-value-parser": { @@ -10179,41 +27191,6 @@ "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" }, - "purgecss": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/purgecss/-/purgecss-3.1.3.tgz", - "integrity": "sha512-hRSLN9mguJ2lzlIQtW4qmPS2kh6oMnA9RxdIYK8sz18QYqd6ePp4GNDl18oWHA1f2v2NEQIh51CO8s/E3YGckQ==", - "requires": { - "commander": "^6.0.0", - "glob": "^7.0.0", - "postcss": "^8.2.1", - "postcss-selector-parser": "^6.0.2" - }, - "dependencies": { - "postcss": { - "version": "8.2.14", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.2.14.tgz", - "integrity": "sha512-+jD0ZijcvyCqPQo/m/CW0UcARpdFylq04of+Q7RKX6f/Tu+dvpUI/9Sp81+i6/vJThnOBX09Quw0ZLOVwpzX3w==", - "requires": { - "colorette": "^1.2.2", - "nanoid": "^3.1.22", - "source-map": "^0.6.1" - }, - "dependencies": { - "nanoid": { - "version": "3.1.23", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.23.tgz", - "integrity": "sha512-FiB0kzdP0FFVGDKlRLEQ1BgDzU87dy5NnzjeW9YZNt+/c3+q82EQDUwniSAUxp/F0gFNI1ZhKU1FqYsMuqZVnw==" - } - } - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" - } - } - }, "q": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz", @@ -10359,9 +27336,9 @@ } }, "readdirp": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.5.0.tgz", - "integrity": "sha512-cMhu7c/8rdhkHXWsY+osBhfSy0JikwpHK/5+imo+LpeasTF8ouErHrlYkwT0++njiyuDvc7OFY5T3ukvZ8qmFQ==", + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", "requires": { "picomatch": "^2.2.1" } @@ -10890,6 +27867,11 @@ "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=" }, + "shell-quote": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.7.2.tgz", + "integrity": "sha512-mRz/m/JVscCrkMyPqHc/bczi3OQHkLTqXHEFu0zDhK/qfv3UcOA4SVmRCLmos4bhjr9ekVQubj/R7waKapmiQg==" + }, "sigmund": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.1.tgz", @@ -11062,8 +28044,7 @@ "source-map-js": { "version": "0.6.2", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-0.6.2.tgz", - "integrity": "sha512-/3GptzWzu0+0MBQFrDKzw/DvvMTUORvgY6k6jd/VS6iCR4RDTKWH6v6WPwQoUO8667uQEf9Oe38DxAYWY5F/Ug==", - "dev": true + "integrity": "sha512-/3GptzWzu0+0MBQFrDKzw/DvvMTUORvgY6k6jd/VS6iCR4RDTKWH6v6WPwQoUO8667uQEf9Oe38DxAYWY5F/Ug==" }, "source-map-resolve": { "version": "0.5.3", @@ -11102,7 +28083,6 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz", "integrity": "sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==", - "dev": true, "requires": { "spdx-expression-parse": "^3.0.0", "spdx-license-ids": "^3.0.0" @@ -11111,14 +28091,12 @@ "spdx-exceptions": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz", - "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==", - "dev": true + "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==" }, "spdx-expression-parse": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", - "dev": true, "requires": { "spdx-exceptions": "^2.1.0", "spdx-license-ids": "^3.0.0" @@ -11127,8 +28105,7 @@ "spdx-license-ids": { "version": "3.0.7", "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.7.tgz", - "integrity": "sha512-U+MTEOO0AiDzxwFvoa4JVnMV6mZlJKk2sBLt90s7G0Gd0Mlknc7kxEn3nuDPNZRta7O2uy8oLcZLVT+4sqNZHQ==", - "dev": true + "integrity": "sha512-U+MTEOO0AiDzxwFvoa4JVnMV6mZlJKk2sBLt90s7G0Gd0Mlknc7kxEn3nuDPNZRta7O2uy8oLcZLVT+4sqNZHQ==" }, "split": { "version": "0.2.10", @@ -11305,6 +28282,21 @@ "xtend": "^4.0.0" } }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "requires": { + "safe-buffer": "~5.1.0" + }, + "dependencies": { + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + } + } + }, "string-width": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", @@ -11333,6 +28325,16 @@ } } }, + "string.prototype.padend": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/string.prototype.padend/-/string.prototype.padend-3.1.2.tgz", + "integrity": "sha512-/AQFLdYvePENU3W5rgurfWSMU6n+Ww8n/3cUt7E+vPBB/D7YDG8x+qjoFs4M/alR2bW7Qg6xMjVwWUOvuQ0XpQ==", + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.18.0-next.2" + } + }, "string.prototype.trimend": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz", @@ -11351,21 +28353,6 @@ "define-properties": "^1.1.3" } }, - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "requires": { - "safe-buffer": "~5.1.0" - }, - "dependencies": { - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - } - } - }, "strip-ansi": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", @@ -11408,6 +28395,16 @@ "postcss-selector-parser": "^3.0.0" }, "dependencies": { + "postcss": { + "version": "7.0.36", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.36.tgz", + "integrity": "sha512-BebJSIUMwJHRH0HAQoxN4u1CN86glsrwsW0q7T+/m44eXOUAxSNdHRkNZPYz5vVUbg17hFgOQDE7fZk7li3pZw==", + "requires": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + } + }, "postcss-selector-parser": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-3.1.2.tgz", @@ -11417,6 +28414,19 @@ "indexes-of": "^1.0.1", "uniq": "^1.0.1" } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + }, + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "requires": { + "has-flag": "^3.0.0" + } } } }, @@ -11465,37 +28475,34 @@ "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==" }, "tailwindcss": { - "version": "npm:@tailwindcss/postcss7-compat@2.2.7", - "resolved": "https://registry.npmjs.org/@tailwindcss/postcss7-compat/-/postcss7-compat-2.2.7.tgz", - "integrity": "sha512-1QkWUEeLV1AoNipMCE6IlL7XYScGb+DAzaXy35ooMDvl0G8kCMHBNqGxyVAnTcK8gyJNUzkKXExkUnbjAndd/g==", - "dev": true, + "version": "2.2.15", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-2.2.15.tgz", + "integrity": "sha512-WgV41xTMbnSoTNMNnJvShQZ+8GmY86DmXTrCgnsveNZJdlybfwCItV8kAqjYmU49YiFr+ofzmT1JlAKajBZboQ==", "requires": { - "arg": "^5.0.0", - "autoprefixer": "^9", + "arg": "^5.0.1", "bytes": "^3.0.0", - "chalk": "^4.1.1", + "chalk": "^4.1.2", "chokidar": "^3.5.2", - "color": "^3.2.0", - "cosmiconfig": "^7.0.0", + "color": "^4.0.1", + "cosmiconfig": "^7.0.1", "detective": "^5.2.0", "didyoumean": "^1.2.2", "dlv": "^1.1.3", "fast-glob": "^3.2.7", "fs-extra": "^10.0.0", - "glob-parent": "^6.0.0", + "glob-parent": "^6.0.1", "html-tags": "^3.1.0", + "is-color-stop": "^1.1.0", "is-glob": "^4.0.1", "lodash": "^4.17.21", "lodash.topath": "^4.5.2", "modern-normalize": "^1.1.0", - "node-emoji": "^1.8.1", + "node-emoji": "^1.11.0", "normalize-path": "^3.0.0", "object-hash": "^2.2.0", - "postcss": "^7", - "postcss-functions": "^3", - "postcss-js": "^2", + "postcss-js": "^3.0.3", "postcss-load-config": "^3.1.0", - "postcss-nested": "^4", + "postcss-nested": "5.0.6", "postcss-selector-parser": "^6.0.6", "postcss-value-parser": "^4.1.0", "pretty-hrtime": "^1.0.3", @@ -11510,86 +28517,37 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, "requires": { "color-convert": "^2.0.1" } }, "arg": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.0.tgz", - "integrity": "sha512-4P8Zm2H+BRS+c/xX1LrHw0qKpEhdlZjLCgWy+d78T9vqa2Z2SiD2wMrYuWIAFy5IZUD7nnNXroRttz+0RzlrzQ==", - "dev": true + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.1.tgz", + "integrity": "sha512-e0hDa9H2Z9AwFkk2qDlwhoMYE4eToKarchkQHovNdLTCYMHZHeRjI71crOh+dio4K6u1IcwubQqo79Ga4CyAQA==" }, "chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, "requires": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, - "chokidar": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.2.tgz", - "integrity": "sha512-ekGhOnNVPgT77r4K/U3GDhu+FQ2S8TnK/s2KbIGXi0SZWuwkZ2QNyfWdZW+TVfn84DpEP7rLeCt2UI6bJ8GwbQ==", - "dev": true, - "requires": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "fsevents": "~2.3.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" - }, - "dependencies": { - "glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "requires": { - "is-glob": "^4.0.1" - } - } - } - }, "color": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/color/-/color-3.2.1.tgz", - "integrity": "sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA==", - "dev": true, + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/color/-/color-4.0.1.tgz", + "integrity": "sha512-rpZjOKN5O7naJxkH2Rx1sZzzBgaiWECc6BYXjeCE6kF0kcASJYbUq02u7JqIHwCb/j3NhV+QhRL2683aICeGZA==", "requires": { - "color-convert": "^1.9.3", + "color-convert": "^2.0.1", "color-string": "^1.6.0" - }, - "dependencies": { - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "requires": { - "color-name": "1.1.3" - } - }, - "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", - "dev": true - } } }, "color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, "requires": { "color-name": "~1.1.4" } @@ -11597,24 +28555,12 @@ "color-name": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "color-string": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.6.0.tgz", - "integrity": "sha512-c/hGS+kRWJutUBEngKKmk4iH3sD59MBkoxVapS/0wgpCz2u7XsNloxknyvBhzwEs1IbV36D9PwqLPJ2DTu3vMA==", - "dev": true, - "requires": { - "color-name": "^1.0.0", - "simple-swizzle": "^0.2.2" - } + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" }, "cosmiconfig": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.0.0.tgz", - "integrity": "sha512-pondGvTuVYDk++upghXJabWzL6Kxu6f26ljFw64Swq9v6sQPUL3EUlVDV56diOjpCayKihL6hVe8exIACU4XcA==", - "dev": true, + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.0.1.tgz", + "integrity": "sha512-a1YWNUV2HwGimB7dU2s1wUMurNKjpx60HxBB6xUM8Re+2s1g1IIfJvFR0/iCF+XHdE0GMTKTuLR32UQff4TEyQ==", "requires": { "@types/parse-json": "^4.0.0", "import-fresh": "^3.2.1", @@ -11623,41 +28569,10 @@ "yaml": "^1.10.0" } }, - "didyoumean": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", - "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==", - "dev": true - }, - "fast-glob": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.7.tgz", - "integrity": "sha512-rYGMRwip6lUMvYD3BTScMwT1HtAs2d71SMv66Vrxs0IekGZEjhM0pcMfjQPnknBt2zeCwQMEupiN02ZP4DiT1Q==", - "dev": true, - "requires": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.2", - "merge2": "^1.3.0", - "micromatch": "^4.0.4" - }, - "dependencies": { - "glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "requires": { - "is-glob": "^4.0.1" - } - } - } - }, "fs-extra": { "version": "10.0.0", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.0.0.tgz", "integrity": "sha512-C5owb14u9eJwizKGdchcDUQeFtlSHHthBk8pbX9Vc1PFZrLombudjDnNns88aYslCyF6IY5SUw3Roz6xShcEIQ==", - "dev": true, "requires": { "graceful-fs": "^4.2.0", "jsonfile": "^6.0.1", @@ -11668,7 +28583,6 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.1.tgz", "integrity": "sha512-kEVjS71mQazDBHKcsq4E9u/vUzaLcw1A8EtUeydawvIWQCJM0qQ08G1H7/XTjFUulla6XQiDOG6MXSaG0HDKog==", - "dev": true, "requires": { "is-glob": "^4.0.1" } @@ -11676,30 +28590,21 @@ "has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" }, "import-fresh": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", - "dev": true, "requires": { "parent-module": "^1.0.0", "resolve-from": "^4.0.0" } }, - "object-hash": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-2.2.0.tgz", - "integrity": "sha512-gScRMn0bS5fH+IuwyIFgnh9zBdo4DV+6GhygmWM9HyNJSgS0hScp1f5vjtm7oIIOiT9trXrShAkLFSc2IqKNgw==", - "dev": true - }, "parse-json": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", - "dev": true, "requires": { "@babel/code-frame": "^7.0.0", "error-ex": "^1.3.1", @@ -11710,64 +28615,55 @@ "path-type": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", - "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", - "dev": true + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==" }, - "postcss-selector-parser": { - "version": "6.0.6", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.6.tgz", - "integrity": "sha512-9LXrvaaX3+mcv5xkg5kFwqSzSH1JIObIx51PrndZwlmznwXRfxMddDvo9gve3gVR8ZTKgoFDdWkbRFmEhT4PMg==", - "dev": true, + "postcss-js": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-3.0.3.tgz", + "integrity": "sha512-gWnoWQXKFw65Hk/mi2+WTQTHdPD5UJdDXZmX073EY/B3BWnYjO4F4t0VneTCnCGQ5E5GsCdMkzPaTXwl3r5dJw==", "requires": { - "cssesc": "^3.0.0", - "util-deprecate": "^1.0.2" + "camelcase-css": "^2.0.1", + "postcss": "^8.1.6" + } + }, + "postcss-load-config": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-3.1.0.tgz", + "integrity": "sha512-ipM8Ds01ZUophjDTQYSVP70slFSYg3T0/zyfII5vzhN6V57YSxMgG5syXuwi5VtS8wSf3iL30v0uBdoIVx4Q0g==", + "requires": { + "import-cwd": "^3.0.0", + "lilconfig": "^2.0.3", + "yaml": "^1.10.2" + } + }, + "postcss-nested": { + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-5.0.6.tgz", + "integrity": "sha512-rKqm2Fk0KbA8Vt3AdGN0FB9OBOMDVajMG6ZCf/GoHgdxUJ4sBFp0A/uMIRm+MJUdo33YXEtjqIz8u7DAp8B7DA==", + "requires": { + "postcss-selector-parser": "^6.0.6" } }, "purgecss": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/purgecss/-/purgecss-4.0.3.tgz", "integrity": "sha512-PYOIn5ibRIP34PBU9zohUcCI09c7drPJJtTDAc0Q6QlRz2/CHQ8ywGLdE7ZhxU2VTqB7p5wkvj5Qcm05Rz3Jmw==", - "dev": true, "requires": { "commander": "^6.0.0", "glob": "^7.0.0", "postcss": "^8.2.1", "postcss-selector-parser": "^6.0.2" - }, - "dependencies": { - "postcss": { - "version": "8.3.6", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.3.6.tgz", - "integrity": "sha512-wG1cc/JhRgdqB6WHEuyLTedf3KIRuD0hG6ldkFEZNCjRxiC+3i6kkWUUbiJQayP28iwG35cEmAbe98585BYV0A==", - "dev": true, - "requires": { - "colorette": "^1.2.2", - "nanoid": "^3.1.23", - "source-map-js": "^0.6.2" - } - } - } - }, - "readdirp": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", - "dev": true, - "requires": { - "picomatch": "^2.2.1" } }, "resolve-from": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "dev": true + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==" }, "supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, "requires": { "has-flag": "^4.0.0" } @@ -11884,7 +28780,6 @@ "version": "0.2.1", "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz", "integrity": "sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ==", - "dev": true, "requires": { "rimraf": "^3.0.0" }, @@ -11893,7 +28788,6 @@ "version": "3.0.2", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "dev": true, "requires": { "glob": "^7.1.3" } @@ -12137,7 +29031,8 @@ "version": "0.7.0", "resolved": "https://registry.npmjs.org/tslint-no-circular-imports/-/tslint-no-circular-imports-0.7.0.tgz", "integrity": "sha512-k3wxpeMC4ef40UbpfBVHEHIzKfNZq5/SCtAO1YjGsaNTklo+K53/TWLrym+poA65RJFDiYgYNWvkeIIkJNA0Vw==", - "dev": true + "dev": true, + "requires": {} }, "tsutils": { "version": "2.29.0", @@ -12623,8 +29518,7 @@ "typescript": { "version": "3.9.9", "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.9.9.tgz", - "integrity": "sha512-kdMjTiekY+z/ubJCATUPlRDl39vXYiMV9iyeMuEuXZh2we6zz80uovNN2WlAxmmdE/Z/YQe+EbOEXB5RHEED3w==", - "dev": true + "integrity": "sha512-kdMjTiekY+z/ubJCATUPlRDl39vXYiMV9iyeMuEuXZh2we6zz80uovNN2WlAxmmdE/Z/YQe+EbOEXB5RHEED3w==" }, "unbox-primitive": { "version": "1.0.1", @@ -12663,6 +29557,16 @@ "resolved": "https://registry.npmjs.org/is-absolute-url/-/is-absolute-url-3.0.3.tgz", "integrity": "sha512-opmNIX7uFnS96NtPmhWQgQx6/NYFgsUXYMllcfzwWKUMwfo8kku1TvE6hkNcH+Q1ts5cMVrsY7j0bxXQDciu9Q==" }, + "postcss": { + "version": "7.0.36", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.36.tgz", + "integrity": "sha512-BebJSIUMwJHRH0HAQoxN4u1CN86glsrwsW0q7T+/m44eXOUAxSNdHRkNZPYz5vVUbg17hFgOQDE7fZk7li3pZw==", + "requires": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + } + }, "postcss-selector-parser": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.2.tgz", @@ -12672,6 +29576,19 @@ "indexes-of": "^1.0.1", "uniq": "^1.0.1" } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + }, + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "requires": { + "has-flag": "^3.0.0" + } } } }, @@ -12683,7 +29600,8 @@ "underscore.deep": { "version": "0.5.1", "resolved": "https://registry.npmjs.org/underscore.deep/-/underscore.deep-0.5.1.tgz", - "integrity": "sha1-ByZx9I1oc1w0Ij/P72PmnlJ2zCs=" + "integrity": "sha1-ByZx9I1oc1w0Ij/P72PmnlJ2zCs=", + "requires": {} }, "unicode-canonical-property-names-ecmascript": { "version": "1.0.4", @@ -12874,7 +29792,6 @@ "version": "3.0.4", "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", - "dev": true, "requires": { "spdx-correct": "^3.0.0", "spdx-expression-parse": "^3.0.0" @@ -13041,6 +29958,15 @@ "async-limiter": "~1.0.0" } }, + "xdiff": { + "version": "0.2.11", + "resolved": "https://registry.npmjs.org/xdiff/-/xdiff-0.2.11.tgz", + "integrity": "sha1-cj1SPhtvJlojPK/HeGBiLqXS2Mg=", + "dev": true, + "requires": { + "adiff": "~0.2.4" + } + }, "xml-name-validator": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-3.0.0.tgz", @@ -13070,8 +29996,7 @@ "yaml": { "version": "1.10.2", "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", - "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", - "dev": true + "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==" }, "yn": { "version": "3.1.1", diff --git a/package.json b/package.json index 5a99707867..4f8cc5a9d6 100644 --- a/package.json +++ b/package.json @@ -7,8 +7,13 @@ "homepage": "https://mapcomplete.osm.be", "main": "index.js", "scripts": { - "increase-memory": "export NODE_OPTIONS=--max_old_space_size=4096", - "start": "ts-node scripts/generateLayerOverview.ts --no-fail && npm run increase-memory && parcel *.html UI/** Logic/** assets/*.json assets/svg/* assets/generated/* assets/layers/*/*.svg assets/tagRenderings/*.json assets/themes/*/*.svg assets/themes/*/*.png vendor/* vendor/*/*", + "increase-memory": "export NODE_OPTIONS=--max_old_space_size=6182", + "start": "npm run start:prepare && npm-run-all --parallel start:parallel:*", + "strt": "npm run start:prepare && npm run start:parallel:parcel", + "start:prepare": "ts-node scripts/generateLayerOverview.ts --no-fail && npm run increase-memory", + "start:parallel:parcel": "parcel *.html UI/** Logic/** assets/*.json assets/svg/* assets/generated/* assets/layers/*/*.svg assets/layers/*/*.jpg assets/layers/*/*.png assets/tagRenderings/*.json assets/themes/*/*.svg assets/themes/*/*.jpg assets/themes/*/*.png vendor/* vendor/*/*", + "start:parallel:tailwindcli": "tailwindcss -i index.css -o css/index-tailwind-output.css --watch", + "generate:css": "tailwindcss -i index.css -o css/index-tailwind-output.css", "test": "ts-node test/TestAll.ts", "init": "npm ci && npm run generate && npm run generate:editor-layer-index && npm run generate:layouts && npm run clean", "add-weblate-upstream": "git remote add weblate-layers https://hosted.weblate.org/git/mapcomplete/layer-translations/ ; git remote update weblate-layers", @@ -20,10 +25,10 @@ "reset:translations": "ts-node scripts/generateTranslations.ts --ignore-weblate", "generate:layouts": "ts-node scripts/generateLayouts.ts", "generate:docs": "ts-node scripts/generateDocs.ts && ts-node scripts/generateTaginfoProjectFiles.ts", - "generate:cache:speelplekken:mini": "npm run generate:layeroverview && ts-node scripts/generateCache.ts speelplekken 14 ../pietervdvn.github.io/speelplekken_cache/ 51.181710380278176 4.423413276672363 51.193007664772495 4.444141387939452", + "generate:cache:speelplekken:mini": "ts-node scripts/generateCache.ts speelplekken 14 ../pietervdvn.github.io/speelplekken_cache_mini/ 51.181710380278176 4.423413276672363 51.193007664772495 4.444141387939452", "generate:cache:speelplekken": "npm run generate:layeroverview && ts-node scripts/generateCache.ts speelplekken 14 ../pietervdvn.github.io/speelplekken_cache/ 51.20 4.35 51.09 4.56", "generate:cache:natuurpunt": "npm run generate:layeroverview && ts-node scripts/generateCache.ts natuurpunt 12 ../pietervdvn.github.io/natuurpunt_cache/ 50.40 2.1 51.54 6.4 --generate-point-overview nature_reserve,visitor_information_centre", - "generate:layeroverview": "npm run generate:licenses && echo {\\\"layers\\\":[], \\\"themes\\\":[]} > ./assets/generated/known_layers_and_themes.json && ts-node scripts/generateLayerOverview.ts --no-fail", + "generate:layeroverview": "echo {\\\"layers\\\":[], \\\"themes\\\":[]} > ./assets/generated/known_layers_and_themes.json && ts-node scripts/generateLayerOverview.ts --no-fail", "generate:licenses": "ts-node scripts/generateLicenseInfo.ts --no-fail", "query:licenses": "ts-node scripts/generateLicenseInfo.ts --query", "generate:report": "cd Docs/Tools && ./compileStats.sh && git commit . -m 'New statistics ands graphs' && git push", @@ -32,7 +37,7 @@ "validate:licenses": "ts-node scripts/generateLicenseInfo.ts --report", "optimize-images": "cd assets/generated/ && find -name '*.png' -exec optipng '{}' \\; && echo 'PNGs are optimized'", "reset:layeroverview": "echo {\\\"layers\\\":[], \\\"themes\\\":[]} > ./assets/generated/known_layers_and_themes.json", - "generate": "mkdir -p ./assets/generated && npm run reset:layeroverview && npm run generate:images && npm run generate:translations && npm run generate:layeroverview", + "generate": "mkdir -p ./assets/generated && npm run reset:layeroverview && npm run generate:images && npm run generate:translations && npm run generate:licenses && npm run generate:layeroverview", "build": "rm -rf dist/ && npm run generate && parcel build --public-url ./ *.html assets/** assets/**/** assets/**/**/** vendor/* vendor/*/*", "generate:charging-stations": "cd ./assets/layers/charging_station && ts-node csvToJson.ts && cd -", "prepare-deploy": "npm run generate && npm run test && npm run generate:editor-layer-index && npm run generate:charging-stations && npm run generate:layeroverview && npm run generate:layouts && npm run build && rm -rf .cache", @@ -51,7 +56,6 @@ "license": "GPL", "dependencies": { "@babel/preset-env": "7.13.8", - "@tailwindcss/postcss7-compat": "^2.0.2", "@turf/buffer": "^6.3.0", "@turf/collect": "^6.3.0", "@turf/distance": "^6.3.0", @@ -63,7 +67,6 @@ "@types/leaflet.markercluster": "^1.4.3", "@types/lz-string": "^1.3.34", "@types/prompt-sync": "^4.1.0", - "autoprefixer": "^9.8.6", "country-language": "^0.1.7", "email-validator": "^2.0.4", "escape-html": "^1.0.3", @@ -80,12 +83,13 @@ "lz-string": "^1.4.4", "mangrove-reviews": "^0.1.3", "moment": "^2.29.0", + "npm-run-all": "^4.1.5", "opening_hours": "^3.6.0", "osm-auth": "^1.0.2", "osmtogeojson": "^3.0.0-beta.4", "parcel": "^1.2.4", - "postcss": "^7.0.36", "prompt-sync": "^4.2.0", + "tailwindcss": "^2.2.15", "tslint": "^6.1.3" }, "devDependencies": { @@ -93,10 +97,10 @@ "@types/node": "^7.0.5", "assert": "^2.0.0", "fs": "0.0.1-security", + "git-json-merge": "^0.4.5", "marked": "^2.0.0", "read-file": "^0.2.0", "sharp": "^0.28.3", - "tailwindcss": "npm:@tailwindcss/postcss7-compat@^2.2.7", "ts-node": "^9.0.0", "ts-node-dev": "^1.0.0-pre.63", "tslint-no-circular-imports": "^0.7.0", diff --git a/postcss.config.js b/postcss.config.js deleted file mode 100644 index ca0c0cdaaf..0000000000 --- a/postcss.config.js +++ /dev/null @@ -1,6 +0,0 @@ -module.exports = { - plugins: { - tailwindcss: {}, - autoprefixer: {}, - } -} \ No newline at end of file diff --git a/preferences.ts b/preferences.ts index 3108920033..c96912b5d7 100644 --- a/preferences.ts +++ b/preferences.ts @@ -10,9 +10,17 @@ import LZString from "lz-string"; import BaseUIElement from "./UI/BaseUIElement"; import Table from "./UI/Base/Table"; import {LayoutConfigJson} from "./Models/ThemeConfig/Json/LayoutConfigJson"; +import {Changes} from "./Logic/Osm/Changes"; +import {ElementStorage} from "./Logic/ElementStorage"; -const connection = new OsmConnection(false, false, new UIEventSource(undefined), ""); +const connection = new OsmConnection({ + osmConfiguration: 'osm', + changes: new Changes(), + layoutName: '', + allElements: new ElementStorage() +}); + let rendered = false; @@ -20,6 +28,7 @@ function salvageThemes(preferences: any) { const knownThemeNames = new Set(); const correctThemeNames = [] for (const key in preferences) { + try{ if (!(typeof key === "string")) { continue; } @@ -36,7 +45,7 @@ function salvageThemes(preferences: any) { } else { knownThemeNames.add(theme); } - } + }catch(e){console.error(e)}} for (const correctThemeName of correctThemeNames) { knownThemeNames.delete(correctThemeName); @@ -65,8 +74,13 @@ function salvageThemes(preferences: any) { try { jsonObject = JSON.parse(atob(combined)); } catch (e) { + try{ + // We try to decode with lz-string jsonObject = JSON.parse(Utils.UnMinify(LZString.decompressFromBase64(combined))) as LayoutConfigJson; + }catch(e0){ + console.log("Could not salvage theme. Initial parsing failed due to:", e,"\nWith LZ failed due ", e0) + } } diff --git a/scripts/CycleHighwayFix.ts b/scripts/CycleHighwayFix.ts index 94a3db1b42..b6e4509d65 100644 --- a/scripts/CycleHighwayFix.ts +++ b/scripts/CycleHighwayFix.ts @@ -10,7 +10,7 @@ writeFileSync("cycleHighwayFix.osc", "", "utf8") const ids = JSON.parse(readFileSync("export.geojson", "utf-8")).features.map(f => f.properties["@id"]) console.log(ids) -ids.map(id => OsmObject.DownloadReferencingRelations(id).addCallbackAndRunD(relations => { +ids.map(id => OsmObject.DownloadReferencingRelations(id).then(relations => { console.log(relations) const changeparts = relations.filter(relation => relation.tags["cycle_highway"] == "yes" && relation.tags["note:state"] == undefined) .map(relation => { @@ -18,5 +18,4 @@ ids.map(id => OsmObject.DownloadReferencingRelations(id).addCallbackAndRunD(rela return relation.ChangesetXML(undefined) }) appendFileSync("cycleHighwayFix.osc", changeparts.join("\n"), "utf8") - return true; })) \ No newline at end of file diff --git a/scripts/ScriptUtils.ts b/scripts/ScriptUtils.ts index 702c40f4dd..5469b56451 100644 --- a/scripts/ScriptUtils.ts +++ b/scripts/ScriptUtils.ts @@ -7,7 +7,6 @@ import {LayerConfigJson} from "../Models/ThemeConfig/Json/LayerConfigJson"; Utils.runningFromConsole = true - export default class ScriptUtils { @@ -49,15 +48,12 @@ export default class ScriptUtils { }) } - public static DownloadJSON(url, options?: { - headers: any - }): Promise { + public static DownloadJSON(url, headers?: any): Promise { return new Promise((resolve, reject) => { try { - - const headers = options?.headers ?? {} + headers = headers ?? {} headers.accept = "application/json" - + console.log("ScriptUtils.DownloadJson(", url.substring(0,40), url.length > 40 ? "...":"" ,")") const urlObj = new URL(url) https.get({ host: urlObj.host, diff --git a/scripts/generateCache.ts b/scripts/generateCache.ts index c043bc2245..1377b3153e 100644 --- a/scripts/generateCache.ts +++ b/scripts/generateCache.ts @@ -2,27 +2,30 @@ * Generates a collection of geojson files based on an overpass query for a given theme */ import {Utils} from "../Utils"; -Utils.runningFromConsole = true import {Overpass} from "../Logic/Osm/Overpass"; -import * as fs from "fs"; import {existsSync, readFileSync, writeFileSync} from "fs"; import {TagsFilter} from "../Logic/Tags/TagsFilter"; import {Or} from "../Logic/Tags/Or"; import {AllKnownLayouts} from "../Customizations/AllKnownLayouts"; -import ExtractRelations from "../Logic/Osm/ExtractRelations"; +import RelationsTracker from "../Logic/Osm/RelationsTracker"; import * as OsmToGeoJson from "osmtogeojson"; import MetaTagging from "../Logic/MetaTagging"; -import {GeoOperations} from "../Logic/GeoOperations"; import {UIEventSource} from "../Logic/UIEventSource"; -import {TileRange} from "../Models/TileRange"; +import {TileRange, Tiles} from "../Models/TileRange"; import LayoutConfig from "../Models/ThemeConfig/LayoutConfig"; -import LayerConfig from "../Models/ThemeConfig/LayerConfig"; import ScriptUtils from "./ScriptUtils"; +import PerLayerFeatureSourceSplitter from "../Logic/FeatureSource/PerLayerFeatureSourceSplitter"; +import FilteredLayer from "../Models/FilteredLayer"; +import FeatureSource, {FeatureSourceForLayer} from "../Logic/FeatureSource/FeatureSource"; +import StaticFeatureSource from "../Logic/FeatureSource/Sources/StaticFeatureSource"; +import TiledFeatureSource from "../Logic/FeatureSource/TiledFeatureSource/TiledFeatureSource"; +import Constants from "../Models/Constants"; + ScriptUtils.fixUtils() -function createOverpassObject(theme: LayoutConfig) { +function createOverpassObject(theme: LayoutConfig, relationTracker: RelationsTracker, backend: string) { let filters: TagsFilter[] = []; let extraScripts: string[] = []; for (const layer of theme.layers) { @@ -53,8 +56,8 @@ function createOverpassObject(theme: LayoutConfig) { if (filters.length + extraScripts.length === 0) { throw "Nothing to download! The theme doesn't declare anything to download" } - return new Overpass(new Or(filters), extraScripts, new UIEventSource("https://overpass.kumi.systems/api/interpreter"), //https://overpass-api.de/api/interpreter"), - new UIEventSource(60)); + return new Overpass(new Or(filters), extraScripts, backend, + new UIEventSource(60), relationTracker); } function rawJsonName(targetDir: string, x: number, y: number, z: number): string { @@ -66,7 +69,7 @@ function geoJsonName(targetDir: string, x: number, y: number, z: number): string } /// Downloads the given feature and saves them to disk -async function downloadRaw(targetdir: string, r: TileRange, overpass: Overpass)/* : {failed: number, skipped :number} */ { +async function downloadRaw(targetdir: string, r: TileRange, theme: LayoutConfig, relationTracker: RelationsTracker)/* : {failed: number, skipped :number} */ { let downloaded = 0 let failed = 0 let skipped = 0 @@ -75,48 +78,40 @@ async function downloadRaw(targetdir: string, r: TileRange, overpass: Overpass)/ downloaded++; const filename = rawJsonName(targetdir, x, y, r.zoomlevel) if (existsSync(filename)) { - console.log("Already exists: ", filename) + console.log("Already exists (not downloading again): ", filename) skipped++ continue; } console.log("x:", (x - r.xstart), "/", (r.xend - r.xstart), "; y:", (y - r.ystart), "/", (r.yend - r.ystart), "; total: ", downloaded, "/", r.total, "failed: ", failed, "skipped: ", skipped) - const boundsArr = Utils.tile_bounds(r.zoomlevel, x, y) + const boundsArr = Tiles.tile_bounds(r.zoomlevel, x, y) const bounds = { north: Math.max(boundsArr[0][0], boundsArr[1][0]), south: Math.min(boundsArr[0][0], boundsArr[1][0]), east: Math.max(boundsArr[0][1], boundsArr[1][1]), west: Math.min(boundsArr[0][1], boundsArr[1][1]) } + const overpass = createOverpassObject(theme, relationTracker, Constants.defaultOverpassUrls[(downloaded + failed) % Constants.defaultOverpassUrls.length]) const url = overpass.buildQuery("[bbox:" + bounds.south + "," + bounds.west + "," + bounds.north + "," + bounds.east + "]") - await ScriptUtils.DownloadJSON(url) - .then(json => { - if (json.elements.length === 0) { - console.log("Got an empty response!") - if ((json.remark ?? "").startsWith("runtime error")) { - console.error("Got a runtime error: ", json.remark) - failed++; - return - } + try { - } - - - console.log("Got the response - writing to ", filename) - writeFileSync(filename, JSON.stringify(json, null, " ")); - } - ) - .catch(err => { - console.log(url) - console.log("Could not download - probably hit the rate limit; waiting a bit. (" + err + ")") + const json = await ScriptUtils.DownloadJSON(url) + if ((json.remark ?? "").startsWith("runtime error")) { + console.error("Got a runtime error: ", json.remark) failed++; - return ScriptUtils.sleep(60000).then(() => console.log("Waiting is done")) - }) + }else if (json.elements.length === 0) { + console.log("Got an empty response! Writing anyway") + } + - if (x < r.xend || y < r.yend) { - console.debug("Cooling down 10s") - await ScriptUtils.sleep(10000) + console.log("Got the response - writing to ", filename) + writeFileSync(filename, JSON.stringify(json, null, " ")); + } catch (err) { + console.log(url) + console.log("Could not download - probably hit the rate limit; waiting a bit. (" + err + ")") + failed++; + await ScriptUtils.sleep(1000) } } } @@ -145,14 +140,16 @@ async function downloadExtraData(theme: LayoutConfig)/* : any[] */ { return allFeatures; } -function postProcess(targetdir: string, r: TileRange, theme: LayoutConfig, extraFeatures: any[]) { + +function loadAllTiles(targetdir: string, r: TileRange, theme: LayoutConfig, extraFeatures: any[]): FeatureSource { + + let allFeatures = [...extraFeatures] let processed = 0; - const layerIndex = theme.LayerIndex(); for (let x = r.xstart; x <= r.xend; x++) { for (let y = r.ystart; y <= r.yend; y++) { processed++; const filename = rawJsonName(targetdir, x, y, r.zoomlevel) - ScriptUtils.erasableLog(" Post processing", processed, "/", r.total, filename) + console.log(" Loading and processing", processed, "/", r.total, filename) if (!existsSync(filename)) { console.error("Not found - and not downloaded. Run this script again!: " + filename) continue; @@ -163,152 +160,100 @@ function postProcess(targetdir: string, r: TileRange, theme: LayoutConfig, extra // Create and save the geojson file - which is the main chunk of the data const geojson = OsmToGeoJson.default(rawOsm); - const osmTime = new Date(rawOsm.osm3s.timestamp_osm_base); - // And merge in the extra features - needed for the metatagging - geojson.features.push(...extraFeatures); - for (const feature of geojson.features) { + allFeatures.push(...geojson.features) + } + } + return new StaticFeatureSource(allFeatures, false) +} - for (const layer of theme.layers) { - if (layer.source.osmTags.matchesProperties(feature.properties)) { - feature["_matching_layer_id"] = layer.id; - break; - } +/** + * Load all the tiles into memory from disk + */ +function postProcess(allFeatures: FeatureSource, theme: LayoutConfig, relationsTracker: RelationsTracker, targetdir: string) { + + + function handleLayer(source: FeatureSourceForLayer) { + const layer = source.layer.layerDef; + const layerId = layer.id + if (layer.source.isOsmCacheLayer !== true) { + return; + } + console.log("Handling layer ", layerId, "which has", source.features.data.length, "features") + if (source.features.data.length === 0) { + return; + } + MetaTagging.addMetatags(source.features.data, + { + memberships: relationsTracker, + getFeaturesWithin: _ => { + return [allFeatures.features.data.map(f => f.feature)] } - } - const featuresFreshness = geojson.features.map(feature => { - return ({ - freshness: osmTime, - feature: feature - }); + }, + layer, + { + includeDates: false, + includeNonDates: true }); - // Extract the relationship information - const relations = ExtractRelations.BuildMembershipTable(ExtractRelations.GetRelationElements(rawOsm)) - MetaTagging.addMetatags(featuresFreshness, new UIEventSource<{ feature: any; freshness: Date }[]>(featuresFreshness), relations, theme.layers, false); - - - for (const feature of geojson.features) { - const layer = layerIndex.get(feature["_matching_layer_id"]) - if (layer === undefined) { - // Probably some extra, unneeded data, e.g. a point of a way - continue + const createdTiles = [] + // At this point, we have all the features of the entire area. + // However, we want to export them per tile of a fixed size, so we use a dynamicTileSOurce to split it up + TiledFeatureSource.createHierarchy(source, { + minZoomLevel: 14, + maxZoomLevel: 14, + maxFeatureCount: undefined, + registerTile: tile => { + if (tile.z < 12) { + return; } - - if (layer.wayHandling == LayerConfig.WAYHANDLING_CENTER_ONLY) { - - const centerpoint = GeoOperations.centerpointCoordinates(feature) - - feature.geometry.type = "Point" - feature.geometry["coordinates"] = centerpoint; - + if (tile.features.data.length === 0) { + return } + for (const feature of tile.features.data) { + // Some cleanup + delete feature.feature["bbox"] + } + // Lets save this tile! + const [z, x, y] = Tiles.tile_from_index(tile.tileIndex) + console.log("Writing tile ", z, x, y, layerId) + const targetPath = geoJsonName(targetdir + "_" + layerId, x, y, z) + createdTiles.push(tile.tileIndex) + // This is the geojson file containing all features for this tile + writeFileSync(targetPath, JSON.stringify({ + type: "FeatureCollection", + features: tile.features.data.map(f => f.feature) + }, null, " ")) } - for (const feature of geojson.features) { - // Some cleanup - delete feature["bbox"] + }) + + // All the tiles are written at this point + // Only thing left to do is to create the index + const path = targetdir + "_" + layerId + "_overview.json" + const perX = {} + createdTiles.map(i => Tiles.tile_from_index(i)).forEach(([z, x, y]) => { + const key = "" + x + if (perX[key] === undefined) { + perX[key] = [] } + perX[key].push(y) + }) + writeFileSync(path, JSON.stringify(perX)) - const targetPath = geoJsonName(targetdir + ".unfiltered", x, y, r.zoomlevel) - // This is the geojson file containing all features - writeFileSync(targetPath, JSON.stringify(geojson, null, " ")) - } } + + new PerLayerFeatureSourceSplitter( + new UIEventSource(theme.layers.map(l => ({ + layerDef: l, + isDisplayed: new UIEventSource(true), + appliedFilters: new UIEventSource(undefined) + }))), + handleLayer, + allFeatures + ) } -function splitPerLayer(targetdir: string, r: TileRange, theme: LayoutConfig) { - const z = r.zoomlevel; - const generated = {} // layer --> x --> y[] - for (let x = r.xstart; x <= r.xend; x++) { - for (let y = r.ystart; y <= r.yend; y++) { - const file = readFileSync(geoJsonName(targetdir + ".unfiltered", x, y, z), "UTF8") - - for (const layer of theme.layers) { - if (!layer.source.isOsmCacheLayer) { - continue; - } - const geojson = JSON.parse(file) - const oldLength = geojson.features.length; - geojson.features = geojson.features - .filter(f => f._matching_layer_id === layer.id) - .filter(f => { - const isShown = layer.isShown.GetRenderValue(f.properties).txt - return isShown !== "no"; - - }) - const new_path = geoJsonName(targetdir + "_" + layer.id, x, y, z); - ScriptUtils.erasableLog(new_path, " has ", geojson.features.length, " features after filtering (dropped ", oldLength - geojson.features.length, ")") - if (geojson.features.length == 0) { - continue; - } - writeFileSync(new_path, JSON.stringify(geojson, null, " ")) - - if (generated[layer.id] === undefined) { - generated[layer.id] = {} - } - if (generated[layer.id][x] === undefined) { - generated[layer.id][x] = [] - } - generated[layer.id][x].push(y) - - } - } - } - - for (const layer of theme.layers) { - const id = layer.id - const loaded = generated[id] - if (loaded === undefined) { - console.log("No features loaded for layer ", id) - continue; - } - writeFileSync(targetdir + "_" + id + "_overview.json", JSON.stringify(loaded)) - } - -} - -async function createOverview(targetdir: string, r: TileRange, z: number, layername: string) { - const allFeatures = [] - for (let x = r.xstart; x <= r.xend; x++) { - for (let y = r.ystart; y <= r.yend; y++) { - const read_path = geoJsonName(targetdir + "_" + layername, x, y, z); - if (!fs.existsSync(read_path)) { - continue; - } - const features = JSON.parse(fs.readFileSync(read_path, "UTF-8")).features - const pointsOnly = features.map(f => { - - f.properties["_last_edit:timestamp"] = "1970-01-01" - - if (f.geometry.type === "Point") { - return f - } else { - return GeoOperations.centerpoint(f) - } - - }) - allFeatures.push(...pointsOnly) - } - } - - const featuresDedup = [] - const seen = new Set() - for (const feature of allFeatures) { - const id = feature.properties.id - if (seen.has(id)) { - continue - } - seen.add(id) - featuresDedup.push(feature) - } - - const geojson = { - "type": "FeatureCollection", - "features": featuresDedup - } - writeFileSync(targetdir + "_" + layername + "_points.geojson", JSON.stringify(geojson, null, " ")) -} async function main(args: string[]) { @@ -324,7 +269,7 @@ async function main(args: string[]) { const lat1 = Number(args[5]) const lon1 = Number(args[6]) - const tileRange = Utils.TileRangeBetween(zoomlevel, lat0, lon0, lat1, lon1) + const tileRange = Tiles.TileRangeBetween(zoomlevel, lat0, lon0, lat1, lon1) const theme = AllKnownLayouts.allKnownLayouts.get(themeName) if (theme === undefined) { @@ -335,12 +280,11 @@ async function main(args: string[]) { console.error("The theme " + theme + " was not found; try one of ", keys); return } - - const overpass = createOverpassObject(theme) + const relationTracker = new RelationsTracker() let failed = 0; do { - const cachingResult = await downloadRaw(targetdir, tileRange, overpass) + const cachingResult = await downloadRaw(targetdir, tileRange, theme, relationTracker) failed = cachingResult.failed if (failed > 0) { await ScriptUtils.sleep(30000) @@ -348,21 +292,13 @@ async function main(args: string[]) { } while (failed > 0) const extraFeatures = await downloadExtraData(theme); - postProcess(targetdir, tileRange, theme, extraFeatures) - splitPerLayer(targetdir, tileRange, theme) + const allFeaturesSource = loadAllTiles(targetdir, tileRange, theme, extraFeatures) + postProcess(allFeaturesSource, theme, relationTracker, targetdir) - if (args[7] === "--generate-point-overview") { - const targetLayers = args[8].split(",") - for (const targetLayer of targetLayers) { - if (!theme.layers.some(l => l.id === targetLayer)) { - throw "Target layer " + targetLayer + " not found, did you mistype the name? Found layers are: " + theme.layers.map(l => l.id).join(",") - } - createOverview(targetdir, tileRange, zoomlevel, targetLayer) - } - } } let args = [...process.argv] args.splice(0, 2) -main(args); \ No newline at end of file +main(args); +console.log("All done!") \ No newline at end of file diff --git a/scripts/generateLayerOverview.ts b/scripts/generateLayerOverview.ts index 969f9e9b74..d97c235cba 100644 --- a/scripts/generateLayerOverview.ts +++ b/scripts/generateLayerOverview.ts @@ -5,6 +5,8 @@ import {LayoutConfigJson} from "../Models/ThemeConfig/Json/LayoutConfigJson"; import LayoutConfig from "../Models/ThemeConfig/LayoutConfig"; import {LayerConfigJson} from "../Models/ThemeConfig/Json/LayerConfigJson"; import LayerConfig from "../Models/ThemeConfig/LayerConfig"; +import {Translation} from "../UI/i18n/Translation"; +import {Utils} from "../Utils"; // This scripts scans 'assets/layers/*.json' for layer definition files and 'assets/themes/*.json' for theme definition files. // It spits out an overview of those to be used to load them @@ -139,6 +141,16 @@ class LayerOverviewUtils { } } } + + const referencedLayers = Utils.NoNull(themeFile.layers.map(layer => { + if(typeof layer === "string"){ + return layer + } + if(layer["builtin"] !== undefined){ + return layer["builtin"] + } + return undefined + })) themeFile.layers = themeFile.layers .filter(l => typeof l != "string") // We remove all the builtin layer references as they don't work with ts-node for some weird reason @@ -154,6 +166,20 @@ class LayerOverviewUtils { if (theme.id !== filename) { themeErrorCount.push("Theme ids should be the same as the name.json, but we got id: " + theme.id + " and filename " + filename + " (" + themePath + ")") } + const neededLanguages = themeFile["mustHaveLanguage"] + if (neededLanguages !== undefined) { + console.log("Checking language requerements for ", theme.id, "as it must have", neededLanguages.join(", ")) + const allTranslations = [].concat(Translation.ExtractAllTranslationsFrom(theme, theme.id), ...referencedLayers.map(layerId => Translation.ExtractAllTranslationsFrom(knownLayerIds.get(layerId), theme.id+"->"+layerId))) + for (const neededLanguage of neededLanguages) { + allTranslations + .filter(t => t.tr.translations[neededLanguage] === undefined && t.tr.translations["*"] === undefined) + .forEach(missing => { + themeErrorCount.push("The theme " + theme.id + " should be translation-complete for " + neededLanguage + ", but it lacks a translation for " + missing.context) + }) + } + + + } } catch (e) { themeErrorCount.push("Could not parse theme " + themeFile["id"] + "due to", e) diff --git a/scripts/generateLicenseInfo.ts b/scripts/generateLicenseInfo.ts index 0a559a77cf..f9e6d485fc 100644 --- a/scripts/generateLicenseInfo.ts +++ b/scripts/generateLicenseInfo.ts @@ -72,6 +72,14 @@ knownLicenses.set("me", { sources: [] }) +knownLicenses.set("streetcomplete", { + authors: ["Tobias Zwick (westnordost)"], + path: undefined, + license: "CC0", + sources: ["https://github.com/streetcomplete/StreetComplete/tree/master/res/graphics","https://f-droid.org/packages/de.westnordost.streetcomplete/"] +}) + + knownLicenses.set("t", { authors: [], path: undefined, diff --git a/scripts/generateTranslations.ts b/scripts/generateTranslations.ts index 27b2482ac2..d6d8c11d20 100644 --- a/scripts/generateTranslations.ts +++ b/scripts/generateTranslations.ts @@ -33,7 +33,7 @@ class TranslationPart { } const v = translations[translationsKey] if (typeof (v) != "string") { - console.error("Non-string object in translation: ", translations[translationsKey]) + console.error("Non-string object in translation while trying to add more translations to '", translationsKey ,"': ", v) throw "Error in an object depicting a translation: a non-string object was found. (" + context + ")\n You probably put some other section accidentally in the translation" } this.contents.set(translationsKey, v) @@ -41,9 +41,7 @@ class TranslationPart { } recursiveAdd(object: any, context: string) { - - - const isProbablyTranslationObject = knownLanguages.map(l => object.hasOwnProperty(l)).filter(x => x).length > 0; + const isProbablyTranslationObject = knownLanguages.some(l => object.hasOwnProperty(l)); if (isProbablyTranslationObject) { this.addTranslationObject(object, context) return; @@ -302,7 +300,11 @@ function MergeTranslation(source: any, target: any, language: string, context: s } if (typeof sourceV === "object") { if (targetV === undefined) { + try{ target[language] = sourceV; + }catch(e){ + throw `At context${context}: Could not add a translation in language ${language} due to ${e}` + } } else { MergeTranslation(sourceV, targetV, language, context + "." + key); } diff --git a/scripts/generateWikiPage.ts b/scripts/generateWikiPage.ts index ca03e3fb58..688dfd2fed 100644 --- a/scripts/generateWikiPage.ts +++ b/scripts/generateWikiPage.ts @@ -20,8 +20,8 @@ function generateWikiEntry(layout: LayoutConfig) { |name= [https://mapcomplete.osm.be/${layout.id} ${layout.id}] |region= Worldwide |lang= ${languages} -|descr= A MapComplete theme: ${Translations.W(layout.description) - .InnerRenderAsString() +|descr= A MapComplete theme: ${Translations.WT(layout.description) + .textFor("en") .replace(".*<\/a>/, "]]") } diff --git a/tailwind.config.js b/tailwind.config.js index 7d9320cc49..fe744d0b2c 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -1,9 +1,10 @@ const plugin = require('tailwindcss/plugin') module.exports = { + mode: 'jit', purge: [ - // './**/*.html', - // './**/*.js', + './**/*.html', + './**/*.ts', ], darkMode: false, // or 'media' or 'class' theme: { @@ -20,9 +21,9 @@ module.exports = { } }, plugins: [ - plugin(function ({addVariant, e}) { - addVariant('landscape', ({modifySelectors, separator}) => { - modifySelectors(({className}) => { + plugin(function ({ addVariant, e }) { + addVariant('landscape', ({ modifySelectors, separator }) => { + modifySelectors(({ className }) => { return `.${e(`landscape${separator}${className}`)}:landscape` }) }) diff --git a/test.html b/test.html index dd85639392..e3f35827bb 100644 --- a/test.html +++ b/test.html @@ -10,6 +10,8 @@ + + diff --git a/test.ts b/test.ts index c34a6c3033..e6f2a99662 100644 --- a/test.ts +++ b/test.ts @@ -1,16 +1,26 @@ -const client_token = "MLY|4441509239301885|b40ad2d3ea105435bd40c7e76993ae85" +import FeatureInfoBox from "./UI/Popup/FeatureInfoBox"; +import {UIEventSource} from "./Logic/UIEventSource"; +import AllKnownLayers from "./Customizations/AllKnownLayers"; +import State from "./State"; +import {AllKnownLayouts} from "./Customizations/AllKnownLayouts"; -const image_id = '196804715753265'; -const api_url = 'https://graph.mapillary.com/' + image_id + '?fields=thumb_1024_url&&access_token=' + client_token; -fetch(api_url, - { - headers: {'Authorization': 'OAuth ' + client_token} - } -).then(response => { - return response.json() -}).then( - json => { - const thumbnail_url = json["thumb_1024"] - console.log(thumbnail_url) - } -) \ No newline at end of file +State.state = new State(AllKnownLayouts.allKnownLayouts.get("charging_stations")) +State.state.changes.pendingChanges.setData([]) +const geojson = { + type: "Feature", + geometry: { + type: "Point", + coordinates: [51.0, 4] + }, + properties: + { + id: "node/42", + amenity: "charging_station", + } +} +State.state.allElements.addOrGetElement(geojson) +const tags = State.state.allElements.getEventSourceById("node/42") +new FeatureInfoBox( + tags, + AllKnownLayers.sharedLayers.get("charging_station") +).AttachTo("maindiv") \ No newline at end of file diff --git a/test/GeoOperations.spec.ts b/test/GeoOperations.spec.ts index 7a556b20d2..56e07acaf1 100644 --- a/test/GeoOperations.spec.ts +++ b/test/GeoOperations.spec.ts @@ -2,6 +2,8 @@ import {Utils} from "../Utils"; import * as Assert from "assert"; import T from "./TestHelper"; import {GeoOperations} from "../Logic/GeoOperations"; +import {equal} from "assert"; +import {BBox} from "../Logic/BBox"; Utils.runningFromConsole = true; @@ -176,7 +178,16 @@ export default class GeoOperationsSpec extends T { const overlap = GeoOperations.calculateOverlap(point, [GeoOperationsSpec.polygon]); Assert.equal(1, overlap.length) - }] + }], + ["bbox bounds test", + () => { + const bbox = BBox.fromTile(16, 32754, 21785) + equal(-0.076904296875, bbox.minLon) + equal(-0.0714111328125, bbox.maxLon) + equal(51.5292513551899, bbox.minLat) + equal(51.53266860674158, bbox.maxLat) + } + ] ] ) diff --git a/test/ImageAttribution.spec.ts b/test/ImageAttribution.spec.ts index c83d9efa19..ac21096441 100644 --- a/test/ImageAttribution.spec.ts +++ b/test/ImageAttribution.spec.ts @@ -10,7 +10,7 @@ Utils.runningFromConsole = true; export default class ImageAttributionSpec extends T { constructor() { super( - "ImageAttribution Tests", [ + "imageattribution", [ [ "Should find all the images", () => { diff --git a/test/ImageSearcher.spec.ts b/test/ImageSearcher.spec.ts deleted file mode 100644 index 49254fd62b..0000000000 --- a/test/ImageSearcher.spec.ts +++ /dev/null @@ -1,28 +0,0 @@ -import {Utils} from "../Utils"; -import {equal} from "assert"; -import T from "./TestHelper"; -import {UIEventSource} from "../Logic/UIEventSource"; -import {ImageSearcher} from "../Logic/Actors/ImageSearcher"; - -Utils.runningFromConsole = true; -export default class ImageSearcherSpec extends T { - - constructor() { - super("ImageSearcher", [ - [ - "Should find images", - () => { - const tags = new UIEventSource({ - "mapillary": "https://www.mapillary.com/app/?pKey=bYH6FFl8LXAPapz4PNSh3Q" - }); - const searcher = ImageSearcher.construct(tags) - const result = searcher.data[0]; - equal(result.url, "https://www.mapillary.com/map/im/bYH6FFl8LXAPapz4PNSh3Q"); - } - ], - ]); - - } - - -} diff --git a/test/OsmConnection.spec.ts b/test/OsmConnection.spec.ts index a2f7cca479..9d7b114f50 100644 --- a/test/OsmConnection.spec.ts +++ b/test/OsmConnection.spec.ts @@ -2,6 +2,9 @@ import T from "./TestHelper"; import UserDetails, {OsmConnection} from "../Logic/Osm/OsmConnection"; import {UIEventSource} from "../Logic/UIEventSource"; import ScriptUtils from "../scripts/ScriptUtils"; +import {AllKnownLayouts} from "../Customizations/AllKnownLayouts"; +import {ElementStorage} from "../Logic/ElementStorage"; +import {Changes} from "../Logic/Osm/Changes"; export default class OsmConnectionSpec extends T { @@ -12,15 +15,17 @@ export default class OsmConnectionSpec extends T { private static _osm_token = "LJFmv2nUicSNmBNsFeyCHx5KKx6Aiesx8pXPbX4n" constructor() { - super("OsmConnectionSpec-test", [ + super("osmconnection", [ ["login on dev", () => { - const osmConn = new OsmConnection(false, false, - new UIEventSource(undefined), - "Unit test", - true, - "osm-test" - ) + const osmConn = new OsmConnection({ + osmConfiguration: "osm-test", + layoutName: "Unit test", + allElements: new ElementStorage(), + changes: new Changes(), + oauth_token: new UIEventSource(OsmConnectionSpec._osm_token) + } + ); osmConn.userDetails.map((userdetails: UserDetails) => { if (userdetails.loggedIn) { diff --git a/test/OsmObject.spec.ts b/test/OsmObject.spec.ts index 563c5ade6b..e26dae4d6d 100644 --- a/test/OsmObject.spec.ts +++ b/test/OsmObject.spec.ts @@ -1,27 +1,26 @@ import T from "./TestHelper"; import {OsmObject} from "../Logic/Osm/OsmObject"; import ScriptUtils from "../scripts/ScriptUtils"; +import {UIEventSource} from "../Logic/UIEventSource"; export default class OsmObjectSpec extends T { + private static async runTest(){ + const ways = await OsmObject.DownloadReferencingWays("node/1124134958") + if(ways === undefined){ + throw "Did not get the ways" + } + if (ways.length !== 4) { + throw "Expected 4 ways but got "+ways.length + } + } + + constructor() { - super("OsmObject", [ + super("osmobject", [ [ "Download referencing ways", () => { - let downloaded = false; - OsmObject.DownloadReferencingWays("node/1124134958").addCallbackAndRunD(ways => { - downloaded = true; - console.log(ways) - }) - let timeout = 10 - while (!downloaded && timeout >= 0) { - ScriptUtils.sleep(1000) - - timeout--; - } - if (!downloaded) { - throw "Timeout: referencing ways not found" - } + OsmObjectSpec.runTest().then(_ => console.log("Referencing ways test is done (async)")) } ] diff --git a/test/RelationSplitHandler.spec.ts b/test/RelationSplitHandler.spec.ts new file mode 100644 index 0000000000..0b5261f136 --- /dev/null +++ b/test/RelationSplitHandler.spec.ts @@ -0,0 +1,89 @@ +import T from "./TestHelper"; +import {InPlaceReplacedmentRTSH} from "../Logic/Osm/Actions/RelationSplitHandler"; +import {OsmObject, OsmRelation} from "../Logic/Osm/OsmObject"; +import {Changes} from "../Logic/Osm/Changes"; +import {equal} from "assert"; +import {Utils} from "../Utils"; + +export default class RelationSplitHandlerSpec extends T { + + private static async split(): Promise { + // Lets mimick a split action of https://www.openstreetmap.org/way/295132739 + + const relation: OsmRelation = await OsmObject.DownloadObjectAsync("relation/9572808") + const originalNodeIds = [5273988967, + 170497153, + 1507524582, + 4524321710, + 170497155, + 170497157, + 170497158, + 3208166179, + 1507524610, + 170497160, + 3208166178, + 1507524573, + 1575932830, + 6448669326] + + const withSplit = [[5273988967, + 170497153, + 1507524582, + 4524321710, + 170497155, + 170497157, + 170497158], + [ + 3208166179, + 1507524610, + 170497160, + 3208166178, + 1507524573, + 1575932830, + 6448669326]] + + const splitter = new InPlaceReplacedmentRTSH( + { + relation: relation, + originalWayId: 295132739, + allWayIdsInOrder: [295132739, -1], + originalNodes: originalNodeIds, + allWaysNodesInOrder: withSplit + }) + const changeDescription = await splitter.CreateChangeDescriptions(new Changes()) + const allIds = changeDescription[0].changes["members"].map(m => m.ref).join(",") + const expected = "687866206,295132739,-1,690497698" + if (allIds.indexOf(expected) < 0) { + throw "Invalid order or the split ways. If this suddenly breaks, the parent relation at https://osm.org/relation/9572808 has probably changed and the test must be updated" + } + } + + constructor() { + + Utils.injectJsonDownloadForTests( + "https://www.openstreetmap.org/api/0.6/node/1124134958/ways", + {"version":"0.6","generator":"CGImap 0.8.5 (2937646 spike-07.openstreetmap.org)","copyright":"OpenStreetMap and contributors","attribution":"http://www.openstreetmap.org/copyright","license":"http://opendatacommons.org/licenses/odbl/1-0/","elements":[{"type":"way","id":97038428,"timestamp":"2019-06-19T12:26:24Z","version":6,"changeset":71399984,"user":"Pieter Vander Vennet","uid":3818858,"nodes":[1124134958,323729212,323729351,2542460408,187073405],"tags":{"highway":"residential","name":"Brugs-Kerkhofstraat","sett:pattern":"arc","surface":"sett"}},{"type":"way","id":97038434,"timestamp":"2019-06-19T12:26:24Z","version":5,"changeset":71399984,"user":"Pieter Vander Vennet","uid":3818858,"nodes":[1124134958,1124135024,187058607],"tags":{"bicycle":"use_sidepath","highway":"residential","name":"Kerkhofblommenstraat","sett:pattern":"arc","surface":"sett"}},{"type":"way","id":97038435,"timestamp":"2017-12-21T21:41:08Z","version":4,"changeset":54826837,"user":"Jakka","uid":2403313,"nodes":[1124134958,2576628889,1124135035,5298371485,5298371495],"tags":{"bicycle":"use_sidepath","highway":"residential","name":"Kerkhofblommenstraat"}},{"type":"way","id":251446313,"timestamp":"2019-01-07T19:22:47Z","version":4,"changeset":66106872,"user":"M!dgard","uid":763799,"nodes":[1124134958,5243143198,4555715455],"tags":{"foot":"yes","highway":"service"}}]} + ) + + Utils.injectJsonDownloadForTests( + "https://www.openstreetmap.org/api/0.6/relation/9572808", + {"version":"0.6","generator":"CGImap 0.8.5 (3128319 spike-07.openstreetmap.org)","copyright":"OpenStreetMap and contributors","attribution":"http://www.openstreetmap.org/copyright","license":"http://opendatacommons.org/licenses/odbl/1-0/","elements":[{"type":"relation","id":9572808,"timestamp":"2021-08-12T12:44:06Z","version":11,"changeset":109573204,"user":"A67-A67","uid":553736,"members":[{"type":"way","ref":173662702,"role":""},{"type":"way","ref":467606230,"role":""},{"type":"way","ref":126267167,"role":""},{"type":"way","ref":301897426,"role":""},{"type":"way","ref":687866206,"role":""},{"type":"way","ref":295132739,"role":""},{"type":"way","ref":690497698,"role":""},{"type":"way","ref":627893684,"role":""},{"type":"way","ref":295132741,"role":""},{"type":"way","ref":301903120,"role":""},{"type":"way","ref":672541156,"role":""},{"type":"way","ref":126264330,"role":""},{"type":"way","ref":280440853,"role":""},{"type":"way","ref":838499667,"role":""},{"type":"way","ref":838499663,"role":""},{"type":"way","ref":690497623,"role":""},{"type":"way","ref":301902946,"role":""},{"type":"way","ref":280460715,"role":""},{"type":"way","ref":972534369,"role":""},{"type":"way","ref":695680702,"role":""},{"type":"way","ref":690497860,"role":""},{"type":"way","ref":295410363,"role":""},{"type":"way","ref":823864063,"role":""},{"type":"way","ref":663172088,"role":""},{"type":"way","ref":659950322,"role":""},{"type":"way","ref":659950323,"role":""},{"type":"way","ref":230180094,"role":""},{"type":"way","ref":690497912,"role":""},{"type":"way","ref":39588765,"role":""}],"tags":{"distance":"13 km","name":"Abdijenroute","network":"lcn","old_name":"Spoorlijn 58","operator":"Toerisme West-Vlaanderen","railway":"abandoned","route":"bicycle","type":"route","wikipedia":"nl:Spoorlijn 58"}}]} + ) + + Utils.injectJsonDownloadForTests( + "https://www.openstreetmap.org/api/0.6/way/687866206/full", + {"version":"0.6","generator":"CGImap 0.8.5 (2601512 spike-07.openstreetmap.org)","copyright":"OpenStreetMap and contributors","attribution":"http://www.openstreetmap.org/copyright","license":"http://opendatacommons.org/licenses/odbl/1-0/","elements":[{"type":"node","id":5273988959,"lat":51.1811406,"lon":3.2427712,"timestamp":"2021-07-29T21:14:53Z","version":6,"changeset":108847202,"user":"kaart_fietser","uid":11022240,"tags":{"network:type":"node_network","rwn_ref":"32"}},{"type":"node","id":6448669326,"lat":51.1811346,"lon":3.242891,"timestamp":"2019-05-04T22:44:12Z","version":1,"changeset":69891295,"user":"Pieter Vander Vennet","uid":3818858,"tags":{"barrier":"bollard"}},{"type":"way","id":687866206,"timestamp":"2019-05-06T20:52:20Z","version":2,"changeset":69951497,"user":"noelbov","uid":8054928,"nodes":[6448669326,5273988959],"tags":{"highway":"cycleway","name":"Abdijenroute","railway":"abandoned","surface":"asphalt"}}]} + ) + + Utils.injectJsonDownloadForTests( + "https://www.openstreetmap.org/api/0.6/way/690497698/full" , + {"version":"0.6","generator":"CGImap 0.8.5 (3023311 spike-07.openstreetmap.org)","copyright":"OpenStreetMap and contributors","attribution":"http://www.openstreetmap.org/copyright","license":"http://opendatacommons.org/licenses/odbl/1-0/","elements":[{"type":"node","id":170497152,"lat":51.1832353,"lon":3.2498759,"timestamp":"2018-04-24T00:29:37Z","version":7,"changeset":58357376,"user":"Pieter Vander Vennet","uid":3818858},{"type":"node","id":2988218625,"lat":51.1835053,"lon":3.2503067,"timestamp":"2018-09-24T21:48:46Z","version":2,"changeset":62895918,"user":"A67-A67","uid":553736},{"type":"node","id":5273988967,"lat":51.182659,"lon":3.249004,"timestamp":"2017-12-09T18:40:21Z","version":1,"changeset":54493533,"user":"CacherB","uid":1999108},{"type":"way","id":690497698,"timestamp":"2021-07-29T21:14:53Z","version":3,"changeset":108847202,"user":"kaart_fietser","uid":11022240,"nodes":[2988218625,170497152,5273988967],"tags":{"highway":"cycleway","lit":"no","name":"Abdijenroute","oneway":"no","railway":"abandoned","surface":"compacted"}}]} + ) + + + super("relationsplithandler", [ + ["split 295132739", + () => RelationSplitHandlerSpec.split().then(_ => console.log("OK"))] + ]); + } +} \ No newline at end of file diff --git a/test/SplitAction.spec.ts b/test/SplitAction.spec.ts new file mode 100644 index 0000000000..6e4ed77a9a --- /dev/null +++ b/test/SplitAction.spec.ts @@ -0,0 +1,274 @@ +import T from "./TestHelper"; +import {Changes} from "../Logic/Osm/Changes"; +import SplitAction from "../Logic/Osm/Actions/SplitAction"; +import {equal} from "assert"; +import {Utils} from "../Utils"; + +export default class SplitActionSpec extends T { + + + private static async split(): Promise { + + Utils.injectJsonDownloadForTests( + "https://www.openstreetmap.org/api/0.6/way/295132739/full", + { + "version": "0.6", + "generator": "CGImap 0.8.5 (3138407 spike-07.openstreetmap.org)", + "copyright": "OpenStreetMap and contributors", + "attribution": "http://www.openstreetmap.org/copyright", + "license": "http://opendatacommons.org/licenses/odbl/1-0/", + "elements": [{ + "type": "node", + "id": 170497153, + "lat": 51.1825167, + "lon": 3.2487885, + "timestamp": "2011-11-18T16:33:43Z", + "version": 5, + "changeset": 9865255, + "user": "TripleBee", + "uid": 497177 + }, { + "type": "node", + "id": 170497155, + "lat": 51.1817632, + "lon": 3.2472706, + "timestamp": "2011-11-18T16:33:43Z", + "version": 5, + "changeset": 9865255, + "user": "TripleBee", + "uid": 497177 + }, { + "type": "node", + "id": 170497157, + "lat": 51.1815203, + "lon": 3.2465569, + "timestamp": "2011-11-18T16:33:43Z", + "version": 5, + "changeset": 9865255, + "user": "TripleBee", + "uid": 497177 + }, { + "type": "node", + "id": 170497158, + "lat": 51.1812261, + "lon": 3.2454261, + "timestamp": "2011-11-18T16:33:43Z", + "version": 5, + "changeset": 9865255, + "user": "TripleBee", + "uid": 497177 + }, { + "type": "node", + "id": 170497160, + "lat": 51.1810957, + "lon": 3.2443030, + "timestamp": "2011-11-18T16:33:43Z", + "version": 5, + "changeset": 9865255, + "user": "TripleBee", + "uid": 497177 + }, { + "type": "node", + "id": 1507524573, + "lat": 51.1810778, + "lon": 3.2437148, + "timestamp": "2011-11-18T16:33:36Z", + "version": 1, + "changeset": 9865255, + "user": "TripleBee", + "uid": 497177 + }, { + "type": "node", + "id": 1507524582, + "lat": 51.1821130, + "lon": 3.2481284, + "timestamp": "2011-11-18T16:33:37Z", + "version": 1, + "changeset": 9865255, + "user": "TripleBee", + "uid": 497177 + }, { + "type": "node", + "id": 1507524610, + "lat": 51.1811645, + "lon": 3.2450828, + "timestamp": "2011-11-18T16:33:38Z", + "version": 1, + "changeset": 9865255, + "user": "TripleBee", + "uid": 497177 + }, { + "type": "node", + "id": 1575932830, + "lat": 51.1811153, + "lon": 3.2431503, + "timestamp": "2019-05-04T22:44:13Z", + "version": 2, + "changeset": 69891295, + "user": "Pieter Vander Vennet", + "uid": 3818858 + }, { + "type": "node", + "id": 3208166178, + "lat": 51.1810837, + "lon": 3.2439090, + "timestamp": "2014-11-27T20:23:10Z", + "version": 1, + "changeset": 27076816, + "user": "JanFi", + "uid": 672253 + }, { + "type": "node", + "id": 3208166179, + "lat": 51.1812062, + "lon": 3.2453151, + "timestamp": "2014-11-27T20:23:10Z", + "version": 1, + "changeset": 27076816, + "user": "JanFi", + "uid": 672253 + }, { + "type": "node", + "id": 4524321710, + "lat": 51.1820656, + "lon": 3.2480253, + "timestamp": "2017-12-09T18:56:37Z", + "version": 2, + "changeset": 54493928, + "user": "CacherB", + "uid": 1999108 + }, { + "type": "node", + "id": 5273988967, + "lat": 51.1826590, + "lon": 3.2490040, + "timestamp": "2017-12-09T18:40:21Z", + "version": 1, + "changeset": 54493533, + "user": "CacherB", + "uid": 1999108 + }, { + "type": "node", + "id": 6448669326, + "lat": 51.1811346, + "lon": 3.2428910, + "timestamp": "2019-05-04T22:44:12Z", + "version": 1, + "changeset": 69891295, + "user": "Pieter Vander Vennet", + "uid": 3818858, + "tags": {"barrier": "bollard"} + }, { + "type": "way", + "id": 295132739, + "timestamp": "2021-07-29T21:14:53Z", + "version": 17, + "changeset": 108847202, + "user": "kaart_fietser", + "uid": 11022240, + "nodes": [5273988967, 170497153, 1507524582, 4524321710, 170497155, 170497157, 170497158, 3208166179, 1507524610, 170497160, 3208166178, 1507524573, 1575932830, 6448669326], + "tags": { + "highway": "cycleway", + "name": "Abdijenroute", + "railway": "abandoned", + "surface": "compacted" + } + }] + }) + Utils.injectJsonDownloadForTests( + "https://www.openstreetmap.org/api/0.6/way/295132739/relations", + // Mimick that there are no relations relation is missing + { + "version": "0.6", + "generator": "CGImap 0.8.5 (2935793 spike-07.openstreetmap.org)", + "copyright": "OpenStreetMap and contributors", + "attribution": "http://www.openstreetmap.org/copyright", + "license": "http://opendatacommons.org/licenses/odbl/1-0/", + "elements": [] + } + ) + + // Lets split road https://www.openstreetmap.org/way/295132739 + const id = "way/295132739" + const splitPoint: [number, number] = [3.246733546257019, 51.181710380278176] + const splitter = new SplitAction(id, [splitPoint]) + const changeDescription = await splitter.CreateChangeDescriptions(new Changes()) + + equal(changeDescription[0].type, "node") + equal(changeDescription[0].id, -1) + equal(changeDescription[0].changes["lat"], 51.181710380278176) + equal(changeDescription[0].changes["lon"], 3.246733546257019) + + equal(changeDescription[1].type, "way") + equal(changeDescription[1].id, -2) + equal(changeDescription[1].changes["coordinates"].length, 6) + equal(changeDescription[1].changes["coordinates"][5][0], splitPoint[0]) + equal(changeDescription[1].changes["coordinates"][5][1], splitPoint[1]) + + equal(changeDescription[2].type, "way") + equal(changeDescription[2].id, 295132739) + equal(changeDescription[2].changes["coordinates"].length, 10) + equal(changeDescription[2].changes["coordinates"][0][0], splitPoint[0]) + equal(changeDescription[2].changes["coordinates"][0][1], splitPoint[1]) + } + + + private static async SplitHoutkaai() : Promise{ + + Utils.injectJsonDownloadForTests( + "https://www.openstreetmap.org/api/0.6/way/61435323/full" , + {"version":"0.6","generator":"CGImap 0.8.5 (53092 spike-08.openstreetmap.org)","copyright":"OpenStreetMap and contributors","attribution":"http://www.openstreetmap.org/copyright","license":"http://opendatacommons.org/licenses/odbl/1-0/","elements":[{"type":"node","id":766990983,"lat":51.2170219,"lon":3.2022337,"timestamp":"2021-04-26T15:48:22Z","version":6,"changeset":103647857,"user":"M!dgard","uid":763799},{"type":"node","id":766990985,"lat":51.2169574,"lon":3.2017548,"timestamp":"2016-07-05T22:41:12Z","version":6,"changeset":40511250,"user":"M!dgard","uid":763799},{"type":"node","id":8669018379,"lat":51.2169592,"lon":3.2017683,"timestamp":"2021-04-26T15:48:22Z","version":1,"changeset":103647857,"user":"M!dgard","uid":763799},{"type":"way","id":61435323,"timestamp":"2021-08-21T12:24:13Z","version":7,"changeset":110026637,"user":"Thibault Rommel","uid":5846458,"nodes":[766990983,8669018379,766990985],"tags":{"bicycle":"yes","bridge":"yes","cycleway":"shared_lane","highway":"unclassified","layer":"1","maxspeed":"50","name":"Houtkaai","surface":"asphalt","zone:traffic":"BE-VLG:urban"}}]} + ) + Utils.injectJsonDownloadForTests( + "https://www.openstreetmap.org/api/0.6/way/61435323/relations" , + {"version":"0.6","generator":"CGImap 0.8.5 (3622541 spike-06.openstreetmap.org)","copyright":"OpenStreetMap and contributors","attribution":"http://www.openstreetmap.org/copyright","license":"http://opendatacommons.org/licenses/odbl/1-0/","elements":[{"type":"relation","id":1723870,"timestamp":"2021-09-18T06:29:31Z","version":183,"changeset":111362343,"user":"emvee","uid":5211,"members":[{"type":"way","ref":261428947,"role":""},{"type":"way","ref":162774622,"role":""},{"type":"way","ref":317060244,"role":""},{"type":"way","ref":81155378,"role":""},{"type":"way","ref":99749583,"role":""},{"type":"way","ref":131332113,"role":""},{"type":"way","ref":949518831,"role":""},{"type":"way","ref":99749584,"role":""},{"type":"way","ref":129133519,"role":""},{"type":"way","ref":73241312,"role":""},{"type":"way","ref":785514256,"role":""},{"type":"way","ref":58509643,"role":""},{"type":"way","ref":73241332,"role":""},{"type":"way","ref":58509653,"role":""},{"type":"way","ref":100044097,"role":""},{"type":"way","ref":946999067,"role":""},{"type":"way","ref":73241327,"role":""},{"type":"way","ref":58509617,"role":""},{"type":"way","ref":58509627,"role":""},{"type":"way","ref":69990655,"role":""},{"type":"way","ref":73241311,"role":""},{"type":"way","ref":123142336,"role":""},{"type":"way","ref":249671053,"role":""},{"type":"way","ref":73241324,"role":""},{"type":"way","ref":66706953,"role":""},{"type":"way","ref":112679357,"role":""},{"type":"way","ref":112679358,"role":""},{"type":"way","ref":53105113,"role":""},{"type":"way","ref":66706952,"role":""},{"type":"way","ref":64083661,"role":""},{"type":"way","ref":53105162,"role":""},{"type":"way","ref":249671070,"role":""},{"type":"way","ref":249671064,"role":""},{"type":"way","ref":101498587,"role":""},{"type":"way","ref":69001236,"role":""},{"type":"way","ref":101498585,"role":""},{"type":"way","ref":70909444,"role":""},{"type":"way","ref":73241314,"role":""},{"type":"way","ref":69001235,"role":""},{"type":"way","ref":113150200,"role":""},{"type":"way","ref":137305843,"role":""},{"type":"way","ref":936827687,"role":""},{"type":"way","ref":936827688,"role":""},{"type":"way","ref":112952373,"role":""},{"type":"way","ref":930798379,"role":""},{"type":"way","ref":930798378,"role":""},{"type":"way","ref":112951439,"role":""},{"type":"way","ref":445541591,"role":""},{"type":"way","ref":103843896,"role":""},{"type":"way","ref":23734118,"role":""},{"type":"way","ref":103840557,"role":""},{"type":"way","ref":433852210,"role":""},{"type":"way","ref":313604670,"role":""},{"type":"way","ref":103839402,"role":""},{"type":"way","ref":23736061,"role":""},{"type":"way","ref":73241328,"role":""},{"type":"way","ref":295392689,"role":""},{"type":"way","ref":297168171,"role":""},{"type":"way","ref":297168170,"role":""},{"type":"way","ref":433852205,"role":""},{"type":"way","ref":295392695,"role":""},{"type":"way","ref":663268954,"role":""},{"type":"way","ref":663267598,"role":""},{"type":"way","ref":292478843,"role":""},{"type":"way","ref":981853853,"role":""},{"type":"way","ref":663270140,"role":""},{"type":"way","ref":981853854,"role":""},{"type":"way","ref":295392703,"role":""},{"type":"way","ref":663304916,"role":""},{"type":"way","ref":297169116,"role":""},{"type":"way","ref":295400810,"role":""},{"type":"way","ref":981853855,"role":""},{"type":"way","ref":663304806,"role":""},{"type":"way","ref":516452870,"role":""},{"type":"way","ref":66459239,"role":""},{"type":"way","ref":791430504,"role":""},{"type":"way","ref":178926037,"role":""},{"type":"way","ref":864799431,"role":""},{"type":"way","ref":178926107,"role":""},{"type":"way","ref":663320459,"role":""},{"type":"way","ref":62033993,"role":""},{"type":"way","ref":62283023,"role":""},{"type":"way","ref":62283057,"role":""},{"type":"way","ref":62283032,"role":""},{"type":"way","ref":490551085,"role":""},{"type":"way","ref":435318979,"role":""},{"type":"way","ref":371750677,"role":""},{"type":"way","ref":371750670,"role":""},{"type":"way","ref":371750673,"role":""},{"type":"way","ref":371750675,"role":""},{"type":"way","ref":459885691,"role":""},{"type":"way","ref":371750669,"role":""},{"type":"way","ref":371750668,"role":""},{"type":"way","ref":371750667,"role":""},{"type":"way","ref":428848639,"role":""},{"type":"way","ref":371750666,"role":""},{"type":"way","ref":371750665,"role":""},{"type":"way","ref":825496473,"role":""},{"type":"way","ref":371750664,"role":""},{"type":"way","ref":371750662,"role":""},{"type":"way","ref":371750663,"role":""},{"type":"way","ref":371750660,"role":""},{"type":"way","ref":371750658,"role":""},{"type":"way","ref":40507374,"role":""},{"type":"way","ref":165878356,"role":""},{"type":"way","ref":165878355,"role":""},{"type":"way","ref":8494219,"role":""},{"type":"way","ref":5023947,"role":""},{"type":"way","ref":5023939,"role":""},{"type":"way","ref":26718843,"role":""},{"type":"way","ref":79437029,"role":""},{"type":"way","ref":87522151,"role":""},{"type":"way","ref":26718848,"role":""},{"type":"way","ref":233169831,"role":""},{"type":"way","ref":85934460,"role":""},{"type":"way","ref":145892210,"role":""},{"type":"way","ref":79434764,"role":""},{"type":"way","ref":127079185,"role":""},{"type":"way","ref":67794715,"role":""},{"type":"way","ref":85934250,"role":""},{"type":"way","ref":421566302,"role":""},{"type":"way","ref":123445537,"role":""},{"type":"way","ref":308077683,"role":""},{"type":"way","ref":308077684,"role":""},{"type":"way","ref":972955357,"role":""},{"type":"way","ref":308077682,"role":""},{"type":"way","ref":659880052,"role":""},{"type":"way","ref":308077681,"role":""},{"type":"way","ref":66364130,"role":""},{"type":"way","ref":51086959,"role":""},{"type":"way","ref":51086961,"role":""},{"type":"way","ref":102154586,"role":""},{"type":"way","ref":102154589,"role":""},{"type":"way","ref":703008376,"role":""},{"type":"way","ref":703008375,"role":""},{"type":"way","ref":54435150,"role":""},{"type":"way","ref":115913100,"role":""},{"type":"way","ref":79433785,"role":""},{"type":"way","ref":51204355,"role":""},{"type":"way","ref":422395066,"role":""},{"type":"way","ref":116628138,"role":""},{"type":"way","ref":690189323,"role":""},{"type":"way","ref":132068368,"role":""},{"type":"way","ref":690220771,"role":""},{"type":"way","ref":690220772,"role":""},{"type":"way","ref":690226744,"role":""},{"type":"way","ref":690226745,"role":""},{"type":"way","ref":60253953,"role":""},{"type":"way","ref":690195774,"role":""},{"type":"way","ref":688104939,"role":""},{"type":"way","ref":422395064,"role":"forward"},{"type":"way","ref":422309497,"role":"forward"},{"type":"way","ref":25677204,"role":"forward"},{"type":"way","ref":51570941,"role":""},{"type":"way","ref":807329786,"role":""},{"type":"way","ref":165500495,"role":""},{"type":"way","ref":689494106,"role":""},{"type":"way","ref":131476435,"role":""},{"type":"way","ref":689493508,"role":""},{"type":"way","ref":12126873,"role":""},{"type":"way","ref":32789519,"role":""},{"type":"way","ref":27288122,"role":""},{"type":"way","ref":116717060,"role":""},{"type":"way","ref":176380249,"role":""},{"type":"way","ref":116717052,"role":""},{"type":"way","ref":176380250,"role":""},{"type":"way","ref":421998791,"role":""},{"type":"way","ref":34562745,"role":""},{"type":"way","ref":130473931,"role":""},{"type":"way","ref":136487196,"role":""},{"type":"way","ref":23792223,"role":""},{"type":"way","ref":23775021,"role":""},{"type":"way","ref":560506339,"role":""},{"type":"way","ref":337945886,"role":""},{"type":"way","ref":61435332,"role":""},{"type":"way","ref":61435323,"role":""},{"type":"way","ref":509668834,"role":""},{"type":"way","ref":130473917,"role":""},{"type":"way","ref":369929894,"role":""},{"type":"way","ref":805247467,"role":"forward"},{"type":"way","ref":840210016,"role":"forward"},{"type":"way","ref":539026983,"role":"forward"},{"type":"way","ref":539037793,"role":"forward"},{"type":"way","ref":244428576,"role":"forward"},{"type":"way","ref":243333119,"role":"forward"},{"type":"way","ref":243333108,"role":"forward"},{"type":"way","ref":243333106,"role":"forward"},{"type":"way","ref":243333110,"role":"forward"},{"type":"way","ref":230511503,"role":"forward"},{"type":"way","ref":510520445,"role":"forward"},{"type":"way","ref":688103605,"role":"forward"},{"type":"way","ref":668577053,"role":"forward"},{"type":"way","ref":4332489,"role":"forward"},{"type":"way","ref":668577051,"role":"forward"},{"type":"way","ref":185476761,"role":"forward"},{"type":"way","ref":100774483,"role":"forward"},{"type":"way","ref":668672434,"role":"backward"},{"type":"way","ref":488558133,"role":"backward"},{"type":"way","ref":13943237,"role":"forward"},{"type":"way","ref":840241791,"role":"forward"},{"type":"way","ref":805247468,"role":"forward"},{"type":"way","ref":539040946,"role":"forward"},{"type":"way","ref":539026103,"role":"forward"},{"type":"way","ref":539037781,"role":"forward"},{"type":"way","ref":28942112,"role":"forward"},{"type":"way","ref":699841535,"role":"forward"},{"type":"way","ref":635374201,"role":"forward"},{"type":"way","ref":28942118,"role":"forward"},{"type":"way","ref":185476755,"role":"forward"},{"type":"way","ref":78794903,"role":"forward"},{"type":"way","ref":688103599,"role":"forward"},{"type":"way","ref":688103600,"role":"backward"},{"type":"way","ref":32699077,"role":"backward"},{"type":"way","ref":249092420,"role":"backward"},{"type":"way","ref":540048295,"role":""},{"type":"way","ref":13942938,"role":""},{"type":"way","ref":827705395,"role":""},{"type":"way","ref":72492953,"role":""},{"type":"way","ref":61435342,"role":""},{"type":"way","ref":95106180,"role":""},{"type":"way","ref":182691326,"role":""},{"type":"way","ref":180915274,"role":""},{"type":"way","ref":61435340,"role":""},{"type":"way","ref":95506626,"role":""},{"type":"way","ref":183330864,"role":""},{"type":"way","ref":318631002,"role":""},{"type":"way","ref":4332470,"role":""},{"type":"way","ref":318631014,"role":""},{"type":"way","ref":337969633,"role":""},{"type":"way","ref":668566903,"role":""},{"type":"way","ref":668566904,"role":""},{"type":"way","ref":248228679,"role":""},{"type":"way","ref":419296358,"role":""},{"type":"way","ref":601005356,"role":""},{"type":"way","ref":497802656,"role":""},{"type":"way","ref":948484806,"role":""},{"type":"way","ref":756223825,"role":""},{"type":"way","ref":23206884,"role":""},{"type":"way","ref":157436856,"role":""},{"type":"way","ref":829398288,"role":""},{"type":"way","ref":829398289,"role":""},{"type":"way","ref":674490354,"role":""},{"type":"way","ref":131704173,"role":""},{"type":"way","ref":120976014,"role":""},{"type":"way","ref":38864144,"role":""},{"type":"way","ref":38864143,"role":""},{"type":"way","ref":32147475,"role":""},{"type":"way","ref":962256846,"role":""},{"type":"way","ref":32147479,"role":""},{"type":"way","ref":32147481,"role":""},{"type":"way","ref":49486734,"role":""},{"type":"way","ref":829394351,"role":""},{"type":"way","ref":829394349,"role":""},{"type":"way","ref":235193261,"role":""},{"type":"way","ref":130495866,"role":""},{"type":"way","ref":978366962,"role":""},{"type":"way","ref":39588752,"role":""},{"type":"way","ref":436528651,"role":""},{"type":"way","ref":27370335,"role":""},{"type":"way","ref":157558803,"role":""},{"type":"way","ref":39590466,"role":""},{"type":"way","ref":157558804,"role":""},{"type":"way","ref":27370165,"role":""},{"type":"way","ref":970841665,"role":""}],"tags":{"name":"Euroroute R1 - part Belgium","name:de":"Europaradweg R1 - Abschnitt Belgien","name:nl":"Euroroute R1 - deel België","network":"icn","ref":"R1","route":"bicycle","type":"route"}},{"type":"relation","id":1757007,"timestamp":"2020-10-13T01:31:44Z","version":10,"changeset":92380204,"user":"Diabolix","uid":2123963,"members":[{"type":"way","ref":509668834,"role":""},{"type":"way","ref":61435323,"role":""},{"type":"way","ref":61435332,"role":""},{"type":"way","ref":337945886,"role":""},{"type":"way","ref":560506339,"role":""},{"type":"way","ref":23775021,"role":""},{"type":"way","ref":23792223,"role":""}],"tags":{"network":"rcn","network:type":"node_network","ref":"4-36","route":"bicycle","type":"route"}},{"type":"relation","id":5150189,"timestamp":"2021-09-09T20:15:58Z","version":44,"changeset":110993632,"user":"JosV","uid":170722,"members":[{"type":"way","ref":13943237,"role":""},{"type":"way","ref":488558133,"role":""},{"type":"way","ref":369929894,"role":""},{"type":"way","ref":130473917,"role":""},{"type":"way","ref":509668834,"role":""},{"type":"way","ref":61435323,"role":""},{"type":"way","ref":61435332,"role":""},{"type":"way","ref":337945886,"role":""},{"type":"way","ref":560506339,"role":""},{"type":"way","ref":23775021,"role":""},{"type":"way","ref":23792223,"role":""},{"type":"way","ref":136487196,"role":""},{"type":"way","ref":130473931,"role":""},{"type":"way","ref":34562745,"role":""},{"type":"way","ref":421998791,"role":""},{"type":"way","ref":126996864,"role":""},{"type":"way","ref":126996861,"role":""},{"type":"way","ref":170989337,"role":""},{"type":"way","ref":72482534,"role":""},{"type":"way","ref":58913500,"role":""},{"type":"way","ref":72482539,"role":""},{"type":"way","ref":246969243,"role":""},{"type":"way","ref":153150902,"role":""},{"type":"way","ref":116748588,"role":""},{"type":"way","ref":72482544,"role":""},{"type":"way","ref":72482542,"role":""},{"type":"way","ref":337013552,"role":""},{"type":"way","ref":132790401,"role":""},{"type":"way","ref":105166767,"role":""},{"type":"way","ref":720356345,"role":""},{"type":"way","ref":197829999,"role":""},{"type":"way","ref":105166552,"role":""},{"type":"way","ref":61979075,"role":""},{"type":"way","ref":197830184,"role":""},{"type":"way","ref":61979070,"role":""},{"type":"way","ref":948826013,"role":""},{"type":"way","ref":197830182,"role":""},{"type":"way","ref":672535497,"role":""},{"type":"way","ref":672535498,"role":""},{"type":"way","ref":948826015,"role":""},{"type":"way","ref":11378674,"role":""},{"type":"way","ref":672535496,"role":""},{"type":"way","ref":70023921,"role":""},{"type":"way","ref":948826017,"role":""},{"type":"way","ref":197830260,"role":""},{"type":"way","ref":152210843,"role":""},{"type":"way","ref":33748055,"role":""},{"type":"way","ref":344701437,"role":""},{"type":"way","ref":422150672,"role":""},{"type":"way","ref":156228338,"role":""},{"type":"way","ref":422150674,"role":""},{"type":"way","ref":223674432,"role":""},{"type":"way","ref":223674437,"role":""},{"type":"way","ref":156228327,"role":""},{"type":"way","ref":223674372,"role":""},{"type":"way","ref":592937889,"role":""},{"type":"way","ref":592937890,"role":""},{"type":"way","ref":422099666,"role":""},{"type":"way","ref":422100304,"role":""},{"type":"way","ref":948826022,"role":""},{"type":"way","ref":15092930,"role":""},{"type":"way","ref":948826024,"role":""},{"type":"way","ref":105182226,"role":""},{"type":"way","ref":133606215,"role":""},{"type":"way","ref":533395656,"role":""},{"type":"way","ref":187115987,"role":""},{"type":"way","ref":105182230,"role":""},{"type":"way","ref":105182232,"role":""},{"type":"way","ref":196011634,"role":""},{"type":"way","ref":153273480,"role":""},{"type":"way","ref":153273481,"role":""},{"type":"way","ref":881767783,"role":""},{"type":"way","ref":153273479,"role":""},{"type":"way","ref":13462242,"role":""},{"type":"way","ref":498093425,"role":""},{"type":"way","ref":70009137,"role":""},{"type":"way","ref":12086805,"role":""},{"type":"way","ref":52523332,"role":""},{"type":"way","ref":70009138,"role":""},{"type":"way","ref":592937884,"role":""},{"type":"way","ref":15071942,"role":""},{"type":"way","ref":180798233,"role":""},{"type":"way","ref":70010670,"role":""},{"type":"way","ref":15802818,"role":""},{"type":"way","ref":15802809,"role":""},{"type":"way","ref":70011254,"role":""},{"type":"way","ref":671368756,"role":""},{"type":"way","ref":840241791,"role":""},{"type":"way","ref":369929367,"role":""},{"type":"way","ref":539038988,"role":""},{"type":"way","ref":80130513,"role":""},{"type":"way","ref":540214122,"role":""},{"type":"way","ref":765795083,"role":""},{"type":"way","ref":13943005,"role":""},{"type":"way","ref":72492950,"role":""},{"type":"way","ref":183330864,"role":""},{"type":"way","ref":318631002,"role":""},{"type":"way","ref":4332470,"role":""},{"type":"way","ref":318631014,"role":""},{"type":"way","ref":337969633,"role":""},{"type":"way","ref":668566903,"role":""},{"type":"way","ref":668566904,"role":""},{"type":"way","ref":248228679,"role":""},{"type":"way","ref":419296358,"role":""},{"type":"way","ref":601005356,"role":""},{"type":"way","ref":497802656,"role":""},{"type":"way","ref":948484806,"role":""},{"type":"way","ref":100323579,"role":""},{"type":"way","ref":100708215,"role":""},{"type":"way","ref":124559834,"role":""},{"type":"way","ref":124559835,"role":""},{"type":"way","ref":239484694,"role":""},{"type":"way","ref":972646812,"role":""},{"type":"way","ref":124559832,"role":""},{"type":"way","ref":361686157,"role":""},{"type":"way","ref":361686155,"role":""},{"type":"way","ref":239484693,"role":""},{"type":"way","ref":19861731,"role":""},{"type":"way","ref":967906429,"role":""},{"type":"way","ref":126402539,"role":""},{"type":"way","ref":94427058,"role":""},{"type":"way","ref":126402541,"role":""},{"type":"way","ref":313693839,"role":""},{"type":"way","ref":313693838,"role":""},{"type":"way","ref":970740536,"role":""},{"type":"way","ref":361719175,"role":""},{"type":"way","ref":663186012,"role":""},{"type":"way","ref":744625794,"role":""},{"type":"way","ref":94569877,"role":""},{"type":"way","ref":188973964,"role":""},{"type":"way","ref":948484822,"role":""},{"type":"way","ref":28857260,"role":""},{"type":"way","ref":948484821,"role":""},{"type":"way","ref":219185860,"role":""},{"type":"way","ref":948484818,"role":""},{"type":"way","ref":219185861,"role":""},{"type":"way","ref":229885580,"role":""},{"type":"way","ref":28857247,"role":""},{"type":"way","ref":128813937,"role":""},{"type":"way","ref":32148201,"role":""},{"type":"way","ref":829398290,"role":""},{"type":"way","ref":829398288,"role":""},{"type":"way","ref":157436856,"role":""},{"type":"way","ref":23206887,"role":""},{"type":"way","ref":657081380,"role":""},{"type":"way","ref":948484817,"role":""},{"type":"way","ref":657081379,"role":""},{"type":"way","ref":657083379,"role":""},{"type":"way","ref":657083378,"role":""},{"type":"way","ref":72492956,"role":""},{"type":"way","ref":183763716,"role":""},{"type":"way","ref":497802654,"role":""},{"type":"way","ref":497802655,"role":""},{"type":"way","ref":348402994,"role":""},{"type":"way","ref":497802653,"role":""},{"type":"way","ref":948484813,"role":""},{"type":"way","ref":272353449,"role":"forward"},{"type":"way","ref":497802652,"role":"forward"},{"type":"way","ref":948484811,"role":""},{"type":"way","ref":948484810,"role":""},{"type":"way","ref":136564089,"role":""},{"type":"way","ref":970740538,"role":""},{"type":"way","ref":970740539,"role":""},{"type":"way","ref":433455263,"role":""},{"type":"way","ref":23206893,"role":""},{"type":"way","ref":95506626,"role":""},{"type":"way","ref":61435340,"role":""},{"type":"way","ref":180915274,"role":""},{"type":"way","ref":182691326,"role":""},{"type":"way","ref":95106180,"role":""},{"type":"way","ref":61435342,"role":""},{"type":"way","ref":72492953,"role":""},{"type":"way","ref":827705395,"role":""},{"type":"way","ref":13942938,"role":""},{"type":"way","ref":540048295,"role":""},{"type":"way","ref":249092420,"role":""},{"type":"way","ref":32699077,"role":""},{"type":"way","ref":688103600,"role":""},{"type":"way","ref":654338684,"role":"forward"},{"type":"way","ref":11018710,"role":"forward"},{"type":"way","ref":510825612,"role":"forward"},{"type":"way","ref":70011248,"role":"forward"},{"type":"way","ref":654338685,"role":"forward"},{"type":"way","ref":14626290,"role":""},{"type":"way","ref":70011250,"role":""},{"type":"way","ref":12295471,"role":""},{"type":"way","ref":397097504,"role":""},{"type":"way","ref":12295484,"role":""},{"type":"way","ref":41990436,"role":""},{"type":"way","ref":70011252,"role":""},{"type":"way","ref":61503690,"role":""},{"type":"way","ref":182978284,"role":""},{"type":"way","ref":790820260,"role":"forward"},{"type":"way","ref":592937894,"role":"forward"},{"type":"way","ref":926028042,"role":"forward"},{"type":"way","ref":592937902,"role":"forward"},{"type":"way","ref":592937901,"role":"forward"},{"type":"way","ref":182978255,"role":"forward"},{"type":"way","ref":592937903,"role":"forward"},{"type":"way","ref":12123659,"role":"forward"},{"type":"way","ref":666877213,"role":"forward"},{"type":"way","ref":790820259,"role":"forward"},{"type":"way","ref":510825618,"role":""},{"type":"way","ref":13496412,"role":""},{"type":"way","ref":654338689,"role":""},{"type":"way","ref":740935312,"role":""},{"type":"way","ref":52288671,"role":""},{"type":"way","ref":52288667,"role":""},{"type":"way","ref":12123458,"role":""},{"type":"way","ref":508681905,"role":""},{"type":"way","ref":15071314,"role":""},{"type":"way","ref":61503700,"role":""},{"type":"way","ref":41989874,"role":""},{"type":"way","ref":328002077,"role":""},{"type":"way","ref":396377151,"role":""},{"type":"way","ref":396377150,"role":""},{"type":"way","ref":396377125,"role":""},{"type":"way","ref":328985990,"role":""},{"type":"way","ref":328985992,"role":""},{"type":"way","ref":328985993,"role":""},{"type":"way","ref":328985991,"role":""},{"type":"way","ref":632506298,"role":""},{"type":"way","ref":101191104,"role":""},{"type":"way","ref":499129522,"role":""},{"type":"way","ref":15071174,"role":""},{"type":"way","ref":297023609,"role":""},{"type":"way","ref":297023610,"role":""},{"type":"way","ref":297023608,"role":""},{"type":"way","ref":112695115,"role":""},{"type":"way","ref":584024902,"role":""},{"type":"way","ref":243543197,"role":""},{"type":"way","ref":101191119,"role":"forward"},{"type":"way","ref":173530022,"role":"forward"},{"type":"way","ref":265137637,"role":"forward"},{"type":"way","ref":160627684,"role":"forward"},{"type":"way","ref":657163351,"role":"forward"},{"type":"way","ref":160627682,"role":"forward"},{"type":"way","ref":160632906,"role":"forward"},{"type":"way","ref":176870850,"role":"forward"},{"type":"way","ref":173662701,"role":"forward"},{"type":"way","ref":173662702,"role":""},{"type":"way","ref":467606230,"role":""},{"type":"way","ref":126267167,"role":""},{"type":"way","ref":301897426,"role":""},{"type":"way","ref":687866206,"role":""},{"type":"way","ref":295132739,"role":""},{"type":"way","ref":690497698,"role":""},{"type":"way","ref":627893684,"role":""},{"type":"way","ref":295132741,"role":""},{"type":"way","ref":301903120,"role":""},{"type":"way","ref":672541156,"role":""},{"type":"way","ref":126264330,"role":""},{"type":"way","ref":280440853,"role":""},{"type":"way","ref":838499667,"role":""},{"type":"way","ref":838499663,"role":""},{"type":"way","ref":690497623,"role":""},{"type":"way","ref":301902946,"role":""},{"type":"way","ref":280460715,"role":""},{"type":"way","ref":972534369,"role":""},{"type":"way","ref":588764361,"role":""},{"type":"way","ref":981365419,"role":""},{"type":"way","ref":188979882,"role":""},{"type":"way","ref":578030518,"role":""},{"type":"way","ref":124559857,"role":""},{"type":"way","ref":284568605,"role":""},{"type":"way","ref":126405025,"role":""},{"type":"way","ref":188978777,"role":""},{"type":"way","ref":272353445,"role":"forward"},{"type":"way","ref":221443952,"role":"forward"},{"type":"way","ref":172708119,"role":"forward"},{"type":"way","ref":173061662,"role":"forward"},{"type":"way","ref":441663456,"role":"forward"},{"type":"way","ref":160627680,"role":"forward"},{"type":"way","ref":176870852,"role":"forward"},{"type":"way","ref":39588762,"role":"forward"},{"type":"way","ref":172709466,"role":"forward"},{"type":"way","ref":598459103,"role":"forward"},{"type":"way","ref":688054392,"role":"forward"},{"type":"way","ref":155986859,"role":"forward"}],"tags":{"name":"Groene Gordel Brugge","network":"lcn","ref":"GGB","route":"bicycle","type":"route"}},{"type":"relation","id":8369765,"timestamp":"2021-08-23T14:22:45Z","version":19,"changeset":110120188,"user":"Pieter Vander Vennet","uid":3818858,"members":[{"type":"way","ref":539038988,"role":""},{"type":"way","ref":369929367,"role":""},{"type":"way","ref":840241791,"role":""},{"type":"way","ref":488558133,"role":""},{"type":"way","ref":369929894,"role":""},{"type":"way","ref":130473917,"role":""},{"type":"way","ref":509668834,"role":""},{"type":"way","ref":61435323,"role":""},{"type":"way","ref":61435332,"role":""},{"type":"way","ref":337945886,"role":""},{"type":"way","ref":560506339,"role":""},{"type":"way","ref":23775021,"role":""},{"type":"way","ref":23792223,"role":""},{"type":"way","ref":136487196,"role":""},{"type":"way","ref":130473931,"role":""},{"type":"way","ref":34562745,"role":""},{"type":"way","ref":421998791,"role":""},{"type":"way","ref":176380250,"role":""},{"type":"way","ref":116717052,"role":""},{"type":"way","ref":176380249,"role":""},{"type":"way","ref":116717060,"role":""},{"type":"way","ref":27288122,"role":""},{"type":"way","ref":32789519,"role":""},{"type":"way","ref":12126873,"role":""},{"type":"way","ref":689493508,"role":""},{"type":"way","ref":131476435,"role":""},{"type":"way","ref":689494106,"role":""},{"type":"way","ref":165500495,"role":""},{"type":"way","ref":807329786,"role":""},{"type":"way","ref":51570941,"role":""},{"type":"way","ref":422309497,"role":""},{"type":"way","ref":240869981,"role":""},{"type":"way","ref":240869873,"role":""},{"type":"way","ref":240869980,"role":""},{"type":"way","ref":165503767,"role":""},{"type":"way","ref":165503764,"role":""},{"type":"way","ref":421566315,"role":""},{"type":"way","ref":165503768,"role":""},{"type":"way","ref":245236630,"role":""},{"type":"way","ref":658500046,"role":"forward"},{"type":"way","ref":646903393,"role":"forward"},{"type":"way","ref":245236632,"role":"forward"},{"type":"way","ref":245236633,"role":"forward"},{"type":"way","ref":90485426,"role":""},{"type":"way","ref":596073878,"role":""},{"type":"way","ref":10898401,"role":"backward"},{"type":"way","ref":658500044,"role":"forward"},{"type":"way","ref":474253371,"role":"forward"},{"type":"way","ref":474253369,"role":"forward"},{"type":"way","ref":474253376,"role":"forward"},{"type":"way","ref":165845350,"role":"backward"},{"type":"way","ref":130697218,"role":""},{"type":"way","ref":61565721,"role":""},{"type":"way","ref":497202210,"role":""},{"type":"way","ref":130697226,"role":""},{"type":"way","ref":227617858,"role":""},{"type":"way","ref":227617857,"role":""},{"type":"way","ref":681804956,"role":""},{"type":"way","ref":165881675,"role":""},{"type":"way","ref":806146504,"role":""},{"type":"way","ref":806146505,"role":""},{"type":"way","ref":659762284,"role":""}],"tags":{"alt_name":"Fietssnelweg F30 Brugge - Oostende","bicycle:type":"utility","cycle_highway":"yes","cycle_network":"BE-VLG:cycle_highway","name":"F30 Fietssnelweg Brugge - Oostende","network":"ncn","operator":"Provincie West-Vlaanderen","ref":"F30","route":"bicycle","state":"proposed","type":"route","website":"https://fietssnelwegen.be/f30","wikidata":"Q107485732"}},{"type":"relation","id":13060733,"timestamp":"2021-09-19T18:08:57Z","version":5,"changeset":111419581,"user":"L'imaginaire","uid":654234,"members":[{"type":"way","ref":23792223,"role":""},{"type":"way","ref":23775021,"role":""},{"type":"way","ref":560506339,"role":""},{"type":"way","ref":337945886,"role":""},{"type":"way","ref":61435332,"role":""},{"type":"way","ref":61435323,"role":""},{"type":"way","ref":509668834,"role":""},{"type":"way","ref":839596136,"role":""},{"type":"way","ref":840488274,"role":""},{"type":"way","ref":839596137,"role":""},{"type":"way","ref":146172188,"role":""},{"type":"way","ref":749212030,"role":""},{"type":"way","ref":799479035,"role":""},{"type":"way","ref":130473928,"role":""},{"type":"way","ref":61414103,"role":""},{"type":"way","ref":539672618,"role":""},{"type":"way","ref":799479034,"role":""},{"type":"way","ref":539672617,"role":""},{"type":"way","ref":539672616,"role":""},{"type":"way","ref":539671786,"role":""},{"type":"way","ref":172317285,"role":""},{"type":"way","ref":35328157,"role":""},{"type":"way","ref":249119335,"role":""},{"type":"way","ref":584214875,"role":""},{"type":"way","ref":584217798,"role":""},{"type":"way","ref":676801473,"role":""},{"type":"way","ref":456588356,"role":""},{"type":"way","ref":456589109,"role":""},{"type":"way","ref":456588496,"role":""},{"type":"way","ref":487199906,"role":""},{"type":"way","ref":299450868,"role":""},{"type":"way","ref":165548222,"role":""},{"type":"way","ref":4329135,"role":""},{"type":"way","ref":4329771,"role":""},{"type":"way","ref":155149803,"role":""},{"type":"way","ref":305625031,"role":""},{"type":"way","ref":100842624,"role":""},{"type":"way","ref":18102445,"role":""},{"type":"way","ref":541116658,"role":""},{"type":"way","ref":591094005,"role":""},{"type":"way","ref":591094004,"role":""},{"type":"way","ref":184684947,"role":""},{"type":"way","ref":34945088,"role":""},{"type":"way","ref":235195315,"role":""},{"type":"way","ref":497849660,"role":""}],"tags":{"colour":"#e40613","cycle_network":"BE-VLG:icoonroutes","description":"segment 2 van de Kunststedenroute","fixme":"incomplete","from":"Oostende","name":"Kunststedenroute - 02 - Oostende - Brugge","network":"ncn","operator":"Toerisme Vlaanderen","ref":"Kunst","route":"bicycle","to":"Brugge","type":"route","website":"https://www.vlaanderenmetdefiets.be/routes/kunststeden.html","wikidata":"Q106529274","wikipedia":"nl:LF Kunststedenroute"}}]} + ) + Utils.injectJsonDownloadForTests( + "https://www.openstreetmap.org/api/0.6/way/61435332/full" , + {"version":"0.6","generator":"CGImap 0.8.5 (3819319 spike-06.openstreetmap.org)","copyright":"OpenStreetMap and contributors","attribution":"http://www.openstreetmap.org/copyright","license":"http://opendatacommons.org/licenses/odbl/1-0/","elements":[{"type":"node","id":766990985,"lat":51.2169574,"lon":3.2017548,"timestamp":"2016-07-05T22:41:12Z","version":6,"changeset":40511250,"user":"M!dgard","uid":763799},{"type":"node","id":3450208876,"lat":51.2169482,"lon":3.2016802,"timestamp":"2016-07-05T22:41:11Z","version":2,"changeset":40511250,"user":"M!dgard","uid":763799},{"type":"way","id":61435332,"timestamp":"2021-08-21T12:24:13Z","version":8,"changeset":110026637,"user":"Thibault Rommel","uid":5846458,"nodes":[766990985,3450208876],"tags":{"bicycle":"yes","cycleway":"shared_lane","highway":"unclassified","maxspeed":"50","name":"Houtkaai","surface":"asphalt","zone:traffic":"BE-VLG:urban"}}]} + ) + Utils.injectJsonDownloadForTests( + "https://www.openstreetmap.org/api/0.6/way/509668834/full" , + {"version":"0.6","generator":"CGImap 0.8.5 (3735280 spike-06.openstreetmap.org)","copyright":"OpenStreetMap and contributors","attribution":"http://www.openstreetmap.org/copyright","license":"http://opendatacommons.org/licenses/odbl/1-0/","elements":[{"type":"node","id":131917824,"lat":51.2170327,"lon":3.2023577,"timestamp":"2019-09-16T09:48:28Z","version":17,"changeset":74521581,"user":"Peter Elderson","uid":7103674,"tags":{"network:type":"node_network","rcn_ref":"4","rcn_region":"Brugse Ommeland"}},{"type":"node","id":766990983,"lat":51.2170219,"lon":3.2022337,"timestamp":"2021-04-26T15:48:22Z","version":6,"changeset":103647857,"user":"M!dgard","uid":763799},{"type":"way","id":509668834,"timestamp":"2021-08-21T12:24:13Z","version":5,"changeset":110026637,"user":"Thibault Rommel","uid":5846458,"nodes":[131917824,766990983],"tags":{"bicycle":"yes","cycleway":"shared_lane","highway":"residential","lit":"yes","maxspeed":"30","name":"Houtkaai","sidewalk":"both","surface":"paving_stones","zone:maxspeed":"BE:30","zone:traffic":"BE-VLG:urban"}}]} + ) + + + const id = "way/61435323" + const splitPoint: [number, number] = [ 3.2021324336528774, 51.2170001600597] + const splitter = new SplitAction(id, [splitPoint]) + const changeDescription = await splitter.CreateChangeDescriptions(new Changes()) + + // Should be a new node + equal(changeDescription[0].type ,"node") + equal(changeDescription[3].type , "relation") + } + + private static async splitWithPointReuse(): Promise { + // Lets split road near an already existing point https://www.openstreetmap.org/way/295132739 + const id = "way/295132739" + const splitPoint: [number, number] = [3.2451081275939937, 51.18116898253599] + const splitter = new SplitAction(id, [splitPoint]) + const changeDescription = await splitter.CreateChangeDescriptions(new Changes()) + + equal(2, changeDescription.length) + const ch0 = changeDescription[0] + const ch1 = changeDescription[1] + const nodes0: number[] = ch0.changes["nodes"] + const nodes1: number[] = ch1.changes["nodes"] + equal(ch0.type, "way") + equal(ch1.type, "way") + equal(nodes0[nodes0.length - 1], nodes1[0]) + equal(3208166179, nodes1[0]) + } + + constructor() { + super("splitaction", [ + ["split 295132739", + () => SplitActionSpec.split().then(_ => console.log("OK"))], + ["split 295132739 on already existing node", + () => SplitActionSpec.splitWithPointReuse().then(_ => console.log("OK"))], + ["split 61435323 on already existing node", + () => SplitActionSpec.SplitHoutkaai().then(_ => console.log("OK"))] + ]); + } +} \ No newline at end of file diff --git a/test/Tag.spec.ts b/test/Tag.spec.ts index 96afa16fcb..a85d3c58dd 100644 --- a/test/Tag.spec.ts +++ b/test/Tag.spec.ts @@ -16,7 +16,7 @@ Utils.runningFromConsole = true; export default class TagSpec extends T { constructor() { - super("Tags", [ + super("tag", [ ["Tag replacement works in translation", () => { const tr = new Translation({ "en": "Test {key} abc" diff --git a/test/TestAll.ts b/test/TestAll.ts index 8eff8b56db..bdb92afdb1 100644 --- a/test/TestAll.ts +++ b/test/TestAll.ts @@ -1,51 +1,66 @@ -import {Utils} from "../Utils"; -Utils.runningFromConsole = true; import TagSpec from "./Tag.spec"; import ImageAttributionSpec from "./ImageAttribution.spec"; import GeoOperationsSpec from "./GeoOperations.spec"; -import ImageSearcherSpec from "./ImageSearcher.spec"; import ThemeSpec from "./Theme.spec"; import UtilsSpec from "./Utils.spec"; -import OsmConnectionSpec from "./OsmConnection.spec"; -import T from "./TestHelper"; -import {FixedUiElement} from "../UI/Base/FixedUiElement"; -import Combine from "../UI/Base/Combine"; import OsmObjectSpec from "./OsmObject.spec"; import ScriptUtils from "../scripts/ScriptUtils"; import UnitsSpec from "./Units.spec"; +import RelationSplitHandlerSpec from "./RelationSplitHandler.spec"; +import SplitActionSpec from "./SplitAction.spec"; +import {Utils} from "../Utils"; +import TileFreshnessCalculatorSpec from "./TileFreshnessCalculator.spec"; +import WikidataSpecTest from "./Wikidata.spec.test"; - -export default class TestAll { - private needsBrowserTests: T[] = [new OsmConnectionSpec()] - - public testAll(): void { - Utils.runningFromConsole = false - for (const test of this.needsBrowserTests.concat(allTests)) { - if (test.failures.length > 0) { - new Combine([new FixedUiElement("TEST FAILED: " + test.name).SetStyle("background: red"), - ...test.failures]) - .AttachTo("maindiv") - throw "Some test failed" - } - } - } -} ScriptUtils.fixUtils() const allTests = [ new OsmObjectSpec(), new TagSpec(), new ImageAttributionSpec(), new GeoOperationsSpec(), - new ImageSearcherSpec(), new ThemeSpec(), new UtilsSpec(), - new UnitsSpec() + new UnitsSpec(), + new RelationSplitHandlerSpec(), + new SplitActionSpec(), + new TileFreshnessCalculatorSpec(), + new WikidataSpecTest() ] +Utils.externalDownloadFunction = async (url) => { + console.error("Fetching ", url, "blocked in tests, use Utils.injectJsonDownloadForTests") + const data = await ScriptUtils.DownloadJSON(url) + console.log("\n\n ----------- \nBLOCKED DATA\n Utils.injectJsonDownloadForTests(\n" + + " ", JSON.stringify(url),", \n", + " ", JSON.stringify(data), "\n )\n------------------\n\n") + throw "Detected internet access for URL " + url + ", please inject it with Utils.injectJsonDownloadForTests" +} -for (const test of allTests) { - if (test.failures.length > 0) { - throw "Some test failed: " + test.failures.join(", ") +let args = [...process.argv] +args.splice(0, 2) +args = args.map(a => a.toLowerCase()) + +const allFailures: { testsuite: string, name: string, msg: string } [] = [] +let testsToRun = allTests +if (args.length > 0) { + testsToRun = allTests.filter(t => args.indexOf(t.name) >= 0) +} + +if (testsToRun.length == 0) { + throw "No tests found" +} + +for (let i = 0; i < testsToRun.length; i++) { + const test = testsToRun[i]; + console.log(" Running test", i, "/", allTests.length, test.name) + allFailures.push(...(test.Run() ?? [])) + console.log("OK!") +} +if (allFailures.length > 0) { + for (const failure of allFailures) { + console.error(" !! " + failure.testsuite + "." + failure.name + " failed due to: " + failure.msg) } -} \ No newline at end of file + throw "Some test failed" +} +console.log("All tests successful: ", testsToRun.map(t => t.name).join(", ")) diff --git a/test/TestHelper.ts b/test/TestHelper.ts index f080c0aa6b..971b64396e 100644 --- a/test/TestHelper.ts +++ b/test/TestHelper.ts @@ -1,23 +1,31 @@ export default class T { - public readonly failures: string[] = [] public readonly name: string; + private readonly _tests: [string, (() => void)][]; constructor(testsuite: string, tests: [string, () => void][]) { this.name = testsuite - for (const [name, test] of tests) { + this._tests = tests; + } + + /** + * RUns the test, returns the error messages. + * Returns an empty list if successful + * @constructor + */ + public Run() : ({testsuite: string, name: string, msg: string} []) { + const failures: {testsuite: string, name: string, msg: string} [] = [] + for (const [name, test] of this._tests) { try { test(); } catch (e) { - this.failures.push(name); - console.warn(`>>> Failed test in ${this.name}: ${name}because${e}`); + failures.push({testsuite: this.name, name: name, msg: ""+e}); } } - if (this.failures.length == 0) { - console.log(`All tests of ${testsuite} done!`) + if (failures.length == 0) { + return undefined } else { - console.warn(this.failures.length, `tests of ${testsuite} failed :(`) - console.log("Failed tests: ", this.failures.join(",")) + return failures } } diff --git a/test/Theme.spec.ts b/test/Theme.spec.ts index d4b071d6ed..cb6cc27565 100644 --- a/test/Theme.spec.ts +++ b/test/Theme.spec.ts @@ -8,7 +8,7 @@ Utils.runningFromConsole = true; export default class ThemeSpec extends T { constructor() { - super("Theme tests", + super("theme", [ ["Nested overrides work", () => { diff --git a/test/TileFreshnessCalculator.spec.ts b/test/TileFreshnessCalculator.spec.ts new file mode 100644 index 0000000000..6305b9a5dc --- /dev/null +++ b/test/TileFreshnessCalculator.spec.ts @@ -0,0 +1,31 @@ +import T from "./TestHelper"; +import TileFreshnessCalculator from "../Logic/FeatureSource/TileFreshnessCalculator"; +import {Tiles} from "../Models/TileRange"; +import {equal} from "assert"; + +export default class TileFreshnessCalculatorSpec extends T { + + constructor() { + super("TileFreshnessCalculatorSpec", [ + [ + "TileFresnessTests", + () => { + const calc = new TileFreshnessCalculator(); + // 19/266407/175535 + const date = new Date() + date.setTime(42) + calc.addTileLoad(Tiles.tile_index(19, 266406, 175534), date) + equal(42, calc.freshnessFor(19, 266406, 175534).getTime()) + equal(42, calc.freshnessFor(20, 266406 * 2, 175534 * 2 + 1).getTime()) + equal(undefined, calc.freshnessFor(19, 266406, 175535)) + equal(undefined, calc.freshnessFor(18, 266406 / 2, 175534 / 2)) + calc.addTileLoad(Tiles.tile_index(19, 266406, 175534+1), date) + calc.addTileLoad(Tiles.tile_index(19, 266406+1, 175534), date) + calc.addTileLoad(Tiles.tile_index(19, 266406+1, 175534+1), date) + equal(42, calc.freshnessFor(18, 266406 / 2, 175534 / 2).getTime()) + } + ] + ]) + } + +} \ No newline at end of file diff --git a/test/Units.spec.ts b/test/Units.spec.ts index 3d316535a8..e0ffdee101 100644 --- a/test/Units.spec.ts +++ b/test/Units.spec.ts @@ -6,7 +6,7 @@ import {Denomination} from "../Models/Denomination"; export default class UnitsSpec extends T { constructor() { - super("Units", [ + super("units", [ ["Simple canonicalize", () => { const unit = new Denomination({ diff --git a/test/Utils.spec.ts b/test/Utils.spec.ts index 0975621e1d..fd630257d0 100644 --- a/test/Utils.spec.ts +++ b/test/Utils.spec.ts @@ -39,7 +39,7 @@ export default class UtilsSpec extends T { } constructor() { - super("Utils", [ + super("utils", [ ["Sort object keys", () => { const o = { x: 'x', diff --git a/test/Wikidata.spec.test.ts b/test/Wikidata.spec.test.ts new file mode 100644 index 0000000000..000a772176 --- /dev/null +++ b/test/Wikidata.spec.test.ts @@ -0,0 +1,29 @@ +import Wikidata from "../Logic/Web/Wikidata"; +import * as assert from "assert"; +import {equal} from "assert"; +import T from "./TestHelper"; +import {Utils} from "../Utils"; + +export default class WikidataSpecTest extends T { + constructor() { + super("Wikidata", + [ + ["download wikidata", + async () => { + + Utils.injectJsonDownloadForTests( + "https://www.wikidata.org/wiki/Special:EntityData/Q14517013.json" , + {"entities":{"Q14517013":{"pageid":16187848,"ns":0,"title":"Q14517013","lastrevid":1408823680,"modified":"2021-04-26T07:35:01Z","type":"item","id":"Q14517013","labels":{"nl":{"language":"nl","value":"Vredesmolen"},"en":{"language":"en","value":"Peace Mill"}},"descriptions":{"nl":{"language":"nl","value":"molen in West-Vlaanderen"}},"aliases":{},"claims":{"P625":[{"mainsnak":{"snaktype":"value","property":"P625","hash":"d86538f14e8cca00bbf30fb029829aacbc6903a0","datavalue":{"value":{"latitude":50.99444,"longitude":2.92528,"altitude":null,"precision":0.0001,"globe":"http://www.wikidata.org/entity/Q2"},"type":"globecoordinate"},"datatype":"globe-coordinate"},"type":"statement","id":"Q14517013$DBFBFD69-F54D-4C92-A7F4-A44F876E5776","rank":"normal","references":[{"hash":"732ec1c90a6f0694c7db9a71bf09fe7f2b674172","snaks":{"P143":[{"snaktype":"value","property":"P143","hash":"9123b0de1cc9c3954366ba797d598e4e1ea4146f","datavalue":{"value":{"entity-type":"item","numeric-id":10000,"id":"Q10000"},"type":"wikibase-entityid"},"datatype":"wikibase-item"}]},"snaks-order":["P143"]}]}],"P17":[{"mainsnak":{"snaktype":"value","property":"P17","hash":"c2859f311753176d6bdfa7da54ceeeac7acb52c8","datavalue":{"value":{"entity-type":"item","numeric-id":31,"id":"Q31"},"type":"wikibase-entityid"},"datatype":"wikibase-item"},"type":"statement","id":"Q14517013$C12E4DA5-44E1-41ED-BF3D-C84381246429","rank":"normal"}],"P18":[{"mainsnak":{"snaktype":"value","property":"P18","hash":"af765166ecaa7d01ea800812b5b356886b8849a0","datavalue":{"value":"Klerken Vredesmolen R01.jpg","type":"string"},"datatype":"commonsMedia"},"type":"statement","id":"Q14517013$5291801E-11BE-4CE7-8F42-D0D6A120F390","rank":"normal"}],"P2867":[{"mainsnak":{"snaktype":"value","property":"P2867","hash":"b1c627972ba2cc71e3567d2fb56cb5f90dd64007","datavalue":{"value":"893","type":"string"},"datatype":"external-id"},"type":"statement","id":"Q14517013$2aff9dcd-4d24-cd92-b5af-f6268425695f","rank":"normal"}],"P31":[{"mainsnak":{"snaktype":"value","property":"P31","hash":"9b48263bb51c506553aac2281ae331353b5c9002","datavalue":{"value":{"entity-type":"item","numeric-id":38720,"id":"Q38720"},"type":"wikibase-entityid"},"datatype":"wikibase-item"},"type":"statement","id":"Q14517013$46dd9d89-4999-eee6-20a4-c4f6650b1d9c","rank":"normal"},{"mainsnak":{"snaktype":"value","property":"P31","hash":"a1d6f3409c57de0361c68263c9397a99dabe19ea","datavalue":{"value":{"entity-type":"item","numeric-id":3851468,"id":"Q3851468"},"type":"wikibase-entityid"},"datatype":"wikibase-item"},"type":"statement","id":"Q14517013$C83A8B1F-7798-493A-86C9-EC0EFEE356B3","rank":"normal"},{"mainsnak":{"snaktype":"value","property":"P31","hash":"ee5ba9185bdf9f0eb80b52e1cdc70c5883fac95a","datavalue":{"value":{"entity-type":"item","numeric-id":623605,"id":"Q623605"},"type":"wikibase-entityid"},"datatype":"wikibase-item"},"type":"statement","id":"Q14517013$CF74DC2E-6814-4755-9BAD-6EE9FEF637DD","rank":"normal"}],"P2671":[{"mainsnak":{"snaktype":"value","property":"P2671","hash":"83fb38a3c6407f7d0d7bb051d1c31cea8ae26975","datavalue":{"value":"/g/121cb15z","type":"string"},"datatype":"external-id"},"type":"statement","id":"Q14517013$E6FFEF32-0131-42FD-9C66-1A406B68059A","rank":"normal"}]},"sitelinks":{"commonswiki":{"site":"commonswiki","title":"Category:Vredesmolen, Klerken","badges":[],"url":"https://commons.wikimedia.org/wiki/Category:Vredesmolen,_Klerken"},"nlwiki":{"site":"nlwiki","title":"Vredesmolen","badges":[],"url":"https://nl.wikipedia.org/wiki/Vredesmolen"}}}}} + ) + + + const wdata = await Wikidata.LoadWikidataEntryAsync(14517013) + T.isTrue(wdata.wikisites.has("nl"), "dutch for wikisite not found") + equal("Vredesmolen", wdata.wikisites.get("nl")) + } + + ] + ]); + } + +} \ No newline at end of file